Repair vlib API socket server
[vpp.git] / src / vlibmemory / socksvr_vlib.c
1 /*
2  *------------------------------------------------------------------
3  * socksvr_vlib.c
4  *
5  * Copyright (c) 2009 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <sys/ioctl.h>
24 #include <vppinfra/byte_order.h>
25 #include <svm/memfd.h>
26
27 #include <fcntl.h>
28 #include <sys/stat.h>
29
30 #include <vlibmemory/api.h>
31
32 #include <vlibmemory/vl_memory_msg_enum.h>
33
34 #define vl_typedefs             /* define message structures */
35 #include <vlibmemory/vl_memory_api_h.h>
36 #undef vl_typedefs
37
38 /* instantiate all the print functions we know about */
39 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
40 #define vl_printfun
41 #include <vlibmemory/vl_memory_api_h.h>
42 #undef vl_printfun
43
44 /* instantiate all the endian swap functions we know about */
45 #define vl_endianfun
46 #include <vlibmemory/vl_memory_api_h.h>
47 #undef vl_endianfun
48
49 void
50 dump_socket_clients (vlib_main_t * vm, api_main_t * am)
51 {
52   vl_api_registration_t *reg;
53   socket_main_t *sm = &socket_main;
54   clib_file_main_t *fm = &file_main;
55   clib_file_t *f;
56
57   /*
58    * Must have at least one active client, not counting the
59    * REGISTRATION_TYPE_SOCKET_LISTEN bind/accept socket
60    */
61   if (pool_elts (sm->registration_pool) < 2)
62     return;
63
64   vlib_cli_output (vm, "Socket clients");
65   vlib_cli_output (vm, "%16s %8s", "Name", "Fildesc");
66     /* *INDENT-OFF* */
67     pool_foreach (reg, sm->registration_pool,
68     ({
69         if (reg->registration_type == REGISTRATION_TYPE_SOCKET_SERVER) {
70             f = pool_elt_at_index (fm->file_pool, reg->clib_file_index);
71             vlib_cli_output (vm, "%16s %8d",
72                              reg->name, f->file_descriptor);
73         }
74     }));
75 /* *INDENT-ON* */
76 }
77
78 void
79 vl_socket_api_send (vl_api_registration_t * rp, u8 * elem)
80 {
81   u16 msg_id = ntohs (*(u16 *) elem);
82   api_main_t *am = &api_main;
83   msgbuf_t *mb = (msgbuf_t *) (elem - offsetof (msgbuf_t, data));
84 #if CLIB_DEBUG > 1
85   u32 output_length;
86 #endif
87   clib_file_t *cf = rp->clib_file_index + file_main.file_pool;
88
89   ASSERT (rp->registration_type > REGISTRATION_TYPE_SHMEM);
90
91   if (msg_id >= vec_len (am->api_trace_cfg))
92     {
93       clib_warning ("id out of range: %d", msg_id);
94       vl_msg_api_free ((void *) elem);
95       return;
96     }
97
98   /* Add the msgbuf_t to the output vector */
99   vl_socket_add_pending_output_no_flush (cf,
100                                          rp->vl_api_registration_pool_index +
101                                          socket_main.registration_pool,
102                                          (u8 *) mb, sizeof (*mb));
103   /* Send the message */
104   vl_socket_add_pending_output (cf,
105                                 rp->vl_api_registration_pool_index
106                                 + socket_main.registration_pool,
107                                 elem, ntohl (mb->data_len));
108
109 #if CLIB_DEBUG > 1
110   output_length = sizeof (*mb) + ntohl (mb->data_len);
111   clib_warning ("wrote %u bytes to fd %d", output_length,
112                 cf->file_descriptor);
113 #endif
114
115   vl_msg_api_free ((void *) elem);
116 }
117
118 void
119 vl_free_socket_registration_index (u32 pool_index)
120 {
121   int i;
122   vl_api_registration_t *rp;
123   if (pool_is_free_index (socket_main.registration_pool, pool_index))
124     {
125       clib_warning ("main pool index %d already free", pool_index);
126       return;
127     }
128   rp = pool_elt_at_index (socket_main.registration_pool, pool_index);
129
130   ASSERT (rp->registration_type != REGISTRATION_TYPE_FREE);
131   for (i = 0; i < vec_len (rp->additional_fds_to_close); i++)
132     if (close (rp->additional_fds_to_close[i]) < 0)
133       clib_unix_warning ("close");
134   vec_free (rp->additional_fds_to_close);
135   vec_free (rp->name);
136   vec_free (rp->unprocessed_input);
137   vec_free (rp->output_vector);
138   rp->registration_type = REGISTRATION_TYPE_FREE;
139   pool_put (socket_main.registration_pool, rp);
140 }
141
142 void
143 vl_api_socket_process_msg (clib_file_t * uf, vl_api_registration_t * rp,
144                            i8 * input_v)
145 {
146   msgbuf_t *mbp = (msgbuf_t *) input_v;
147
148   u8 *the_msg = (u8 *) (mbp->data);
149   socket_main.current_uf = uf;
150   socket_main.current_rp = rp;
151   vl_msg_api_socket_handler (the_msg);
152   socket_main.current_uf = 0;
153   socket_main.current_rp = 0;
154 }
155
156 clib_error_t *
157 vl_socket_read_ready (clib_file_t * uf)
158 {
159   clib_file_main_t *fm = &file_main;
160   vlib_main_t *vm = vlib_get_main ();
161   vl_api_registration_t *rp;
162   int n;
163   i8 *msg_buffer = 0;
164   u8 *data_for_process;
165   u32 msg_len;
166   u32 save_input_buffer_length = vec_len (socket_main.input_buffer);
167   vl_socket_args_for_process_t *a;
168   msgbuf_t *mbp;
169   int mbp_set = 0;
170
171   rp = pool_elt_at_index (socket_main.registration_pool, uf->private_data);
172
173   n = read (uf->file_descriptor, socket_main.input_buffer,
174             vec_len (socket_main.input_buffer));
175
176   if (n <= 0 && errno != EAGAIN)
177     {
178       clib_file_del (fm, uf);
179
180       if (!pool_is_free (socket_main.registration_pool, rp))
181         {
182           u32 index = rp - socket_main.registration_pool;
183           vl_free_socket_registration_index (index);
184         }
185       else
186         {
187           clib_warning ("client index %d already free?",
188                         rp->vl_api_registration_pool_index);
189         }
190       return 0;
191     }
192
193   _vec_len (socket_main.input_buffer) = n;
194
195   /*
196    * Look for bugs here. This code is tricky because
197    * data read from a stream socket does not honor message
198    * boundaries. In the case of a long message (>4K bytes)
199    * we have to do (at least) 2 reads, etc.
200    */
201   do
202     {
203       if (vec_len (rp->unprocessed_input))
204         {
205           vec_append (rp->unprocessed_input, socket_main.input_buffer);
206           msg_buffer = rp->unprocessed_input;
207         }
208       else
209         {
210           msg_buffer = socket_main.input_buffer;
211           mbp_set = 0;
212         }
213
214       if (mbp_set == 0)
215         {
216           /* Any chance that we have a complete message? */
217           if (vec_len (msg_buffer) <= sizeof (msgbuf_t))
218             goto save_and_split;
219
220           mbp = (msgbuf_t *) msg_buffer;
221           msg_len = ntohl (mbp->data_len);
222           mbp_set = 1;
223         }
224
225       /* We don't have the entire message yet. */
226       if (mbp_set == 0
227           || (msg_len + sizeof (msgbuf_t)) > vec_len (msg_buffer))
228         {
229         save_and_split:
230           /* if we were using the input buffer save the fragment */
231           if (msg_buffer == socket_main.input_buffer)
232             {
233               ASSERT (vec_len (rp->unprocessed_input) == 0);
234               vec_validate (rp->unprocessed_input, vec_len (msg_buffer) - 1);
235               clib_memcpy (rp->unprocessed_input, msg_buffer,
236                            vec_len (msg_buffer));
237               _vec_len (rp->unprocessed_input) = vec_len (msg_buffer);
238             }
239           _vec_len (socket_main.input_buffer) = save_input_buffer_length;
240           return 0;
241         }
242
243       data_for_process = (u8 *) vec_dup (msg_buffer);
244       _vec_len (data_for_process) = (msg_len + sizeof (msgbuf_t));
245       pool_get (socket_main.process_args, a);
246       a->clib_file = uf;
247       a->regp = rp;
248       a->data = data_for_process;
249
250       vlib_process_signal_event (vm, memclnt_node.index,
251                                  SOCKET_READ_EVENT,
252                                  a - socket_main.process_args);
253       if (n > (msg_len + sizeof (*mbp)))
254         vec_delete (msg_buffer, msg_len + sizeof (*mbp), 0);
255       else
256         _vec_len (msg_buffer) = 0;
257       n -= msg_len + sizeof (msgbuf_t);
258       msg_len = 0;
259       mbp_set = 0;
260     }
261   while (n > 0);
262
263   _vec_len (socket_main.input_buffer) = save_input_buffer_length;
264
265   return 0;
266 }
267
268 void
269 vl_socket_add_pending_output (clib_file_t * uf,
270                               vl_api_registration_t * rp,
271                               u8 * buffer, uword buffer_bytes)
272 {
273   clib_file_main_t *fm = &file_main;
274
275   vec_add (rp->output_vector, buffer, buffer_bytes);
276   if (vec_len (rp->output_vector) > 0)
277     {
278       int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
279       uf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
280       if (!skip_update)
281         fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
282     }
283 }
284
285 void
286 vl_socket_add_pending_output_no_flush (clib_file_t * uf,
287                                        vl_api_registration_t * rp,
288                                        u8 * buffer, uword buffer_bytes)
289 {
290   vec_add (rp->output_vector, buffer, buffer_bytes);
291 }
292
293 static void
294 socket_del_pending_output (clib_file_t * uf,
295                            vl_api_registration_t * rp, uword n_bytes)
296 {
297   clib_file_main_t *fm = &file_main;
298
299   vec_delete (rp->output_vector, n_bytes, 0);
300   if (vec_len (rp->output_vector) <= 0)
301     {
302       int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
303       uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
304       if (!skip_update)
305         fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
306     }
307 }
308
309 clib_error_t *
310 vl_socket_write_ready (clib_file_t * uf)
311 {
312   clib_file_main_t *fm = &file_main;
313   vl_api_registration_t *rp;
314   int n;
315
316   rp = pool_elt_at_index (socket_main.registration_pool, uf->private_data);
317
318   /* Flush output vector. */
319   n = write (uf->file_descriptor,
320              rp->output_vector, vec_len (rp->output_vector));
321
322   if (n < 0)
323     {
324 #if DEBUG > 2
325       clib_warning ("write error, close the file...\n");
326 #endif
327       clib_file_del (fm, uf);
328
329       vl_free_socket_registration_index (rp - socket_main.registration_pool);
330       return 0;
331     }
332
333   else if (n > 0)
334     socket_del_pending_output (uf, rp, n);
335
336   return 0;
337 }
338
339 clib_error_t *
340 vl_socket_error_ready (clib_file_t * uf)
341 {
342   vl_api_registration_t *rp;
343   clib_file_main_t *fm = &file_main;
344
345   rp = pool_elt_at_index (socket_main.registration_pool, uf->private_data);
346   clib_file_del (fm, uf);
347   vl_free_socket_registration_index (rp - socket_main.registration_pool);
348
349   return 0;
350 }
351
352 void
353 socksvr_file_add (clib_file_main_t * fm, int fd)
354 {
355   vl_api_registration_t *rp;
356   clib_file_t template = { 0 };
357
358   pool_get (socket_main.registration_pool, rp);
359   memset (rp, 0, sizeof (*rp));
360
361   template.read_function = vl_socket_read_ready;
362   template.write_function = vl_socket_write_ready;
363   template.error_function = vl_socket_error_ready;
364   template.file_descriptor = fd;
365   template.private_data = rp - socket_main.registration_pool;
366
367   rp->registration_type = REGISTRATION_TYPE_SOCKET_SERVER;
368   rp->vl_api_registration_pool_index = rp - socket_main.registration_pool;
369   rp->clib_file_index = clib_file_add (fm, &template);
370 }
371
372 static clib_error_t *
373 socksvr_accept_ready (clib_file_t * uf)
374 {
375   clib_file_main_t *fm = &file_main;
376   socket_main_t *sm = &socket_main;
377   clib_socket_t *sock = &sm->socksvr_listen_socket;
378   clib_socket_t client;
379   clib_error_t *error;
380
381   error = clib_socket_accept (sock, &client);
382
383   if (error)
384     return error;
385
386   socksvr_file_add (fm, client.fd);
387   return 0;
388 }
389
390 static clib_error_t *
391 socksvr_bogus_write (clib_file_t * uf)
392 {
393   clib_warning ("why am I here?");
394   return 0;
395 }
396
397 /*
398  * vl_api_sockclnt_create_t_handler
399  */
400 void
401 vl_api_sockclnt_create_t_handler (vl_api_sockclnt_create_t * mp)
402 {
403   vl_api_registration_t *regp;
404   vl_api_sockclnt_create_reply_t *rp;
405   int rv = 1;
406
407   regp = socket_main.current_rp;
408
409   ASSERT (regp->registration_type == REGISTRATION_TYPE_SOCKET_SERVER);
410
411   regp->name = format (0, "%s%c", mp->name, 0);
412
413   rp = vl_msg_api_alloc (sizeof (*rp));
414   rp->_vl_msg_id = htons (VL_API_SOCKCLNT_CREATE_REPLY);
415   rp->handle = (uword) regp;
416   rp->index = (uword) regp->vl_api_registration_pool_index;
417   rp->context = mp->context;
418   rp->response = htonl (rv);
419
420   vl_msg_api_send (regp, (u8 *) rp);
421 }
422
423 /*
424  * vl_api_sockclnt_delete_t_handler
425  */
426 void
427 vl_api_sockclnt_delete_t_handler (vl_api_sockclnt_delete_t * mp)
428 {
429   vl_api_registration_t *regp;
430   vl_api_sockclnt_delete_reply_t *rp;
431
432   if (!pool_is_free_index (socket_main.registration_pool, mp->index))
433     {
434       regp = pool_elt_at_index (socket_main.registration_pool, mp->index);
435
436       rp = vl_msg_api_alloc (sizeof (*rp));
437       rp->_vl_msg_id = htons (VL_API_SOCKCLNT_DELETE_REPLY);
438       rp->handle = mp->handle;
439       rp->response = htonl (1);
440
441       vl_msg_api_send (regp, (u8 *) rp);
442
443       clib_file_del (&file_main, file_main.file_pool + regp->clib_file_index);
444
445       vl_free_socket_registration_index (mp->index);
446     }
447   else
448     {
449       clib_warning ("unknown client ID %d", mp->index);
450     }
451 }
452
453 static clib_error_t *
454 send_fd_msg (int socket_fd, int fd_to_share)
455 {
456   struct msghdr mh = { 0 };
457   struct iovec iov[1];
458   char ctl[CMSG_SPACE (sizeof (int))];
459   char *msg = "memfd";
460   int rv;
461
462   iov[0].iov_base = msg;
463   iov[0].iov_len = strlen (msg);
464   mh.msg_iov = iov;
465   mh.msg_iovlen = 1;
466
467   struct cmsghdr *cmsg;
468   memset (&ctl, 0, sizeof (ctl));
469   mh.msg_control = ctl;
470   mh.msg_controllen = sizeof (ctl);
471   cmsg = CMSG_FIRSTHDR (&mh);
472   cmsg->cmsg_len = CMSG_LEN (sizeof (int));
473   cmsg->cmsg_level = SOL_SOCKET;
474   cmsg->cmsg_type = SCM_RIGHTS;
475   memcpy (CMSG_DATA (cmsg), &fd_to_share, sizeof (int));
476
477   rv = sendmsg (socket_fd, &mh, 0);
478   if (rv < 0)
479     return clib_error_return_unix (0, "sendmsg");
480   return 0;
481 }
482
483 /*
484  * Create a memory-fd segment.
485  */
486 void
487 vl_api_memfd_segment_create_t_handler (vl_api_memfd_segment_create_t * mp)
488 {
489   vl_api_memfd_segment_create_reply_t *rmp;
490   api_main_t *am = &api_main;
491   clib_file_t *cf;
492   memfd_private_t _memfd_private, *memfd = &_memfd_private;
493   vl_api_registration_t *regp;
494   vlib_main_t *vm = vlib_get_main ();
495   svm_map_region_args_t _args, *a = &_args;
496   svm_region_t *vlib_rp;
497   int rv;
498
499   regp = vl_api_client_index_to_registration (mp->client_index);
500
501   if (regp == 0)
502     {
503       clib_warning ("API client disconnected");
504       return;
505     }
506
507   if (regp->registration_type != REGISTRATION_TYPE_SOCKET_SERVER)
508     {
509       rv = -31;                 /* VNET_API_ERROR_INVALID_REGISTRATION */
510       goto reply;
511     }
512
513   memset (memfd, 0, sizeof (*memfd));
514
515   /* Embed in api_main_t */
516   memfd->memfd_size = mp->requested_size;
517   memfd->requested_va = 0ULL;
518   memfd->i_am_master = 1;
519   memfd->name = format (0, "%s%c", regp->name, 0);
520
521   /* Set up a memfd segment of the requested size */
522   rv = memfd_master_init (memfd, mp->client_index);
523
524   if (rv)
525     goto reply;
526
527   /* Remember to close this fd when the socket connection goes away */
528   vec_add1 (regp->additional_fds_to_close, memfd->fd);
529
530   /* And create a plausible svm_region in it */
531   memset (a, 0, sizeof (*a));
532   a->baseva = memfd->sh->memfd_va + MMAP_PAGESIZE;
533   a->size = memfd->memfd_size - MMAP_PAGESIZE;
534   /* $$$$ might want a different config parameter */
535   a->pvt_heap_size = am->api_pvt_heap_size;
536   a->flags = SVM_FLAGS_MHEAP;
537   svm_region_init_mapped_region (a, (svm_region_t *) a->baseva);
538
539   vlib_rp = (svm_region_t *) a->baseva;
540
541   /*
542    * Part deux, initialize the svm_region_t shared-memory header
543    * api allocation rings, and so on.
544    */
545   vl_init_shmem (vlib_rp, 1 /* is_vlib (dont-care) */ , 1 /* is_private */ );
546
547   vec_add1 (am->vlib_private_rps, vlib_rp);
548
549   memfd->sh->ready = 1;
550
551   /* Recompute the set of input queues to poll in memclnt_process */
552   vec_reset_length (vl_api_queue_cursizes);
553
554 reply:
555
556   /* send the reply message */
557
558   rmp = vl_msg_api_alloc (sizeof (*rmp));
559   rmp->_vl_msg_id = htons (VL_API_MEMFD_SEGMENT_CREATE_REPLY);
560   rmp->context = mp->context;
561   rmp->retval = htonl (rv);
562
563   vl_msg_api_send (regp, (u8 *) rmp);
564
565   if (rv != 0)
566     return;
567
568   /*
569    * We need the reply message to make it out the back door
570    * before we send the magic fd message.
571    */
572   vlib_process_suspend (vm, 11e-6);
573
574   cf = file_main.file_pool + regp->clib_file_index;
575
576   /* send the magic "here's your sign (aka fd)" socket message */
577   send_fd_msg (cf->file_descriptor, memfd->fd);
578 }
579
580 #define foreach_vlib_api_msg                    \
581 _(SOCKCLNT_CREATE, sockclnt_create)             \
582 _(SOCKCLNT_DELETE, sockclnt_delete)             \
583 _(MEMFD_SEGMENT_CREATE, memfd_segment_create)
584
585 clib_error_t *
586 socksvr_api_init (vlib_main_t * vm)
587 {
588   clib_file_main_t *fm = &file_main;
589   clib_file_t template = { 0 };
590   vl_api_registration_t *rp;
591   vl_msg_api_msg_config_t cfg;
592   vl_msg_api_msg_config_t *c = &cfg;
593   socket_main_t *sm = &socket_main;
594   clib_socket_t *sock = &sm->socksvr_listen_socket;
595   clib_error_t *error;
596
597   /* If not explicitly configured, do not bind/enable, etc. */
598   if (sm->socket_name == 0)
599     return 0;
600
601 #define _(N,n) do {                                             \
602     c->id = VL_API_##N;                                         \
603     c->name = #n;                                               \
604     c->handler = vl_api_##n##_t_handler;                        \
605     c->cleanup = vl_noop_handler;                               \
606     c->endian = vl_api_##n##_t_endian;                          \
607     c->print = vl_api_##n##_t_print;                            \
608     c->size = sizeof(vl_api_##n##_t);                           \
609     c->traced = 1; /* trace, so these msgs print */             \
610     c->replay = 0; /* don't replay client create/delete msgs */ \
611     c->message_bounce = 0; /* don't bounce this message */      \
612     vl_msg_api_config(c);} while (0);
613
614   foreach_vlib_api_msg;
615 #undef _
616
617   vec_resize (sm->input_buffer, 4096);
618
619   sock->config = (char *) sm->socket_name;
620
621   /* mkdir of file socket, only under /run  */
622   if (strncmp (sock->config, "/run", 4) == 0)
623     {
624       u8 *tmp = format (0, "%s", sock->config);
625       int i = vec_len (tmp);
626       while (i && tmp[--i] != '/')
627         ;
628
629       tmp[i] = 0;
630
631       if (i)
632         vlib_unix_recursive_mkdir ((char *) tmp);
633       vec_free (tmp);
634     }
635
636   sock->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_SEQPACKET |
637     CLIB_SOCKET_F_ALLOW_GROUP_WRITE;
638   error = clib_socket_init (sock);
639   if (error)
640     return error;
641
642   pool_get (sm->registration_pool, rp);
643   memset (rp, 0, sizeof (*rp));
644
645   rp->registration_type = REGISTRATION_TYPE_SOCKET_LISTEN;
646
647   template.read_function = socksvr_accept_ready;
648   template.write_function = socksvr_bogus_write;
649   template.file_descriptor = sock->fd;
650   template.private_data = rp - sm->registration_pool;
651
652   rp->clib_file_index = clib_file_add (fm, &template);
653   return 0;
654 }
655
656 static clib_error_t *
657 socket_exit (vlib_main_t * vm)
658 {
659   clib_file_main_t *fm = &file_main;
660   socket_main_t *sm = &socket_main;
661   vl_api_registration_t *rp;
662
663   /* Defensive driving in case something wipes out early */
664   if (sm->registration_pool)
665     {
666       u32 index;
667         /* *INDENT-OFF* */
668         pool_foreach (rp, sm->registration_pool, ({
669             clib_file_del (fm, fm->file_pool + rp->clib_file_index);
670             index = rp->vl_api_registration_pool_index;
671             vl_free_socket_registration_index (index);
672         }));
673 /* *INDENT-ON* */
674     }
675
676   return 0;
677 }
678
679 VLIB_MAIN_LOOP_EXIT_FUNCTION (socket_exit);
680
681 static clib_error_t *
682 socksvr_config (vlib_main_t * vm, unformat_input_t * input)
683 {
684   socket_main_t *sm = &socket_main;
685
686   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
687     {
688       if (unformat (input, "socket-name %s", &sm->socket_name))
689         ;
690       else if (unformat (input, "default"))
691         {
692           sm->socket_name = format (0, "%s%c", API_SOCKET_FILE, 0);
693         }
694       else
695         {
696           return clib_error_return (0, "unknown input '%U'",
697                                     format_unformat_error, input);
698         }
699     }
700   return 0;
701 }
702
703 VLIB_CONFIG_FUNCTION (socksvr_config, "socksvr");
704
705 clib_error_t *
706 vlibsocket_init (vlib_main_t * vm)
707 {
708   return 0;
709 }
710
711 VLIB_INIT_FUNCTION (vlibsocket_init);
712
713 /*
714  * fd.io coding-style-patch-verification: ON
715  *
716  * Local Variables:
717  * eval: (c-set-style "gnu")
718  * End:
719  */