OpenStack Networking (Neutron)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

iptables_driver.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. # Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. from oslo_config import cfg
  15. from oslo_log import helpers as log_helpers
  16. from oslo_log import log as logging
  17. from oslo_utils import importutils
  18. from neutron._i18n import _
  19. from neutron.agent.l3 import dvr_snat_ns
  20. from neutron.agent.l3 import namespaces
  21. from neutron.agent.linux import ip_lib
  22. from neutron.agent.linux import iptables_manager
  23. from neutron.common import constants
  24. from neutron.common import ipv6_utils
  25. from neutron.conf.agent import common as config
  26. from neutron.services.metering.drivers import abstract_driver
  27. LOG = logging.getLogger(__name__)
  28. NS_PREFIX = 'qrouter-'
  29. WRAP_NAME = 'neutron-meter'
  30. EXTERNAL_DEV_PREFIX = 'qg-'
  31. ROUTER_2_FIP_DEV_PREFIX = namespaces.ROUTER_2_FIP_DEV_PREFIX
  32. TOP_CHAIN = WRAP_NAME + "-FORWARD"
  33. RULE = '-r-'
  34. LABEL = '-l-'
  35. config.register_interface_driver_opts_helper(cfg.CONF)
  36. config.register_interface_opts()
  37. class IptablesManagerTransaction(object):
  38. __transactions = {}
  39. def __init__(self, im):
  40. self.im = im
  41. transaction = self.__transactions.get(im, 0)
  42. transaction += 1
  43. self.__transactions[im] = transaction
  44. def __enter__(self):
  45. return self.im
  46. def __exit__(self, type, value, traceback):
  47. transaction = self.__transactions.get(self.im)
  48. if transaction == 1:
  49. self.im.apply()
  50. del self.__transactions[self.im]
  51. else:
  52. transaction -= 1
  53. self.__transactions[self.im] = transaction
  54. class RouterWithMetering(object):
  55. def __init__(self, conf, router):
  56. self.conf = conf
  57. self.id = router['id']
  58. self.router = router
  59. # TODO(cbrandily): deduplicate ns_name generation in metering/l3
  60. self.ns_name = NS_PREFIX + self.id
  61. self.iptables_manager = None
  62. self.snat_iptables_manager = None
  63. if self.router['distributed']:
  64. # If distributed routers then we need to apply the
  65. # metering agent label rules in the snat namespace as well.
  66. snat_ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(
  67. self.id)
  68. # Check for namespace existence before we assign the
  69. # snat_iptables_manager
  70. if ip_lib.network_namespace_exists(snat_ns_name):
  71. self.snat_iptables_manager = iptables_manager.IptablesManager(
  72. namespace=snat_ns_name,
  73. binary_name=WRAP_NAME,
  74. state_less=True,
  75. use_ipv6=ipv6_utils.is_enabled_and_bind_by_default())
  76. # Check of namespace existence before we assign the iptables_manager
  77. # NOTE(Swami): If distributed routers, all external traffic on a
  78. # compute node will flow through the rfp interface in the router
  79. # namespace.
  80. if ip_lib.network_namespace_exists(self.ns_name):
  81. self.iptables_manager = iptables_manager.IptablesManager(
  82. namespace=self.ns_name,
  83. binary_name=WRAP_NAME,
  84. state_less=True,
  85. use_ipv6=ipv6_utils.is_enabled_and_bind_by_default())
  86. self.metering_labels = {}
  87. class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver):
  88. def __init__(self, plugin, conf):
  89. self.plugin = plugin
  90. self.conf = conf or cfg.CONF
  91. self.routers = {}
  92. if not self.conf.interface_driver:
  93. raise SystemExit(_('An interface driver must be specified'))
  94. LOG.info("Loading interface driver %s", self.conf.interface_driver)
  95. self.driver = importutils.import_object(self.conf.interface_driver,
  96. self.conf)
  97. def _update_router(self, router):
  98. r = self.routers.get(router['id'],
  99. RouterWithMetering(self.conf, router))
  100. r.router = router
  101. self.routers[r.id] = r
  102. return r
  103. @log_helpers.log_method_call
  104. def update_routers(self, context, routers):
  105. # disassociate removed routers
  106. router_ids = set(router['id'] for router in routers)
  107. for router_id, rm in self.routers.items():
  108. if router_id not in router_ids:
  109. self._process_disassociate_metering_label(rm.router)
  110. for router in routers:
  111. old_gw_port_id = None
  112. old_rm = self.routers.get(router['id'])
  113. if old_rm:
  114. old_gw_port_id = old_rm.router['gw_port_id']
  115. gw_port_id = router['gw_port_id']
  116. if gw_port_id != old_gw_port_id:
  117. if old_rm:
  118. if router.get('distributed'):
  119. old_rm_im = old_rm.snat_iptables_manager
  120. else:
  121. old_rm_im = old_rm.iptables_manager
  122. with IptablesManagerTransaction(old_rm_im):
  123. self._process_disassociate_metering_label(router)
  124. if gw_port_id:
  125. self._process_associate_metering_label(router)
  126. elif gw_port_id:
  127. self._process_associate_metering_label(router)
  128. @log_helpers.log_method_call
  129. def remove_router(self, context, router_id):
  130. if router_id in self.routers:
  131. del self.routers[router_id]
  132. def get_external_device_names(self, rm):
  133. gw_port_id = rm.router.get('gw_port_id')
  134. if not gw_port_id:
  135. return None, None
  136. # NOTE (Swami): External device 'qg' should be used on the
  137. # Router namespace if the router is legacy and should be used on
  138. # SNAT namespace if the router is distributed.
  139. ext_dev = (EXTERNAL_DEV_PREFIX +
  140. gw_port_id)[:self.driver.DEV_NAME_LEN]
  141. ext_snat_dev = (ROUTER_2_FIP_DEV_PREFIX +
  142. rm.id)[:self.driver.DEV_NAME_LEN]
  143. return ext_dev, ext_snat_dev
  144. def _process_metering_label_rules(self, rules, label_chain,
  145. rules_chain, ext_dev, im):
  146. if not ext_dev:
  147. return
  148. for rule in rules:
  149. self._add_rule_to_chain(ext_dev, rule, im,
  150. label_chain, rules_chain)
  151. def _process_metering_label_rule_add(self, rule, ext_dev,
  152. label_chain, rules_chain, im):
  153. self._add_rule_to_chain(ext_dev, rule, im, label_chain, rules_chain)
  154. def _process_metering_label_rule_delete(self, rule, ext_dev,
  155. label_chain, rules_chain, im):
  156. self._remove_rule_from_chain(ext_dev, rule, im,
  157. label_chain, rules_chain)
  158. def _add_rule_to_chain(self, ext_dev, rule, im,
  159. label_chain, rules_chain):
  160. ipt_rule = self._prepare_rule(ext_dev, rule, label_chain)
  161. if rule['excluded']:
  162. im.ipv4['filter'].add_rule(rules_chain, ipt_rule,
  163. wrap=False, top=True)
  164. else:
  165. im.ipv4['filter'].add_rule(rules_chain, ipt_rule,
  166. wrap=False, top=False)
  167. def _remove_rule_from_chain(self, ext_dev, rule, im,
  168. label_chain, rules_chain):
  169. ipt_rule = self._prepare_rule(ext_dev, rule, label_chain)
  170. if rule['excluded']:
  171. im.ipv4['filter'].remove_rule(rules_chain, ipt_rule,
  172. wrap=False, top=True)
  173. else:
  174. im.ipv4['filter'].remove_rule(rules_chain, ipt_rule,
  175. wrap=False, top=False)
  176. def _prepare_rule(self, ext_dev, rule, label_chain):
  177. remote_ip = rule['remote_ip_prefix']
  178. if rule['direction'] == 'egress':
  179. dir_opt = '-s %s -o %s' % (remote_ip, ext_dev)
  180. else:
  181. dir_opt = '-d %s -i %s' % (remote_ip, ext_dev)
  182. if rule['excluded']:
  183. ipt_rule = '%s -j RETURN' % dir_opt
  184. else:
  185. ipt_rule = '%s -j %s' % (dir_opt, label_chain)
  186. return ipt_rule
  187. def _process_ns_specific_metering_label(self, router, ext_dev, im):
  188. '''Process metering label based on the associated namespaces.'''
  189. rm = self.routers.get(router['id'])
  190. with IptablesManagerTransaction(im):
  191. labels = router.get(constants.METERING_LABEL_KEY, [])
  192. for label in labels:
  193. label_id = label['id']
  194. label_chain = iptables_manager.get_chain_name(
  195. WRAP_NAME + LABEL + label_id, wrap=False)
  196. rules_chain = iptables_manager.get_chain_name(
  197. WRAP_NAME + RULE + label_id, wrap=False)
  198. exists = rm.metering_labels.get(label_id)
  199. if not exists:
  200. self._create_metering_label_chain(rm,
  201. label_chain,
  202. rules_chain)
  203. rm.metering_labels[label_id] = label
  204. rules = label.get('rules')
  205. if rules:
  206. self._process_metering_label_rules(
  207. rules, label_chain, rules_chain, ext_dev, im)
  208. def _process_associate_metering_label(self, router):
  209. self._update_router(router)
  210. rm = self.routers.get(router['id'])
  211. ext_dev, ext_snat_dev = self.get_external_device_names(rm)
  212. for (im, dev) in [(rm.iptables_manager, ext_dev),
  213. (rm.snat_iptables_manager, ext_snat_dev)]:
  214. if im:
  215. self._process_ns_specific_metering_label(router, dev, im)
  216. def _process_ns_specific_disassociate_metering_label(self, router, im):
  217. '''Disassociate metering label based on specific namespaces.'''
  218. rm = self.routers.get(router['id'])
  219. with IptablesManagerTransaction(im):
  220. labels = router.get(constants.METERING_LABEL_KEY, [])
  221. for label in labels:
  222. label_id = label['id']
  223. if label_id not in rm.metering_labels:
  224. continue
  225. label_chain = iptables_manager.get_chain_name(
  226. WRAP_NAME + LABEL + label_id, wrap=False)
  227. rules_chain = iptables_manager.get_chain_name(
  228. WRAP_NAME + RULE + label_id, wrap=False)
  229. im.ipv4['filter'].remove_chain(label_chain, wrap=False)
  230. im.ipv4['filter'].remove_chain(rules_chain, wrap=False)
  231. def _process_disassociate_metering_label(self, router):
  232. rm = self.routers.get(router['id'])
  233. if not rm:
  234. return
  235. for im in [rm.iptables_manager, rm.snat_iptables_manager]:
  236. if im:
  237. self._process_ns_specific_disassociate_metering_label(
  238. router, im)
  239. labels = router.get(constants.METERING_LABEL_KEY, [])
  240. for label in labels:
  241. label_id = label['id']
  242. del rm.metering_labels[label_id]
  243. @log_helpers.log_method_call
  244. def add_metering_label(self, context, routers):
  245. for router in routers:
  246. self._process_associate_metering_label(router)
  247. @log_helpers.log_method_call
  248. def add_metering_label_rule(self, context, routers):
  249. for router in routers:
  250. self._add_metering_label_rule(router)
  251. @log_helpers.log_method_call
  252. def remove_metering_label_rule(self, context, routers):
  253. for router in routers:
  254. self._remove_metering_label_rule(router)
  255. @log_helpers.log_method_call
  256. def update_metering_label_rules(self, context, routers):
  257. for router in routers:
  258. self._update_metering_label_rules(router)
  259. def _add_metering_label_rule(self, router):
  260. self._process_metering_rule_action(router, 'create')
  261. def _remove_metering_label_rule(self, router):
  262. self._process_metering_rule_action(router, 'delete')
  263. def _create_metering_label_chain(self, rm, label_chain, rules_chain):
  264. rm.iptables_manager.ipv4['filter'].add_chain(label_chain, wrap=False)
  265. rm.iptables_manager.ipv4['filter'].add_chain(rules_chain, wrap=False)
  266. rm.iptables_manager.ipv4['filter'].add_rule(
  267. TOP_CHAIN, '-j ' + rules_chain, wrap=False)
  268. rm.iptables_manager.ipv4['filter'].add_rule(
  269. label_chain, '', wrap=False)
  270. def _process_metering_rule_action_based_on_ns(
  271. self, router, action, ext_dev, im):
  272. '''Process metering rule actions based specific namespaces.'''
  273. rm = self.routers.get(router['id'])
  274. with IptablesManagerTransaction(im):
  275. labels = router.get(constants.METERING_LABEL_KEY, [])
  276. for label in labels:
  277. label_id = label['id']
  278. label_chain = iptables_manager.get_chain_name(
  279. WRAP_NAME + LABEL + label_id, wrap=False)
  280. rules_chain = iptables_manager.get_chain_name(
  281. WRAP_NAME + RULE + label_id, wrap=False)
  282. exists = rm.metering_labels.get(label_id)
  283. if action == 'create' and not exists:
  284. self._create_metering_label_chain(rm,
  285. label_chain,
  286. rules_chain)
  287. rm.metering_labels[label_id] = label
  288. rule = label.get('rule')
  289. if rule:
  290. if action == 'create':
  291. self._process_metering_label_rule_add(
  292. rule, ext_dev, label_chain, rules_chain, im)
  293. elif action == 'delete':
  294. self._process_metering_label_rule_delete(
  295. rule, ext_dev, label_chain, rules_chain, im)
  296. def _process_metering_rule_action(self, router, action):
  297. rm = self.routers.get(router['id'])
  298. if not rm:
  299. return
  300. ext_dev, ext_snat_dev = self.get_external_device_names(rm)
  301. for (im, dev) in [(rm.iptables_manager, ext_dev),
  302. (rm.snat_iptables_manager, ext_snat_dev)]:
  303. if im and dev:
  304. self._process_metering_rule_action_based_on_ns(
  305. router, action, dev, im)
  306. def _update_metering_label_rules_based_on_ns(self, router, ext_dev, im):
  307. '''Update metering lable rules based on namespace.'''
  308. with IptablesManagerTransaction(im):
  309. labels = router.get(constants.METERING_LABEL_KEY, [])
  310. for label in labels:
  311. label_id = label['id']
  312. label_chain = iptables_manager.get_chain_name(
  313. WRAP_NAME + LABEL + label_id, wrap=False)
  314. rules_chain = iptables_manager.get_chain_name(
  315. WRAP_NAME + RULE + label_id, wrap=False)
  316. im.ipv4['filter'].empty_chain(rules_chain, wrap=False)
  317. rules = label.get('rules')
  318. if rules:
  319. self._process_metering_label_rules(
  320. rules, label_chain, rules_chain, ext_dev, im)
  321. def _update_metering_label_rules(self, router):
  322. rm = self.routers.get(router['id'])
  323. if not rm:
  324. return
  325. ext_dev, ext_snat_dev = self.get_external_device_names(rm)
  326. for (im, dev) in [(rm.iptables_manager, ext_dev),
  327. (rm.snat_iptables_manager, ext_snat_dev)]:
  328. if im and dev:
  329. self._update_metering_label_rules_based_on_ns(router, dev, im)
  330. @log_helpers.log_method_call
  331. def remove_metering_label(self, context, routers):
  332. for router in routers:
  333. self._process_disassociate_metering_label(router)
  334. @log_helpers.log_method_call
  335. def get_traffic_counters(self, context, routers):
  336. accs = {}
  337. routers_to_reconfigure = set()
  338. for router in routers:
  339. rm = self.routers.get(router['id'])
  340. if not rm:
  341. continue
  342. for label_id in rm.metering_labels:
  343. try:
  344. chain = iptables_manager.get_chain_name(WRAP_NAME +
  345. LABEL +
  346. label_id,
  347. wrap=False)
  348. chain_acc = rm.iptables_manager.get_traffic_counters(
  349. chain, wrap=False, zero=True)
  350. except RuntimeError:
  351. LOG.exception('Failed to get traffic counters, '
  352. 'router: %s', router)
  353. routers_to_reconfigure.add(router['id'])
  354. continue
  355. if not chain_acc:
  356. continue
  357. acc = accs.get(label_id, {'pkts': 0, 'bytes': 0})
  358. acc['pkts'] += chain_acc['pkts']
  359. acc['bytes'] += chain_acc['bytes']
  360. accs[label_id] = acc
  361. for router_id in routers_to_reconfigure:
  362. del self.routers[router_id]
  363. return accs