LCOV - code coverage report
Current view: top level - plugins/dhcp - dhcp6_client_common_dp.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 195 221 88.2 %
Date: 2023-10-26 01:39:38 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.14