neutron/agent/l3/router_info.py
def __init__(self,
router_id,
router,
agent_conf,
interface_driver,
use_ipv6=False):
self.router_id = router_id
self.ex_gw_port = None
self._snat_enabled = None
self.fip_map = {}
self.internal_ports = []
self.floating_ips = set()
# Invoke the setter for establishing initial SNAT action
self.router = router
self.use_ipv6 = use_ipv6
ns = self.create_router_namespace_object(
router_id, agent_conf, interface_driver, use_ipv6)
self.router_namespace = ns
self.ns_name = ns.name
self.available_mark_ids = set(range(ADDRESS_SCOPE_MARK_ID_MIN,
ADDRESS_SCOPE_MARK_ID_MAX))
self._address_scope_to_mark_id = {
DEFAULT_ADDRESS_SCOPE: self.available_mark_ids.pop()}
self.iptables_manager = iptables_manager.IptablesManager(
use_ipv6=use_ipv6,
namespace=self.ns_name)
self.initialize_address_scope_iptables()
self.routes = []
self.agent_conf = agent_conf
self.driver = interface_driver
# radvd is a neutron.agent.linux.ra.DaemonMonitor
self.radvd = None
- 调用
create_router_namespace_object
创建一个RouterNamespace
对象(用来管理 qrouter- namespace) - 创建一个
IptablesManager
对象,用于对 qrouter- 下的 Iptables 进行操作 - 调用
initialize_address_scope_iptables
def create_router_namespace_object(
self, router_id, agent_conf, iface_driver, use_ipv6):
return namespaces.RouterNamespace(
router_id, agent_conf, iface_driver, use_ipv6)
def initialize_address_scope_iptables(self):
self._initialize_address_scope_iptables(self.iptables_manager)
- 在 qrouter- namespace 上,做一下 iptables 的操作(我们这里只拿 ipv4 解释):
- 对于 mangle 表:
-N neutron-l3-agent-float-snat
-N neutron-l3-agent-floatingip
-N neutron-l3-agent-scope
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-scope
-A neutron-l3-agent-PREROUTING -m connmark ! --mark 0x0/0xffff0000 -j CONNMARK --restore-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-floatingip
-A neutron-l3-agent-float-snat -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
- 对于 filter 表:
-N neutron-l3-agent-scope
-A neutron-l3-agent-FORWARD -j neutron-l3-agent-scope
- 注释:
- 数据包标记(nfmark)
- 连接标记(ctmark)
- 参考
- 创建一个
DaemonMonitor
实例,用来管理radvd
服务 - 创建 qrouter- namespace
@property
def router(self):
return self._router
@router.setter
def router(self, value):
self._router = value
if not self._router:
return
# enable_snat by default if it wasn't specified by plugin
self._snat_enabled = self._router.get('enable_snat', True)
属性方法,router 数据的读取和设置。 设置 router 数据时,判断该 router 是否支持 snat
获取 qr- 网络设备的名称
获取 qg- 网络设备的名称
def get_external_device_interface_name(self, ex_gw_port):
return self.get_external_device_name(ex_gw_port['id'])
def update_routing_table(self, operation, route):
self._update_routing_table(operation, route, self.ns_name)
在当前的 qrouter- namespace 下,调用 ip route 命令更新路由表
- 调用
common_utils.diff_list_of_dict
找出增加和删除的 route - 调用
update_routing_table
实现 route 记录的删除和替换
def get_ex_gw_port(self):
return self.router.get('gw_port')
def get_floating_ips(self):
"""Filter Floating IPs to be hosted on this agent."""
return self.router.get(lib_constants.FLOATINGIP_KEY, [])
根据 floating ip 和 fixed ip 的匹配,构造 snat 和 dnat 的功能。
在 qrouter- namespace 中:
-A neutron-l3-agent-PREROUTING -d 172.16.100.245/32 -i rfp-61eaacf9-d -j DNAT --to-destination 192.168.100.5
-A neutron-l3-agent-float-snat -s 192.168.100.5/32 -j SNAT --to-source 172.16.100.245
def floating_forward_rules(self, floating_ip, fixed_ip):
return [('PREROUTING', '-d %s/32 -j DNAT --to-destination %s' %
(floating_ip, fixed_ip)),
('OUTPUT', '-d %s/32 -j DNAT --to-destination %s' %
(floating_ip, fixed_ip)),
('float-snat', '-s %s/32 -j SNAT --to-source %s' %
(fixed_ip, floating_ip))]
- 貌似 OUTPUT chain 中的这个未实现
def floating_mangle_rules(self, floating_ip, fixed_ip, internal_mark):
mark_traffic_to_floating_ip = (
'floatingip', '-d %s/32 -j MARK --set-xmark %s' % (
floating_ip, internal_mark))
mark_traffic_from_fixed_ip = (
'FORWARD', '-s %s/32 -j $float-snat' % fixed_ip)
return [mark_traffic_to_floating_ip, mark_traffic_from_fixed_ip]
为 floating ip 设定 mangle 表的规则
这个貌似也没有实现
生成 mark 标记,默认都是:0x4000000/0xffff0000
调用 get_address_scope_mark_mask
为 port 上的所有 address_scope 生成 mark
- 先清除 nat 表中带有
floating_ip
tag 的 rule - 调用
get_floating_ips
获取该 router 上的 floating ip - 调用
floating_forward_rules
构造关于 floating ip 的 rule - 应用这些 iptables rule
- 情况说明:
可能有些与 floating ip 绑定的 fixed ip 的 address scope 与 gateway port 的 fixed ip 所在的 address scope 不一致,该方法就是来为这些 floating ip 和 fixed ip 增加 iptables rule 的
- 先清除 mangle 表上的 floating_ip 有关的 rule
- 调用
get_floating_ips
获取所有的 floating ip - 调用
_get_external_address_scope
获取 external gateway port 上 ipv4 的 address scope - 过滤掉 floating ip 的 fixed ip address scope 与 external gateway port 一致的 floating ip
- 若存在不与 gateway port 绑定的 floating ip,则:
- 调用
get_address_scope_mark_mask
为 external gateway port 上的 ipv4 地址创建 mark/mask - 调用
_get_address_scope_mark
获取该 router 上所有 port 的 address scope 的 mark/mask - 为该 port 设置 mark
- 对于所有的不与 gateway port 绑定的 floating ip:
- 获取与这些 floating ip 绑定的 internal fixed ip
- 获取 fixed ip 的 address scope
- 调用
floating_mangle_rules
生成 fixed ip 与 floating ip 的 mangle mark 规则 - 应用这些规则
获取 external gateway port 上 ipv4 的 address scope
- 获取 router 上的 interface(这里不包含 gatway port,我们将这些 port 称为 Internal port)
- 调用
_get_port_devicename_scopemark
获取这些 interface 的 address scope mark/mask - 调用
get_ex_gw_port
获取 router 的 gateway port - 若存在 gateway port,则调用
_get_port_devicename_scopemark
为 gateway port address scope 创建 mark/mask,并将其与 Internal port 的数据合并在一起 - 返回该 router 上所有 port 的 address scope 的 mark/mask
- 对于所有的 ports:
- 获取该 port 在 host 上的 name
- 获取 port 的 ip 地址的 cidr
- 调用
get_port_address_scope_mark
获取该 port 的 address_scope 的 mark/mask - 构造该 port 的 address_scope 的 mark/mask 信息
def process_snat_dnat_for_fip(self):
try:
self.process_floating_ip_nat_rules()
except Exception:
# TODO(salv-orlando): Less broad catching
msg = _('L3 agent failure to setup NAT for floating IPs')
LOG.exception(msg)
raise n_exc.FloatingIpSetupException(msg)
调用 process_floating_ip_nat_rules
实现 snat 表中,floating ip 与 fixed ip 的 snat 与 dnat 的转换
为某个网络设备增加一个 floating ip 地址
待实现
未实现
def remove_floating_ip(self, device, ip_cidr):
device.delete_addr_and_conntrack_state(ip_cidr)
从某个网络设备上删除 floating ip 的地址,并调用 conntrack
清除与该 floating ip 有关的连接记录
def move_floating_ip(self, fip):
return lib_constants.FLOATINGIP_STATUS_ACTIVE
删除设备的上的 ip_cidr 地址,清除其连接记录
获取网络设备的 ip cidr
- 以
IPDevice
来描述该设备 - 调用
get_router_cidrs
获取该设备上已经存在的 Ip 地址 - 调用
_get_gw_ips_cidr
获取网关上的 ipv4 版本的地址 - 调用
get_floating_ips
获取该 router 上的 floating ip - 对于所有的 floating ip:
- 若当前的设备上没有,则给其添加上
- 若 floating ip 绑定的 fixed ip 发生了变化,则调用
move_floating_ip
返回其 active 状态 - 若 floating ip 的状态无变化,则将其置为
NOCHANGE
- 获取该设备上那些被删除掉的 floating ip
- 调用
remove_floating_ip
从该设备上删除那些被删除掉的 floating ip
- 调用
get_ex_gw_port
获取网关地址 - 获取该网关上的 ipv4 地址
调用 process_floating_ip_addresses
实现
def put_fips_in_error_state(self):
fip_statuses = {}
for fip in self.router.get(lib_constants.FLOATINGIP_KEY, []):
fip_statuses[fip['id']] = lib_constants.FLOATINGIP_STATUS_ERROR
return fip_statuses
将 router 上的所有 floating ip 都置为 error 状态
- 清空该 router 的 gw_port、interface、floating ip 记录
- 调用
process_delete
删除 internal 和 external port - 调用
disable_radvd
停止 radvr 服务 - 删除 qrouter- namespace
def disable_radvd(self):
LOG.debug('Terminating radvd daemon in router device: %s',
self.router_id)
self.radvd.disable()
- 若当前还存在 qrouter- namespace,则:
- 调用
_process_internal_ports
处理 internal port 的删除问题 - 调用
_process_external_on_delete
删除 external port
- 获取当前记录的 Interface
- 根据记录获取新的 port 和 旧 port
- 调用
_get_updated_ports
获取 ip 地址发生变化的 port(更新的 port。**所以:**这里一共有三种 port,一种是新增加的、一种是不再需要的旧的、一种是发生了更新的) - 对于新增的 port,调用
internal_network_added
增加新的 port - 对于不再需要的 port,调用
internal_network_removed
删除 port - 对于更新的 port,调用
internal_network_updated
重新设置 port 的 ip - 处理当前 router 上 port 的 ipv6 事项
- 启动 radvd 服务
- 再次判断是否还存在不应该存在的 port,对其进行删除
获取已经存在的但 ip 地址发生变化的 port
调用 _internal_network_added
实现
def _internal_network_added(self, ns_name, network_id, port_id, fixed_ips, mac_address, interface_name, prefix, mtu=None)
- 调用 interface driver plug 增加 port
- 调用 interface driver init_router_port 为 port 添加 ip,并初始化 port 的 route
- 调用
ip_lib.send_ip_addr_adv_notif
发送 arp 通知
- 调用
ip_lib.device_exists
判断该设备是否存在 - 调用 interface driver unplug 删除该 port
def internal_network_updated(self, interface_name, ip_cidrs):
self.driver.init_router_port(
interface_name,
ip_cidrs=ip_cidrs,
namespace=self.ns_name)
设置 port 的 ip,更新其 route 记录
- 调用
get_ex_gw_port
获取 gateway port - 调用
_process_external_gateway
清理所有的 gateway port - 调用
get_external_device_interface_name
获取 gateway port 的名称 - 调用
configure_fip_addresses
处理 gatway port floating ip
- 调用
get_external_device_name
获取 gateway port 的名称 - 若是当前 router 数据中还不存在 external gateway port 则调用
external_gateway_added
为 router 增加 gateway - 若 router 中已经存在 external gateway port 的记录,则调用
_gateway_ports_equal
判断数据是否相等,若是不相等,则调用external_gateway_updated
更新 gateway port - 若 router 中存在 gateway port 的记录,但是 ex_gw_port 不存在,则调用
external_gateway_removed
清除 gateway port - 若二者同时不存在,则调用
gateway_redirect_cleanup
- 调用
_delete_stale_external_devices
删除那些与 gateway port name 不一致的 qg- 设备 - 调用
_handle_router_snat_rules
情况 snat 规则
def external_gateway_added(self, ex_gw_port, interface_name):
preserve_ips = self._list_floating_ip_cidrs()
self._external_gateway_added(
ex_gw_port, interface_name, self.ns_name, preserve_ips)
- 调用
_plug_external_gateway
增加 gateway port - 调用
_get_external_gw_ips
获取 gateway port 所在 subnet 的 gateway ip - 调用
_add_route_to_gw
增加 gateway route 记录 - 调用 interface driver init_router_port 方法为该 port 增加 ip 和对于的 route
- 获取 gateway port 初始化完成后与其绑定的 gateway ip
- 对于那些没有使用的 gateway ip,删除其路由记录
- 对于那些正在使用的 gateway ip,增加其路由记录
- 调用
ip_lib.send_ip_addr_adv_notif
发送 arp 通知
调用 interface driver plug 创建 gateway port
获取 gateway port 所在 subnet 的 gateway ip
- 对于 gateway 上的所有 subnet:
- 判断 subnet 的 gateway ip 是否在 subnet 内
- 若不在 subnet 内,则调用
IPDevice.route.add_route
增加此 ip route
@staticmethod
def _gateway_ports_equal(port1, port2):
return port1 == port2
def external_gateway_updated(self, ex_gw_port, interface_name):
preserve_ips = self._list_floating_ip_cidrs()
self._external_gateway_added(
ex_gw_port, interface_name, self.ns_name, preserve_ips)
- 调用
IPDevice
来描述该设备 - 调用
remove_external_gateway_ip
删除 gateway port 的 Ip 地址 - 调用 interface driver unplug 删除该设备
删除名称与 interface_name 不一样的 external port
- 调用
_empty_snat_chains
清空与 snat 有关的 chain - 将 snat 转发至 floating-nat chain
- 调用
_add_snat_rules
处理相应的规则
清空该 qrouter- 上的 nat 表与 mangle 表上的 POSTROUTING、snat、mark chain
- 调用
process_external_port_address_scope_routing
处理 gateway port address scope 的 route - 若存在 gateway port,则:
- 若该 gateway port 上绑定有 ipv4 地址:
- 调用
external_gateway_nat_snat_rules
增加 snat 规则 - 调用
external_gateway_nat_fip_rules
增加 - 调用
external_gateway_mangle_rules
生成 mangle 表的规则
- 若不支持 snat 则退出
- 若不存在 gateway port 则退出
-A neutron-l3-agent-POSTROUTING -o qg-c68ba5c4-7b -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
- 调用
_get_external_address_scope
获取 external network address scope,为其增加相应的规则
对 gateway port 增加 snat 规则
- 调用
_prevent_snat_for_internal_traffic_rule
阻止内部网络之间走 snat 方法 - 增加如下规则:
-A neutron-l3-agent-snat -m mark ! --mark 0x2/0xffff -m conntrack --ctstate DNAT -j SNAT --to-source 172.16.100.247
nat 表增加如下规则:
-A neutron-l3-agent-POSTROUTING ! -i qg-c68ba5c4-7b ! -o qg-c68ba5c4-7b -m conntrack ! --ctstate DNAT -j ACCEPT
-A neutron-l3-agent-scope -i qg-c68ba5c4-7b -j MARK --set-xmark 0x4000000/0xffff0000
调用 process_floating_ip_addresses
实现
与 ipv6 相关
获取当前的 namespace 中存在的网络设备
判断 port 上是否含有 ipv6 地址 subnet
def address_scope_mangle_rule(self, device_name, mark_mask):
return '-i %s -j MARK --set-xmark %s' % (device_name, mark_mask)
def address_scope_filter_rule(self, device_name, mark_mask):
return '-o %s -m mark ! --mark %s -j DROP' % (
device_name, mark_mask)
在 Interface 上启动 ipv6 ra
判断 ip 中是否含有 ipv6 版本的地址
调用 plugin_rpc.update_floatingip_statuses
更新 Server 端 floating ip 的状态
- 调用
address_scope_mangle_rule
- 调用
address_scope_filter_rule
def process_ports_address_scope_iptables(self):
ports_scopemark = self._get_address_scope_mark()
self._add_address_scope_mark(self.iptables_manager, ports_scopemark)
def process_address_scope(self):
with self.iptables_manager.defer_apply():
self.process_ports_address_scope_iptables()
self.process_floating_ip_address_scope_rules()
- 调用
_process_internal_ports
更新 l3 agent 上的 internal port - 调用
process_external
更新 l3 agent 上的 external port - 调用
process_address_scope
处理 address scope 相关 - 调用
routes_updated
更新 route - 更新自身的 ex_gw_port、fip_map、enable_snat 数据