Implement DHCPv6 PD client (VPP-718, VPP-1050)
[vpp.git] / src / vnet / dhcp / dhcp6_pd_client_dp.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vlib/vlib.h>
17 #include <vnet/dhcp/dhcp6_packet.h>
18 #include <vnet/dhcp/dhcp_proxy.h>
19 #include <vnet/mfib/mfib_table.h>
20 #include <vnet/mfib/ip6_mfib.h>
21 #include <vnet/fib/fib.h>
22 #include <vnet/adj/adj_mcast.h>
23 #include <vnet/ip/ip6_neighbor.h>
24 #include <vlibapi/api_common.h>
25 #include <vlibmemory/api.h>
26 #include <vnet/dhcp/dhcp6_pd_client_dp.h>
27
28 #include <vnet/vnet_msg_enum.h>
29
30 #define vl_typedefs             /* define message structures */
31 #include <vnet/vnet_all_api_h.h>
32 #undef vl_typedefs
33
34 #define vl_endianfun            /* define message structures */
35 #include <vnet/vnet_all_api_h.h>
36 #undef vl_endianfun
37
38 /* instantiate all the print functions we know about */
39 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
40 #define vl_printfun
41 #include <vnet/vnet_all_api_h.h>
42 #undef vl_printfun
43
44 #include <vlibapi/api_helper_macros.h>
45
46 typedef struct
47 {
48   u8 entry_valid;
49   u8 keep_sending_client_message;       /* when true then next fields are valid */
50   dhcp6_pd_send_client_message_params_t params;
51   f64 transaction_start;
52   f64 sleep_interval;
53   f64 due_time;
54   u32 n_left;
55   f64 start_time;
56   u32 transaction_id;
57   vlib_buffer_t *buffer;
58   u32 elapsed_pos;
59   u32 adj_index;
60 } dhcp6_pd_client_state_t;
61
62 typedef struct
63 {
64   u8 *data;
65   u16 len;
66 } server_id_t;
67
68 typedef struct
69 {
70   dhcp6_pd_client_state_t *client_state_by_sw_if_index;
71   server_id_t *server_ids;
72
73   uword publisher_node;
74   uword publisher_et;
75
76   u32 seed;
77
78   /* convenience */
79   vlib_main_t *vlib_main;
80   vnet_main_t *vnet_main;
81 } dhcp6_pd_client_main_t;
82
83 static dhcp6_pd_client_main_t dhcp6_pd_client_main;
84 dhcp6_pd_client_public_main_t dhcp6_pd_client_public_main;
85
86 typedef struct
87 {
88   ip6_address_t prefix;
89   u8 prefix_length;
90   u32 valid_time;
91   u32 preferred_time;
92   u16 status_code;
93 } prefix_info_t;
94
95 typedef struct
96 {
97   u32 sw_if_index;
98   u32 server_index;
99   u8 msg_type;
100   u32 T1;
101   u32 T2;
102   u16 inner_status_code;
103   u16 status_code;
104   u8 preference;
105   u32 n_prefixes;
106   prefix_info_t *prefixes;
107 } report_t;
108
109 typedef union
110 {
111   CLIB_PACKED (struct
112                {
113                u16 duid_type;
114                u16 hardware_type;
115                u32 time;
116                u8 lla[6];
117                });
118   char bin_string[14];
119 } dhcpv6_duid_string_t;
120
121 static dhcpv6_duid_string_t client_duid;
122 #define CLIENT_DUID_LENGTH sizeof (client_duid)
123 #define DHCPV6_CLIENT_IAID 1
124
125 static void
126 signal_report (report_t * r)
127 {
128   vlib_main_t *vm = vlib_get_main ();
129   dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
130   uword ni = cm->publisher_node;
131   uword et = cm->publisher_et;
132
133   if (ni == (uword) ~ 0)
134     return;
135   report_t *q = vlib_process_signal_event_data (vm, ni, et, 1, sizeof *q);
136
137   *q = *r;
138 }
139
140 static int
141 publish_report (report_t * r)
142 {
143   void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
144   vl_api_rpc_call_main_thread (signal_report, (u8 *) r, sizeof *r);
145   return 0;
146 }
147
148 void
149 dhcp6_pd_set_publisher_node (uword node_index, uword event_type)
150 {
151   dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
152   cm->publisher_node = node_index;
153   cm->publisher_et = event_type;
154 }
155
156 #define foreach_dhcpv6_pd_client \
157   _(DROP, "error-drop")          \
158   _(LOOKUP, "ip6-lookup")
159
160 typedef enum
161 {
162 #define _(sym,str) DHCPV6_PD_CLIENT_NEXT_##sym,
163   foreach_dhcpv6_pd_client
164 #undef _
165     DHCPV6_PD_CLIENT_N_NEXT,
166 } dhcpv6_pd_client_next_t;
167
168 /**
169  * per-packet trace data
170  */
171 typedef struct dhcpv6_pd_client_trace_t_
172 {
173 } dhcpv6_pd_client_trace_t;
174
175 static u8 *
176 format_dhcpv6_pd_client_trace (u8 * s, va_list * args)
177 {
178   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
179   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
180   //dhcpv6_pd_client_trace_t *t = va_arg (*args, dhcpv6_pd_client_trace_t *);
181
182   s = format (s, "nothing");
183
184   return s;
185 }
186
187 static u32
188 server_index_get_or_create (u8 * data, u16 len)
189 {
190   dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
191   u32 i;
192   server_id_t *se;
193   server_id_t new_se;
194
195   for (i = 0; i < vec_len (cm->server_ids); i++)
196     {
197       se = &cm->server_ids[i];
198       if (se->len == len && 0 == memcmp (se->data, data, len))
199         return i;
200     }
201
202   new_se.len = len;
203   new_se.data = 0;
204   vec_validate (new_se.data, len - 1);
205   memcpy (new_se.data, data, len);
206
207   vec_add1 (cm->server_ids, new_se);
208
209   return vec_len (cm->server_ids) - 1;
210 }
211
212 static void
213 stop_sending_client_message (vlib_main_t * vm,
214                              dhcp6_pd_client_state_t * client_state)
215 {
216   u32 bi0;
217
218   client_state->keep_sending_client_message = 0;
219   vec_free (client_state->params.prefixes);
220   if (client_state->buffer)
221     {
222       bi0 = vlib_get_buffer_index (vm, client_state->buffer);
223       vlib_buffer_free (vm, &bi0, 1);
224       client_state->buffer = 0;
225       adj_unlock (client_state->adj_index);
226       client_state->adj_index = ~0;
227     }
228 }
229
230 static uword
231 dhcpv6_pd_client_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
232                           vlib_frame_t * frame)
233 {
234   dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
235
236   dhcpv6_pd_client_next_t next_index;
237   u32 n_left_from, *from, *to_next;
238   next_index = 0;
239   n_left_from = frame->n_vectors;
240   from = vlib_frame_vector_args (frame);
241
242   while (n_left_from > 0)
243     {
244       u32 n_left_to_next;
245
246       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
247
248       while (n_left_from > 0 && n_left_to_next > 0)
249         {
250           ip6_header_t *ip0;
251           u32 options_length;
252           dhcpv6_header_t *dhcpv60;
253           dhcpv6_option_t *option;
254           vlib_buffer_t *b0;
255           report_t report;
256           u32 next0 = DHCPV6_PD_CLIENT_NEXT_DROP;
257           u32 bi0;
258           u32 xid;
259           u32 sw_if_index;
260           u32 iaid;
261           u8 client_id_present = 0;
262           u8 discard = 0;
263
264           dhcp6_pd_client_state_t *client_state = NULL;
265
266           bi0 = from[0];
267           to_next[0] = bi0;
268           from += 1;
269           to_next += 1;
270           n_left_from -= 1;
271           n_left_to_next -= 1;
272
273           b0 = vlib_get_buffer (vm, bi0);
274
275           dhcpv60 = vlib_buffer_get_current (b0);
276           ip0 = (void *) (b0->data + vnet_buffer (b0)->l3_hdr_offset);
277           u32 dhcpv6_ip6_palyoad_offset =
278             (u8 *) dhcpv60 - ((u8 *) ip0 + sizeof (*ip0));
279           options_length =
280             ntohs (ip0->payload_length) - dhcpv6_ip6_palyoad_offset -
281             sizeof (*dhcpv60);
282
283           memset (&report, 0, sizeof (report));
284
285           sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
286           if (sw_if_index >= vec_len (cm->client_state_by_sw_if_index))
287             client_state = 0;
288           else
289             client_state = &cm->client_state_by_sw_if_index[sw_if_index];
290
291           xid =
292             (dhcpv60->xid[0] << 16) + (dhcpv60->xid[1] << 8) +
293             dhcpv60->xid[2];
294           if (!client_state || client_state->transaction_id != xid)
295             {
296               clib_warning
297                 ("Received DHCPv6 message with wrong Transaction ID");
298               discard = 1;
299             }
300
301           report.sw_if_index = sw_if_index;
302           report.msg_type = dhcpv60->msg_type;
303           report.server_index = ~0;
304
305           switch (dhcpv60->msg_type)
306             {
307             case DHCPV6_MSG_ADVERTISE:
308             case DHCPV6_MSG_REPLY:
309               option = (dhcpv6_option_t *) (dhcpv60 + 1);
310               while (options_length > 0)
311                 {
312                   if (options_length <
313                       ntohs (option->length) + sizeof (*option))
314                     {
315                       clib_warning
316                         ("remaining payload length < option length (%d < %d)",
317                          options_length,
318                          ntohs (option->length) + sizeof (*option));
319                       break;
320                     }
321                   u16 oo = ntohs (option->option);
322                   if (oo == DHCPV6_OPTION_IA_PD)
323                     {
324                       u8 discard_ia_pd = 0;
325                       dhcpv6_ia_header_t *ia_header = (void *) option;
326                       iaid = ntohl (ia_header->iaid);
327                       u32 T1 = ntohl (ia_header->t1);
328                       u32 T2 = ntohl (ia_header->t2);
329                       if (iaid != DHCPV6_CLIENT_IAID)
330                         discard_ia_pd = 1;
331                       if (T1 != 0 && T2 != 0 && T1 > T2)
332                         discard_ia_pd = 1;
333                       if (!discard_ia_pd)
334                         {
335                           report.T1 = T1;
336                           report.T2 = T2;
337                         }
338                       dhcpv6_option_t *inner_option =
339                         (void *) ia_header->data;
340                       u16 inner_options_length =
341                         ntohs (option->length) - (sizeof (*ia_header) -
342                                                   sizeof (dhcpv6_option_t));
343                       while (inner_options_length > 0)
344                         {
345                           u16 inner_oo = ntohs (inner_option->option);
346                           if (discard_ia_pd)
347                             ;
348                           else if (inner_oo == DHCPV6_OPTION_IAPREFIX)
349                             {
350                               dhcpv6_ia_opt_pd_t *iaprefix =
351                                 (void *) inner_option;
352                               vec_validate (report.prefixes,
353                                             report.n_prefixes);
354                               prefix_info_t *prefix_info =
355                                 &report.prefixes[report.n_prefixes];
356                               report.n_prefixes++;
357                               prefix_info->preferred_time =
358                                 ntohl (iaprefix->preferred);
359                               prefix_info->valid_time =
360                                 ntohl (iaprefix->valid);
361                               prefix_info->prefix_length = iaprefix->prefix;
362                               prefix_info->prefix = iaprefix->addr;
363                             }
364                           else if (inner_oo == DHCPV6_OPTION_STATUS_CODE)
365                             {
366                               dhcpv6_status_code_t *sc =
367                                 (void *) inner_option;
368                               report.inner_status_code =
369                                 ntohs (sc->status_code);
370                             }
371                           inner_options_length -=
372                             sizeof (*inner_option) +
373                             ntohs (inner_option->length);
374                           inner_option =
375                             (void *) ((u8 *) inner_option +
376                                       sizeof (*inner_option) +
377                                       ntohs (inner_option->length));
378                         }
379                     }
380                   else if (oo == DHCPV6_OPTION_CLIENTID)
381                     {
382                       if (client_id_present)
383                         {
384                           clib_warning
385                             ("Duplicate Client ID in received DHVPv6 message");
386                           discard = 1;
387                         }
388                       else
389                         {
390                           u16 len = ntohs (option->length);
391                           client_id_present = 1;
392                           if (len != CLIENT_DUID_LENGTH ||
393                               0 != memcmp (option->data,
394                                            client_duid.bin_string,
395                                            CLIENT_DUID_LENGTH))
396                             {
397                               clib_warning
398                                 ("Unrecognized client DUID inside received DHVPv6 message");
399                               discard = 1;
400                             }
401                         }
402                     }
403                   else if (oo == DHCPV6_OPTION_SERVERID)
404                     {
405                       if (report.server_index != ~0)
406                         {
407                           clib_warning
408                             ("Duplicate Server ID in received DHVPv6 message");
409                           discard = 1;
410                         }
411                       else
412                         report.server_index =
413                           server_index_get_or_create (option->data,
414                                                       ntohs (option->length));
415                     }
416                   else if (oo == DHCPV6_OPTION_PREFERENCE)
417                     {
418                       report.preference = option->data[0];
419                     }
420                   else if (oo == DHCPV6_OPTION_STATUS_CODE)
421                     {
422                       dhcpv6_status_code_t *sc = (void *) option;
423                       report.status_code = ntohs (sc->status_code);
424                     }
425                   options_length -= sizeof (*option) + ntohs (option->length);
426                   option =
427                     (void *) ((u8 *) option + sizeof (*option) +
428                               ntohs (option->length));
429                 }
430
431               if (!client_id_present)
432                 {
433                   clib_warning
434                     ("Missing Client ID in received DHVPv6 message");
435                   discard = 1;
436                 }
437               if (report.server_index == ~0)
438                 {
439                   clib_warning
440                     ("Missing Server ID in received DHVPv6 message");
441                   discard = 1;
442                 }
443
444               if (!discard)
445                 publish_report (&report);
446               else
447                 vec_free (report.prefixes);
448
449               break;
450             default:
451               break;
452             }
453
454           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
455             {
456               dhcpv6_pd_client_trace_t *t =
457                 vlib_add_trace (vm, node, b0, sizeof (*t));
458             }
459
460           /* verify speculative enqueue, maybe switch current next frame */
461           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
462                                            to_next, n_left_to_next,
463                                            bi0, next0);
464         }
465
466       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
467     }
468
469   return frame->n_vectors;
470 }
471
472 /* *INDENT-OFF* */
473 VLIB_REGISTER_NODE (dhcpv6_pd_client_node, static) = {
474     .function = dhcpv6_pd_client_node_fn,
475     .name = "dhcpv6-pd-client",
476     .vector_size = sizeof (u32),
477
478     .n_errors = 0,
479
480     .n_next_nodes = DHCPV6_PD_CLIENT_N_NEXT,
481     .next_nodes = {
482   #define _(s,n) [DHCPV6_PD_CLIENT_NEXT_##s] = n,
483       foreach_dhcpv6_pd_client
484   #undef _
485     },
486
487     .format_trace = format_dhcpv6_pd_client_trace,
488 };
489 /* *INDENT-ON* */
490
491 static_always_inline f64
492 random_f64_from_to (f64 from, f64 to)
493 {
494   static u32 seed = 0;
495   static u8 seed_set = 0;
496   if (!seed_set)
497     {
498       seed = random_default_seed ();
499       seed_set = 1;
500     }
501   return random_f64 (&seed) * (to - from) + from;
502 }
503
504 static const ip6_address_t all_dhcp6_relay_agents_and_servers = {
505   .as_u8 = {
506             0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}
508 };
509
510 static vlib_buffer_t *
511 create_buffer_for_client_message (vlib_main_t * vm,
512                                   u32 sw_if_index,
513                                   dhcp6_pd_client_state_t
514                                   * client_state, u32 type)
515 {
516   dhcp6_pd_client_main_t *dm = &dhcp6_pd_client_main;
517   vnet_main_t *vnm = vnet_get_main ();
518
519   vlib_buffer_t *b;
520   u32 bi;
521   ip6_header_t *ip;
522   udp_header_t *udp;
523   dhcpv6_header_t *dhcp;
524   ip6_address_t src_addr;
525   u32 dhcp_opt_len = 0;
526   client_state->transaction_start = vlib_time_now (vm);
527   u32 n_prefixes;
528   u32 i;
529
530   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
531   vnet_sw_interface_t *sup_sw = vnet_get_sup_sw_interface (vnm, sw_if_index);
532   vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index);
533
534   /* Interface(s) down? */
535   if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
536     return NULL;
537   if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
538     return NULL;
539   if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
540     return NULL;
541
542   /* Get a link-local address */
543   src_addr = ip6_neighbor_get_link_local_address (sw_if_index);
544
545   if (src_addr.as_u8[0] != 0xfe)
546     {
547       clib_warning ("Could not find source address to send DHCPv6 packet");
548       return NULL;
549     }
550
551   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
552     {
553       clib_warning ("Buffer allocation failed");
554       return NULL;
555     }
556
557   b = vlib_get_buffer (vm, bi);
558   vnet_buffer (b)->sw_if_index[VLIB_RX] = sw_if_index;
559   vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
560   client_state->adj_index = adj_mcast_add_or_lock (FIB_PROTOCOL_IP6,
561                                                    VNET_LINK_IP6,
562                                                    sw_if_index);
563   vnet_buffer (b)->ip.adj_index[VLIB_TX] = client_state->adj_index;
564   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
565
566   ip = (ip6_header_t *) vlib_buffer_get_current (b);
567   udp = (udp_header_t *) (ip + 1);
568   dhcp = (dhcpv6_header_t *) (udp + 1);
569
570   ip->src_address = src_addr;
571   ip->hop_limit = 255;
572   ip->ip_version_traffic_class_and_flow_label =
573     clib_host_to_net_u32 (0x6 << 28);
574   ip->payload_length = 0;
575   ip->protocol = IP_PROTOCOL_UDP;
576
577   udp->src_port = clib_host_to_net_u16 (DHCPV6_CLIENT_PORT);
578   udp->dst_port = clib_host_to_net_u16 (DHCPV6_SERVER_PORT);
579   udp->checksum = 0;
580   udp->length = 0;
581
582   dhcp->msg_type = type;
583   dhcp->xid[0] = (client_state->transaction_id & 0x00ff0000) >> 16;
584   dhcp->xid[1] = (client_state->transaction_id & 0x0000ff00) >> 8;
585   dhcp->xid[2] = (client_state->transaction_id & 0x000000ff) >> 0;
586
587   void *d = (void *) dhcp->data;
588   dhcpv6_option_t *duid;
589   dhcpv6_elapsed_t *elapsed;
590   dhcpv6_ia_header_t *ia_hdr;
591   dhcpv6_ia_opt_pd_t *opt_pd;
592   if (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST ||
593       type == DHCPV6_MSG_RENEW || type == DHCPV6_MSG_REBIND ||
594       type == DHCPV6_MSG_RELEASE)
595     {
596       duid = (dhcpv6_option_t *) d;
597       duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_CLIENTID);
598       duid->length = clib_host_to_net_u16 (CLIENT_DUID_LENGTH);
599       clib_memcpy (duid + 1, client_duid.bin_string, CLIENT_DUID_LENGTH);
600       d += sizeof (*duid) + CLIENT_DUID_LENGTH;
601
602       if (client_state->params.server_index != ~0)
603         {
604           server_id_t *se =
605             &dm->server_ids[client_state->params.server_index];
606
607           duid = (dhcpv6_option_t *) d;
608           duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_SERVERID);
609           duid->length = clib_host_to_net_u16 (se->len);
610           clib_memcpy (duid + 1, se->data, se->len);
611           d += sizeof (*duid) + se->len;
612         }
613
614       elapsed = (dhcpv6_elapsed_t *) d;
615       elapsed->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_ELAPSED_TIME);
616       elapsed->opt.length =
617         clib_host_to_net_u16 (sizeof (*elapsed) - sizeof (elapsed->opt));
618       elapsed->elapsed_10ms = 0;
619       client_state->elapsed_pos =
620         (char *) &elapsed->elapsed_10ms -
621         (char *) vlib_buffer_get_current (b);
622       d += sizeof (*elapsed);
623
624       ia_hdr = (dhcpv6_ia_header_t *) d;
625       ia_hdr->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IA_PD);
626       ia_hdr->iaid = clib_host_to_net_u32 (DHCPV6_CLIENT_IAID);
627       ia_hdr->t1 = clib_host_to_net_u32 (client_state->params.T1);
628       ia_hdr->t2 = clib_host_to_net_u32 (client_state->params.T2);
629       d += sizeof (*ia_hdr);
630
631       n_prefixes = vec_len (client_state->params.prefixes);
632
633       ia_hdr->opt.length =
634         clib_host_to_net_u16 (sizeof (*ia_hdr) +
635                               n_prefixes * sizeof (*opt_pd) -
636                               sizeof (ia_hdr->opt));
637
638       for (i = 0; i < n_prefixes; i++)
639         {
640           dhcp6_pd_send_client_message_params_prefix_t *pref =
641             &client_state->params.prefixes[i];
642           opt_pd = (dhcpv6_ia_opt_pd_t *) d;
643           opt_pd->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IAPREFIX);
644           opt_pd->opt.length =
645             clib_host_to_net_u16 (sizeof (*opt_pd) - sizeof (opt_pd->opt));
646           opt_pd->addr = pref->prefix;
647           opt_pd->prefix = pref->prefix_length;
648           opt_pd->valid = clib_host_to_net_u32 (pref->valid_lt);
649           opt_pd->preferred = clib_host_to_net_u32 (pref->preferred_lt);
650           d += sizeof (*opt_pd);
651         }
652     }
653   else
654     {
655       clib_warning ("State not implemented");
656     }
657
658   dhcp_opt_len = ((u8 *) d) - dhcp->data;
659   udp->length =
660     clib_host_to_net_u16 (sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len);
661   ip->payload_length = udp->length;
662   b->current_length =
663     sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len;
664
665   ip->dst_address = all_dhcp6_relay_agents_and_servers;
666
667   return b;
668 }
669
670 static inline u8
671 check_pd_send_client_message (vlib_main_t * vm,
672                               dhcp6_pd_client_state_t * client_state,
673                               f64 current_time, f64 * due_time)
674 {
675   vlib_buffer_t *p0;
676   vlib_frame_t *f;
677   u32 *to_next;
678   u32 next_index;
679   vlib_buffer_t *c0;
680   ip6_header_t *ip;
681   udp_header_t *udp;
682   u32 ci0;
683   int bogus_length = 0;
684
685   dhcp6_pd_send_client_message_params_t *params;
686
687   f64 now = vlib_time_now (vm);
688
689   if (!client_state->keep_sending_client_message)
690     return false;
691
692   params = &client_state->params;
693
694   if (client_state->due_time > current_time)
695     {
696       *due_time = client_state->due_time;
697       return true;
698     }
699
700   p0 = client_state->buffer;
701
702   next_index = ip6_rewrite_mcast_node.index;
703
704   c0 = vlib_buffer_copy (vm, p0);
705   ci0 = vlib_get_buffer_index (vm, c0);
706
707   ip = (ip6_header_t *) vlib_buffer_get_current (c0);
708   udp = (udp_header_t *) (ip + 1);
709
710   u16 *elapsed_field = (u16 *) ((void *) ip + client_state->elapsed_pos);
711   *elapsed_field =
712     clib_host_to_net_u16 ((u16)
713                           ((now - client_state->transaction_start) * 100));
714
715   udp->checksum = 0;
716   udp->checksum =
717     ip6_tcp_udp_icmp_compute_checksum (vm, 0, ip, &bogus_length);
718
719   f = vlib_get_frame_to_node (vm, next_index);
720   to_next = vlib_frame_vector_args (f);
721   to_next[0] = ci0;
722   f->n_vectors = 1;
723   vlib_put_frame_to_node (vm, next_index, f);
724
725   if (params->mrc != 0 && --client_state->n_left == 0)
726     stop_sending_client_message (vm, client_state);
727   else
728     {
729       client_state->sleep_interval =
730         (2 + random_f64_from_to (-0.1, 0.1)) * client_state->sleep_interval;
731       if (client_state->sleep_interval > params->mrt)
732         client_state->sleep_interval =
733           (1 + random_f64_from_to (-0.1, 0.1)) * params->mrt;
734
735       client_state->due_time = current_time + client_state->sleep_interval;
736
737       if (params->mrd != 0
738           && current_time > client_state->start_time + params->mrd)
739         stop_sending_client_message (vm, client_state);
740       else
741         *due_time = client_state->due_time;
742     }
743
744   return client_state->keep_sending_client_message;
745 }
746
747 static uword
748 send_dhcp6_pd_client_message_process (vlib_main_t * vm,
749                                       vlib_node_runtime_t * rt,
750                                       vlib_frame_t * f0)
751 {
752   dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
753   dhcp6_pd_client_state_t *client_state;
754   uword *event_data = 0;
755   f64 sleep_time = 1e9;
756   f64 current_time;
757   f64 due_time;
758   f64 dt = 0;
759   int i;
760
761   while (true)
762     {
763       vlib_process_wait_for_event_or_clock (vm, sleep_time);
764       vlib_process_get_events (vm, &event_data);
765       vec_reset_length (event_data);
766
767       current_time = vlib_time_now (vm);
768       do
769         {
770           due_time = current_time + 1e9;
771           for (i = 0; i < vec_len (cm->client_state_by_sw_if_index); i++)
772             {
773               client_state = &cm->client_state_by_sw_if_index[i];
774               if (!client_state->entry_valid)
775                 continue;
776               if (check_pd_send_client_message
777                   (vm, client_state, current_time, &dt) && (dt < due_time))
778                 due_time = dt;
779             }
780           current_time = vlib_time_now (vm);
781         }
782       while (due_time < current_time);
783
784       sleep_time = due_time - current_time;
785     }
786
787   return 0;
788 }
789
790 /* *INDENT-OFF* */
791 VLIB_REGISTER_NODE (send_dhcp6_pd_client_message_process_node) = {
792     .function = send_dhcp6_pd_client_message_process,
793     .type = VLIB_NODE_TYPE_PROCESS,
794     .name = "send-dhcp6-pd-client-message-process",
795 };
796 /* *INDENT-ON* */
797
798 void
799 dhcp6_pd_send_client_message (vlib_main_t * vm, u32 sw_if_index, u8 stop,
800                               dhcp6_pd_send_client_message_params_t * params)
801 {
802   dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
803   dhcp6_pd_client_state_t *client_state = 0;
804   dhcp6_pd_client_state_t empty_state = {
805     0,
806   };
807
808   ASSERT (~0 != sw_if_index);
809
810   vec_validate_init_empty (cm->client_state_by_sw_if_index, sw_if_index,
811                            empty_state);
812   client_state = &cm->client_state_by_sw_if_index[sw_if_index];
813   if (!client_state->entry_valid)
814     {
815       client_state->entry_valid = 1;
816       client_state->adj_index = ~0;
817     }
818
819   stop_sending_client_message (vm, client_state);
820
821   if (!stop)
822     {
823       client_state->keep_sending_client_message = 1;
824       vec_free (client_state->params.prefixes);
825       client_state->params = *params;
826       client_state->params.prefixes = vec_dup (params->prefixes);
827       client_state->n_left = params->mrc;
828       client_state->start_time = vlib_time_now (vm);
829       client_state->sleep_interval =
830         (1 + random_f64_from_to (-0.1, 0.1)) * params->irt;
831       client_state->due_time = 0;       /* send first packet ASAP */
832       client_state->transaction_id = random_u32 (&cm->seed) & 0x00ffffff;
833       client_state->buffer =
834         create_buffer_for_client_message (vm, sw_if_index, client_state,
835                                           params->msg_type);
836       if (!client_state->buffer)
837         client_state->keep_sending_client_message = 0;
838       else
839         vlib_process_signal_event (vm,
840                                    send_dhcp6_pd_client_message_process_node.index,
841                                    1, 0);
842     }
843 }
844
845 void
846   vl_api_dhcp6_pd_send_client_message_t_handler
847   (vl_api_dhcp6_pd_send_client_message_t * mp)
848 {
849   vl_api_dhcp6_pd_send_client_message_reply_t *rmp;
850   dhcp6_pd_send_client_message_params_t params;
851   vlib_main_t *vm = vlib_get_main ();
852   u32 n_prefixes;
853   u32 i;
854   int rv = 0;
855
856   VALIDATE_SW_IF_INDEX (mp);
857
858   BAD_SW_IF_INDEX_LABEL;
859   REPLY_MACRO (VL_API_DHCP6_PD_SEND_CLIENT_MESSAGE_REPLY);
860
861   if (rv != 0)
862     return;
863
864   params.sw_if_index = ntohl (mp->sw_if_index);
865   params.server_index = ntohl (mp->server_index);
866   params.irt = ntohl (mp->irt);
867   params.mrt = ntohl (mp->mrt);
868   params.mrc = ntohl (mp->mrc);
869   params.mrd = ntohl (mp->mrd);
870   params.msg_type = mp->msg_type;
871   params.T1 = ntohl (mp->T1);
872   params.T2 = ntohl (mp->T2);
873   n_prefixes = ntohl (mp->n_prefixes);
874   params.prefixes = 0;
875   if (n_prefixes > 0)
876     vec_validate (params.prefixes, n_prefixes - 1);
877   for (i = 0; i < n_prefixes; i++)
878     {
879       vl_api_dhcp6_pd_prefix_info_t *pi = &mp->prefixes[i];
880       dhcp6_pd_send_client_message_params_prefix_t *pref =
881         &params.prefixes[i];
882       pref->preferred_lt = ntohl (pi->preferred_time);
883       pref->valid_lt = ntohl (pi->valid_time);
884       memcpy (pref->prefix.as_u8, pi->prefix, 16);
885       pref->prefix_length = pi->prefix_length;
886     }
887
888   dhcp6_pd_send_client_message (vm, ntohl (mp->sw_if_index), mp->stop,
889                                 &params);
890 }
891
892 static clib_error_t *
893 call_dhcp6_pd_reply_event_callbacks (void *data,
894                                      _vnet_dhcp6_pd_reply_event_function_list_elt_t
895                                      * elt)
896 {
897   clib_error_t *error = 0;
898
899   while (elt)
900     {
901       error = elt->fp (data);
902       if (error)
903         return error;
904       elt = elt->next_dhcp6_pd_reply_event_function;
905     }
906
907   return error;
908 }
909
910 static uword
911 dhcp6_pd_reply_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
912                         vlib_frame_t * f)
913 {
914   /* These cross the longjmp  boundry (vlib_process_wait_for_event)
915    * and need to be volatile - to prevent them from being optimized into
916    * a register - which could change during suspension */
917
918   while (1)
919     {
920       vlib_process_wait_for_event (vm);
921       uword event_type = DHCP6_PD_DP_REPLY_REPORT;
922       void *event_data = vlib_process_get_event_data (vm, &event_type);
923
924       int i;
925       if (event_type == DHCP6_PD_DP_REPLY_REPORT)
926         {
927           report_t *events = event_data;
928           for (i = 0; i < vec_len (events); i++)
929             {
930               u32 event_size =
931                 sizeof (vl_api_dhcp6_pd_reply_event_t) +
932                 vec_len (events[i].prefixes) *
933                 sizeof (vl_api_dhcp6_pd_prefix_info_t);
934               vl_api_dhcp6_pd_reply_event_t *event =
935                 clib_mem_alloc (event_size);
936               memset (event, 0, event_size);
937
938               event->sw_if_index = htonl (events[i].sw_if_index);
939               event->server_index = htonl (events[i].server_index);
940               event->msg_type = events[i].msg_type;
941               event->T1 = htonl (events[i].T1);
942               event->T2 = htonl (events[i].T2);
943               event->inner_status_code = htons (events[i].inner_status_code);
944               event->status_code = htons (events[i].status_code);
945               event->preference = events[i].preference;
946
947               event->n_prefixes = htonl (vec_len (events[i].prefixes));
948               vl_api_dhcp6_pd_prefix_info_t *prefix =
949                 (typeof (prefix)) event->prefixes;
950               u32 j;
951               for (j = 0; j < vec_len (events[i].prefixes); j++)
952                 {
953                   prefix_info_t *info = &events[i].prefixes[j];
954                   memcpy (prefix->prefix, &info->prefix, 16);
955                   prefix->prefix_length = info->prefix_length;
956                   prefix->valid_time = htonl (info->valid_time);
957                   prefix->preferred_time = htonl (info->preferred_time);
958                   prefix++;
959                 }
960
961               dhcp6_pd_client_public_main_t *dpcpm =
962                 &dhcp6_pd_client_public_main;
963               call_dhcp6_pd_reply_event_callbacks (event, dpcpm->functions);
964
965               vpe_client_registration_t *reg;
966               /* *INDENT-OFF* */
967               pool_foreach(reg, vpe_api_main.dhcp6_pd_reply_events_registrations,
968               ({
969                 vl_api_registration_t *vl_reg;
970                 vl_reg =
971                   vl_api_client_index_to_registration (reg->client_index);
972                 if (vl_reg && vl_api_can_send_msg (vl_reg))
973                   {
974                     vl_api_dhcp6_pd_reply_event_t *msg =
975                       vl_msg_api_alloc (event_size);
976                     clib_memcpy (msg, event, event_size);
977                     msg->_vl_msg_id = htons (VL_API_DHCP6_PD_REPLY_EVENT);
978                     msg->client_index = reg->client_index;
979                     msg->pid = reg->client_pid;
980                     vl_api_send_msg (vl_reg, (u8 *) msg);
981                   }
982               }));
983               /* *INDENT-ON* */
984
985               clib_mem_free (event);
986             }
987         }
988       vlib_process_put_event_data (vm, event_data);
989     }
990
991   return 0;
992 }
993
994 /* *INDENT-OFF* */
995 VLIB_REGISTER_NODE (dhcp6_pd_reply_process_node, ) = {
996   .function = dhcp6_pd_reply_process,
997   .type = VLIB_NODE_TYPE_PROCESS,
998   .name = "dhcp6-pd-reply-publisher-process",
999 };
1000 /* *INDENT-ON* */
1001
1002 void
1003   vl_api_want_dhcp6_pd_reply_events_t_handler
1004   (vl_api_want_dhcp6_pd_reply_events_t * mp)
1005 {
1006   vpe_api_main_t *am = &vpe_api_main;
1007   vl_api_want_dhcp6_pd_reply_events_reply_t *rmp;
1008   int rv = 0;
1009
1010   uword *p =
1011     hash_get (am->dhcp6_pd_reply_events_registration_hash, mp->client_index);
1012   vpe_client_registration_t *rp;
1013   if (p)
1014     {
1015       if (mp->enable_disable)
1016         {
1017           clib_warning ("pid %d: already enabled...", ntohl (mp->pid));
1018           rv = VNET_API_ERROR_INVALID_REGISTRATION;
1019           goto reply;
1020         }
1021       else
1022         {
1023           rp =
1024             pool_elt_at_index (am->dhcp6_pd_reply_events_registrations, p[0]);
1025           pool_put (am->dhcp6_pd_reply_events_registrations, rp);
1026           hash_unset (am->dhcp6_pd_reply_events_registration_hash,
1027                       mp->client_index);
1028           if (pool_elts (am->dhcp6_pd_reply_events_registrations) == 0)
1029             dhcp6_pd_set_publisher_node (~0, REPORT_MAX);
1030           goto reply;
1031         }
1032     }
1033   if (mp->enable_disable == 0)
1034     {
1035       clib_warning ("pid %d: already disabled...", ntohl (mp->pid));
1036       rv = VNET_API_ERROR_INVALID_REGISTRATION;
1037       goto reply;
1038     }
1039   pool_get (am->dhcp6_pd_reply_events_registrations, rp);
1040   rp->client_index = mp->client_index;
1041   rp->client_pid = ntohl (mp->pid);
1042   hash_set (am->dhcp6_pd_reply_events_registration_hash, rp->client_index,
1043             rp - am->dhcp6_pd_reply_events_registrations);
1044   dhcp6_pd_set_publisher_node (dhcp6_pd_reply_process_node.index,
1045                                DHCP6_PD_DP_REPLY_REPORT);
1046
1047 reply:
1048   REPLY_MACRO (VL_API_WANT_DHCP6_PD_REPLY_EVENTS_REPLY);
1049 }
1050
1051 void
1052 dhcp6_clients_enable_disable (u8 enable)
1053 {
1054   vlib_main_t *vm = vlib_get_main ();
1055
1056   if (enable)
1057     udp_register_dst_port (vm, UDP_DST_PORT_dhcpv6_to_client,
1058                            dhcpv6_pd_client_node.index, 0 /* is_ip6 */ );
1059   else
1060     udp_unregister_dst_port (vm, UDP_DST_PORT_dhcpv6_to_client,
1061                              0 /* is_ip6 */ );
1062 }
1063
1064 void
1065   vl_api_dhcp6_clients_enable_disable_t_handler
1066   (vl_api_dhcp6_clients_enable_disable_t * mp)
1067 {
1068   vl_api_dhcp6_clients_enable_disable_reply_t *rmp;
1069   int rv = 0;
1070
1071   dhcp6_clients_enable_disable (mp->enable);
1072
1073   REPLY_MACRO (VL_API_WANT_DHCP6_PD_REPLY_EVENTS_REPLY);
1074 }
1075
1076 static void
1077 genereate_client_duid (void)
1078 {
1079   client_duid.duid_type = htons (DHCPV6_DUID_LLT);
1080   client_duid.hardware_type = htons (1);
1081   u32 time_since_2000 = (u32) time (0) - 946684800;
1082   client_duid.time = htonl (time_since_2000);
1083
1084   vnet_main_t *vnm = vnet_get_main ();
1085   vnet_interface_main_t *im = &vnm->interface_main;
1086   vnet_hw_interface_t *hi;
1087   ethernet_interface_t *eth_if = 0;
1088
1089   /* *INDENT-OFF* */
1090   pool_foreach (hi, im->hw_interfaces,
1091   ({
1092     eth_if = ethernet_get_interface (&ethernet_main, hi->hw_if_index);
1093     if (eth_if)
1094       break;
1095   }));
1096   /* *INDENT-ON* */
1097
1098   if (eth_if)
1099     clib_memcpy (client_duid.lla, eth_if->address, 6);
1100   else
1101     {
1102       clib_warning ("Failed to find any Ethernet interface, "
1103                     "setting DHCPv6 DUID link-layer address to random value");
1104       u32 seed = random_default_seed ();
1105       random_u32 (&seed);
1106       client_duid.lla[0] = 0xc2;        /* locally administered unicast */
1107       client_duid.lla[1] = 0x18;
1108       client_duid.lla[2] = 0x44;
1109       client_duid.lla[3] = random_u32 (&seed);
1110       client_duid.lla[4] = random_u32 (&seed);
1111       client_duid.lla[5] = random_u32 (&seed);
1112     }
1113 }
1114
1115 static clib_error_t *
1116 dhcp6_pd_client_init (vlib_main_t * vm)
1117 {
1118   dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
1119
1120   cm->vlib_main = vm;
1121   cm->vnet_main = vnet_get_main ();
1122
1123   cm->publisher_node = ~0;
1124
1125   cm->seed = 0xdeaddabe;
1126
1127   // TODO: should be stored in non-volatile memory
1128   genereate_client_duid ();
1129
1130   return 0;
1131 }
1132
1133 VLIB_INIT_FUNCTION (dhcp6_pd_client_init);
1134
1135 /*
1136  * fd.io coding-style-patch-verification: ON
1137  *
1138  * Local Variables:
1139  * eval: (c-set-style "gnu")
1140  * End:
1141  */