分享

openstack Neutron分析(4)—— neutron-l3-agent中的iptables

pig2 发表于 2014-10-15 18:20:02 [显示全部楼层] 只看大图 回帖奖励 阅读模式 关闭右栏 0 33931
本帖最后由 pig2 于 2014-10-15 21:20 编辑
问题导读

本文比较复杂,特别是最后的流程图
1.iptables表结构,在neutron中主要用到那两张表?
2.iptables_manager是在哪个class中完成的?
3.nat表如何添加规则?




一.iptables简介
1.iptables数据包处理流程



1.jpg


以本机为目的的包,由上至下,走左边的路
本机产生的包,从local process开始走左边的路
本机转发的包,由上至下走右边的路
简化流程如下:

2.png

2.iptables表结构
在neutron中主要用到filter表和nat表
filter表:
Chain INPUT
Chain FORWARD
Chain OUTPUT
filter表用于信息包过滤,它包含INPUT、OUTPUT和FORWARD 链。
nat表:
Chain PREROUTING
Chain OUTPUT
Chain POSTROUTING
nat表用于网络地址转换,PREROUTING链由指定信息包一到达防火墙就改变它们的规则所组成,而 POSTROUTING 链由指定正当信息包打算离开防火墙时改变它们的规则所组成。

二.l3 agent消息处理


  1. _rpc_loop  ---  _process_router            ---  _router_added
  2.                                                          ---  process_router
  3.                                  
  4.                                                          ---  _router_removed
  5.            
  6.            ---  _process_router_delete        ---  _router_removed
复制代码



在上面几个方法中,会涉及到iptables的处理。
三.iptables_manager初始化
iptables_manager的初始化是在class IptablesManager中完成的,它对iptables的链进行了包装。
源码目录:neutron/neutron/agent/linux/iptables_manager.py
主要操作:
新建一个neutron-filter-top链,这个是没有包装的,加在原生的FORWARD和OUTPUT链上。
对filter表的INPUT,OUTPUT,FORWARD链进行包装,将到达原链的数据包转发到包装链,还增加一个包装的local链。
对于nat表,PREROUTING,OUTPUT,POSTROUTING链进行包装,另外在POSTROUTING链之后加了snat链。
代码分析:
对于l3 agent,binary_name是neturon-l3-agent。
filter表的操作:
增加一个链neutron-filter-top,增加规则:
-A FORWARD -j neutron-filter-top
-A OUTPUT -j neutron-filter-top
增加一个包装链neutron-l3-agent-local,增加规则:
-A neutron-filter-top -j neutron-l3-agent-local

  1. # Add a neutron-filter-top chain. It's intended to be shared
  2.         # among the various nova components. It sits at the very top
  3.         # of FORWARD and OUTPUT.
  4.         for tables in [self.ipv4, self.ipv6]:
  5.             tables['filter'].add_chain('neutron-filter-top', wrap=False)
  6.             tables['filter'].add_rule('FORWARD', '-j neutron-filter-top',
  7.                                       wrap=False, top=True)
  8.             tables['filter'].add_rule('OUTPUT', '-j neutron-filter-top',
  9.                                       wrap=False, top=True)
  10.             tables['filter'].add_chain('local')
  11.             tables['filter'].add_rule('neutron-filter-top', '-j $local',
  12.                                       wrap=False)
复制代码

包装IPv4和IPv6 filter表的INPUT,OUTPUT,FORWARD链,以及IPv4 nat表的PREROUTING,OUTPUT,POSTROUTING链。
将到达原链的数据包转发到包装链:

  1. # Wrap the built-in chains
  2.         builtin_chains = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']},
  3.                           6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}}
  4.         if not state_less:
  5.             self.ipv4.update(
  6.                 {'nat': IptablesTable(binary_name=self.wrap_name)})
  7.             builtin_chains[4].update({'nat': ['PREROUTING',
  8.                                       'OUTPUT', 'POSTROUTING']})
  9.         for ip_version in builtin_chains:
  10.             if ip_version == 4:
  11.                 tables = self.ipv4
  12.             elif ip_version == 6:
  13.                 tables = self.ipv6
  14.             for table, chains in builtin_chains[ip_version].iteritems():
  15.                 for chain in chains:
  16.                     tables[table].add_chain(chain)
  17.                     tables[table].add_rule(chain, '-j $%s' %
  18.                                            (chain), wrap=False)
复制代码

包装链neutron-l3-agent-INPUT,neutron-l3-agent-OUTPUT,neutron-l3-agent-FORWARD,添加规则:
-A INPUT -j neutron-l3-agent-INPUT
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A FORWARD -j neutron-l3-agent-FORWARD
nat表的操作:
(承上面的代码)
包装链neutron-l3-agent-PREROUTING,neutron-l3-agent-OUTPUT,neutron-l3-agent-POSTROUTING,添加规则:
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
nat表中添加neutron-postrouting-bottom链,增加规则:
-A POSTROUTING -j neutron-postrouting-bottom
nat表中添加包装链neutron-l3-agent-snat,增加规则:
-A neutron-postrouting-bottom -j neutron-l3-agent-snat
nat表中添加包装链neutron-l3-agent-float-snat,增加规则:
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
代码如下:

  1. if not state_less:
  2.             # Add a neutron-postrouting-bottom chain. It's intended to be
  3.             # shared among the various nova components. We set it as the last
  4.             # chain of POSTROUTING chain.
  5.             self.ipv4['nat'].add_chain('neutron-postrouting-bottom',
  6.                                        wrap=False)
  7.             self.ipv4['nat'].add_rule('POSTROUTING',
  8.                                       '-j neutron-postrouting-bottom',
  9.                                       wrap=False)
  10.             # We add a snat chain to the shared neutron-postrouting-bottom
  11.             # chain so that it's applied last.
  12.             self.ipv4['nat'].add_chain('snat')
  13.             self.ipv4['nat'].add_rule('neutron-postrouting-bottom',
  14.                                       '-j $snat', wrap=False)
  15.             # And then we add a float-snat chain and jump to first thing in
  16.             # the snat chain.
  17.             self.ipv4['nat'].add_chain('float-snat')
  18.             self.ipv4['nat'].add_rule('snat', '-j $float-snat')
复制代码

四.l3 agent代码中关于iptables的处理1._router_added
_router_added方法,创建和metadata相关的iptables规则:

  1. def _router_added(self, router_id, router):
  2.         ri = RouterInfo(router_id, self.root_helper,
  3.                         self.conf.use_namespaces, router)
  4.         self.router_info[router_id] = ri
  5.         if self.conf.use_namespaces:
  6.             self._create_router_namespace(ri)
  7.         for c, r in self.metadata_filter_rules():
  8.             ri.iptables_manager.ipv4['filter'].add_rule(c, r)
  9.         for c, r in self.metadata_nat_rules():
  10.             ri.iptables_manager.ipv4['nat'].add_rule(c, r)
  11.         ri.iptables_manager.apply()
  12.         super(L3NATAgent, self).process_router_add(ri)
  13.         if self.conf.enable_metadata_proxy:
  14.             self._spawn_metadata_proxy(ri.router_id, ri.ns_name)
复制代码

1.metadata_filter_rules方法中,如果enable_metadata_proxy为True,增加规则
  1.   def metadata_filter_rules(self):
  2.         rules = []
  3.         if self.conf.enable_metadata_proxy:
  4.             rules.append(('INPUT', '-s 0.0.0.0/0 -d 127.0.0.1 '
  5.                           '-p tcp -m tcp --dport %s '
  6.                           '-j ACCEPT' % self.conf.metadata_port))
  7.         return rules
复制代码

然后在filter表中增加这条规则,接受所有从外面进来到达metadata_port端口的数据包:
-A neutron-l3-agent-INPUT -s 0.0.0.0/0 -d 127.0.0.1 -p tcp -m tcp –dport 9697 -j ACCEPT
2.metadata_nat_rules方法,如果enable_metadata_proxy为True,增加规则

  1. def metadata_nat_rules(self):
  2.         rules = []
  3.         if self.conf.enable_metadata_proxy:
  4.             rules.append(('PREROUTING', '-s 0.0.0.0/0 -d 169.254.169.254/32 '
  5.                           '-p tcp -m tcp --dport 80 -j REDIRECT '
  6.                           '--to-port %s' % self.conf.metadata_port))
  7.         return rules
复制代码

然后在nat表中增加这条规则做DNAT转换,在route之前,将虚拟机访问169.254.169.254端口80的数据包重定向到metadat_port端口:
-A neutron-l3-agent-PREROUTING -s 0.0.0.0/0 -d 169.254.169.254/32 -p tcp -m tcp –dport 80 -j REDIRECT –to-port 9697
再调用iptables_manager.apply()方法,应用规则:
iptables-save -c ,获取当前所有iptables信息;
iptables-restore -c ,应用最新的iptables配置;
2.process_router
process_router方法:
1.perform_snat_action,为external gateway处理SNAT规则

  1. def _handle_router_snat_rules(self, ri, ex_gw_port, internal_cidrs,
  2.                                   interface_name, action):
  3.         # Remove all the rules
  4.         # This is safe because if use_namespaces is set as False
  5.         # then the agent can only configure one router, otherwise
  6.         # each router's SNAT rules will be in their own namespace
  7.         ri.iptables_manager.ipv4['nat'].empty_chain('POSTROUTING')
  8.         ri.iptables_manager.ipv4['nat'].empty_chain('snat')
  9.         # Add back the jump to float-snat
  10.         ri.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat')
  11.         # And add them back if the action if add_rules
  12.         if action == 'add_rules' and ex_gw_port:
  13.             # ex_gw_port should not be None in this case
  14.             # NAT rules are added only if ex_gw_port has an IPv4 address
  15.             for ip_addr in ex_gw_port['fixed_ips']:
  16.                 ex_gw_ip = ip_addr['ip_address']
  17.                 if netaddr.IPAddress(ex_gw_ip).version == 4:
  18.                     rules = self.external_gateway_nat_rules(ex_gw_ip,
  19.                                                             internal_cidrs,
  20.                                                             interface_name)
  21.                     for rule in rules:
  22.                         ri.iptables_manager.ipv4['nat'].add_rule(*rule)
  23.                     break
  24.         ri.iptables_manager.apply()
复制代码

先清空nat表的neutron-l3-agent-POSTROUTING链和neutron-l3-agent-snat链;
再在nat表的neutron-l3-agent-snat链添加规则:
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
然后对应add_rules操作,则处理external_gateway_nat_rules,处理完后在nat表中添加规则:

  1. def external_gateway_nat_rules(self, ex_gw_ip, internal_cidrs,
  2.                                    interface_name):
  3.         rules = [('POSTROUTING', '! -i %(interface_name)s '
  4.                   '! -o %(interface_name)s -m conntrack ! '
  5.                   '--ctstate DNAT -j ACCEPT' %
  6.                   {'interface_name': interface_name})]
  7.         for cidr in internal_cidrs:
  8.             rules.extend(self.internal_network_nat_rules(ex_gw_ip, cidr))
  9.         return rules
复制代码

规则命令如下:
-A neutron-l3-agent-POSTROUTING ! -i qg-XXX ! -o qg-XXX -m conntrack ! –ctstate DNAT -j ACCEPT
这条命令的意思是除了出口和入口都为qg-XXX,(qg即是router上的外部网关接口)匹配除了DNAT之外的其他状态。
然后处理internal_network_nat_rules:

  1.    def internal_network_nat_rules(self, ex_gw_ip, internal_cidr):
  2.         rules = [('snat', '-s %s -j SNAT --to-source %s' %
  3.                  (internal_cidr, ex_gw_ip))]
  4.         return rules
复制代码

规则命令如下:
-A neutron-l3-agent-snat -s internal_cidr -j SNAT –to-source ex_gw_ip
2.process_router_floating_ip_nat_rules方法,处理floating ip,作SNAT/DNAT转换。

  1. def process_router_floating_ip_nat_rules(self, ri):
  2.         """Configure NAT rules for the router's floating IPs.
  3.         Configures iptables rules for the floating ips of the given router
  4.         """
  5.         # Clear out all iptables rules for floating ips
  6.         ri.iptables_manager.ipv4['nat'].clear_rules_by_tag('floating_ip')
  7.         # Loop once to ensure that floating ips are configured.
  8.         for fip in ri.router.get(l3_constants.FLOATINGIP_KEY, []):
  9.             # Rebuild iptables rules for the floating ip.
  10.             fixed = fip['fixed_ip_address']
  11.             fip_ip = fip['floating_ip_address']
  12.             for chain, rule in self.floating_forward_rules(fip_ip, fixed):
  13.                 ri.iptables_manager.ipv4['nat'].add_rule(chain, rule,
  14.                                                          tag='floating_ip')
  15.         ri.iptables_manager.apply()
  16.    def floating_forward_rules(self, floating_ip, fixed_ip):
  17.         return [('PREROUTING', '-d %s -j DNAT --to %s' %
  18.                  (floating_ip, fixed_ip)),
  19.                 ('OUTPUT', '-d %s -j DNAT --to %s' %
  20.                  (floating_ip, fixed_ip)),
  21.                 ('float-snat', '-s %s -j SNAT --to %s' %
  22.                  (fixed_ip, floating_ip))]
复制代码

先清理nat表所有的floationg ip规则;然后floating_forward_rules方法,在nat表中处理floating ip和fixed ip的NAT转换:

具体规则如下:
-A neutron-l3-agent-PREROUTING -d floating_ip -j DNAT –to fixed_ip
-A neutron-l3-agent-OUTPUT -d floating_ip -j DNAT –to fixed_ip
-A neutron-l3-agent-float-snat -s fixed_ip -j SNAT –to floating_ip
3._router_removed
_router_removed方法,删除和metadata相关的规则:

  1.   def _router_removed(self, router_id):
  2.         ri = self.router_info.get(router_id)
  3.         if ri is None:
  4.             LOG.warn(_("Info for router %s were not found. "
  5.                        "Skipping router removal"), router_id)
  6.             return
  7.         ri.router['gw_port'] = None
  8.         ri.router[l3_constants.INTERFACE_KEY] = []
  9.         ri.router[l3_constants.FLOATINGIP_KEY] = []
  10.         self.process_router(ri)
  11.         for c, r in self.metadata_filter_rules():
  12.             ri.iptables_manager.ipv4['filter'].remove_rule(c, r)
  13.         for c, r in self.metadata_nat_rules():
  14.             ri.iptables_manager.ipv4['nat'].remove_rule(c, r)
  15.         ri.iptables_manager.apply()
  16.         if self.conf.enable_metadata_proxy:
  17.             self._destroy_metadata_proxy(ri.router_id, ri.ns_name)
  18.         del self.router_info[router_id]
  19.         self._destroy_router_namespace(ri.ns_name)
复制代码

五.总结
l3 agent初始化完成后,iptables处理流程如下:
3.png




openstack Neutron分析(2)—— neutron-l3-agent


openstack Neutron分析(3)—— neutron-dhcp-agent源码分析


openstack Neutron分析(5)-- neutron openvswitch agent


Openstack之neutron入门


Openstack之neutron入门二







http://www.cnblogs.com/feisky/p/3849219.html


没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条