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.

817 lines
35KB

  1. # Copyright 2015 Red Hat, Inc.
  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. import mock
  15. from neutron_lib.callbacks import events as callbacks_events
  16. from neutron_lib.callbacks import registry as callbacks_registry
  17. from neutron_lib.callbacks import resources as callbacks_resources
  18. from neutron_lib import constants
  19. import testtools
  20. from neutron.agent.common import ovs_lib
  21. from neutron.agent.common import utils
  22. from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts
  23. from neutron.agent.linux.openvswitch_firewall import exceptions
  24. from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw
  25. from neutron.common import constants as n_const
  26. from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \
  27. as ovs_consts
  28. from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
  29. import ovs_bridge
  30. from neutron.tests import base
  31. TESTING_VLAN_TAG = 1
  32. def create_ofport(port_dict):
  33. ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1,
  34. port_name="port-name")
  35. return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG)
  36. class TestCreateRegNumbers(base.BaseTestCase):
  37. def test_no_registers_defined(self):
  38. flow = {'foo': 'bar'}
  39. ovsfw.create_reg_numbers(flow)
  40. self.assertEqual({'foo': 'bar'}, flow)
  41. def test_all_registers_defined(self):
  42. flow = {'foo': 'bar', 'reg_port': 1, 'reg_net': 2,
  43. 'reg_remote_group': 3}
  44. expected_flow = {'foo': 'bar',
  45. 'reg{:d}'.format(ovsfw_consts.REG_PORT): 1,
  46. 'reg{:d}'.format(ovsfw_consts.REG_NET): 2,
  47. 'reg{:d}'.format(ovsfw_consts.REG_REMOTE_GROUP): 3}
  48. ovsfw.create_reg_numbers(flow)
  49. self.assertEqual(expected_flow, flow)
  50. class TestSecurityGroup(base.BaseTestCase):
  51. def setUp(self):
  52. super(TestSecurityGroup, self).setUp()
  53. self.sg = ovsfw.SecurityGroup('123')
  54. self.sg.members = {'type': [1, 2, 3, 4]}
  55. def test_update_rules_split(self):
  56. rules = [
  57. {'foo': 'bar', 'rule': 'all'}, {'bar': 'foo'},
  58. {'remote_group_id': '123456', 'foo': 'bar'}]
  59. expected_raw_rules = [{'foo': 'bar', 'rule': 'all'}, {'bar': 'foo'}]
  60. expected_remote_rules = [{'remote_group_id': '123456', 'foo': 'bar'}]
  61. self.sg.update_rules(rules)
  62. self.assertEqual(expected_raw_rules, self.sg.raw_rules)
  63. self.assertEqual(expected_remote_rules, self.sg.remote_rules)
  64. def test_update_rules_protocols(self):
  65. rules = [
  66. {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP,
  67. 'ethertype': constants.IPv4},
  68. {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP,
  69. 'ethertype': constants.IPv6},
  70. {'foo': 'bar', 'protocol': constants.PROTO_NAME_IPV6_ICMP_LEGACY,
  71. 'ethertype': constants.IPv6},
  72. {'foo': 'bar', 'protocol': constants.PROTO_NAME_TCP},
  73. {'foo': 'bar', 'protocol': '94'},
  74. {'foo': 'bar', 'protocol': 'baz'},
  75. {'foo': 'no_proto'}]
  76. self.sg.update_rules(rules)
  77. self.assertEqual({'foo': 'no_proto'}, self.sg.raw_rules.pop())
  78. protos = [rule['protocol'] for rule in self.sg.raw_rules]
  79. self.assertEqual([constants.PROTO_NUM_ICMP,
  80. constants.PROTO_NUM_IPV6_ICMP,
  81. constants.PROTO_NUM_IPV6_ICMP,
  82. constants.PROTO_NUM_TCP,
  83. 94,
  84. 'baz'], protos)
  85. def test_get_ethertype_filtered_addresses(self):
  86. addresses = self.sg.get_ethertype_filtered_addresses('type')
  87. expected_addresses = [1, 2, 3, 4]
  88. self.assertEqual(expected_addresses, addresses)
  89. class TestOFPort(base.BaseTestCase):
  90. def setUp(self):
  91. super(TestOFPort, self).setUp()
  92. self.ipv4_addresses = ['10.0.0.1', '192.168.0.1']
  93. self.ipv6_addresses = ['fe80::f816:3eff:fe2e:1']
  94. port_dict = {'device': 1,
  95. 'fixed_ips': self.ipv4_addresses + self.ipv6_addresses}
  96. self.port = create_ofport(port_dict)
  97. def test_ipv4_address(self):
  98. ipv4_addresses = self.port.ipv4_addresses
  99. self.assertEqual(self.ipv4_addresses, ipv4_addresses)
  100. def test_ipv6_address(self):
  101. ipv6_addresses = self.port.ipv6_addresses
  102. self.assertEqual(self.ipv6_addresses, ipv6_addresses)
  103. def test__get_allowed_pairs(self):
  104. port = {
  105. 'allowed_address_pairs': [
  106. {'mac_address': 'foo', 'ip_address': '10.0.0.1'},
  107. {'mac_address': 'bar', 'ip_address': '192.168.0.1'},
  108. {'mac_address': 'qux', 'ip_address': '169.254.0.0/16'},
  109. {'mac_address': 'baz', 'ip_address': '2003::f'},
  110. ]}
  111. allowed_pairs_v4 = ovsfw.OFPort._get_allowed_pairs(port, version=4)
  112. allowed_pairs_v6 = ovsfw.OFPort._get_allowed_pairs(port, version=6)
  113. expected_aap_v4 = {('foo', '10.0.0.1'), ('bar', '192.168.0.1'),
  114. ('qux', '169.254.0.0/16')}
  115. expected_aap_v6 = {('baz', '2003::f')}
  116. self.assertEqual(expected_aap_v4, allowed_pairs_v4)
  117. self.assertEqual(expected_aap_v6, allowed_pairs_v6)
  118. def test__get_allowed_pairs_empty(self):
  119. port = {}
  120. allowed_pairs = ovsfw.OFPort._get_allowed_pairs(port, version=4)
  121. self.assertFalse(allowed_pairs)
  122. def test_update(self):
  123. old_port_dict = self.port.neutron_port_dict
  124. new_port_dict = old_port_dict.copy()
  125. added_ips = [1, 2, 3]
  126. new_port_dict.update({
  127. 'fixed_ips': added_ips,
  128. 'allowed_address_pairs': [
  129. {'mac_address': '00:00:00:00:00:01',
  130. 'ip_address': '192.168.0.1'},
  131. {'mac_address': '00:00:00:00:00:01',
  132. 'ip_address': '2003::f'}],
  133. })
  134. self.port.update(new_port_dict)
  135. self.assertEqual(new_port_dict, self.port.neutron_port_dict)
  136. self.assertIsNot(new_port_dict, self.port.neutron_port_dict)
  137. self.assertEqual(added_ips, self.port.fixed_ips)
  138. self.assertEqual({('00:00:00:00:00:01', '192.168.0.1')},
  139. self.port.allowed_pairs_v4)
  140. self.assertIn(('00:00:00:00:00:01', '2003::f'),
  141. self.port.allowed_pairs_v6)
  142. class TestSGPortMap(base.BaseTestCase):
  143. def setUp(self):
  144. super(TestSGPortMap, self).setUp()
  145. self.map = ovsfw.SGPortMap()
  146. def test_get_or_create_sg_existing_sg(self):
  147. self.map.sec_groups['id'] = mock.sentinel
  148. sg = self.map.get_or_create_sg('id')
  149. self.assertIs(mock.sentinel, sg)
  150. def test_get_or_create_sg_nonexisting_sg(self):
  151. with mock.patch.object(ovsfw, 'SecurityGroup') as sg_mock:
  152. sg = self.map.get_or_create_sg('id')
  153. self.assertEqual(sg_mock.return_value, sg)
  154. def _check_port(self, port_id, expected_sg_ids):
  155. port = self.map.ports[port_id]
  156. expected_sgs = [self.map.sec_groups[sg_id]
  157. for sg_id in expected_sg_ids]
  158. self.assertEqual(port.sec_groups, expected_sgs)
  159. def _check_sg(self, sg_id, expected_port_ids):
  160. sg = self.map.sec_groups[sg_id]
  161. expected_ports = {self.map.ports[port_id]
  162. for port_id in expected_port_ids}
  163. self.assertEqual(sg.ports, expected_ports)
  164. def _create_ports_and_sgroups(self):
  165. sg_1 = ovsfw.SecurityGroup(1)
  166. sg_2 = ovsfw.SecurityGroup(2)
  167. sg_3 = ovsfw.SecurityGroup(3)
  168. port_a = create_ofport({'device': 'a'})
  169. port_b = create_ofport({'device': 'b'})
  170. self.map.ports = {'a': port_a, 'b': port_b}
  171. self.map.sec_groups = {1: sg_1, 2: sg_2, 3: sg_3}
  172. port_a.sec_groups = [sg_1, sg_2]
  173. port_b.sec_groups = [sg_2, sg_3]
  174. sg_1.ports = {port_a}
  175. sg_2.ports = {port_a, port_b}
  176. sg_3.ports = {port_b}
  177. def test_create_port(self):
  178. port = create_ofport({'device': 'a'})
  179. sec_groups = ['1', '2']
  180. port_dict = {'security_groups': sec_groups}
  181. self.map.create_port(port, port_dict)
  182. self._check_port('a', sec_groups)
  183. self._check_sg('1', ['a'])
  184. self._check_sg('2', ['a'])
  185. def test_update_port_sg_added(self):
  186. self._create_ports_and_sgroups()
  187. port_dict = {'security_groups': [1, 2, 3]}
  188. self.map.update_port(self.map.ports['b'], port_dict)
  189. self._check_port('a', [1, 2])
  190. self._check_port('b', [1, 2, 3])
  191. self._check_sg(1, ['a', 'b'])
  192. self._check_sg(2, ['a', 'b'])
  193. self._check_sg(3, ['b'])
  194. def test_update_port_sg_removed(self):
  195. self._create_ports_and_sgroups()
  196. port_dict = {'security_groups': [1]}
  197. self.map.update_port(self.map.ports['b'], port_dict)
  198. self._check_port('a', [1, 2])
  199. self._check_port('b', [1])
  200. self._check_sg(1, ['a', 'b'])
  201. self._check_sg(2, ['a'])
  202. self._check_sg(3, [])
  203. def test_remove_port(self):
  204. self._create_ports_and_sgroups()
  205. self.map.remove_port(self.map.ports['a'])
  206. self._check_port('b', [2, 3])
  207. self._check_sg(1, [])
  208. self._check_sg(2, ['b'])
  209. self._check_sg(3, ['b'])
  210. self.assertNotIn('a', self.map.ports)
  211. def test_update_rules(self):
  212. """Just make sure it doesn't crash"""
  213. self.map.update_rules(1, [])
  214. def test_update_members(self):
  215. """Just make sure we doesn't crash"""
  216. self.map.update_members(1, [])
  217. class TestConjIdMap(base.BaseTestCase):
  218. def setUp(self):
  219. super(TestConjIdMap, self).setUp()
  220. self.conj_id_map = ovsfw.ConjIdMap()
  221. def test_get_conj_id(self):
  222. allocated = []
  223. for direction in [constants.EGRESS_DIRECTION,
  224. constants.INGRESS_DIRECTION]:
  225. id_ = self.conj_id_map.get_conj_id(
  226. 'sg', 'remote', direction, constants.IPv4)
  227. allocated.append(id_)
  228. self.assertEqual(len(set(allocated)), 2)
  229. self.assertEqual(len(self.conj_id_map.id_map), 2)
  230. self.assertEqual(self.conj_id_map.get_conj_id(
  231. 'sg', 'remote', constants.EGRESS_DIRECTION, constants.IPv4),
  232. allocated[0])
  233. def test_get_conj_id_invalid(self):
  234. self.assertRaises(ValueError, self.conj_id_map.get_conj_id,
  235. 'sg', 'remote', 'invalid-direction',
  236. constants.IPv6)
  237. def test_delete_sg(self):
  238. test_data = [('sg1', 'sg1'), ('sg1', 'sg2')]
  239. ids = []
  240. for sg_id, remote_sg_id in test_data:
  241. ids.append(self.conj_id_map.get_conj_id(
  242. sg_id, remote_sg_id,
  243. constants.INGRESS_DIRECTION, constants.IPv6))
  244. result = self.conj_id_map.delete_sg('sg1')
  245. self.assertIn(('sg1', ids[0]), result)
  246. self.assertIn(('sg2', ids[1]), result)
  247. self.assertFalse(self.conj_id_map.id_map)
  248. reallocated = self.conj_id_map.get_conj_id(
  249. 'sg-foo', 'sg-foo', constants.INGRESS_DIRECTION,
  250. constants.IPv6)
  251. self.assertIn(reallocated, ids)
  252. class TestConjIPFlowManager(base.BaseTestCase):
  253. def setUp(self):
  254. super(TestConjIPFlowManager, self).setUp()
  255. self.driver = mock.Mock()
  256. self.manager = ovsfw.ConjIPFlowManager(self.driver)
  257. self.vlan_tag = 100
  258. self.conj_id = 16
  259. def test_update_flows_for_vlan_no_ports(self):
  260. remote_group = self.driver.sg_port_map.get_sg.return_value
  261. remote_group.ports = {}
  262. with mock.patch.object(self.manager.conj_id_map,
  263. 'get_conj_id') as get_conj_id_mock:
  264. get_conj_id_mock.return_value = self.conj_id
  265. self.manager.add(self.vlan_tag, 'sg', 'remote_id',
  266. constants.INGRESS_DIRECTION, constants.IPv4, 0)
  267. self.manager.update_flows_for_vlan(self.vlan_tag)
  268. self.assertFalse(remote_group.get_ethertype_filtered_addresses.called)
  269. self.assertFalse(self.driver._add_flow.called)
  270. def test_update_flows_for_vlan(self):
  271. remote_group = self.driver.sg_port_map.get_sg.return_value
  272. remote_group.get_ethertype_filtered_addresses.return_value = [
  273. '10.22.3.4']
  274. with mock.patch.object(self.manager.conj_id_map,
  275. 'get_conj_id') as get_conj_id_mock:
  276. get_conj_id_mock.return_value = self.conj_id
  277. self.manager.add(self.vlan_tag, 'sg', 'remote_id',
  278. constants.INGRESS_DIRECTION, constants.IPv4, 0)
  279. self.manager.add(self.vlan_tag, 'sg', 'remote_id',
  280. constants.INGRESS_DIRECTION, constants.IPv4, 3)
  281. self.manager.update_flows_for_vlan(self.vlan_tag)
  282. self.assertEqual(self.driver._add_flow.call_args_list,
  283. [mock.call(actions='conjunction(16,1/2)', ct_state='+est-rel-rpl',
  284. dl_type=2048, nw_src='10.22.3.4/32', priority=70,
  285. reg_net=self.vlan_tag, table=82),
  286. mock.call(actions='conjunction(17,1/2)', ct_state='+new-est',
  287. dl_type=2048, nw_src='10.22.3.4/32', priority=70,
  288. reg_net=self.vlan_tag, table=82),
  289. mock.call(actions='conjunction(22,1/2)', ct_state='+est-rel-rpl',
  290. dl_type=2048, nw_src='10.22.3.4/32', priority=73,
  291. reg_net=self.vlan_tag, table=82),
  292. mock.call(actions='conjunction(23,1/2)', ct_state='+new-est',
  293. dl_type=2048, nw_src='10.22.3.4/32', priority=73,
  294. reg_net=self.vlan_tag, table=82)])
  295. def test_sg_removed(self):
  296. with mock.patch.object(self.manager.conj_id_map,
  297. 'get_conj_id') as get_id_mock, \
  298. mock.patch.object(self.manager.conj_id_map,
  299. 'delete_sg') as delete_sg_mock:
  300. get_id_mock.return_value = self.conj_id
  301. delete_sg_mock.return_value = [('remote_id', self.conj_id)]
  302. self.manager.add(self.vlan_tag, 'sg', 'remote_id',
  303. constants.INGRESS_DIRECTION, constants.IPv4, 0)
  304. self.manager.flow_state[self.vlan_tag][(
  305. constants.INGRESS_DIRECTION, constants.IPv4)] = {
  306. '10.22.3.4': [self.conj_id]}
  307. self.manager.sg_removed('sg')
  308. self.driver._add_flow.assert_not_called()
  309. self.driver.delete_flows_for_ip_addresses.assert_called_once_with(
  310. {'10.22.3.4'}, constants.INGRESS_DIRECTION, constants.IPv4,
  311. self.vlan_tag)
  312. class FakeOVSPort(object):
  313. def __init__(self, name, port, mac):
  314. self.port_name = name
  315. self.ofport = port
  316. self.vif_mac = mac
  317. class TestOVSFirewallDriver(base.BaseTestCase):
  318. def setUp(self):
  319. super(TestOVSFirewallDriver, self).setUp()
  320. mock_bridge = mock.patch.object(
  321. ovs_lib, 'OVSBridge', autospec=True).start()
  322. self.firewall = ovsfw.OVSFirewallDriver(mock_bridge)
  323. self.mock_bridge = self.firewall.int_br
  324. self.mock_bridge.reset_mock()
  325. self.fake_ovs_port = FakeOVSPort('port', 1, '00:00:00:00:00:00')
  326. self.mock_bridge.br.get_vif_port_by_id.return_value = \
  327. self.fake_ovs_port
  328. def _prepare_security_group(self):
  329. security_group_rules = [
  330. {'ethertype': constants.IPv4,
  331. 'protocol': constants.PROTO_NAME_TCP,
  332. 'direction': constants.INGRESS_DIRECTION,
  333. 'port_range_min': 123,
  334. 'port_range_max': 123}]
  335. self.firewall.update_security_group_rules(1, security_group_rules)
  336. security_group_rules = [
  337. {'ethertype': constants.IPv4,
  338. 'protocol': constants.PROTO_NAME_UDP,
  339. 'direction': constants.EGRESS_DIRECTION},
  340. {'ethertype': constants.IPv6,
  341. 'protocol': constants.PROTO_NAME_TCP,
  342. 'remote_group_id': 2,
  343. 'direction': constants.EGRESS_DIRECTION}]
  344. self.firewall.update_security_group_rules(2, security_group_rules)
  345. @property
  346. def port_ofport(self):
  347. return self.mock_bridge.br.get_vif_port_by_id.return_value.ofport
  348. @property
  349. def port_mac(self):
  350. return self.mock_bridge.br.get_vif_port_by_id.return_value.vif_mac
  351. def test_callbacks_registered(self):
  352. with mock.patch.object(callbacks_registry, "subscribe") as subscribe:
  353. firewall = ovsfw.OVSFirewallDriver(mock.MagicMock())
  354. subscribe.assert_called_once_with(
  355. firewall._init_firewall_callback,
  356. callbacks_resources.AGENT,
  357. callbacks_events.OVS_RESTARTED)
  358. def test_initialize_bridge(self):
  359. br = self.firewall.initialize_bridge(self.mock_bridge)
  360. self.assertEqual(br, self.mock_bridge.deferred.return_value)
  361. def test__add_flow_dl_type_formatted_to_string(self):
  362. dl_type = 0x0800
  363. self.firewall._add_flow(dl_type=dl_type)
  364. def test__add_flow_registers_are_replaced(self):
  365. self.firewall._add_flow(in_port=1, reg_port=1, reg_net=2)
  366. expected_calls = {'in_port': 1,
  367. 'reg{:d}'.format(ovsfw_consts.REG_PORT): 1,
  368. 'reg{:d}'.format(ovsfw_consts.REG_NET): 2}
  369. self.mock_bridge.br.add_flow.assert_called_once_with(
  370. **expected_calls)
  371. def test__drop_all_unmatched_flows(self):
  372. self.firewall._drop_all_unmatched_flows()
  373. expected_calls = [
  374. mock.call(actions='drop', priority=0,
  375. table=ovs_consts.BASE_EGRESS_TABLE),
  376. mock.call(actions='drop', priority=0,
  377. table=ovs_consts.RULES_EGRESS_TABLE),
  378. mock.call(actions='drop', priority=0,
  379. table=ovs_consts.ACCEPT_OR_INGRESS_TABLE),
  380. mock.call(actions='drop', priority=0,
  381. table=ovs_consts.BASE_INGRESS_TABLE),
  382. mock.call(actions='drop', priority=0,
  383. table=ovs_consts.RULES_INGRESS_TABLE)]
  384. actual_calls = self.firewall.int_br.br.add_flow.call_args_list
  385. self.assertEqual(expected_calls, actual_calls)
  386. def test_get_or_create_ofport_non_existing(self):
  387. port_dict = {
  388. 'device': 'port-id',
  389. 'security_groups': [123, 456]}
  390. port = self.firewall.get_or_create_ofport(port_dict)
  391. sg1, sg2 = sorted(
  392. self.firewall.sg_port_map.sec_groups.values(),
  393. key=lambda x: x.id)
  394. self.assertIn(port, self.firewall.sg_port_map.ports.values())
  395. self.assertEqual(
  396. sorted(port.sec_groups, key=lambda x: x.id), [sg1, sg2])
  397. self.assertIn(port, sg1.ports)
  398. self.assertIn(port, sg2.ports)
  399. def test_get_or_create_ofport_existing(self):
  400. port_dict = {
  401. 'device': 'port-id',
  402. 'security_groups': [123, 456]}
  403. of_port = create_ofport(port_dict)
  404. self.firewall.sg_port_map.ports[of_port.id] = of_port
  405. port = self.firewall.get_or_create_ofport(port_dict)
  406. sg1, sg2 = sorted(
  407. self.firewall.sg_port_map.sec_groups.values(),
  408. key=lambda x: x.id)
  409. self.assertIs(of_port, port)
  410. self.assertIn(port, self.firewall.sg_port_map.ports.values())
  411. self.assertEqual(
  412. sorted(port.sec_groups, key=lambda x: x.id), [sg1, sg2])
  413. self.assertIn(port, sg1.ports)
  414. self.assertIn(port, sg2.ports)
  415. def test_get_or_create_ofport_changed(self):
  416. port_dict = {
  417. 'device': 'port-id',
  418. 'security_groups': [123, 456]}
  419. of_port = create_ofport(port_dict)
  420. self.firewall.sg_port_map.ports[of_port.id] = of_port
  421. fake_ovs_port = FakeOVSPort('port', 2, '00:00:00:00:00:00')
  422. self.mock_bridge.br.get_vif_port_by_id.return_value = \
  423. fake_ovs_port
  424. port = self.firewall.get_or_create_ofport(port_dict)
  425. self.assertIn(of_port.id, self.firewall.sg_port_map.ports.keys())
  426. self.assertEqual(port.ofport, 2)
  427. def test_get_or_create_ofport_missing(self):
  428. port_dict = {
  429. 'device': 'port-id',
  430. 'security_groups': [123, 456]}
  431. self.mock_bridge.br.get_vif_port_by_id.return_value = None
  432. with testtools.ExpectedException(exceptions.OVSFWPortNotFound):
  433. self.firewall.get_or_create_ofport(port_dict)
  434. def test_get_or_create_ofport_missing_nocreate(self):
  435. port_dict = {
  436. 'device': 'port-id',
  437. 'security_groups': [123, 456]}
  438. self.mock_bridge.br.get_vif_port_by_id.return_value = None
  439. self.assertIsNone(self.firewall.get_ofport(port_dict))
  440. self.assertFalse(self.mock_bridge.br.get_vif_port_by_id.called)
  441. def test_is_port_managed_managed_port(self):
  442. port_dict = {'device': 'port-id'}
  443. self.firewall.sg_port_map.ports[port_dict['device']] = object()
  444. is_managed = self.firewall.is_port_managed(port_dict)
  445. self.assertTrue(is_managed)
  446. def test_is_port_managed_not_managed_port(self):
  447. port_dict = {'device': 'port-id'}
  448. is_managed = self.firewall.is_port_managed(port_dict)
  449. self.assertFalse(is_managed)
  450. def test_prepare_port_filter(self):
  451. port_dict = {'device': 'port-id',
  452. 'security_groups': [1],
  453. 'fixed_ips': ["10.0.0.1"]}
  454. self._prepare_security_group()
  455. self.firewall.prepare_port_filter(port_dict)
  456. exp_egress_classifier = mock.call(
  457. actions='set_field:{:d}->reg5,set_field:{:d}->reg6,'
  458. 'resubmit(,{:d})'.format(
  459. self.port_ofport, TESTING_VLAN_TAG,
  460. ovs_consts.BASE_EGRESS_TABLE),
  461. in_port=self.port_ofport,
  462. priority=100,
  463. table=ovs_consts.TRANSIENT_TABLE)
  464. exp_ingress_classifier = mock.call(
  465. actions='set_field:{:d}->reg5,set_field:{:d}->reg6,'
  466. 'strip_vlan,resubmit(,{:d})'.format(
  467. self.port_ofport, TESTING_VLAN_TAG,
  468. ovs_consts.BASE_INGRESS_TABLE),
  469. dl_dst=self.port_mac,
  470. dl_vlan='0x%x' % TESTING_VLAN_TAG,
  471. priority=90,
  472. table=ovs_consts.TRANSIENT_TABLE)
  473. filter_rule = mock.call(
  474. actions='ct(commit,zone=NXM_NX_REG6[0..15]),'
  475. 'output:{:d},resubmit(,{:d})'.format(
  476. self.port_ofport,
  477. ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE),
  478. dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP),
  479. nw_proto=constants.PROTO_NUM_TCP,
  480. priority=77,
  481. reg5=self.port_ofport,
  482. ct_state=ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED,
  483. table=ovs_consts.RULES_INGRESS_TABLE,
  484. tcp_dst='0x007b')
  485. calls = self.mock_bridge.br.add_flow.call_args_list
  486. for call in exp_ingress_classifier, exp_egress_classifier, filter_rule:
  487. self.assertIn(call, calls)
  488. def test_prepare_port_filter_port_security_disabled(self):
  489. port_dict = {'device': 'port-id',
  490. 'security_groups': [1],
  491. 'port_security_enabled': False}
  492. self._prepare_security_group()
  493. with mock.patch.object(
  494. self.firewall, 'initialize_port_flows') as m_init_flows:
  495. self.firewall.prepare_port_filter(port_dict)
  496. self.assertFalse(m_init_flows.called)
  497. def test_prepare_port_filter_initialized_port(self):
  498. port_dict = {'device': 'port-id',
  499. 'security_groups': [1]}
  500. self._prepare_security_group()
  501. self.firewall.prepare_port_filter(port_dict)
  502. self.assertFalse(self.mock_bridge.br.delete_flows.called)
  503. self.firewall.prepare_port_filter(port_dict)
  504. self.assertTrue(self.mock_bridge.br.delete_flows.called)
  505. def test_update_port_filter(self):
  506. port_dict = {'device': 'port-id',
  507. 'security_groups': [1]}
  508. self._prepare_security_group()
  509. self.firewall.prepare_port_filter(port_dict)
  510. port_dict['security_groups'] = [2]
  511. self.mock_bridge.reset_mock()
  512. self.firewall.update_port_filter(port_dict)
  513. self.assertTrue(self.mock_bridge.br.delete_flows.called)
  514. conj_id = self.firewall.conj_ip_manager.conj_id_map.get_conj_id(
  515. 2, 2, constants.EGRESS_DIRECTION, constants.IPv6)
  516. filter_rules = [mock.call(
  517. actions='resubmit(,{:d})'.format(
  518. ovs_consts.ACCEPT_OR_INGRESS_TABLE),
  519. dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP),
  520. nw_proto=constants.PROTO_NUM_UDP,
  521. priority=77,
  522. ct_state=ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED,
  523. reg5=self.port_ofport,
  524. table=ovs_consts.RULES_EGRESS_TABLE),
  525. mock.call(
  526. actions='conjunction({:d},2/2)'.format(conj_id + 6),
  527. ct_state=ovsfw_consts.OF_STATE_ESTABLISHED_NOT_REPLY,
  528. dl_type=mock.ANY,
  529. nw_proto=6,
  530. priority=73, reg5=self.port_ofport,
  531. table=ovs_consts.RULES_EGRESS_TABLE)]
  532. self.mock_bridge.br.add_flow.assert_has_calls(
  533. filter_rules, any_order=True)
  534. def test_update_port_filter_create_new_port_if_not_present(self):
  535. port_dict = {'device': 'port-id',
  536. 'security_groups': [1]}
  537. self._prepare_security_group()
  538. with mock.patch.object(
  539. self.firewall, 'prepare_port_filter'
  540. ) as prepare_mock, mock.patch.object(
  541. self.firewall, 'initialize_port_flows'
  542. ) as initialize_port_flows_mock, mock.patch.object(
  543. self.firewall, 'add_flows_from_rules'
  544. ) as add_flows_from_rules_mock:
  545. self.firewall.update_port_filter(port_dict)
  546. self.assertFalse(prepare_mock.called)
  547. self.assertFalse(self.mock_bridge.br.delete_flows.called)
  548. self.assertTrue(initialize_port_flows_mock.called)
  549. self.assertTrue(add_flows_from_rules_mock.called)
  550. def test_update_port_filter_port_security_disabled(self):
  551. port_dict = {'device': 'port-id',
  552. 'security_groups': [1]}
  553. self._prepare_security_group()
  554. self.firewall.prepare_port_filter(port_dict)
  555. port_dict['port_security_enabled'] = False
  556. self.firewall.update_port_filter(port_dict)
  557. self.assertTrue(self.mock_bridge.br.delete_flows.called)
  558. def test_update_port_filter_applies_added_flows(self):
  559. """Check flows are applied right after _set_flows is called."""
  560. port_dict = {'device': 'port-id',
  561. 'security_groups': [1]}
  562. self._prepare_security_group()
  563. self.firewall.prepare_port_filter(port_dict)
  564. with self.firewall.defer_apply():
  565. self.firewall.update_port_filter(port_dict)
  566. self.assertEqual(2, self.mock_bridge.apply_flows.call_count)
  567. def test_update_port_filter_clean_when_port_not_found(self):
  568. """Check flows are cleaned if port is not found in the bridge."""
  569. port_dict = {'device': 'port-id',
  570. 'security_groups': [1]}
  571. self._prepare_security_group()
  572. self.firewall.prepare_port_filter(port_dict)
  573. self.mock_bridge.br.get_vif_port_by_id.return_value = None
  574. self.firewall.update_port_filter(port_dict)
  575. self.assertTrue(self.mock_bridge.br.delete_flows.called)
  576. def test_remove_port_filter(self):
  577. port_dict = {'device': 'port-id',
  578. 'security_groups': [1]}
  579. self._prepare_security_group()
  580. self.firewall.prepare_port_filter(port_dict)
  581. self.firewall.remove_port_filter(port_dict)
  582. self.assertTrue(self.mock_bridge.br.delete_flows.called)
  583. self.assertIn(1, self.firewall.sg_to_delete)
  584. def test_remove_port_filter_port_security_disabled(self):
  585. port_dict = {'device': 'port-id',
  586. 'security_groups': [1]}
  587. self.firewall.remove_port_filter(port_dict)
  588. self.assertFalse(self.mock_bridge.br.delete_flows.called)
  589. def test_update_security_group_rules(self):
  590. """Just make sure it doesn't crash"""
  591. new_rules = [
  592. {'ethertype': constants.IPv4,
  593. 'direction': constants.INGRESS_DIRECTION,
  594. 'protocol': constants.PROTO_NAME_ICMP},
  595. {'ethertype': constants.IPv4,
  596. 'direction': constants.EGRESS_DIRECTION,
  597. 'remote_group_id': 2}]
  598. self.firewall.update_security_group_rules(1, new_rules)
  599. def test_update_security_group_members(self):
  600. """Just make sure it doesn't crash"""
  601. new_members = {constants.IPv4: [1, 2, 3, 4]}
  602. self.firewall.update_security_group_members(2, new_members)
  603. def test__cleanup_stale_sg(self):
  604. self._prepare_security_group()
  605. self.firewall.sg_to_delete = {1}
  606. with mock.patch.object(self.firewall.conj_ip_manager,
  607. 'sg_removed') as sg_removed_mock,\
  608. mock.patch.object(self.firewall.sg_port_map,
  609. 'delete_sg') as delete_sg_mock:
  610. self.firewall._cleanup_stale_sg()
  611. sg_removed_mock.assert_called_once_with(1)
  612. delete_sg_mock.assert_called_once_with(1)
  613. def test_get_ovs_port(self):
  614. ovs_port = self.firewall.get_ovs_port('port_id')
  615. self.assertEqual(self.fake_ovs_port, ovs_port)
  616. def test_get_ovs_port_non_existent(self):
  617. self.mock_bridge.br.get_vif_port_by_id.return_value = None
  618. with testtools.ExpectedException(exceptions.OVSFWPortNotFound):
  619. self.firewall.get_ovs_port('port_id')
  620. def test__initialize_egress_no_port_security_sends_to_egress(self):
  621. self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG}
  622. self.firewall._initialize_egress_no_port_security('port_id')
  623. expected_call = mock.call(
  624. table=ovs_consts.TRANSIENT_TABLE,
  625. priority=100,
  626. in_port=self.fake_ovs_port.ofport,
  627. actions='set_field:%d->reg%d,'
  628. 'set_field:%d->reg%d,'
  629. 'resubmit(,%d)' % (
  630. self.fake_ovs_port.ofport,
  631. ovsfw_consts.REG_PORT,
  632. TESTING_VLAN_TAG,
  633. ovsfw_consts.REG_NET,
  634. ovs_consts.ACCEPT_OR_INGRESS_TABLE)
  635. )
  636. calls = self.mock_bridge.br.add_flow.call_args_list
  637. self.assertIn(expected_call, calls)
  638. def test__initialize_egress_no_port_security_no_tag(self):
  639. self.mock_bridge.br.db_get_val.return_value = {}
  640. self.firewall._initialize_egress_no_port_security('port_id')
  641. self.assertFalse(self.mock_bridge.br.add_flow.called)
  642. def test__remove_egress_no_port_security_deletes_flow(self):
  643. self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG}
  644. self.firewall.sg_port_map.unfiltered['port_id'] = 1
  645. self.firewall._remove_egress_no_port_security('port_id')
  646. expected_call = mock.call(
  647. table=ovs_consts.TRANSIENT_TABLE,
  648. in_port=self.fake_ovs_port.ofport,
  649. )
  650. calls = self.mock_bridge.br.delete_flows.call_args_list
  651. self.assertIn(expected_call, calls)
  652. def test__remove_egress_no_port_security_non_existing_port(self):
  653. with testtools.ExpectedException(exceptions.OVSFWPortNotHandled):
  654. self.firewall._remove_egress_no_port_security('foo')
  655. def test_process_trusted_ports_caches_port_id(self):
  656. vif_port = ovs_lib.VifPort('name', 1, 'id', 'mac', mock.ANY)
  657. with mock.patch.object(self.firewall.int_br.br, 'get_vifs_by_ids',
  658. return_value={'port_id': vif_port}):
  659. self.firewall.process_trusted_ports(['port_id'])
  660. self.assertEqual(1, len(self.firewall.sg_port_map.unfiltered))
  661. self.assertEqual(vif_port.ofport,
  662. self.firewall.sg_port_map.unfiltered['port_id'])
  663. def test_process_trusted_ports_port_not_found(self):
  664. """Check that exception is not propagated outside."""
  665. with mock.patch.object(self.firewall.int_br.br, 'get_vifs_by_ids',
  666. return_value={}):
  667. self.firewall.process_trusted_ports(['port_id'])
  668. # Processing should have failed so port is not cached
  669. self.assertEqual(0, len(self.firewall.sg_port_map.unfiltered))
  670. def test_remove_trusted_ports_clears_cached_port_id(self):
  671. self.firewall.sg_port_map.unfiltered['port_id'] = 1
  672. self.firewall.remove_trusted_ports(['port_id'])
  673. self.assertNotIn('port_id', self.firewall.sg_port_map.unfiltered)
  674. def test_remove_trusted_ports_not_managed_port(self):
  675. """Check that exception is not propagated outside."""
  676. self.firewall.remove_trusted_ports(['port_id'])
  677. class TestCookieContext(base.BaseTestCase):
  678. def setUp(self):
  679. super(TestCookieContext, self).setUp()
  680. # Don't attempt to connect to ovsdb
  681. mock.patch('neutron.agent.ovsdb.impl_idl.api_factory').start()
  682. # Don't trigger iptables -> ovsfw migration
  683. mock.patch(
  684. 'neutron.agent.linux.openvswitch_firewall.iptables.Helper').start()
  685. self.execute = mock.patch.object(
  686. utils, "execute", spec=utils.execute).start()
  687. bridge = ovs_bridge.OVSAgentBridge('foo')
  688. mock.patch.object(
  689. ovsfw.OVSFirewallDriver, 'initialize_bridge',
  690. return_value=bridge.deferred(
  691. full_ordered=True, use_bundle=True)).start()
  692. self.firewall = ovsfw.OVSFirewallDriver(bridge)
  693. # Remove calls from firewall initialization
  694. self.execute.reset_mock()
  695. def test_cookie_is_different_in_context(self):
  696. default_cookie = self.firewall.int_br.br.default_cookie
  697. with self.firewall.update_cookie_context():
  698. self.firewall._add_flow(actions='drop')
  699. update_cookie = self.firewall._update_cookie
  700. self.firewall._add_flow(actions='drop')
  701. expected_calls = [
  702. mock.call(
  703. mock.ANY,
  704. process_input='hard_timeout=0,idle_timeout=0,priority=1,'
  705. 'cookie=%d,actions=drop' % cookie,
  706. run_as_root=mock.ANY,
  707. ) for cookie in (update_cookie, default_cookie)
  708. ]
  709. self.execute.assert_has_calls(expected_calls)
  710. def test_context_cookie_is_not_left_as_used(self):
  711. with self.firewall.update_cookie_context():
  712. update_cookie = self.firewall._update_cookie
  713. self.assertNotIn(
  714. update_cookie,
  715. self.firewall.int_br.br._reserved_cookies)