Response to dhcp packets with lport MAC

(instead of broadcast)

Change-Id: I0e56fc0658c6135111b588d78d63b57fc2dc6b0e
Closes-Bug:1703168
This commit is contained in:
Eyal Leshem 2017-10-29 16:53:29 +02:00
parent ed24870dae
commit 62f243e4da
6 changed files with 47 additions and 24 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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)