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

Generated by: LCOV version 1.14