Fix icmp/udp/tcp punt/drop paths
[vpp.git] / src / vnet / ip / punt.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /**
17  * @file
18  * @brief Local TCP/IP stack punt infrastructure.
19  *
20  * Provides a set of VPP nodes together with the relevant APIs and CLI
21  * commands in order to adjust and dispatch packets from the VPP data plane
22  * to the local TCP/IP stack
23  */
24
25 #include <vnet/ip/ip.h>
26 #include <vlib/vlib.h>
27 #include <vnet/pg/pg.h>
28 #include <vnet/udp/udp.h>
29 #include <vnet/tcp/tcp.h>
30 #include <vnet/ip/punt.h>
31 #include <vppinfra/sparse_vec.h>
32 #include <vlib/unix/unix.h>
33
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <sys/socket.h>
37 #include <sys/un.h>
38 #include <sys/uio.h>
39 #include <stdlib.h>
40 #include <stdbool.h>
41
42 #define foreach_punt_next                       \
43   _ (PUNT4, "ip4-punt")                         \
44   _ (PUNT6, "ip6-punt")
45
46 typedef enum
47 {
48 #define _(s,n) PUNT_NEXT_##s,
49   foreach_punt_next
50 #undef _
51     PUNT_N_NEXT,
52 } punt_next_t;
53
54 enum punt_socket_rx_next_e
55 {
56   PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT,
57   PUNT_SOCKET_RX_NEXT_IP4_LOOKUP,
58   PUNT_SOCKET_RX_NEXT_IP6_LOOKUP,
59   PUNT_SOCKET_RX_N_NEXT
60 };
61
62 #define punt_next_punt(is_ip4) (is_ip4 ? PUNT_NEXT_PUNT4 : PUNT_NEXT_PUNT6)
63
64 vlib_node_registration_t udp4_punt_node;
65 vlib_node_registration_t udp6_punt_node;
66 vlib_node_registration_t udp4_punt_socket_node;
67 vlib_node_registration_t udp6_punt_socket_node;
68 static vlib_node_registration_t punt_socket_rx_node;
69
70 punt_main_t punt_main;
71
72 char *
73 vnet_punt_get_server_pathname (void)
74 {
75   punt_main_t *pm = &punt_main;
76   return pm->sun_path;
77 }
78
79 /** @brief IPv4/IPv6 UDP punt node main loop.
80
81     This is the main loop inline function for IPv4/IPv6 UDP punt
82     transition node.
83
84     @param vm vlib_main_t corresponding to the current thread
85     @param node vlib_node_runtime_t
86     @param frame vlib_frame_t whose contents should be dispatched
87     @param is_ipv4 indicates if called for IPv4 or IPv6 node
88 */
89 always_inline uword
90 udp46_punt_inline (vlib_main_t * vm,
91                    vlib_node_runtime_t * node,
92                    vlib_frame_t * from_frame, int is_ip4)
93 {
94   u32 n_left_from, *from, *to_next;
95   word advance;
96
97   from = vlib_frame_vector_args (from_frame);
98   n_left_from = from_frame->n_vectors;
99
100   /* udp[46]_lookup hands us the data payload, not the IP header */
101   if (is_ip4)
102     advance = -(sizeof (ip4_header_t) + sizeof (udp_header_t));
103   else
104     advance = -(sizeof (ip6_header_t) + sizeof (udp_header_t));
105
106   while (n_left_from > 0)
107     {
108       u32 n_left_to_next;
109
110       vlib_get_next_frame (vm, node, punt_next_punt (is_ip4), to_next,
111                            n_left_to_next);
112
113       while (n_left_from > 0 && n_left_to_next > 0)
114         {
115           u32 bi0;
116           vlib_buffer_t *b0;
117
118           bi0 = from[0];
119           to_next[0] = bi0;
120           from += 1;
121           to_next += 1;
122           n_left_from -= 1;
123           n_left_to_next -= 1;
124
125           b0 = vlib_get_buffer (vm, bi0);
126           vlib_buffer_advance (b0, advance);
127           b0->error = node->errors[PUNT_ERROR_UDP_PORT];
128         }
129
130       vlib_put_next_frame (vm, node, punt_next_punt (is_ip4), n_left_to_next);
131     }
132
133   return from_frame->n_vectors;
134 }
135
136 static char *punt_error_strings[] = {
137 #define punt_error(n,s) s,
138 #include "punt_error.def"
139 #undef punt_error
140 };
141
142 /** @brief IPv4 UDP punt node.
143     @node ip4-udp-punt
144
145     This is the IPv4 UDP punt transition node. It is registered as a next
146     node for the "ip4-udp-lookup" handling UDP port(s) requested for punt.
147     The buffer's current data pointer is adjusted to the original packet
148     IPv4 header. All buffers are dispatched to "error-punt".
149
150     @param vm vlib_main_t corresponding to the current thread
151     @param node vlib_node_runtime_t
152     @param frame vlib_frame_t whose contents should be dispatched
153
154     @par Graph mechanics: next index usage
155
156     @em Sets:
157     - <code>vnet_buffer(b)->current_data</code>
158     - <code>vnet_buffer(b)->current_len</code>
159
160     <em>Next Index:</em>
161     - Dispatches the packet to the "error-punt" node
162 */
163 static uword
164 udp4_punt (vlib_main_t * vm,
165            vlib_node_runtime_t * node, vlib_frame_t * from_frame)
166 {
167   return udp46_punt_inline (vm, node, from_frame, 1 /* is_ip4 */ );
168 }
169
170 /** @brief IPv6 UDP punt node.
171     @node ip6-udp-punt
172
173     This is the IPv6 UDP punt transition node. It is registered as a next
174     node for the "ip6-udp-lookup" handling UDP port(s) requested for punt.
175     The buffer's current data pointer is adjusted to the original packet
176     IPv6 header. All buffers are dispatched to "error-punt".
177
178     @param vm vlib_main_t corresponding to the current thread
179     @param node vlib_node_runtime_t
180     @param frame vlib_frame_t whose contents should be dispatched
181
182     @par Graph mechanics: next index usage
183
184     @em Sets:
185     - <code>vnet_buffer(b)->current_data</code>
186     - <code>vnet_buffer(b)->current_len</code>
187
188     <em>Next Index:</em>
189     - Dispatches the packet to the "error-punt" node
190 */
191 static uword
192 udp6_punt (vlib_main_t * vm,
193            vlib_node_runtime_t * node, vlib_frame_t * from_frame)
194 {
195   return udp46_punt_inline (vm, node, from_frame, 0 /* is_ip4 */ );
196 }
197
198 /* *INDENT-OFF* */
199 VLIB_REGISTER_NODE (udp4_punt_node) = {
200   .function = udp4_punt,
201   .name = "ip4-udp-punt",
202   /* Takes a vector of packets. */
203   .vector_size = sizeof (u32),
204
205   .n_errors = PUNT_N_ERROR,
206   .error_strings = punt_error_strings,
207
208   .n_next_nodes = PUNT_N_NEXT,
209   .next_nodes = {
210 #define _(s,n) [PUNT_NEXT_##s] = n,
211      foreach_punt_next
212 #undef _
213   },
214 };
215
216 VLIB_NODE_FUNCTION_MULTIARCH (udp4_punt_node, udp4_punt);
217
218 VLIB_REGISTER_NODE (udp6_punt_node) = {
219   .function = udp6_punt,
220   .name = "ip6-udp-punt",
221   /* Takes a vector of packets. */
222   .vector_size = sizeof (u32),
223
224   .n_errors = PUNT_N_ERROR,
225   .error_strings = punt_error_strings,
226
227   .n_next_nodes = PUNT_N_NEXT,
228   .next_nodes = {
229 #define _(s,n) [PUNT_NEXT_##s] = n,
230      foreach_punt_next
231 #undef _
232   },
233 };
234
235 VLIB_NODE_FUNCTION_MULTIARCH (udp6_punt_node, udp6_punt);;
236
237 /* *INDENT-ON* */
238
239 static punt_client_t *
240 punt_client_get (bool is_ip4, u16 port)
241 {
242   punt_main_t *pm = &punt_main;
243   punt_client_t *v =
244     is_ip4 ? pm->clients_by_dst_port4 : pm->clients_by_dst_port6;
245
246   u16 i = sparse_vec_index (v, port);
247   if (i == SPARSE_VEC_INVALID_INDEX)
248     return 0;
249
250   return &vec_elt (v, i);
251 }
252
253 static struct sockaddr_un *
254 punt_socket_get (bool is_ip4, u16 port)
255 {
256   punt_client_t *v = punt_client_get (is_ip4, port);
257   if (v)
258     return &v->caddr;
259
260   return NULL;
261 }
262
263 static void
264 punt_socket_register (bool is_ip4, u8 protocol, u16 port,
265                       char *client_pathname)
266 {
267   punt_main_t *pm = &punt_main;
268   punt_client_t c, *n;
269   punt_client_t *v = is_ip4 ? pm->clients_by_dst_port4 :
270     pm->clients_by_dst_port6;
271
272   memset (&c, 0, sizeof (c));
273   memcpy (c.caddr.sun_path, client_pathname, sizeof (c.caddr.sun_path));
274   c.caddr.sun_family = AF_UNIX;
275   c.port = port;
276   n = sparse_vec_validate (v, port);
277   n[0] = c;
278 }
279
280 /* $$$$ Just leaves the mapping in place for now */
281 static void
282 punt_socket_unregister (bool is_ip4, u8 protocol, u16 port)
283 {
284   return;
285 }
286
287 typedef struct
288 {
289   punt_client_t client;
290   u8 is_midchain;
291 } udp_punt_trace_t;
292
293 u8 *
294 format_udp_punt_trace (u8 * s, va_list * args)
295 {
296   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
297   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
298   udp_punt_trace_t *t = va_arg (*args, udp_punt_trace_t *);
299   u32 indent = format_get_indent (s);
300   s = format (s, "to: %s", t->client.caddr.sun_path);
301   if (t->is_midchain)
302     {
303       s = format (s, "\n%U(buffer is part of chain)", format_white_space,
304                   indent);
305     }
306   return s;
307 }
308
309 always_inline uword
310 udp46_punt_socket_inline (vlib_main_t * vm,
311                           vlib_node_runtime_t * node,
312                           vlib_frame_t * frame, bool is_ip4)
313 {
314   u32 *buffers = vlib_frame_args (frame);
315   uword n_packets = frame->n_vectors;
316   struct iovec *iovecs = 0;
317   punt_main_t *pm = &punt_main;
318   int i;
319
320   u32 node_index = is_ip4 ? udp4_punt_socket_node.index :
321     udp6_punt_socket_node.index;
322
323   for (i = 0; i < n_packets; i++)
324     {
325       struct iovec *iov;
326       vlib_buffer_t *b;
327       uword l;
328       punt_packetdesc_t packetdesc;
329
330       b = vlib_get_buffer (vm, buffers[i]);
331
332       /* Reverse UDP Punt advance */
333       udp_header_t *udp;
334       if (is_ip4)
335         {
336           vlib_buffer_advance (b, -(sizeof (ip4_header_t) +
337                                     sizeof (udp_header_t)));
338           ip4_header_t *ip = vlib_buffer_get_current (b);
339           udp = (udp_header_t *) (ip + 1);
340         }
341       else
342         {
343           vlib_buffer_advance (b, -(sizeof (ip6_header_t) +
344                                     sizeof (udp_header_t)));
345           ip6_header_t *ip = vlib_buffer_get_current (b);
346           udp = (udp_header_t *) (ip + 1);
347         }
348
349       u16 port = clib_net_to_host_u16 (udp->dst_port);
350
351       /*
352        * Find registerered client
353        * If no registered client, drop packet and count
354        */
355       struct sockaddr_un *caddr;
356       caddr = punt_socket_get (is_ip4, port);
357       if (!caddr)
358         {
359           vlib_node_increment_counter (vm, node_index,
360                                        PUNT_ERROR_SOCKET_TX_ERROR, 1);
361           goto error;
362         }
363
364       punt_client_t *c = NULL;
365       if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
366         {
367           c = punt_client_get (is_ip4, port);
368           udp_punt_trace_t *t;
369           t = vlib_add_trace (vm, node, b, sizeof (t[0]));
370           clib_memcpy (&t->client, c, sizeof (t->client));
371         }
372
373       /* Re-set iovecs if present. */
374       if (iovecs)
375         _vec_len (iovecs) = 0;
376
377       /* Add packet descriptor */
378       packetdesc.sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
379       packetdesc.action = 0;
380       vec_add2 (iovecs, iov, 1);
381       iov->iov_base = &packetdesc;
382       iov->iov_len = sizeof (packetdesc);
383
384       /** VLIB buffer chain -> Unix iovec(s). */
385       vlib_buffer_advance (b, -(sizeof (ethernet_header_t)));
386       vec_add2 (iovecs, iov, 1);
387       iov->iov_base = b->data + b->current_data;
388       iov->iov_len = l = b->current_length;
389
390       if (PREDICT_FALSE (b->flags & VLIB_BUFFER_NEXT_PRESENT))
391         {
392           do
393             {
394               b = vlib_get_buffer (vm, b->next_buffer);
395               if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
396                 {
397                   if (PREDICT_FALSE (!c))
398                     {
399                       c = punt_client_get (is_ip4, port);
400                     }
401                   udp_punt_trace_t *t;
402                   t = vlib_add_trace (vm, node, b, sizeof (t[0]));
403                   clib_memcpy (&t->client, c, sizeof (t->client));
404                   t->is_midchain = 1;
405                 }
406
407               vec_add2 (iovecs, iov, 1);
408
409               iov->iov_base = b->data + b->current_data;
410               iov->iov_len = b->current_length;
411               l += b->current_length;
412             }
413           while (b->flags & VLIB_BUFFER_NEXT_PRESENT);
414         }
415
416       struct msghdr msg = {
417         .msg_name = caddr,
418         .msg_namelen = sizeof (*caddr),
419         .msg_iov = iovecs,
420         .msg_iovlen = vec_len (iovecs),
421       };
422
423       if (sendmsg (pm->socket_fd, &msg, 0) < (ssize_t) l)
424         vlib_node_increment_counter (vm, node_index,
425                                      PUNT_ERROR_SOCKET_TX_ERROR, 1);
426     }
427
428 error:
429   vlib_buffer_free (vm, buffers, n_packets);
430
431   return n_packets;
432 }
433
434 static uword
435 udp4_punt_socket (vlib_main_t * vm,
436                   vlib_node_runtime_t * node, vlib_frame_t * from_frame)
437 {
438   return udp46_punt_socket_inline (vm, node, from_frame, true /* is_ip4 */ );
439 }
440
441 static uword
442 udp6_punt_socket (vlib_main_t * vm,
443                   vlib_node_runtime_t * node, vlib_frame_t * from_frame)
444 {
445   return udp46_punt_socket_inline (vm, node, from_frame, false /* is_ip4 */ );
446 }
447
448
449 /* *INDENT-OFF* */
450 VLIB_REGISTER_NODE (udp4_punt_socket_node) = {
451   .function = udp4_punt_socket,
452   .name = "ip4-udp-punt-socket",
453   .format_trace = format_udp_punt_trace,
454   .flags = VLIB_NODE_FLAG_IS_DROP,
455   /* Takes a vector of packets. */
456   .vector_size = sizeof (u32),
457   .n_errors = PUNT_N_ERROR,
458   .error_strings = punt_error_strings,
459 };
460 VLIB_REGISTER_NODE (udp6_punt_socket_node) = {
461   .function = udp6_punt_socket,
462   .name = "ip6-udp-punt-socket",
463   .format_trace = format_udp_punt_trace,
464   .flags = VLIB_NODE_FLAG_IS_DROP,
465   .vector_size = sizeof (u32),
466   .n_errors = PUNT_N_ERROR,
467   .error_strings = punt_error_strings,
468 };
469 /* *INDENT-ON* */
470
471 typedef struct
472 {
473   enum punt_action_e action;
474   u32 sw_if_index;
475 } punt_trace_t;
476
477 static u8 *
478 format_punt_trace (u8 * s, va_list * va)
479 {
480   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
481   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
482   vnet_main_t *vnm = vnet_get_main ();
483   punt_trace_t *t = va_arg (*va, punt_trace_t *);
484   s = format (s, "%U Action: %d", format_vnet_sw_if_index_name,
485               vnm, t->sw_if_index, t->action);
486   return s;
487 }
488
489 static uword
490 punt_socket_rx_fd (vlib_main_t * vm, vlib_node_runtime_t * node, u32 fd)
491 {
492   const uword buffer_size = VLIB_BUFFER_DATA_SIZE;
493   u32 n_trace = vlib_get_trace_count (vm, node);
494   u32 next = node->cached_next_index;
495   u32 n_left_to_next, next_index;
496   u32 *to_next;
497   u32 error = PUNT_ERROR_NONE;
498   vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
499
500   /* $$$$ Only dealing with one buffer at the time for now */
501
502   u32 bi;
503   vlib_buffer_t *b;
504   punt_packetdesc_t packetdesc;
505   ssize_t size;
506   struct iovec io[2];
507
508   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
509     {
510       error = PUNT_ERROR_NOBUFFER;
511       goto error;
512     }
513
514   b = vlib_get_buffer (vm, bi);
515   io[0].iov_base = &packetdesc;
516   io[0].iov_len = sizeof (packetdesc);
517   io[1].iov_base = b->data;
518   io[1].iov_len = buffer_size;
519
520   size = readv (fd, io, 2);
521   /* We need at least the packet descriptor plus a header */
522   if (size <= (int) (sizeof (packetdesc) + sizeof (ip4_header_t)))
523     {
524       vlib_buffer_free (vm, &bi, 1);
525       error = PUNT_ERROR_READV;
526       goto error;
527     }
528
529   b->flags = VNET_BUFFER_F_LOCALLY_ORIGINATED;
530   b->current_length = size - sizeof (packetdesc);
531
532   VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b);
533
534   switch (packetdesc.action)
535     {
536     case PUNT_L2:
537       vnet_buffer (b)->sw_if_index[VLIB_TX] = packetdesc.sw_if_index;
538       next_index = PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT;
539       break;
540
541     case PUNT_IP4_ROUTED:
542       vnet_buffer (b)->sw_if_index[VLIB_RX] = packetdesc.sw_if_index;
543       vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
544       next_index = PUNT_SOCKET_RX_NEXT_IP4_LOOKUP;
545       break;
546
547     case PUNT_IP6_ROUTED:
548       vnet_buffer (b)->sw_if_index[VLIB_RX] = packetdesc.sw_if_index;
549       vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
550       next_index = PUNT_SOCKET_RX_NEXT_IP6_LOOKUP;
551       break;
552
553     default:
554       error = PUNT_ERROR_ACTION;
555       vlib_buffer_free (vm, &bi, 1);
556       goto error;
557     }
558
559   if (PREDICT_FALSE (n_trace > 0))
560     {
561       punt_trace_t *t;
562       vlib_trace_buffer (vm, node, next_index, b, 1 /* follow_chain */ );
563       vlib_set_trace_count (vm, node, --n_trace);
564       t = vlib_add_trace (vm, node, b, sizeof (*t));
565       t->sw_if_index = packetdesc.sw_if_index;
566       t->action = packetdesc.action;
567     }
568
569   to_next[0] = bi;
570   to_next++;
571   n_left_to_next--;
572
573   vlib_validate_buffer_enqueue_x1 (vm, node, next, to_next, n_left_to_next,
574                                    bi, next_index);
575   vlib_put_next_frame (vm, node, next, n_left_to_next);
576   return 1;
577
578 error:
579   vlib_node_increment_counter (vm, punt_socket_rx_node.index, error, 1);
580   return 0;
581 }
582
583 static uword
584 punt_socket_rx (vlib_main_t * vm,
585                 vlib_node_runtime_t * node, vlib_frame_t * frame)
586 {
587   punt_main_t *pm = &punt_main;
588   u32 total_count = 0;
589   int i;
590
591   for (i = 0; i < vec_len (pm->ready_fds); i++)
592     {
593       total_count += punt_socket_rx_fd (vm, node, pm->ready_fds[i]);
594       vec_del1 (pm->ready_fds, i);
595     }
596   return total_count;
597 }
598
599 VLIB_REGISTER_NODE (punt_socket_rx_node, static) =
600 {
601   .function = punt_socket_rx,.name = "punt-socket-rx",.type =
602     VLIB_NODE_TYPE_INPUT,.state = VLIB_NODE_STATE_INTERRUPT,.vector_size =
603     1,.n_errors = PUNT_N_ERROR,.error_strings =
604     punt_error_strings,.n_next_nodes = PUNT_SOCKET_RX_N_NEXT,.next_nodes =
605   {
606 [PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT] = "interface-output",
607       [PUNT_SOCKET_RX_NEXT_IP4_LOOKUP] = "ip4-lookup",
608       [PUNT_SOCKET_RX_NEXT_IP6_LOOKUP] = "ip6-lookup",},.format_trace =
609     format_punt_trace,};
610
611 static clib_error_t *
612 punt_socket_read_ready (clib_file_t * uf)
613 {
614   vlib_main_t *vm = vlib_get_main ();
615   punt_main_t *pm = &punt_main;
616
617   /** Schedule the rx node */
618   vlib_node_set_interrupt_pending (vm, punt_socket_rx_node.index);
619   vec_add1 (pm->ready_fds, uf->file_descriptor);
620
621   return 0;
622 }
623
624 clib_error_t *
625 vnet_punt_socket_add (vlib_main_t * vm, u32 header_version,
626                       bool is_ip4, u8 protocol, u16 port,
627                       char *client_pathname)
628 {
629   punt_main_t *pm = &punt_main;
630
631   if (!pm->is_configured)
632     return clib_error_return (0, "socket is not configured");
633
634   if (header_version != PUNT_PACKETDESC_VERSION)
635     return clib_error_return (0, "Invalid packet descriptor version");
636
637   /* For now we only support UDP punt */
638   if (protocol != IP_PROTOCOL_UDP)
639     return clib_error_return (0,
640                               "only UDP protocol (%d) is supported, got %d",
641                               IP_PROTOCOL_UDP, protocol);
642
643   if (port == (u16) ~ 0)
644     return clib_error_return (0, "UDP port number required");
645
646   /* Register client */
647   punt_socket_register (is_ip4, protocol, port, client_pathname);
648
649   u32 node_index = is_ip4 ? udp4_punt_socket_node.index :
650     udp6_punt_socket_node.index;
651
652   udp_register_dst_port (vm, port, node_index, is_ip4);
653
654   return 0;
655 }
656
657 clib_error_t *
658 vnet_punt_socket_del (vlib_main_t * vm, bool is_ip4, u8 l4_protocol, u16 port)
659 {
660   punt_main_t *pm = &punt_main;
661
662   if (!pm->is_configured)
663     return clib_error_return (0, "socket is not configured");
664
665   punt_socket_unregister (is_ip4, l4_protocol, port);
666   udp_unregister_dst_port (vm, port, is_ip4);
667
668   return 0;
669 }
670
671 /**
672  * @brief Request IP traffic punt to the local TCP/IP stack.
673  *
674  * @em Note
675  * - UDP and TCP are the only protocols supported in the current implementation
676  *
677  * @param vm       vlib_main_t corresponding to the current thread
678  * @param ipv      IP protcol version.
679  *                 4 - IPv4, 6 - IPv6, ~0 for both IPv6 and IPv4
680  * @param protocol 8-bits L4 protocol value
681  *                 UDP is 17
682  *                 TCP is 1
683  * @param port     16-bits L4 (TCP/IP) port number when applicable (UDP only)
684  *
685  * @returns 0 on success, non-zero value otherwise
686  */
687 clib_error_t *
688 vnet_punt_add_del (vlib_main_t * vm, u8 ipv, u8 protocol, u16 port,
689                    bool is_add)
690 {
691
692   /* For now we only support UDP punt */
693   if (protocol != IP_PROTOCOL_UDP && protocol != IP_PROTOCOL_TCP)
694     return clib_error_return (0,
695                               "only UDP (%d) and TCP (%d) protocols are supported, got %d",
696                               IP_PROTOCOL_UDP, IP_PROTOCOL_TCP, protocol);
697
698   if (ipv != (u8) ~ 0 && ipv != 4 && ipv != 6)
699     return clib_error_return (0, "IP version must be 4 or 6, got %d", ipv);
700
701   if (port == (u16) ~ 0)
702     {
703       if ((ipv == 4) || (ipv == (u8) ~ 0))
704         {
705           if (protocol == IP_PROTOCOL_UDP)
706             udp_punt_unknown (vm, 1, is_add);
707           else if (protocol == IP_PROTOCOL_TCP)
708             tcp_punt_unknown (vm, 1, is_add);
709         }
710
711       if ((ipv == 6) || (ipv == (u8) ~ 0))
712         {
713           if (protocol == IP_PROTOCOL_UDP)
714             udp_punt_unknown (vm, 0, is_add);
715           else if (protocol == IP_PROTOCOL_TCP)
716             tcp_punt_unknown (vm, 0, is_add);
717         }
718
719       return 0;
720     }
721
722   else if (is_add)
723     {
724       if (protocol == IP_PROTOCOL_TCP)
725         return clib_error_return (0, "punt TCP ports is not supported yet");
726
727       if (ipv == 4 || ipv == (u8) ~ 0)
728         udp_register_dst_port (vm, port, udp4_punt_node.index, 1);
729
730       if (ipv == 6 || ipv == (u8) ~ 0)
731         udp_register_dst_port (vm, port, udp6_punt_node.index, 0);
732
733       return 0;
734     }
735   else
736     return clib_error_return (0, "punt delete is not supported yet");
737 }
738
739 static clib_error_t *
740 punt_cli (vlib_main_t * vm,
741           unformat_input_t * input, vlib_cli_command_t * cmd)
742 {
743   u32 port;
744   bool is_add = true;
745   u32 protocol = ~0;
746   clib_error_t *error = NULL;
747
748   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
749     {
750       if (unformat (input, "del"))
751         is_add = false;
752       else if (unformat (input, "all"))
753         {
754           /* punt both IPv6 and IPv4 when used in CLI */
755           error = vnet_punt_add_del (vm, ~0, protocol, ~0, is_add);
756           if (error)
757             {
758               clib_error_report (error);
759               goto done;
760             }
761         }
762       else if (unformat (input, "%d", &port))
763         {
764           /* punt both IPv6 and IPv4 when used in CLI */
765           error = vnet_punt_add_del (vm, ~0, protocol, port, is_add);
766           if (error)
767             {
768               clib_error_report (error);
769               goto done;
770             }
771         }
772       else if (unformat (input, "udp"))
773         protocol = IP_PROTOCOL_UDP;
774       else if (unformat (input, "tcp"))
775         protocol = IP_PROTOCOL_TCP;
776       else
777         {
778           error = clib_error_return (0, "parse error: '%U'",
779                                      format_unformat_error, input);
780           goto done;
781         }
782     }
783 done:
784   return error;
785 }
786
787 /*?
788  * The set of '<em>set punt</em>' commands allows specific IP traffic to
789  * be punted to the host TCP/IP stack
790  *
791  * @em Note
792  * - UDP is the only protocol supported in the current implementation
793  * - All TCP traffic is currently punted to the host by default
794  *
795  * @cliexpar
796  * @parblock
797  * Example of how to request NTP traffic to be punted
798  * @cliexcmd{set punt udp 125}
799  *
800  * Example of how to request all 'unknown' UDP traffic to be punted
801  * @cliexcmd{set punt udp all}
802  *
803  * Example of how to stop all 'unknown' UDP traffic to be punted
804  * @cliexcmd{set punt udp del all}
805  * @endparblock
806 ?*/
807 /* *INDENT-OFF* */
808 VLIB_CLI_COMMAND (punt_command, static) = {
809   .path = "set punt",
810   .short_help = "set punt [udp|tcp] [del] <all | port-num1 [port-num2 ...]>",
811   .function = punt_cli,
812 };
813 /* *INDENT-ON* */
814
815 clib_error_t *
816 punt_init (vlib_main_t * vm)
817 {
818   punt_main_t *pm = &punt_main;
819
820   pm->clients_by_dst_port6 = sparse_vec_new
821     (sizeof (pm->clients_by_dst_port6[0]),
822      BITS (((udp_header_t *) 0)->dst_port));
823   pm->clients_by_dst_port4 = sparse_vec_new
824     (sizeof (pm->clients_by_dst_port4[0]),
825      BITS (((udp_header_t *) 0)->dst_port));
826
827   pm->is_configured = false;
828   pm->interface_output_node = vlib_get_node_by_name (vm,
829                                                      (u8 *)
830                                                      "interface-output");
831   return 0;
832 }
833
834 VLIB_INIT_FUNCTION (punt_init);
835
836 static clib_error_t *
837 punt_config (vlib_main_t * vm, unformat_input_t * input)
838 {
839   punt_main_t *pm = &punt_main;
840   char *socket_path = 0;
841
842   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
843     {
844       if (unformat (input, "socket %s", &socket_path))
845         strncpy (pm->sun_path, socket_path, 108 - 1);
846       else
847         return clib_error_return (0, "unknown input `%U'",
848                                   format_unformat_error, input);
849     }
850
851   if (socket_path == 0)
852     return 0;
853
854   /* UNIX domain socket */
855   struct sockaddr_un addr;
856   if ((pm->socket_fd = socket (AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
857     {
858       return clib_error_return (0, "socket error");
859     }
860
861   memset (&addr, 0, sizeof (addr));
862   addr.sun_family = AF_UNIX;
863   if (*socket_path == '\0')
864     {
865       *addr.sun_path = '\0';
866       strncpy (addr.sun_path + 1, socket_path + 1,
867                sizeof (addr.sun_path) - 2);
868     }
869   else
870     {
871       strncpy (addr.sun_path, socket_path, sizeof (addr.sun_path) - 1);
872       unlink (socket_path);
873     }
874
875   if (bind (pm->socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
876     {
877       return clib_error_return (0, "bind error");
878     }
879
880   /* Register socket */
881   clib_file_main_t *fm = &file_main;
882   clib_file_t template = { 0 };
883   template.read_function = punt_socket_read_ready;
884   template.file_descriptor = pm->socket_fd;
885   pm->clib_file_index = clib_file_add (fm, &template);
886
887   pm->is_configured = true;
888
889   return 0;
890 }
891
892 VLIB_CONFIG_FUNCTION (punt_config, "punt");
893
894 /*
895  * fd.io coding-style-patch-verification: ON
896  *
897  * Local Variables:
898  * eval: (c-set-style "gnu")
899  * End:
900  */