wireguard: add burst mode
[vpp.git] / src / plugins / wireguard / wireguard_input.c
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 #include <wireguard/wireguard.h>
21
22 #include <wireguard/wireguard_send.h>
23 #include <wireguard/wireguard_if.h>
24
25 #define foreach_wg_input_error                                                \
26   _ (NONE, "No error")                                                        \
27   _ (HANDSHAKE_MAC, "Invalid MAC handshake")                                  \
28   _ (PEER, "Peer error")                                                      \
29   _ (INTERFACE, "Interface error")                                            \
30   _ (DECRYPTION, "Failed during decryption")                                  \
31   _ (KEEPALIVE_SEND, "Failed while sending Keepalive")                        \
32   _ (HANDSHAKE_SEND, "Failed while sending Handshake")                        \
33   _ (HANDSHAKE_RECEIVE, "Failed while receiving Handshake")                   \
34   _ (TOO_BIG, "Packet too big")                                               \
35   _ (UNDEFINED, "Undefined error")
36
37 typedef enum
38 {
39 #define _(sym,str) WG_INPUT_ERROR_##sym,
40   foreach_wg_input_error
41 #undef _
42     WG_INPUT_N_ERROR,
43 } wg_input_error_t;
44
45 static char *wg_input_error_strings[] = {
46 #define _(sym,string) string,
47   foreach_wg_input_error
48 #undef _
49 };
50
51 typedef struct
52 {
53   message_type_t type;
54   u16 current_length;
55   bool is_keepalive;
56   index_t peer;
57 } wg_input_trace_t;
58
59 u8 *
60 format_wg_message_type (u8 * s, va_list * args)
61 {
62   message_type_t type = va_arg (*args, message_type_t);
63
64   switch (type)
65     {
66 #define _(v,a) case MESSAGE_##v: return (format (s, "%s", a));
67       foreach_wg_message_type
68 #undef _
69     }
70   return (format (s, "unknown"));
71 }
72
73 /* packet trace format function */
74 static u8 *
75 format_wg_input_trace (u8 * s, va_list * args)
76 {
77   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
78   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
79
80   wg_input_trace_t *t = va_arg (*args, wg_input_trace_t *);
81
82   s = format (s, "Wireguard input: \n");
83   s = format (s, "    Type: %U\n", format_wg_message_type, t->type);
84   s = format (s, "    Peer: %d\n", t->peer);
85   s = format (s, "    Length: %d\n", t->current_length);
86   s = format (s, "    Keepalive: %s", t->is_keepalive ? "true" : "false");
87
88   return s;
89 }
90
91 typedef enum
92 {
93   WG_INPUT_NEXT_HANDOFF_HANDSHAKE,
94   WG_INPUT_NEXT_HANDOFF_DATA,
95   WG_INPUT_NEXT_IP4_INPUT,
96   WG_INPUT_NEXT_IP6_INPUT,
97   WG_INPUT_NEXT_PUNT,
98   WG_INPUT_NEXT_ERROR,
99   WG_INPUT_N_NEXT,
100 } wg_input_next_t;
101
102 /* static void */
103 /* set_peer_address (wg_peer_t * peer, ip4_address_t ip4, u16 udp_port) */
104 /* { */
105 /*   if (peer) */
106 /*     { */
107 /*       ip46_address_set_ip4 (&peer->dst.addr, &ip4); */
108 /*       peer->dst.port = udp_port; */
109 /*     } */
110 /* } */
111
112 static u8
113 is_ip4_header (u8 *data)
114 {
115   return (data[0] >> 4) == 0x4;
116 }
117
118 static wg_input_error_t
119 wg_handshake_process (vlib_main_t *vm, wg_main_t *wmp, vlib_buffer_t *b,
120                       u32 node_idx, u8 is_ip4)
121 {
122   ASSERT (vm->thread_index == 0);
123
124   enum cookie_mac_state mac_state;
125   bool packet_needs_cookie;
126   bool under_load;
127   index_t *wg_ifs;
128   wg_if_t *wg_if;
129   wg_peer_t *peer = NULL;
130
131   void *current_b_data = vlib_buffer_get_current (b);
132
133   ip46_address_t src_ip;
134   if (is_ip4)
135     {
136       ip4_header_t *iph4 =
137         current_b_data - sizeof (udp_header_t) - sizeof (ip4_header_t);
138       ip46_address_set_ip4 (&src_ip, &iph4->src_address);
139     }
140   else
141     {
142       ip6_header_t *iph6 =
143         current_b_data - sizeof (udp_header_t) - sizeof (ip6_header_t);
144       ip46_address_set_ip6 (&src_ip, &iph6->src_address);
145     }
146
147   udp_header_t *uhd = current_b_data - sizeof (udp_header_t);
148   u16 udp_src_port = clib_host_to_net_u16 (uhd->src_port);;
149   u16 udp_dst_port = clib_host_to_net_u16 (uhd->dst_port);;
150
151   message_header_t *header = current_b_data;
152   under_load = false;
153
154   if (PREDICT_FALSE (header->type == MESSAGE_HANDSHAKE_COOKIE))
155     {
156       message_handshake_cookie_t *packet =
157         (message_handshake_cookie_t *) current_b_data;
158       u32 *entry =
159         wg_index_table_lookup (&wmp->index_table, packet->receiver_index);
160       if (entry)
161         peer = wg_peer_get (*entry);
162       else
163         return WG_INPUT_ERROR_PEER;
164
165       // TODO: Implement cookie_maker_consume_payload
166
167       return WG_INPUT_ERROR_NONE;
168     }
169
170   u32 len = (header->type == MESSAGE_HANDSHAKE_INITIATION ?
171              sizeof (message_handshake_initiation_t) :
172              sizeof (message_handshake_response_t));
173
174   message_macs_t *macs = (message_macs_t *)
175     ((u8 *) current_b_data + len - sizeof (*macs));
176
177   index_t *ii;
178   wg_ifs = wg_if_indexes_get_by_port (udp_dst_port);
179   if (NULL == wg_ifs)
180     return WG_INPUT_ERROR_INTERFACE;
181
182   vec_foreach (ii, wg_ifs)
183     {
184       wg_if = wg_if_get (*ii);
185       if (NULL == wg_if)
186         continue;
187
188       mac_state = cookie_checker_validate_macs (
189         vm, &wg_if->cookie_checker, macs, current_b_data, len, under_load,
190         &src_ip, udp_src_port);
191       if (mac_state == INVALID_MAC)
192         {
193           wg_if = NULL;
194           continue;
195         }
196       break;
197     }
198
199   if (NULL == wg_if)
200     return WG_INPUT_ERROR_HANDSHAKE_MAC;
201
202   if ((under_load && mac_state == VALID_MAC_WITH_COOKIE)
203       || (!under_load && mac_state == VALID_MAC_BUT_NO_COOKIE))
204     packet_needs_cookie = false;
205   else if (under_load && mac_state == VALID_MAC_BUT_NO_COOKIE)
206     packet_needs_cookie = true;
207   else
208     return WG_INPUT_ERROR_HANDSHAKE_MAC;
209
210   switch (header->type)
211     {
212     case MESSAGE_HANDSHAKE_INITIATION:
213       {
214         message_handshake_initiation_t *message = current_b_data;
215
216         if (packet_needs_cookie)
217           {
218             // TODO: Add processing
219           }
220         noise_remote_t *rp;
221         if (noise_consume_initiation
222             (vm, noise_local_get (wg_if->local_idx), &rp,
223              message->sender_index, message->unencrypted_ephemeral,
224              message->encrypted_static, message->encrypted_timestamp))
225           {
226             peer = wg_peer_get (rp->r_peer_idx);
227           }
228         else
229           {
230             return WG_INPUT_ERROR_PEER;
231           }
232
233         // set_peer_address (peer, ip4_src, udp_src_port);
234         if (PREDICT_FALSE (!wg_send_handshake_response (vm, peer)))
235           {
236             vlib_node_increment_counter (vm, node_idx,
237                                          WG_INPUT_ERROR_HANDSHAKE_SEND, 1);
238           }
239         else
240           {
241             wg_peer_update_flags (rp->r_peer_idx, WG_PEER_ESTABLISHED, true);
242           }
243         break;
244       }
245     case MESSAGE_HANDSHAKE_RESPONSE:
246       {
247         message_handshake_response_t *resp = current_b_data;
248         index_t peeri = INDEX_INVALID;
249         u32 *entry =
250           wg_index_table_lookup (&wmp->index_table, resp->receiver_index);
251
252         if (PREDICT_TRUE (entry != NULL))
253           {
254             peeri = *entry;
255             peer = wg_peer_get (peeri);
256             if (wg_peer_is_dead (peer))
257               return WG_INPUT_ERROR_PEER;
258           }
259         else
260           return WG_INPUT_ERROR_PEER;
261
262         if (!noise_consume_response
263             (vm, &peer->remote, resp->sender_index,
264              resp->receiver_index, resp->unencrypted_ephemeral,
265              resp->encrypted_nothing))
266           {
267             return WG_INPUT_ERROR_PEER;
268           }
269         if (packet_needs_cookie)
270           {
271             // TODO: Add processing
272           }
273
274         // set_peer_address (peer, ip4_src, udp_src_port);
275         if (noise_remote_begin_session (vm, &peer->remote))
276           {
277
278             wg_timers_session_derived (peer);
279             wg_timers_handshake_complete (peer);
280             if (PREDICT_FALSE (!wg_send_keepalive (vm, peer)))
281               {
282                 vlib_node_increment_counter (vm, node_idx,
283                                              WG_INPUT_ERROR_KEEPALIVE_SEND, 1);
284               }
285             else
286               {
287                 wg_peer_update_flags (peeri, WG_PEER_ESTABLISHED, true);
288               }
289           }
290         break;
291       }
292     default:
293       return WG_INPUT_ERROR_HANDSHAKE_RECEIVE;
294     }
295
296   wg_timers_any_authenticated_packet_received (peer);
297   wg_timers_any_authenticated_packet_traversal (peer);
298   return WG_INPUT_ERROR_NONE;
299 }
300
301 static_always_inline void
302 wg_input_process_ops (vlib_main_t *vm, vlib_node_runtime_t *node,
303                       vnet_crypto_op_t *ops, vlib_buffer_t *b[], u16 *nexts,
304                       u16 drop_next)
305 {
306   u32 n_fail, n_ops = vec_len (ops);
307   vnet_crypto_op_t *op = ops;
308
309   if (n_ops == 0)
310     return;
311
312   n_fail = n_ops - vnet_crypto_process_ops (vm, op, n_ops);
313
314   while (n_fail)
315     {
316       ASSERT (op - ops < n_ops);
317
318       if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
319         {
320           u32 bi = op->user_data;
321           b[bi]->error = node->errors[WG_INPUT_ERROR_DECRYPTION];
322           nexts[bi] = drop_next;
323           n_fail--;
324         }
325       op++;
326     }
327 }
328
329 always_inline uword
330 wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
331                  vlib_frame_t *frame, u8 is_ip4)
332 {
333   wg_main_t *wmp = &wg_main;
334   wg_per_thread_data_t *ptd =
335     vec_elt_at_index (wmp->per_thread_data, vm->thread_index);
336   u32 *from = vlib_frame_vector_args (frame);
337   u32 n_left_from = frame->n_vectors;
338
339   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
340   u32 thread_index = vm->thread_index;
341   vnet_crypto_op_t **crypto_ops = &ptd->crypto_ops;
342   const u16 drop_next = WG_INPUT_NEXT_PUNT;
343   message_type_t header_type;
344   vlib_buffer_t *data_bufs[VLIB_FRAME_SIZE];
345   u32 data_bi[VLIB_FRAME_SIZE];  /* buffer index for data */
346   u32 other_bi[VLIB_FRAME_SIZE]; /* buffer index for drop or handoff */
347   u16 other_nexts[VLIB_FRAME_SIZE], *other_next = other_nexts, n_other = 0;
348   u16 data_nexts[VLIB_FRAME_SIZE], *data_next = data_nexts, n_data = 0;
349
350   vlib_get_buffers (vm, from, bufs, n_left_from);
351   vec_reset_length (ptd->crypto_ops);
352
353   f64 time = clib_time_now (&vm->clib_time) + vm->time_offset;
354
355   wg_peer_t *peer = NULL;
356   u32 *last_peer_time_idx = NULL;
357   u32 last_rec_idx = ~0;
358
359   bool is_keepalive = false;
360   u32 *peer_idx = NULL;
361
362   while (n_left_from > 0)
363     {
364       if (n_left_from > 2)
365         {
366           u8 *p;
367           vlib_prefetch_buffer_header (b[2], LOAD);
368           p = vlib_buffer_get_current (b[1]);
369           CLIB_PREFETCH (p, CLIB_CACHE_LINE_BYTES, LOAD);
370           CLIB_PREFETCH (vlib_buffer_get_tail (b[1]), CLIB_CACHE_LINE_BYTES,
371                          LOAD);
372         }
373
374       other_next[n_other] = WG_INPUT_NEXT_PUNT;
375       data_nexts[n_data] = WG_INPUT_N_NEXT;
376
377       header_type =
378         ((message_header_t *) vlib_buffer_get_current (b[0]))->type;
379
380       if (PREDICT_TRUE (header_type == MESSAGE_DATA))
381         {
382           message_data_t *data = vlib_buffer_get_current (b[0]);
383           u8 *iv_data = b[0]->pre_data;
384           peer_idx = wg_index_table_lookup (&wmp->index_table,
385                                             data->receiver_index);
386
387           if (data->receiver_index != last_rec_idx)
388             {
389               peer_idx = wg_index_table_lookup (&wmp->index_table,
390                                                 data->receiver_index);
391               if (PREDICT_TRUE (peer_idx != NULL))
392                 {
393                   peer = wg_peer_get (*peer_idx);
394                 }
395               last_rec_idx = data->receiver_index;
396             }
397
398           if (PREDICT_FALSE (!peer_idx))
399             {
400               other_next[n_other] = WG_INPUT_NEXT_ERROR;
401               b[0]->error = node->errors[WG_INPUT_ERROR_PEER];
402               other_bi[n_other] = from[b - bufs];
403               n_other += 1;
404               goto out;
405             }
406
407           if (PREDICT_FALSE (~0 == peer->input_thread_index))
408             {
409               /* this is the first packet to use this peer, claim the peer
410                * for this thread.
411                */
412               clib_atomic_cmp_and_swap (&peer->input_thread_index, ~0,
413                                         wg_peer_assign_thread (thread_index));
414             }
415
416           if (PREDICT_TRUE (thread_index != peer->input_thread_index))
417             {
418               other_next[n_other] = WG_INPUT_NEXT_HANDOFF_DATA;
419               other_bi[n_other] = from[b - bufs];
420               n_other += 1;
421               goto next;
422             }
423
424           u16 encr_len = b[0]->current_length - sizeof (message_data_t);
425           u16 decr_len = encr_len - NOISE_AUTHTAG_LEN;
426           if (PREDICT_FALSE (decr_len >= WG_DEFAULT_DATA_SIZE))
427             {
428               b[0]->error = node->errors[WG_INPUT_ERROR_TOO_BIG];
429               other_bi[n_other] = from[b - bufs];
430               n_other += 1;
431               goto out;
432             }
433
434           enum noise_state_crypt state_cr = noise_sync_remote_decrypt (
435             vm, crypto_ops, &peer->remote, data->receiver_index, data->counter,
436             data->encrypted_data, decr_len, data->encrypted_data, n_data,
437             iv_data, time);
438
439           if (PREDICT_FALSE (state_cr == SC_CONN_RESET))
440             {
441               wg_timers_handshake_complete (peer);
442               data_bufs[n_data] = b[0];
443               data_bi[n_data] = from[b - bufs];
444               n_data += 1;
445               goto next;
446             }
447           else if (PREDICT_FALSE (state_cr == SC_KEEP_KEY_FRESH))
448             {
449               wg_send_handshake_from_mt (*peer_idx, false);
450               data_bufs[n_data] = b[0];
451               data_bi[n_data] = from[b - bufs];
452               n_data += 1;
453               goto next;
454             }
455           else if (PREDICT_FALSE (state_cr == SC_FAILED))
456             {
457               wg_peer_update_flags (*peer_idx, WG_PEER_ESTABLISHED, false);
458               other_next[n_other] = WG_INPUT_NEXT_ERROR;
459               b[0]->error = node->errors[WG_INPUT_ERROR_DECRYPTION];
460               other_bi[n_other] = from[b - bufs];
461               n_other += 1;
462               goto out;
463             }
464           else if (PREDICT_TRUE (state_cr == SC_OK))
465             {
466               data_bufs[n_data] = b[0];
467               data_bi[n_data] = from[b - bufs];
468               n_data += 1;
469               goto next;
470             }
471         }
472       else
473         {
474           peer_idx = NULL;
475
476           /* Handshake packets should be processed in main thread */
477           if (thread_index != 0)
478             {
479               other_next[n_other] = WG_INPUT_NEXT_HANDOFF_HANDSHAKE;
480               other_bi[n_other] = from[b - bufs];
481               n_other += 1;
482               goto next;
483             }
484
485           wg_input_error_t ret =
486             wg_handshake_process (vm, wmp, b[0], node->node_index, is_ip4);
487           if (ret != WG_INPUT_ERROR_NONE)
488             {
489               other_next[n_other] = WG_INPUT_NEXT_ERROR;
490               b[0]->error = node->errors[ret];
491               other_bi[n_other] = from[b - bufs];
492               n_other += 1;
493             }
494           else
495             {
496               other_bi[n_other] = from[b - bufs];
497               n_other += 1;
498             }
499         }
500
501     out:
502       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
503                          (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
504         {
505           wg_input_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
506           t->type = header_type;
507           t->current_length = b[0]->current_length;
508           t->is_keepalive = is_keepalive;
509           t->peer = peer_idx ? *peer_idx : INDEX_INVALID;
510         }
511
512     next:
513       n_left_from -= 1;
514       b += 1;
515     }
516
517   /* decrypt packets */
518   wg_input_process_ops (vm, node, ptd->crypto_ops, data_bufs, data_nexts,
519                         drop_next);
520
521   /* process after decryption */
522   b = data_bufs;
523   n_left_from = n_data;
524   n_data = 0;
525   last_rec_idx = ~0;
526   last_peer_time_idx = NULL;
527   while (n_left_from > 0)
528     {
529       bool is_keepalive = false;
530       u32 *peer_idx = NULL;
531
532       if (data_next[n_data] == WG_INPUT_NEXT_PUNT)
533         {
534           goto trace;
535         }
536       else
537         {
538           data_next[n_data] = WG_INPUT_NEXT_PUNT;
539         }
540
541       message_data_t *data = vlib_buffer_get_current (b[0]);
542
543       if (data->receiver_index != last_rec_idx)
544         {
545           peer_idx =
546             wg_index_table_lookup (&wmp->index_table, data->receiver_index);
547           /* already checked and excisting */
548           peer = wg_peer_get (*peer_idx);
549           last_rec_idx = data->receiver_index;
550         }
551
552       noise_keypair_t *kp =
553         wg_get_active_keypair (&peer->remote, data->receiver_index);
554
555       if (!noise_counter_recv (&kp->kp_ctr, data->counter))
556         {
557           goto trace;
558         }
559
560       u16 encr_len = b[0]->current_length - sizeof (message_data_t);
561       u16 decr_len = encr_len - NOISE_AUTHTAG_LEN;
562
563       vlib_buffer_advance (b[0], sizeof (message_data_t));
564       b[0]->current_length = decr_len;
565       vnet_buffer_offload_flags_clear (b[0], VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
566
567       if (PREDICT_FALSE (peer_idx && (last_peer_time_idx != peer_idx)))
568         {
569           wg_timers_any_authenticated_packet_received_opt (peer, time);
570           wg_timers_any_authenticated_packet_traversal (peer);
571           last_peer_time_idx = peer_idx;
572         }
573
574       /* Keepalive packet has zero length */
575       if (decr_len == 0)
576         {
577           is_keepalive = true;
578           goto trace;
579         }
580
581       wg_timers_data_received (peer);
582
583       ip46_address_t src_ip;
584       u8 is_ip4_inner = is_ip4_header (vlib_buffer_get_current (b[0]));
585       if (is_ip4_inner)
586         {
587           ip46_address_set_ip4 (
588             &src_ip,
589             &((ip4_header_t *) vlib_buffer_get_current (b[0]))->src_address);
590         }
591       else
592         {
593           ip46_address_set_ip6 (
594             &src_ip,
595             &((ip6_header_t *) vlib_buffer_get_current (b[0]))->src_address);
596         }
597
598       const fib_prefix_t *allowed_ip;
599       bool allowed = false;
600
601       /*
602        * we could make this into an ACL, but the expectation
603        * is that there aren't many allowed IPs and thus a linear
604        * walk is fater than an ACL
605        */
606
607       vec_foreach (allowed_ip, peer->allowed_ips)
608         {
609           if (fib_prefix_is_cover_addr_46 (allowed_ip, &src_ip))
610             {
611               allowed = true;
612               break;
613             }
614         }
615       if (allowed)
616         {
617           vnet_buffer (b[0])->sw_if_index[VLIB_RX] = peer->wg_sw_if_index;
618           data_next[n_data] =
619             is_ip4_inner ? WG_INPUT_NEXT_IP4_INPUT : WG_INPUT_NEXT_IP6_INPUT;
620         }
621     trace:
622       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
623                          (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
624         {
625           wg_input_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
626           t->type = header_type;
627           t->current_length = b[0]->current_length;
628           t->is_keepalive = is_keepalive;
629           t->peer = peer_idx ? *peer_idx : INDEX_INVALID;
630         }
631
632       b += 1;
633       n_left_from -= 1;
634       n_data += 1;
635     }
636   /* enqueue other bufs */
637   vlib_buffer_enqueue_to_next (vm, node, other_bi, other_next, n_other);
638
639   /* enqueue data bufs */
640   vlib_buffer_enqueue_to_next (vm, node, data_bi, data_nexts, n_data);
641
642   return frame->n_vectors;
643 }
644
645 VLIB_NODE_FN (wg4_input_node)
646 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
647 {
648   return wg_input_inline (vm, node, frame, /* is_ip4 */ 1);
649 }
650
651 VLIB_NODE_FN (wg6_input_node)
652 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
653 {
654   return wg_input_inline (vm, node, frame, /* is_ip4 */ 0);
655 }
656
657 /* *INDENT-OFF* */
658 VLIB_REGISTER_NODE (wg4_input_node) =
659 {
660   .name = "wg4-input",
661   .vector_size = sizeof (u32),
662   .format_trace = format_wg_input_trace,
663   .type = VLIB_NODE_TYPE_INTERNAL,
664   .n_errors = ARRAY_LEN (wg_input_error_strings),
665   .error_strings = wg_input_error_strings,
666   .n_next_nodes = WG_INPUT_N_NEXT,
667   /* edit / add dispositions here */
668   .next_nodes = {
669         [WG_INPUT_NEXT_HANDOFF_HANDSHAKE] = "wg4-handshake-handoff",
670         [WG_INPUT_NEXT_HANDOFF_DATA] = "wg4-input-data-handoff",
671         [WG_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
672         [WG_INPUT_NEXT_IP6_INPUT] = "ip6-input",
673         [WG_INPUT_NEXT_PUNT] = "error-punt",
674         [WG_INPUT_NEXT_ERROR] = "error-drop",
675   },
676 };
677
678 VLIB_REGISTER_NODE (wg6_input_node) =
679 {
680   .name = "wg6-input",
681   .vector_size = sizeof (u32),
682   .format_trace = format_wg_input_trace,
683   .type = VLIB_NODE_TYPE_INTERNAL,
684   .n_errors = ARRAY_LEN (wg_input_error_strings),
685   .error_strings = wg_input_error_strings,
686   .n_next_nodes = WG_INPUT_N_NEXT,
687   /* edit / add dispositions here */
688   .next_nodes = {
689         [WG_INPUT_NEXT_HANDOFF_HANDSHAKE] = "wg6-handshake-handoff",
690         [WG_INPUT_NEXT_HANDOFF_DATA] = "wg6-input-data-handoff",
691         [WG_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
692         [WG_INPUT_NEXT_IP6_INPUT] = "ip6-input",
693         [WG_INPUT_NEXT_PUNT] = "error-punt",
694         [WG_INPUT_NEXT_ERROR] = "error-drop",
695   },
696 };
697 /* *INDENT-ON* */
698
699 /*
700  * fd.io coding-style-patch-verification: ON
701  *
702  * Local Variables:
703  * eval: (c-set-style "gnu")
704  * End:
705  */