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