LCOV - code coverage report
Current view: top level - plugins/wireguard - wireguard_output_tun.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 330 426 77.5 %
Date: 2023-07-05 22:20:52 Functions: 29 30 96.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 Doc.ai and/or its affiliates.
       3             :  * Copyright (c) 2020 Cisco and/or its affiliates.
       4             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5             :  * you may not use this file except in compliance with the License.
       6             :  * You may obtain a copy of the License at:
       7             :  *
       8             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9             :  *
      10             :  * Unless required by applicable law or agreed to in writing, software
      11             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13             :  * See the License for the specific language governing permissions and
      14             :  * limitations under the License.
      15             :  */
      16             : 
      17             : #include <vlib/vlib.h>
      18             : #include <vnet/vnet.h>
      19             : #include <vppinfra/error.h>
      20             : 
      21             : #include <wireguard/wireguard.h>
      22             : #include <wireguard/wireguard_send.h>
      23             : 
      24             : #define foreach_wg_output_error                                               \
      25             :   _ (NONE, "No error")                                                        \
      26             :   _ (PEER, "Peer error")                                                      \
      27             :   _ (KEYPAIR, "Keypair error")                                                \
      28             :   _ (NO_BUFFERS, "No buffers")                                                \
      29             :   _ (CRYPTO_ENGINE_ERROR, "crypto engine error (packet dropped)")
      30             : 
      31             : typedef enum
      32             : {
      33             : #define _(sym,str) WG_OUTPUT_ERROR_##sym,
      34             :   foreach_wg_output_error
      35             : #undef _
      36             :     WG_OUTPUT_N_ERROR,
      37             : } wg_output_error_t;
      38             : 
      39             : static char *wg_output_error_strings[] = {
      40             : #define _(sym,string) string,
      41             :   foreach_wg_output_error
      42             : #undef _
      43             : };
      44             : 
      45             : typedef enum
      46             : {
      47             :   WG_OUTPUT_NEXT_ERROR,
      48             :   WG_OUTPUT_NEXT_HANDOFF,
      49             :   WG_OUTPUT_NEXT_INTERFACE_OUTPUT,
      50             :   WG_OUTPUT_N_NEXT,
      51             : } wg_output_next_t;
      52             : 
      53             : typedef struct
      54             : {
      55             :   index_t peer;
      56             :   u8 header[sizeof (ip6_udp_header_t)];
      57             :   u8 is_ip4;
      58             : } wg_output_tun_trace_t;
      59             : 
      60             : typedef struct
      61             : {
      62             :   index_t peer;
      63             :   u32 next_index;
      64             : } wg_output_tun_post_trace_t;
      65             : 
      66             : u8 *
      67         905 : format_ip4_udp_header (u8 * s, va_list * args)
      68             : {
      69         905 :   ip4_udp_header_t *hdr4 = va_arg (*args, ip4_udp_header_t *);
      70             : 
      71         905 :   s = format (s, "%U:$U", format_ip4_header, &hdr4->ip4, format_udp_header,
      72             :               &hdr4->udp);
      73         905 :   return (s);
      74             : }
      75             : 
      76             : u8 *
      77         264 : format_ip6_udp_header (u8 *s, va_list *args)
      78             : {
      79         264 :   ip6_udp_header_t *hdr6 = va_arg (*args, ip6_udp_header_t *);
      80             : 
      81         264 :   s = format (s, "%U:$U", format_ip6_header, &hdr6->ip6, format_udp_header,
      82             :               &hdr6->udp);
      83         264 :   return (s);
      84             : }
      85             : 
      86             : /* packet trace format function */
      87             : static u8 *
      88        1169 : format_wg_output_tun_trace (u8 * s, va_list * args)
      89             : {
      90        1169 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
      91        1169 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
      92             : 
      93        1169 :   wg_output_tun_trace_t *t = va_arg (*args, wg_output_tun_trace_t *);
      94             : 
      95        1169 :   s = format (s, "peer: %d\n", t->peer);
      96        1169 :   s = format (s, "  Encrypted packet: ");
      97             : 
      98        1169 :   s = t->is_ip4 ? format (s, "%U", format_ip4_udp_header, t->header) :
      99         264 :                   format (s, "%U", format_ip6_udp_header, t->header);
     100        1169 :   return s;
     101             : }
     102             : 
     103             : /* post node - packet trace format function */
     104             : static u8 *
     105           0 : format_wg_output_tun_post_trace (u8 *s, va_list *args)
     106             : {
     107           0 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     108           0 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     109             : 
     110           0 :   wg_output_tun_post_trace_t *t = va_arg (*args, wg_output_tun_post_trace_t *);
     111             : 
     112           0 :   s = format (s, "peer: %d\n", t->peer);
     113           0 :   s = format (s, "  wg-post: next node index %u", t->next_index);
     114           0 :   return s;
     115             : }
     116             : 
     117             : static_always_inline void
     118          12 : wg_output_chain_crypto (vlib_main_t *vm, wg_per_thread_data_t *ptd,
     119             :                         vlib_buffer_t *b, vlib_buffer_t *lb, u8 *start,
     120             :                         u32 start_len, u16 *n_ch)
     121             : {
     122             :   vnet_crypto_op_chunk_t *ch;
     123          12 :   vlib_buffer_t *cb = b;
     124          12 :   u32 n_chunks = 1;
     125             : 
     126          12 :   vec_add2 (ptd->chunks, ch, 1);
     127          12 :   ch->len = start_len;
     128          12 :   ch->src = ch->dst = start;
     129          12 :   cb = vlib_get_buffer (vm, cb->next_buffer);
     130             : 
     131             :   while (1)
     132             :     {
     133          16 :       vec_add2 (ptd->chunks, ch, 1);
     134          16 :       n_chunks += 1;
     135          16 :       if (lb == cb)
     136          12 :         ch->len = cb->current_length - NOISE_AUTHTAG_LEN;
     137             :       else
     138           4 :         ch->len = cb->current_length;
     139             : 
     140          16 :       ch->src = ch->dst = vlib_buffer_get_current (cb);
     141             : 
     142          16 :       if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
     143          12 :         break;
     144             : 
     145           4 :       cb = vlib_get_buffer (vm, cb->next_buffer);
     146             :     }
     147             : 
     148          12 :   if (n_ch)
     149          12 :     *n_ch = n_chunks;
     150          12 : }
     151             : 
     152             : static_always_inline void
     153        3355 : wg_prepare_sync_enc_op (vlib_main_t *vm, wg_per_thread_data_t *ptd,
     154             :                         vlib_buffer_t *b, vlib_buffer_t *lb,
     155             :                         vnet_crypto_op_t **crypto_ops, u8 *src, u32 src_len,
     156             :                         u8 *dst, u8 *aad, u32 aad_len, u64 nonce,
     157             :                         vnet_crypto_key_index_t key_index, u32 bi, u8 *iv)
     158             : {
     159        3355 :   vnet_crypto_op_t _op, *op = &_op;
     160        3355 :   u8 src_[] = {};
     161             : 
     162        3355 :   clib_memset (iv, 0, 4);
     163        3355 :   clib_memcpy (iv + 4, &nonce, sizeof (nonce));
     164             : 
     165        3355 :   vec_add2_aligned (crypto_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
     166        3355 :   vnet_crypto_op_init (op, VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC);
     167             : 
     168        3355 :   op->tag_len = NOISE_AUTHTAG_LEN;
     169        3355 :   op->tag = vlib_buffer_get_tail (lb) - NOISE_AUTHTAG_LEN;
     170        3355 :   op->key_index = key_index;
     171        3355 :   op->aad = aad;
     172        3355 :   op->aad_len = aad_len;
     173        3355 :   op->iv = iv;
     174        3355 :   op->user_data = bi;
     175             : 
     176        3355 :   if (b != lb)
     177             :     {
     178             :       /* Chained buffers */
     179          12 :       op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
     180          12 :       op->chunk_index = vec_len (ptd->chunks);
     181          12 :       wg_output_chain_crypto (vm, ptd, b, lb, src, src_len, &op->n_chunks);
     182             :     }
     183             :   else
     184             :     {
     185        3343 :       op->src = !src ? src_ : src;
     186        3343 :       op->len = src_len;
     187        3343 :       op->dst = dst;
     188             :     }
     189        3355 : }
     190             : 
     191             : static_always_inline void
     192          33 : wg_output_process_chained_ops (vlib_main_t *vm, vlib_node_runtime_t *node,
     193             :                                vnet_crypto_op_t *ops, vlib_buffer_t *b[],
     194             :                                u16 *nexts, vnet_crypto_op_chunk_t *chunks,
     195             :                                u16 drop_next)
     196             : {
     197          33 :   u32 n_fail, n_ops = vec_len (ops);
     198          33 :   vnet_crypto_op_t *op = ops;
     199             : 
     200          33 :   if (n_ops == 0)
     201          29 :     return;
     202             : 
     203           4 :   n_fail = n_ops - vnet_crypto_process_chained_ops (vm, op, chunks, n_ops);
     204             : 
     205           4 :   while (n_fail)
     206             :     {
     207           0 :       ASSERT (op - ops < n_ops);
     208             : 
     209           0 :       if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
     210             :         {
     211           0 :           u32 bi = op->user_data;
     212           0 :           b[bi]->error = node->errors[WG_OUTPUT_ERROR_CRYPTO_ENGINE_ERROR];
     213           0 :           nexts[bi] = drop_next;
     214           0 :           n_fail--;
     215             :         }
     216           0 :       op++;
     217             :     }
     218             : }
     219             : 
     220             : static_always_inline void
     221          33 : wg_output_process_ops (vlib_main_t *vm, vlib_node_runtime_t *node,
     222             :                        vnet_crypto_op_t *ops, vlib_buffer_t *b[], u16 *nexts,
     223             :                        u16 drop_next)
     224             : {
     225          33 :   u32 n_fail, n_ops = vec_len (ops);
     226          33 :   vnet_crypto_op_t *op = ops;
     227             : 
     228          33 :   if (n_ops == 0)
     229           0 :     return;
     230             : 
     231          33 :   n_fail = n_ops - vnet_crypto_process_ops (vm, op, n_ops);
     232             : 
     233          33 :   while (n_fail)
     234             :     {
     235           0 :       ASSERT (op - ops < n_ops);
     236             : 
     237           0 :       if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
     238             :         {
     239           0 :           u32 bi = op->user_data;
     240           0 :           b[bi]->error = node->errors[WG_OUTPUT_ERROR_CRYPTO_ENGINE_ERROR];
     241           0 :           nexts[bi] = drop_next;
     242           0 :           n_fail--;
     243             :         }
     244           0 :       op++;
     245             :     }
     246             : }
     247             : 
     248             : static_always_inline void
     249          20 : wg_output_tun_add_to_frame (vlib_main_t *vm, vnet_crypto_async_frame_t *f,
     250             :                             u32 key_index, u32 crypto_len,
     251             :                             i16 crypto_start_offset, u32 buffer_index,
     252             :                             u16 next_node, u8 *iv, u8 *tag, u8 flags)
     253             : {
     254             :   vnet_crypto_async_frame_elt_t *fe;
     255             :   u16 index;
     256             : 
     257          20 :   ASSERT (f->n_elts < VNET_CRYPTO_FRAME_SIZE);
     258             : 
     259          20 :   index = f->n_elts;
     260          20 :   fe = &f->elts[index];
     261          20 :   f->n_elts++;
     262          20 :   fe->key_index = key_index;
     263          20 :   fe->crypto_total_length = crypto_len;
     264          20 :   fe->crypto_start_offset = crypto_start_offset;
     265          20 :   fe->iv = iv;
     266          20 :   fe->tag = tag;
     267          20 :   fe->flags = flags;
     268          20 :   f->buffer_indices[index] = buffer_index;
     269          20 :   f->next_node_index[index] = next_node;
     270          20 : }
     271             : 
     272             : static_always_inline enum noise_state_crypt
     273        3355 : wg_output_tun_process (vlib_main_t *vm, wg_per_thread_data_t *ptd,
     274             :                        vlib_buffer_t *b, vlib_buffer_t *lb,
     275             :                        vnet_crypto_op_t **crypto_ops, noise_remote_t *r,
     276             :                        uint32_t *r_idx, uint64_t *nonce, uint8_t *src,
     277             :                        size_t srclen, uint8_t *dst, u32 bi, u8 *iv, f64 time)
     278             : {
     279             :   noise_keypair_t *kp;
     280        3355 :   enum noise_state_crypt ret = SC_FAILED;
     281             : 
     282        3355 :   if ((kp = r->r_current) == NULL)
     283           0 :     goto error;
     284             : 
     285             :   /* We confirm that our values are within our tolerances. We want:
     286             :    *  - a valid keypair
     287             :    *  - our keypair to be less than REJECT_AFTER_TIME seconds old
     288             :    *  - our receive counter to be less than REJECT_AFTER_MESSAGES
     289             :    *  - our send counter to be less than REJECT_AFTER_MESSAGES
     290             :    */
     291        6710 :   if (!kp->kp_valid ||
     292        3355 :       wg_birthdate_has_expired_opt (kp->kp_birthdate, REJECT_AFTER_TIME,
     293        3355 :                                     time) ||
     294        3355 :       kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES ||
     295        3355 :       ((*nonce = noise_counter_send (&kp->kp_ctr)) > REJECT_AFTER_MESSAGES))
     296           0 :     goto error;
     297             : 
     298             :   /* We encrypt into the same buffer, so the caller must ensure that buf
     299             :    * has NOISE_AUTHTAG_LEN bytes to store the MAC. The nonce and index
     300             :    * are passed back out to the caller through the provided data pointer. */
     301        3355 :   *r_idx = kp->kp_remote_index;
     302             : 
     303        3355 :   wg_prepare_sync_enc_op (vm, ptd, b, lb, crypto_ops, src, srclen, dst, NULL,
     304             :                           0, *nonce, kp->kp_send_index, bi, iv);
     305             : 
     306             :   /* If our values are still within tolerances, but we are approaching
     307             :    * the tolerances, we notify the caller with ESTALE that they should
     308             :    * establish a new keypair. The current keypair can continue to be used
     309             :    * until the tolerances are hit. We notify if:
     310             :    *  - our send counter is valid and not less than REKEY_AFTER_MESSAGES
     311             :    *  - we're the initiator and our keypair is older than
     312             :    *    REKEY_AFTER_TIME seconds */
     313        3355 :   ret = SC_KEEP_KEY_FRESH;
     314        3355 :   if ((kp->kp_valid && *nonce >= REKEY_AFTER_MESSAGES) ||
     315        3355 :       (kp->kp_is_initiator && wg_birthdate_has_expired_opt (
     316             :                                 kp->kp_birthdate, REKEY_AFTER_TIME, time)))
     317           0 :     goto error;
     318             : 
     319        3355 :   ret = SC_OK;
     320        3355 : error:
     321        3355 :   return ret;
     322             : }
     323             : 
     324             : static_always_inline enum noise_state_crypt
     325          20 : wg_add_to_async_frame (vlib_main_t *vm, wg_per_thread_data_t *ptd,
     326             :                        vnet_crypto_async_frame_t **async_frame,
     327             :                        vlib_buffer_t *b, vlib_buffer_t *lb, u8 *payload,
     328             :                        u32 payload_len, u32 bi, u16 next, u16 async_next,
     329             :                        noise_remote_t *r, uint32_t *r_idx, uint64_t *nonce,
     330             :                        u8 *iv, f64 time)
     331             : {
     332          20 :   wg_post_data_t *post = wg_post_data (b);
     333          20 :   u8 flag = 0;
     334             :   u8 *tag;
     335             :   noise_keypair_t *kp;
     336             : 
     337          20 :   post->next_index = next;
     338             : 
     339             :   /* crypto */
     340          20 :   enum noise_state_crypt ret = SC_FAILED;
     341             : 
     342          20 :   if ((kp = r->r_current) == NULL)
     343           0 :     goto error;
     344             : 
     345             :   /* We confirm that our values are within our tolerances. We want:
     346             :    *  - a valid keypair
     347             :    *  - our keypair to be less than REJECT_AFTER_TIME seconds old
     348             :    *  - our receive counter to be less than REJECT_AFTER_MESSAGES
     349             :    *  - our send counter to be less than REJECT_AFTER_MESSAGES
     350             :    */
     351          40 :   if (!kp->kp_valid ||
     352          20 :       wg_birthdate_has_expired_opt (kp->kp_birthdate, REJECT_AFTER_TIME,
     353          20 :                                     time) ||
     354          20 :       kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES ||
     355          20 :       ((*nonce = noise_counter_send (&kp->kp_ctr)) > REJECT_AFTER_MESSAGES))
     356           0 :     goto error;
     357             : 
     358             :   /* We encrypt into the same buffer, so the caller must ensure that buf
     359             :    * has NOISE_AUTHTAG_LEN bytes to store the MAC. The nonce and index
     360             :    * are passed back out to the caller through the provided data pointer. */
     361          20 :   *r_idx = kp->kp_remote_index;
     362             : 
     363          20 :   clib_memset (iv, 0, 4);
     364          20 :   clib_memcpy (iv + 4, nonce, sizeof (*nonce));
     365             : 
     366             :   /* get a frame for this op if we don't yet have one or it's full  */
     367          20 :   if (NULL == *async_frame || vnet_crypto_async_frame_is_full (*async_frame))
     368             :     {
     369           8 :       *async_frame = vnet_crypto_async_get_frame (
     370             :         vm, VNET_CRYPTO_OP_CHACHA20_POLY1305_TAG16_AAD0_ENC);
     371           8 :       if (PREDICT_FALSE (NULL == *async_frame))
     372           0 :         goto error;
     373             :       /* Save the frame to the list we'll submit at the end */
     374           8 :       vec_add1 (ptd->async_frames, *async_frame);
     375             :     }
     376             : 
     377          20 :   if (b != lb)
     378          12 :     flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
     379             : 
     380          20 :   tag = vlib_buffer_get_tail (lb) - NOISE_AUTHTAG_LEN;
     381             : 
     382             :   /* this always succeeds because we know the frame is not full */
     383          20 :   wg_output_tun_add_to_frame (vm, *async_frame, kp->kp_send_index, payload_len,
     384          20 :                               payload - b->data, bi, async_next, iv, tag,
     385             :                               flag);
     386             : 
     387             :   /* If our values are still within tolerances, but we are approaching
     388             :    * the tolerances, we notify the caller with ESTALE that they should
     389             :    * establish a new keypair. The current keypair can continue to be used
     390             :    * until the tolerances are hit. We notify if:
     391             :    *  - our send counter is valid and not less than REKEY_AFTER_MESSAGES
     392             :    *  - we're the initiator and our keypair is older than
     393             :    *    REKEY_AFTER_TIME seconds */
     394          20 :   ret = SC_KEEP_KEY_FRESH;
     395          20 :   if ((kp->kp_valid && *nonce >= REKEY_AFTER_MESSAGES) ||
     396          20 :       (kp->kp_is_initiator && wg_birthdate_has_expired_opt (
     397             :                                 kp->kp_birthdate, REKEY_AFTER_TIME, time)))
     398           0 :     goto error;
     399             : 
     400          20 :   ret = SC_OK;
     401          20 : error:
     402          20 :   return ret;
     403             : }
     404             : 
     405             : static_always_inline void
     406        3375 : wg_calc_checksum (vlib_main_t *vm, vlib_buffer_t *b)
     407             : {
     408        3375 :   int bogus = 0;
     409        3375 :   u8 ip_ver_out = (*((u8 *) vlib_buffer_get_current (b)) >> 4);
     410             : 
     411             :   /* IPv6 UDP checksum is mandatory */
     412        3375 :   if (ip_ver_out == 6)
     413             :     {
     414             :       ip6_header_t *ip6 =
     415        1054 :         (ip6_header_t *) ((u8 *) vlib_buffer_get_current (b));
     416        1054 :       udp_header_t *udp = ip6_next_header (ip6);
     417        1054 :       udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
     418             :     }
     419        3375 : }
     420             : 
     421             : /* is_ip4 - inner header flag */
     422             : always_inline uword
     423          64 : wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
     424             :                       vlib_frame_t *frame, u8 is_ip4, u16 async_next_node)
     425             : {
     426          64 :   wg_main_t *wmp = &wg_main;
     427          64 :   wg_per_thread_data_t *ptd =
     428          64 :     vec_elt_at_index (wmp->per_thread_data, vm->thread_index);
     429          64 :   u32 *from = vlib_frame_vector_args (frame);
     430          64 :   u32 n_left_from = frame->n_vectors;
     431          64 :   ip4_udp_wg_header_t *hdr4_out = NULL;
     432          64 :   ip6_udp_wg_header_t *hdr6_out = NULL;
     433          64 :   message_data_t *message_data_wg = NULL;
     434          64 :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
     435             :   vlib_buffer_t *lb;
     436             :   vnet_crypto_op_t **crypto_ops;
     437          64 :   u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
     438             :   vlib_buffer_t *sync_bufs[VLIB_FRAME_SIZE];
     439          64 :   u32 thread_index = vm->thread_index;
     440          64 :   u16 n_sync = 0;
     441          64 :   const u16 drop_next = WG_OUTPUT_NEXT_ERROR;
     442          64 :   const u8 is_async = wg_op_mode_is_set_ASYNC ();
     443          64 :   vnet_crypto_async_frame_t *async_frame = NULL;
     444          64 :   u16 n_async = 0;
     445          64 :   u16 noop_nexts[VLIB_FRAME_SIZE], *noop_next = noop_nexts, n_noop = 0;
     446          64 :   u16 err = !0;
     447             :   u32 sync_bi[VLIB_FRAME_SIZE];
     448             :   u32 noop_bi[VLIB_FRAME_SIZE];
     449             : 
     450          64 :   vlib_get_buffers (vm, from, bufs, n_left_from);
     451          64 :   vec_reset_length (ptd->crypto_ops);
     452          64 :   vec_reset_length (ptd->chained_crypto_ops);
     453          64 :   vec_reset_length (ptd->chunks);
     454          64 :   vec_reset_length (ptd->async_frames);
     455             : 
     456          64 :   wg_peer_t *peer = NULL;
     457          64 :   u32 adj_index = 0;
     458          64 :   u32 last_adj_index = ~0;
     459          64 :   index_t peeri = INDEX_INVALID;
     460             : 
     461          64 :   f64 time = clib_time_now (&vm->clib_time) + vm->time_offset;
     462             : 
     463        3716 :   while (n_left_from > 0)
     464             :     {
     465        3652 :       u8 iph_offset = 0;
     466        3652 :       u8 is_ip4_out = 1;
     467             :       u8 *plain_data;
     468             :       u16 plain_data_len;
     469             :       u16 plain_data_len_total;
     470             :       u16 n_bufs;
     471             :       u16 b_space_left_at_beginning;
     472        3652 :       u32 bi = from[b - bufs];
     473             : 
     474        3652 :       if (n_left_from > 2)
     475             :         {
     476             :           u8 *p;
     477        3559 :           vlib_prefetch_buffer_header (b[2], LOAD);
     478        3559 :           p = vlib_buffer_get_current (b[1]);
     479        3559 :           CLIB_PREFETCH (p, CLIB_CACHE_LINE_BYTES, LOAD);
     480        3559 :           CLIB_PREFETCH (vlib_buffer_get_tail (b[1]), CLIB_CACHE_LINE_BYTES,
     481             :                          LOAD);
     482             :         }
     483             : 
     484        3652 :       noop_next[0] = WG_OUTPUT_NEXT_ERROR;
     485        3652 :       err = WG_OUTPUT_NEXT_ERROR;
     486             : 
     487        3652 :       adj_index = vnet_buffer (b[0])->ip.adj_index[VLIB_TX];
     488             : 
     489        3652 :       if (PREDICT_FALSE (last_adj_index != adj_index))
     490             :         {
     491         318 :           peeri = wg_peer_get_by_adj_index (adj_index);
     492         318 :           if (peeri == INDEX_INVALID)
     493             :             {
     494           4 :               b[0]->error = node->errors[WG_OUTPUT_ERROR_PEER];
     495           4 :               goto out;
     496             :             }
     497         314 :           peer = wg_peer_get (peeri);
     498             :         }
     499             : 
     500        3648 :       if (!peer || wg_peer_is_dead (peer))
     501             :         {
     502           0 :           b[0]->error = node->errors[WG_OUTPUT_ERROR_PEER];
     503           0 :           goto out;
     504             :         }
     505        3648 :       if (PREDICT_FALSE (~0 == peer->output_thread_index))
     506             :         {
     507             :           /* this is the first packet to use this peer, claim the peer
     508             :            * for this thread.
     509             :            */
     510          38 :           clib_atomic_cmp_and_swap (&peer->output_thread_index, ~0,
     511             :                                     wg_peer_assign_thread (thread_index));
     512             :         }
     513             : 
     514        3648 :       if (PREDICT_FALSE (thread_index != peer->output_thread_index))
     515             :         {
     516         255 :           noop_next[0] = WG_OUTPUT_NEXT_HANDOFF;
     517         255 :           err = WG_OUTPUT_NEXT_HANDOFF;
     518         255 :           goto next;
     519             :         }
     520             : 
     521        3393 :       if (PREDICT_FALSE (!peer->remote.r_current))
     522             :         {
     523          18 :           wg_send_handshake_from_mt (peeri, false);
     524          18 :           b[0]->error = node->errors[WG_OUTPUT_ERROR_KEYPAIR];
     525          18 :           goto out;
     526             :         }
     527             : 
     528        3375 :       lb = b[0];
     529        3375 :       n_bufs = vlib_buffer_chain_linearize (vm, b[0]);
     530        3375 :       if (n_bufs == 0)
     531             :         {
     532           0 :           b[0]->error = node->errors[WG_OUTPUT_ERROR_NO_BUFFERS];
     533           0 :           goto out;
     534             :         }
     535             : 
     536        3375 :       if (n_bufs > 1)
     537             :         {
     538             :           /* Find last buffer in the chain */
     539          40 :           while (lb->flags & VLIB_BUFFER_NEXT_PRESENT)
     540          24 :             lb = vlib_get_buffer (vm, lb->next_buffer);
     541             :         }
     542             : 
     543             :       /* Ensure there is enough free space at the beginning of the first buffer
     544             :        * to write ethernet header (e.g. IPv6 VxLAN over IPv6 Wireguard will
     545             :        * trigger this)
     546             :        */
     547        3375 :       ASSERT ((signed) b[0]->current_data >=
     548             :               (signed) -VLIB_BUFFER_PRE_DATA_SIZE);
     549        3375 :       b_space_left_at_beginning =
     550        3375 :         b[0]->current_data + VLIB_BUFFER_PRE_DATA_SIZE;
     551        3375 :       if (PREDICT_FALSE (b_space_left_at_beginning <
     552             :                          sizeof (ethernet_header_t)))
     553             :         {
     554          10 :           u32 size_diff =
     555          10 :             sizeof (ethernet_header_t) - b_space_left_at_beginning;
     556             : 
     557             :           /* Can only move buffer when it's single and has enough free space*/
     558          20 :           if (lb == b[0] &&
     559          10 :               vlib_buffer_space_left_at_end (vm, b[0]) >= size_diff)
     560             :             {
     561          10 :               vlib_buffer_move (vm, b[0],
     562          10 :                                 b[0]->current_data + (signed) size_diff);
     563             :             }
     564             :           else
     565             :             {
     566           0 :               b[0]->error = node->errors[WG_OUTPUT_ERROR_NO_BUFFERS];
     567           0 :               goto out;
     568             :             }
     569             :         }
     570             : 
     571             :       /*
     572             :        * Ensure there is enough free space at the end of the last buffer to
     573             :        * write auth tag */
     574        3375 :       if (PREDICT_FALSE (vlib_buffer_space_left_at_end (vm, lb) <
     575             :                          NOISE_AUTHTAG_LEN))
     576             :         {
     577           8 :           u32 tmp_bi = 0;
     578           8 :           if (vlib_buffer_alloc (vm, &tmp_bi, 1) != 1)
     579             :             {
     580           0 :               b[0]->error = node->errors[WG_OUTPUT_ERROR_NO_BUFFERS];
     581           0 :               goto out;
     582             :             }
     583           8 :           lb = vlib_buffer_chain_buffer (vm, lb, tmp_bi);
     584             :         }
     585             : 
     586        3375 :       iph_offset = vnet_buffer (b[0])->ip.save_rewrite_length;
     587        3375 :       plain_data = vlib_buffer_get_current (b[0]) + iph_offset;
     588        3375 :       plain_data_len = b[0]->current_length - iph_offset;
     589        3375 :       plain_data_len_total =
     590        3375 :         vlib_buffer_length_in_chain (vm, b[0]) - iph_offset;
     591        3375 :       size_t encrypted_packet_len = message_data_len (plain_data_len_total);
     592        3375 :       vlib_buffer_chain_increase_length (b[0], lb, NOISE_AUTHTAG_LEN);
     593        3375 :       u8 *iv_data = b[0]->pre_data;
     594             : 
     595        3375 :       is_ip4_out = ip46_address_is_ip4 (&peer->src.addr);
     596        3375 :       if (is_ip4_out)
     597             :         {
     598        2321 :           hdr4_out = vlib_buffer_get_current (b[0]);
     599        2321 :           message_data_wg = &hdr4_out->wg;
     600             :         }
     601             :       else
     602             :         {
     603        1054 :           hdr6_out = vlib_buffer_get_current (b[0]);
     604        1054 :           message_data_wg = &hdr6_out->wg;
     605             :         }
     606             : 
     607        3375 :       if (PREDICT_FALSE (last_adj_index != adj_index))
     608             :         {
     609          41 :           wg_timers_any_authenticated_packet_sent_opt (peer, time);
     610          41 :           wg_timers_data_sent_opt (peer, time);
     611          41 :           wg_timers_any_authenticated_packet_traversal (peer);
     612          41 :           last_adj_index = adj_index;
     613             :         }
     614             : 
     615             :       /* Here we are sure that can send packet to next node */
     616        3375 :       next[0] = WG_OUTPUT_NEXT_INTERFACE_OUTPUT;
     617             : 
     618        3375 :       if (lb != b[0])
     619          24 :         crypto_ops = &ptd->chained_crypto_ops;
     620             :       else
     621        3351 :         crypto_ops = &ptd->crypto_ops;
     622             : 
     623             :       enum noise_state_crypt state;
     624             : 
     625        3375 :       if (is_async)
     626             :         {
     627          20 :           state = wg_add_to_async_frame (
     628             :             vm, ptd, &async_frame, b[0], lb, plain_data, plain_data_len_total,
     629          20 :             bi, next[0], async_next_node, &peer->remote,
     630          20 :             &message_data_wg->receiver_index, &message_data_wg->counter,
     631             :             iv_data, time);
     632             :         }
     633             :       else
     634             :         {
     635        3355 :           state = wg_output_tun_process (
     636             :             vm, ptd, b[0], lb, crypto_ops, &peer->remote,
     637        3355 :             &message_data_wg->receiver_index, &message_data_wg->counter,
     638             :             plain_data, plain_data_len, plain_data, n_sync, iv_data, time);
     639             :         }
     640             : 
     641        3375 :       if (PREDICT_FALSE (state == SC_KEEP_KEY_FRESH))
     642             :         {
     643           0 :           wg_send_handshake_from_mt (peeri, false);
     644             :         }
     645        3375 :       else if (PREDICT_FALSE (state == SC_FAILED))
     646             :         {
     647             :           // TODO: Maybe wrong
     648           0 :           wg_send_handshake_from_mt (peeri, false);
     649           0 :           wg_peer_update_flags (peeri, WG_PEER_ESTABLISHED, false);
     650           0 :           noop_next[0] = WG_OUTPUT_NEXT_ERROR;
     651           0 :           goto out;
     652             :         }
     653             : 
     654        3375 :       err = WG_OUTPUT_NEXT_INTERFACE_OUTPUT;
     655             : 
     656        3375 :       if (is_ip4_out)
     657             :         {
     658        2321 :           hdr4_out->wg.header.type = MESSAGE_DATA;
     659        2321 :           hdr4_out->udp.length = clib_host_to_net_u16 (encrypted_packet_len +
     660             :                                                        sizeof (udp_header_t));
     661        2321 :           ip4_header_set_len_w_chksum (
     662        2321 :             &hdr4_out->ip4, clib_host_to_net_u16 (encrypted_packet_len +
     663             :                                                   sizeof (ip4_udp_header_t)));
     664             :         }
     665             :       else
     666             :         {
     667        1054 :           hdr6_out->wg.header.type = MESSAGE_DATA;
     668        1054 :           hdr6_out->ip6.payload_length = hdr6_out->udp.length =
     669        1054 :             clib_host_to_net_u16 (encrypted_packet_len +
     670             :                                   sizeof (udp_header_t));
     671             :         }
     672             : 
     673        3397 :     out:
     674        3397 :       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
     675             :                          && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
     676             :         {
     677             :           wg_output_tun_trace_t *t =
     678        3397 :             vlib_add_trace (vm, node, b[0], sizeof (*t));
     679             : 
     680        3397 :           t->peer = peeri;
     681        3397 :           t->is_ip4 = is_ip4_out;
     682        3397 :           if (hdr4_out)
     683        2321 :             clib_memcpy (t->header, hdr4_out, sizeof (ip4_udp_header_t));
     684        1076 :           else if (hdr6_out)
     685        1054 :             clib_memcpy (t->header, hdr6_out, sizeof (ip6_udp_header_t));
     686             :         }
     687             : 
     688          22 :     next:
     689        3652 :       if (PREDICT_FALSE (err != WG_OUTPUT_NEXT_INTERFACE_OUTPUT))
     690             :         {
     691         277 :           noop_bi[n_noop] = bi;
     692         277 :           n_noop++;
     693         277 :           noop_next++;
     694         277 :           goto next_left;
     695             :         }
     696        3375 :       if (!is_async)
     697             :         {
     698        3355 :           sync_bi[n_sync] = bi;
     699        3355 :           sync_bufs[n_sync] = b[0];
     700        3355 :           n_sync += 1;
     701        3355 :           next += 1;
     702             :         }
     703             :       else
     704             :         {
     705          20 :           n_async++;
     706             :         }
     707        3652 :     next_left:
     708        3652 :       n_left_from -= 1;
     709        3652 :       b += 1;
     710             :     }
     711             : 
     712          64 :   if (n_sync)
     713             :     {
     714             :       /* wg-output-process-ops */
     715          33 :       wg_output_process_ops (vm, node, ptd->crypto_ops, sync_bufs, nexts,
     716             :                              drop_next);
     717          33 :       wg_output_process_chained_ops (vm, node, ptd->chained_crypto_ops,
     718             :                                      sync_bufs, nexts, ptd->chunks, drop_next);
     719             : 
     720          33 :       int n_left_from_sync_bufs = n_sync;
     721        3388 :       while (n_left_from_sync_bufs > 0)
     722             :         {
     723        3355 :           n_left_from_sync_bufs--;
     724        3355 :           wg_calc_checksum (vm, sync_bufs[n_left_from_sync_bufs]);
     725             :         }
     726             : 
     727          33 :       vlib_buffer_enqueue_to_next (vm, node, sync_bi, nexts, n_sync);
     728             :     }
     729          64 :   if (n_async)
     730             :     {
     731             :       /* submit all of the open frames */
     732             :       vnet_crypto_async_frame_t **async_frame;
     733             : 
     734          16 :       vec_foreach (async_frame, ptd->async_frames)
     735             :         {
     736           8 :           if (PREDICT_FALSE (
     737             :                 vnet_crypto_async_submit_open_frame (vm, *async_frame) < 0))
     738             :             {
     739           0 :               u32 n_drop = (*async_frame)->n_elts;
     740           0 :               u32 *bi = (*async_frame)->buffer_indices;
     741           0 :               u16 index = n_noop;
     742           0 :               while (n_drop--)
     743             :                 {
     744           0 :                   noop_bi[index] = bi[0];
     745           0 :                   vlib_buffer_t *b = vlib_get_buffer (vm, bi[0]);
     746           0 :                   noop_nexts[index] = drop_next;
     747           0 :                   b->error = node->errors[WG_OUTPUT_ERROR_CRYPTO_ENGINE_ERROR];
     748           0 :                   bi++;
     749           0 :                   index++;
     750             :                 }
     751           0 :               n_noop += (*async_frame)->n_elts;
     752             : 
     753           0 :               vnet_crypto_async_reset_frame (*async_frame);
     754           0 :               vnet_crypto_async_free_frame (vm, *async_frame);
     755             :             }
     756             :         }
     757             :     }
     758          64 :   if (n_noop)
     759             :     {
     760          23 :       vlib_buffer_enqueue_to_next (vm, node, noop_bi, noop_nexts, n_noop);
     761             :     }
     762             : 
     763          64 :   return frame->n_vectors;
     764             : }
     765             : 
     766             : always_inline uword
     767           8 : wg_output_tun_post (vlib_main_t *vm, vlib_node_runtime_t *node,
     768             :                     vlib_frame_t *frame)
     769             : {
     770           8 :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
     771           8 :   u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
     772           8 :   u32 *from = vlib_frame_vector_args (frame);
     773           8 :   u32 n_left = frame->n_vectors;
     774             : 
     775           8 :   index_t peeri = ~0;
     776             : 
     777           8 :   vlib_get_buffers (vm, from, b, n_left);
     778             : 
     779           8 :   if (n_left >= 4)
     780             :     {
     781           4 :       vlib_prefetch_buffer_header (b[0], LOAD);
     782           4 :       vlib_prefetch_buffer_header (b[1], LOAD);
     783           4 :       vlib_prefetch_buffer_header (b[2], LOAD);
     784           4 :       vlib_prefetch_buffer_header (b[3], LOAD);
     785             :     }
     786             : 
     787           8 :   while (n_left > 8)
     788             :     {
     789           0 :       vlib_prefetch_buffer_header (b[4], LOAD);
     790           0 :       vlib_prefetch_buffer_header (b[5], LOAD);
     791           0 :       vlib_prefetch_buffer_header (b[6], LOAD);
     792           0 :       vlib_prefetch_buffer_header (b[7], LOAD);
     793             : 
     794           0 :       next[0] = (wg_post_data (b[0]))->next_index;
     795           0 :       next[1] = (wg_post_data (b[1]))->next_index;
     796           0 :       next[2] = (wg_post_data (b[2]))->next_index;
     797           0 :       next[3] = (wg_post_data (b[3]))->next_index;
     798             : 
     799           0 :       wg_calc_checksum (vm, b[0]);
     800           0 :       wg_calc_checksum (vm, b[1]);
     801           0 :       wg_calc_checksum (vm, b[2]);
     802           0 :       wg_calc_checksum (vm, b[3]);
     803             : 
     804           0 :       if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
     805             :         {
     806           0 :           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
     807             :             {
     808             :               wg_output_tun_post_trace_t *tr =
     809           0 :                 vlib_add_trace (vm, node, b[0], sizeof (*tr));
     810           0 :               peeri = wg_peer_get_by_adj_index (
     811           0 :                 vnet_buffer (b[0])->ip.adj_index[VLIB_TX]);
     812           0 :               tr->peer = peeri;
     813           0 :               tr->next_index = next[0];
     814             :             }
     815           0 :           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
     816             :             {
     817             :               wg_output_tun_post_trace_t *tr =
     818           0 :                 vlib_add_trace (vm, node, b[1], sizeof (*tr));
     819           0 :               peeri = wg_peer_get_by_adj_index (
     820           0 :                 vnet_buffer (b[1])->ip.adj_index[VLIB_TX]);
     821           0 :               tr->next_index = next[1];
     822             :             }
     823           0 :           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
     824             :             {
     825             :               wg_output_tun_post_trace_t *tr =
     826           0 :                 vlib_add_trace (vm, node, b[2], sizeof (*tr));
     827           0 :               peeri = wg_peer_get_by_adj_index (
     828           0 :                 vnet_buffer (b[2])->ip.adj_index[VLIB_TX]);
     829           0 :               tr->next_index = next[2];
     830             :             }
     831           0 :           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
     832             :             {
     833             :               wg_output_tun_post_trace_t *tr =
     834           0 :                 vlib_add_trace (vm, node, b[3], sizeof (*tr));
     835           0 :               peeri = wg_peer_get_by_adj_index (
     836           0 :                 vnet_buffer (b[3])->ip.adj_index[VLIB_TX]);
     837           0 :               tr->next_index = next[3];
     838             :             }
     839             :         }
     840             : 
     841           0 :       b += 4;
     842           0 :       next += 4;
     843           0 :       n_left -= 4;
     844             :     }
     845             : 
     846          28 :   while (n_left > 0)
     847             :     {
     848          20 :       wg_calc_checksum (vm, b[0]);
     849             : 
     850          20 :       next[0] = (wg_post_data (b[0]))->next_index;
     851          20 :       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
     852             :                          (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
     853             :         {
     854             :           wg_output_tun_post_trace_t *tr =
     855           0 :             vlib_add_trace (vm, node, b[0], sizeof (*tr));
     856           0 :           peeri = wg_peer_get_by_adj_index (
     857           0 :             vnet_buffer (b[0])->ip.adj_index[VLIB_TX]);
     858           0 :           tr->next_index = next[0];
     859             :         }
     860             : 
     861          20 :       b += 1;
     862          20 :       next += 1;
     863          20 :       n_left -= 1;
     864             :     }
     865             : 
     866           8 :   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
     867           8 :   return frame->n_vectors;
     868             : }
     869             : 
     870        1119 : VLIB_REGISTER_NODE (wg4_output_tun_post_node) = {
     871             :   .name = "wg4-output-tun-post-node",
     872             :   .vector_size = sizeof (u32),
     873             :   .format_trace = format_wg_output_tun_post_trace,
     874             :   .type = VLIB_NODE_TYPE_INTERNAL,
     875             :   .sibling_of = "wg4-output-tun",
     876             :   .n_errors = ARRAY_LEN (wg_output_error_strings),
     877             :   .error_strings = wg_output_error_strings,
     878             : };
     879             : 
     880        1119 : VLIB_REGISTER_NODE (wg6_output_tun_post_node) = {
     881             :   .name = "wg6-output-tun-post-node",
     882             :   .vector_size = sizeof (u32),
     883             :   .format_trace = format_wg_output_tun_post_trace,
     884             :   .type = VLIB_NODE_TYPE_INTERNAL,
     885             :   .sibling_of = "wg6-output-tun",
     886             :   .n_errors = ARRAY_LEN (wg_output_error_strings),
     887             :   .error_strings = wg_output_error_strings,
     888             : };
     889             : 
     890         563 : VLIB_NODE_FN (wg4_output_tun_post_node)
     891             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
     892             : {
     893           4 :   return wg_output_tun_post (vm, node, from_frame);
     894             : }
     895             : 
     896         563 : VLIB_NODE_FN (wg6_output_tun_post_node)
     897             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
     898             : {
     899           4 :   return wg_output_tun_post (vm, node, from_frame);
     900             : }
     901             : 
     902         599 : VLIB_NODE_FN (wg4_output_tun_node)
     903             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     904             : {
     905          80 :   return wg_output_tun_inline (vm, node, frame, /* is_ip4 */ 1,
     906          40 :                                wg_encrypt_async_next.wg4_post_next);
     907             : }
     908             : 
     909         583 : VLIB_NODE_FN (wg6_output_tun_node)
     910             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     911             : {
     912          48 :   return wg_output_tun_inline (vm, node, frame, /* is_ip4 */ 0,
     913          24 :                                wg_encrypt_async_next.wg6_post_next);
     914             : }
     915             : 
     916             : /* *INDENT-OFF* */
     917        1119 : VLIB_REGISTER_NODE (wg4_output_tun_node) =
     918             : {
     919             :   .name = "wg4-output-tun",
     920             :   .vector_size = sizeof (u32),
     921             :   .format_trace = format_wg_output_tun_trace,
     922             :   .type = VLIB_NODE_TYPE_INTERNAL,
     923             :   .n_errors = ARRAY_LEN (wg_output_error_strings),
     924             :   .error_strings = wg_output_error_strings,
     925             :   .n_next_nodes = WG_OUTPUT_N_NEXT,
     926             :   .next_nodes = {
     927             :         [WG_OUTPUT_NEXT_HANDOFF] = "wg4-output-tun-handoff",
     928             :         [WG_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx",
     929             :         [WG_OUTPUT_NEXT_ERROR] = "error-drop",
     930             :   },
     931             : };
     932             : 
     933        1119 : VLIB_REGISTER_NODE (wg6_output_tun_node) =
     934             : {
     935             :   .name = "wg6-output-tun",
     936             :   .vector_size = sizeof (u32),
     937             :   .format_trace = format_wg_output_tun_trace,
     938             :   .type = VLIB_NODE_TYPE_INTERNAL,
     939             :   .n_errors = ARRAY_LEN (wg_output_error_strings),
     940             :   .error_strings = wg_output_error_strings,
     941             :   .n_next_nodes = WG_OUTPUT_N_NEXT,
     942             :   .next_nodes = {
     943             :         [WG_OUTPUT_NEXT_HANDOFF] = "wg6-output-tun-handoff",
     944             :         [WG_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx",
     945             :         [WG_OUTPUT_NEXT_ERROR] = "error-drop",
     946             :   },
     947             : };
     948             : /* *INDENT-ON* */
     949             : 
     950             : /*
     951             :  * fd.io coding-style-patch-verification: ON
     952             :  *
     953             :  * Local Variables:
     954             :  * eval: (c-set-style "gnu")
     955             :  * End:
     956             :  */

Generated by: LCOV version 1.14