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