IP Multicast FIB (mfib)
[vpp.git] / test / test_dhcp.py
1 #!/usr/bin/env python
2
3 import unittest
4 import socket
5
6 from framework import VppTestCase, VppTestRunner
7
8 from scapy.layers.l2 import Ether, getmacbyip
9 from scapy.layers.inet import IP, UDP, ICMP
10 from scapy.layers.inet6 import IPv6, in6_getnsmac, in6_mactoifaceid
11 from scapy.layers.dhcp import DHCP, BOOTP, DHCPTypes
12 from scapy.layers.dhcp6 import DHCP6, DHCP6_Solicit, DHCP6_RelayForward, \
13     DHCP6_RelayReply, DHCP6_Advertise, DHCP6OptRelayMsg, DHCP6OptIfaceId, \
14     DHCP6OptStatusCode, DHCP6OptVSS, DHCP6OptClientLinkLayerAddr
15 from socket import AF_INET, AF_INET6
16 from scapy.utils import inet_pton, inet_ntop
17 from scapy.utils6 import in6_ptop
18
19 DHCP4_CLIENT_PORT = 68
20 DHCP4_SERVER_PORT = 67
21 DHCP6_CLIENT_PORT = 547
22 DHCP6_SERVER_PORT = 546
23
24
25 def mk_ll_addr(mac):
26
27     euid = in6_mactoifaceid(mac)
28     addr = "fe80::" + euid
29     return addr
30
31
32 class TestDHCP(VppTestCase):
33     """ DHCP Test Case """
34
35     def setUp(self):
36         super(TestDHCP, self).setUp()
37
38         # create 3 pg interfaces
39         self.create_pg_interfaces(range(4))
40
41         # pg0 and 1 are IP configured in VRF 0 and 1.
42         # pg2 and 3 are non IP-configured in VRF 0 and 1
43         table_id = 0
44         for i in self.pg_interfaces[:2]:
45             i.admin_up()
46             i.set_table_ip4(table_id)
47             i.set_table_ip6(table_id)
48             i.config_ip4()
49             i.resolve_arp()
50             i.config_ip6()
51             i.resolve_ndp()
52             table_id += 1
53
54         table_id = 0
55         for i in self.pg_interfaces[2:]:
56             i.admin_up()
57             i.set_table_ip4(table_id)
58             i.set_table_ip6(table_id)
59             table_id += 1
60
61     def send_and_assert_no_replies(self, intf, pkts, remark):
62         intf.add_stream(pkts)
63         self.pg_enable_capture(self.pg_interfaces)
64         self.pg_start()
65         for i in self.pg_interfaces:
66             i.assert_nothing_captured(remark=remark)
67
68     def validate_option_82(self, pkt, intf, ip_addr):
69         dhcp = pkt[DHCP]
70         found = 0
71         data = []
72
73         for i in dhcp.options:
74             if type(i) is tuple:
75                 if i[0] == "relay_agent_Information":
76                     #
77                     # There are two sb-options present - each of length 6.
78                     #
79                     data = i[1]
80                     self.assertEqual(len(data), 12)
81
82                     #
83                     # First sub-option is ID 1, len 4, then encoded
84                     #  sw_if_index. This test uses low valued indicies
85                     # so [2:4] are 0.
86                     # The ID space is VPP internal - so no matching value
87                     # scapy
88                     #
89                     self.assertEqual(ord(data[0]), 1)
90                     self.assertEqual(ord(data[1]), 4)
91                     self.assertEqual(ord(data[2]), 0)
92                     self.assertEqual(ord(data[3]), 0)
93                     self.assertEqual(ord(data[4]), 0)
94                     self.assertEqual(ord(data[5]), intf._sw_if_index)
95
96                     #
97                     # next sub-option is the IP address of the client side
98                     # interface.
99                     # sub-option ID=5, length (of a v4 address)=4
100                     #
101                     claddr = socket.inet_pton(AF_INET, ip_addr)
102
103                     self.assertEqual(ord(data[6]), 5)
104                     self.assertEqual(ord(data[7]), 4)
105                     self.assertEqual(data[8], claddr[0])
106                     self.assertEqual(data[9], claddr[1])
107                     self.assertEqual(data[10], claddr[2])
108                     self.assertEqual(data[11], claddr[3])
109
110                     found = 1
111         self.assertTrue(found)
112
113         return data
114
115     def verify_dhcp_offer(self, pkt, intf, check_option_82=True):
116         ether = pkt[Ether]
117         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
118         self.assertEqual(ether.src, intf.local_mac)
119
120         ip = pkt[IP]
121         self.assertEqual(ip.dst, "255.255.255.255")
122         self.assertEqual(ip.src, intf.local_ip4)
123
124         udp = pkt[UDP]
125         self.assertEqual(udp.dport, DHCP4_CLIENT_PORT)
126         self.assertEqual(udp.sport, DHCP4_SERVER_PORT)
127
128         dhcp = pkt[DHCP]
129         is_offer = False
130         for o in dhcp.options:
131             if type(o) is tuple:
132                 if o[0] == "message-type" \
133                    and DHCPTypes[o[1]] == "offer":
134                     is_offer = True
135         self.assertTrue(is_offer)
136
137         if check_option_82:
138             data = self.validate_option_82(pkt, intf, intf.local_ip4)
139
140     def verify_dhcp_discover(self, pkt, intf, src_intf=None,
141                              option_82_present=True):
142         ether = pkt[Ether]
143         self.assertEqual(ether.dst, intf.remote_mac)
144         self.assertEqual(ether.src, intf.local_mac)
145
146         ip = pkt[IP]
147         self.assertEqual(ip.dst, intf.remote_ip4)
148         self.assertEqual(ip.src, intf.local_ip4)
149
150         udp = pkt[UDP]
151         self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
152         self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
153
154         dhcp = pkt[DHCP]
155
156         is_discover = False
157         for o in dhcp.options:
158             if type(o) is tuple:
159                 if o[0] == "message-type" \
160                    and DHCPTypes[o[1]] == "discover":
161                     is_discover = True
162         self.assertTrue(is_discover)
163
164         if option_82_present:
165             data = self.validate_option_82(pkt, src_intf, src_intf.local_ip4)
166             return data
167         else:
168             for i in dhcp.options:
169                 if type(i) is tuple:
170                     self.assertNotEqual(i[0], "relay_agent_Information")
171
172     def verify_dhcp6_solicit(self, pkt, intf,
173                              peer_ip, peer_mac,
174                              fib_id=0,
175                              oui=0):
176         ether = pkt[Ether]
177         self.assertEqual(ether.dst, intf.remote_mac)
178         self.assertEqual(ether.src, intf.local_mac)
179
180         ip = pkt[IPv6]
181         self.assertEqual(in6_ptop(ip.dst), in6_ptop(intf.remote_ip6))
182         self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
183
184         udp = pkt[UDP]
185         self.assertEqual(udp.dport, DHCP6_CLIENT_PORT)
186         self.assertEqual(udp.sport, DHCP6_SERVER_PORT)
187
188         relay = pkt[DHCP6_RelayForward]
189         self.assertEqual(in6_ptop(relay.peeraddr), in6_ptop(peer_ip))
190         oid = pkt[DHCP6OptIfaceId]
191         cll = pkt[DHCP6OptClientLinkLayerAddr]
192         self.assertEqual(cll.optlen, 8)
193         self.assertEqual(cll.lltype, 1)
194         self.assertEqual(cll.clladdr, peer_mac)
195
196         vss = pkt[DHCP6OptVSS]
197         self.assertEqual(vss.optlen, 8)
198         self.assertEqual(vss.type, 1)
199         # the OUI and FIB-id are really 3 and 4 bytes resp.
200         # but the tested range is small
201         self.assertEqual(ord(vss.data[0]), 0)
202         self.assertEqual(ord(vss.data[1]), 0)
203         self.assertEqual(ord(vss.data[2]), oui)
204         self.assertEqual(ord(vss.data[3]), 0)
205         self.assertEqual(ord(vss.data[4]), 0)
206         self.assertEqual(ord(vss.data[5]), 0)
207         self.assertEqual(ord(vss.data[6]), fib_id)
208
209         # the relay message should be an encoded Solicit
210         msg = pkt[DHCP6OptRelayMsg]
211         sol = DHCP6_Solicit()
212         self.assertEqual(msg.optlen, len(str(sol)))
213         self.assertEqual(str(sol), (str(msg[1]))[:msg.optlen])
214
215     def verify_dhcp6_advert(self, pkt, intf, peer):
216         ether = pkt[Ether]
217         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
218         self.assertEqual(ether.src, intf.local_mac)
219
220         ip = pkt[IPv6]
221         self.assertEqual(in6_ptop(ip.dst), in6_ptop(peer))
222         self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
223
224         udp = pkt[UDP]
225         self.assertEqual(udp.dport, DHCP6_SERVER_PORT)
226         self.assertEqual(udp.sport, DHCP6_CLIENT_PORT)
227
228         # not sure why this is not decoding
229         # adv = pkt[DHCP6_Advertise]
230
231     def test_dhcp_proxy(self):
232         """ DHCPv4 Proxy """
233
234         #
235         # Verify no response to DHCP request without DHCP config
236         #
237         p_disc_vrf0 = (Ether(dst="ff:ff:ff:ff:ff:ff",
238                              src=self.pg2.remote_mac) /
239                        IP(src="0.0.0.0", dst="255.255.255.255") /
240                        UDP(sport=DHCP4_CLIENT_PORT,
241                            dport=DHCP4_SERVER_PORT) /
242                        BOOTP(op=1) /
243                        DHCP(options=[('message-type', 'discover'), ('end')]))
244         pkts_disc_vrf0 = [p_disc_vrf0]
245         p_disc_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
246                              src=self.pg3.remote_mac) /
247                        IP(src="0.0.0.0", dst="255.255.255.255") /
248                        UDP(sport=DHCP4_CLIENT_PORT,
249                            dport=DHCP4_SERVER_PORT) /
250                        BOOTP(op=1) /
251                        DHCP(options=[('message-type', 'discover'), ('end')]))
252         pkts_disc_vrf1 = [p_disc_vrf0]
253
254         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
255                                         "DHCP with no configuration")
256         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
257                                         "DHCP with no configuration")
258
259         #
260         # Enable DHCP proxy in VRF 0
261         #
262         server_addr = self.pg0.remote_ip4n
263         src_addr = self.pg0.local_ip4n
264
265         self.vapi.dhcp_proxy_config(server_addr,
266                                     src_addr,
267                                     rx_table_id=0)
268
269         #
270         # Now a DHCP request on pg2, which is in the same VRF
271         # as the DHCP config, will result in a relayed DHCP
272         # message to the [fake] server
273         #
274         self.pg2.add_stream(pkts_disc_vrf0)
275         self.pg_enable_capture(self.pg_interfaces)
276         self.pg_start()
277
278         rx = self.pg0.get_capture(1)
279         rx = rx[0]
280
281         #
282         # Rx'd packet should be to the server address and from the configured
283         # source address
284         # UDP source ports are unchanged
285         # we've no option 82 config so that should be absent
286         #
287         self.verify_dhcp_discover(rx, self.pg0, option_82_present=False)
288
289         #
290         # Inject a response from the server
291         #  VPP will only relay the offer if option 82 is present.
292         #  so this one is dropped
293         #
294         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
295              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
296              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
297              BOOTP(op=1) /
298              DHCP(options=[('message-type', 'offer'), ('end')]))
299         pkts = [p]
300
301         self.send_and_assert_no_replies(self.pg0, pkts,
302                                         "DHCP offer no option 82")
303
304         #
305         # Configure sending option 82 in relayed messages
306         #
307         self.vapi.dhcp_proxy_config(server_addr,
308                                     src_addr,
309                                     rx_table_id=0,
310                                     insert_circuit_id=1)
311
312         #
313         # Send a request:
314         #  again dropped, but ths time because there is no IP addrees on the
315         #  clinet interfce to fill in the option.
316         #
317         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
318                                         "DHCP no relay address")
319
320         #
321         # configure an IP address on the client facing interface
322         #
323         self.pg2.config_ip4()
324
325         #
326         # Try again with a discover packet
327         # Rx'd packet should be to the server address and from the configured
328         # source address
329         # UDP source ports are unchanged
330         # we've no option 82 config so that should be absent
331         #
332         self.pg2.add_stream(pkts_disc_vrf0)
333         self.pg_enable_capture(self.pg_interfaces)
334         self.pg_start()
335
336         rx = self.pg0.get_capture(1)
337         rx = rx[0]
338
339         option_82 = self.verify_dhcp_discover(rx, self.pg0, src_intf=self.pg2)
340
341         #
342         # Create an DHCP offer reply from the server with a correctly formatted
343         # option 82. i.e. send back what we just captured
344         # The offer, sent mcast to the client, still has option 82.
345         #
346         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
347              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
348              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
349              BOOTP(op=1) /
350              DHCP(options=[('message-type', 'offer'),
351                            ('relay_agent_Information', option_82),
352                            ('end')]))
353         pkts = [p]
354
355         self.pg0.add_stream(pkts)
356         self.pg_enable_capture(self.pg_interfaces)
357         self.pg_start()
358
359         rx = self.pg2.get_capture(1)
360         rx = rx[0]
361
362         self.verify_dhcp_offer(rx, self.pg2)
363
364         #
365         # Bogus Option 82:
366         #
367         # 1. not our IP address = not checked by VPP? so offer is replayed
368         #    to client
369         bad_ip = option_82[0:8] + chr(33) + option_82[9:]
370
371         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
372              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
373              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
374              BOOTP(op=1) /
375              DHCP(options=[('message-type', 'offer'),
376                            ('relay_agent_Information', bad_ip),
377                            ('end')]))
378         pkts = [p]
379
380         self.pg0.add_stream(pkts)
381         self.pg_enable_capture(self.pg_interfaces)
382         self.pg_start()
383         rx = self.pg2.get_capture(1)
384         rx = rx[0]
385
386         self.verify_dhcp_offer(rx, self.pg2, check_option_82=False)
387         self.pg0.assert_nothing_captured(remark="")
388
389         # 2. Not a sw_if_index VPP knows
390         bad_if_index = option_82[0:2] + chr(33) + option_82[3:]
391
392         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
393              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
394              UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
395              BOOTP(op=1) /
396              DHCP(options=[('message-type', 'offer'),
397                            ('relay_agent_Information', bad_if_index),
398                            ('end')]))
399         pkts = [p]
400         self.send_and_assert_no_replies(self.pg0, pkts,
401                                         "DHCP offer option 82 bad if index")
402
403         #
404         # Send a DHCP request in VRF 1. should be dropped.
405         #
406         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
407                                         "DHCP with no configuration VRF 1")
408
409         #
410         # Delete the DHCP config in VRF 0
411         # Should now drop requests.
412         #
413         self.vapi.dhcp_proxy_config(server_addr,
414                                     src_addr,
415                                     rx_table_id=0,
416                                     is_add=0,
417                                     insert_circuit_id=1)
418
419         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
420                                         "DHCP config removed VRF 0")
421         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
422                                         "DHCP config removed VRF 1")
423
424         #
425         # Add DHCP config for VRF 1
426         #
427         server_addr = self.pg1.remote_ip4n
428         src_addr = self.pg1.local_ip4n
429         self.vapi.dhcp_proxy_config(server_addr,
430                                     src_addr,
431                                     rx_table_id=1,
432                                     server_table_id=1,
433                                     insert_circuit_id=1)
434
435         #
436         # Confim DHCP requests ok in VRF 1.
437         #  - dropped on IP config on client interface
438         #
439         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
440                                         "DHCP config removed VRF 1")
441
442         #
443         # configure an IP address on the client facing interface
444         #
445         self.pg3.config_ip4()
446
447         self.pg3.add_stream(pkts_disc_vrf1)
448         self.pg_enable_capture(self.pg_interfaces)
449         self.pg_start()
450
451         rx = self.pg1.get_capture(1)
452         rx = rx[0]
453         self.verify_dhcp_discover(rx, self.pg1, src_intf=self.pg3)
454
455         #
456         # remove DHCP config to cleanup
457         #
458         self.vapi.dhcp_proxy_config(server_addr,
459                                     src_addr,
460                                     rx_table_id=1,
461                                     server_table_id=1,
462                                     insert_circuit_id=1,
463                                     is_add=0)
464
465         self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0,
466                                         "DHCP cleanup VRF 0")
467         self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1,
468                                         "DHCP cleanup VRF 1")
469
470     def test_dhcp6_proxy(self):
471         """ DHCPv6 Proxy"""
472         #
473         # Verify no response to DHCP request without DHCP config
474         #
475         dhcp_solicit_dst = "ff02::1:2"
476         dhcp_solicit_src_vrf0 = mk_ll_addr(self.pg2.remote_mac)
477         dhcp_solicit_src_vrf1 = mk_ll_addr(self.pg3.remote_mac)
478         server_addr_vrf0 = self.pg0.remote_ip6n
479         src_addr_vrf0 = self.pg0.local_ip6n
480         server_addr_vrf1 = self.pg1.remote_ip6n
481         src_addr_vrf1 = self.pg1.local_ip6n
482
483         dmac = in6_getnsmac(inet_pton(socket.AF_INET6, dhcp_solicit_dst))
484         p_solicit_vrf0 = (Ether(dst=dmac, src=self.pg2.remote_mac) /
485                           IPv6(src=dhcp_solicit_src_vrf0,
486                                dst=dhcp_solicit_dst) /
487                           UDP(sport=DHCP6_SERVER_PORT,
488                               dport=DHCP6_CLIENT_PORT) /
489                           DHCP6_Solicit())
490         pkts_solicit_vrf0 = [p_solicit_vrf0]
491         p_solicit_vrf1 = (Ether(dst=dmac, src=self.pg3.remote_mac) /
492                           IPv6(src=dhcp_solicit_src_vrf1,
493                                dst=dhcp_solicit_dst) /
494                           UDP(sport=DHCP6_SERVER_PORT,
495                               dport=DHCP6_CLIENT_PORT) /
496                           DHCP6_Solicit())
497         pkts_solicit_vrf1 = [p_solicit_vrf1]
498
499         self.send_and_assert_no_replies(self.pg2, pkts_solicit_vrf0,
500                                         "DHCP with no configuration")
501         self.send_and_assert_no_replies(self.pg3, pkts_solicit_vrf1,
502                                         "DHCP with no configuration")
503
504         #
505         # DHCPv6 config in VRF 0.
506         # Packets still dropped because the client facing interface has no
507         # IPv6 config
508         #
509         self.vapi.dhcp_proxy_config(server_addr_vrf0,
510                                     src_addr_vrf0,
511                                     rx_table_id=0,
512                                     server_table_id=0,
513                                     insert_circuit_id=1,
514                                     is_ipv6=1)
515
516         self.send_and_assert_no_replies(self.pg2, pkts_solicit_vrf0,
517                                         "DHCP with no configuration")
518         self.send_and_assert_no_replies(self.pg3, pkts_solicit_vrf1,
519                                         "DHCP with no configuration")
520
521         #
522         # configure an IP address on the client facing interface
523         #
524         self.pg2.config_ip6()
525
526         #
527         # Now the DHCP requests are relayed to the server
528         #
529         self.pg2.add_stream(pkts_solicit_vrf0)
530         self.pg_enable_capture(self.pg_interfaces)
531         self.pg_start()
532
533         rx = self.pg0.get_capture(1)
534         rx = rx[0]
535         self.verify_dhcp6_solicit(rx, self.pg0,
536                                   dhcp_solicit_src_vrf0,
537                                   self.pg2.remote_mac)
538
539         #
540         # Exception cases for rejected relay responses
541         #
542
543         # 1 - not a relay reply
544         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
545                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
546                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
547                       DHCP6_Advertise())
548         pkts_adv_vrf0 = [p_adv_vrf0]
549         self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0,
550                                         "DHCP6 not a relay reply")
551
552         # 2 - no relay message option
553         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
554                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
555                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
556                       DHCP6_RelayReply() /
557                       DHCP6_Advertise())
558         pkts_adv_vrf0 = [p_adv_vrf0]
559         self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0,
560                                         "DHCP not a relay message")
561
562         # 3 - no circuit ID
563         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
564                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
565                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
566                       DHCP6_RelayReply() /
567                       DHCP6OptRelayMsg(optlen=0) /
568                       DHCP6_Advertise())
569         pkts_adv_vrf0 = [p_adv_vrf0]
570         self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0,
571                                         "DHCP6 no circuit ID")
572         # 4 - wrong circuit ID
573         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
574                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
575                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
576                       DHCP6_RelayReply() /
577                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
578                       DHCP6OptRelayMsg(optlen=0) /
579                       DHCP6_Advertise())
580         pkts_adv_vrf0 = [p_adv_vrf0]
581         self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0,
582                                         "DHCP6 wrong circuit ID")
583
584         #
585         # Send the relay response (the advertisement)
586         #   - no peer address
587         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
588                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
589                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
590                       DHCP6_RelayReply() /
591                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x03') /
592                       DHCP6OptRelayMsg(optlen=0) /
593                       DHCP6_Advertise(trid=1) /
594                       DHCP6OptStatusCode(statuscode=0))
595         pkts_adv_vrf0 = [p_adv_vrf0]
596
597         self.pg0.add_stream(pkts_adv_vrf0)
598         self.pg_enable_capture(self.pg_interfaces)
599         self.pg_start()
600
601         rx = self.pg2.get_capture(1)
602         rx = rx[0]
603         self.verify_dhcp6_advert(rx, self.pg2, "::")
604
605         #
606         # Send the relay response (the advertisement)
607         #   - with peer address
608         p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
609                       IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
610                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
611                       DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf0) /
612                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x03') /
613                       DHCP6OptRelayMsg(optlen=0) /
614                       DHCP6_Advertise(trid=1) /
615                       DHCP6OptStatusCode(statuscode=0))
616         pkts_adv_vrf0 = [p_adv_vrf0]
617
618         self.pg0.add_stream(pkts_adv_vrf0)
619         self.pg_enable_capture(self.pg_interfaces)
620         self.pg_start()
621
622         rx = self.pg2.get_capture(1)
623         rx = rx[0]
624         self.verify_dhcp6_advert(rx, self.pg2, dhcp_solicit_src_vrf0)
625
626         #
627         # Add all the config for VRF 1
628         #
629         self.vapi.dhcp_proxy_config(server_addr_vrf1,
630                                     src_addr_vrf1,
631                                     rx_table_id=1,
632                                     server_table_id=1,
633                                     insert_circuit_id=1,
634                                     is_ipv6=1)
635         self.pg3.config_ip6()
636
637         #
638         # VRF 1 solicit
639         #
640         self.pg3.add_stream(pkts_solicit_vrf1)
641         self.pg_enable_capture(self.pg_interfaces)
642         self.pg_start()
643
644         rx = self.pg1.get_capture(1)
645         rx = rx[0]
646         self.verify_dhcp6_solicit(rx, self.pg1,
647                                   dhcp_solicit_src_vrf1,
648                                   self.pg3.remote_mac)
649
650         #
651         # VRF 1 Advert
652         #
653         p_adv_vrf1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
654                       IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) /
655                       UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
656                       DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
657                       DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
658                       DHCP6OptRelayMsg(optlen=0) /
659                       DHCP6_Advertise(trid=1) /
660                       DHCP6OptStatusCode(statuscode=0))
661         pkts_adv_vrf1 = [p_adv_vrf1]
662
663         self.pg1.add_stream(pkts_adv_vrf1)
664         self.pg_enable_capture(self.pg_interfaces)
665         self.pg_start()
666
667         rx = self.pg3.get_capture(1)
668         rx = rx[0]
669         self.verify_dhcp6_advert(rx, self.pg3, dhcp_solicit_src_vrf1)
670
671         #
672         # Add VSS config
673         #  table=1, fib=id=1, oui=4
674         self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1)
675
676         self.pg3.add_stream(pkts_solicit_vrf1)
677         self.pg_enable_capture(self.pg_interfaces)
678         self.pg_start()
679
680         rx = self.pg1.get_capture(1)
681         rx = rx[0]
682         self.verify_dhcp6_solicit(rx, self.pg1,
683                                   dhcp_solicit_src_vrf1,
684                                   self.pg3.remote_mac,
685                                   fib_id=1,
686                                   oui=4)
687
688         #
689         # Remove the VSS config
690         #  relayed DHCP has default vlaues in the option.
691         #
692         self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1, is_add=0)
693
694         self.pg3.add_stream(pkts_solicit_vrf1)
695         self.pg_enable_capture(self.pg_interfaces)
696         self.pg_start()
697
698         rx = self.pg1.get_capture(1)
699         rx = rx[0]
700         self.verify_dhcp6_solicit(rx, self.pg1,
701                                   dhcp_solicit_src_vrf1,
702                                   self.pg3.remote_mac)
703
704         #
705         # Cleanup
706         #
707         self.vapi.dhcp_proxy_config(server_addr_vrf1,
708                                     src_addr_vrf1,
709                                     rx_table_id=1,
710                                     server_table_id=1,
711                                     insert_circuit_id=1,
712                                     is_ipv6=1,
713                                     is_add=0)
714         self.vapi.dhcp_proxy_config(server_addr_vrf1,
715                                     src_addr_vrf1,
716                                     rx_table_id=0,
717                                     server_table_id=0,
718                                     insert_circuit_id=1,
719                                     is_ipv6=1,
720                                     is_add=0)
721
722 if __name__ == '__main__':
723     unittest.main(testRunner=VppTestRunner)