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