• OpenStack-Neutron-Fwaas-代码【一】


    Kilo的代码中对Fwaas做了优化,可以将规则应用到单个路由上

    但是juno里面还是应用到租户的所有路由上的,因此对juno的代码做了修改

    1. 介绍

    在FWaaS中,一个租户可以创建多个防火墙,而每一个防火墙和多个Policies关联,每个Policies由多个Rules按序组成。不能直接应用一个rule,它必须先加入到一个policy中,因为它需要先审计。如下图:

    审计的功能目前是这样的:新建一个策略的时候默认审计是False,在新加或者修改一个规则时这个值都是False,需要手动去确认后设置为True

    代码里暂时没有看到用这个字段作功能上的判断,fwaas的规则是写在qr-这个port上的

    2. 创建一个防火墙

    juno中创建墙是不指定路由id的,因为会应用到所有的路由上。kilo中优化了这里,让用户在创建防火墙的时候给定路由的id列表,如果不给定,默认是所有路由

    文件路径:neutronservicesfirewallfwaas_plugin.py

    line:230 

    方法:create_firewall

    这个plugin是在配置文件中指定的setup.cfg中 [neutron.service_plugins] section下面

    def create_firewall(self, context, firewall):
            LOG.debug("create_firewall() called")
            tenant_id = self._get_tenant_id_for_create(context,
                firewall['firewall'])
    
            fw_new_rtrs = self._get_routers_for_create_firewall( #【1】获取用户参数中的router_ids,如果没有给定返回所有的路由ids。同时会校验给定的路由是否已经被使用
                tenant_id, context, firewall)
    
            if not fw_new_rtrs: #当前租户没有创建路由
                # no messaging to agent needed, and fw needs to go
                # to INACTIVE(no associated rtrs) state.
                status = const.INACTIVE
                fw = super(FirewallPlugin, self).create_firewall(
                    context, firewall, status)
                fw['router_ids'] = []
                return fw
            else: 
                fw = super(FirewallPlugin, self).create_firewall(
                    context, firewall)  #【2】调用db中的create_firewall方法,创建防火墙数据库记录以及防火墙和路由的绑定记录
                fw['router_ids'] = fw_new_rtrs
    
            fw_with_rules = (
                self._make_firewall_dict_with_rules(context, fw['id'])) #【3】根据policy_id获取数据库对象,获取到策略中的防火墙规则列表
    
            fw_with_rtrs = {'fw_id': fw['id'],
                            'router_ids': fw_new_rtrs}
            self.set_routers_for_firewall(context, fw_with_rtrs) #【4】创建防火墙和路由的绑定记录
            fw_with_rules['add-router-ids'] = fw_new_rtrs #【5】表示是添加的路由 和修改或者删除路由方法对应
            fw_with_rules['del-router-ids'] = []
    
            self.agent_rpc.create_firewall(context, fw_with_rules) #通知防火墙agent去创建防火墙
    
            return fw
    

    【1】获取路由id列表

    def _get_routers_for_create_firewall(self, tenant_id, context, firewall):
            # pop router_id as this goes in the router association db
            # and not firewall db
            router_ids = firewall['firewall'].pop('router_ids', None)
            if router_ids == attr.ATTR_NOT_SPECIFIED: #object() 即用户没有给定路由id列表
                # old semantics router-ids keyword not specified pick up
                # all routers on tenant.
                l3_plugin = manager.NeutronManager.get_service_plugins().get(
                    const.L3_ROUTER_NAT)
                ctx = neutron_context.get_admin_context()
                routers = l3_plugin.get_routers(ctx) #获取租户所有路由列表
                router_ids = [
                    router['id']
                    for router in routers
                    if router['tenant_id'] == tenant_id]
                # validation can still fail this if there is another fw
                # which is associated with one of these routers.
                self.validate_firewall_routers_not_in_use(context, router_ids) #验证路由是否被占用
                return router_ids
            else:
                if not router_ids:
                    # This indicates that user specifies no routers.
                    return []
                else:
                    # some router(s) provided.
                    self.validate_firewall_routers_not_in_use(context, router_ids) #验证路由是否被占用
            return router_ids

    【2】db中的create_firewall方法

    def create_firewall(self, context, firewall, status=None):
            LOG.debug("create_firewall() called")
            fw = firewall['firewall']
            tenant_id = self._get_tenant_id_for_create(context, fw)
            # distributed routers may required a more complex state machine;
            # the introduction of a new 'CREATED' state allows this, whilst
            # keeping a backward compatible behavior of the logical resource.
            if not status:
                status = (p_const.CREATED if cfg.CONF.router_distributed
                          else p_const.PENDING_CREATE)
            with context.session.begin(subtransactions=True):
                if not fw.get('tenant_id'):
                    fw['tenant_id'] = self._get_tenant_id_for_create(context, fw) #_validata_fw_parameters方法会校验fw对象中的tenant_id
                self._validate_fw_parameters(context, fw) #验证firewall对象的tenent_id和policy中的tenant_id是否一致,验证policy的shared字段是否为True
                firewall_db = Firewall(
                    id=uuidutils.generate_uuid(),
                    tenant_id=tenant_id,
                    name=fw['name'],
                    description=fw['description'],
                    firewall_policy_id=fw['firewall_policy_id'],
                    admin_state_up=fw['admin_state_up'],
                    status=status)
                context.session.add(firewall_db) #添加数据库记录
            return self._make_firewall_dict(firewall_db)
    

    【3】获取防火墙规则

    def _make_firewall_dict_with_rules(self, context, firewall_id):
            firewall = self.get_firewall(context, firewall_id)
            fw_policy_id = firewall['firewall_policy_id']
            if fw_policy_id:
                fw_policy = self.get_firewall_policy(context, fw_policy_id) #根据策略id获取数据库对象
                fw_rules_list = [self.get_firewall_rule(
                    context, rule_id) for rule_id in fw_policy['firewall_rules']] #遍历出规则列表
                firewall['firewall_rule_list'] = fw_rules_list
            else:
                firewall['firewall_rule_list'] = []
            # FIXME(Sumit): If the size of the firewall object we are creating
            # here exceeds the largest message size supported by rabbit/qpid
            # then we will have a problem.
            return firewall
    

    【4】防火墙和路由绑定

    def set_routers_for_firewall(self, context, fw):
            """Sets the routers associated with the fw."""
            with context.session.begin(subtransactions=True):
                for r_id in fw['router_ids']:
                    fw_rtr_db = FirewallRouterAssociation(fw_id=fw['fw_id'],
                                       router_id=r_id) 
                    context.session.add(fw_rtr_db)
    

    neutronservicesfirewallagentsl3referencefirewall_l3_agent.py

    【5】agent中创建防火墙

    def create_firewall(self, context, firewall, host):
            """Handle Rpc from plugin to create a firewall."""
    
            router_ids = self._get_router_ids_for_fw(context, firewall) #获取防火墙对象中的add-router-ids属性值
            if not router_ids:
                return
            router_info_list = self._get_router_info_list_for_tenant(
                router_ids,
                firewall['tenant_id']) #根据路由id获取数据库对象列表
            LOG.debug("Create: Add firewall on Router List: '%s'",
                [ri.router['id'] for ri in router_info_list])
            # call into the driver
            try:
                self.fwaas_driver.create_firewall( #【5.1】这里默认使用iptables driver
                    self.conf.agent_mode,
                    router_info_list,
                    firewall)
                if firewall['admin_state_up']:
                    status = constants.ACTIVE
                else:
                    status = constants.DOWN
            except fw_ext.FirewallInternalDriverError:
                LOG.error(_LE("Firewall Driver Error for create_firewall "
                              "for firewall: %(fwid)s"),
                    {'fwid': firewall['id']})
                status = constants.ERROR
    
            try:
                # send status back to plugin
                self.fwplugin_rpc.set_firewall_status(
                    context,
                    firewall['id'],
                    status)  #【5.2】rpc调用plugin中的更新防火墙状态的方法
            except Exception:
                LOG.error(
                    _LE("FWaaS RPC failure in create_firewall "
                        "for firewall: %(fwid)s"),
                    {'fwid': firewall['id']})
                self.services_sync = True
    

    neutronservicesfirewalldriverslinuxiptables_fwaas.py

    【5.1】创建防火墙

    def create_firewall(self, agent_mode, apply_list, firewall):
            LOG.debug('Creating firewall %(fw_id)s for tenant %(tid)s)',
                      {'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
            try:
                if firewall['admin_state_up']:
                    self._setup_firewall(agent_mode, apply_list, firewall) #【5.1.1】设置防火墙规则
                else:
                    self.apply_default_policy(agent_mode, apply_list, firewall) #这个方法自行查看,就是初始化了基本的规则
            except (LookupError, RuntimeError):
                # catch known library exceptions and raise Fwaas generic exception
                LOG.error(_LE("Failed to create firewall: %s"), firewall['id'])
                raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
    

    【5.1.1】设置防火墙规则

    这里我们重点看规则的添加顺序,规则的拼凑其实很简单

    def _setup_firewall(self, agent_mode, apply_list, firewall):
            fwid = firewall['id']
            for router_info in apply_list: #apply_list就是路由对象列表
                ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
                    agent_mode, router_info)  #获取iptables_manager和网卡前缀列表
                for ipt_if_prefix in ipt_if_prefix_list:
                    ipt_mgr = ipt_if_prefix['ipt']
                    # the following only updates local memory; no hole in FW
                    self._remove_chains(fwid, ipt_mgr) #删除v4 v6 上行和下行方向的链
                    self._remove_default_chains(ipt_mgr) #删除default链,default只有一条drop规则
    
                    # create default 'DROP ALL' policy chain
                    self._add_default_policy_chain_v4v6(ipt_mgr) #重新添加default链
                    #create chain based on configured policy
                    self._setup_chains(firewall, ipt_if_prefix) #重点,请看下面方法中的解释
    
                    # apply the changes immediately (no defer in firewall path)
                    ipt_mgr.defer_apply_off() #将规则应用到iptables中
    
        def _get_chain_name(self, fwid, ver, direction):
            return '%s%s%s' % (CHAIN_NAME_PREFIX[direction],
                               IP_VER_TAG[ver],
                               fwid) #命令规则  i/0+v4/v6+防火墙uuid,这里字符串的长度已经超过设置了,后面在iptables_mangager中会过滤
    
        def _setup_chains(self, firewall, ipt_if_prefix):
            """Create Fwaas chain using the rules in the policy
            """
            fw_rules_list = firewall['firewall_rule_list']
            fwid = firewall['id']
            ipt_mgr = ipt_if_prefix['ipt']
    
            #default rules for invalid packets and established sessions
            invalid_rule = self._drop_invalid_packets_rule() #drop invalid包
            est_rule = self._allow_established_rule() #允许established包,这些都是默认规则,在初始化链规则的时候使用
    
            for ver in [IPV4, IPV6]:
                if ver == IPV4:
                    table = ipt_mgr.ipv4['filter']
                else:
                    table = ipt_mgr.ipv6['filter']
                ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
                ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
                for name in [ichain_name, ochain_name]:
                    table.add_chain(name)  #add_chain方法会将链的长度裁剪,长度小于16
                    table.add_rule(name, invalid_rule) #添加默认规则
                    table.add_rule(name, est_rule) #添加默认规则
    
            for rule in fw_rules_list:
                if not rule['enabled']: #如果值为False,跳过
                    continue
                iptbl_rule = self._convert_fwaas_to_iptables_rule(rule) #将界面给定的规则转换成iptables规则,就是拼凑
                if rule['ip_version'] == 4:
                    ver = IPV4
                    table = ipt_mgr.ipv4['filter']
                else:
                    ver = IPV6
                    table = ipt_mgr.ipv6['filter']
                ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
                ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
                table.add_rule(ichain_name, iptbl_rule)  #将界面规则添加到上行和下行链中,这里可以看到是同时添加到两个链中
                table.add_rule(ochain_name, iptbl_rule)
            self._enable_policy_chain(fwid, ipt_if_prefix)  #【5.1.1.1】重点,看下面的解释
    
        def _remove_default_chains(self, nsid):
            """Remove fwaas default policy chain."""
            self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid)
            self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid)
    
        def _remove_chains(self, fwid, ipt_mgr):
            """Remove fwaas policy chain."""
            for ver in [IPV4, IPV6]:
                for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
                    chain_name = self._get_chain_name(fwid, ver, direction)
                    self._remove_chain_by_name(ver, chain_name, ipt_mgr) 
    def _add_default_policy_chain_v4v6(self, ipt_mgr): ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN) ipt_mgr.ipv4['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP') ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN) ipt_mgr.ipv6['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')

    【5.1.1.1】将规则添加到链中

    def _enable_policy_chain(self, fwid, ipt_if_prefix):
          bname = iptables_manager.binary_name #获取前缀字符串,这里获取到的是neutron-l3-agent
          ipt_mgr = ipt_if_prefix['ipt']
          if_prefix = ipt_if_prefix['if_prefix']
      
          for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']),
                             (IPV6, ipt_mgr.ipv6['filter'])]:
              for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
                  chain_name = self._get_chain_name(fwid, ver, direction)
                  chain_name = iptables_manager.get_chain_name(chain_name) #这里的chain_name已经是一个长度小于16的字符串了
                  if chain_name in tbl.chains:
                      jump_rule = ['%s %s+ -j %s-%s' % (IPTABLES_DIR[direction],
                          if_prefix, bname, chain_name)]
                      self._add_rules_to_chain(ipt_mgr,  #在neutron-l3-agent-FORWARD链中添加跳跃规则,跳到neutron-l3-agent-iv4xxx链或者neutron-l3-agent-ov4xxx链
                          ver, 'FORWARD', jump_rule)
    
          #jump to DROP_ALL policy
          chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
          jump_rule = ['-o %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
          self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
          self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule) #在neutron-l3-agent-FORWARD链中添加跳跃规则,跳到neutron-l3-agent-fwaas-defau链
        #jump to DROP_ALL policy
          chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
          jump_rule = ['-i %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
          self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
          self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)#在neutron-l3-agent-FORWARD链中添加跳跃规则,跳到neutron-l3-agent-fwaas-defau链
    

    总结一下链添加规则的流程

    1. 初始化链(增加内置链的包装链,并将包装链作为规则添加到内置链中),这一步是在iptables_manager.py中初始化的

    2. 创建并初始化iv4xxx/ov4xxx链

    3. 将界面的规则添加到链中(同时添加到出和入链中)

    4. 将iv4xxx/ov4xxx链放到neutron-l3-agent-FORWARD链中

    5. 更新iptables规则

    下一节通过查看iptables的具体规则来说明fwaas的流程

  • 相关阅读:
    C++:随机数生成
    C++:typedef 与 #define 的区别
    读书笔记
    从零开始:Spring Security Oauth2 讲解及实战
    SpingCloud:Gateway+Nginx+Stomp+Minio构建聊天室并进行文件传输
    Docker image-Ubuntu(jessie)下postgresql-client安装方法
    7张图了解kafka基本概念
    打包上传python代码到pypi,通过pip安装使用
    Asp.Net Core 中的HTTP协议详解
    LeetCode 1931. Painting a Grid With Three Different Colors(DP)
  • 原文地址:https://www.cnblogs.com/smallcoderhujin/p/4498153.html
Copyright © 2020-2023  润新知