6fc291824ec6c44c19737caa8eb2e2d9bc533d7f
[vpp.git] / test / test_dhcp.py
1 #!/usr/bin/env python
2
3 import unittest
4 import socket
5 import struct
6
7 from framework import VppTestCase, VppTestRunner
8 from vpp_neighbor import VppNeighbor
9 from vpp_ip_route import find_route
10 from util import mk_ll_addr
11
12 from scapy.layers.l2 import Ether, getmacbyip, ARP
13 from scapy.layers.inet import IP, UDP, ICMP
14 from scapy.layers.inet6 import IPv6, in6_getnsmac, in6_mactoifaceid
15 from scapy.layers.dhcp import DHCP, BOOTP, DHCPTypes
16 from scapy.layers.dhcp6 import DHCP6, DHCP6_Solicit, DHCP6_RelayForward, \
17     DHCP6_RelayReply, DHCP6_Advertise, DHCP6OptRelayMsg, DHCP6OptIfaceId, \
18     DHCP6OptStatusCode, DHCP6OptVSS, DHCP6OptClientLinkLayerAddr, DHCP6_Request
19 from socket import AF_INET, AF_INET6
20 from scapy.utils import inet_pton, inet_ntop
21 from scapy.utils6 import in6_ptop
22
23 DHCP4_CLIENT_PORT = 68
24 DHCP4_SERVER_PORT = 67
25 DHCP6_CLIENT_PORT = 547
26 DHCP6_SERVER_PORT = 546
27
28
29 class TestDHCP(VppTestCase):
30     """ DHCP Test Case """
31
32     def setUp(self):
33         super(TestDHCP, self).setUp()
34
35         # create 3 pg interfaces
36         self.create_pg_interfaces(range(4))
37
38         # pg0 and 1 are IP configured in VRF 0 and 1.
39         # pg2 and 3 are non IP-configured in VRF 0 and 1
40         table_id = 0
41         for i in self.pg_interfaces[:2]:
42             i.admin_up()
43             i.set_table_ip4(table_id)
44             i.set_table_ip6(table_id)
45             i.config_ip4()
46             i.resolve_arp()
47             i.config_ip6()
48             i.resolve_ndp()
49             table_id += 1
50
51         table_id = 0
52         for i in self.pg_interfaces[2:]:
53             i.admin_up()
54             i.set_table_ip4(table_id)
55             i.set_table_ip6(table_id)
56             table_id += 1
57
58     def tearDown(self):
59         super(TestDHCP, self).tearDown()
60         for i in self.pg_interfaces:
61             i.unconfig_ip4()
62             i.unconfig_ip6()
63             i.admin_down()
64
65     def send_and_assert_no_replies(self, intf, pkts, remark):
66         intf.add_stream(pkts)
67         self.pg_enable_capture(self.pg_interfaces)
68         self.pg_start()
69         for i in self.pg_interfaces:
70             i.assert_nothing_captured(remark=remark)
71
72     def verify_dhcp_has_option(self, pkt, option, value):
73         dhcp = pkt[DHCP]
74         found = False
75
76         for i in dhcp.options:
77             if type(i) is tuple:
78                 if i[0] == option:
79                     self.assertEqual(i[1], value)
80                     found = True
81
82         self.assertTrue(found)
83
84     def validate_relay_options(self, pkt, intf, ip_addr, fib_id, oui):
85         dhcp = pkt[DHCP]
86         found = 0
87         data = []
88
89         for i in dhcp.options:
90             if type(i) is tuple:
91                 if i[0] == "relay_agent_Information":
92                     #
93                     # There are two sb-options present - each of length 6.
94                     #
95                     data = i[1]
96                     if oui != 0:
97                         self.assertEqual(len(data), 24)
98                     else:
99                         self.assertEqual(len(data), 12)
100
101                     #
102                     # First sub-option is ID 1, len 4, then encoded
103                     #  sw_if_index. This test uses low valued indicies
104                     # so [2:4] are 0.
105                     # The ID space is VPP internal - so no matching value
106                     # scapy
107                     #
108                     self.assertEqual(ord(data[0]), 1)
109                     self.assertEqual(ord(data[1]), 4)
110                     self.assertEqual(ord(data[2]), 0)
111                     self.assertEqual(ord(data[3]), 0)
112                     self.assertEqual(ord(data[4]), 0)
113                     self.assertEqual(ord(data[5]), intf._sw_if_index)
114
115                     #
116                     # next sub-option is the IP address of the client side
117                     # interface.
118                     # sub-option ID=5, length (of a v4 address)=4
119                     #
120                     claddr = socket.inet_pton(AF_INET, ip_addr)
121
122                     self.assertEqual(ord(data[6]), 5)
123                     self.assertEqual(ord(data[7]), 4)
124                     self.assertEqual(data[8], claddr[0])
125                     self.assertEqual(data[9], claddr[1])
126                     self.assertEqual(data[10], claddr[2])
127                     self.assertEqual(data[11], claddr[3])
128
129                     if oui != 0:
130                         # sub-option 151 encodes the 3 byte oui
131                         # and the 4 byte fib_id
132                         self.assertEqual(ord(data[12]), 151)
133                         self.assertEqual(ord(data[13]), 8)
134                         self.assertEqual(ord(data[14]), 1)
135                         self.assertEqual(ord(data[15]), 0)
136                         self.assertEqual(ord(data[16]), 0)
137                         self.assertEqual(ord(data[17]), oui)
138                         self.assertEqual(ord(data[18]), 0)
139                         self.assertEqual(ord(data[19]), 0)
140                         self.assertEqual(ord(data[20]), 0)
141                         self.assertEqual(ord(data[21]), fib_id)
142
143                         # VSS control sub-option
144                         self.assertEqual(ord(data[22]), 152)
145                         self.assertEqual(ord(data[23]), 0)
146
147                     found = 1
148         self.assertTrue(found)
149
150         return data
151
152     def verify_dhcp_msg_type(self, pkt, name):
153         dhcp = pkt[DHCP]
154         found = False
155         for o in dhcp.options:
156             if type(o) is tuple:
157                 if o[0] == "message-type" \
158                    and DHCPTypes[o[1]] == name:
159                     found = True
160         self.assertTrue(found)
161
162     def verify_dhcp_offer(self, pkt, intf, fib_id=0, oui=0):
163         ether = pkt[Ether]
164         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
165         self.assertEqual(ether.src, intf.local_mac)
166
167         ip = pkt[IP]
168         self.assertEqual(ip.dst, "255.255.255.255")
169         self.assertEqual(ip.src, intf.local_ip4)
170
171         udp = pkt[UDP]
172         self.assertEqual(udp.dport, DHCP4_CLIENT_PORT)
173         self.assertEqual(udp.sport, DHCP4_SERVER_PORT)
174
175         self.verify_dhcp_msg_type(pkt, "offer")
176         data = self.validate_relay_options(pkt, intf, intf.local_ip4,
177                                            fib_id, oui)
178
179     def verify_orig_dhcp_pkt(self, pkt, intf):
180         ether = pkt[Ether]
181         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
182         self.assertEqual(ether.src, intf.local_mac)
183
184         ip = pkt[IP]
185         self.assertEqual(ip.dst, "255.255.255.255")
186         self.assertEqual(ip.src, "0.0.0.0")
187
188         udp = pkt[UDP]
189         self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
190         self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
191
192     def verify_orig_dhcp_discover(self, pkt, intf, hostname, client_id=None):
193         self.verify_orig_dhcp_pkt(pkt, intf)
194
195         self.verify_dhcp_msg_type(pkt, "discover")
196         self.verify_dhcp_has_option(pkt, "hostname", hostname)
197         if client_id:
198             self.verify_dhcp_has_option(pkt, "client_id", client_id)
199         bootp = pkt[BOOTP]
200         self.assertEqual(bootp.ciaddr, "0.0.0.0")
201         self.assertEqual(bootp.giaddr, "0.0.0.0")
202         self.assertEqual(bootp.flags, 0x8000)
203
204     def verify_orig_dhcp_request(self, pkt, intf, hostname, ip):
205         self.verify_orig_dhcp_pkt(pkt, intf)
206
207         self.verify_dhcp_msg_type(pkt, "request")
208         self.verify_dhcp_has_option(pkt, "hostname", hostname)
209         self.verify_dhcp_has_option(pkt, "requested_addr", ip)
210         bootp = pkt[BOOTP]
211         self.assertEqual(bootp.ciaddr, "0.0.0.0")
212         self.assertEqual(bootp.giaddr, "0.0.0.0")
213         self.assertEqual(bootp.flags, 0x8000)
214
215     def verify_relayed_dhcp_discover(self, pkt, intf, src_intf=None,
216                                      fib_id=0, oui=0,
217                                      dst_mac=None, dst_ip=None):
218         if not dst_mac:
219             dst_mac = intf.remote_mac
220         if not dst_ip:
221             dst_ip = intf.remote_ip4
222
223         ether = pkt[Ether]
224         self.assertEqual(ether.dst, dst_mac)
225         self.assertEqual(ether.src, intf.local_mac)
226
227         ip = pkt[IP]
228         self.assertEqual(ip.dst, dst_ip)
229         self.assertEqual(ip.src, intf.local_ip4)
230
231         udp = pkt[UDP]
232         self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
233         self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
234
235         dhcp = pkt[DHCP]
236
237         is_discover = False
238         for o in dhcp.options:
239             if type(o) is tuple:
240                 if o[0] == "message-type" \
241                    and DHCPTypes[o[1]] == "discover":
242                     is_discover = True
243         self.assertTrue(is_discover)
244
245         data = self.validate_relay_options(pkt, src_intf,
246                                            src_intf.local_ip4,
247                                            fib_id, oui)
248         return data
249
250     def verify_dhcp6_solicit(self, pkt, intf,
251                              peer_ip, peer_mac,
252                              fib_id=0,
253                              oui=0,
254                              dst_mac=None,
255                              dst_ip=None):
256         if not dst_mac:
257             dst_mac = intf.remote_mac
258         if not dst_ip:
259             dst_ip = in6_ptop(intf.remote_ip6)
260
261         ether = pkt[Ether]
262         self.assertEqual(ether.dst, dst_mac)
263         self.assertEqual(ether.src, intf.local_mac)
264
265         ip = pkt[IPv6]
266         self.assertEqual(in6_ptop(ip.dst), dst_ip)
267         self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
268
269         udp = pkt[UDP]
270         self.assertEqual(udp.dport, DHCP6_CLIENT_PORT)
271         self.assertEqual(udp.sport, DHCP6_SERVER_PORT)
272
273         relay = pkt[DHCP6_RelayForward]
274         self.assertEqual(in6_ptop(relay.peeraddr), in6_ptop(peer_ip))
275         oid = pkt[DHCP6OptIfaceId]
276         cll = pkt[DHCP6OptClientLinkLayerAddr]
277         self.assertEqual(cll.optlen, 8)
278         self.assertEqual(cll.lltype, 1)
279         self.assertEqual(cll.clladdr, peer_mac)
280
281         if fib_id != 0:
282             vss = pkt[DHCP6OptVSS]
283             self.assertEqual(vss.optlen, 8)
284             self.assertEqual(vss.type, 1)
285             # the OUI and FIB-id are really 3 and 4 bytes resp.
286             # but the tested range is small
287             self.assertEqual(ord(vss.data[0]), 0)
288             self.assertEqual(ord(vss.data[1]), 0)
289             self.assertEqual(ord(vss.data[2]), oui)
290             self.assertEqual(ord(vss.data[3]), 0)
291             self.assertEqual(ord(vss.data[4]), 0)
292             self.assertEqual(ord(vss.data[5]), 0)
293             self.assertEqual(ord(vss.data[6]), fib_id)
294
295         # the relay message should be an encoded Solicit
296         msg = pkt[DHCP6OptRelayMsg]
297         sol = DHCP6_Solicit()
298         self.assertEqual(msg.optlen, len(str(sol)))
299         self.assertEqual(str(sol), (str(msg[1]))[:msg.optlen])
300
301     def verify_dhcp6_advert(self, pkt, intf, peer):
302         ether = pkt[Ether]
303         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
304         self.assertEqual(ether.src, intf.local_mac)
305
306         ip = pkt[IPv6]
307         self.assertEqual(in6_ptop(ip.dst), in6_ptop(peer))
308         self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
309
310         udp = pkt[UDP]
311         self.assertEqual(udp.dport, DHCP6_SERVER_PORT)
312         self.assertEqual(udp.sport, DHCP6_CLIENT_PORT)
313
314         # not sure why this is not decoding
315         # adv = pkt[DHCP6_Advertise]
316
317     def test_dhcp_proxy(self):
318         """ DHCPv4 Proxy """
319
320         #
321         # Verify no response to DHCP request without DHCP config
322         #
323         p_disc_vrf0 = (Ether(dst="ff:ff:ff:ff:ff:ff",
324                              src=self.pg2.remote_mac) /
325                        IP(src="0.0.0.0", dst="255.255.255.255") /
326                        UDP(sport=DHCP4_CLIENT_PORT,
327                            dport=DHCP4_SERVER_PORT) /
328                        BOOTP(op=1) /
329                        DHCP(options=[('message-type', 'discover'), ('end')]))
330         pkts_disc_vrf0 = [p_disc_vrf0]
331         p_disc_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
332                              src=self.pg3.remote_mac) /
333                        IP(src="0.0.0.0", dst="255.255.255.255") /
334                        UDP(sport=DHCP4_CLIENT_PORT,
335                            dport=DHCP4_SERVER_PORT) /
336                        BOOTP(op=1) /
337                        DHCP(options=[('message-type', 'discover'), ('end')]))
338         pkts_disc_vrf1 = [p_disc_vrf0]
339
340         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
341                                         "DHCP with no configuration")
342         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
343                                         "DHCP with no configuration")
344
345         #
346         # Enable DHCP proxy in VRF 0
347         #
348         server_addr = self.pg0.remote_ip4n
349         src_addr = self.pg0.local_ip4n
350
351         self.vapi.dhcp_proxy_config(server_addr,
352                                     src_addr,
353                                     rx_table_id=0)
354
355         #
356         # Discover packets from the client are dropped because there is no
357         # IP address configured on the client facing interface
358         #
359         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
360                                         "Discover DHCP no relay address")
361
362         #
363         # Inject a response from the server
364         #  dropped, because there is no IP addrees on the
365         #  client interfce to fill in the option.
366         #
367         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
368              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
369              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
370              BOOTP(op=1) /
371              DHCP(options=[('message-type', 'offer'), ('end')]))
372         pkts = [p]
373
374         self.send_and_assert_no_replies(self.pg2, pkts,
375                                         "Offer DHCP no relay address")
376
377         #
378         # configure an IP address on the client facing interface
379         #
380         self.pg2.config_ip4()
381
382         #
383         # Try again with a discover packet
384         # Rx'd packet should be to the server address and from the configured
385         # source address
386         # UDP source ports are unchanged
387         # we've no option 82 config so that should be absent
388         #
389         self.pg2.add_stream(pkts_disc_vrf0)
390         self.pg_enable_capture(self.pg_interfaces)
391         self.pg_start()
392
393         rx = self.pg0.get_capture(1)
394         rx = rx[0]
395
396         option_82 = self.verify_relayed_dhcp_discover(rx, self.pg0,
397                                                       src_intf=self.pg2)
398
399         #
400         # Create an DHCP offer reply from the server with a correctly formatted
401         # option 82. i.e. send back what we just captured
402         # The offer, sent mcast to the client, still has option 82.
403         #
404         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
405              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
406              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
407              BOOTP(op=1) /
408              DHCP(options=[('message-type', 'offer'),
409                            ('relay_agent_Information', option_82),
410                            ('end')]))
411         pkts = [p]
412
413         self.pg0.add_stream(pkts)
414         self.pg_enable_capture(self.pg_interfaces)
415         self.pg_start()
416
417         rx = self.pg2.get_capture(1)
418         rx = rx[0]
419
420         self.verify_dhcp_offer(rx, self.pg2)
421
422         #
423         # Bogus Option 82:
424         #
425         # 1. not our IP address = not checked by VPP? so offer is replayed
426         #    to client
427         bad_ip = option_82[0:8] + chr(33) + option_82[9:]
428
429         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
430              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
431              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
432              BOOTP(op=1) /
433              DHCP(options=[('message-type', 'offer'),
434                            ('relay_agent_Information', bad_ip),
435                            ('end')]))
436         pkts = [p]
437         self.send_and_assert_no_replies(self.pg0, pkts,
438                                         "DHCP offer option 82 bad address")
439
440         # 2. Not a sw_if_index VPP knows
441         bad_if_index = option_82[0:2] + chr(33) + option_82[3:]
442
443         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
444              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
445              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
446              BOOTP(op=1) /
447              DHCP(options=[('message-type', 'offer'),
448                            ('relay_agent_Information', bad_if_index),
449                            ('end')]))
450         pkts = [p]
451         self.send_and_assert_no_replies(self.pg0, pkts,
452                                         "DHCP offer option 82 bad if index")
453
454         #
455         # Send a DHCP request in VRF 1. should be dropped.
456         #
457         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
458                                         "DHCP with no configuration VRF 1")
459
460         #
461         # Delete the DHCP config in VRF 0
462         # Should now drop requests.
463         #
464         self.vapi.dhcp_proxy_config(server_addr,
465                                     src_addr,
466                                     rx_table_id=0,
467                                     is_add=0)
468
469         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
470                                         "DHCP config removed VRF 0")
471         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
472                                         "DHCP config removed VRF 1")
473
474         #
475         # Add DHCP config for VRF 1
476         #
477         server_addr = self.pg1.remote_ip4n
478         src_addr = self.pg1.local_ip4n
479         self.vapi.dhcp_proxy_config(server_addr,
480                                     src_addr,
481                                     rx_table_id=1,
482                                     server_table_id=1)
483
484         #
485         # Confim DHCP requests ok in VRF 1.
486         #  - dropped on IP config on client interface
487         #
488         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
489                                         "DHCP config removed VRF 1")
490
491         #
492         # configure an IP address on the client facing interface
493         #
494         self.pg3.config_ip4()
495
496         self.pg3.add_stream(pkts_disc_vrf1)
497         self.pg_enable_capture(self.pg_interfaces)
498         self.pg_start()
499
500         rx = self.pg1.get_capture(1)
501         rx = rx[0]
502         self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg3)
503
504         #
505         # Add VSS config
506         #  table=1, fib=id=1, oui=4
507         self.vapi.dhcp_proxy_set_vss(1, 1, 4)
508
509         self.pg3.add_stream(pkts_disc_vrf1)
510         self.pg_enable_capture(self.pg_interfaces)
511         self.pg_start()
512
513         rx = self.pg1.get_capture(1)
514         rx = rx[0]
515         self.verify_relayed_dhcp_discover(rx, self.pg1,
516                                           src_intf=self.pg3,
517                                           fib_id=1, oui=4)
518
519         #
520         # Add a second DHCP server in VRF 1
521         #  expect clients messages to be relay to both configured servers
522         #
523         self.pg1.generate_remote_hosts(2)
524         server_addr2 = socket.inet_pton(AF_INET, self.pg1.remote_hosts[1].ip4)
525
526         self.vapi.dhcp_proxy_config(server_addr2,
527                                     src_addr,
528                                     rx_table_id=1,
529                                     server_table_id=1,
530                                     is_add=1)
531
532         #
533         # We'll need an ARP entry for the server to send it packets
534         #
535         arp_entry = VppNeighbor(self,
536                                 self.pg1.sw_if_index,
537                                 self.pg1.remote_hosts[1].mac,
538                                 self.pg1.remote_hosts[1].ip4)
539         arp_entry.add_vpp_config()
540
541         #
542         # Send a discover from the client. expect two relayed messages
543         # The frist packet is sent to the second server
544         # We're not enforcing that here, it's just the way it is.
545         #
546         self.pg3.add_stream(pkts_disc_vrf1)
547         self.pg_enable_capture(self.pg_interfaces)
548         self.pg_start()
549
550         rx = self.pg1.get_capture(2)
551
552         option_82 = self.verify_relayed_dhcp_discover(
553             rx[0], self.pg1,
554             src_intf=self.pg3,
555             dst_mac=self.pg1.remote_hosts[1].mac,
556             dst_ip=self.pg1.remote_hosts[1].ip4,
557             fib_id=1, oui=4)
558         self.verify_relayed_dhcp_discover(rx[1], self.pg1,
559                                           src_intf=self.pg3,
560                                           fib_id=1, oui=4)
561
562         #
563         # Send both packets back. Client gets both.
564         #
565         p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
566               IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) /
567               UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
568               BOOTP(op=1) /
569               DHCP(options=[('message-type', 'offer'),
570                             ('relay_agent_Information', option_82),
571                             ('end')]))
572         p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
573               IP(src=self.pg1.remote_hosts[1].ip4, dst=self.pg1.local_ip4) /
574               UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
575               BOOTP(op=1) /
576               DHCP(options=[('message-type', 'offer'),
577                             ('relay_agent_Information', option_82),
578                             ('end')]))
579         pkts = [p1, p2]
580
581         self.pg1.add_stream(pkts)
582         self.pg_enable_capture(self.pg_interfaces)
583         self.pg_start()
584
585         rx = self.pg3.get_capture(2)
586
587         self.verify_dhcp_offer(rx[0], self.pg3, fib_id=1, oui=4)
588         self.verify_dhcp_offer(rx[1], self.pg3, fib_id=1, oui=4)
589
590         #
591         # Ensure offers from non-servers are dropeed
592         #
593         p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
594               IP(src="8.8.8.8", dst=self.pg1.local_ip4) /
595               UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
596               BOOTP(op=1) /
597               DHCP(options=[('message-type', 'offer'),
598                             ('relay_agent_Information', option_82),
599                             ('end')]))
600         self.send_and_assert_no_replies(self.pg1, p2,
601                                         "DHCP offer from non-server")
602
603         #
604         # Ensure only the discover is sent to multiple servers
605         #
606         p_req_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
607                             src=self.pg3.remote_mac) /
608                       IP(src="0.0.0.0", dst="255.255.255.255") /
609                       UDP(sport=DHCP4_CLIENT_PORT,
610                           dport=DHCP4_SERVER_PORT) /
611                       BOOTP(op=1) /
612                       DHCP(options=[('message-type', 'request'),
613                                     ('end')]))
614
615         self.pg3.add_stream(p_req_vrf1)
616         self.pg_enable_capture(self.pg_interfaces)
617         self.pg_start()
618
619         rx = self.pg1.get_capture(1)
620
621         #
622         # Remove the second DHCP server
623         #
624         self.vapi.dhcp_proxy_config(server_addr2,
625                                     src_addr,
626                                     rx_table_id=1,
627                                     server_table_id=1,
628                                     is_add=0)
629
630         #
631         # Test we can still relay with the first
632         #
633         self.pg3.add_stream(pkts_disc_vrf1)
634         self.pg_enable_capture(self.pg_interfaces)
635         self.pg_start()
636
637         rx = self.pg1.get_capture(1)
638         rx = rx[0]
639         self.verify_relayed_dhcp_discover(rx, self.pg1,
640                                           src_intf=self.pg3,
641                                           fib_id=1, oui=4)
642
643         #
644         # Remove the VSS config
645         #  relayed DHCP has default vlaues in the option.
646         #
647         self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_add=0)
648
649         self.pg3.add_stream(pkts_disc_vrf1)
650         self.pg_enable_capture(self.pg_interfaces)
651         self.pg_start()
652
653         rx = self.pg1.get_capture(1)
654         rx = rx[0]
655         self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg3)
656
657         #
658         # remove DHCP config to cleanup
659         #
660         self.vapi.dhcp_proxy_config(server_addr,
661                                     src_addr,
662                                     rx_table_id=1,
663                                     server_table_id=1,
664                                     is_add=0)
665
666         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
667                                         "DHCP cleanup VRF 0")
668         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
669                                         "DHCP cleanup VRF 1")
670
671     def test_dhcp6_proxy(self):
672         """ DHCPv6 Proxy"""
673         #
674         # Verify no response to DHCP request without DHCP config
675         #
676         dhcp_solicit_dst = "ff02::1:2"
677         dhcp_solicit_src_vrf0 = mk_ll_addr(self.pg2.remote_mac)
678         dhcp_solicit_src_vrf1 = mk_ll_addr(self.pg3.remote_mac)
679         server_addr_vrf0 = self.pg0.remote_ip6n
680         src_addr_vrf0 = self.pg0.local_ip6n
681         server_addr_vrf1 = self.pg1.remote_ip6n
682         src_addr_vrf1 = self.pg1.local_ip6n
683
684         dmac = in6_getnsmac(inet_pton(socket.AF_INET6, dhcp_solicit_dst))
685         p_solicit_vrf0 = (Ether(dst=dmac, src=self.pg2.remote_mac) /
686                           IPv6(src=dhcp_solicit_src_vrf0,
687                                dst=dhcp_solicit_dst) /
688                           UDP(sport=DHCP6_SERVER_PORT,
689                               dport=DHCP6_CLIENT_PORT) /
690                           DHCP6_Solicit())
691         p_solicit_vrf1 = (Ether(dst=dmac, src=self.pg3.remote_mac) /
692                           IPv6(src=dhcp_solicit_src_vrf1,
693                                dst=dhcp_solicit_dst) /
694                           UDP(sport=DHCP6_SERVER_PORT,
695                               dport=DHCP6_CLIENT_PORT) /
696                           DHCP6_Solicit())
697
698         self.send_and_assert_no_replies(self.pg2, p_solicit_vrf0,
699                                         "DHCP with no configuration")
700         self.send_and_assert_no_replies(self.pg3, p_solicit_vrf1,
701                                         "DHCP with no configuration")
702
703         #
704         # DHCPv6 config in VRF 0.
705         # Packets still dropped because the client facing interface has no
706         # IPv6 config
707         #
708         self.vapi.dhcp_proxy_config(server_addr_vrf0,
709                                     src_addr_vrf0,
710                                     rx_table_id=0,
711                                     server_table_id=0,
712                                     is_ipv6=1)
713
714         self.send_and_assert_no_replies(self.pg2, p_solicit_vrf0,
715                                         "DHCP with no configuration")
716         self.send_and_assert_no_replies(self.pg3, p_solicit_vrf1,
717                                         "DHCP with no configuration")
718
719         #
720         # configure an IP address on the client facing interface
721         #
722         self.pg2.config_ip6()
723
724         #
725         # Now the DHCP requests are relayed to the server
726         #
727         self.pg2.add_stream(p_solicit_vrf0)
728         self.pg_enable_capture(self.pg_interfaces)
729         self.pg_start()
730
731         rx = self.pg0.get_capture(1)
732
733         self.verify_dhcp6_solicit(rx[0], self.pg0,
734                                   dhcp_solicit_src_vrf0,
735                                   self.pg2.remote_mac)
736
737         #
738         # Exception cases for rejected relay responses
739         #
740
741         # 1 - not a relay reply
742         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
743                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
744                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
745                       DHCP6_Advertise())
746         self.send_and_assert_no_replies(self.pg2, p_adv_vrf0,
747                                         "DHCP6 not a relay reply")
748
749         # 2 - no relay message option
750         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
751                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
752                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
753                       DHCP6_RelayReply() /
754                       DHCP6_Advertise())
755         self.send_and_assert_no_replies(self.pg2, p_adv_vrf0,
756                                         "DHCP not a relay message")
757
758         # 3 - no circuit ID
759         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
760                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
761                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
762                       DHCP6_RelayReply() /
763                       DHCP6OptRelayMsg(optlen=0) /
764                       DHCP6_Advertise())
765         self.send_and_assert_no_replies(self.pg2, p_adv_vrf0,
766                                         "DHCP6 no circuit ID")
767         # 4 - wrong circuit ID
768         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
769                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
770                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
771                       DHCP6_RelayReply() /
772                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
773                       DHCP6OptRelayMsg(optlen=0) /
774                       DHCP6_Advertise())
775         self.send_and_assert_no_replies(self.pg2, p_adv_vrf0,
776                                         "DHCP6 wrong circuit ID")
777
778         #
779         # Send the relay response (the advertisement)
780         #   - no peer address
781         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
782                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
783                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
784                       DHCP6_RelayReply() /
785                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x03') /
786                       DHCP6OptRelayMsg(optlen=0) /
787                       DHCP6_Advertise(trid=1) /
788                       DHCP6OptStatusCode(statuscode=0))
789         pkts_adv_vrf0 = [p_adv_vrf0]
790
791         self.pg0.add_stream(pkts_adv_vrf0)
792         self.pg_enable_capture(self.pg_interfaces)
793         self.pg_start()
794
795         rx = self.pg2.get_capture(1)
796
797         self.verify_dhcp6_advert(rx[0], self.pg2, "::")
798
799         #
800         # Send the relay response (the advertisement)
801         #   - with peer address
802         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
803                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
804                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
805                       DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf0) /
806                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x03') /
807                       DHCP6OptRelayMsg(optlen=0) /
808                       DHCP6_Advertise(trid=1) /
809                       DHCP6OptStatusCode(statuscode=0))
810         pkts_adv_vrf0 = [p_adv_vrf0]
811
812         self.pg0.add_stream(pkts_adv_vrf0)
813         self.pg_enable_capture(self.pg_interfaces)
814         self.pg_start()
815
816         rx = self.pg2.get_capture(1)
817
818         self.verify_dhcp6_advert(rx[0], self.pg2, dhcp_solicit_src_vrf0)
819
820         #
821         # Add all the config for VRF 1
822         #
823         self.vapi.dhcp_proxy_config(server_addr_vrf1,
824                                     src_addr_vrf1,
825                                     rx_table_id=1,
826                                     server_table_id=1,
827                                     is_ipv6=1)
828         self.pg3.config_ip6()
829
830         #
831         # VRF 1 solicit
832         #
833         self.pg3.add_stream(p_solicit_vrf1)
834         self.pg_enable_capture(self.pg_interfaces)
835         self.pg_start()
836
837         rx = self.pg1.get_capture(1)
838
839         self.verify_dhcp6_solicit(rx[0], self.pg1,
840                                   dhcp_solicit_src_vrf1,
841                                   self.pg3.remote_mac)
842
843         #
844         # VRF 1 Advert
845         #
846         p_adv_vrf1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
847                       IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) /
848                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
849                       DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
850                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
851                       DHCP6OptRelayMsg(optlen=0) /
852                       DHCP6_Advertise(trid=1) /
853                       DHCP6OptStatusCode(statuscode=0))
854         pkts_adv_vrf1 = [p_adv_vrf1]
855
856         self.pg1.add_stream(pkts_adv_vrf1)
857         self.pg_enable_capture(self.pg_interfaces)
858         self.pg_start()
859
860         rx = self.pg3.get_capture(1)
861
862         self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf1)
863
864         #
865         # Add VSS config
866         #  table=1, fib=id=1, oui=4
867         self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1)
868
869         self.pg3.add_stream(p_solicit_vrf1)
870         self.pg_enable_capture(self.pg_interfaces)
871         self.pg_start()
872
873         rx = self.pg1.get_capture(1)
874
875         self.verify_dhcp6_solicit(rx[0], self.pg1,
876                                   dhcp_solicit_src_vrf1,
877                                   self.pg3.remote_mac,
878                                   fib_id=1,
879                                   oui=4)
880
881         #
882         # Remove the VSS config
883         #  relayed DHCP has default vlaues in the option.
884         #
885         self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1, is_add=0)
886
887         self.pg3.add_stream(p_solicit_vrf1)
888         self.pg_enable_capture(self.pg_interfaces)
889         self.pg_start()
890
891         rx = self.pg1.get_capture(1)
892
893         self.verify_dhcp6_solicit(rx[0], self.pg1,
894                                   dhcp_solicit_src_vrf1,
895                                   self.pg3.remote_mac)
896
897         #
898         # Add a second DHCP server in VRF 1
899         #  expect clients messages to be relay to both configured servers
900         #
901         self.pg1.generate_remote_hosts(2)
902         server_addr2 = socket.inet_pton(AF_INET6, self.pg1.remote_hosts[1].ip6)
903
904         self.vapi.dhcp_proxy_config(server_addr2,
905                                     src_addr_vrf1,
906                                     rx_table_id=1,
907                                     server_table_id=1,
908                                     is_ipv6=1)
909
910         #
911         # We'll need an ND entry for the server to send it packets
912         #
913         nd_entry = VppNeighbor(self,
914                                self.pg1.sw_if_index,
915                                self.pg1.remote_hosts[1].mac,
916                                self.pg1.remote_hosts[1].ip6,
917                                af=AF_INET6)
918         nd_entry.add_vpp_config()
919
920         #
921         # Send a discover from the client. expect two relayed messages
922         # The frist packet is sent to the second server
923         # We're not enforcing that here, it's just the way it is.
924         #
925         self.pg3.add_stream(p_solicit_vrf1)
926         self.pg_enable_capture(self.pg_interfaces)
927         self.pg_start()
928
929         rx = self.pg1.get_capture(2)
930
931         self.verify_dhcp6_solicit(rx[0], self.pg1,
932                                   dhcp_solicit_src_vrf1,
933                                   self.pg3.remote_mac)
934         self.verify_dhcp6_solicit(rx[1], self.pg1,
935                                   dhcp_solicit_src_vrf1,
936                                   self.pg3.remote_mac,
937                                   dst_mac=self.pg1.remote_hosts[1].mac,
938                                   dst_ip=self.pg1.remote_hosts[1].ip6)
939
940         #
941         # Send both packets back. Client gets both.
942         #
943         p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
944               IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) /
945               UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
946               DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
947               DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
948               DHCP6OptRelayMsg(optlen=0) /
949               DHCP6_Advertise(trid=1) /
950               DHCP6OptStatusCode(statuscode=0))
951         p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) /
952               IPv6(dst=self.pg1.local_ip6, src=self.pg1._remote_hosts[1].ip6) /
953               UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
954               DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
955               DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
956               DHCP6OptRelayMsg(optlen=0) /
957               DHCP6_Advertise(trid=1) /
958               DHCP6OptStatusCode(statuscode=0))
959
960         pkts = [p1, p2]
961
962         self.pg1.add_stream(pkts)
963         self.pg_enable_capture(self.pg_interfaces)
964         self.pg_start()
965
966         rx = self.pg3.get_capture(2)
967
968         self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf1)
969         self.verify_dhcp6_advert(rx[1], self.pg3, dhcp_solicit_src_vrf1)
970
971         #
972         # Ensure only solicit messages are duplicated
973         #
974         p_request_vrf1 = (Ether(dst=dmac, src=self.pg3.remote_mac) /
975                           IPv6(src=dhcp_solicit_src_vrf1,
976                                dst=dhcp_solicit_dst) /
977                           UDP(sport=DHCP6_SERVER_PORT,
978                               dport=DHCP6_CLIENT_PORT) /
979                           DHCP6_Request())
980
981         self.pg3.add_stream(p_request_vrf1)
982         self.pg_enable_capture(self.pg_interfaces)
983         self.pg_start()
984
985         rx = self.pg1.get_capture(1)
986
987         #
988         # Test we drop DHCP packets from addresses that are not configured as
989         # DHCP servers
990         #
991         p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) /
992               IPv6(dst=self.pg1.local_ip6, src="3001::1") /
993               UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
994               DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
995               DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
996               DHCP6OptRelayMsg(optlen=0) /
997               DHCP6_Advertise(trid=1) /
998               DHCP6OptStatusCode(statuscode=0))
999         self.send_and_assert_no_replies(self.pg1, p2,
1000                                         "DHCP6 not from server")
1001
1002         #
1003         # Remove the second DHCP server
1004         #
1005         self.vapi.dhcp_proxy_config(server_addr2,
1006                                     src_addr_vrf1,
1007                                     rx_table_id=1,
1008                                     server_table_id=1,
1009                                     is_ipv6=1,
1010                                     is_add=0)
1011
1012         #
1013         # Test we can still relay with the first
1014         #
1015         self.pg3.add_stream(p_solicit_vrf1)
1016         self.pg_enable_capture(self.pg_interfaces)
1017         self.pg_start()
1018
1019         rx = self.pg1.get_capture(1)
1020
1021         self.verify_dhcp6_solicit(rx[0], self.pg1,
1022                                   dhcp_solicit_src_vrf1,
1023                                   self.pg3.remote_mac)
1024
1025         #
1026         # Cleanup
1027         #
1028         self.vapi.dhcp_proxy_config(server_addr_vrf1,
1029                                     src_addr_vrf1,
1030                                     rx_table_id=1,
1031                                     server_table_id=1,
1032                                     is_ipv6=1,
1033                                     is_add=0)
1034         self.vapi.dhcp_proxy_config(server_addr_vrf0,
1035                                     src_addr_vrf0,
1036                                     rx_table_id=0,
1037                                     server_table_id=0,
1038                                     is_ipv6=1,
1039                                     is_add=0)
1040
1041         # duplicate delete
1042         self.vapi.dhcp_proxy_config(server_addr_vrf0,
1043                                     src_addr_vrf0,
1044                                     rx_table_id=0,
1045                                     server_table_id=0,
1046                                     is_ipv6=1,
1047                                     is_add=0)
1048
1049     def test_dhcp_client(self):
1050         """ DHCP Client"""
1051
1052         hostname = 'universal-dp'
1053
1054         self.pg_enable_capture(self.pg_interfaces)
1055
1056         #
1057         # Configure DHCP client on PG2 and capture the discover sent
1058         #
1059         self.vapi.dhcp_client(self.pg2.sw_if_index, hostname)
1060
1061         rx = self.pg2.get_capture(1)
1062
1063         self.verify_orig_dhcp_discover(rx[0], self.pg2, hostname)
1064
1065         #
1066         # Sned back on offer, expect the request
1067         #
1068         p_offer = (Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac) /
1069                    IP(src=self.pg2.remote_ip4, dst="255.255.255.255") /
1070                    UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1071                    BOOTP(op=1, yiaddr=self.pg2.local_ip4) /
1072                    DHCP(options=[('message-type', 'offer'),
1073                                  ('server_id', self.pg2.remote_ip4),
1074                                  ('end')]))
1075
1076         self.pg2.add_stream(p_offer)
1077         self.pg_enable_capture(self.pg_interfaces)
1078         self.pg_start()
1079
1080         rx = self.pg2.get_capture(1)
1081         self.verify_orig_dhcp_request(rx[0], self.pg2, hostname,
1082                                       self.pg2.local_ip4)
1083
1084         #
1085         # Send an acknowloedgement
1086         #
1087         p_ack = (Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac) /
1088                  IP(src=self.pg2.remote_ip4, dst="255.255.255.255") /
1089                  UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1090                  BOOTP(op=1, yiaddr=self.pg2.local_ip4) /
1091                  DHCP(options=[('message-type', 'ack'),
1092                                ('subnet_mask', "255.255.255.0"),
1093                                ('router', self.pg2.remote_ip4),
1094                                ('server_id', self.pg2.remote_ip4),
1095                                ('lease_time', 43200),
1096                                ('end')]))
1097
1098         self.pg2.add_stream(p_ack)
1099         self.pg_enable_capture(self.pg_interfaces)
1100         self.pg_start()
1101
1102         #
1103         # We'll get an ARP request for the router address
1104         #
1105         rx = self.pg2.get_capture(1)
1106
1107         self.assertEqual(rx[0][ARP].pdst, self.pg2.remote_ip4)
1108         self.pg_enable_capture(self.pg_interfaces)
1109
1110         #
1111         # At the end of this procedure there should be a connected route
1112         # in the FIB
1113         #
1114         self.assertTrue(find_route(self, self.pg2.local_ip4, 24))
1115         self.assertTrue(find_route(self, self.pg2.local_ip4, 32))
1116
1117         # remove the left over ARP entry
1118         self.vapi.ip_neighbor_add_del(self.pg2.sw_if_index,
1119                                       self.pg2.remote_mac,
1120                                       self.pg2.remote_ip4,
1121                                       is_add=0)
1122         #
1123         # remove the DHCP config
1124         #
1125         self.vapi.dhcp_client(self.pg2.sw_if_index, hostname, is_add=0)
1126
1127         #
1128         # and now the route should be gone
1129         #
1130         self.assertFalse(find_route(self, self.pg2.local_ip4, 32))
1131         self.assertFalse(find_route(self, self.pg2.local_ip4, 24))
1132
1133         #
1134         # Start the procedure again. this time have VPP send the client-ID
1135         #
1136         self.pg2.admin_down()
1137         self.sleep(1)
1138         self.pg2.admin_up()
1139         self.vapi.dhcp_client(self.pg2.sw_if_index, hostname,
1140                               client_id=self.pg2.local_mac)
1141
1142         rx = self.pg2.get_capture(1)
1143
1144         self.verify_orig_dhcp_discover(rx[0], self.pg2, hostname,
1145                                        self.pg2.local_mac)
1146
1147         self.pg2.add_stream(p_offer)
1148         self.pg_enable_capture(self.pg_interfaces)
1149         self.pg_start()
1150
1151         rx = self.pg2.get_capture(1)
1152         self.verify_orig_dhcp_request(rx[0], self.pg2, hostname,
1153                                       self.pg2.local_ip4)
1154
1155         #
1156         # unicast the ack to the offered address
1157         #
1158         p_ack = (Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac) /
1159                  IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) /
1160                  UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1161                  BOOTP(op=1, yiaddr=self.pg2.local_ip4) /
1162                  DHCP(options=[('message-type', 'ack'),
1163                                ('subnet_mask', "255.255.255.0"),
1164                                ('router', self.pg2.remote_ip4),
1165                                ('server_id', self.pg2.remote_ip4),
1166                                ('lease_time', 43200),
1167                                ('end')]))
1168
1169         self.pg2.add_stream(p_ack)
1170         self.pg_enable_capture(self.pg_interfaces)
1171         self.pg_start()
1172
1173         #
1174         # At the end of this procedure there should be a connected route
1175         # in the FIB
1176         #
1177         self.assertTrue(find_route(self, self.pg2.local_ip4, 32))
1178         self.assertTrue(find_route(self, self.pg2.local_ip4, 24))
1179
1180         #
1181         # remove the DHCP config
1182         #
1183         self.vapi.dhcp_client(self.pg2.sw_if_index, hostname, is_add=0)
1184
1185         self.assertFalse(find_route(self, self.pg2.local_ip4, 32))
1186         self.assertFalse(find_route(self, self.pg2.local_ip4, 24))
1187
1188
1189 if __name__ == '__main__':
1190     unittest.main(testRunner=VppTestRunner)