blob: 67c54c3c5c119ca0a676477bf24006a48f1b6917 [file] [log] [blame]
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -07001/*
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 *
Ole Troanf7a55ad2017-05-16 14:59:29 +020020 * Provides a set of VPP nodes together with the relevant APIs and CLI
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -070021 * commands in order to adjust and dispatch packets from the VPP data plane
22 * to the local TCP/IP stack
23 */
Ole Troanf7a55ad2017-05-16 14:59:29 +020024
25#include <vnet/ip/ip.h>
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -070026#include <vlib/vlib.h>
27#include <vnet/pg/pg.h>
Dave Barach68b0fb02017-02-28 15:15:56 -050028#include <vnet/udp/udp.h>
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -070029#include <vnet/ip/punt.h>
Ole Troanf7a55ad2017-05-16 14:59:29 +020030#include <vppinfra/sparse_vec.h>
31#include <vlib/unix/unix.h>
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -070032
Ole Troanf7a55ad2017-05-16 14:59:29 +020033#include <stdio.h>
34#include <unistd.h>
35#include <sys/socket.h>
36#include <sys/un.h>
Marco Varlese22349832017-09-08 10:40:34 +020037#include <sys/uio.h>
Ole Troanf7a55ad2017-05-16 14:59:29 +020038#include <stdlib.h>
39#include <stdbool.h>
40
41#define foreach_punt_next \
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -070042 _ (PUNT, "error-punt")
43
44typedef enum
45{
46#define _(s,n) PUNT_NEXT_##s,
47 foreach_punt_next
48#undef _
49 PUNT_N_NEXT,
50} punt_next_t;
51
Ole Troanf7a55ad2017-05-16 14:59:29 +020052enum 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
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -070060vlib_node_registration_t udp4_punt_node;
61vlib_node_registration_t udp6_punt_node;
Ole Troanf7a55ad2017-05-16 14:59:29 +020062vlib_node_registration_t udp4_punt_socket_node;
63vlib_node_registration_t udp6_punt_socket_node;
64static vlib_node_registration_t punt_socket_rx_node;
65
66punt_main_t punt_main;
67
68char *
69vnet_punt_get_server_pathname (void)
70{
71 punt_main_t *pm = &punt_main;
72 return pm->sun_path;
73}
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -070074
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*/
85always_inline uword
86udp46_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
131static 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*/
158static uword
159udp4_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*/
186static uword
187udp6_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* */
194VLIB_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
Dave Barachd7cb1b52016-12-09 09:52:16 -0500211VLIB_NODE_FUNCTION_MULTIARCH (udp4_punt_node, udp4_punt);
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700212
213VLIB_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};
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700229
Dave Barachd7cb1b52016-12-09 09:52:16 -0500230VLIB_NODE_FUNCTION_MULTIARCH (udp6_punt_node, udp6_punt);;
231
Ole Troanf7a55ad2017-05-16 14:59:29 +0200232/* *INDENT-ON* */
233
234static struct sockaddr_un *
235punt_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
248static void
249punt_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 */
266static void
267punt_socket_unregister (bool is_ip4, u8 protocol, u16 port)
268{
269 return;
270}
271
272always_inline uword
273udp46_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
371error:
372 vlib_buffer_free_no_next (vm, buffers, n_packets);
373
374 return n_packets;
375}
376
377static uword
378udp4_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
384static uword
385udp6_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* */
393VLIB_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};
402VLIB_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
412typedef struct
413{
414 enum punt_action_e action;
415 u32 sw_if_index;
416} punt_trace_t;
417
418static u8 *
419format_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
430static uword
431punt_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
519error:
520 vlib_node_increment_counter (vm, punt_socket_rx_node.index, error, 1);
521 return 0;
522}
523
524static uword
525punt_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
540VLIB_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
552static clib_error_t *
553punt_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
565clib_error_t *
566vnet_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
598clib_error_t *
599vnet_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
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700612/**
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 */
629clib_error_t *
630vnet_punt_add_del (vlib_main_t * vm, u8 ipv, u8 protocol, u16 port,
Ole Troanf7a55ad2017-05-16 14:59:29 +0200631 bool is_add)
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700632{
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800633 /* 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);
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700638
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800639 if (ipv != (u8) ~ 0 && ipv != 4 && ipv != 6)
640 return clib_error_return (0, "IP version must be 4 or 6, got %d", ipv);
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700641
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800642 if (port == (u16) ~ 0)
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700643 {
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800644 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)
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700656 udp_register_dst_port (vm, port, udp4_punt_node.index, 1);
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800657
658 if (ipv == 6 || ipv == (u8) ~ 0)
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700659 udp_register_dst_port (vm, port, udp6_punt_node.index, 0);
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800660
661 return 0;
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700662 }
663 else
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800664 return clib_error_return (0, "punt delete is not supported yet");
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700665}
666
667static clib_error_t *
668udp_punt_cli (vlib_main_t * vm,
669 unformat_input_t * input, vlib_cli_command_t * cmd)
670{
671 u32 udp_port;
Ole Troanf7a55ad2017-05-16 14:59:29 +0200672 bool is_add = true;
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700673 clib_error_t *error;
674
675 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
676 {
677 if (unformat (input, "del"))
Ole Troanf7a55ad2017-05-16 14:59:29 +0200678 is_add = false;
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800679 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 }
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700686 else if (unformat (input, "%d", &udp_port))
687 {
688 /* punt both IPv6 and IPv4 when used in CLI */
Ole Troanf7a55ad2017-05-16 14:59:29 +0200689 error =
690 vnet_punt_add_del (vm, ~0, IP_PROTOCOL_UDP, udp_port, is_add);
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700691 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
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700705 * - 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 *
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800712 * 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}
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700717 * @endparblock
718?*/
719/* *INDENT-OFF* */
720VLIB_CLI_COMMAND (punt_udp_command, static) = {
721 .path = "set punt udp",
Alexander Popovsky (apopovsk)740bcdb2016-11-15 15:36:23 -0800722 .short_help = "set punt udp [del] <all | port-num1 [port-num2 ...]>",
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700723 .function = udp_punt_cli,
724};
725/* *INDENT-ON* */
726
Ole Troanf7a55ad2017-05-16 14:59:29 +0200727clib_error_t *
728punt_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
746VLIB_INIT_FUNCTION (punt_init);
747
748static clib_error_t *
749punt_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
804VLIB_CONFIG_FUNCTION (punt_config, "punt");
805
Alexander Popovsky (apopovsk)4a7e58b2016-10-05 22:31:23 -0700806/*
807 * fd.io coding-style-patch-verification: ON
808 *
809 * Local Variables:
810 * eval: (c-set-style "gnu")
811 * End:
812 */