dhcp: multiple additions
[vpp.git] / src / vnet / dhcpv6 / proxy_node.c
1 /*
2  * proxy_node.c: dhcpv6 proxy node processing
3  *
4  * Copyright (c) 2013 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vlib/vlib.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/dhcpv6/proxy.h>
21 #include <vnet/dhcp/proxy.h>
22 #include <vnet/fib/ip6_fib.h>
23 #include <vnet/mfib/mfib_table.h>
24 #include <vnet/mfib/ip6_mfib.h>
25
26 static char * dhcpv6_proxy_error_strings[] = {
27 #define dhcpv6_proxy_error(n,s) s,
28 #include "proxy_error.def"
29 #undef dhcpv6_proxy_error
30 };
31
32 #define foreach_dhcpv6_proxy_to_server_input_next \
33   _ (DROP, "error-drop")                        \
34   _ (LOOKUP, "ip6-lookup")                      \
35   _ (SEND_TO_CLIENT, "dhcpv6-proxy-to-client")
36
37
38 typedef enum {
39 #define _(s,n) DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_##s,
40   foreach_dhcpv6_proxy_to_server_input_next
41 #undef _
42   DHCPV6_PROXY_TO_SERVER_INPUT_N_NEXT,
43 } dhcpv6_proxy_to_server_input_next_t;
44
45 typedef struct {
46   /* 0 => to server, 1 => to client */
47   int which;
48   u8 packet_data[64];
49   u32 error;
50   u32 sw_if_index;
51   u32 original_sw_if_index;
52 } dhcpv6_proxy_trace_t;
53
54 vlib_node_registration_t dhcpv6_proxy_to_server_node;
55 vlib_node_registration_t dhcpv6_proxy_to_client_node;
56
57
58 u8 * format_dhcpv6_proxy_trace (u8 * s, va_list * args)
59 {
60   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
61   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
62   dhcpv6_proxy_trace_t * t = va_arg (*args, dhcpv6_proxy_trace_t *);
63
64   if (t->which == 0)
65     s = format (s, "DHCPV6 proxy: sent to server %U",
66                 format_ip6_address, &t->packet_data, sizeof (ip6_address_t));
67   else
68     s = format (s, "DHCPV6 proxy: sent to client from %U",
69                 format_ip6_address, &t->packet_data, sizeof (ip6_address_t));
70   if (t->error != (u32)~0)
71     s = format (s, " error: %s\n", dhcpv6_proxy_error_strings[t->error]);
72
73   s = format (s, "  original_sw_if_index: %d, sw_if_index: %d\n",
74               t->original_sw_if_index, t->sw_if_index);
75
76   return s;
77 }
78
79 u8 * format_dhcpv6_proxy_header_with_length (u8 * s, va_list * args)
80 {
81   dhcpv6_header_t * h = va_arg (*args, dhcpv6_header_t *);
82   u32 max_header_bytes = va_arg (*args, u32);
83   u32 header_bytes;
84
85   header_bytes = sizeof (h[0]);
86   if (max_header_bytes != 0 && header_bytes > max_header_bytes)
87     return format (s, "dhcpv6 header truncated");
88
89   s = format (s, "DHCPV6 Proxy");
90
91   return s;
92 }
93 /* get first interface address */
94 static ip6_address_t *
95 ip6_interface_first_global_or_site_address (ip6_main_t * im, u32 sw_if_index)
96 {
97   ip_lookup_main_t * lm = &im->lookup_main;
98   ip_interface_address_t * ia = 0;
99   ip6_address_t * result = 0;
100
101   foreach_ip_interface_address (lm, ia, sw_if_index,
102                                 1 /* honor unnumbered */,
103   ({
104     ip6_address_t * a = ip_interface_address_get_address (lm, ia);
105     if ((a->as_u8[0] & 0xe0) == 0x20 ||
106         (a->as_u8[0] & 0xfe) == 0xfc)  {
107         result = a;
108         break;
109     }
110   }));
111   return result;
112 }
113
114 static inline void copy_ip6_address (ip6_address_t *dst, ip6_address_t *src) 
115 {
116
117   dst->as_u64[0] = src->as_u64[0];
118   dst->as_u64[1] = src->as_u64[1];
119
120
121 static inline dhcpv6_vss_info *
122 dhcpv6_get_vss_info (dhcpv6_proxy_main_t *dm,
123                      u32 rx_fib_index)
124 {
125   dhcpv6_vss_info *v;
126
127   if (vec_len(dm->vss_index_by_rx_fib_index) <= rx_fib_index ||
128       dm->vss_index_by_rx_fib_index[rx_fib_index] == ~0)
129   {
130       v = NULL;
131   }
132   else
133   {
134       v = pool_elt_at_index (dm->vss,
135                              dm->vss_index_by_rx_fib_index[rx_fib_index]);
136   }
137
138   return (v);
139 }
140
141 static inline dhcpv6_server_t *
142 dhcpv6_get_server (dhcpv6_proxy_main_t *dm,
143                    u32 rx_fib_index)
144 {
145   dhcpv6_server_t *s = NULL;
146
147   if (vec_len(dm->dhcp6_server_index_by_rx_fib_index) > rx_fib_index &&
148       dm->dhcp6_server_index_by_rx_fib_index[rx_fib_index] != ~0)
149   {
150       s = pool_elt_at_index (dm->dhcp6_servers,
151                              dm->dhcp6_server_index_by_rx_fib_index[rx_fib_index]);
152   }
153
154   return (s);
155 }
156
157 static uword
158 dhcpv6_proxy_to_server_input (vlib_main_t * vm,
159                             vlib_node_runtime_t * node,
160                             vlib_frame_t * from_frame)
161 {
162   u32 n_left_from, next_index, * from, * to_next;
163   dhcpv6_proxy_main_t * dpm = &dhcpv6_proxy_main;
164   from = vlib_frame_vector_args (from_frame);
165   n_left_from = from_frame->n_vectors;
166   u32 pkts_to_server=0, pkts_to_client=0, pkts_no_server=0;
167   u32 pkts_no_interface_address=0, pkts_no_exceeding_max_hop=0;
168   u32 pkts_no_src_address=0;
169   u32 pkts_wrong_msg_type=0;
170   u32 pkts_too_big=0;
171   ip6_main_t * im = &ip6_main;
172   ip6_address_t * src;
173   int bogus_length;
174   dhcpv6_server_t * server;
175   u32  rx_fib_idx = 0, server_fib_idx = 0;
176
177   next_index = node->cached_next_index;
178
179   while (n_left_from > 0)
180     {
181       u32 n_left_to_next;
182
183       vlib_get_next_frame (vm, node, next_index,
184                            to_next, n_left_to_next);
185
186       while (n_left_from > 0 && n_left_to_next > 0)
187         {
188           vnet_main_t *vnm = vnet_get_main();
189           u32 sw_if_index = 0;
190           u32 rx_sw_if_index = 0;
191           vnet_sw_interface_t *swif;
192           u32 bi0;
193           vlib_buffer_t * b0;
194           udp_header_t * u0, *u1;
195           dhcpv6_header_t * h0;  // client msg hdr
196           ip6_header_t * ip0, *ip1;
197           ip6_address_t _ia0, *ia0=&_ia0;
198           u32 next0;
199           u32 error0 = (u32) ~0;
200           dhcpv6_option_t *fwd_opt;
201           dhcpv6_relay_hdr_t *r1;
202           u16 len;
203           dhcpv6_int_id_t *id1;
204           dhcpv6_vss_t *vss1;
205           dhcpv6_client_mac_t *cmac; // client mac
206           ethernet_header_t * e_h0;
207           u8 client_src_mac[6];
208           vlib_buffer_free_list_t *fl;
209           dhcpv6_vss_info *vss;
210
211           bi0 = from[0];
212           to_next[0] = bi0;
213           from += 1;
214           to_next += 1;
215           n_left_from -= 1;
216           n_left_to_next -= 1;
217
218           b0 = vlib_get_buffer (vm, bi0);
219
220           h0 = vlib_buffer_get_current (b0);
221
222           /*
223            * udp_local hands us the DHCPV6 header.
224            */
225           u0 = (void *)h0 -(sizeof(*u0));
226           ip0 = (void *)u0 -(sizeof(*ip0));
227           e_h0 = (void *)ip0 - ethernet_buffer_header_size(b0);
228
229           clib_memcpy(client_src_mac, e_h0->src_address, 6);
230
231           switch (h0->u.msg_type) {
232             case DHCPV6_MSG_SOLICIT:
233             case DHCPV6_MSG_REQUEST:
234             case DHCPV6_MSG_CONFIRM:
235             case DHCPV6_MSG_RENEW:
236             case DHCPV6_MSG_REBIND:
237             case DHCPV6_MSG_RELEASE:
238             case DHCPV6_MSG_DECLINE:
239             case DHCPV6_MSG_INFORMATION_REQUEST:
240             case DHCPV6_MSG_RELAY_FORW:
241                 /* send to server */
242                 break;
243             case DHCPV6_MSG_RELAY_REPL:
244                 /* send to client */
245                 next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_SEND_TO_CLIENT;
246                 error0 = 0;
247                 pkts_to_client++;
248                 goto do_enqueue;
249             default:
250                 /* drop the packet */
251                 pkts_wrong_msg_type++;
252                 error0 = DHCPV6_PROXY_ERROR_WRONG_MESSAGE_TYPE;
253                 next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP;
254                 goto do_trace;
255
256           }
257
258           /* Send to DHCPV6 server via the configured FIB */
259           rx_sw_if_index = sw_if_index =  vnet_buffer(b0)->sw_if_index[VLIB_RX];
260           rx_fib_idx = im->fib_index_by_sw_if_index [rx_sw_if_index];
261           server = dhcpv6_get_server(dpm, rx_fib_idx);
262
263           if (PREDICT_FALSE (NULL == server))
264           {
265               error0 = DHCPV6_PROXY_ERROR_NO_SERVER;
266               next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP;
267               pkts_no_server++;
268               goto do_trace;
269           }
270
271           server_fib_idx = server->server_fib6_index;
272           vnet_buffer(b0)->sw_if_index[VLIB_TX] = server_fib_idx;
273
274
275           /* relay-option header pointer */
276           vlib_buffer_advance(b0, -(sizeof(*fwd_opt)));
277           fwd_opt = vlib_buffer_get_current(b0);
278           /* relay message header pointer */
279           vlib_buffer_advance(b0, -(sizeof(*r1)));
280           r1 = vlib_buffer_get_current(b0);
281
282           vlib_buffer_advance(b0, -(sizeof(*u1)));
283           u1 = vlib_buffer_get_current(b0);
284
285           vlib_buffer_advance(b0, -(sizeof(*ip1)));
286           ip1 = vlib_buffer_get_current(b0);
287
288           /* fill in all that rubbish... */
289           len = clib_net_to_host_u16(u0->length) - sizeof(udp_header_t);
290           copy_ip6_address(&r1->peer_addr, &ip0->src_address);
291
292           r1->msg_type = DHCPV6_MSG_RELAY_FORW;
293           fwd_opt->length = clib_host_to_net_u16(len);
294           fwd_opt->option = clib_host_to_net_u16(DHCPV6_OPTION_RELAY_MSG);
295
296           r1->hop_count++;
297           r1->hop_count = (h0->u.msg_type != DHCPV6_MSG_RELAY_FORW) ? 0 : r1->hop_count;
298
299           if (PREDICT_FALSE(r1->hop_count >= HOP_COUNT_LIMIT))
300             {
301               error0 =  DHCPV6_RELAY_PKT_DROP_MAX_HOPS;
302               next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP;
303               pkts_no_exceeding_max_hop++;
304               goto do_trace;
305             }
306
307
308           /* If relay-fwd and src address is site or global unicast address  */
309           if (h0->u.msg_type == DHCPV6_MSG_RELAY_FORW &&
310               ((ip0->src_address.as_u8[0] & 0xe0) == 0x20 ||
311                (ip0->src_address.as_u8[0] & 0xfe) == 0xfc))
312             {
313               /* Set link address to zero */
314               r1->link_addr.as_u64[0] = 0;
315               r1->link_addr.as_u64[1] = 0;
316               goto link_address_set;
317             }
318
319           /* if receiving interface is unnumbered, use receiving interface
320            * IP address as link address, otherwise use the loopback interface
321            * IP address as link address.
322            */
323
324           swif = vnet_get_sw_interface (vnm, rx_sw_if_index);
325           if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
326               sw_if_index = swif->unnumbered_sw_if_index;
327
328           ia0 = ip6_interface_first_global_or_site_address(&ip6_main, sw_if_index);
329           if (ia0 == 0)
330             {
331               error0 = DHCPV6_PROXY_ERROR_NO_INTERFACE_ADDRESS;
332               next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP;
333               pkts_no_interface_address++;
334               goto do_trace;
335             }
336
337           copy_ip6_address(&r1->link_addr, ia0);
338
339         link_address_set:
340           fl = vlib_buffer_get_free_list (vm, b0->free_list_index);
341
342           if ((b0->current_length+sizeof(*id1)+sizeof(*vss1)+sizeof(*cmac))
343               > fl->n_data_bytes)
344             {
345               error0 = DHCPV6_PROXY_ERROR_PKT_TOO_BIG;
346               next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP;
347               pkts_too_big++;
348               goto do_trace;
349             }
350
351           id1 = (dhcpv6_int_id_t *) (((uword) ip1) + b0->current_length);
352           b0->current_length += (sizeof (*id1));
353
354           id1->opt.option = clib_host_to_net_u16(DHCPV6_OPTION_INTERFACE_ID);
355           id1->opt.length = clib_host_to_net_u16(sizeof(rx_sw_if_index));
356           id1->int_idx = clib_host_to_net_u32(rx_sw_if_index);
357
358           u1->length =0;
359           if (h0->u.msg_type != DHCPV6_MSG_RELAY_FORW)
360             {
361                cmac = (dhcpv6_client_mac_t *) (((uword) ip1) + b0->current_length);
362                b0->current_length += (sizeof (*cmac));
363                cmac->opt.length =clib_host_to_net_u16(sizeof(*cmac) -
364                                                       sizeof(cmac->opt));
365                cmac->opt.option = clib_host_to_net_u16(DHCPV6_OPTION_CLIENT_LINK_LAYER_ADDRESS);
366                cmac->link_type = clib_host_to_net_u16(1); // ethernet
367                clib_memcpy(cmac->data, client_src_mac, 6);
368                u1->length += sizeof(*cmac);
369             }
370
371           //TODO: Revisit if hash makes sense here
372           vss = dhcpv6_get_vss_info(dpm, rx_fib_idx);
373
374           if (NULL != vss) {
375               vss1 = (dhcpv6_vss_t *) (((uword) ip1) + b0->current_length);
376               b0->current_length += (sizeof (*vss1));
377               vss1->opt.length =clib_host_to_net_u16(sizeof(*vss1) -
378                                                      sizeof(vss1->opt));
379               vss1->opt.option = clib_host_to_net_u16(DHCPV6_OPTION_VSS);
380               vss1->data[0] = 1;   // type
381               vss1->data[1] = vss->vpn_id.oui >>16 & 0xff;
382               vss1->data[2] = vss->vpn_id.oui >>8  & 0xff;
383               vss1->data[3] = vss->vpn_id.oui & 0xff;
384               vss1->data[4] = vss->vpn_id.fib_id >> 24 & 0xff;
385               vss1->data[5] = vss->vpn_id.fib_id >> 16 & 0xff;
386               vss1->data[6] = vss->vpn_id.fib_id >> 8 & 0xff;
387               vss1->data[7] = vss->vpn_id.fib_id & 0xff;
388               u1->length += sizeof(*vss1);
389           }
390
391           pkts_to_server++;
392           u1->checksum = 0;
393           u1->src_port = clib_host_to_net_u16(UDP_DST_PORT_dhcpv6_to_client);
394           u1->dst_port = clib_host_to_net_u16(UDP_DST_PORT_dhcpv6_to_server);
395
396           u1->length =
397               clib_host_to_net_u16( clib_net_to_host_u16(fwd_opt->length) +
398                                     sizeof(*r1) + sizeof(*fwd_opt) +
399                                     sizeof(*u1) + sizeof(*id1) + u1->length);
400
401           memset(ip1, 0, sizeof(*ip1));
402           ip1->ip_version_traffic_class_and_flow_label = 0x60;
403           ip1->payload_length =  u1->length;
404           ip1->protocol = PROTO_UDP;
405           ip1->hop_limit = HOP_COUNT_LIMIT;
406               src = (server->dhcp6_server.as_u64[0] || server->dhcp6_server.as_u64[1]) ?
407                 &server->dhcp6_server : &dpm->all_dhcpv6_server_address;
408           copy_ip6_address(&ip1->dst_address, src);
409
410
411           ia0 = ip6_interface_first_global_or_site_address
412               (&ip6_main, vnet_buffer(b0)->sw_if_index[VLIB_RX]);
413
414               src = (server->dhcp6_src_address.as_u64[0] || server->dhcp6_src_address.as_u64[1]) ?
415                 &server->dhcp6_src_address : ia0;
416           if (ia0 == 0)
417             {
418               error0 = DHCPV6_PROXY_ERROR_NO_SRC_ADDRESS;
419               next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP;
420               pkts_no_src_address++;
421               goto do_trace;
422             }
423
424           copy_ip6_address (&ip1->src_address, src);
425
426
427           u1->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0, ip1,
428                                                            &bogus_length);
429           ASSERT(bogus_length == 0);
430
431           next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP;
432
433         do_trace:
434           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
435             {
436                dhcpv6_proxy_trace_t *tr = vlib_add_trace (vm, node,
437                                                           b0, sizeof (*tr));
438                tr->which = 0; /* to server */
439                tr->error = error0;
440                tr->original_sw_if_index = rx_sw_if_index;
441                tr->sw_if_index = sw_if_index;
442                if (DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP == next0)
443                  copy_ip6_address((ip6_address_t *)&tr->packet_data[0], &server->dhcp6_server);
444             }
445
446         do_enqueue:
447           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
448                                            to_next, n_left_to_next,
449                                            bi0, next0);
450         }
451
452       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
453     }
454
455   vlib_node_increment_counter (vm, dhcpv6_proxy_to_server_node.index,
456                                DHCPV6_PROXY_ERROR_RELAY_TO_CLIENT,
457                                pkts_to_client);
458   vlib_node_increment_counter (vm, dhcpv6_proxy_to_server_node.index,
459                                DHCPV6_PROXY_ERROR_RELAY_TO_SERVER,
460                                pkts_to_server);
461   vlib_node_increment_counter (vm, dhcpv6_proxy_to_server_node.index,
462                                DHCPV6_PROXY_ERROR_NO_INTERFACE_ADDRESS,
463                                pkts_no_interface_address);
464   vlib_node_increment_counter (vm, dhcpv6_proxy_to_server_node.index,
465                                DHCPV6_PROXY_ERROR_WRONG_MESSAGE_TYPE,
466                                pkts_wrong_msg_type);
467   vlib_node_increment_counter (vm, dhcpv6_proxy_to_server_node.index,
468                                DHCPV6_PROXY_ERROR_NO_SRC_ADDRESS,
469                                pkts_no_src_address);
470   vlib_node_increment_counter (vm, dhcpv6_proxy_to_server_node.index,
471                                DHCPV6_PROXY_ERROR_PKT_TOO_BIG,
472                                pkts_too_big);
473   return from_frame->n_vectors;
474 }
475
476 VLIB_REGISTER_NODE (dhcpv6_proxy_to_server_node) = {
477   .function = dhcpv6_proxy_to_server_input,
478   .name = "dhcpv6-proxy-to-server",
479   /* Takes a vector of packets. */
480   .vector_size = sizeof (u32),
481
482   .n_errors = DHCPV6_PROXY_N_ERROR,
483   .error_strings = dhcpv6_proxy_error_strings,
484
485   .n_next_nodes = DHCPV6_PROXY_TO_SERVER_INPUT_N_NEXT,
486   .next_nodes = {
487 #define _(s,n) [DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_##s] = n,
488     foreach_dhcpv6_proxy_to_server_input_next
489 #undef _
490   },
491
492   .format_buffer = format_dhcpv6_proxy_header_with_length,
493   .format_trace = format_dhcpv6_proxy_trace,
494 #if 0
495   .unformat_buffer = unformat_dhcpv6_proxy_header,
496 #endif
497 };
498
499 static uword
500 dhcpv6_proxy_to_client_input (vlib_main_t * vm,
501                             vlib_node_runtime_t * node,
502                             vlib_frame_t * from_frame)
503 {
504
505   u32 n_left_from, * from;
506   ethernet_main_t *em = ethernet_get_main (vm);
507   dhcpv6_proxy_main_t * dm = &dhcpv6_proxy_main;
508   dhcpv6_server_t * server;
509   vnet_main_t * vnm = vnet_get_main();
510   int bogus_length;
511
512   from = vlib_frame_vector_args (from_frame);
513   n_left_from = from_frame->n_vectors;
514
515   while (n_left_from > 0)
516     {
517       u32 bi0;
518       vlib_buffer_t * b0;
519       udp_header_t * u0, *u1=0;
520       dhcpv6_relay_hdr_t * h0;
521       ip6_header_t * ip1 = 0, *ip0;
522       ip6_address_t _ia0, * ia0 = &_ia0;
523       ip6_address_t client_address;
524       ethernet_interface_t *ei0;
525       ethernet_header_t *mac0;
526       vnet_hw_interface_t *hi0;
527       vlib_frame_t *f0;
528       u32 * to_next0;
529       u32 sw_if_index = ~0;
530       u32 original_sw_if_index = ~0;
531       vnet_sw_interface_t *si0;
532       u32 error0 = (u32)~0;
533       vnet_sw_interface_t *swif;
534       dhcpv6_option_t *r0 = 0, *o;
535       u16 len = 0;
536       u8 interface_opt_flag = 0;
537       u8 relay_msg_opt_flag = 0;
538       ip6_main_t * im = &ip6_main;
539       u32 server_fib_idx, client_fib_idx;
540
541       bi0 = from[0];
542       from += 1;
543       n_left_from -= 1;
544
545       b0 = vlib_get_buffer (vm, bi0);
546       h0 = vlib_buffer_get_current (b0);
547
548       if (DHCPV6_MSG_RELAY_REPL != h0->msg_type)
549         {
550           error0 =  DHCPV6_PROXY_ERROR_WRONG_MESSAGE_TYPE;
551
552         drop_packet:
553           vlib_node_increment_counter (vm, dhcpv6_proxy_to_client_node.index,
554                                        error0, 1);
555
556           f0 = vlib_get_frame_to_node (vm, dm->error_drop_node_index);
557           to_next0 = vlib_frame_vector_args (f0);
558           to_next0[0] = bi0;
559           f0->n_vectors = 1;
560           vlib_put_frame_to_node (vm, dm->error_drop_node_index, f0);
561           goto do_trace;
562         }
563       /* hop count seems not need to be checked */
564       if (HOP_COUNT_LIMIT < h0->hop_count)
565         {
566           error0 =  DHCPV6_RELAY_PKT_DROP_MAX_HOPS;
567           goto drop_packet;
568         }
569       u0 = (void *)h0 -(sizeof(*u0));
570       ip0 = (void *)u0 -(sizeof(*ip0));
571
572       vlib_buffer_advance (b0, sizeof(*h0));
573       o = vlib_buffer_get_current (b0);
574
575       /* Parse through TLVs looking for option 18 (DHCPV6_OPTION_INTERFACE_ID)
576          _and_ option 9 (DHCPV6_OPTION_RELAY_MSG) option which must be there.
577          Currently assuming no other options need to be processed
578          The interface-ID is the FIB number we need
579          to track down the client-facing interface */
580
581       while ((u8 *) o < (b0->data + b0->current_data + b0->current_length))
582         {
583            if (DHCPV6_OPTION_INTERFACE_ID == clib_net_to_host_u16(o->option))
584              {
585                 interface_opt_flag = 1;
586                 if (clib_net_to_host_u16(o->length) == sizeof(sw_if_index))
587                     sw_if_index = clib_net_to_host_u32(((dhcpv6_int_id_t*)o)->int_idx);
588                 if (sw_if_index >= vec_len (im->fib_index_by_sw_if_index))
589                   {
590                     error0 = DHCPV6_PROXY_ERROR_WRONG_INTERFACE_ID_OPTION;
591                     goto drop_packet;
592                   }
593              }
594            if (DHCPV6_OPTION_RELAY_MSG == clib_net_to_host_u16(o->option))
595              {
596                 relay_msg_opt_flag = 1;
597                 r0 = vlib_buffer_get_current (b0);
598              }
599            if ((relay_msg_opt_flag == 1) && (interface_opt_flag == 1))
600              break;
601            vlib_buffer_advance (b0, sizeof(*o) + clib_net_to_host_u16(o->length));
602            o = (dhcpv6_option_t *) (((uword) o) + clib_net_to_host_u16(o->length) + sizeof(*o));
603         }
604
605       if ((relay_msg_opt_flag == 0) || (r0 == 0))
606         {
607           error0 = DHCPV6_PROXY_ERROR_NO_RELAY_MESSAGE_OPTION;
608           goto drop_packet;
609         }
610
611       if ((u32)~0 == sw_if_index)
612         {
613           error0 = DHCPV6_PROXY_ERROR_NO_CIRCUIT_ID_OPTION;
614           goto drop_packet;
615         }
616
617       //Advance buffer to start of encapsulated DHCPv6 message
618       vlib_buffer_advance (b0, sizeof(*r0));
619
620       client_fib_idx = im->fib_index_by_sw_if_index[sw_if_index];
621       server = dhcpv6_get_server(dm, client_fib_idx);
622
623       if (NULL == server)
624       {
625           error0 = DHCPV6_PROXY_ERROR_NO_SERVER;
626           goto drop_packet;
627       }
628
629       server_fib_idx = im->fib_index_by_sw_if_index
630           [vnet_buffer(b0)->sw_if_index[VLIB_RX]];
631
632       if (server_fib_idx != server->server_fib6_index ||
633           ip0->src_address.as_u64[0] != server->dhcp6_server.as_u64[0] ||
634           ip0->src_address.as_u64[1] != server->dhcp6_server.as_u64[1])
635         {
636           //drop packet if not from server with configured address or FIB
637           error0 = DHCPV6_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
638           goto drop_packet;
639         }
640
641       vnet_buffer (b0)->sw_if_index[VLIB_TX] = original_sw_if_index
642           = sw_if_index;
643
644       swif = vnet_get_sw_interface (vnm, original_sw_if_index);
645       if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
646           sw_if_index = swif->unnumbered_sw_if_index;
647
648
649       /*
650        * udp_local hands us the DHCPV6 header, need udp hdr,
651        * ip hdr to relay to client
652        */
653       vlib_buffer_advance (b0, -(sizeof(*u1)));
654       u1 = vlib_buffer_get_current (b0);
655
656       vlib_buffer_advance (b0, -(sizeof(*ip1)));
657       ip1 = vlib_buffer_get_current (b0);
658
659       copy_ip6_address(&client_address, &h0->peer_addr);
660
661       ia0 = ip6_interface_first_address (&ip6_main, sw_if_index);
662       if (ia0 == 0)
663         {
664           error0 = DHCPV6_PROXY_ERROR_NO_INTERFACE_ADDRESS;
665           goto drop_packet;
666         }
667
668       len =  clib_net_to_host_u16(r0->length);
669       memset(ip1, 0, sizeof(*ip1));
670       copy_ip6_address(&ip1->dst_address, &client_address);
671       u1->checksum = 0;
672       u1->src_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcpv6_to_server);
673       u1->dst_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcpv6_to_client);
674       u1->length = clib_host_to_net_u16 (len + sizeof(udp_header_t));
675
676       ip1->ip_version_traffic_class_and_flow_label =
677           ip0->ip_version_traffic_class_and_flow_label &
678           0x00000fff;
679       ip1->payload_length =  u1->length;
680       ip1->protocol = PROTO_UDP;
681       ip1->hop_limit = HOP_COUNT_LIMIT;
682       copy_ip6_address(&ip1->src_address, ia0);
683
684       u1->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0, ip1,
685                                                        &bogus_length);
686       ASSERT(bogus_length == 0);
687
688       vlib_buffer_advance (b0, -(sizeof(ethernet_header_t)));
689       si0 = vnet_get_sw_interface (vnm, original_sw_if_index);
690       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
691           vlib_buffer_advance (b0, -4 /* space for VLAN tag */);
692
693       mac0 = vlib_buffer_get_current (b0);
694
695       hi0 = vnet_get_sup_hw_interface (vnm, original_sw_if_index);
696       ei0 = pool_elt_at_index (em->interfaces, hi0->hw_instance);
697       clib_memcpy (mac0->src_address, ei0->address, sizeof (ei0->address));
698       memset (&mac0->dst_address, 0xff, sizeof (mac0->dst_address));
699       mac0->type = (si0->type == VNET_SW_INTERFACE_TYPE_SUB) ?
700         clib_net_to_host_u16(0x8100) : clib_net_to_host_u16 (0x86dd);
701
702       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
703         {
704           u32 * vlan_tag = (u32 *)(mac0+1);
705           u32 tmp;
706           tmp = (si0->sub.id << 16) | 0x0800;
707           *vlan_tag = clib_host_to_net_u32 (tmp);
708         }
709
710       /* $$$ consider adding a dynamic next to the graph node, for performance */
711       f0 = vlib_get_frame_to_node (vm, hi0->output_node_index);
712       to_next0 = vlib_frame_vector_args (f0);
713       to_next0[0] = bi0;
714       f0->n_vectors = 1;
715       vlib_put_frame_to_node (vm, hi0->output_node_index, f0);
716
717     do_trace:
718       if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
719         {
720           dhcpv6_proxy_trace_t *tr = vlib_add_trace (vm, node,
721                                                      b0, sizeof (*tr));
722           tr->which = 1; /* to client */
723           if (ia0)
724               copy_ip6_address((ip6_address_t*)tr->packet_data, ia0);
725           tr->error = error0;
726           tr->original_sw_if_index = original_sw_if_index;
727           tr->sw_if_index = sw_if_index;
728         }
729     }
730   return from_frame->n_vectors;
731
732 }
733
734 VLIB_REGISTER_NODE (dhcpv6_proxy_to_client_node) = {
735   .function = dhcpv6_proxy_to_client_input,
736   .name = "dhcpv6-proxy-to-client",
737   /* Takes a vector of packets. */
738   .vector_size = sizeof (u32),
739
740   .n_errors = DHCPV6_PROXY_N_ERROR,
741   .error_strings = dhcpv6_proxy_error_strings,
742   .format_buffer = format_dhcpv6_proxy_header_with_length,
743   .format_trace = format_dhcpv6_proxy_trace,
744 #if 0
745   .unformat_buffer = unformat_dhcpv6_proxy_header,
746 #endif
747 };
748
749 clib_error_t * dhcpv6_proxy_init (vlib_main_t * vm)
750 {
751   dhcpv6_proxy_main_t * dm = &dhcpv6_proxy_main;
752   vlib_node_t * error_drop_node;
753   dhcpv6_server_t * server;
754
755   dm->vlib_main = vm;
756   dm->vnet_main = vnet_get_main();
757   error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
758   dm->error_drop_node_index = error_drop_node->index;
759
760   dm->vss_index_by_rx_fib_index = NULL;
761
762   /* RFC says this is the dhcpv6 server address  */
763   dm->all_dhcpv6_server_address.as_u64[0] = clib_host_to_net_u64 (0xFF05000000000000);
764   dm->all_dhcpv6_server_address.as_u64[1] = clib_host_to_net_u64 (0x00010003);
765
766   /* RFC says this is the server and agent address */
767   dm->all_dhcpv6_server_relay_agent_address.as_u64[0] = clib_host_to_net_u64 (0xFF02000000000000);
768   dm->all_dhcpv6_server_relay_agent_address.as_u64[1] = clib_host_to_net_u64 (0x00010002);
769
770   udp_register_dst_port (vm, UDP_DST_PORT_dhcpv6_to_client,
771                          dhcpv6_proxy_to_client_node.index, 0 /* is_ip6 */);
772
773   udp_register_dst_port (vm, UDP_DST_PORT_dhcpv6_to_server,
774                          dhcpv6_proxy_to_server_node.index, 0 /* is_ip6 */);
775
776   /* Create the default server, don't mark it valid */
777   pool_get (dm->dhcp6_servers, server);
778   memset (server, 0, sizeof (*server));
779
780   return 0;
781 }
782
783 VLIB_INIT_FUNCTION (dhcpv6_proxy_init);
784
785 int dhcpv6_proxy_set_server (ip6_address_t *addr,
786                              ip6_address_t *src_address,
787                              u32 rx_fib_id,
788                              u32 server_fib_id,
789                              int is_del)
790 {
791   dhcpv6_proxy_main_t * dm = &dhcpv6_proxy_main;
792   dhcpv6_server_t * server = 0;
793   u32 rx_fib_index = 0;
794   int rc = 0;
795
796   rx_fib_index = ip6_mfib_table_find_or_create_and_lock(rx_fib_id);
797
798   const mfib_prefix_t all_dhcp_servers = {
799       .fp_len = 128,
800       .fp_proto = FIB_PROTOCOL_IP6,
801       .fp_grp_addr = {
802           .ip6 = dm->all_dhcpv6_server_relay_agent_address,
803       }
804   };
805
806   if (is_del)
807     {
808       server = dhcpv6_get_server(dm, rx_fib_index);
809
810       if (NULL == server)
811         {
812           rc = VNET_API_ERROR_NO_SUCH_ENTRY;
813           goto out;
814         }
815
816       /*
817        * release the locks held on the server fib and rx mfib
818        */
819       mfib_table_entry_delete(rx_fib_index,
820                               &all_dhcp_servers,
821                               MFIB_SOURCE_DHCP);
822       mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6);
823       fib_table_unlock(server->server_fib6_index, FIB_PROTOCOL_IP6);
824
825       dm->dhcp6_server_index_by_rx_fib_index[rx_fib_index] = ~0;
826
827       memset (server, 0, sizeof (*server));
828       pool_put (dm->dhcp6_servers, server);
829     }
830   else
831     {
832       if (addr->as_u64[0] == 0 &&
833           addr->as_u64[1] == 0 )
834       {
835           rc = VNET_API_ERROR_INVALID_DST_ADDRESS;
836           goto out;
837       }
838       if (src_address->as_u64[0] == 0 &&
839           src_address->as_u64[1] == 0)
840       {
841           rc = VNET_API_ERROR_INVALID_SRC_ADDRESS;
842           goto out;
843       }
844
845       server = dhcpv6_get_server(dm, rx_fib_index);
846
847       if (NULL != server)
848         {
849           /* modify of an existing entry */
850           ip6_fib_t *fib;
851
852           fib = ip6_fib_get(server->server_fib6_index);
853
854           if (fib->table_id != server_fib_id)
855             {
856               /* swap tables */
857               fib_table_unlock(server->server_fib6_index, FIB_PROTOCOL_IP6);
858               server->server_fib6_index =
859                   ip6_fib_table_find_or_create_and_lock(server_fib_id);
860             }
861         }
862       else
863         {
864           /* Allocate a new server */
865           pool_get (dm->dhcp6_servers, server);
866
867           vec_validate_init_empty (dm->dhcp6_server_index_by_rx_fib_index,
868                                    rx_fib_index, ~0);
869           dm->dhcp6_server_index_by_rx_fib_index[rx_fib_index] =
870               server - dm->dhcp6_servers;
871
872           server->server_fib6_index =
873               ip6_fib_table_find_or_create_and_lock(server_fib_id);
874           mfib_table_lock(rx_fib_index, FIB_PROTOCOL_IP6);
875
876           const mfib_prefix_t all_dhcp_servers = {
877               .fp_len = 128,
878               .fp_proto = FIB_PROTOCOL_IP6,
879               .fp_grp_addr = {
880                   .ip6 = dm->all_dhcpv6_server_relay_agent_address,
881               }
882             };
883           const fib_route_path_t path_for_us = {
884               .frp_proto = FIB_PROTOCOL_IP6,
885               .frp_addr = zero_addr,
886               .frp_sw_if_index = 0xffffffff,
887               .frp_fib_index = ~0,
888               .frp_weight = 0,
889               .frp_flags = FIB_ROUTE_PATH_LOCAL,
890           };
891           mfib_table_entry_path_update(rx_fib_index,
892                                        &all_dhcp_servers,
893                                        MFIB_SOURCE_DHCP,
894                                        &path_for_us,
895                                        MFIB_ITF_FLAG_FORWARD);
896           /*
897            * Each interface that is enabled in this table, needs to be added
898            * as an accepting interface, but this is not easily doable in VPP.
899            * So we cheat. Add a flag to the entry that indicates accept form
900            * any interface.
901            * We will still only accept on v6 enabled interfaces, since the
902            * input feature ensures this.
903            */
904           mfib_table_entry_update(rx_fib_index,
905                                   &all_dhcp_servers,
906                                   MFIB_SOURCE_DHCP,
907                                   MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
908         }
909       copy_ip6_address(&server->dhcp6_server, addr);
910       copy_ip6_address(&server->dhcp6_src_address, src_address);
911   }
912
913 out:
914   mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6);
915
916   return (rc);
917 }
918
919 static clib_error_t *
920 dhcpv6_proxy_set_command_fn (vlib_main_t * vm,
921                            unformat_input_t * input,
922                            vlib_cli_command_t * cmd)
923 {
924   ip6_address_t addr, src_addr;
925   int set_server = 0, set_src_address = 0;
926   u32 rx_fib_id = 0, server_fib_id = 0;
927   int is_del = 0;
928
929   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
930     {
931       if (unformat (input, "server %U",
932                     unformat_ip6_address, &addr))
933          set_server = 1;
934       else if (unformat(input, "src-address %U",
935                         unformat_ip6_address, &src_addr))
936           set_src_address =1;
937        else if (unformat (input, "server-fib-id %d", &server_fib_id))
938         ;
939        else if (unformat (input, "rx-fib-id %d", &rx_fib_id))
940          ;
941        else if (unformat (input, "delete") ||
942                 unformat (input, "del"))
943            is_del = 1;
944       else
945         break;
946     }
947
948   if (is_del || (set_server && set_src_address))
949   {
950       int rv;
951
952       rv = dhcpv6_proxy_set_server (&addr, &src_addr, rx_fib_id,
953                                     server_fib_id, is_del);
954
955       //TODO: Complete the errors
956       switch (rv)
957         {
958         case 0:
959           return 0;
960
961         case -1:
962           return clib_error_return (0, "FIB id %d does not exist", server_fib_id);
963
964         default:
965           return clib_error_return (0, "BUG: rv %d", rv);
966         }
967   }
968   else
969     return clib_error_return (0, "parse error`%U'",
970                               format_unformat_error, input);
971 }
972
973 VLIB_CLI_COMMAND (dhcpv6_proxy_set_command, static) = {
974   .path = "set dhcpv6 proxy",
975   .short_help = "set dhcpv6 proxy [del] server <ipv6-addr> src-address <ipv6-addr> "
976                   "[server-fib-id <fib-id>] [rx-fib-id <fib-id>] ",
977   .function = dhcpv6_proxy_set_command_fn,
978 };
979
980 u8 * format_dhcpv6_proxy_server (u8 * s, va_list * args)
981 {
982   dhcpv6_proxy_main_t * dm = va_arg (*args, dhcpv6_proxy_main_t *);
983   dhcpv6_server_t * server = va_arg (*args, dhcpv6_server_t *);
984   u32 rx_fib_index = va_arg (*args, u32);
985   ip6_fib_t * rx_fib, * server_fib;
986   u32 server_fib_id = (u32)~0, rx_fib_id = ~0;
987
988   if (dm == 0)
989     {
990       s = format (s, "%=40s%=40s%=14s%=14s", "Server Address", "Source Address",
991                   "Server FIB", "RX FIB");
992       return s;
993     }
994
995   server_fib = ip6_fib_get(server->server_fib6_index);
996   if (server_fib)
997           server_fib_id= server_fib->table_id;
998
999   rx_fib= ip6_fib_get(rx_fib_index);
1000
1001   if (rx_fib)
1002           rx_fib_id = rx_fib->table_id;
1003
1004   s = format (s, "%=40U%=40U%=14u%=14u",
1005               format_ip6_address, &server->dhcp6_server,
1006               format_ip6_address, &server->dhcp6_src_address,
1007                           server_fib_id, rx_fib_id);
1008   return s;
1009 }
1010
1011 static clib_error_t *
1012 dhcpv6_proxy_show_command_fn (vlib_main_t * vm,
1013                             unformat_input_t * input,
1014                             vlib_cli_command_t * cmd)
1015 {
1016   dhcpv6_proxy_main_t * dpm = &dhcpv6_proxy_main;
1017   int i;
1018   u32 server_index;
1019   dhcpv6_server_t * server;
1020
1021   vlib_cli_output (vm, "%U", format_dhcpv6_proxy_server, 0 /* header line */,
1022                   0, 0);
1023   vec_foreach_index (i, dpm->dhcp6_server_index_by_rx_fib_index)
1024   {
1025       server_index = dpm->dhcp6_server_index_by_rx_fib_index[i];
1026       if (~0 == server_index)
1027           continue;
1028
1029       server = pool_elt_at_index (dpm->dhcp6_servers, server_index);
1030
1031       vlib_cli_output (vm, "%U", format_dhcpv6_proxy_server, dpm, 
1032                        server, i);
1033     }
1034
1035   return 0;
1036 }
1037
1038 VLIB_CLI_COMMAND (dhcpv6_proxy_show_command, static) = {
1039   .path = "show dhcpv6 proxy",
1040   .short_help = "Display dhcpv6 proxy info",
1041   .function = dhcpv6_proxy_show_command_fn,
1042 };
1043
1044 void
1045 dhcpv6_proxy_dump (void *opaque,
1046                    u32 context)
1047 {
1048   dhcpv6_proxy_main_t * dpm = &dhcpv6_proxy_main;
1049   ip6_fib_t *s_fib, *r_fib;
1050   dhcpv6_server_t * server;
1051   u32 server_index, i;
1052   dhcpv6_vss_info *v;
1053
1054   vec_foreach_index (i, dpm->dhcp6_server_index_by_rx_fib_index)
1055   {
1056       server_index = dpm->dhcp6_server_index_by_rx_fib_index[i];
1057       if (~0 == server_index)
1058           continue;
1059
1060       server = pool_elt_at_index (dpm->dhcp6_servers, server_index);
1061       v = dhcpv6_get_vss_info(dpm, i);
1062
1063       ip46_address_t src_addr = {
1064           .ip6 = server->dhcp6_src_address,
1065       };
1066       ip46_address_t server_addr = {
1067           .ip6 = server->dhcp6_server,
1068       };
1069
1070       s_fib = ip6_fib_get(server->server_fib6_index);
1071       r_fib = ip6_fib_get(i);
1072
1073       dhcp_send_details(opaque,
1074                         context,
1075                         &server_addr,
1076                         &src_addr,
1077                         s_fib->table_id,
1078                         r_fib->table_id,
1079                         (v ? v->vpn_id.fib_id : 0),
1080                         (v ? v->vpn_id.oui : 0));
1081   }
1082 }
1083
1084 int dhcpv6_proxy_set_vss(u32 tbl_id,
1085                          u32 oui,
1086                          u32 fib_id,
1087                          int is_del)
1088 {
1089   dhcpv6_proxy_main_t *dm = &dhcpv6_proxy_main;
1090   dhcpv6_vss_info *v = NULL;
1091   u32  rx_fib_index;
1092   int rc = 0;
1093
1094   rx_fib_index = ip6_fib_table_find_or_create_and_lock(tbl_id);
1095   v = dhcpv6_get_vss_info(dm, rx_fib_index);
1096
1097   if (NULL != v)
1098   {
1099       if (is_del)
1100       {
1101           /* release the lock held on the table when the VSS
1102            * info was created */
1103           fib_table_unlock (rx_fib_index,
1104                             FIB_PROTOCOL_IP6);
1105
1106           pool_put (dm->vss, v);
1107           dm->vss_index_by_rx_fib_index[rx_fib_index] = ~0;
1108       }
1109       else
1110       {
1111           /* this is a modify */
1112           v->vpn_id.fib_id = fib_id;
1113           v->vpn_id.oui = oui;
1114       }
1115   }
1116   else
1117   {
1118       if (is_del)
1119           rc = VNET_API_ERROR_NO_SUCH_ENTRY;
1120       else
1121       {
1122           /* create a new entry */
1123           vec_validate_init_empty(dm->vss_index_by_rx_fib_index,
1124                                   rx_fib_index, ~0);
1125
1126           /* hold a lock on the table whilst the VSS info exist */
1127           fib_table_lock (rx_fib_index,
1128                           FIB_PROTOCOL_IP6);
1129
1130           pool_get (dm->vss, v);
1131           v->vpn_id.fib_id = fib_id;
1132           v->vpn_id.oui = oui;
1133           dm->vss_index_by_rx_fib_index[rx_fib_index] = v - dm->vss;
1134       }
1135   }
1136
1137   /* Release the lock taken during the create_or_lock at the start */
1138   fib_table_unlock (rx_fib_index,
1139                     FIB_PROTOCOL_IP6);
1140
1141   return (rc);
1142 }
1143
1144
1145 static clib_error_t *
1146 dhcpv6_vss_command_fn (vlib_main_t * vm,
1147                            unformat_input_t * input,
1148                            vlib_cli_command_t * cmd)
1149 {
1150   int is_del = 0, got_new_vss=0;
1151   u32 oui=0;
1152   u32 fib_id=0, tbl_id=~0;
1153
1154   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
1155     {
1156       if (unformat (input, "oui %d", &oui))
1157           got_new_vss = 1;
1158       else if (unformat (input, "vpn-id %d", &fib_id))
1159           got_new_vss = 1;
1160       else if (unformat (input, "table %d", &tbl_id))
1161           got_new_vss = 1;
1162       else if (unformat(input, "delete") || unformat(input, "del"))
1163           is_del = 1;
1164       else
1165           break;
1166     }
1167
1168   if (tbl_id ==~0)
1169       return clib_error_return (0, "no table ID specified.");
1170
1171   if (is_del || got_new_vss)
1172     {
1173       int rv;
1174
1175       rv = dhcpv6_proxy_set_vss(tbl_id, oui, fib_id, is_del);
1176       switch (rv)
1177         {
1178         case 0:
1179           return 0;
1180
1181         case VNET_API_ERROR_NO_SUCH_FIB:
1182             return clib_error_return (0, "vss info (oui:%d, vpn-id:%d)  not found in table %d.",
1183                                       oui, fib_id, tbl_id);
1184
1185         case VNET_API_ERROR_NO_SUCH_ENTRY:
1186             return clib_error_return (0, "vss for table %d not found in pool.",
1187                                       tbl_id);
1188
1189         default:
1190           return clib_error_return (0, "BUG: rv %d", rv);
1191         }
1192     }
1193   else
1194       return clib_error_return (0, "parse error`%U'",
1195                                 format_unformat_error, input);
1196
1197 }
1198
1199 VLIB_CLI_COMMAND (dhcpv6_proxy_vss_command, static) = {
1200   .path = "set dhcpv6 vss",
1201   .short_help = "set dhcpv6 vss table <table-id> oui <oui> vpn-idx <vpn-idx>",
1202   .function = dhcpv6_vss_command_fn,
1203 };
1204
1205 static clib_error_t *
1206 dhcpv6_vss_show_command_fn (vlib_main_t * vm,
1207                             unformat_input_t * input,
1208                             vlib_cli_command_t * cmd)
1209
1210 {
1211   dhcpv6_proxy_main_t * dm = &dhcpv6_proxy_main;
1212   dhcpv6_vss_info *v;
1213   ip6_fib_t *fib;
1214   u32 *fib_index;
1215
1216   vlib_cli_output (vm, "%=6s%=6s%=12s","Table", "OUI", "VPN ID");
1217   pool_foreach (fib_index, dm->vss_index_by_rx_fib_index,
1218   ({
1219       fib = ip6_fib_get (*fib_index);
1220       v = pool_elt_at_index (dm->vss, *fib_index);
1221
1222       vlib_cli_output (vm, "%=6d%=6d%=12d",
1223                        fib->table_id,
1224                        v->vpn_id.oui,
1225                        v->vpn_id.fib_id);
1226   }));
1227
1228   return 0;
1229 }
1230
1231 VLIB_CLI_COMMAND (dhcpv6_proxy_vss_show_command, static) = {
1232   .path = "show dhcpv6 vss",
1233   .short_help = "show dhcpv6 VSS",
1234   .function = dhcpv6_vss_show_command_fn,
1235 };
1236
1237 static clib_error_t *
1238 dhcpv6_link_address_show_command_fn (vlib_main_t * vm,
1239                                 unformat_input_t * input,
1240                                 vlib_cli_command_t * cmd)
1241
1242 {
1243   dhcpv6_proxy_main_t *dm = &dhcpv6_proxy_main;
1244   vnet_main_t *vnm = vnet_get_main();
1245   u32 sw_if_index0=0, sw_if_index;
1246   ip6_address_t *ia0;
1247   vnet_sw_interface_t *swif;
1248
1249   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
1250     {
1251
1252       if (unformat(input, "%U",
1253                    unformat_vnet_sw_interface, dm->vnet_main, &sw_if_index0))
1254         {
1255             swif = vnet_get_sw_interface (vnm, sw_if_index0);
1256             sw_if_index = (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED) ?
1257                 swif->unnumbered_sw_if_index : sw_if_index0;
1258             ia0 = ip6_interface_first_address(&ip6_main, sw_if_index);
1259             if (ia0)
1260               {
1261                   vlib_cli_output (vm, "%=20s%=48s", "interface", "link-address");
1262
1263                   vlib_cli_output (vm, "%=20U%=48U",
1264                                    format_vnet_sw_if_index_name, dm->vnet_main, sw_if_index0,
1265                                    format_ip6_address, ia0);
1266               } else
1267                 vlib_cli_output (vm, "%=34s%=20U", "No IPv6 address configured on",
1268                                  format_vnet_sw_if_index_name, dm->vnet_main, sw_if_index);
1269         } else
1270           break;
1271     }
1272
1273   return 0;
1274 }
1275
1276 VLIB_CLI_COMMAND (dhcpv6_proxy_address_show_command, static) = {
1277   .path = "show dhcpv6 link-address interface",
1278   .short_help = "show dhcpv6 link-address interface <interface>",
1279   .function = dhcpv6_link_address_show_command_fn,
1280 };