Line data Source code
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/udp/udp.h>
28 : #include <vnet/tcp/tcp.h>
29 : #include <vnet/ip/punt.h>
30 : #include <vlib/unix/unix.h>
31 :
32 : #include <stdio.h>
33 : #include <unistd.h>
34 : #include <sys/socket.h>
35 : #include <sys/uio.h>
36 : #include <stdlib.h>
37 :
38 : punt_main_t punt_main;
39 :
40 : char *
41 70 : vnet_punt_get_server_pathname (void)
42 : {
43 70 : punt_main_t *pm = &punt_main;
44 70 : return pm->sun_path;
45 : }
46 :
47 : static void
48 26 : punt_client_l4_db_add (ip_address_family_t af, u16 port, u32 index)
49 : {
50 26 : punt_main_t *pm = &punt_main;
51 :
52 26 : pm->db.clients_by_l4_port = hash_set (pm->db.clients_by_l4_port,
53 : punt_client_l4_mk_key (af, port),
54 : index);
55 26 : }
56 :
57 : static u32
58 27 : punt_client_l4_db_remove (ip_address_family_t af, u16 port)
59 : {
60 27 : punt_main_t *pm = &punt_main;
61 27 : u32 key, index = ~0;
62 : uword *p;
63 :
64 27 : key = punt_client_l4_mk_key (af, port);
65 27 : p = hash_get (pm->db.clients_by_l4_port, key);
66 :
67 27 : if (p)
68 26 : index = p[0];
69 :
70 27 : hash_unset (pm->db.clients_by_l4_port, key);
71 :
72 27 : return (index);
73 : }
74 :
75 : static void
76 4 : punt_client_ip_proto_db_add (ip_address_family_t af,
77 : ip_protocol_t proto, u32 index)
78 : {
79 4 : punt_main_t *pm = &punt_main;
80 :
81 4 : pm->db.clients_by_ip_proto = hash_set (pm->db.clients_by_ip_proto,
82 : punt_client_ip_proto_mk_key (af,
83 : proto),
84 : index);
85 4 : }
86 :
87 : static u32
88 4 : punt_client_ip_proto_db_remove (ip_address_family_t af, ip_protocol_t proto)
89 : {
90 4 : punt_main_t *pm = &punt_main;
91 4 : u32 key, index = ~0;
92 : uword *p;
93 :
94 4 : key = punt_client_ip_proto_mk_key (af, proto);
95 4 : p = hash_get (pm->db.clients_by_ip_proto, key);
96 :
97 4 : if (p)
98 4 : index = p[0];
99 :
100 4 : hash_unset (pm->db.clients_by_ip_proto, key);
101 :
102 4 : return (index);
103 : }
104 :
105 : static void
106 5 : punt_client_exception_db_add (vlib_punt_reason_t reason, u32 pci)
107 : {
108 5 : punt_main_t *pm = &punt_main;
109 :
110 9 : vec_validate_init_empty (pm->db.clients_by_exception, reason, ~0);
111 :
112 5 : pm->db.clients_by_exception[reason] = pci;
113 5 : }
114 :
115 : static u32
116 5 : punt_client_exception_db_remove (vlib_punt_reason_t reason)
117 : {
118 5 : punt_main_t *pm = &punt_main;
119 5 : u32 pci = ~0;
120 :
121 5 : if (punt_client_exception_get (reason))
122 : {
123 5 : pci = pm->db.clients_by_exception[reason];
124 5 : pm->db.clients_by_exception[reason] = ~0;
125 : }
126 :
127 5 : return pci;
128 : }
129 :
130 : static clib_error_t *
131 0 : punt_socket_read_ready (clib_file_t * uf)
132 : {
133 0 : vlib_main_t *vm = vlib_get_main ();
134 0 : punt_main_t *pm = &punt_main;
135 :
136 : /** Schedule the rx node */
137 0 : vlib_node_set_interrupt_pending (vm, punt_socket_rx_node.index);
138 0 : vec_add1 (pm->ready_fds, uf->file_descriptor);
139 :
140 0 : return 0;
141 : }
142 :
143 : static clib_error_t *
144 26 : punt_socket_register_l4 (vlib_main_t * vm,
145 : ip_address_family_t af,
146 : u8 protocol, u16 port, char *client_pathname)
147 : {
148 26 : punt_main_t *pm = &punt_main;
149 : punt_client_t *c;
150 :
151 26 : if (port == (u16) ~ 0)
152 0 : return clib_error_return (0, "Port number required");
153 :
154 : u32 node_index;
155 26 : switch (protocol)
156 : {
157 26 : case IP_PROTOCOL_UDP:
158 26 : node_index = (af == AF_IP4 ? udp4_punt_socket_node.index :
159 : udp6_punt_socket_node.index);
160 26 : udp_register_dst_port (vm, port, node_index, af == AF_IP4);
161 26 : break;
162 0 : case IP_PROTOCOL_ICMP6:
163 0 : if (af != AF_IP6)
164 0 : return clib_error_return (
165 : 0, "only UDP or ICMP6 protocol (%d, %d) is supported, got %d",
166 : IP_PROTOCOL_UDP, IP_PROTOCOL_ICMP6, protocol);
167 :
168 0 : node_index = icmp6_punt_socket_node.index;
169 0 : icmp6_register_type (vm, port, node_index);
170 0 : break;
171 0 : default:
172 0 : return clib_error_return (
173 : 0, "only UDP or ICMP6 protocol (%d) is supported, got %d",
174 : IP_PROTOCOL_UDP, protocol);
175 : }
176 :
177 26 : c = punt_client_l4_get (af, port);
178 :
179 26 : if (NULL == c)
180 : {
181 26 : pool_get_zero (pm->punt_client_pool, c);
182 26 : punt_client_l4_db_add (af, port, c - pm->punt_client_pool);
183 : }
184 :
185 26 : snprintf (c->caddr.sun_path, sizeof (c->caddr.sun_path), "%s",
186 : client_pathname);
187 26 : c->caddr.sun_family = AF_UNIX;
188 26 : c->reg.type = PUNT_TYPE_L4;
189 26 : c->reg.punt.l4.port = port;
190 26 : c->reg.punt.l4.protocol = protocol;
191 26 : c->reg.punt.l4.af = af;
192 :
193 26 : return (NULL);
194 : }
195 :
196 : static clib_error_t *
197 4 : punt_socket_register_ip_proto (vlib_main_t * vm,
198 : ip_address_family_t af,
199 : ip_protocol_t proto, char *client_pathname)
200 : {
201 4 : punt_main_t *pm = &punt_main;
202 : punt_client_t *c;
203 :
204 4 : c = punt_client_ip_proto_get (af, proto);
205 :
206 4 : if (NULL == c)
207 : {
208 4 : pool_get_zero (pm->punt_client_pool, c);
209 4 : punt_client_ip_proto_db_add (af, proto, c - pm->punt_client_pool);
210 : }
211 :
212 4 : snprintf (c->caddr.sun_path, sizeof (c->caddr.sun_path), "%s",
213 : client_pathname);
214 4 : c->caddr.sun_family = AF_UNIX;
215 4 : c->reg.type = PUNT_TYPE_IP_PROTO;
216 4 : c->reg.punt.ip_proto.protocol = proto;
217 4 : c->reg.punt.ip_proto.af = af;
218 :
219 4 : if (af == AF_IP4)
220 4 : ip4_register_protocol (proto, ip4_proto_punt_socket_node.index);
221 : else
222 0 : ip6_register_protocol (proto, ip6_proto_punt_socket_node.index);
223 :
224 4 : return (NULL);
225 : }
226 :
227 : static clib_error_t *
228 5 : punt_socket_register_exception (vlib_main_t * vm,
229 : vlib_punt_reason_t reason,
230 : char *client_pathname)
231 : {
232 5 : punt_main_t *pm = &punt_main;
233 : punt_client_t *pc;
234 :
235 5 : pc = punt_client_exception_get (reason);
236 :
237 5 : if (NULL == pc)
238 : {
239 5 : pool_get_zero (pm->punt_client_pool, pc);
240 5 : punt_client_exception_db_add (reason, pc - pm->punt_client_pool);
241 : }
242 :
243 5 : snprintf (pc->caddr.sun_path, sizeof (pc->caddr.sun_path), "%s",
244 : client_pathname);
245 5 : pc->caddr.sun_family = AF_UNIX;
246 5 : pc->reg.type = PUNT_TYPE_EXCEPTION;
247 5 : pc->reg.punt.exception.reason = reason;
248 :
249 5 : vlib_punt_register (pm->hdl,
250 5 : pc->reg.punt.exception.reason, "exception-punt-socket");
251 :
252 5 : return (NULL);
253 : }
254 :
255 : static clib_error_t *
256 27 : punt_socket_unregister_l4 (ip_address_family_t af,
257 : ip_protocol_t protocol, u16 port)
258 : {
259 : u32 pci;
260 :
261 27 : udp_unregister_dst_port (vlib_get_main (), port, af == AF_IP4);
262 :
263 27 : pci = punt_client_l4_db_remove (af, port);
264 :
265 27 : if (~0 != pci)
266 26 : pool_put_index (punt_main.punt_client_pool, pci);
267 :
268 27 : return (NULL);
269 : }
270 :
271 : static clib_error_t *
272 4 : punt_socket_unregister_ip_proto (ip_address_family_t af, ip_protocol_t proto)
273 : {
274 : u32 pci;
275 :
276 4 : if (af == AF_IP4)
277 4 : ip4_unregister_protocol (proto);
278 : else
279 0 : ip6_unregister_protocol (proto);
280 :
281 4 : pci = punt_client_ip_proto_db_remove (af, proto);
282 :
283 4 : if (~0 != pci)
284 4 : pool_put_index (punt_main.punt_client_pool, pci);
285 :
286 4 : return (NULL);
287 : }
288 :
289 : static clib_error_t *
290 5 : punt_socket_unregister_exception (vlib_punt_reason_t reason)
291 : {
292 : u32 pci;
293 :
294 5 : pci = punt_client_exception_db_remove (reason);
295 :
296 5 : if (~0 != pci)
297 5 : pool_put_index (punt_main.punt_client_pool, pci);
298 :
299 5 : return (NULL);
300 : }
301 :
302 : clib_error_t *
303 35 : vnet_punt_socket_add (vlib_main_t * vm, u32 header_version,
304 : const punt_reg_t * pr, char *client_pathname)
305 : {
306 35 : punt_main_t *pm = &punt_main;
307 :
308 35 : if (!pm->is_configured)
309 0 : return clib_error_return (0, "socket is not configured");
310 :
311 35 : if (header_version != PUNT_PACKETDESC_VERSION)
312 0 : return clib_error_return (0, "Invalid packet descriptor version");
313 :
314 35 : if (strncmp (client_pathname, vnet_punt_get_server_pathname (),
315 : UNIX_PATH_MAX) == 0)
316 0 : return clib_error_return (0,
317 : "Punt socket: Invalid client path: %s",
318 : client_pathname);
319 :
320 : /* Register client */
321 35 : switch (pr->type)
322 : {
323 26 : case PUNT_TYPE_L4:
324 26 : return (punt_socket_register_l4 (vm,
325 26 : pr->punt.l4.af,
326 26 : pr->punt.l4.protocol,
327 26 : pr->punt.l4.port, client_pathname));
328 4 : case PUNT_TYPE_IP_PROTO:
329 4 : return (punt_socket_register_ip_proto (vm,
330 4 : pr->punt.ip_proto.af,
331 4 : pr->punt.ip_proto.protocol,
332 : client_pathname));
333 5 : case PUNT_TYPE_EXCEPTION:
334 5 : return (punt_socket_register_exception (vm,
335 : pr->punt.exception.reason,
336 : client_pathname));
337 : }
338 :
339 0 : return 0;
340 : }
341 :
342 : clib_error_t *
343 36 : vnet_punt_socket_del (vlib_main_t * vm, const punt_reg_t * pr)
344 : {
345 36 : punt_main_t *pm = &punt_main;
346 :
347 36 : if (!pm->is_configured)
348 0 : return clib_error_return (0, "socket is not configured");
349 :
350 36 : switch (pr->type)
351 : {
352 27 : case PUNT_TYPE_L4:
353 27 : return (punt_socket_unregister_l4 (pr->punt.l4.af,
354 27 : pr->punt.l4.protocol,
355 27 : pr->punt.l4.port));
356 4 : case PUNT_TYPE_IP_PROTO:
357 4 : return (punt_socket_unregister_ip_proto (pr->punt.ip_proto.af,
358 4 : pr->punt.ip_proto.protocol));
359 5 : case PUNT_TYPE_EXCEPTION:
360 5 : return (punt_socket_unregister_exception (pr->punt.exception.reason));
361 : }
362 :
363 0 : return 0;
364 : }
365 :
366 : /**
367 : * @brief Request IP L4 traffic punt to the local TCP/IP stack.
368 : *
369 : * @em Note
370 : * - UDP is the only protocol supported in the current implementation
371 : *
372 : * @param vm vlib_main_t corresponding to the current thread
373 : * @param af IP address family.
374 : * @param protocol 8-bits L4 protocol value
375 : * UDP is 17
376 : * TCP is 1
377 : * @param port 16-bits L4 (TCP/IP) port number when applicable (UDP only)
378 : *
379 : * @returns 0 on success, non-zero value otherwise
380 : */
381 : static clib_error_t *
382 22 : punt_l4_add_del (vlib_main_t * vm,
383 : ip_address_family_t af,
384 : ip_protocol_t protocol, u16 port, bool is_add)
385 : {
386 22 : int is_ip4 = af == AF_IP4;
387 :
388 : /* For now we only support TCP and UDP punt */
389 22 : if (protocol != IP_PROTOCOL_UDP && protocol != IP_PROTOCOL_TCP)
390 0 : return clib_error_return (0,
391 : "only UDP (%d) and TCP (%d) protocols are supported, got %d",
392 : IP_PROTOCOL_UDP, IP_PROTOCOL_TCP, protocol);
393 :
394 22 : if (port == (u16) ~ 0)
395 : {
396 8 : if (protocol == IP_PROTOCOL_UDP)
397 4 : udp_punt_unknown (vm, is_ip4, is_add);
398 4 : else if (protocol == IP_PROTOCOL_TCP)
399 4 : tcp_punt_unknown (vm, is_ip4, is_add);
400 :
401 8 : return 0;
402 : }
403 :
404 14 : else if (is_add)
405 : {
406 14 : const vlib_node_registration_t *punt_node =
407 : is_ip4 ? &udp4_punt_node : &udp6_punt_node;
408 :
409 14 : if (protocol == IP_PROTOCOL_TCP)
410 0 : return clib_error_return (0, "punt TCP ports is not supported yet");
411 :
412 14 : udp_register_dst_port (vm, port, punt_node->index, is_ip4);
413 :
414 14 : return 0;
415 : }
416 : else
417 : {
418 0 : if (protocol == IP_PROTOCOL_TCP)
419 0 : return clib_error_return (0, "punt TCP ports is not supported yet");
420 :
421 0 : udp_unregister_dst_port (vm, port, is_ip4);
422 :
423 0 : return 0;
424 : }
425 : }
426 :
427 : /**
428 : * @brief Request exception traffic punt.
429 : *
430 : * @param reason Punting reason
431 : *
432 : * @returns 0 on success, non-zero value otherwise
433 : */
434 : static clib_error_t *
435 0 : punt_exception_add_del (vlib_punt_reason_t reason, bool is_add)
436 : {
437 0 : punt_main_t *pm = &punt_main;
438 0 : int rv = 0;
439 0 : vnet_punt_reason_flag_t flag = vlib_punt_reason_get_flags (reason);
440 0 : const char *node_name =
441 0 : vnet_punt_reason_flag_is_IP6_PACKET (flag) ? "ip6-punt" : "ip4-punt";
442 0 : if (is_add)
443 0 : rv = vlib_punt_register (pm->hdl, reason, node_name);
444 : else
445 0 : rv = vlib_punt_unregister (pm->hdl, reason, node_name);
446 0 : if (!rv)
447 0 : return 0;
448 : else
449 0 : return clib_error_return (0, is_add ? "Existing punting registration..." :
450 : "Punting registration not found...");
451 : }
452 :
453 : clib_error_t *
454 22 : vnet_punt_add_del (vlib_main_t * vm, const punt_reg_t * pr, bool is_add)
455 : {
456 22 : switch (pr->type)
457 : {
458 22 : case PUNT_TYPE_L4:
459 22 : return (punt_l4_add_del (vm, pr->punt.l4.af, pr->punt.l4.protocol,
460 22 : pr->punt.l4.port, is_add));
461 0 : case PUNT_TYPE_EXCEPTION:
462 0 : return punt_exception_add_del (pr->punt.exception.reason, is_add);
463 0 : case PUNT_TYPE_IP_PROTO:
464 0 : break;
465 : }
466 :
467 0 : return (clib_error_return (0, "Unsupported punt type: %d", pr->type));
468 : }
469 :
470 : static clib_error_t *
471 0 : punt_cli (vlib_main_t * vm,
472 : unformat_input_t * input__, vlib_cli_command_t * cmd)
473 : {
474 0 : unformat_input_t line_input, *input = &line_input;
475 0 : clib_error_t *error = NULL;
476 0 : bool is_add = true;
477 : /* *INDENT-OFF* */
478 0 : punt_reg_t pr = {
479 : .punt = {
480 : .l4 = {
481 : .af = AF_IP4,
482 : .port = ~0,
483 : .protocol = IP_PROTOCOL_UDP,
484 : },
485 : },
486 : .type = PUNT_TYPE_L4,
487 : };
488 : u32 port;
489 : /* *INDENT-ON* */
490 :
491 0 : if (!unformat_user (input__, unformat_line_input, input))
492 0 : return 0;
493 :
494 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
495 : {
496 0 : if (unformat (input, "del"))
497 0 : is_add = false;
498 0 : else if (unformat (input, "reason %U", unformat_punt_reason,
499 : &pr.punt.exception.reason))
500 0 : pr.type = PUNT_TYPE_EXCEPTION;
501 0 : else if (unformat (input, "ipv4"))
502 0 : pr.punt.l4.af = AF_IP4;
503 0 : else if (unformat (input, "ipv6"))
504 0 : pr.punt.l4.af = AF_IP6;
505 0 : else if (unformat (input, "ip6"))
506 0 : pr.punt.l4.af = AF_IP6;
507 0 : else if (unformat (input, "%d", &port))
508 0 : pr.punt.l4.port = port;
509 0 : else if (unformat (input, "all"))
510 0 : pr.punt.l4.port = ~0;
511 0 : else if (unformat (input, "udp"))
512 0 : pr.punt.l4.protocol = IP_PROTOCOL_UDP;
513 0 : else if (unformat (input, "tcp"))
514 0 : pr.punt.l4.protocol = IP_PROTOCOL_TCP;
515 : else
516 : {
517 0 : error = clib_error_return (0, "parse error: '%U'",
518 : format_unformat_error, input);
519 0 : goto done;
520 : }
521 : }
522 :
523 : /* punt both IPv6 and IPv4 when used in CLI */
524 0 : error = vnet_punt_add_del (vm, &pr, is_add);
525 0 : if (error)
526 : {
527 0 : clib_error_report (error);
528 : }
529 :
530 0 : done:
531 0 : unformat_free (input);
532 0 : return error;
533 : }
534 :
535 : /*?
536 : * The set of '<em>set punt</em>' commands allows specific IP traffic to
537 : * be punted to the host TCP/IP stack
538 : *
539 : * @em Note
540 : * - UDP is the only protocol supported in the current implementation
541 : * - All TCP traffic is currently punted to the host by default
542 : *
543 : * @cliexpar
544 : * @parblock
545 : * Example of how to request NTP traffic to be punted
546 : * @cliexcmd{set punt udp 125}
547 : *
548 : * Example of how to request all 'unknown' UDP traffic to be punted
549 : * @cliexcmd{set punt udp all}
550 : *
551 : * Example of how to stop all 'unknown' UDP traffic to be punted
552 : * @cliexcmd{set punt udp del all}
553 : * @endparblock
554 : ?*/
555 : /* *INDENT-OFF* */
556 285289 : VLIB_CLI_COMMAND (punt_command, static) = {
557 : .path = "set punt",
558 : .short_help = "set punt [IPV4|ip6|ipv6] [UDP|tcp] [del] [ALL|<port-num>]",
559 : .function = punt_cli,
560 : };
561 : /* *INDENT-ON* */
562 :
563 : static clib_error_t *
564 0 : punt_socket_register_cmd (vlib_main_t * vm,
565 : unformat_input_t * input__,
566 : vlib_cli_command_t * cmd)
567 : {
568 0 : unformat_input_t line_input, *input = &line_input;
569 0 : u8 *socket_name = 0;
570 0 : clib_error_t *error = NULL;
571 : /* *INDENT-OFF* */
572 0 : punt_reg_t pr = {
573 : .punt = {
574 : .l4 = {
575 : .af = AF_IP4,
576 : .port = ~0,
577 : .protocol = IP_PROTOCOL_UDP,
578 : },
579 : },
580 : .type = PUNT_TYPE_L4,
581 : };
582 : /* *INDENT-ON* */
583 :
584 0 : if (!unformat_user (input__, unformat_line_input, input))
585 0 : return 0;
586 :
587 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
588 : {
589 0 : if (unformat (input, "ipv4"))
590 0 : pr.punt.l4.af = AF_IP4;
591 0 : else if (unformat (input, "ipv6"))
592 0 : pr.punt.l4.af = AF_IP6;
593 0 : else if (unformat (input, "udp"))
594 0 : pr.punt.l4.protocol = IP_PROTOCOL_UDP;
595 0 : else if (unformat (input, "tcp"))
596 0 : pr.punt.l4.protocol = IP_PROTOCOL_TCP;
597 0 : else if (unformat (input, "%d", &pr.punt.l4.port))
598 : ;
599 0 : else if (unformat (input, "all"))
600 0 : pr.punt.l4.port = ~0;
601 0 : else if (unformat (input, "socket %s", &socket_name))
602 : ;
603 0 : else if (unformat (input, "reason %U", unformat_punt_reason,
604 : &pr.punt.exception.reason))
605 0 : pr.type = PUNT_TYPE_EXCEPTION;
606 : else
607 : {
608 0 : error = clib_error_return (0, "parse error: '%U'",
609 : format_unformat_error, input);
610 0 : goto done;
611 : }
612 : }
613 :
614 0 : if (!socket_name)
615 0 : error = clib_error_return (0, "socket name not specified");
616 : else
617 0 : error = vnet_punt_socket_add (vm, 1, &pr, (char *) socket_name);
618 :
619 0 : done:
620 0 : unformat_free (input);
621 0 : return error;
622 : }
623 :
624 : /*?
625 : *
626 : * @cliexpar
627 : * @cliexcmd{punt socket register socket punt_l4_foo.sock}
628 :
629 : ?*/
630 : /* *INDENT-OFF* */
631 285289 : VLIB_CLI_COMMAND (punt_socket_register_command, static) =
632 : {
633 : .path = "punt socket register",
634 : .function = punt_socket_register_cmd,
635 : .short_help = "punt socket register [IPV4|ipv6] [UDP|tcp] [ALL|<port-num>] socket <socket>",
636 : .is_mp_safe = 1,
637 : };
638 : /* *INDENT-ON* */
639 :
640 : static clib_error_t *
641 0 : punt_socket_deregister_cmd (vlib_main_t * vm,
642 : unformat_input_t * input__,
643 : vlib_cli_command_t * cmd)
644 : {
645 0 : unformat_input_t line_input, *input = &line_input;
646 0 : clib_error_t *error = NULL;
647 : /* *INDENT-OFF* */
648 0 : punt_reg_t pr = {
649 : .punt = {
650 : .l4 = {
651 : .af = AF_IP4,
652 : .port = ~0,
653 : .protocol = IP_PROTOCOL_UDP,
654 : },
655 : },
656 : .type = PUNT_TYPE_L4,
657 : };
658 : /* *INDENT-ON* */
659 :
660 0 : if (!unformat_user (input__, unformat_line_input, input))
661 0 : return 0;
662 :
663 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
664 : {
665 0 : if (unformat (input, "ipv4"))
666 0 : pr.punt.l4.af = AF_IP4;
667 0 : else if (unformat (input, "ipv6"))
668 0 : pr.punt.l4.af = AF_IP6;
669 0 : else if (unformat (input, "udp"))
670 0 : pr.punt.l4.protocol = IP_PROTOCOL_UDP;
671 0 : else if (unformat (input, "tcp"))
672 0 : pr.punt.l4.protocol = IP_PROTOCOL_TCP;
673 0 : else if (unformat (input, "%d", &pr.punt.l4.port))
674 : ;
675 0 : else if (unformat (input, "all"))
676 0 : pr.punt.l4.port = ~0;
677 0 : else if (unformat (input, "reason %U", unformat_punt_reason,
678 : &pr.punt.exception.reason))
679 0 : pr.type = PUNT_TYPE_EXCEPTION;
680 : else
681 : {
682 0 : error = clib_error_return (0, "parse error: '%U'",
683 : format_unformat_error, input);
684 0 : goto done;
685 : }
686 : }
687 :
688 0 : error = vnet_punt_socket_del (vm, &pr);
689 0 : done:
690 0 : unformat_free (input);
691 0 : return error;
692 : }
693 :
694 : /*?
695 : *
696 : * @cliexpar
697 : * @cliexcmd{punt socket register}
698 : ?*/
699 : /* *INDENT-OFF* */
700 285289 : VLIB_CLI_COMMAND (punt_socket_deregister_command, static) =
701 : {
702 : .path = "punt socket deregister",
703 : .function = punt_socket_deregister_cmd,
704 : .short_help = "punt socket deregister [IPV4|ipv6] [UDP|tcp] [ALL|<port-num>]",
705 : .is_mp_safe = 1,
706 : };
707 : /* *INDENT-ON* */
708 :
709 : void
710 36 : punt_client_walk (punt_type_t pt, punt_client_walk_cb_t cb, void *ctx)
711 : {
712 36 : punt_main_t *pm = &punt_main;
713 :
714 36 : switch (pt)
715 : {
716 23 : case PUNT_TYPE_L4:
717 : {
718 : u32 pci, key;
719 :
720 : /* *INDENT-OFF* */
721 1389 : hash_foreach(key, pci, pm->db.clients_by_l4_port,
722 : ({
723 : cb (pool_elt_at_index(pm->punt_client_pool, pci), ctx);
724 : }));
725 : /* *INDENT-ON* */
726 23 : break;
727 : }
728 6 : case PUNT_TYPE_IP_PROTO:
729 : {
730 : u32 pci, key;
731 :
732 : /* *INDENT-OFF* */
733 333 : hash_foreach(key, pci, pm->db.clients_by_ip_proto,
734 : ({
735 : cb (pool_elt_at_index(pm->punt_client_pool, pci), ctx);
736 : }));
737 : /* *INDENT-ON* */
738 6 : break;
739 : }
740 7 : case PUNT_TYPE_EXCEPTION:
741 : {
742 : u32 *pci;
743 :
744 25 : vec_foreach (pci, pm->db.clients_by_exception)
745 : {
746 18 : if (~0 != *pci)
747 9 : cb (pool_elt_at_index (pm->punt_client_pool, *pci), ctx);
748 : }
749 :
750 7 : break;
751 : }
752 : }
753 36 : }
754 :
755 : static u8 *
756 5 : format_punt_client (u8 * s, va_list * args)
757 : {
758 5 : punt_client_t *pc = va_arg (*args, punt_client_t *);
759 :
760 5 : s = format (s, " punt ");
761 :
762 5 : switch (pc->reg.type)
763 : {
764 0 : case PUNT_TYPE_L4:
765 0 : s = format (s, "%U %U port %d",
766 0 : format_ip_address_family, pc->reg.punt.l4.af,
767 0 : format_ip_protocol, pc->reg.punt.l4.protocol,
768 0 : pc->reg.punt.l4.port);
769 0 : break;
770 2 : case PUNT_TYPE_IP_PROTO:
771 2 : s = format (s, "%U %U",
772 2 : format_ip_address_family, pc->reg.punt.ip_proto.af,
773 2 : format_ip_protocol, pc->reg.punt.ip_proto.protocol);
774 2 : break;
775 3 : case PUNT_TYPE_EXCEPTION:
776 3 : s = format (s, " %U", format_vlib_punt_reason,
777 3 : pc->reg.punt.exception.reason);
778 3 : break;
779 : }
780 :
781 5 : s = format (s, " to socket %s \n", pc->caddr.sun_path);
782 :
783 5 : return (s);
784 : }
785 :
786 : static walk_rc_t
787 5 : punt_client_show_one (const punt_client_t * pc, void *ctx)
788 : {
789 5 : vlib_cli_output (ctx, "%U", format_punt_client, pc);
790 :
791 5 : return (WALK_CONTINUE);
792 : }
793 :
794 : static clib_error_t *
795 4 : punt_socket_show_cmd (vlib_main_t * vm,
796 : unformat_input_t * input__, vlib_cli_command_t * cmd)
797 : {
798 4 : unformat_input_t line_input, *input = &line_input;
799 4 : clib_error_t *error = NULL;
800 : punt_type_t pt;
801 :
802 4 : pt = PUNT_TYPE_L4;
803 :
804 4 : if (!unformat_user (input__, unformat_line_input, input))
805 1 : return 0;
806 :
807 6 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
808 : {
809 3 : if (unformat (input, "exception"))
810 2 : pt = PUNT_TYPE_EXCEPTION;
811 1 : else if (unformat (input, "l4"))
812 0 : pt = PUNT_TYPE_L4;
813 1 : else if (unformat (input, "ip"))
814 1 : pt = PUNT_TYPE_IP_PROTO;
815 : else
816 : {
817 0 : error = clib_error_return (0, "parse error: '%U'",
818 : format_unformat_error, input);
819 0 : goto done;
820 : }
821 : }
822 :
823 3 : punt_client_walk (pt, punt_client_show_one, vm);
824 :
825 3 : done:
826 3 : unformat_free (input);
827 3 : return (error);
828 : }
829 :
830 : /*?
831 : *
832 : * @cliexpar
833 : * @cliexcmd{show punt socket ipv4}
834 : ?*/
835 : /* *INDENT-OFF* */
836 285289 : VLIB_CLI_COMMAND (show_punt_socket_registration_command, static) =
837 : {
838 : .path = "show punt socket registrations",
839 : .function = punt_socket_show_cmd,
840 : .short_help = "show punt socket registrations [l4|exception]",
841 : .is_mp_safe = 1,
842 : };
843 : /* *INDENT-ON* */
844 :
845 : clib_error_t *
846 575 : ip_punt_init (vlib_main_t * vm)
847 : {
848 575 : clib_error_t *error = NULL;
849 575 : punt_main_t *pm = &punt_main;
850 575 : vlib_thread_main_t *tm = vlib_get_thread_main ();
851 :
852 575 : pm->is_configured = false;
853 575 : pm->interface_output_node =
854 575 : vlib_get_node_by_name (vm, (u8 *) "interface-output");
855 :
856 575 : if ((error = vlib_call_init_function (vm, punt_init)))
857 0 : return error;
858 :
859 575 : pm->hdl = vlib_punt_client_register ("ip-punt");
860 :
861 575 : vec_validate_aligned (pm->thread_data, tm->n_vlib_mains,
862 : CLIB_CACHE_LINE_BYTES);
863 :
864 575 : return (error);
865 : }
866 :
867 : u8 *
868 1721 : format_vnet_punt_reason_flags (u8 *s, va_list *args)
869 : {
870 1721 : vnet_punt_reason_flag_t flag = va_arg (*args, int);
871 : #define _(pos, len, value, name, str) \
872 : if (vnet_punt_reason_flag_is_##name (flag)) \
873 : s = format (s, "%s ", str);
874 :
875 1721 : foreach_vnet_punt_reason_flag
876 : #undef _
877 1721 : return (s);
878 : }
879 :
880 47231 : VLIB_INIT_FUNCTION (ip_punt_init);
881 :
882 : static clib_error_t *
883 575 : punt_config (vlib_main_t * vm, unformat_input_t * input)
884 : {
885 575 : punt_main_t *pm = &punt_main;
886 575 : char *socket_path = 0;
887 :
888 580 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
889 : {
890 5 : if (unformat (input, "socket %s", &socket_path))
891 5 : strncpy (pm->sun_path, socket_path, UNIX_PATH_MAX - 1);
892 : else
893 0 : return clib_error_return (0, "unknown input `%U'",
894 : format_unformat_error, input);
895 : }
896 :
897 575 : if (socket_path == 0)
898 570 : return 0;
899 :
900 : /* UNIX domain socket */
901 : struct sockaddr_un addr;
902 5 : if ((pm->socket_fd = socket (AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
903 : {
904 0 : return clib_error_return (0, "socket error");
905 : }
906 :
907 5 : clib_memset (&addr, 0, sizeof (addr));
908 5 : addr.sun_family = AF_UNIX;
909 5 : if (*socket_path == '\0')
910 : {
911 0 : *addr.sun_path = '\0';
912 0 : strncpy (addr.sun_path + 1, socket_path + 1,
913 : sizeof (addr.sun_path) - 2);
914 : }
915 : else
916 : {
917 5 : strncpy (addr.sun_path, socket_path, sizeof (addr.sun_path) - 1);
918 5 : unlink (socket_path);
919 : }
920 :
921 5 : if (bind (pm->socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
922 : {
923 0 : return clib_error_return (0, "bind error");
924 : }
925 :
926 5 : int n_bytes = 0x10000;
927 :
928 5 : if (setsockopt
929 : (pm->socket_fd, SOL_SOCKET, SO_SNDBUF, &n_bytes,
930 : sizeof (n_bytes)) == -1)
931 : {
932 0 : return clib_error_return (0, "setsockopt error");
933 : }
934 :
935 : /* Register socket */
936 5 : clib_file_main_t *fm = &file_main;
937 5 : clib_file_t template = { 0 };
938 5 : template.read_function = punt_socket_read_ready;
939 5 : template.file_descriptor = pm->socket_fd;
940 5 : template.description = format (0, "punt socket %s", socket_path);
941 5 : pm->clib_file_index = clib_file_add (fm, &template);
942 :
943 5 : pm->is_configured = true;
944 :
945 5 : return 0;
946 : }
947 :
948 7514 : VLIB_CONFIG_FUNCTION (punt_config, "punt");
949 :
950 : /*
951 : * fd.io coding-style-patch-verification: ON
952 : *
953 : * Local Variables:
954 : * eval: (c-set-style "gnu")
955 : * End:
956 : */
|