neutron 安全组代码实现(二)

前面了解了创建安全组时neutron server侧的具体流程,当给虚机绑定安全组时我们看下具体做了啥
  1. 创建一个自定义的安全组,虚机使用该安全组,然后变更该安全组
#neutron security-group-create sg-debug
#neutron security-group-rule-create sg-debug --direction ingress --protocol tcp --port-range-min 22 --port-range-max 22 --ethertype IPv4
  1. neutron/db/securitygroups_rpc_base.py

    由前面安全组代码实现一中,我们知道,最终执行了ML2 plugin的create_security_group_rule 方法,由类继承我们知道最终执行了 SecurityGroupServerNotifierRpcMixin.create_security_group_rule, 函数中执行了父类sg_db.SecurityGroupDbMixin 中的create_security_group_rule

class SecurityGroupServerNotifierRpcMixin(sg_db.SecurityGroupDbMixin):
    .......
     def create_security_group_rule(self, context, security_group_rule):
        rule = super(SecurityGroupServerNotifierRpcMixin,
                     self).create_security_group_rule(context,
                                                      security_group_rule)
        sgids = [rule['security_group_id']]
        self.notifier.security_groups_rule_updated(context, sgids)
        return rule
  1. neutron/db/securitygroups_db.py

    数据库中插入rule记录,并notify通知

class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
                           rbac_mixin.RbacPluginMixin):
                           
    ..............                       
    @db_api.retry_if_session_inactive()
    def create_security_group_rule(self, context, security_group_rule):
        res = self._create_security_group_rule(context, security_group_rule)
        registry.notify(
            resources.SECURITY_GROUP_RULE, events.AFTER_CREATE, self,
            context=context, security_group_rule=res)
        return res

    def _create_security_group_rule(self, context, security_group_rule,
                                    validate=True):
        if validate:
            sg_id = self._validate_security_group_rule(context,
                                                       security_group_rule)
        rule_dict = security_group_rule['security_group_rule']
        remote_ip_prefix = rule_dict.get('remote_ip_prefix')
        if remote_ip_prefix:
            remote_ip_prefix = utils.AuthenticIPNetwork(remote_ip_prefix)

        protocol = rule_dict.get('protocol')
        if protocol:
            # object expects strings only
            protocol = six.text_type(protocol)

        args = {
            'id': (rule_dict.get('id') or uuidutils.generate_uuid()),
            'project_id': rule_dict['tenant_id'],
            'security_group_id': rule_dict['security_group_id'],
            'direction': rule_dict['direction'],
            'remote_group_id': rule_dict.get('remote_group_id'),
            'ethertype': rule_dict['ethertype'],
            'protocol': protocol,
            'remote_ip_prefix': remote_ip_prefix,
            'description': rule_dict.get('description'),
        }

        port_range_min = self._safe_int(rule_dict['port_range_min'])
        if port_range_min is not None:
            args['port_range_min'] = port_range_min

        port_range_max = self._safe_int(rule_dict['port_range_max'])
        if port_range_max is not None:
            args['port_range_max'] = port_range_max

        kwargs = {
            'context': context,
            'security_group_rule': args
        }
        self._registry_notify(resources.SECURITY_GROUP_RULE,
                              events.BEFORE_CREATE,
                              exc_cls=ext_sg.SecurityGroupConflict, **kwargs)
        with db_api.CONTEXT_WRITER.using(context):
            if validate:
                self._check_for_duplicate_rules(context, sg_id,
                                                [security_group_rule])
            sg_rule = sg_obj.SecurityGroupRule(context, **args)
            sg_rule.create()

            # fetch sg_rule from db to load the sg rules with sg model
            # otherwise a DetachedInstanceError can occur for model extensions
            sg_rule = sg_obj.SecurityGroupRule.get_object(context,
                                                          id=sg_rule.id)
            res_rule_dict = self._make_security_group_rule_dict(sg_rule.db_obj)
            kwargs['security_group_rule'] = res_rule_dict
            self._registry_notify(
                resources.SECURITY_GROUP_RULE,
                events.PRECOMMIT_CREATE,
                exc_cls=ext_sg.SecurityGroupConflict, **kwargs)
        return res_rule_dict
  1. neutron/db/securitygroups_rpc_base.py

self.notifier.security_groups_rule_updated(context, sgids) 最终发送了rpc广播给l2 agent

 def create_security_group_rule(self, context, security_group_rule):
        rule = super(SecurityGroupServerNotifierRpcMixin,
                     self).create_security_group_rule(context,
                                                      security_group_rule)
        sgids = [rule['security_group_id']]
        self.notifier.security_groups_rule_updated(context, sgids)
        return rule
        
class SecurityGroupAgentRpcApiMixin(object):
    def security_groups_rule_updated(self, context, security_groups):
        """Notify rule updated security groups."""
        if not security_groups:
            return
        cctxt = self.client.prepare(version=self.SG_RPC_VERSION,
                                    topic=self._get_security_group_topic(),
                                    fanout=True)
        cctxt.cast(context, 'security_groups_rule_updated',
                   security_groups=security_groups)
  1. neutron/agent/securitygroups_rpc.py

agent收到消息后,self.devices_to_refilter 中加入了update的device,最后经ovs agent rpc_loop时检测到了,执行了process_network_ports 更新了port的安全组规则

    def security_groups_rule_updated(self, security_groups):
        LOG.info("Security group "
                 "rule updated %r", security_groups)
        self._security_group_updated(
            security_groups,
            'security_groups',
            'sg_rule')
    def _security_group_updated(self, security_groups, attribute, action_type):
        devices = []
        sec_grp_set = set(security_groups)
        for device in self.firewall.ports.values():
            if sec_grp_set & set(device.get(attribute, [])):
                devices.append(device['device'])
        if devices:
            if self.use_enhanced_rpc:
                self.firewall.security_group_updated(action_type, sec_grp_set)
            if self.defer_refresh_firewall:
                LOG.debug("Adding %s devices to the list of devices "
                          "for which firewall needs to be refreshed",
                          devices)
                #最终将device加到devices_to_refilter中
                self.devices_to_refilter |= set(devices)
            else:
                self.refresh_firewall(devices)        
  1. neutron/plugins/ml/drivers/openvswitchagent/ovs_neutron_agent.py

    ovs在loop 循环中,因 devices_to_refilter变更,导致需要process_network_ports进而setup_port_filters,执行了 refresh_firewall

def firewall_refresh_needed(self):
        return self.global_refresh_firewall or self.devices_to_refilter
.....
def rpc_loop(self, polling_manager):
	.....................................
	while self._check_and_handle_signal():
	 				...........................
					# Secure and wire/unwire VIFs and update their status
                    # on Neutron server
                    if (self._port_info_has_changes(port_info) or
                            self.sg_agent.firewall_refresh_needed() or
                            ovs_restarted):
                        LOG.debug("Starting to process devices in:%s",
                                  port_info)
                        provisioning_needed = (
                                ovs_restarted or bridges_recreated)
                        failed_devices = self.process_network_ports(
                            port_info, provisioning_needed)
                        if need_clean_stale_flow:
                            self.cleanup_stale_flows()
                            need_clean_stale_flow = False
                        LOG.debug("Agent rpc_loop - iteration:%(iter_num)d - "
                                  "ports processed. Elapsed:%(elapsed).3f",
                                  {'iter_num': self.iter_num,
                                   'elapsed': time.time() - start})
                                   
 def process_network_ports(self, port_info, provisioning_needed):
        failed_devices = {'added': set(), 'removed': set()}	
    .............
	self.sg_agent.setup_port_filters(added_ports,
                                         port_info.get('updated', set()))
  1. neutron/agent/securitygroups_rpc.py

最终执行了refresh_firewall , 最终调用firewall driver更新

     def setup_port_filters(self, new_devices, updated_devices):
     .................
             if updated_devices:
                LOG.debug("Refreshing firewall for %d devices",
                          len(updated_devices))
                self.refresh_firewall(updated_devices)
     @skip_if_noopfirewall_or_firewall_disabled
    def refresh_firewall(self, device_ids=None):
        LOG.info("Refresh firewall rules")
        if not device_ids:
            device_ids = self.firewall.ports.keys()
            if not device_ids:
                LOG.info("No ports here to refresh firewall")
                return
        self._apply_port_filter(device_ids, update_filter=True)
        
    def _apply_port_filter(self, device_ids, update_filter=False):
        step = common_constants.AGENT_RES_PROCESSING_STEP
        if self.use_enhanced_rpc:
            devices = {}
            security_groups = {}
            security_group_member_ips = {}
            for i in range(0, len(device_ids), step):
            	#这里调用rpc
                devices_info = self.plugin_rpc.security_group_info_for_devices(
                    self.context, list(device_ids)[i:i + step])
                devices.update(devices_info['devices'])
                security_groups.update(devices_info['security_groups'])
                security_group_member_ips.update(devices_info['sg_member_ips'])
        else:
        	#调用父类SecurityGroupInfoAPIMixin的函数
            devices = self.plugin_rpc.security_group_rules_for_devices(
                self.context, list(device_ids))
        trusted_devices = self._get_trusted_devices(device_ids, devices)

        with self.firewall.defer_apply():
            if self.use_enhanced_rpc:
                LOG.debug("Update security group information for ports %s",
                          devices.keys())
                self._update_security_group_info(
                    security_groups, security_group_member_ips)
            for device in devices.values():
                if update_filter:
                    LOG.debug("Update port filter for %s", device['device'])
                    #最终调用firewall driver更新防火墙规则
                    self.firewall.update_port_filter(device)
                else:
                    LOG.debug("Prepare port filter for %s", device['device'])
                    self.firewall.prepare_port_filter(device)
            self.firewall.process_trusted_ports(trusted_devices)
  1. neutron/api/rpc/handlers/securitygroups_rpc.py

security_group_info_for_ports调用父类SecurityGroupInfoAPIMixin的函数

  1. neutron/db/securitygroups_rpc_base.py

从db中查询port的安全组信息

    def security_group_info_for_ports(self, context, ports):
        sg_info = {'devices': ports,
                   'security_groups': {},
                   'sg_member_ips': {}}
        rules_in_db = self._select_rules_for_ports(context, ports)
        remote_security_group_info = {}
        for (port_id, rule_in_db) in rules_in_db:
            remote_gid = rule_in_db.get('remote_group_id')
            security_group_id = rule_in_db.get('security_group_id')
            ethertype = rule_in_db['ethertype']
            if ('security_group_source_groups'
                    not in sg_info['devices'][port_id]):
                sg_info['devices'][port_id][
                    'security_group_source_groups'] = []

            if remote_gid:
                if (remote_gid
                    not in sg_info['devices'][port_id][
                        'security_group_source_groups']):
                    sg_info['devices'][port_id][
                        'security_group_source_groups'].append(remote_gid)
                if remote_gid not in remote_security_group_info:
                    remote_security_group_info[remote_gid] = {}
                if ethertype not in remote_security_group_info[remote_gid]:
                    # this set will be serialized into a list by rpc code
                    remote_security_group_info[remote_gid][ethertype] = set()

            direction = rule_in_db['direction']
            rule_dict = {
                'direction': direction,
                'ethertype': ethertype}

            for key in ('protocol', 'port_range_min', 'port_range_max',
                        'remote_ip_prefix', 'remote_group_id', 'action', 'priority'):
                if rule_in_db.get(key) is not None:
                    if key == 'remote_ip_prefix':
                        direction_ip_prefix = DIRECTION_IP_PREFIX[direction]
                        rule_dict[direction_ip_prefix] = rule_in_db[key]
                        continue
                    rule_dict[key] = rule_in_db[key]
                else:
                    if key == 'action':
                        rule_dict[key] = 'allow'
                    if key == 'priority':
                        rule_dict[key] = 1000

            if security_group_id not in sg_info['security_groups']:
                sg_info['security_groups'][security_group_id] = []
            if rule_dict not in sg_info['security_groups'][security_group_id]:
                sg_info['security_groups'][security_group_id].append(
                    rule_dict)
        # Update the security groups info if they don't have any rules
        sg_ids = self._select_sg_ids_for_ports(context, ports)
        for (sg_id, ) in sg_ids:
            if sg_id not in sg_info['security_groups']:
                sg_info['security_groups'][sg_id] = []

        sg_info['sg_member_ips'] = remote_security_group_info
        # the provider rules do not belong to any security group, so these
        # rules still reside in sg_info['devices'] [port_id]
        self._apply_provider_rule(context, sg_info['devices'])

        return self._get_security_group_member_ips(context, sg_info)
  1. neutron/agent/linux/iptables_firewall.py

我们主要还是用的iptables,所以这里跳到iptables driver, , 先remove掉iptables相关chain,再新建相关chain

class IptablesFirewallDriver(firewall.FirewallDriver):
    .......................
    def update_port_filter(self, port):
        LOG.debug("Updating device (%s) filter", port['device'])
        if port['device'] not in self.ports:
            LOG.info('Attempted to update port filter which is not '
                     'filtered %s', port['device'])
            return
        #清空iptables chain
        self._remove_chains()
        self._set_ports(port)
        self._setup_chains()
        return self.iptables.apply()
   	def _setup_chain(self, port, DIRECTION):
        self._add_chain(port, DIRECTION)
        #将规则格式化后加到安全组中
        self._add_rules_by_security_group(port, DIRECTION)
    def _add_rules_by_security_group(self, port, direction):
        # select rules for current port and direction
        security_group_rules = self._select_sgr_by_direction(port, direction)
        security_group_rules += self._select_sg_rules_for_port(port, direction)
        # split groups by ip version
        # for ipv4, iptables command is used
        # for ipv6, iptables6 command is used
        ipv4_sg_rules, ipv6_sg_rules = self._split_sgr_by_ethertype(
            security_group_rules)
        ipv4_iptables_rules = []
        ipv6_iptables_rules = []
        # include fixed egress/ingress rules
        if direction == constants.EGRESS_DIRECTION:
            self._add_fixed_egress_rules(port,
                                         ipv4_iptables_rules,
                                         ipv6_iptables_rules)
        elif direction == constants.INGRESS_DIRECTION:
            ipv6_iptables_rules += self._accept_inbound_icmpv6()
        # include IPv4 and IPv6 iptable rules from security group
        #按照规则生成iptables规则命令
        ipv4_iptables_rules += self._convert_sgr_to_iptables_rules(
            ipv4_sg_rules)
        ipv6_iptables_rules += self._convert_sgr_to_iptables_rules(
            ipv6_sg_rules)
        # finally add the rules to the port chain for a given direction
        self._add_rules_to_chain_v4v6(self._port_chain_name(port, direction),
                                      ipv4_iptables_rules,
                                      ipv6_iptables_rules)  
    def _convert_sgr_to_iptables_rules(self, security_group_rules):
        iptables_rules = []
        self._allow_established(iptables_rules)
        seen_sg_rules = set()
        for rule in sorted(security_group_rules, key=lambda security_group_rules: (security_group_rules['priority'], len(security_group_rules['action']))):
            #_convert_sg_rule_to_iptables_args 是具体rule转为iptables命令的方法
            args = self._convert_sg_rule_to_iptables_args(rule)
            if args:
                rule_command = ' '.join(args)
                if rule_command in seen_sg_rules:
                    # since these rules are from multiple security groups,
                    # there may be duplicates so we prune them out here
                    continue
                seen_sg_rules.add(rule_command)
                iptables_rules.append(rule_command)

        self._drop_invalid_packets(iptables_rules)
        iptables_rules += [comment_rule('-j $sg-fallback',
                                        comment=ic.UNMATCHED)]
        return iptables_rules                                    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

robin5911

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值