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