diff --git a/dragonflow/controller/apps/dhcp.py b/dragonflow/controller/apps/dhcp.py index 2a5ae5240..a08ebe3f7 100644 --- a/dragonflow/controller/apps/dhcp.py +++ b/dragonflow/controller/apps/dhcp.py @@ -65,8 +65,22 @@ class DHCPApp(df_base_app.DFlowApp): self.packet_in_handler) self._dhcp_ip_by_subnet = {} + def _get_dhcp_port_by_network(self, network_unique_key): + + lswitch = self.db_store.get_one(l2.LogicalSwitch( + unique_key=network_unique_key), + index=l2.LogicalSwitch.get_index('unique_key')) + + return self.db_store.get_one( + l2.LogicalPort( + device_owner=n_const.DEVICE_OWNER_DHCP, + lswitch=lswitch + ), + index=l2.LogicalPort.get_index('switch,owner') + ) + def switch_features_handler(self, ev): - self._install_dhcp_broadcast_match_flow() + self._install_dhcp_packet_match_flow() self.add_flow_go_to_table(const.DHCP_TABLE, const.PRIORITY_DEFAULT, const.L2_LOOKUP_TABLE) @@ -94,6 +108,12 @@ class DHCPApp(df_base_app.DFlowApp): index=l2.LogicalPort.get_index('unique_key'), ) + network_key = msg.match.get('metadata') + dhcp_lport = self._get_dhcp_port_by_network(network_key) + if not dhcp_lport: + LOG.error("No DHCP port for network {}".format(str(network_key))) + return + if self._check_port_limit(lport): self._block_port_dhcp_traffic(unique_key, lport) LOG.warning("pass rate limit for %(port_id)s blocking DHCP " @@ -106,11 +126,11 @@ class DHCPApp(df_base_app.DFlowApp): LOG.error("Port %s no longer found.", lport.id) return try: - self._handle_dhcp_request(pkt, lport) + self._handle_dhcp_request(pkt, lport, dhcp_lport) except Exception: LOG.exception("Unable to handle packet %s", msg) - def _handle_dhcp_request(self, packet, lport): + def _handle_dhcp_request(self, packet, lport, dhcp_port): dhcp_packet = packet.get_protocol(dhcp.dhcp) dhcp_message_type = self._get_dhcp_message_type_opt(dhcp_packet) send_packet = None @@ -119,7 +139,8 @@ class DHCPApp(df_base_app.DFlowApp): packet, dhcp_packet, dhcp.DHCP_OFFER, - lport) + lport, + dhcp_port) LOG.info("sending DHCP offer for port IP %(port_ip)s " "port id %(port_id)s", {'port_ip': lport.ip, 'port_id': lport.id}) @@ -128,7 +149,8 @@ class DHCPApp(df_base_app.DFlowApp): packet, dhcp_packet, dhcp.DHCP_ACK, - lport) + lport, + dhcp_port) LOG.info("sending DHCP ACK for port IP %(port_ip)s " "port id %(tunnel_id)s", {'port_ip': lport.ip, @@ -141,7 +163,7 @@ class DHCPApp(df_base_app.DFlowApp): self.dispatch_packet(send_packet, unique_key) def _create_dhcp_response(self, packet, dhcp_request, - response_type, lport): + response_type, lport, dhcp_port): pkt_ipv4 = packet.get_protocol(ipv4.ipv4) pkt_ethernet = packet.get_protocol(ethernet.ethernet) @@ -169,7 +191,7 @@ class DHCPApp(df_base_app.DFlowApp): dhcp_response.add_protocol(ethernet.ethernet( ethertype=ether.ETH_TYPE_IP, dst=pkt_ethernet.src, - src=pkt_ethernet.dst)) + src=dhcp_port.mac)) dhcp_response.add_protocol(ipv4.ipv4(dst=pkt_ipv4.src, src=dhcp_server_address, proto=pkt_ipv4.proto)) @@ -375,11 +397,10 @@ class DHCPApp(df_base_app.DFlowApp): table_id=const.DHCP_TABLE, match=match) - def _install_dhcp_broadcast_match_flow(self): + def _install_dhcp_packet_match_flow(self): parser = self.parser match = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP, - eth_dst=const.BROADCAST_MAC, ip_proto=n_const.PROTO_NUM_UDP, udp_src=const.DHCP_CLIENT_PORT, udp_dst=const.DHCP_SERVER_PORT) diff --git a/dragonflow/controller/apps/portsec.py b/dragonflow/controller/apps/portsec.py index 8c5808ff0..df10a9fb3 100644 --- a/dragonflow/controller/apps/portsec.py +++ b/dragonflow/controller/apps/portsec.py @@ -179,7 +179,6 @@ class PortSecApp(df_base_app.DFlowApp): # DHCP packets with the vm mac pass match = parser.OFPMatch(reg6=unique_key, eth_src=vm_mac, - eth_dst=const.BROADCAST_MAC, eth_type=ether.ETH_TYPE_IP, ip_proto=n_const.PROTO_NUM_UDP, udp_src=const.DHCP_CLIENT_PORT, @@ -225,7 +224,6 @@ class PortSecApp(df_base_app.DFlowApp): # Remove DHCP packets with the vm mac pass match = parser.OFPMatch(reg6=unique_key, eth_src=vm_mac, - eth_dst=const.BROADCAST_MAC, eth_type=ether.ETH_TYPE_IP, ip_proto=n_const.PROTO_NUM_UDP, udp_src=const.DHCP_CLIENT_PORT, diff --git a/dragonflow/db/models/l2.py b/dragonflow/db/models/l2.py index b8d456595..f69f82390 100644 --- a/dragonflow/db/models/l2.py +++ b/dragonflow/db/models/l2.py @@ -138,6 +138,7 @@ EVENT_UNBIND_REMOTE = 'unbind_remote' 'chassis_id': 'binding.chassis.id', 'lswitch_id': 'lswitch.id', 'ip,lswitch': ('ips', 'lswitch.id'), + 'switch,owner': ('lswitch.unique_key', 'device_owner') }) class LogicalPort(mf.ModelBase, mixins.Name, mixins.Version, mixins.Topic, mixins.UniqueKey, mixins.BasicEvents): diff --git a/dragonflow/tests/fullstack/test_dhcp_flows.py b/dragonflow/tests/fullstack/test_dhcp_flows.py index 9f65096f6..347b97327 100644 --- a/dragonflow/tests/fullstack/test_dhcp_flows.py +++ b/dragonflow/tests/fullstack/test_dhcp_flows.py @@ -33,7 +33,7 @@ class TestOVSFlowsForDHCP(test_base.DFTestBase): return ip['ip_address'] return None - def test_broadcast_dhcp_rule(self): + def test_dhcp_packet_rule(self): found_dhcp_cast_flow = False ovs = utils.OvsFlowsParser() flows = ovs.dump(self.integration_bridge) @@ -43,7 +43,7 @@ class TestOVSFlowsForDHCP(test_base.DFTestBase): for flow in flows: if (flow['table'] == str(constants.SERVICES_CLASSIFICATION_TABLE) and flow['actions'] == goto_dhcp): - if ('udp,dl_dst=' + constants.BROADCAST_MAC + dhcp_ports + if ('udp' + dhcp_ports in flow['match']): found_dhcp_cast_flow = True break diff --git a/dragonflow/tests/fullstack/test_portsec_flows.py b/dragonflow/tests/fullstack/test_portsec_flows.py index 3a5dbf1b1..fa10bb770 100644 --- a/dragonflow/tests/fullstack/test_portsec_flows.py +++ b/dragonflow/tests/fullstack/test_portsec_flows.py @@ -71,10 +71,9 @@ class TestOVSFlowsForPortSecurity(test_base.DFTestBase): # priority: High, match: reg6=unique_key, dl_src=$vm_mac, # dl_dst=ff:ff:ff:ff:ff:ff, udp, tp_src=68, tp_dst=67, # actions: goto const.EGRESS_CONNTRACK_TABLE - dl_dst_match = "dl_dst=" + const.BROADCAST_MAC expected_flow_list.append({ "priority": str(const.PRIORITY_HIGH), - "match_list": [unique_key_match, dl_src_match, dl_dst_match, + "match_list": [unique_key_match, dl_src_match, "udp", "tp_src=" + str(const.DHCP_CLIENT_PORT), "tp_dst=" + str(const.DHCP_SERVER_PORT)], "actions": goto_conntrack_table_action diff --git a/dragonflow/tests/unit/test_dhcp_app.py b/dragonflow/tests/unit/test_dhcp_app.py index cb46ecdfa..921a4efe6 100644 --- a/dragonflow/tests/unit/test_dhcp_app.py +++ b/dragonflow/tests/unit/test_dhcp_app.py @@ -96,14 +96,14 @@ class TestDHCPApp(test_app_base.DFAppTestBase): lswitch=test_app_base.fake_logic_switch1, subnets=test_app_base.fake_lswitch_default_subnets, ips=('10.0.0.2',), + macs=("11:22:33:44:55:66",), device_owner=n_const.DEVICE_OWNER_DHCP ) return fake_dhcp_port - def _build_dhcp_test_fake_lport(self, dhcp_params=None): + def _build_dhcp_test_fake_lport(self, dhcp_port, dhcp_params=None): - dhcp_port = self._create_dhcp_port() self.app._lport_created(dhcp_port) fake_loprt = test_app_base.make_fake_local_port( @@ -115,7 +115,7 @@ class TestDHCPApp(test_app_base.DFAppTestBase): return fake_loprt - def _send_dhcp_req_to_app(self, lport, options=None): + def _send_dhcp_req_to_app(self, lport, dhcp_port, options=None): req = dhcp.dhcp(op=dhcp.DHCP_DISCOVER, chaddr='aa:aa:aa:aa:aa:aa', options=dhcp.options(options)) @@ -123,7 +123,8 @@ class TestDHCPApp(test_app_base.DFAppTestBase): dhcp_response_pkt = self.app._create_dhcp_response(pkt, req, dhcp.DHCP_OFFER, - lport) + lport, + dhcp_port) return dhcp_response_pkt @@ -135,18 +136,20 @@ class TestDHCPApp(test_app_base.DFAppTestBase): return pkt def test_dhcp_repsonse(self): - - fake_loprt = self._build_dhcp_test_fake_lport() - dhcp_response_pkt = self._send_dhcp_req_to_app(fake_loprt) + dhcp_port = self._create_dhcp_port() + fake_loprt = self._build_dhcp_test_fake_lport(dhcp_port) + dhcp_response_pkt = self._send_dhcp_req_to_app(fake_loprt, dhcp_port) self.assertTrue(dhcp_response_pkt) dhcp_response = dhcp_response_pkt.get_protocol(dhcp.dhcp) self.assertEqual('10.0.0.1', str(dhcp_response.yiaddr)) + dhcp_eth = dhcp_response_pkt.get_protocol(ethernet.ethernet) + self.assertEqual("11:22:33:44:55:66", str(dhcp_eth.src)) def _create_dhcp_reponse(self, dhcp_opts, requested): - + dhcp_port = self._create_dhcp_port() dhcp_params = {"opts": {} if not dhcp_opts else dhcp_opts} - fake_lport = self._build_dhcp_test_fake_lport(dhcp_params) + fake_lport = self._build_dhcp_test_fake_lport(dhcp_port, dhcp_params) requested_option_connected = ''.join([chr(x) for x in requested]) option_list = [dhcp.option(dhcp.DHCP_PARAMETER_REQUEST_LIST_OPT, @@ -155,6 +158,7 @@ class TestDHCPApp(test_app_base.DFAppTestBase): ] dhcp_response_pkt = self._send_dhcp_req_to_app(fake_lport, + dhcp_port, option_list) dhcp_res = dhcp_response_pkt.get_protocol(dhcp.dhcp)