mactime: add a "top" command to watch device stats
[vpp.git] / src / vnet / dhcp / dhcp6_client_common_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 <vnet/ethernet/ethernet.h>
17 #include <vnet/dhcp/dhcp6_client_common_dp.h>
18 #include <vnet/dhcp/dhcp6_ia_na_client_dp.h>
19 #include <vnet/dhcp/dhcp6_pd_client_dp.h>
20 #include <vnet/dhcp/dhcp6_packet.h>
21 #include <vnet/udp/udp.h>
22
23 dhcp6_client_common_main_t dhcp6_client_common_main;
24 dhcpv6_duid_ll_string_t client_duid;
25
26 u32
27 server_index_get_or_create (u8 * data, u16 len)
28 {
29   dhcp6_client_common_main_t *ccm = &dhcp6_client_common_main;
30   u32 i;
31   server_id_t *se;
32   server_id_t new_se;
33
34   for (i = 0; i < vec_len (ccm->server_ids); i++)
35     {
36       se = &ccm->server_ids[i];
37       if (se->len == len && 0 == memcmp (se->data, data, len))
38         return i;
39     }
40
41   new_se.len = len;
42   new_se.data = 0;
43   vec_validate (new_se.data, len - 1);
44   memcpy (new_se.data, data, len);
45
46   vec_add1 (ccm->server_ids, new_se);
47
48   return vec_len (ccm->server_ids) - 1;
49 }
50
51 void
52 vl_api_dhcp6_duid_ll_set_t_handler (vl_api_dhcp6_duid_ll_set_t * mp)
53 {
54   vl_api_dhcp6_duid_ll_set_reply_t *rmp;
55   dhcpv6_duid_ll_string_t *duid;
56   int rv = 0;
57
58   duid = (dhcpv6_duid_ll_string_t *) mp->duid_ll;
59   if (duid->duid_type != htonl (DHCPV6_DUID_LL))
60     {
61       rv = VNET_API_ERROR_INVALID_VALUE;
62       goto reply;
63     }
64   clib_memcpy (&client_duid, &duid, sizeof (client_duid));
65
66 reply:
67   REPLY_MACRO (VL_API_DHCP6_DUID_LL_SET_REPLY);
68 }
69
70 static void
71 generate_client_duid (void)
72 {
73   client_duid.duid_type = htons (DHCPV6_DUID_LL);
74   client_duid.hardware_type = htons (1);
75
76   vnet_main_t *vnm = vnet_get_main ();
77   vnet_interface_main_t *im = &vnm->interface_main;
78   vnet_hw_interface_t *hi;
79   ethernet_interface_t *eth_if = 0;
80
81   /* *INDENT-OFF* */
82   pool_foreach (hi, im->hw_interfaces,
83   ({
84     eth_if = ethernet_get_interface (&ethernet_main, hi->hw_if_index);
85     if (eth_if)
86       break;
87   }));
88   /* *INDENT-ON* */
89
90   if (eth_if)
91     clib_memcpy (client_duid.lla, eth_if->address, 6);
92   else
93     {
94       clib_warning ("Failed to find any Ethernet interface, "
95                     "setting DHCPv6 DUID link-layer address to random value");
96       u32 seed = random_default_seed ();
97       random_u32 (&seed);
98       client_duid.lla[0] = 0xc2;        /* locally administered unicast */
99       client_duid.lla[1] = 0x18;
100       client_duid.lla[2] = 0x44;
101       client_duid.lla[3] = random_u32 (&seed);
102       client_duid.lla[4] = random_u32 (&seed);
103       client_duid.lla[5] = random_u32 (&seed);
104     }
105 }
106
107 #define foreach_dhcpv6_client \
108   _(DROP, "error-drop")       \
109   _(LOOKUP, "ip6-lookup")
110
111 typedef enum
112 {
113 #define _(sym,str) DHCPV6_CLIENT_NEXT_##sym,
114   foreach_dhcpv6_client
115 #undef _
116     DHCPV6_CLIENT_N_NEXT,
117 } dhcpv6_client_next_t;
118
119 /**
120  * per-packet trace data
121  */
122 typedef struct dhcpv6_client_trace_t_
123 {
124 } dhcpv6_client_trace_t;
125
126 static u8 *
127 format_dhcpv6_client_trace (u8 * s, va_list * args)
128 {
129   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
130   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
131   //dhcpv6_client_trace_t *t = va_arg (*args, dhcpv6_client_trace_t *);
132
133   s = format (s, "nothing");
134
135   return s;
136 }
137
138 static uword
139 dhcpv6_client_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
140                        vlib_frame_t * frame)
141 {
142   dhcp6_ia_na_client_main_t *icm = &dhcp6_ia_na_client_main;
143   dhcp6_pd_client_main_t *pcm = &dhcp6_pd_client_main;
144
145   dhcpv6_client_next_t next_index;
146   u32 n_left_from, *from, *to_next;
147   next_index = 0;
148   n_left_from = frame->n_vectors;
149   from = vlib_frame_vector_args (frame);
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           ip6_header_t *ip0;
160           u32 options_length;
161           dhcpv6_header_t *dhcpv60;
162           dhcpv6_option_t *option;
163           vlib_buffer_t *b0;
164           dhcp6_report_common_t report;
165           dhcp6_address_info_t *addresses = 0;
166           dhcp6_prefix_info_t *prefixes = 0;
167           u32 next0 = DHCPV6_CLIENT_NEXT_DROP;
168           u32 bi0;
169           u32 xid;
170           u32 sw_if_index;
171           u32 iaid;
172           u8 client_id_present = 0;
173           u8 discard = 0;
174           u8 is_pd_packet = 0;
175
176           dhcp6_ia_na_client_state_t *ia_na_client_state = NULL;
177           dhcp6_pd_client_state_t *pd_client_state = NULL;
178
179           bi0 = from[0];
180           to_next[0] = bi0;
181           from += 1;
182           to_next += 1;
183           n_left_from -= 1;
184           n_left_to_next -= 1;
185
186           b0 = vlib_get_buffer (vm, bi0);
187
188           dhcpv60 = vlib_buffer_get_current (b0);
189           ip0 = (void *) (b0->data + vnet_buffer (b0)->l3_hdr_offset);
190           u32 dhcpv6_ip6_payload_offset =
191             (u8 *) dhcpv60 - ((u8 *) ip0 + sizeof (*ip0));
192           options_length =
193             ntohs (ip0->payload_length) - dhcpv6_ip6_payload_offset -
194             sizeof (*dhcpv60);
195
196           clib_memset (&report, 0, sizeof (report));
197
198           sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
199           if (sw_if_index >= vec_len (icm->client_state_by_sw_if_index))
200             ia_na_client_state = 0;
201           else
202             ia_na_client_state =
203               &icm->client_state_by_sw_if_index[sw_if_index];
204           if (sw_if_index >= vec_len (pcm->client_state_by_sw_if_index))
205             pd_client_state = 0;
206           else
207             pd_client_state = &pcm->client_state_by_sw_if_index[sw_if_index];
208
209           xid =
210             (dhcpv60->xid[0] << 16) + (dhcpv60->xid[1] << 8) +
211             dhcpv60->xid[2];
212           if (ia_na_client_state && ia_na_client_state->transaction_id == xid)
213             is_pd_packet = 0;
214           else if (pd_client_state && pd_client_state->transaction_id == xid)
215             is_pd_packet = 1;
216           else
217             {
218               clib_warning
219                 ("Received DHCPv6 message with wrong Transaction ID");
220               discard = 1;
221             }
222
223           report.sw_if_index = sw_if_index;
224           report.msg_type = dhcpv60->msg_type;
225           report.server_index = ~0;
226
227           switch (dhcpv60->msg_type)
228             {
229             case DHCPV6_MSG_ADVERTISE:
230             case DHCPV6_MSG_REPLY:
231               option = (dhcpv6_option_t *) (dhcpv60 + 1);
232               while (options_length > 0)
233                 {
234                   if (options_length <
235                       ntohs (option->length) + sizeof (*option))
236                     {
237                       clib_warning
238                         ("remaining payload length < option length (%d < %d)",
239                          options_length,
240                          ntohs (option->length) + sizeof (*option));
241                       break;
242                     }
243                   u16 oo = ntohs (option->option);
244                   if (oo == DHCPV6_OPTION_IA_NA || oo == DHCPV6_OPTION_IA_PD)
245                     {
246                       u8 discard_option = 0;
247                       dhcpv6_ia_header_t *ia_header = (void *) option;
248                       iaid = ntohl (ia_header->iaid);
249                       u32 T1 = ntohl (ia_header->t1);
250                       u32 T2 = ntohl (ia_header->t2);
251                       if (iaid != DHCPV6_CLIENT_IAID)
252                         discard_option = 1;
253                       if (T1 != 0 && T2 != 0 && T1 > T2)
254                         discard_option = 1;
255                       if (!discard_option)
256                         {
257                           report.T1 = T1;
258                           report.T2 = T2;
259                         }
260                       dhcpv6_option_t *inner_option =
261                         (void *) ia_header->data;
262                       u16 inner_options_length =
263                         ntohs (option->length) - (sizeof (*ia_header) -
264                                                   sizeof (dhcpv6_option_t));
265                       while (inner_options_length > 0)
266                         {
267                           u16 inner_oo = ntohs (inner_option->option);
268                           if (discard_option)
269                             ;
270                           else if (inner_oo == DHCPV6_OPTION_IAADDR)
271                             {
272                               dhcpv6_ia_opt_addr_t *iaaddr =
273                                 (void *) inner_option;
274                               u32 n_addresses = vec_len (addresses);
275                               vec_validate (addresses, n_addresses);
276                               dhcp6_address_info_t *address_info =
277                                 &addresses[n_addresses];
278                               address_info->preferred_time =
279                                 ntohl (iaaddr->preferred);
280                               address_info->valid_time =
281                                 ntohl (iaaddr->valid);
282                               address_info->address = iaaddr->addr;
283                             }
284                           else if (inner_oo == DHCPV6_OPTION_IAPREFIX)
285                             {
286                               dhcpv6_ia_opt_pd_t *iaprefix =
287                                 (void *) inner_option;
288                               u32 n_prefixes = vec_len (prefixes);
289                               vec_validate (prefixes, n_prefixes);
290                               dhcp6_prefix_info_t *prefix_info =
291                                 &prefixes[n_prefixes];
292                               prefix_info->preferred_time =
293                                 ntohl (iaprefix->preferred);
294                               prefix_info->valid_time =
295                                 ntohl (iaprefix->valid);
296                               prefix_info->prefix_length = iaprefix->prefix;
297                               prefix_info->prefix = iaprefix->addr;
298                             }
299                           else if (inner_oo == DHCPV6_OPTION_STATUS_CODE)
300                             {
301                               dhcpv6_status_code_t *sc =
302                                 (void *) inner_option;
303                               report.inner_status_code =
304                                 ntohs (sc->status_code);
305                             }
306                           inner_options_length -=
307                             sizeof (*inner_option) +
308                             ntohs (inner_option->length);
309                           inner_option =
310                             (void *) ((u8 *) inner_option +
311                                       sizeof (*inner_option) +
312                                       ntohs (inner_option->length));
313                         }
314                     }
315                   else if (oo == DHCPV6_OPTION_CLIENTID)
316                     {
317                       if (client_id_present)
318                         {
319                           clib_warning
320                             ("Duplicate Client ID in received DHVPv6 message");
321                           discard = 1;
322                         }
323                       else
324                         {
325                           u16 len = ntohs (option->length);
326                           client_id_present = 1;
327                           if (len != CLIENT_DUID_LENGTH ||
328                               0 != memcmp (option->data,
329                                            client_duid.bin_string,
330                                            CLIENT_DUID_LENGTH))
331                             {
332                               clib_warning
333                                 ("Unrecognized client DUID inside received DHVPv6 message");
334                               discard = 1;
335                             }
336                         }
337                     }
338                   else if (oo == DHCPV6_OPTION_SERVERID)
339                     {
340                       if (report.server_index != ~0)
341                         {
342                           clib_warning
343                             ("Duplicate Server ID in received DHVPv6 message");
344                           discard = 1;
345                         }
346                       else
347                         {
348                           u16 ol = ntohs (option->length);
349                           if (ol - 2 /* 2 byte DUID type code */  > 128)
350                             {
351                               clib_warning
352                                 ("Server DUID (without type code) is longer than 128 octets");
353                               discard = 1;
354                             }
355                           else
356                             {
357                               report.server_index =
358                                 server_index_get_or_create (option->data, ol);
359                             }
360                         }
361                     }
362                   else if (oo == DHCPV6_OPTION_PREFERENCE)
363                     {
364                       report.preference = option->data[0];
365                     }
366                   else if (oo == DHCPV6_OPTION_STATUS_CODE)
367                     {
368                       dhcpv6_status_code_t *sc = (void *) option;
369                       report.status_code = ntohs (sc->status_code);
370                     }
371                   options_length -= sizeof (*option) + ntohs (option->length);
372                   option =
373                     (void *) ((u8 *) option + sizeof (*option) +
374                               ntohs (option->length));
375                 }
376
377               if (!client_id_present)
378                 {
379                   clib_warning
380                     ("Missing Client ID in received DHVPv6 message");
381                   discard = 1;
382                 }
383               if (report.server_index == ~0)
384                 {
385                   clib_warning
386                     ("Missing Server ID in received DHVPv6 message");
387                   discard = 1;
388                 }
389
390               if (!discard)
391                 {
392                   if (!is_pd_packet)
393                     {
394                       address_report_t r;
395                       r.body = report;
396                       r.n_addresses = vec_len (addresses);
397                       r.addresses = addresses;
398                       dhcp6_publish_report (&r);
399                       /* We just gave addresses to another process! */
400                       addresses = 0;
401                     }
402                   else
403                     {
404                       prefix_report_t r;
405                       r.body = report;
406                       r.n_prefixes = vec_len (prefixes);
407                       r.prefixes = prefixes;
408                       dhcp6_pd_publish_report (&r);
409                       /* We just gave prefixes to another process! */
410                       prefixes = 0;
411                     }
412                 }
413               vec_free (addresses);
414               vec_free (prefixes);
415
416               break;
417             default:
418               break;
419             }
420
421           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
422             {
423               dhcpv6_client_trace_t *t =
424                 vlib_add_trace (vm, node, b0, sizeof (*t));
425             }
426
427           /* verify speculative enqueue, maybe switch current next frame */
428           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
429                                            to_next, n_left_to_next,
430                                            bi0, next0);
431         }
432
433       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
434     }
435
436   return frame->n_vectors;
437 }
438
439 /* *INDENT-OFF* */
440 VLIB_REGISTER_NODE (dhcpv6_client_node, static) = {
441     .function = dhcpv6_client_node_fn,
442     .name = "dhcpv6-client",
443     .vector_size = sizeof (u32),
444
445     .n_errors = 0,
446
447     .n_next_nodes = DHCPV6_CLIENT_N_NEXT,
448     .next_nodes = {
449   #define _(s,n) [DHCPV6_CLIENT_NEXT_##s] = n,
450       foreach_dhcpv6_client
451   #undef _
452     },
453
454     .format_trace = format_dhcpv6_client_trace,
455 };
456 /* *INDENT-ON* */
457
458 void
459 dhcp6_clients_enable_disable (u8 enable)
460 {
461   vlib_main_t *vm = vlib_get_main ();
462
463   if (enable)
464     {
465       if (client_duid.duid_type == 0)
466         generate_client_duid ();
467       udp_register_dst_port (vm, UDP_DST_PORT_dhcpv6_to_client,
468                              dhcpv6_client_node.index, 0 /* is_ip6 */ );
469     }
470   else
471     udp_unregister_dst_port (vm, UDP_DST_PORT_dhcpv6_to_client,
472                              0 /* is_ip6 */ );
473 }
474
475 void
476   vl_api_dhcp6_clients_enable_disable_t_handler
477   (vl_api_dhcp6_clients_enable_disable_t * mp)
478 {
479   vl_api_dhcp6_clients_enable_disable_reply_t *rmp;
480   int rv = 0;
481
482   dhcp6_clients_enable_disable (mp->enable);
483
484   REPLY_MACRO (VL_API_DHCP6_CLIENTS_ENABLE_DISABLE_REPLY);
485 }
486
487 /*
488  * fd.io coding-style-patch-verification: ON
489  *
490  * Local Variables:
491  * eval: (c-set-style "gnu")
492  * End:
493  */