LCOV - code coverage report
Current view: top level - plugins/wireguard - wireguard_timer.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 144 195 73.8 %
Date: 2023-10-26 01:39:38 Functions: 27 31 87.1 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 Doc.ai 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 <vlibmemory/api.h>
      17             : #include <wireguard/wireguard.h>
      18             : #include <wireguard/wireguard_send.h>
      19             : #include <wireguard/wireguard_timer.h>
      20             : 
      21             : static u32
      22         298 : get_random_u32_max (u32 max)
      23             : {
      24         298 :   vlib_main_t *vm = vlib_get_main ();
      25         298 :   u32 seed = (u32) (vlib_time_now (vm) * 1e6);
      26         298 :   return random_u32 (&seed) % max;
      27             : }
      28             : 
      29             : static u32
      30          41 : get_random_u32_max_opt (u32 max, f64 time)
      31             : {
      32          41 :   u32 seed = (u32) (time * 1e6);
      33          41 :   return random_u32 (&seed) % max;
      34             : }
      35             : 
      36             : static void
      37         750 : stop_timer (wg_peer_t * peer, u32 timer_id)
      38             : {
      39         750 :   if (peer->timers[timer_id] != ~0)
      40             :     {
      41         437 :       tw_timer_stop_16t_2w_512sl (peer->timer_wheel, peer->timers[timer_id]);
      42         437 :       peer->timers[timer_id] = ~0;
      43             :     }
      44         750 : }
      45             : 
      46             : static void
      47         591 : start_timer (wg_peer_t * peer, u32 timer_id, u32 interval_ticks)
      48             : {
      49         591 :   ASSERT (vlib_get_thread_index () == 0);
      50             : 
      51         591 :   if (peer->timers[timer_id] == ~0)
      52             :     {
      53         591 :       peer->timers[timer_id] =
      54         591 :         tw_timer_start_16t_2w_512sl (peer->timer_wheel, peer - wg_peer_pool,
      55             :                                      timer_id, interval_ticks);
      56             :     }
      57         591 : }
      58             : 
      59             : typedef struct
      60             : {
      61             :   u32 peer_idx;
      62             :   u32 timer_id;
      63             :   u32 interval_ticks;
      64             : 
      65             : } wg_timers_args;
      66             : 
      67             : static void *
      68         591 : start_timer_thread_fn (void *arg)
      69             : {
      70         591 :   wg_timers_args *a = arg;
      71         591 :   wg_peer_t *peer = wg_peer_get (a->peer_idx);
      72         591 :   start_timer (peer, a->timer_id, a->interval_ticks);
      73         591 :   return 0;
      74             : }
      75             : 
      76             : static_always_inline void
      77        1419 : start_timer_from_mt (u32 peer_idx, u32 timer_id, u32 interval_ticks)
      78             : {
      79        1419 :   wg_timers_args a = {
      80             :     .peer_idx = peer_idx,
      81             :     .timer_id = timer_id,
      82             :     .interval_ticks = interval_ticks,
      83             :   };
      84        1419 :   wg_peer_t *peer = wg_peer_get (peer_idx);
      85        1419 :   if (PREDICT_FALSE (!peer->timers_dispatched[timer_id]))
      86         591 :     if (!clib_atomic_cmp_and_swap (&peer->timers_dispatched[timer_id], 0, 1))
      87         591 :       vl_api_rpc_call_main_thread (start_timer_thread_fn, (u8 *) &a,
      88             :                                    sizeof (a));
      89        1419 : }
      90             : 
      91             : static inline u32
      92         150 : timer_ticks_left (vlib_main_t * vm, f64 init_time_sec, u32 interval_ticks)
      93             : {
      94             :   static const int32_t rounding = (int32_t) (WHZ / 2);
      95             :   int32_t ticks_remain;
      96             : 
      97         150 :   ticks_remain = (init_time_sec - vlib_time_now (vm)) * WHZ + interval_ticks;
      98         150 :   return (ticks_remain > rounding) ? (u32) ticks_remain : 0;
      99             : }
     100             : 
     101             : static void
     102          16 : wg_expired_retransmit_handshake (vlib_main_t * vm, wg_peer_t * peer)
     103             : {
     104          16 :   if (peer->rehandshake_started == ~0)
     105           4 :     return;
     106             : 
     107          12 :   u32 ticks = timer_ticks_left (vm, peer->rehandshake_started,
     108             :                                 peer->rehandshake_interval_tick);
     109          12 :   if (ticks)
     110             :     {
     111           0 :       start_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE, ticks);
     112           0 :       return;
     113             :     }
     114             : 
     115          12 :   if (peer->timer_handshake_attempts > MAX_TIMER_HANDSHAKES)
     116             :     {
     117           0 :       stop_timer (peer, WG_TIMER_SEND_KEEPALIVE);
     118             : 
     119             :       /* We set a timer for destroying any residue that might be left
     120             :        * of a partial exchange.
     121             :        */
     122           0 :       start_timer (peer, WG_TIMER_KEY_ZEROING, REJECT_AFTER_TIME * 3 * WHZ);
     123             : 
     124             :     }
     125             :   else
     126             :     {
     127          12 :       ++peer->timer_handshake_attempts;
     128          12 :       wg_send_handshake (vm, peer, true);
     129             :     }
     130             : }
     131             : 
     132             : static void
     133           0 : wg_expired_send_keepalive (vlib_main_t * vm, wg_peer_t * peer)
     134             : {
     135           0 :   if (peer->last_sent_packet < peer->last_received_packet)
     136             :     {
     137           0 :       u32 ticks = timer_ticks_left (vm, peer->last_received_packet,
     138             :                                     KEEPALIVE_TIMEOUT * WHZ);
     139           0 :       if (ticks)
     140             :         {
     141           0 :           start_timer (peer, WG_TIMER_SEND_KEEPALIVE, ticks);
     142           0 :           return;
     143             :         }
     144             : 
     145           0 :       wg_send_keepalive (vm, peer);
     146           0 :       if (peer->timer_need_another_keepalive)
     147             :         {
     148           0 :           peer->timer_need_another_keepalive = false;
     149           0 :           start_timer (peer, WG_TIMER_SEND_KEEPALIVE,
     150             :                        KEEPALIVE_TIMEOUT * WHZ);
     151             :         }
     152             :     }
     153             : }
     154             : 
     155             : static void
     156           0 : wg_expired_send_persistent_keepalive (vlib_main_t * vm, wg_peer_t * peer)
     157             : {
     158           0 :   if (peer->persistent_keepalive_interval)
     159             :     {
     160           0 :       f64 latest_time = peer->last_sent_packet > peer->last_received_packet
     161           0 :         ? peer->last_sent_packet : peer->last_received_packet;
     162             : 
     163           0 :       u32 ticks = timer_ticks_left (vm, latest_time,
     164           0 :                                     peer->persistent_keepalive_interval *
     165             :                                     WHZ);
     166           0 :       if (ticks)
     167             :         {
     168           0 :           start_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE, ticks);
     169           0 :           return;
     170             :         }
     171             : 
     172           0 :       wg_send_keepalive (vm, peer);
     173             :     }
     174             : }
     175             : 
     176             : static void
     177         138 : wg_expired_new_handshake (vlib_main_t * vm, wg_peer_t * peer)
     178             : {
     179         138 :   u32 ticks = timer_ticks_left (vm, peer->last_sent_packet,
     180             :                                 peer->new_handshake_interval_tick);
     181         138 :   if (ticks)
     182             :     {
     183           0 :       start_timer (peer, WG_TIMER_NEW_HANDSHAKE, ticks);
     184           0 :       return;
     185             :     }
     186             : 
     187         138 :   wg_send_handshake (vm, peer, false);
     188             : }
     189             : 
     190             : static void
     191           0 : wg_expired_zero_key_material (vlib_main_t * vm, wg_peer_t * peer)
     192             : {
     193             :   u32 ticks =
     194           0 :     timer_ticks_left (vm, peer->session_derived, REJECT_AFTER_TIME * 3 * WHZ);
     195           0 :   if (ticks)
     196             :     {
     197           0 :       start_timer (peer, WG_TIMER_KEY_ZEROING, ticks);
     198           0 :       return;
     199             :     }
     200             : 
     201           0 :   if (!wg_peer_is_dead (peer))
     202             :     {
     203           0 :       noise_remote_clear (vm, &peer->remote);
     204             :     }
     205             : }
     206             : 
     207             : inline void
     208         433 : wg_timers_any_authenticated_packet_traversal (wg_peer_t *peer)
     209             : {
     210         433 :   if (peer->persistent_keepalive_interval)
     211             :     {
     212         433 :       start_timer_from_mt (peer - wg_peer_pool,
     213             :                            WG_TIMER_PERSISTENT_KEEPALIVE,
     214         433 :                            peer->persistent_keepalive_interval * WHZ);
     215             :     }
     216         433 : }
     217             : 
     218             : void
     219         244 : wg_timers_any_authenticated_packet_sent (wg_peer_t * peer)
     220             : {
     221         244 :   peer->last_sent_packet = vlib_time_now (vlib_get_main ());
     222         244 : }
     223             : 
     224             : inline void
     225          41 : wg_timers_any_authenticated_packet_sent_opt (wg_peer_t *peer, f64 time)
     226             : {
     227          41 :   peer->last_sent_packet = time;
     228          41 : }
     229             : 
     230             : void
     231         150 : wg_timers_handshake_initiated (wg_peer_t * peer)
     232             : {
     233         150 :   peer->rehandshake_started = vlib_time_now (vlib_get_main ());
     234         150 :   peer->rehandshake_interval_tick =
     235         150 :     REKEY_TIMEOUT * WHZ + get_random_u32_max (REKEY_TIMEOUT_JITTER);
     236             : 
     237         150 :   start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_RETRANSMIT_HANDSHAKE,
     238             :                        peer->rehandshake_interval_tick);
     239         150 : }
     240             : 
     241             : void
     242         148 : wg_timers_send_first_handshake (wg_peer_t *peer)
     243             : {
     244             :   // zero value is not allowed
     245         148 :   peer->new_handshake_interval_tick =
     246         148 :     get_random_u32_max (REKEY_TIMEOUT_JITTER) + 1;
     247         148 :   start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_NEW_HANDSHAKE,
     248             :                        peer->new_handshake_interval_tick);
     249         148 : }
     250             : 
     251             : void
     252          94 : wg_timers_session_derived (wg_peer_t * peer)
     253             : {
     254          94 :   peer->session_derived = vlib_time_now (vlib_get_main ());
     255             : 
     256          94 :   start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_KEY_ZEROING,
     257             :                        REJECT_AFTER_TIME * 3 * WHZ);
     258          94 : }
     259             : 
     260             : /* Should be called after an authenticated data packet is sent. */
     261             : void
     262           0 : wg_timers_data_sent (wg_peer_t * peer)
     263             : {
     264           0 :   peer->new_handshake_interval_tick =
     265           0 :     (KEEPALIVE_TIMEOUT + REKEY_TIMEOUT) * WHZ +
     266           0 :     get_random_u32_max (REKEY_TIMEOUT_JITTER);
     267             : 
     268           0 :   start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_NEW_HANDSHAKE,
     269             :                        peer->new_handshake_interval_tick);
     270           0 : }
     271             : 
     272             : inline void
     273          41 : wg_timers_data_sent_opt (wg_peer_t *peer, f64 time)
     274             : {
     275          41 :   peer->new_handshake_interval_tick =
     276          41 :     (KEEPALIVE_TIMEOUT + REKEY_TIMEOUT) * WHZ +
     277          41 :     get_random_u32_max_opt (REKEY_TIMEOUT_JITTER, time);
     278             : 
     279          41 :   start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_NEW_HANDSHAKE,
     280             :                        peer->new_handshake_interval_tick);
     281          41 : }
     282             : 
     283             : /* Should be called after an authenticated data packet is received. */
     284             : void
     285        3624 : wg_timers_data_received (wg_peer_t * peer)
     286             : {
     287        3624 :   if (peer->timers[WG_TIMER_SEND_KEEPALIVE] == ~0)
     288             :     {
     289         553 :       start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_SEND_KEEPALIVE,
     290             :                            KEEPALIVE_TIMEOUT * WHZ);
     291             :     }
     292             :   else
     293        3071 :     peer->timer_need_another_keepalive = true;
     294        3624 : }
     295             : 
     296             : /* Should be called after a handshake response message is received and processed
     297             :  * or when getting key confirmation via the first data message.
     298             :  */
     299             : void
     300          56 : wg_timers_handshake_complete (wg_peer_t * peer)
     301             : {
     302          56 :   peer->rehandshake_started = ~0;
     303          56 :   peer->timer_handshake_attempts = 0;
     304          56 : }
     305             : 
     306             : void
     307          94 : wg_timers_any_authenticated_packet_received (wg_peer_t * peer)
     308             : {
     309          94 :   peer->last_received_packet = vlib_time_now (vlib_get_main ());
     310          94 : }
     311             : 
     312             : inline void
     313          54 : wg_timers_any_authenticated_packet_received_opt (wg_peer_t *peer, f64 time)
     314             : {
     315          54 :   peer->last_received_packet = time;
     316          54 : }
     317             : 
     318             : static vlib_node_registration_t wg_timer_mngr_node;
     319             : 
     320             : static void
     321         133 : expired_timer_callback (u32 * expired_timers)
     322             : {
     323             :   int i;
     324             :   u32 timer_id;
     325             :   u32 pool_index;
     326             : 
     327         133 :   wg_main_t *wmp = &wg_main;
     328         133 :   vlib_main_t *vm = wmp->vlib_main;
     329             : 
     330             :   wg_peer_t *peer;
     331             : 
     332             :   /* Need to invalidate all of them because one can restart other */
     333         287 :   for (i = 0; i < vec_len (expired_timers); i++)
     334             :     {
     335         154 :       pool_index = expired_timers[i] & 0x0FFFFFFF;
     336         154 :       timer_id = expired_timers[i] >> 28;
     337             : 
     338         154 :       peer = wg_peer_get (pool_index);
     339         154 :       peer->timers[timer_id] = ~0;
     340             : 
     341             :       /* Under barrier, no sync needed */
     342         154 :       peer->timers_dispatched[timer_id] = 0;
     343             :     }
     344             : 
     345         287 :   for (i = 0; i < vec_len (expired_timers); i++)
     346             :     {
     347         154 :       pool_index = expired_timers[i] & 0x0FFFFFFF;
     348         154 :       timer_id = expired_timers[i] >> 28;
     349             : 
     350         154 :       peer = wg_peer_get (pool_index);
     351         154 :       switch (timer_id)
     352             :         {
     353          16 :         case WG_TIMER_RETRANSMIT_HANDSHAKE:
     354          16 :           wg_expired_retransmit_handshake (vm, peer);
     355          16 :           break;
     356           0 :         case WG_TIMER_PERSISTENT_KEEPALIVE:
     357           0 :           wg_expired_send_persistent_keepalive (vm, peer);
     358           0 :           break;
     359           0 :         case WG_TIMER_SEND_KEEPALIVE:
     360           0 :           wg_expired_send_keepalive (vm, peer);
     361           0 :           break;
     362         138 :         case WG_TIMER_NEW_HANDSHAKE:
     363         138 :           wg_expired_new_handshake (vm, peer);
     364         138 :           break;
     365           0 :         case WG_TIMER_KEY_ZEROING:
     366           0 :           wg_expired_zero_key_material (vm, peer);
     367           0 :           break;
     368           0 :         default:
     369           0 :           break;
     370             :         }
     371             :     }
     372         133 : }
     373             : 
     374             : void
     375         575 : wg_timer_wheel_init ()
     376             : {
     377         575 :   wg_main_t *wmp = &wg_main;
     378         575 :   tw_timer_wheel_16t_2w_512sl_t *tw = &wmp->timer_wheel;
     379         575 :   tw_timer_wheel_init_16t_2w_512sl (tw,
     380             :                                     expired_timer_callback,
     381             :                                     WG_TICK /* timer period in s */ , ~0);
     382         575 : }
     383             : 
     384             : static uword
     385         575 : wg_timer_mngr_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
     386             :                   vlib_frame_t * f)
     387             : {
     388         575 :   wg_main_t *wmp = &wg_main;
     389         575 :   uword event_type = 0;
     390             : 
     391             :   /* Park the process until the feature is configured */
     392             :   while (1)
     393             :     {
     394         575 :       vlib_process_wait_for_event (vm);
     395           3 :       event_type = vlib_process_get_events (vm, 0);
     396           3 :       if (event_type == WG_START_EVENT)
     397             :         {
     398           3 :           break;
     399             :         }
     400             :       else
     401             :         {
     402           0 :           clib_warning ("Unknown event type %d", event_type);
     403             :         }
     404             :     }
     405             :   /*
     406             :    * Reset the timer wheel time so it won't try to
     407             :    * expire Avogadro's number of time slots.
     408             :    */
     409           3 :   wmp->timer_wheel.last_run_time = vlib_time_now (vm);
     410             : 
     411             :   while (1)
     412             :     {
     413       28200 :       vlib_process_wait_for_event_or_clock (vm, WG_TICK);
     414       28197 :       vlib_process_get_events (vm, NULL);
     415             : 
     416       28197 :       tw_timer_expire_timers_16t_2w_512sl (&wmp->timer_wheel,
     417             :                                            vlib_time_now (vm));
     418             :     }
     419             : 
     420             :   return 0;
     421             : }
     422             : 
     423             : void
     424         296 : wg_timers_stop (wg_peer_t * peer)
     425             : {
     426         296 :   ASSERT (vlib_get_thread_index () == 0);
     427         296 :   if (peer->timer_wheel)
     428             :     {
     429         150 :       stop_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE);
     430         150 :       stop_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE);
     431         150 :       stop_timer (peer, WG_TIMER_SEND_KEEPALIVE);
     432         150 :       stop_timer (peer, WG_TIMER_NEW_HANDSHAKE);
     433         150 :       stop_timer (peer, WG_TIMER_KEY_ZEROING);
     434             :     }
     435         296 : }
     436             : 
     437             : /* *INDENT-OFF* */
     438        1151 : VLIB_REGISTER_NODE (wg_timer_mngr_node, static) = {
     439             :     .function = wg_timer_mngr_fn,
     440             :     .type = VLIB_NODE_TYPE_PROCESS,
     441             :     .name =
     442             :     "wg-timer-manager",
     443             : };
     444             : /* *INDENT-ON* */
     445             : 
     446             : void
     447         855 : wg_feature_init (wg_main_t * wmp)
     448             : {
     449         855 :   if (wmp->feature_init)
     450         852 :     return;
     451           3 :   vlib_process_signal_event (wmp->vlib_main, wg_timer_mngr_node.index,
     452             :                              WG_START_EVENT, 0);
     453           3 :   wmp->feature_init = 1;
     454             : }
     455             : 
     456             : 
     457             : 
     458             : /*
     459             :  * fd.io coding-style-patch-verification: ON
     460             :  *
     461             :  * Local Variables:
     462             :  * eval: (c-set-style "gnu")
     463             :  * End:
     464             :  */

Generated by: LCOV version 1.14