blob: 4172785f0ef414f50ed31c83ac48c02be5df026f [file] [log] [blame]
Jakub Grajciar7c5c40d2017-08-30 10:13:25 +02001/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2017 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *------------------------------------------------------------------
16 */
17
18#include <stdlib.h>
19#include <stdint.h>
20#include <net/if.h>
21#include <sys/types.h>
22#include <fcntl.h>
23#include <sys/ioctl.h>
24#include <sys/socket.h>
25#include <sys/un.h>
26#include <sys/uio.h>
27#include <sys/mman.h>
28#include <sys/prctl.h>
29#include <inttypes.h>
30#include <string.h>
31#include <stdio.h>
32#include <netdb.h>
33#include <linux/ip.h>
34#include <linux/icmp.h>
35#include <arpa/inet.h>
36#include <stdlib.h>
37#include <netinet/if_ether.h>
38#include <net/if_arp.h>
39#include <asm/byteorder.h>
40#include <byteswap.h>
41#include <string.h>
42#include <sys/epoll.h>
43#include <errno.h>
44#include <unistd.h>
45#include <signal.h>
46
47#include <libmemif.h>
48#include <icmp_proto.h>
49
50#define APP_NAME "ICMP_Responder"
51#define IF_NAME "memif_connection"
52
53
54#ifdef ICMP_DBG
55#define DBG(...) do { \
56 printf (APP_NAME":%s:%d: ", __func__, __LINE__); \
57 printf (__VA_ARGS__); \
58 printf ("\n"); \
59 } while (0)
60#else
61#define DBG(...)
62#endif
63
64#define INFO(...) do { \
65 printf ("INFO: "__VA_ARGS__); \
66 printf ("\n"); \
67 } while (0)
68
69/* maximum tx/rx memif buffers */
70#define MAX_MEMIF_BUFS 256
71#define MAX_CONNS 50
72
73int epfd;
74
75typedef struct
76{
77 uint16_t index;
78 /* memif conenction handle */
79 memif_conn_handle_t conn;
80 /* tx buffers */
81 memif_buffer_t *tx_bufs;
82 /* allocated tx buffers counter */
83 /* number of tx buffers pointing to shared memory */
84 uint16_t tx_buf_num;
85 /* rx buffers */
86 memif_buffer_t *rx_bufs;
87 /* allcoated rx buffers counter */
88 /* number of rx buffers pointing to shared memory */
89 uint16_t rx_buf_num;
90 /* interface ip address */
91 uint8_t ip_addr[4];
92} memif_connection_t;
93
94memif_connection_t memif_connection[MAX_CONNS];
95long ctx[MAX_CONNS];
96
97/* print details for all memif connections */
98static void
99print_memif_details ()
100{
101 memif_details_t md;
102 ssize_t buflen;
103 char *buf;
104 int err, i, e;
105 buflen = 2048;
106 buf = malloc (buflen);
107 printf ("MEMIF DETAILS\n");
108 printf ("==============================\n");
109 for (i = 0; i < MAX_CONNS; i++)
110 {
111 memif_connection_t *c = &memif_connection[i];
112
113 memset (&md, 0, sizeof (md));
114 memset (buf, 0, buflen);
115
116 err = memif_get_details (c->conn, &md, buf, buflen);
117 if (err != MEMIF_ERR_SUCCESS)
118 {
119 if (err != MEMIF_ERR_NOCONN)
120 INFO ("%s", memif_strerror (err));
121 continue;
122 }
123
124 printf ("interface index: %d\n", i);
125
126 printf ("\tinterface ip: %u.%u.%u.%u\n",
127 c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
128 printf ("\tinterface name: %s\n", (char *) md.if_name);
129 printf ("\tapp name: %s\n", (char *) md.inst_name);
130 printf ("\tremote interface name: %s\n", (char *) md.remote_if_name);
131 printf ("\tremote app name: %s\n", (char *) md.remote_inst_name);
132 printf ("\tid: %u\n", md.id);
133 printf ("\tsecret: %s\n", (char *) md.secret);
134 printf ("\trole: ");
135 if (md.role)
136 printf ("slave\n");
137 else
138 printf ("master\n");
139 printf ("\tmode: ");
140 switch (md.mode)
141 {
142 case 0:
143 printf ("ethernet\n");
144 break;
145 case 1:
146 printf ("ip\n");
147 break;
148 case 2:
149 printf ("punt/inject\n");
150 break;
151 default:
152 printf ("unknown\n");
153 break;
154 }
155 printf ("\tsocket filename: %s\n", (char *) md.socket_filename);
156 printf ("\trx queues:\n");
157 for (e = 0; e < md.rx_queues_num; e++)
158 {
159 printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid);
160 printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size);
161 printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size);
162 }
163 printf ("\ttx queues:\n");
164 for (e = 0; e < md.tx_queues_num; e++)
165 {
166 printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid);
167 printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size);
168 printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size);
169 }
170 printf ("\tlink: ");
171 if (md.link_up_down)
172 printf ("up\n");
173 else
174 printf ("down\n");
175 }
176 free (buf);
177}
178
179int
180add_epoll_fd (int fd, uint32_t events)
181{
182 if (fd < 0)
183 {
184 DBG ("invalid fd %d", fd);
185 return -1;
186 }
187 struct epoll_event evt;
188 memset (&evt, 0, sizeof (evt));
189 evt.events = events;
190 evt.data.fd = fd;
191 if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0)
192 {
193 DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
194 return -1;
195 }
196 DBG ("fd %d added to epoll", fd);
197 return 0;
198}
199
200int
201mod_epoll_fd (int fd, uint32_t events)
202{
203 if (fd < 0)
204 {
205 DBG ("invalid fd %d", fd);
206 return -1;
207 }
208 struct epoll_event evt;
209 memset (&evt, 0, sizeof (evt));
210 evt.events = events;
211 evt.data.fd = fd;
212 if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0)
213 {
214 DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
215 return -1;
216 }
217 DBG ("fd %d moddified on epoll", fd);
218 return 0;
219}
220
221int
222del_epoll_fd (int fd)
223{
224 if (fd < 0)
225 {
226 DBG ("invalid fd %d", fd);
227 return -1;
228 }
229 struct epoll_event evt;
230 memset (&evt, 0, sizeof (evt));
231 if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0)
232 {
233 DBG ("epoll_ctl: %s fd %d", strerror (errno), fd);
234 return -1;
235 }
236 DBG ("fd %d removed from epoll", fd);
237 return 0;
238}
239
240/* informs user about connected status. private_ctx is used by user to identify connection
241 (multiple connections WIP) */
242int
243on_connect (memif_conn_handle_t conn, void *private_ctx)
244{
245 INFO ("memif connected!");
246 return 0;
247}
248
249/* informs user about disconnected status. private_ctx is used by user to identify connection
250 (multiple connections WIP) */
251int
252on_disconnect (memif_conn_handle_t conn, void *private_ctx)
253{
254 INFO ("memif disconnected!");
255 return 0;
256}
257
258/* user needs to watch new fd or stop watching fd that is about to be closed.
259 control fd will be modified during connection establishment to minimize CPU usage */
260int
261control_fd_update (int fd, uint8_t events)
262{
263 /* convert memif event definitions to epoll events */
264 if (events & MEMIF_FD_EVENT_DEL)
265 return del_epoll_fd (fd);
266
267 uint32_t evt = 0;
268 if (events & MEMIF_FD_EVENT_READ)
269 evt |= EPOLLIN;
270 if (events & MEMIF_FD_EVENT_WRITE)
271 evt |= EPOLLOUT;
272
273 if (events & MEMIF_FD_EVENT_MOD)
274 return mod_epoll_fd (fd, evt);
275
276 return add_epoll_fd (fd, evt);
277}
278
279int
280icmpr_buffer_alloc (long index, long n, uint16_t qid)
281{
282 memif_connection_t *c = &memif_connection[index];
283 int err;
284 uint16_t r;
285 /* set data pointer to shared memory and set buffer_len to shared mmeory buffer len */
286 err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, n, &r);
287 if (err != MEMIF_ERR_SUCCESS)
288 {
289 INFO ("memif_buffer_alloc: %s", memif_strerror (err));
290 c->tx_buf_num += r;
291 return -1;
292 }
293 c->tx_buf_num += r;
294 DBG ("allocated %d/%ld buffers, %u free buffers", r, n,
295 MAX_MEMIF_BUFS - c->tx_buf_num);
296 return 0;
297}
298
299int
300icmpr_tx_burst (long index, uint16_t qid)
301{
302 memif_connection_t *c = &memif_connection[index];
303 int err;
304 uint16_t r;
305 /* inform peer memif interface about data in shared memory buffers */
306 /* mark memif buffers as free */
307 err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &r);
308 if (err != MEMIF_ERR_SUCCESS)
309 INFO ("memif_tx_burst: %s", memif_strerror (err));
310 DBG ("tx: %d/%u", r, c->tx_buf_num);
311 c->tx_buf_num -= r;
312 return 0;
313}
314
315/* called when event is polled on interrupt file descriptor.
316 there are packets in shared memory ready to be received */
317int
318on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
319{
320 long index = *((long *) private_ctx);
321 memif_connection_t *c = &memif_connection[index];
322 if (c->index != index)
323 {
324 INFO ("invalid context: %ld/%u", index, c->index);
325 return 0;
326 }
327 int err;
328 uint16_t rx;
329 uint16_t fb;
330 /* receive data from shared memory buffers */
331 err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
332 if (err != MEMIF_ERR_SUCCESS)
333 {
334 INFO ("memif_rx_burst: %s", memif_strerror (err));
335 c->rx_buf_num += rx;
336 goto error;
337 }
338 c->rx_buf_num += rx;
339
340 DBG ("received %d buffers. %u/%u alloc/free buffers",
341 rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
342
343 if (icmpr_buffer_alloc (index, rx, qid) < 0)
344 {
345 INFO ("buffer_alloc error");
346 goto error;
347 }
348 int i;
349 for (i = 0; i < rx; i++)
350 {
351 resolve_packet ((void *) (c->rx_bufs + i)->data,
352 (c->rx_bufs + i)->data_len,
353 (void *) (c->tx_bufs + i)->data,
354 &(c->tx_bufs + i)->data_len, c->ip_addr);
355 }
356
357 /* mark memif buffers and shared memory buffers as free */
358 err = memif_buffer_free (c->conn, qid, c->rx_bufs, rx, &fb);
359 if (err != MEMIF_ERR_SUCCESS)
360 INFO ("memif_buffer_free: %s", memif_strerror (err));
361 c->rx_buf_num -= fb;
362
363 DBG ("freed %d buffers. %u/%u alloc/free buffers",
364 fb, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
365
366 icmpr_tx_burst (index, qid);
367
368 return 0;
369
370error:
371 err = memif_buffer_free (c->conn, qid, c->rx_bufs, rx, &fb);
372 if (err != MEMIF_ERR_SUCCESS)
373 INFO ("memif_buffer_free: %s", memif_strerror (err));
374 c->rx_buf_num -= fb;
375 DBG ("freed %d buffers. %u/%u alloc/free buffers",
376 fb, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num);
377 return 0;
378}
379
380int
381icmpr_memif_create (long index, long mode)
382{
383 if (index >= MAX_CONNS)
384 {
385 INFO ("connection array overflow");
386 return 0;
387 }
388 if (index < 0)
389 {
390 INFO ("don't even try...");
391 return 0;
392 }
393 memif_connection_t *c = &memif_connection[index];
394
395 /* setting memif connection arguments */
396 memif_conn_args_t args;
397 int fd = -1;
398 memset (&args, 0, sizeof (args));
399 args.is_master = mode;
400 args.log2_ring_size = 10;
401 args.buffer_size = 2048;
402 args.num_s2m_rings = 2;
403 args.num_m2s_rings = 2;
404 strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
405 strncpy ((char *) args.instance_name, APP_NAME, strlen (APP_NAME));
406 args.mode = 0;
407 /* socket filename is not specified, because this app is supposed to
408 connect to VPP over memif. so default socket filename will be used */
409 /* default socketfile = /run/vpp/memif.sock */
410
411 args.interface_id = index;
412 /* last argument for memif_create (void * private_ctx) is used by user
413 to identify connection. this context is returned with callbacks */
414 int err = memif_create (&c->conn,
415 &args, on_connect, on_disconnect, on_interrupt,
416 &ctx[index]);
417 if (err != MEMIF_ERR_SUCCESS)
418 {
419 INFO ("memif_create: %s", memif_strerror (err));
420 return 0;
421 }
422
423 c->index = index;
424 /* alloc memif buffers */
425 c->rx_buf_num = 0;
426 c->rx_bufs =
427 (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
428 c->tx_buf_num = 0;
429 c->tx_bufs =
430 (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS);
431
432 c->ip_addr[0] = 192;
433 c->ip_addr[1] = 168;
434 c->ip_addr[2] = c->index + 1;
435 c->ip_addr[3] = 2;
436 return 0;
437}
438
439int
440icmpr_memif_delete (long index)
441{
442 if (index >= MAX_CONNS)
443 {
444 INFO ("connection array overflow");
445 return 0;
446 }
447 if (index < 0)
448 {
449 INFO ("don't even try...");
450 return 0;
451 }
452 memif_connection_t *c = &memif_connection[index];
453
454 if (c->rx_bufs)
455 free (c->rx_bufs);
456 c->rx_bufs = NULL;
457 c->rx_buf_num = 0;
458 if (c->tx_bufs)
459 free (c->tx_bufs);
460 c->tx_bufs = NULL;
461 c->tx_buf_num = 0;
462
463 int err;
464 /* disconenct then delete memif connection */
465 err = memif_delete (&c->conn);
466 if (err != MEMIF_ERR_SUCCESS)
467 INFO ("memif_delete: %s", memif_strerror (err));
468 if (c->conn != NULL)
469 INFO ("memif delete fail");
470 return 0;
471}
472
473void
474print_help ()
475{
476 printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME);
477#ifdef ICMP_DBG
478 printf (" (debug)");
479#endif
480 printf ("\n");
481 printf ("==============================\n");
482 printf ("libmemif version: %s", LIBMEMIF_VERSION);
483#ifdef MEMIF_DBG
484 printf (" (debug)");
485#endif
486 printf ("\n");
487 printf ("memif version: %d\n", MEMIF_VERSION);
488 printf ("commands:\n");
489 printf ("\thelp - prints this help\n");
490 printf ("\texit - exit app\n");
491 printf
492 ("\tconn <index> <mode> - create memif. index is also used as interface id, mode 0 = slave 1 = master\n");
493 printf ("\tdel <index> - delete memif\n");
494 printf ("\tshow - show connection details\n");
495 printf ("\tip-set <index> <ip-addr> - set interface ip address\n");
496 printf
497 ("\trx-mode <index> <qid> <polling|interrupt> - set queue rx mode\n");
498}
499
500int
501icmpr_free ()
502{
503 /* application cleanup */
504 int err;
505 long i;
506 for (i = 0; i < MAX_CONNS; i++)
507 {
508 memif_connection_t *c = &memif_connection[i];
509 if (c->conn)
510 icmpr_memif_delete (i);
511 }
512
513 err = memif_cleanup ();
514 if (err != MEMIF_ERR_SUCCESS)
515 INFO ("memif_delete: %s", memif_strerror (err));
516
517 return 0;
518}
519
520int
521icmpr_set_ip (long index, char *ip)
522{
523 if (index >= MAX_CONNS)
524 {
525 INFO ("connection array overflow");
526 return 0;
527 }
528 if (index < 0)
529 {
530 INFO ("don't even try...");
531 return 0;
532 }
533 memif_connection_t *c = &memif_connection[index];
534 if (c->conn == NULL)
535 {
536 INFO ("no connection at index %ld", index);
537 return 0;
538 }
539
540 char *end;
541 char *ui;
542 uint8_t tmp[4];
543 ui = strtok (ip, ".");
544 if (ui == NULL)
545 goto error;
546 tmp[0] = strtol (ui, &end, 10);
547
548 ui = strtok (NULL, ".");
549 if (ui == NULL)
550 goto error;
551 tmp[1] = strtol (ui, &end, 10);
552
553 ui = strtok (NULL, ".");
554 if (ui == NULL)
555 goto error;
556 tmp[2] = strtol (ui, &end, 10);
557
558 ui = strtok (NULL, ".");
559 if (ui == NULL)
560 goto error;
561 tmp[3] = strtol (ui, &end, 10);
562
563 c->ip_addr[0] = tmp[0];
564 c->ip_addr[1] = tmp[1];
565 c->ip_addr[2] = tmp[2];
566 c->ip_addr[3] = tmp[3];
567
568 INFO ("memif %ld ip address set to %u.%u.%u.%u",
569 index, c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]);
570
571 return 0;
572
573error:
574 INFO ("invalid ip address");
575 return 0;
576}
577
578int
579icmpr_set_rx_mode (long index, long qid, char *mode)
580{
581 if (index >= MAX_CONNS)
582 {
583 INFO ("connection array overflow");
584 return 0;
585 }
586 if (index < 0)
587 {
588 INFO ("don't even try...");
589 return 0;
590 }
591 memif_connection_t *c = &memif_connection[index];
592
593 if (c->conn == NULL)
594 {
595 INFO ("no connection at index %ld", index);
596 return 0;
597 }
598
599 if (strncmp (mode, "interrupt", 9) == 0)
600 {
601 memif_set_rx_mode (c->conn, MEMIF_RX_MODE_INTERRUPT, qid);
602 }
603
604 else if (strncmp (mode, "polling", 7) == 0)
605 {
606 memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, qid);
607 }
608 else
609 INFO ("expected rx mode <interrupt|polling>");
610 return 0;
611}
612
613int
614user_input_handler ()
615{
616 int i;
617 char *in = (char *) malloc (256);
618 char *ui = fgets (in, 256, stdin);
619 char *end;
620 long a;
621 if (in[0] == '\n')
622 goto done;
623 ui = strtok (in, " ");
624 if (strncmp (ui, "exit", 4) == 0)
625 {
626 free (in);
627 icmpr_free ();
628 exit (EXIT_SUCCESS);
629 }
630 else if (strncmp (ui, "help", 4) == 0)
631 {
632 print_help ();
633 goto done;
634 }
635 else if (strncmp (ui, "conn", 4) == 0)
636 {
637 ui = strtok (NULL, " ");
638 if (ui != NULL)
639 a = strtol (ui, &end, 10);
640 else
641 {
642 INFO ("expected id");
643 goto done;
644 }
645 ui = strtok (NULL, " ");
646 if (ui != NULL)
647 icmpr_memif_create (a, strtol (ui, &end, 10));
648 else
649 INFO ("expected mode <0|1>");
650 goto done;
651 }
652 else if (strncmp (ui, "del", 3) == 0)
653 {
654 ui = strtok (NULL, " ");
655 if (ui != NULL)
656 icmpr_memif_delete (strtol (ui, &end, 10));
657 else
658 INFO ("expected id");
659 goto done;
660 }
661 else if (strncmp (ui, "show", 4) == 0)
662 {
663 print_memif_details ();
664 goto done;
665 }
666 else if (strncmp (ui, "ip-set", 6) == 0)
667 {
668 ui = strtok (NULL, " ");
669 if (ui != NULL)
670 icmpr_set_ip (strtol (ui, &end, 10), strtok (NULL, " "));
671 else
672 INFO ("expected id");
673 goto done;
674 }
675 else if (strncmp (ui, "rx-mode", 7) == 0)
676 {
677 ui = strtok (NULL, " ");
678 if (ui != NULL)
679 a = strtol (ui, &end, 10);
680 else
681 {
682 INFO ("expected id");
683 goto done;
684 }
685 ui = strtok (NULL, " ");
686 if (ui != NULL)
687 icmpr_set_rx_mode (a, strtol (ui, &end, 10), strtok (NULL, " "));
688 else
689 INFO ("expected qid");
690 goto done;
691 }
692 else
693 {
694 DBG ("unknown command: %s", ui);
695 goto done;
696 }
697
698 return 0;
699done:
700 free (in);
701 return 0;
702}
703
704int
705poll_event (int timeout)
706{
707 struct epoll_event evt, *e;
708 int app_err = 0, memif_err = 0, en = 0;
709 int tmp, nfd;
710 uint32_t events = 0;
711 memset (&evt, 0, sizeof (evt));
712 evt.events = EPOLLIN | EPOLLOUT;
713 sigset_t sigset;
714 sigemptyset (&sigset);
715 en = epoll_pwait (epfd, &evt, 1, timeout, &sigset);
716 if (en < 0)
717 {
718 DBG ("epoll_pwait: %s", strerror (errno));
719 return -1;
720 }
721 if (en > 0)
722 {
723 /* this app does not use any other file descriptors than stds and memif control fds */
724 if (evt.data.fd > 2)
725 {
726 /* event of memif control fd */
727 /* convert epolle events to memif events */
728 if (evt.events & EPOLLIN)
729 events |= MEMIF_FD_EVENT_READ;
730 if (evt.events & EPOLLOUT)
731 events |= MEMIF_FD_EVENT_WRITE;
732 if (evt.events & EPOLLERR)
733 events |= MEMIF_FD_EVENT_ERROR;
734 memif_err = memif_control_fd_handler (evt.data.fd, events);
735 if (memif_err != MEMIF_ERR_SUCCESS)
736 INFO ("memif_control_fd_handler: %s", memif_strerror (memif_err));
737 }
738 else if (evt.data.fd == 0)
739 {
740 app_err = user_input_handler ();
741 }
742 else
743 {
744 DBG ("unexpected event at memif_epfd. fd %d", evt.data.fd);
745 }
746 }
747
748 if ((app_err < 0) || (memif_err < 0))
749 {
750 if (app_err < 0)
751 DBG ("user input handler error");
752 if (memif_err < 0)
753 DBG ("memif control fd handler error");
754 return -1;
755 }
756
757 return 0;
758}
759
760int
761main ()
762{
763 epfd = epoll_create (1);
764 add_epoll_fd (0, EPOLLIN);
765
766 /* initialize memory interface */
767 int err, i;
768 /* if valid callback is passed as argument, fd event polling will be done by user
769 all file descriptors and events will be passed to user in this callback */
770 /* if callback is set to NULL libmemif will handle fd event polling */
771 err = memif_init (control_fd_update, APP_NAME);
772 if (err != MEMIF_ERR_SUCCESS)
773 INFO ("memif_init: %s", memif_strerror (err));
774
775 for (i = 0; i < MAX_CONNS; i++)
776 {
777 memif_connection[i].conn = NULL;
778 ctx[i] = i;
779 }
780
781 print_help ();
782
783 /* main loop */
784 while (1)
785 {
786 if (poll_event (-1) < 0)
787 {
788 DBG ("poll_event error!");
789 }
790 }
791}