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