Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions ansible/roles/test/files/ptftests/dhcp_relay_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ def setUp(self):
self.client_port_index = int(self.test_params['client_port_index'])
self.client_mac = self.dataplane.get_mac(0, self.client_port_index)

self.switch_loopback_ip = self.test_params['switch_loopback_ip']

# 'dual' for dual tor testing
# 'single' for regular single tor testing
self.dual_tor = (self.test_params['testing_mode'] == 'dual')

# option82 is a byte string created by the relay agent. It contains the circuit_id and remote_id fields.
# circuit_id is stored as suboption 1 of option 82.
# It consists of the following:
Expand All @@ -139,6 +145,12 @@ def setUp(self):
self.option82 += struct.pack('BB', 2, len(remote_id_string))
self.option82 += remote_id_string

# In 'dual' testing mode, vlan ip is stored as suboption 5 of option 82.
if self.dual_tor:
link_selection = ''.join([chr(int(byte)) for byte in self.relay_iface_ip.split('.')])
self.option82 += struct.pack('BB', 5, 4)
self.option82 += link_selection

# We'll assign our client the IP address 1 greater than our relay interface (i.e., gateway) IP
self.client_ip = incrementIpAddress(self.relay_iface_ip, 1)
self.client_subnet = self.test_params['relay_iface_netmask']
Expand Down Expand Up @@ -195,7 +207,7 @@ def create_dhcp_discover_relayed_packet(self):
ciaddr=self.DEFAULT_ROUTE_IP,
yiaddr=self.DEFAULT_ROUTE_IP,
siaddr=self.DEFAULT_ROUTE_IP,
giaddr=self.relay_iface_ip,
giaddr=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
chaddr=my_chaddr)
bootp /= scapy.DHCP(options=[('message-type', 'discover'),
('relay_agent_Information', self.option82),
Expand All @@ -214,10 +226,10 @@ def create_dhcp_offer_packet(self):
eth_dst=self.relay_iface_mac,
eth_client=self.client_mac,
ip_server=self.server_ip,
ip_dst=self.relay_iface_ip,
ip_dst=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
ip_offered=self.client_ip,
port_dst=self.DHCP_SERVER_PORT,
ip_gateway=self.relay_iface_ip,
ip_gateway=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
netmask_client=self.client_subnet,
dhcp_lease=self.LEASE_TIME,
padding_bytes=0,
Expand Down Expand Up @@ -246,7 +258,7 @@ def create_dhcp_offer_relayed_packet(self):
ciaddr=self.DEFAULT_ROUTE_IP,
yiaddr=self.client_ip,
siaddr=self.server_ip,
giaddr=self.relay_iface_ip,
giaddr=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
chaddr=my_chaddr)
bootp /= scapy.DHCP(options=[('message-type', 'offer'),
('server_id', self.server_ip),
Expand Down Expand Up @@ -300,7 +312,7 @@ def create_dhcp_request_relayed_packet(self):
ciaddr=self.DEFAULT_ROUTE_IP,
yiaddr=self.DEFAULT_ROUTE_IP,
siaddr=self.DEFAULT_ROUTE_IP,
giaddr=self.relay_iface_ip,
giaddr=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
chaddr=my_chaddr)
bootp /= scapy.DHCP(options=[('message-type', 'request'),
('requested_addr', self.client_ip),
Expand All @@ -321,10 +333,10 @@ def create_dhcp_ack_packet(self):
eth_dst=self.relay_iface_mac,
eth_client=self.client_mac,
ip_server=self.server_ip,
ip_dst=self.relay_iface_ip,
ip_dst=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
ip_offered=self.client_ip,
port_dst=self.DHCP_SERVER_PORT,
ip_gateway=self.relay_iface_ip,
ip_gateway=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
netmask_client=self.client_subnet,
dhcp_lease=self.LEASE_TIME,
padding_bytes=0,
Expand Down Expand Up @@ -353,7 +365,7 @@ def create_dhcp_ack_relayed_packet(self):
ciaddr=self.DEFAULT_ROUTE_IP,
yiaddr=self.client_ip,
siaddr=self.server_ip,
giaddr=self.relay_iface_ip,
giaddr=self.relay_iface_ip if not self.dual_tor else self.switch_loopback_ip,
chaddr=my_chaddr)
bootp /= scapy.DHCP(options=[('message-type', 'ack'),
('server_id', self.server_ip),
Expand Down
91 changes: 81 additions & 10 deletions tests/dhcp_relay/test_dhcp_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
BROADCAST_MAC = 'ff:ff:ff:ff:ff:ff'
DEFAULT_DHCP_CLIENT_PORT = 68


@pytest.fixture(autouse=True)
def ignore_expected_loganalyzer_exceptions(rand_one_dut_hostname, loganalyzer):
"""Ignore expected failures logs during test execution."""
Expand Down Expand Up @@ -42,6 +43,8 @@ def dut_dhcp_relay_data(duthosts, rand_one_dut_hostname, ptfhost, tbinfo):
mg_facts = duthost.get_extended_minigraph_facts(tbinfo)
host_facts = duthost.setup()['ansible_facts']

switch_loopback_ip = mg_facts['minigraph_lo_interfaces'][0]['addr']

# SONiC spawns one DHCP relay agent per VLAN interface configured on the DUT
vlan_dict = mg_facts['minigraph_vlans']
for vlan_iface_name, vlan_info_dict in vlan_dict.items():
Expand Down Expand Up @@ -94,10 +97,13 @@ def dut_dhcp_relay_data(duthosts, rand_one_dut_hostname, ptfhost, tbinfo):
dhcp_relay_data['client_iface'] = client_iface
dhcp_relay_data['uplink_interfaces'] = uplink_interfaces
dhcp_relay_data['uplink_port_indices'] = uplink_port_indices
dhcp_relay_data['switch_loopback_ip'] = str(switch_loopback_ip)

dhcp_relay_data_list.append(dhcp_relay_data)

return dhcp_relay_data_list


@pytest.fixture(scope="module")
def validate_dut_routes_exist(duthosts, rand_one_dut_hostname, dut_dhcp_relay_data):
"""Fixture to valid a route to each DHCP server exist
Expand All @@ -112,12 +118,31 @@ def validate_dut_routes_exist(duthosts, rand_one_dut_hostname, dut_dhcp_relay_da
assert len(rtInfo["nexthops"]) > 0, "Failed to find route to DHCP server '{0}'".format(dhcp_server)


def test_dhcp_relay_default(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist):
def set_dual_tor(duthost):
duthost.shell('redis-cli -n 4 HSET "DEVICE_METADATA|localhost" "subtype" "DualToR"')
duthost.shell('systemctl restart dhcp_relay')
duthost.shell('systemctl reset-failed dhcp_relay')
time.sleep(30)


def reset_dual_tor(duthost):
duthost.shell('redis-cli -n 4 HDEL "DEVICE_METADATA|localhost" "subtype"')
duthost.shell('systemctl restart dhcp_relay')
duthost.shell('systemctl reset-failed dhcp_relay')
time.sleep(30)


@pytest.mark.parametrize("testing_mode", ['single', 'dual'])
def test_dhcp_relay_default(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist, testing_mode):
"""Test DHCP relay functionality on T0 topology.

For each DHCP relay agent running on the DuT, verify DHCP packets are relayed properly
"""
duthost = duthosts[rand_one_dut_hostname]

if testing_mode == 'dual':
set_dual_tor(duthost)

for dhcp_relay in dut_dhcp_relay_data:
# Run the DHCP relay test on the PTF host
ptf_runner(ptfhost,
Expand All @@ -134,17 +159,27 @@ def test_dhcp_relay_default(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_r
"relay_iface_mac": str(dhcp_relay['downlink_vlan_iface']['mac']),
"relay_iface_netmask": str(dhcp_relay['downlink_vlan_iface']['mask']),
"dest_mac_address": BROADCAST_MAC,
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT},
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT,
"switch_loopback_ip": dhcp_relay['switch_loopback_ip'],
"testing_mode": testing_mode},
log_file="/tmp/dhcp_relay_test.DHCPTest.log")

if testing_mode == 'dual':
reset_dual_tor(duthost)


def test_dhcp_relay_after_link_flap(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist):
@pytest.mark.parametrize("testing_mode", ['single', 'dual'])
def test_dhcp_relay_after_link_flap(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist, testing_mode):
"""Test DHCP relay functionality on T0 topology after uplinks flap

For each DHCP relay agent running on the DuT, with relay agent running, flap the uplinks,
then test whether the DHCP relay agent relays packets properly.
"""
duthost = duthosts[rand_one_dut_hostname]

if testing_mode == 'dual':
set_dual_tor(duthost)

for dhcp_relay in dut_dhcp_relay_data:
# Bring all uplink interfaces down
for iface in dhcp_relay['uplink_interfaces']:
Expand Down Expand Up @@ -175,18 +210,28 @@ def test_dhcp_relay_after_link_flap(duthosts, rand_one_dut_hostname, ptfhost, du
"relay_iface_mac": str(dhcp_relay['downlink_vlan_iface']['mac']),
"relay_iface_netmask": str(dhcp_relay['downlink_vlan_iface']['mask']),
"dest_mac_address": BROADCAST_MAC,
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT},
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT,
"switch_loopback_ip": dhcp_relay['switch_loopback_ip'],
"testing_mode": testing_mode},
log_file="/tmp/dhcp_relay_test.DHCPTest.log")

if testing_mode == 'dual':
reset_dual_tor(duthost)


def test_dhcp_relay_start_with_uplinks_down(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist):
@pytest.mark.parametrize("testing_mode", ['single', 'dual'])
def test_dhcp_relay_start_with_uplinks_down(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist, testing_mode):
"""Test DHCP relay functionality on T0 topology when relay agent starts with uplinks down

For each DHCP relay agent running on the DuT, bring the uplinks down, then restart the
relay agent while the uplinks are still down. Then test whether the DHCP relay agent
relays packets properly.
"""
duthost = duthosts[rand_one_dut_hostname]

if testing_mode == 'dual':
set_dual_tor(duthost)

for dhcp_relay in dut_dhcp_relay_data:
# Bring all uplink interfaces down
for iface in dhcp_relay['uplink_interfaces']:
Expand Down Expand Up @@ -224,16 +269,26 @@ def test_dhcp_relay_start_with_uplinks_down(duthosts, rand_one_dut_hostname, ptf
"relay_iface_mac": str(dhcp_relay['downlink_vlan_iface']['mac']),
"relay_iface_netmask": str(dhcp_relay['downlink_vlan_iface']['mask']),
"dest_mac_address": BROADCAST_MAC,
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT},
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT,
"switch_loopback_ip": dhcp_relay['switch_loopback_ip'],
"testing_mode": testing_mode},
log_file="/tmp/dhcp_relay_test.DHCPTest.log")

if testing_mode == 'dual':
reset_dual_tor(duthost)


def test_dhcp_relay_unicast_mac(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist):
@pytest.mark.parametrize("testing_mode", ['single', 'dual'])
def test_dhcp_relay_unicast_mac(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist, testing_mode):
"""Test DHCP relay functionality on T0 topology with unicast mac

Instead of using broadcast MAC, use unicast MAC of DUT and verify that DHCP relay functionality is entact.
"""
duthost = duthosts[rand_one_dut_hostname]

if testing_mode == 'dual':
set_dual_tor(duthost)

for dhcp_relay in dut_dhcp_relay_data:
# Run the DHCP relay test on the PTF host
ptf_runner(ptfhost,
Expand All @@ -250,17 +305,27 @@ def test_dhcp_relay_unicast_mac(duthosts, rand_one_dut_hostname, ptfhost, dut_dh
"relay_iface_mac": str(dhcp_relay['downlink_vlan_iface']['mac']),
"relay_iface_netmask": str(dhcp_relay['downlink_vlan_iface']['mask']),
"dest_mac_address": duthost.facts["router_mac"],
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT},
"client_udp_src_port": DEFAULT_DHCP_CLIENT_PORT,
"switch_loopback_ip": dhcp_relay['switch_loopback_ip'],
"testing_mode": testing_mode},
log_file="/tmp/dhcp_relay_test.DHCPTest.log")

if testing_mode == 'dual':
reset_dual_tor(duthost)


def test_dhcp_relay_random_sport(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist):
@pytest.mark.parametrize("testing_mode", ['single', 'dual'])
def test_dhcp_relay_random_sport(duthosts, rand_one_dut_hostname, ptfhost, dut_dhcp_relay_data, validate_dut_routes_exist, testing_mode):
"""Test DHCP relay functionality on T0 topology with random source port (sport)

If the client is SNAT'd, the source port could be changed to a non-standard port (i.e., not 68).
Verify that DHCP relay works with random high sport.
"""
duthost = duthosts[rand_one_dut_hostname]

if testing_mode == 'dual':
set_dual_tor(duthost)

RANDOM_CLIENT_PORT = random.choice(range(1000, 65535))
for dhcp_relay in dut_dhcp_relay_data:
# Run the DHCP relay test on the PTF host
Expand All @@ -278,5 +343,11 @@ def test_dhcp_relay_random_sport(duthosts, rand_one_dut_hostname, ptfhost, dut_d
"relay_iface_mac": str(dhcp_relay['downlink_vlan_iface']['mac']),
"relay_iface_netmask": str(dhcp_relay['downlink_vlan_iface']['mask']),
"dest_mac_address": BROADCAST_MAC,
"client_udp_src_port": RANDOM_CLIENT_PORT},
"client_udp_src_port": RANDOM_CLIENT_PORT,
"switch_loopback_ip": dhcp_relay['switch_loopback_ip'],
"testing_mode": testing_mode},
log_file="/tmp/dhcp_relay_test.DHCPTest.log")

if testing_mode == 'dual':
reset_dual_tor(duthost)