vpp/test/test_dhcp.py

1797 lines
59 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import unittest
import socket
import six
2023-08-31 00:47:44 -04:00
from framework import VppTestCase
from asfframework import VppTestRunner, tag_run_solo
from vpp_neighbor import VppNeighbor
from vpp_ip_route import find_route, VppIpTable
from util import mk_ll_addr
import scapy.compat
2023-08-31 00:47:44 -04:00
from scapy.layers.l2 import Ether, ARP, Dot1Q
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6, in6_getnsmac
from scapy.layers.dhcp import DHCP, BOOTP, DHCPTypes
from scapy.layers.dhcp6 import (
DHCP6_Solicit,
DHCP6_RelayForward,
DHCP6_RelayReply,
DHCP6_Advertise,
DHCP6OptRelayMsg,
DHCP6OptIfaceId,
DHCP6OptStatusCode,
DHCP6OptVSS,
DHCP6OptClientLinkLayerAddr,
DHCP6_Request,
)
2023-08-31 00:47:44 -04:00
from socket import AF_INET, AF_INET6, inet_pton
from scapy.utils6 import in6_ptop
from vpp_papi import mac_pton, VppEnum
from vpp_sub_interface import VppDot1QSubint
from vpp_qos import VppQosEgressMap, VppQosMark
from vpp_dhcp import VppDHCPClient, VppDHCPProxy
from config import config
DHCP4_CLIENT_PORT = 68
DHCP4_SERVER_PORT = 67
DHCP6_CLIENT_PORT = 547
DHCP6_SERVER_PORT = 546
@tag_run_solo
@unittest.skipIf("dhcp" in config.excluded_plugins, "Exclude DHCP plugin tests")
class TestDHCP(VppTestCase):
"""DHCP Test Case"""
@classmethod
def setUpClass(cls):
super(TestDHCP, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TestDHCP, cls).tearDownClass()
def setUp(self):
super(TestDHCP, self).setUp()
# create 6 pg interfaces for pg0 to pg5
self.create_pg_interfaces(range(6))
self.tables = []
# pg0 to 2 are IP configured in VRF 0, 1 and 2.
# pg3 to 5 are non IP-configured in VRF 0, 1 and 2.
table_id = 0
for table_id in range(1, 4):
tbl4 = VppIpTable(self, table_id)
tbl4.add_vpp_config()
self.tables.append(tbl4)
tbl6 = VppIpTable(self, table_id, is_ip6=1)
tbl6.add_vpp_config()
self.tables.append(tbl6)
table_id = 0
for i in self.pg_interfaces[:3]:
i.admin_up()
i.set_table_ip4(table_id)
i.set_table_ip6(table_id)
i.config_ip4()
i.resolve_arp()
i.config_ip6()
i.resolve_ndp()
table_id += 1
table_id = 0
for i in self.pg_interfaces[3:]:
i.admin_up()
i.set_table_ip4(table_id)
i.set_table_ip6(table_id)
table_id += 1
def tearDown(self):
for i in self.pg_interfaces[:3]:
i.unconfig_ip4()
i.unconfig_ip6()
for i in self.pg_interfaces:
i.set_table_ip4(0)
i.set_table_ip6(0)
i.admin_down()
super(TestDHCP, self).tearDown()
def verify_dhcp_has_option(self, pkt, option, value):
dhcp = pkt[DHCP]
found = False
for i in dhcp.options:
if isinstance(i, tuple):
if i[0] == option:
self.assertEqual(i[1], value)
found = True
self.assertTrue(found)
def validate_relay_options(self, pkt, intf, ip_addr, vpn_id, fib_id, oui):
dhcp = pkt[DHCP]
found = 0
data = []
id_len = len(vpn_id)
for i in dhcp.options:
if isinstance(i, tuple):
if i[0] == "relay_agent_information":
#
# There are two sb-options present - each of length 6.
#
data = i[1]
if oui != 0:
self.assertEqual(len(data), 24)
elif len(vpn_id) > 0:
self.assertEqual(len(data), len(vpn_id) + 17)
else:
self.assertEqual(len(data), 12)
#
# First sub-option is ID 1, len 4, then encoded
# sw_if_index. This test uses low valued indicies
# so [2:4] are 0.
# The ID space is VPP internal - so no matching value
# scapy
#
self.assertEqual(six.byte2int(data[0:1]), 1)
self.assertEqual(six.byte2int(data[1:2]), 4)
self.assertEqual(six.byte2int(data[2:3]), 0)
self.assertEqual(six.byte2int(data[3:4]), 0)
self.assertEqual(six.byte2int(data[4:5]), 0)
self.assertEqual(six.byte2int(data[5:6]), intf._sw_if_index)
#
# next sub-option is the IP address of the client side
# interface.
# sub-option ID=5, length (of a v4 address)=4
#
claddr = socket.inet_pton(AF_INET, ip_addr)
self.assertEqual(six.byte2int(data[6:7]), 5)
self.assertEqual(six.byte2int(data[7:8]), 4)
self.assertEqual(data[8], claddr[0])
self.assertEqual(data[9], claddr[1])
self.assertEqual(data[10], claddr[2])
self.assertEqual(data[11], claddr[3])
if oui != 0:
# sub-option 151 encodes vss_type 1,
# the 3 byte oui and the 4 byte fib_id
self.assertEqual(id_len, 0)
self.assertEqual(six.byte2int(data[12:13]), 151)
self.assertEqual(six.byte2int(data[13:14]), 8)
self.assertEqual(six.byte2int(data[14:15]), 1)
self.assertEqual(six.byte2int(data[15:16]), 0)
self.assertEqual(six.byte2int(data[16:17]), 0)
self.assertEqual(six.byte2int(data[17:18]), oui)
self.assertEqual(six.byte2int(data[18:19]), 0)
self.assertEqual(six.byte2int(data[19:20]), 0)
self.assertEqual(six.byte2int(data[20:21]), 0)
self.assertEqual(six.byte2int(data[21:22]), fib_id)
# VSS control sub-option
self.assertEqual(six.byte2int(data[22:23]), 152)
self.assertEqual(six.byte2int(data[23:24]), 0)
if id_len > 0:
# sub-option 151 encode vss_type of 0
# followerd by vpn_id in ascii
self.assertEqual(oui, 0)
self.assertEqual(six.byte2int(data[12:13]), 151)
self.assertEqual(six.byte2int(data[13:14]), id_len + 1)
self.assertEqual(six.byte2int(data[14:15]), 0)
self.assertEqual(data[15 : 15 + id_len].decode("ascii"), vpn_id)
# VSS control sub-option
self.assertEqual(
six.byte2int(data[15 + len(vpn_id) : 16 + len(vpn_id)]), 152
)
self.assertEqual(
six.byte2int(data[16 + len(vpn_id) : 17 + len(vpn_id)]), 0
)
found = 1
self.assertTrue(found)
return data
def verify_dhcp_msg_type(self, pkt, name):
dhcp = pkt[DHCP]
found = False
for o in dhcp.options:
if isinstance(o, tuple):
if o[0] == "message-type" and DHCPTypes[o[1]] == name:
found = True
self.assertTrue(found)
def verify_dhcp_offer(self, pkt, intf, vpn_id="", fib_id=0, oui=0):
ether = pkt[Ether]
self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
self.assertEqual(ether.src, intf.local_mac)
ip = pkt[IP]
self.assertEqual(ip.dst, "255.255.255.255")
self.assertEqual(ip.src, intf.local_ip4)
udp = pkt[UDP]
self.assertEqual(udp.dport, DHCP4_CLIENT_PORT)
self.assertEqual(udp.sport, DHCP4_SERVER_PORT)
self.verify_dhcp_msg_type(pkt, "offer")
data = self.validate_relay_options(
pkt, intf, intf.local_ip4, vpn_id, fib_id, oui
)
def verify_orig_dhcp_pkt(self, pkt, intf, dscp, l2_bc=True):
ether = pkt[Ether]
if l2_bc:
self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
else:
self.assertEqual(ether.dst, intf.remote_mac)
self.assertEqual(ether.src, intf.local_mac)
ip = pkt[IP]
if l2_bc:
self.assertEqual(ip.dst, "255.255.255.255")
self.assertEqual(ip.src, "0.0.0.0")
else:
self.assertEqual(ip.dst, intf.remote_ip4)
self.assertEqual(ip.src, intf.local_ip4)
self.assertEqual(ip.tos, dscp)
udp = pkt[UDP]
self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
def verify_orig_dhcp_discover(
self, pkt, intf, hostname, client_id=None, broadcast=True, dscp=0
):
self.verify_orig_dhcp_pkt(pkt, intf, dscp)
self.verify_dhcp_msg_type(pkt, "discover")
self.verify_dhcp_has_option(pkt, "hostname", hostname.encode("ascii"))
if client_id:
client_id = "\x00" + client_id
self.verify_dhcp_has_option(pkt, "client_id", client_id.encode("ascii"))
bootp = pkt[BOOTP]
self.assertEqual(bootp.ciaddr, "0.0.0.0")
self.assertEqual(bootp.giaddr, "0.0.0.0")
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
if broadcast:
self.assertEqual(bootp.flags, 0x8000)
else:
self.assertEqual(bootp.flags, 0x0000)
def verify_orig_dhcp_request(
self, pkt, intf, hostname, ip, broadcast=True, l2_bc=True, dscp=0
):
self.verify_orig_dhcp_pkt(pkt, intf, dscp, l2_bc=l2_bc)
self.verify_dhcp_msg_type(pkt, "request")
self.verify_dhcp_has_option(pkt, "hostname", hostname.encode("ascii"))
self.verify_dhcp_has_option(pkt, "requested_addr", ip)
bootp = pkt[BOOTP]
if l2_bc:
self.assertEqual(bootp.ciaddr, "0.0.0.0")
else:
self.assertEqual(bootp.ciaddr, intf.local_ip4)
self.assertEqual(bootp.giaddr, "0.0.0.0")
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
if broadcast:
self.assertEqual(bootp.flags, 0x8000)
else:
self.assertEqual(bootp.flags, 0x0000)
def verify_relayed_dhcp_discover(
self,
pkt,
intf,
src_intf=None,
fib_id=0,
oui=0,
vpn_id="",
dst_mac=None,
dst_ip=None,
):
if not dst_mac:
dst_mac = intf.remote_mac
if not dst_ip:
dst_ip = intf.remote_ip4
ether = pkt[Ether]
self.assertEqual(ether.dst, dst_mac)
self.assertEqual(ether.src, intf.local_mac)
ip = pkt[IP]
self.assertEqual(ip.dst, dst_ip)
self.assertEqual(ip.src, intf.local_ip4)
udp = pkt[UDP]
self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
dhcp = pkt[DHCP]
is_discover = False
for o in dhcp.options:
if isinstance(o, tuple):
if o[0] == "message-type" and DHCPTypes[o[1]] == "discover":
is_discover = True
self.assertTrue(is_discover)
data = self.validate_relay_options(
pkt, src_intf, src_intf.local_ip4, vpn_id, fib_id, oui
)
return data
def verify_dhcp6_solicit(
self,
pkt,
intf,
peer_ip,
peer_mac,
vpn_id="",
fib_id=0,
oui=0,
dst_mac=None,
dst_ip=None,
):
if not dst_mac:
dst_mac = intf.remote_mac
if not dst_ip:
dst_ip = in6_ptop(intf.remote_ip6)
ether = pkt[Ether]
self.assertEqual(ether.dst, dst_mac)
self.assertEqual(ether.src, intf.local_mac)
ip = pkt[IPv6]
self.assertEqual(in6_ptop(ip.dst), dst_ip)
self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
udp = pkt[UDP]
self.assertEqual(udp.dport, DHCP6_CLIENT_PORT)
self.assertEqual(udp.sport, DHCP6_SERVER_PORT)
relay = pkt[DHCP6_RelayForward]
self.assertEqual(in6_ptop(relay.peeraddr), in6_ptop(peer_ip))
oid = pkt[DHCP6OptIfaceId]
cll = pkt[DHCP6OptClientLinkLayerAddr]
self.assertEqual(cll.optlen, 8)
self.assertEqual(cll.lltype, 1)
self.assertEqual(cll.clladdr, peer_mac)
id_len = len(vpn_id)
if fib_id != 0:
self.assertEqual(id_len, 0)
vss = pkt[DHCP6OptVSS]
self.assertEqual(vss.optlen, 8)
self.assertEqual(vss.type, 1)
# the OUI and FIB-id are really 3 and 4 bytes resp.
# but the tested range is small
self.assertEqual(six.byte2int(vss.data[0:1]), 0)
self.assertEqual(six.byte2int(vss.data[1:2]), 0)
self.assertEqual(six.byte2int(vss.data[2:3]), oui)
self.assertEqual(six.byte2int(vss.data[3:4]), 0)
self.assertEqual(six.byte2int(vss.data[4:5]), 0)
self.assertEqual(six.byte2int(vss.data[5:6]), 0)
self.assertEqual(six.byte2int(vss.data[6:7]), fib_id)
if id_len > 0:
self.assertEqual(oui, 0)
vss = pkt[DHCP6OptVSS]
self.assertEqual(vss.optlen, id_len + 1)
self.assertEqual(vss.type, 0)
self.assertEqual(vss.data[0:id_len].decode("ascii"), vpn_id)
# the relay message should be an encoded Solicit
msg = pkt[DHCP6OptRelayMsg]
sol = DHCP6_Solicit()
self.assertEqual(msg.optlen, len(sol))
self.assertEqual(sol, msg[1])
def verify_dhcp6_advert(self, pkt, intf, peer):
ether = pkt[Ether]
self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
self.assertEqual(ether.src, intf.local_mac)
ip = pkt[IPv6]
self.assertEqual(in6_ptop(ip.dst), in6_ptop(peer))
self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
udp = pkt[UDP]
self.assertEqual(udp.dport, DHCP6_SERVER_PORT)
self.assertEqual(udp.sport, DHCP6_CLIENT_PORT)
# not sure why this is not decoding
# adv = pkt[DHCP6_Advertise]
def wait_for_no_route(self, address, length, n_tries=50, s_time=1):
while n_tries:
if not find_route(self, address, length):
return True
n_tries = n_tries - 1
self.sleep(s_time)
return False
def test_dhcp_proxy(self):
"""DHCPv4 Proxy"""
#
# Verify no response to DHCP request without DHCP config
#
p_disc_vrf0 = (
Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg3.remote_mac)
/ IP(src="0.0.0.0", dst="255.255.255.255")
/ UDP(sport=DHCP4_CLIENT_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(options=[("message-type", "discover"), ("end")])
)
pkts_disc_vrf0 = [p_disc_vrf0]
p_disc_vrf1 = (
Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg4.remote_mac)
/ IP(src="0.0.0.0", dst="255.255.255.255")
/ UDP(sport=DHCP4_CLIENT_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(options=[("message-type", "discover"), ("end")])
)
pkts_disc_vrf1 = [p_disc_vrf1]
p_disc_vrf2 = (
Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg5.remote_mac)
/ IP(src="0.0.0.0", dst="255.255.255.255")
/ UDP(sport=DHCP4_CLIENT_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(options=[("message-type", "discover"), ("end")])
)
pkts_disc_vrf2 = [p_disc_vrf2]
self.send_and_assert_no_replies(
self.pg3, pkts_disc_vrf0, "DHCP with no configuration"
)
self.send_and_assert_no_replies(
self.pg4, pkts_disc_vrf1, "DHCP with no configuration"
)
self.send_and_assert_no_replies(
self.pg5, pkts_disc_vrf2, "DHCP with no configuration"
)
#
# Enable DHCP proxy in VRF 0
#
server_addr = self.pg0.remote_ip4
src_addr = self.pg0.local_ip4
Proxy = VppDHCPProxy(self, server_addr, src_addr, rx_vrf_id=0)
Proxy.add_vpp_config()
#
# Discover packets from the client are dropped because there is no
# IP address configured on the client facing interface
#
self.send_and_assert_no_replies(
self.pg3, pkts_disc_vrf0, "Discover DHCP no relay address"
)
#
# Inject a response from the server
# dropped, because there is no IP addrees on the
# client interfce to fill in the option.
#
p = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(options=[("message-type", "offer"), ("end")])
)
pkts = [p]
self.send_and_assert_no_replies(self.pg3, pkts, "Offer DHCP no relay address")
#
# configure an IP address on the client facing interface
#
self.pg3.config_ip4()
#
# Try again with a discover packet
# Rx'd packet should be to the server address and from the configured
# source address
# UDP source ports are unchanged
# we've no option 82 config so that should be absent
#
self.pg3.add_stream(pkts_disc_vrf0)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg0.get_capture(1)
rx = rx[0]
option_82 = self.verify_relayed_dhcp_discover(rx, self.pg0, src_intf=self.pg3)
#
# Create an DHCP offer reply from the server with a correctly formatted
# option 82. i.e. send back what we just captured
# The offer, sent mcast to the client, still has option 82.
#
p = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(
options=[
("message-type", "offer"),
("relay_agent_information", option_82),
("end"),
]
)
)
pkts = [p]
self.pg0.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg3.get_capture(1, timeout=5)
rx = rx[0]
self.verify_dhcp_offer(rx, self.pg3)
#
# Bogus Option 82:
#
# 1. not our IP address = not checked by VPP? so offer is replayed
# to client
bad_ip = option_82[0:8] + scapy.compat.chb(33) + option_82[9:]
p = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(
options=[
("message-type", "offer"),
("relay_agent_information", bad_ip),
("end"),
]
)
)
pkts = [p]
self.send_and_assert_no_replies(
self.pg0, pkts, "DHCP offer option 82 bad address"
)
# 2. Not a sw_if_index VPP knows
bad_if_index = option_82[0:2] + scapy.compat.chb(33) + option_82[3:]
p = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(
options=[
("message-type", "offer"),
("relay_agent_information", bad_if_index),
("end"),
]
)
)
pkts = [p]
self.send_and_assert_no_replies(
self.pg0, pkts, "DHCP offer option 82 bad if index"
)
#
# Send a DHCP request in VRF 1. should be dropped.
#
self.send_and_assert_no_replies(
self.pg4, pkts_disc_vrf1, "DHCP with no configuration VRF 1"
)
#
# Delete the DHCP config in VRF 0
# Should now drop requests.
#
Proxy.remove_vpp_config()
self.send_and_assert_no_replies(
self.pg3, pkts_disc_vrf0, "DHCP config removed VRF 0"
)
self.send_and_assert_no_replies(
self.pg4, pkts_disc_vrf1, "DHCP config removed VRF 1"
)
#
# Add DHCP config for VRF 1 & 2
#
server_addr1 = self.pg1.remote_ip4
src_addr1 = self.pg1.local_ip4
Proxy1 = VppDHCPProxy(
self, server_addr1, src_addr1, rx_vrf_id=1, server_vrf_id=1
)
Proxy1.add_vpp_config()
server_addr2 = self.pg2.remote_ip4
src_addr2 = self.pg2.local_ip4
Proxy2 = VppDHCPProxy(
self, server_addr2, src_addr2, rx_vrf_id=2, server_vrf_id=2
)
Proxy2.add_vpp_config()
#
# Confim DHCP requests ok in VRF 1 & 2.
# - dropped on IP config on client interface
#
self.send_and_assert_no_replies(
self.pg4, pkts_disc_vrf1, "DHCP config removed VRF 1"
)
self.send_and_assert_no_replies(
self.pg5, pkts_disc_vrf2, "DHCP config removed VRF 2"
)
#
# configure an IP address on the client facing interface
#
self.pg4.config_ip4()
self.pg4.add_stream(pkts_disc_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
rx = rx[0]
self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg4)
self.pg5.config_ip4()
self.pg5.add_stream(pkts_disc_vrf2)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg2.get_capture(1)
rx = rx[0]
self.verify_relayed_dhcp_discover(rx, self.pg2, src_intf=self.pg5)
#
# Add VSS config
# table=1, vss_type=1, vpn_index=1, oui=4
# table=2, vss_type=0, vpn_id = "ip4-table-2"
self.vapi.dhcp_proxy_set_vss(tbl_id=1, vss_type=1, vpn_index=1, oui=4, is_add=1)
self.vapi.dhcp_proxy_set_vss(
tbl_id=2, vss_type=0, vpn_ascii_id="ip4-table-2", is_add=1
)
self.pg4.add_stream(pkts_disc_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
rx = rx[0]
self.verify_relayed_dhcp_discover(
rx, self.pg1, src_intf=self.pg4, fib_id=1, oui=4
)
self.pg5.add_stream(pkts_disc_vrf2)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg2.get_capture(1)
rx = rx[0]
self.verify_relayed_dhcp_discover(
rx, self.pg2, src_intf=self.pg5, vpn_id="ip4-table-2"
)
#
# Add a second DHCP server in VRF 1
# expect clients messages to be relay to both configured servers
#
self.pg1.generate_remote_hosts(2)
server_addr12 = self.pg1.remote_hosts[1].ip4
Proxy12 = VppDHCPProxy(
self, server_addr12, src_addr, rx_vrf_id=1, server_vrf_id=1
)
Proxy12.add_vpp_config()
#
# We'll need an ARP entry for the server to send it packets
#
arp_entry = VppNeighbor(
self,
self.pg1.sw_if_index,
self.pg1.remote_hosts[1].mac,
self.pg1.remote_hosts[1].ip4,
)
arp_entry.add_vpp_config()
#
# Send a discover from the client. expect two relayed messages
# The frist packet is sent to the second server
# We're not enforcing that here, it's just the way it is.
#
self.pg4.add_stream(pkts_disc_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(2)
option_82 = self.verify_relayed_dhcp_discover(
rx[0],
self.pg1,
src_intf=self.pg4,
dst_mac=self.pg1.remote_hosts[1].mac,
dst_ip=self.pg1.remote_hosts[1].ip4,
fib_id=1,
oui=4,
)
self.verify_relayed_dhcp_discover(
rx[1], self.pg1, src_intf=self.pg4, fib_id=1, oui=4
)
#
# Send both packets back. Client gets both.
#
p1 = (
Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
/ IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(
options=[
("message-type", "offer"),
("relay_agent_information", option_82),
("end"),
]
)
)
p2 = (
Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
/ IP(src=self.pg1.remote_hosts[1].ip4, dst=self.pg1.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(
options=[
("message-type", "offer"),
("relay_agent_information", option_82),
("end"),
]
)
)
pkts = [p1, p2]
self.pg1.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg4.get_capture(2)
self.verify_dhcp_offer(rx[0], self.pg4, fib_id=1, oui=4)
self.verify_dhcp_offer(rx[1], self.pg4, fib_id=1, oui=4)
#
# Ensure offers from non-servers are dropeed
#
p2 = (
Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
/ IP(src="8.8.8.8", dst=self.pg1.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(
options=[
("message-type", "offer"),
("relay_agent_information", option_82),
("end"),
]
)
)
self.send_and_assert_no_replies(self.pg1, p2, "DHCP offer from non-server")
#
# Ensure only the discover is sent to multiple servers
#
p_req_vrf1 = (
Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg4.remote_mac)
/ IP(src="0.0.0.0", dst="255.255.255.255")
/ UDP(sport=DHCP4_CLIENT_PORT, dport=DHCP4_SERVER_PORT)
/ BOOTP(op=1)
/ DHCP(options=[("message-type", "request"), ("end")])
)
self.pg4.add_stream(p_req_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
#
# Remove the second DHCP server
#
Proxy12.remove_vpp_config()
#
# Test we can still relay with the first
#
self.pg4.add_stream(pkts_disc_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
rx = rx[0]
self.verify_relayed_dhcp_discover(
rx, self.pg1, src_intf=self.pg4, fib_id=1, oui=4
)
#
# Remove the VSS config
# relayed DHCP has default vlaues in the option.
#
self.vapi.dhcp_proxy_set_vss(tbl_id=1, is_add=0)
self.vapi.dhcp_proxy_set_vss(tbl_id=2, is_add=0)
self.pg4.add_stream(pkts_disc_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
rx = rx[0]
self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg4)
#
# remove DHCP config to cleanup
#
Proxy1.remove_vpp_config()
Proxy2.remove_vpp_config()
self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0, "DHCP cleanup VRF 0")
self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1, "DHCP cleanup VRF 1")
self.send_and_assert_no_replies(self.pg5, pkts_disc_vrf2, "DHCP cleanup VRF 2")
self.pg3.unconfig_ip4()
self.pg4.unconfig_ip4()
self.pg5.unconfig_ip4()
def test_dhcp6_proxy(self):
"""DHCPv6 Proxy"""
#
# Verify no response to DHCP request without DHCP config
#
dhcp_solicit_dst = "ff02::1:2"
dhcp_solicit_src_vrf0 = mk_ll_addr(self.pg3.remote_mac)
dhcp_solicit_src_vrf1 = mk_ll_addr(self.pg4.remote_mac)
dhcp_solicit_src_vrf2 = mk_ll_addr(self.pg5.remote_mac)
server_addr_vrf0 = self.pg0.remote_ip6
src_addr_vrf0 = self.pg0.local_ip6
server_addr_vrf1 = self.pg1.remote_ip6
src_addr_vrf1 = self.pg1.local_ip6
server_addr_vrf2 = self.pg2.remote_ip6
src_addr_vrf2 = self.pg2.local_ip6
dmac = in6_getnsmac(inet_pton(socket.AF_INET6, dhcp_solicit_dst))
p_solicit_vrf0 = (
Ether(dst=dmac, src=self.pg3.remote_mac)
/ IPv6(src=dhcp_solicit_src_vrf0, dst=dhcp_solicit_dst)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT)
/ DHCP6_Solicit()
)
p_solicit_vrf1 = (
Ether(dst=dmac, src=self.pg4.remote_mac)
/ IPv6(src=dhcp_solicit_src_vrf1, dst=dhcp_solicit_dst)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT)
/ DHCP6_Solicit()
)
p_solicit_vrf2 = (
Ether(dst=dmac, src=self.pg5.remote_mac)
/ IPv6(src=dhcp_solicit_src_vrf2, dst=dhcp_solicit_dst)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT)
/ DHCP6_Solicit()
)
self.send_and_assert_no_replies(
self.pg3, p_solicit_vrf0, "DHCP with no configuration"
)
self.send_and_assert_no_replies(
self.pg4, p_solicit_vrf1, "DHCP with no configuration"
)
self.send_and_assert_no_replies(
self.pg5, p_solicit_vrf2, "DHCP with no configuration"
)
#
# DHCPv6 config in VRF 0.
# Packets still dropped because the client facing interface has no
# IPv6 config
#
Proxy = VppDHCPProxy(
self, server_addr_vrf0, src_addr_vrf0, rx_vrf_id=0, server_vrf_id=0
)
Proxy.add_vpp_config()
self.send_and_assert_no_replies(
self.pg3, p_solicit_vrf0, "DHCP with no configuration"
)
self.send_and_assert_no_replies(
self.pg4, p_solicit_vrf1, "DHCP with no configuration"
)
#
# configure an IP address on the client facing interface
#
self.pg3.config_ip6()
#
# Now the DHCP requests are relayed to the server
#
self.pg3.add_stream(p_solicit_vrf0)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg0.get_capture(1)
self.verify_dhcp6_solicit(
rx[0], self.pg0, dhcp_solicit_src_vrf0, self.pg3.remote_mac
)
#
# Exception cases for rejected relay responses
#
# 1 - not a relay reply
p_adv_vrf0 = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_Advertise()
)
self.send_and_assert_no_replies(self.pg3, p_adv_vrf0, "DHCP6 not a relay reply")
# 2 - no relay message option
p_adv_vrf0 = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply()
/ DHCP6_Advertise()
)
self.send_and_assert_no_replies(
self.pg3, p_adv_vrf0, "DHCP not a relay message"
)
# 3 - no circuit ID
p_adv_vrf0 = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply()
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise()
)
self.send_and_assert_no_replies(self.pg3, p_adv_vrf0, "DHCP6 no circuit ID")
# 4 - wrong circuit ID
p_adv_vrf0 = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply()
/ DHCP6OptIfaceId(optlen=4, ifaceid="\x00\x00\x00\x05")
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise()
)
self.send_and_assert_no_replies(self.pg3, p_adv_vrf0, "DHCP6 wrong circuit ID")
#
# Send the relay response (the advertisement)
# - no peer address
p_adv_vrf0 = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply()
/ DHCP6OptIfaceId(optlen=4, ifaceid="\x00\x00\x00\x04")
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise(trid=1)
/ DHCP6OptStatusCode(statuscode=0)
)
pkts_adv_vrf0 = [p_adv_vrf0]
self.pg0.add_stream(pkts_adv_vrf0)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg3.get_capture(1)
self.verify_dhcp6_advert(rx[0], self.pg3, "::")
#
# Send the relay response (the advertisement)
# - with peer address
p_adv_vrf0 = (
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
/ IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf0)
/ DHCP6OptIfaceId(optlen=4, ifaceid="\x00\x00\x00\x04")
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise(trid=1)
/ DHCP6OptStatusCode(statuscode=0)
)
pkts_adv_vrf0 = [p_adv_vrf0]
self.pg0.add_stream(pkts_adv_vrf0)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg3.get_capture(1)
self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf0)
#
# Add all the config for VRF 1 & 2
#
Proxy1 = VppDHCPProxy(
self, server_addr_vrf1, src_addr_vrf1, rx_vrf_id=1, server_vrf_id=1
)
Proxy1.add_vpp_config()
self.pg4.config_ip6()
Proxy2 = VppDHCPProxy(
self, server_addr_vrf2, src_addr_vrf2, rx_vrf_id=2, server_vrf_id=2
)
Proxy2.add_vpp_config()
self.pg5.config_ip6()
#
# VRF 1 solicit
#
self.pg4.add_stream(p_solicit_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
self.verify_dhcp6_solicit(
rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg4.remote_mac
)
#
# VRF 2 solicit
#
self.pg5.add_stream(p_solicit_vrf2)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg2.get_capture(1)
self.verify_dhcp6_solicit(
rx[0], self.pg2, dhcp_solicit_src_vrf2, self.pg5.remote_mac
)
#
# VRF 1 Advert
#
p_adv_vrf1 = (
Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
/ IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1)
/ DHCP6OptIfaceId(optlen=4, ifaceid="\x00\x00\x00\x05")
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise(trid=1)
/ DHCP6OptStatusCode(statuscode=0)
)
pkts_adv_vrf1 = [p_adv_vrf1]
self.pg1.add_stream(pkts_adv_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg4.get_capture(1)
self.verify_dhcp6_advert(rx[0], self.pg4, dhcp_solicit_src_vrf1)
#
# Add VSS config
#
self.vapi.dhcp_proxy_set_vss(
tbl_id=1, vss_type=1, oui=4, vpn_index=1, is_ipv6=1, is_add=1
)
self.vapi.dhcp_proxy_set_vss(
tbl_id=2, vss_type=0, vpn_ascii_id="IPv6-table-2", is_ipv6=1, is_add=1
)
self.pg4.add_stream(p_solicit_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
self.verify_dhcp6_solicit(
rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg4.remote_mac, fib_id=1, oui=4
)
self.pg5.add_stream(p_solicit_vrf2)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg2.get_capture(1)
self.verify_dhcp6_solicit(
rx[0],
self.pg2,
dhcp_solicit_src_vrf2,
self.pg5.remote_mac,
vpn_id="IPv6-table-2",
)
#
# Remove the VSS config
# relayed DHCP has default vlaues in the option.
#
self.vapi.dhcp_proxy_set_vss(tbl_id=1, is_ipv6=1, is_add=0)
self.pg4.add_stream(p_solicit_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
self.verify_dhcp6_solicit(
rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg4.remote_mac
)
#
# Add a second DHCP server in VRF 1
# expect clients messages to be relay to both configured servers
#
self.pg1.generate_remote_hosts(2)
server_addr12 = self.pg1.remote_hosts[1].ip6
Proxy12 = VppDHCPProxy(
self, server_addr12, src_addr_vrf1, rx_vrf_id=1, server_vrf_id=1
)
Proxy12.add_vpp_config()
#
# We'll need an ND entry for the server to send it packets
#
nd_entry = VppNeighbor(
self,
self.pg1.sw_if_index,
self.pg1.remote_hosts[1].mac,
self.pg1.remote_hosts[1].ip6,
)
nd_entry.add_vpp_config()
#
# Send a discover from the client. expect two relayed messages
# The frist packet is sent to the second server
# We're not enforcing that here, it's just the way it is.
#
self.pg4.add_stream(p_solicit_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(2)
self.verify_dhcp6_solicit(
rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg4.remote_mac
)
self.verify_dhcp6_solicit(
rx[1],
self.pg1,
dhcp_solicit_src_vrf1,
self.pg4.remote_mac,
dst_mac=self.pg1.remote_hosts[1].mac,
dst_ip=self.pg1.remote_hosts[1].ip6,
)
#
# Send both packets back. Client gets both.
#
p1 = (
Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
/ IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1)
/ DHCP6OptIfaceId(optlen=4, ifaceid="\x00\x00\x00\x05")
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise(trid=1)
/ DHCP6OptStatusCode(statuscode=0)
)
p2 = (
Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac)
/ IPv6(dst=self.pg1.local_ip6, src=self.pg1._remote_hosts[1].ip6)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1)
/ DHCP6OptIfaceId(optlen=4, ifaceid="\x00\x00\x00\x05")
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise(trid=1)
/ DHCP6OptStatusCode(statuscode=0)
)
pkts = [p1, p2]
self.pg1.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg4.get_capture(2)
self.verify_dhcp6_advert(rx[0], self.pg4, dhcp_solicit_src_vrf1)
self.verify_dhcp6_advert(rx[1], self.pg4, dhcp_solicit_src_vrf1)
#
# Ensure only solicit messages are duplicated
#
p_request_vrf1 = (
Ether(dst=dmac, src=self.pg4.remote_mac)
/ IPv6(src=dhcp_solicit_src_vrf1, dst=dhcp_solicit_dst)
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT)
/ DHCP6_Request()
)
self.pg4.add_stream(p_request_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
#
# Test we drop DHCP packets from addresses that are not configured as
# DHCP servers
#
p2 = (
Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac)
/ IPv6(dst=self.pg1.local_ip6, src="3001::1")
/ UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT)
/ DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1)
/ DHCP6OptIfaceId(optlen=4, ifaceid="\x00\x00\x00\x05")
/ DHCP6OptRelayMsg(optlen=0)
/ DHCP6_Advertise(trid=1)
/ DHCP6OptStatusCode(statuscode=0)
)
self.send_and_assert_no_replies(self.pg1, p2, "DHCP6 not from server")
#
# Remove the second DHCP server
#
Proxy12.remove_vpp_config()
#
# Test we can still relay with the first
#
self.pg4.add_stream(p_solicit_vrf1)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg1.get_capture(1)
self.verify_dhcp6_solicit(
rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg4.remote_mac
)
#
# Cleanup
#
Proxy.remove_vpp_config()
Proxy1.remove_vpp_config()
Proxy2.remove_vpp_config()
self.pg3.unconfig_ip6()
self.pg4.unconfig_ip6()
self.pg5.unconfig_ip6()
def test_dhcp_client(self):
"""DHCP Client"""
vdscp = VppEnum.vl_api_ip_dscp_t
hostname = "universal-dp"
self.pg_enable_capture(self.pg_interfaces)
#
# Configure DHCP client on PG3 and capture the discover sent
#
Client = VppDHCPClient(self, self.pg3.sw_if_index, hostname)
Client.add_vpp_config()
self.assertTrue(Client.query_vpp_config())
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname)
#
# Send back on offer, expect the request
#
p_offer = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst="255.255.255.255")
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1, yiaddr=self.pg3.local_ip4, chaddr=mac_pton(self.pg3.local_mac)
)
/ DHCP(
options=[
("message-type", "offer"),
("server_id", self.pg3.remote_ip4),
"end",
]
)
)
self.pg3.add_stream(p_offer)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_request(rx[0], self.pg3, hostname, self.pg3.local_ip4)
#
# Send an acknowledgment
#
p_ack = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst="255.255.255.255")
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1, yiaddr=self.pg3.local_ip4, chaddr=mac_pton(self.pg3.local_mac)
)
/ DHCP(
options=[
("message-type", "ack"),
("subnet_mask", "255.255.255.0"),
("router", self.pg3.remote_ip4),
("server_id", self.pg3.remote_ip4),
("lease_time", 43200),
"end",
]
)
)
self.pg3.add_stream(p_ack)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
#
# We'll get an ARP request for the router address
#
rx = self.pg3.get_capture(1)
self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
self.pg_enable_capture(self.pg_interfaces)
#
# At the end of this procedure there should be a connected route
# in the FIB
#
self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
#
# remove the DHCP config
#
Client.remove_vpp_config()
#
# and now the route should be gone
#
self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
#
# Start the procedure again. this time have VPP send the client-ID
# and set the DSCP value
#
self.pg3.admin_down()
self.sleep(1)
self.pg3.admin_up()
Client.set_client(
self.pg3.sw_if_index,
hostname,
id=self.pg3.local_mac,
dscp=vdscp.IP_API_DSCP_EF,
)
Client.add_vpp_config()
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_discover(
rx[0], self.pg3, hostname, self.pg3.local_mac, dscp=vdscp.IP_API_DSCP_EF
)
# TODO: VPP DHCP client should not accept DHCP OFFER message with
# the XID (Transaction ID) not matching the XID of the most recent
# DHCP DISCOVERY message.
# Such DHCP OFFER message must be silently discarded - RFC2131.
# Reported in Jira ticket: VPP-99
self.pg3.add_stream(p_offer)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_request(
rx[0], self.pg3, hostname, self.pg3.local_ip4, dscp=vdscp.IP_API_DSCP_EF
)
#
# unicast the ack to the offered address
#
p_ack = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1, yiaddr=self.pg3.local_ip4, chaddr=mac_pton(self.pg3.local_mac)
)
/ DHCP(
options=[
("message-type", "ack"),
("subnet_mask", "255.255.255.0"),
("router", self.pg3.remote_ip4),
("server_id", self.pg3.remote_ip4),
("lease_time", 43200),
"end",
]
)
)
self.pg3.add_stream(p_ack)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
#
# We'll get an ARP request for the router address
#
rx = self.pg3.get_capture(1)
self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
self.pg_enable_capture(self.pg_interfaces)
#
# At the end of this procedure there should be a connected route
# in the FIB
#
self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
#
# remove the DHCP config
#
Client.remove_vpp_config()
self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
#
# Rince and repeat, this time with VPP configured not to set
# the braodcast flag in the discover and request messages,
# and for the server to unicast the responses.
#
# Configure DHCP client on PG3 and capture the discover sent
#
Client.set_client(self.pg3.sw_if_index, hostname, set_broadcast_flag=False)
Client.add_vpp_config()
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname, broadcast=False)
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
#
# Send back on offer, unicasted to the offered address.
# Expect the request.
#
p_offer = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1, yiaddr=self.pg3.local_ip4, chaddr=mac_pton(self.pg3.local_mac)
)
/ DHCP(
options=[
("message-type", "offer"),
("server_id", self.pg3.remote_ip4),
"end",
]
)
)
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
self.pg3.add_stream(p_offer)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_request(
rx[0], self.pg3, hostname, self.pg3.local_ip4, broadcast=False
)
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
#
# Send an acknowledgment, the lease renewal time is 2 seconds
# so we should expect the renew straight after
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
#
p_ack = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1, yiaddr=self.pg3.local_ip4, chaddr=mac_pton(self.pg3.local_mac)
)
/ DHCP(
options=[
("message-type", "ack"),
("subnet_mask", "255.255.255.0"),
("router", self.pg3.remote_ip4),
("server_id", self.pg3.remote_ip4),
("lease_time", 43200),
("renewal_time", 2),
"end",
]
)
)
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
self.pg3.add_stream(p_ack)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
#
# We'll get an ARP request for the router address
#
rx = self.pg3.get_capture(1)
self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
self.pg_enable_capture(self.pg_interfaces)
#
# At the end of this procedure there should be a connected route
# in the FIB
#
self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
#
# read the DHCP client details from a dump
#
clients = self.vapi.dhcp_client_dump()
self.assertEqual(clients[0].client.sw_if_index, self.pg3.sw_if_index)
self.assertEqual(clients[0].lease.sw_if_index, self.pg3.sw_if_index)
self.assertEqual(clients[0].client.hostname, hostname)
self.assertEqual(clients[0].lease.hostname, hostname)
# 0 = DISCOVER, 1 = REQUEST, 2 = BOUND
self.assertEqual(clients[0].lease.state, 2)
self.assertEqual(clients[0].lease.mask_width, 24)
self.assertEqual(str(clients[0].lease.router_address), self.pg3.remote_ip4)
self.assertEqual(str(clients[0].lease.host_address), self.pg3.local_ip4)
#
# wait for the unicasted renewal
# the first attempt will be an ARP packet, since we have not yet
# responded to VPP's request
#
self.logger.info(self.vapi.cli("sh dhcp client intfc pg3 verbose"))
rx = self.pg3.get_capture(1, timeout=10)
self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
# respond to the arp
p_arp = Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / ARP(
op="is-at",
hwdst=self.pg3.local_mac,
hwsrc=self.pg3.remote_mac,
pdst=self.pg3.local_ip4,
psrc=self.pg3.remote_ip4,
)
self.pg3.add_stream(p_arp)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
# the next packet is the unicasted renewal
rx = self.pg3.get_capture(1, timeout=10)
self.verify_orig_dhcp_request(
rx[0], self.pg3, hostname, self.pg3.local_ip4, l2_bc=False, broadcast=False
)
# send an ACK with different data from the original offer *
self.pg3.generate_remote_hosts(4)
p_ack = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4)
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1,
yiaddr=self.pg3.remote_hosts[3].ip4,
chaddr=mac_pton(self.pg3.local_mac),
)
/ DHCP(
options=[
("message-type", "ack"),
("subnet_mask", "255.255.255.0"),
("router", self.pg3.remote_hosts[1].ip4),
("server_id", self.pg3.remote_hosts[2].ip4),
("lease_time", 43200),
("renewal_time", 2),
"end",
]
)
)
self.pg3.add_stream(p_ack)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
#
# read the DHCP client details from a dump
#
clients = self.vapi.dhcp_client_dump()
self.assertEqual(clients[0].client.sw_if_index, self.pg3.sw_if_index)
self.assertEqual(clients[0].lease.sw_if_index, self.pg3.sw_if_index)
self.assertEqual(clients[0].client.hostname, hostname)
self.assertEqual(clients[0].lease.hostname, hostname)
# 0 = DISCOVER, 1 = REQUEST, 2 = BOUND
self.assertEqual(clients[0].lease.state, 2)
self.assertEqual(clients[0].lease.mask_width, 24)
self.assertEqual(
str(clients[0].lease.router_address), self.pg3.remote_hosts[1].ip4
)
self.assertEqual(
str(clients[0].lease.host_address), self.pg3.remote_hosts[3].ip4
)
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
#
# remove the DHCP config
#
Client.remove_vpp_config()
For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-17 10:29:10 -08:00
#
# and now the route should be gone
#
self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
#
# Start the procedure again. Use requested lease time option.
# this time wait for the lease to expire and the client to
# self-destruct
#
hostname += "-2"
self.pg3.admin_down()
self.sleep(1)
self.pg3.admin_up()
self.pg_enable_capture(self.pg_interfaces)
Client.set_client(self.pg3.sw_if_index, hostname)
Client.add_vpp_config()
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname)
#
# Send back on offer with requested lease time, expect the request
#
lease_time = 1
p_offer = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst="255.255.255.255")
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1, yiaddr=self.pg3.local_ip4, chaddr=mac_pton(self.pg3.local_mac)
)
/ DHCP(
options=[
("message-type", "offer"),
("server_id", self.pg3.remote_ip4),
("lease_time", lease_time),
"end",
]
)
)
self.pg3.add_stream(p_offer)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = self.pg3.get_capture(1)
self.verify_orig_dhcp_request(rx[0], self.pg3, hostname, self.pg3.local_ip4)
#
# Send an acknowledgment
#
p_ack = (
Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
/ IP(src=self.pg3.remote_ip4, dst="255.255.255.255")
/ UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT)
/ BOOTP(
op=1, yiaddr=self.pg3.local_ip4, chaddr=mac_pton(self.pg3.local_mac)
)
/ DHCP(
options=[
("message-type", "ack"),
("subnet_mask", "255.255.255.0"),
("router", self.pg3.remote_ip4),
("server_id", self.pg3.remote_ip4),
("lease_time", lease_time),
"end",
]
)
)
self.pg3.add_stream(p_ack)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
#
# We'll get an ARP request for the router address
#
rx = self.pg3.get_capture(1)
self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
#
# At the end of this procedure there should be a connected route
# in the FIB
#
self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
#
# the route should be gone after the lease expires
#
self.assertTrue(self.wait_for_no_route(self.pg3.local_ip4, 32))
self.assertTrue(self.wait_for_no_route(self.pg3.local_ip4, 24))
#
# remove the DHCP config
#
Client.remove_vpp_config()
def test_dhcp_client_vlan(self):
"""DHCP Client w/ VLAN"""
vdscp = VppEnum.vl_api_ip_dscp_t
vqos = VppEnum.vl_api_qos_source_t
hostname = "universal-dp"
self.pg_enable_capture(self.pg_interfaces)
vlan_100 = VppDot1QSubint(self, self.pg3, 100)
vlan_100.admin_up()
output = [scapy.compat.chb(4)] * 256
os = b"".join(output)
rows = [{"outputs": os}, {"outputs": os}, {"outputs": os}, {"outputs": os}]
qem1 = VppQosEgressMap(self, 1, rows).add_vpp_config()
qm1 = VppQosMark(
self, vlan_100, qem1, vqos.QOS_API_SOURCE_VLAN
).add_vpp_config()
#
# Configure DHCP client on PG3 and capture the discover sent
#
Client = VppDHCPClient(
self, vlan_100.sw_if_index, hostname, dscp=vdscp.IP_API_DSCP_EF
)
Client.add_vpp_config()
rx = self.pg3.get_capture(1)
self.assertEqual(rx[0][Dot1Q].vlan, 100)
self.assertEqual(rx[0][Dot1Q].prio, 2)
self.verify_orig_dhcp_discover(
rx[0], self.pg3, hostname, dscp=vdscp.IP_API_DSCP_EF
)
if __name__ == "__main__":
unittest.main(testRunner=VppTestRunner)