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