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