LCOV - code coverage report
Current view: top level - plugins/dhcp - dhcp6_ia_na_client_dp.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 205 217 94.5 %
Date: 2023-10-26 01:39:38 Functions: 13 13 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 <vlib/vlib.h>
      17             : #include <dhcp/dhcp6_packet.h>
      18             : #include <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_link.h>
      24             : #include <dhcp/dhcp6_ia_na_client_dp.h>
      25             : #include <dhcp/dhcp6_client_common_dp.h>
      26             : #include <vnet/ip/ip_types_api.h>
      27             : 
      28             : dhcp6_ia_na_client_main_t dhcp6_ia_na_client_main;
      29             : dhcp6_ia_na_client_public_main_t dhcp6_ia_na_client_public_main;
      30             : 
      31             : static void
      32          14 : signal_report (address_report_t * r)
      33             : {
      34          14 :   vlib_main_t *vm = vlib_get_main ();
      35          14 :   dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
      36          14 :   uword ni = cm->publisher_node;
      37          14 :   uword et = cm->publisher_et;
      38             : 
      39          14 :   if (ni == (uword) ~ 0)
      40           0 :     return;
      41             :   address_report_t *q =
      42          14 :     vlib_process_signal_event_data (vm, ni, et, 1, sizeof *q);
      43             : 
      44          14 :   *q = *r;
      45             : }
      46             : 
      47             : int
      48          14 : dhcp6_publish_report (address_report_t * r)
      49             : {
      50             :   void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
      51          14 :   vl_api_rpc_call_main_thread (signal_report, (u8 *) r, sizeof *r);
      52          14 :   return 0;
      53             : }
      54             : 
      55             : void
      56         577 : dhcp6_set_publisher_node (uword node_index, uword event_type)
      57             : {
      58         577 :   dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
      59         577 :   cm->publisher_node = node_index;
      60         577 :   cm->publisher_et = event_type;
      61         577 : }
      62             : 
      63             : static void
      64          30 : stop_sending_client_message (vlib_main_t * vm,
      65             :                              dhcp6_ia_na_client_state_t * client_state)
      66             : {
      67             :   u32 bi0;
      68             : 
      69          30 :   client_state->keep_sending_client_message = 0;
      70          30 :   vec_free (client_state->params.addresses);
      71          30 :   if (client_state->buffer)
      72             :     {
      73          17 :       bi0 = vlib_get_buffer_index (vm, client_state->buffer);
      74          17 :       vlib_buffer_free (vm, &bi0, 1);
      75          17 :       client_state->buffer = 0;
      76          17 :       adj_unlock (client_state->adj_index);
      77          17 :       client_state->adj_index = ~0;
      78             :     }
      79          30 : }
      80             : 
      81             : static vlib_buffer_t *
      82          17 : create_buffer_for_client_message (vlib_main_t * vm, u32 sw_if_index,
      83             :                                   dhcp6_ia_na_client_state_t * client_state,
      84             :                                   u32 type)
      85             : {
      86          17 :   dhcp6_client_common_main_t *ccm = &dhcp6_client_common_main;
      87             :   vlib_buffer_t *b;
      88             :   u32 bi;
      89             :   ip6_header_t *ip;
      90             :   udp_header_t *udp;
      91             :   dhcpv6_header_t *dhcp;
      92             :   const ip6_address_t *src_addr;
      93          17 :   u32 dhcp_opt_len = 0;
      94          17 :   client_state->transaction_start = vlib_time_now (vm);
      95             :   u32 n_addresses;
      96             :   u32 i;
      97             : 
      98             :   /* Get a link-local address */
      99          17 :   src_addr = ip6_get_link_local_address (sw_if_index);
     100             : 
     101          17 :   if (src_addr->as_u8[0] != 0xfe)
     102             :     {
     103           0 :       clib_warning ("Could not find source address to send DHCPv6 packet");
     104           0 :       return NULL;
     105             :     }
     106             : 
     107          17 :   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
     108             :     {
     109           0 :       clib_warning ("Buffer allocation failed");
     110           0 :       return NULL;
     111             :     }
     112             : 
     113          17 :   b = vlib_get_buffer (vm, bi);
     114          17 :   vnet_buffer (b)->sw_if_index[VLIB_RX] = sw_if_index;
     115          17 :   vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
     116          17 :   client_state->adj_index = adj_mcast_add_or_lock (FIB_PROTOCOL_IP6,
     117             :                                                    VNET_LINK_IP6,
     118             :                                                    sw_if_index);
     119          17 :   vnet_buffer (b)->ip.adj_index[VLIB_TX] = client_state->adj_index;
     120          17 :   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     121             : 
     122          17 :   ip = (ip6_header_t *) vlib_buffer_get_current (b);
     123          17 :   udp = (udp_header_t *) (ip + 1);
     124          17 :   dhcp = (dhcpv6_header_t *) (udp + 1);
     125             : 
     126          17 :   ip->src_address = *src_addr;
     127          17 :   ip->hop_limit = 255;
     128          17 :   ip->ip_version_traffic_class_and_flow_label =
     129          17 :     clib_host_to_net_u32 (0x6 << 28);
     130          17 :   ip->payload_length = 0;
     131          17 :   ip->protocol = IP_PROTOCOL_UDP;
     132             : 
     133          17 :   udp->src_port = clib_host_to_net_u16 (DHCPV6_CLIENT_PORT);
     134          17 :   udp->dst_port = clib_host_to_net_u16 (DHCPV6_SERVER_PORT);
     135          17 :   udp->checksum = 0;
     136          17 :   udp->length = 0;
     137             : 
     138          17 :   dhcp->msg_type = type;
     139          17 :   dhcp->xid[0] = (client_state->transaction_id & 0x00ff0000) >> 16;
     140          17 :   dhcp->xid[1] = (client_state->transaction_id & 0x0000ff00) >> 8;
     141          17 :   dhcp->xid[2] = (client_state->transaction_id & 0x000000ff) >> 0;
     142             : 
     143          17 :   void *d = (void *) dhcp->data;
     144             :   dhcpv6_option_t *duid;
     145             :   dhcpv6_elapsed_t *elapsed;
     146             :   dhcpv6_ia_header_t *ia_hdr;
     147             :   dhcpv6_ia_opt_addr_t *opt_addr;
     148          17 :   if (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST ||
     149           1 :       type == DHCPV6_MSG_RENEW || type == DHCPV6_MSG_REBIND ||
     150             :       type == DHCPV6_MSG_RELEASE)
     151             :     {
     152          17 :       duid = (dhcpv6_option_t *) d;
     153          17 :       duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_CLIENTID);
     154          17 :       duid->length = clib_host_to_net_u16 (CLIENT_DUID_LENGTH);
     155          17 :       clib_memcpy (duid + 1, client_duid.bin_string, CLIENT_DUID_LENGTH);
     156          17 :       d += sizeof (*duid) + CLIENT_DUID_LENGTH;
     157             : 
     158          17 :       if (client_state->params.server_index != ~0)
     159             :         {
     160           7 :           server_id_t *se =
     161           7 :             &ccm->server_ids[client_state->params.server_index];
     162             : 
     163           7 :           duid = (dhcpv6_option_t *) d;
     164           7 :           duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_SERVERID);
     165           7 :           duid->length = clib_host_to_net_u16 (se->len);
     166           7 :           clib_memcpy (duid + 1, se->data, se->len);
     167           7 :           d += sizeof (*duid) + se->len;
     168             :         }
     169             : 
     170          17 :       elapsed = (dhcpv6_elapsed_t *) d;
     171          17 :       elapsed->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_ELAPSED_TIME);
     172          17 :       elapsed->opt.length =
     173          17 :         clib_host_to_net_u16 (sizeof (*elapsed) - sizeof (elapsed->opt));
     174          17 :       elapsed->elapsed_10ms = 0;
     175          17 :       client_state->elapsed_pos =
     176          34 :         (char *) &elapsed->elapsed_10ms -
     177          17 :         (char *) vlib_buffer_get_current (b);
     178          17 :       d += sizeof (*elapsed);
     179             : 
     180          17 :       ia_hdr = (dhcpv6_ia_header_t *) d;
     181          17 :       ia_hdr->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IA_NA);
     182          17 :       ia_hdr->iaid = clib_host_to_net_u32 (DHCPV6_CLIENT_IAID);
     183          17 :       ia_hdr->t1 = clib_host_to_net_u32 (client_state->params.T1);
     184          17 :       ia_hdr->t2 = clib_host_to_net_u32 (client_state->params.T2);
     185          17 :       d += sizeof (*ia_hdr);
     186             : 
     187          17 :       n_addresses = vec_len (client_state->params.addresses);
     188             : 
     189          17 :       ia_hdr->opt.length =
     190          17 :         clib_host_to_net_u16 (sizeof (*ia_hdr) +
     191          17 :                               n_addresses * sizeof (*opt_addr) -
     192             :                               sizeof (ia_hdr->opt));
     193             : 
     194          19 :       for (i = 0; i < n_addresses; i++)
     195             :         {
     196           2 :           dhcp6_send_client_message_params_address_t *addr =
     197           2 :             &client_state->params.addresses[i];
     198           2 :           opt_addr = (dhcpv6_ia_opt_addr_t *) d;
     199           2 :           opt_addr->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IAADDR);
     200           2 :           opt_addr->opt.length =
     201           2 :             clib_host_to_net_u16 (sizeof (*opt_addr) -
     202             :                                   sizeof (opt_addr->opt));
     203           2 :           opt_addr->addr = addr->address;
     204           2 :           opt_addr->valid = clib_host_to_net_u32 (addr->valid_lt);
     205           2 :           opt_addr->preferred = clib_host_to_net_u32 (addr->preferred_lt);
     206           2 :           d += sizeof (*opt_addr);
     207             :         }
     208             :     }
     209             :   else
     210             :     {
     211           0 :       clib_warning ("State not implemented");
     212             :     }
     213             : 
     214          17 :   dhcp_opt_len = ((u8 *) d) - dhcp->data;
     215          17 :   udp->length =
     216          17 :     clib_host_to_net_u16 (sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len);
     217          17 :   ip->payload_length = udp->length;
     218          17 :   b->current_length =
     219          17 :     sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len;
     220             : 
     221          17 :   ip->dst_address = all_dhcp6_relay_agents_and_servers;
     222             : 
     223          17 :   return b;
     224             : }
     225             : 
     226             : static inline u8
     227          27 : check_send_client_message (vlib_main_t * vm,
     228             :                            dhcp6_ia_na_client_state_t * client_state,
     229             :                            f64 current_time, f64 * due_time)
     230             : {
     231             :   vlib_buffer_t *p0;
     232             :   vlib_frame_t *f;
     233             :   u32 *to_next;
     234             :   u32 next_index;
     235             :   vlib_buffer_t *c0;
     236             :   ip6_header_t *ip;
     237             :   udp_header_t *udp;
     238             :   u32 ci0;
     239          27 :   int bogus_length = 0;
     240             : 
     241             :   dhcp6_send_client_message_params_t *params;
     242             : 
     243          27 :   f64 now = vlib_time_now (vm);
     244             : 
     245          27 :   if (!client_state->keep_sending_client_message)
     246           1 :     return false;
     247             : 
     248          26 :   params = &client_state->params;
     249             : 
     250          26 :   if (client_state->due_time > current_time)
     251             :     {
     252           5 :       *due_time = client_state->due_time;
     253           5 :       return true;
     254             :     }
     255             : 
     256          21 :   p0 = client_state->buffer;
     257             : 
     258          21 :   next_index = ip6_rewrite_mcast_node.index;
     259             : 
     260          21 :   c0 = vlib_buffer_copy (vm, p0);
     261          21 :   if (c0 == NULL)
     262           0 :     return client_state->keep_sending_client_message;
     263             : 
     264          21 :   ci0 = vlib_get_buffer_index (vm, c0);
     265             : 
     266          21 :   ip = (ip6_header_t *) vlib_buffer_get_current (c0);
     267          21 :   udp = (udp_header_t *) (ip + 1);
     268             : 
     269          21 :   u16 *elapsed_field = (u16 *) ((void *) ip + client_state->elapsed_pos);
     270          21 :   *elapsed_field =
     271          21 :     clib_host_to_net_u16 ((u16)
     272          21 :                           ((now - client_state->transaction_start) * 100));
     273             : 
     274          21 :   udp->checksum = 0;
     275          21 :   udp->checksum =
     276          21 :     ip6_tcp_udp_icmp_compute_checksum (vm, 0, ip, &bogus_length);
     277             : 
     278          21 :   f = vlib_get_frame_to_node (vm, next_index);
     279          21 :   to_next = vlib_frame_vector_args (f);
     280          21 :   to_next[0] = ci0;
     281          21 :   f->n_vectors = 1;
     282          21 :   vlib_put_frame_to_node (vm, next_index, f);
     283             : 
     284          21 :   if (params->mrc != 0 && --client_state->n_left == 0)
     285           1 :     stop_sending_client_message (vm, client_state);
     286             :   else
     287             :     {
     288          20 :       client_state->sleep_interval =
     289          20 :         (2 + random_f64_from_to (-0.1, 0.1)) * client_state->sleep_interval;
     290          20 :       if (client_state->sleep_interval > params->mrt)
     291           0 :         client_state->sleep_interval =
     292           0 :           (1 + random_f64_from_to (-0.1, 0.1)) * params->mrt;
     293             : 
     294          20 :       client_state->due_time = current_time + client_state->sleep_interval;
     295             : 
     296          20 :       if (params->mrd != 0
     297           0 :           && current_time > client_state->start_time + params->mrd)
     298           0 :         stop_sending_client_message (vm, client_state);
     299             :       else
     300          20 :         *due_time = client_state->due_time;
     301             :     }
     302             : 
     303          21 :   return client_state->keep_sending_client_message;
     304             : }
     305             : 
     306             : static uword
     307         575 : send_dhcp6_client_message_process (vlib_main_t * vm,
     308             :                                    vlib_node_runtime_t * rt,
     309             :                                    vlib_frame_t * f0)
     310             : {
     311         575 :   dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
     312             :   dhcp6_ia_na_client_state_t *client_state;
     313         575 :   uword *event_data = 0;
     314         575 :   f64 sleep_time = 1e9;
     315             :   f64 current_time;
     316             :   f64 due_time;
     317         575 :   f64 dt = 0;
     318             :   int i;
     319             : 
     320             :   while (true)
     321             :     {
     322         602 :       vlib_process_wait_for_event_or_clock (vm, sleep_time);
     323          27 :       vlib_process_get_events (vm, &event_data);
     324          27 :       vec_reset_length (event_data);
     325             : 
     326          27 :       current_time = vlib_time_now (vm);
     327             :       do
     328             :         {
     329          27 :           due_time = current_time + 1e9;
     330          81 :           for (i = 0; i < vec_len (cm->client_state_by_sw_if_index); i++)
     331             :             {
     332          54 :               client_state = &cm->client_state_by_sw_if_index[i];
     333          54 :               if (!client_state->entry_valid)
     334          27 :                 continue;
     335          27 :               if (check_send_client_message
     336          25 :                   (vm, client_state, current_time, &dt) && (dt < due_time))
     337          25 :                 due_time = dt;
     338             :             }
     339          27 :           current_time = vlib_time_now (vm);
     340             :         }
     341          27 :       while (due_time < current_time);
     342             : 
     343          27 :       sleep_time = due_time - current_time;
     344             :     }
     345             : 
     346             :   return 0;
     347             : }
     348             : 
     349             : /* *INDENT-OFF* */
     350      153836 : VLIB_REGISTER_NODE (send_dhcp6_client_message_process_node, static) = {
     351             :     .function = send_dhcp6_client_message_process,
     352             :     .type = VLIB_NODE_TYPE_PROCESS,
     353             :     .name = "send-dhcp6-client-message-process",
     354             : };
     355             : /* *INDENT-ON* */
     356             : 
     357             : void
     358          29 : dhcp6_send_client_message (vlib_main_t * vm, u32 sw_if_index, u8 stop,
     359             :                            dhcp6_send_client_message_params_t * params)
     360             : {
     361          29 :   dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
     362          29 :   dhcp6_ia_na_client_state_t *client_state = 0;
     363          29 :   dhcp6_ia_na_client_state_t empty_state = { 0, };
     364             : 
     365          29 :   ASSERT (~0 != sw_if_index);
     366             : 
     367          33 :   vec_validate_init_empty (cm->client_state_by_sw_if_index, sw_if_index,
     368             :                            empty_state);
     369          29 :   client_state = &cm->client_state_by_sw_if_index[sw_if_index];
     370          29 :   if (!client_state->entry_valid)
     371             :     {
     372           2 :       client_state->entry_valid = 1;
     373           2 :       client_state->adj_index = ~0;
     374             :     }
     375             : 
     376          29 :   stop_sending_client_message (vm, client_state);
     377             : 
     378          29 :   if (!stop)
     379             :     {
     380          17 :       client_state->keep_sending_client_message = 1;
     381          17 :       vec_free (client_state->params.addresses);
     382          17 :       client_state->params = *params;
     383          17 :       client_state->params.addresses = vec_dup (params->addresses);
     384          17 :       client_state->n_left = params->mrc;
     385          17 :       client_state->start_time = vlib_time_now (vm);
     386          17 :       client_state->sleep_interval =
     387          17 :         (1 + random_f64_from_to (-0.1, 0.1)) * params->irt;
     388          17 :       client_state->due_time = 0;    /* send first packet ASAP */
     389          17 :       client_state->transaction_id = random_u32 (&cm->seed) & 0x00ffffff;
     390          17 :       client_state->buffer =
     391          17 :         create_buffer_for_client_message (vm, sw_if_index, client_state,
     392          17 :                                           params->msg_type);
     393          17 :       if (!client_state->buffer)
     394           0 :         client_state->keep_sending_client_message = 0;
     395             :       else
     396          17 :         vlib_process_signal_event (vm,
     397          17 :                                    send_dhcp6_client_message_process_node.index,
     398             :                                    1, 0);
     399             :     }
     400          29 : }
     401             : 
     402             : static clib_error_t *
     403         575 : dhcp6_client_init (vlib_main_t * vm)
     404             : {
     405         575 :   dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
     406             : 
     407         575 :   cm->vlib_main = vm;
     408         575 :   cm->vnet_main = vnet_get_main ();
     409             : 
     410         575 :   cm->publisher_node = ~0;
     411             : 
     412         575 :   cm->seed = 0xdeaccabe;
     413             : 
     414         575 :   return 0;
     415             : }
     416             : 
     417        1727 : VLIB_INIT_FUNCTION (dhcp6_client_init);
     418             : 
     419             : /*
     420             :  * fd.io coding-style-patch-verification: ON
     421             :  *
     422             :  * Local Variables:
     423             :  * eval: (c-set-style "gnu")
     424             :  * End:
     425             :  */

Generated by: LCOV version 1.14