libmemif: add testing application
[vpp.git] / extras / libmemif / examples / test_app / main.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2022 Cisco Systems, Inc.
3  */
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <inttypes.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <getopt.h>
13
14 #include <libmemif.h>
15 #include <common.h>
16
17 #define APP_NAME "test_app"
18
19 #define IF_NAME0    "libmemif0"
20 #define IF_ID0      0
21 #define IF_NAME1    "libmemif1"
22 #define IF_ID1      1
23 #define SOCKET_PATH "/run/vpp/memif.sock"
24
25 memif_connection_t intf0, intf1;
26 int epfd;
27
28 /* informs user about connected status. private_ctx is used by user to identify
29  * connection */
30 int
31 on_connect (memif_conn_handle_t conn, void *private_ctx)
32 {
33   INFO ("memif connected!");
34   int err;
35
36   memif_connection_t *c = (memif_connection_t *) private_ctx;
37
38   c->is_connected = 1;
39   alloc_memif_buffers (c);
40
41   err = memif_refill_queue (conn, 0, -1, 0);
42   if (err != MEMIF_ERR_SUCCESS)
43     {
44       INFO ("memif_refill_queue: %s", memif_strerror (err));
45       return err;
46     }
47
48   print_memif_details (c);
49
50   return 0;
51 }
52
53 /* informs user about disconnected status. private_ctx is used by user to
54  * identify connection */
55 int
56 on_disconnect (memif_conn_handle_t conn, void *private_ctx)
57 {
58   INFO ("memif disconnected!");
59
60   memif_connection_t *c = (memif_connection_t *) private_ctx;
61
62   c->is_connected = 0;
63   free_memif_buffers (c);
64
65   /* stop event polling thread */
66   int err = memif_cancel_poll_event (memif_get_socket_handle (conn));
67   if (err != MEMIF_ERR_SUCCESS)
68     INFO ("We are doomed...");
69
70   return 0;
71 }
72
73 int
74 on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
75 {
76   memif_connection_t *c = (memif_connection_t *) private_ctx;
77   memif_connection_t *s, *r;
78   int err, i;
79   uint16_t tx;
80
81   if (c == &intf0)
82     {
83       r = &intf0;
84       s = &intf1;
85     }
86   else
87     {
88       r = &intf1;
89       s = &intf0;
90     }
91
92   /* receive packets from the shared memory */
93   err =
94     memif_rx_burst (r->conn, qid, r->rx_bufs, MAX_MEMIF_BUFS, &r->rx_buf_num);
95   if (err != MEMIF_ERR_SUCCESS)
96     {
97       INFO ("memif_rx_burst: %s", memif_strerror (err));
98       return err;
99     }
100
101   do
102     {
103       /* allocate tx buffers */
104       err = memif_buffer_alloc (s->conn, s->tx_qid, s->tx_bufs, r->rx_buf_num,
105                                 &s->tx_buf_num, 2048);
106       /* suppress full ring error MEMIF_ERR_NOBUF_RING */
107       if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING)
108         {
109           INFO ("memif_buffer_alloc: %s", memif_strerror (err));
110           goto error;
111         }
112
113       /* Process the packets */
114       for (i = 0; i < s->tx_buf_num; i++)
115         {
116           memcpy (s->tx_bufs[i].data, r->rx_bufs[i].data, r->rx_bufs[i].len);
117           s->tx_bufs[i].len = r->rx_bufs[i].len;
118         }
119
120       /* Done processing packets */
121       /* refill the queue */
122       err = memif_refill_queue (r->conn, qid, s->tx_buf_num, 0);
123       if (err != MEMIF_ERR_SUCCESS)
124         {
125           INFO ("memif_refill_queue: %s", memif_strerror (err));
126           goto error;
127         }
128       r->rx_buf_num -= s->tx_buf_num;
129
130       err =
131         memif_tx_burst (s->conn, s->tx_qid, s->tx_bufs, s->tx_buf_num, &tx);
132       if (err != MEMIF_ERR_SUCCESS)
133         {
134           INFO ("memif_tx_burst: %s", memif_strerror (err));
135           goto error;
136         }
137       s->tx_buf_num -= tx;
138       /* This should never happen */
139       if (s->tx_buf_num != 0)
140         {
141           INFO ("memif_tx_burst failed to send all allocated buffers.");
142           goto error;
143         }
144     }
145   while (r->rx_buf_num > 0);
146
147   return 0;
148
149 error:
150   err = memif_refill_queue (conn, qid, r->rx_buf_num, 0);
151   if (err != MEMIF_ERR_SUCCESS)
152     {
153       INFO ("memif_refill_queue: %s", memif_strerror (err));
154       return err;
155     }
156   r->rx_buf_num = 0;
157
158   return -1;
159 }
160
161 void
162 print_help ()
163 {
164   printf ("LIBMEMIF TEST APP: %s", APP_NAME);
165 #ifdef TEST_DBG
166   printf (" (debug)");
167 #endif
168   printf ("\n");
169   printf ("==============================\n");
170   print_version ();
171   printf ("==============================\n");
172   printf (
173     "In this testing application, memif endpoints connect to an external "
174     "application.\n");
175   printf ("The test application loopbacks recieved packets from one memif to "
176           "another memif .\n");
177   printf ("The program will exit once the interfaces are disconnected.\n");
178   printf ("==============================\n");
179   printf ("Usage: test_app [OPTIONS]\n\n");
180   printf ("Options:\n");
181   printf ("\t-r\tInterface role <slave|master>. Default: slave\n");
182   printf ("\t-s\tSocket path. Supports abstract socket using @ before the "
183           "path. Default: /run/vpp/memif.sock\n");
184   printf ("\t-i\tInterface id. Default: 0\n");
185   printf ("\t-t\tInterface id2. Default: 1\n");
186   printf ("\t-h\tShow help and exit.\n");
187   printf ("\t-v\tShow libmemif and memif version information and exit.\n");
188 }
189
190 int
191 main (int argc, char *argv[])
192 {
193   memif_socket_args_t memif_socket_args = { 0 };
194   memif_socket_handle_t memif_socket;
195   memif_conn_args_t memif_conn_args = { 0 };
196   int opt, err, ret = 0;
197   uint8_t is_master = 0;
198   char socket_path[108];
199   int id0 = IF_ID0;
200   int id1 = IF_ID1;
201
202   strncpy (socket_path, SOCKET_PATH, strlen (SOCKET_PATH));
203
204   /* prepare the private data */
205   memset (&intf0, 0, sizeof (intf0));
206   memset (&intf1, 0, sizeof (intf1));
207
208   while ((opt = getopt (argc, argv, "rsithv")) != -1)
209     {
210       switch (opt)
211         {
212         case 'r':
213           if (strncmp (optarg, "master", sizeof (optarg)) == 0)
214             {
215               is_master = 1;
216             }
217           else if (strncmp (optarg, "slave", sizeof (optarg)) == 0)
218             {
219               is_master = 0;
220             }
221           else
222             {
223               INFO ("Invalid role value: '%s'", optarg);
224               return -1;
225             }
226           break;
227         case 's':
228           sprintf (socket_path, "%s", optarg);
229           break;
230         case 'i':
231           id0 = atoi (optarg);
232           break;
233         case 't':
234           id1 = atoi (optarg);
235           break;
236         case 'h':
237           print_help ();
238           return 0;
239         case 'v':
240           print_version ();
241           return 0;
242         }
243     }
244
245   /** Create memif socket
246    *
247    * Interfaces are internally stored in a database referenced by memif socket.
248    */
249   sprintf (memif_socket_args.path, "%s", socket_path);
250   /* Set application name */
251   strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME));
252
253   /* configure autoconnect timer */
254   if (is_master == 0)
255     {
256       memif_socket_args.connection_request_timer.it_value.tv_sec = 2;
257       memif_socket_args.connection_request_timer.it_value.tv_nsec = 0;
258       memif_socket_args.connection_request_timer.it_interval.tv_sec = 2;
259       memif_socket_args.connection_request_timer.it_interval.tv_nsec = 0;
260     }
261
262   err = memif_create_socket (&memif_socket, &memif_socket_args, NULL);
263   if (err != MEMIF_ERR_SUCCESS)
264     {
265       INFO ("memif_create_socket: %s", memif_strerror (err));
266       goto error;
267     }
268
269   /**
270    * Create memif interfaces
271    */
272   memif_conn_args.socket = memif_socket;
273   memif_conn_args.interface_id = id0;
274   strncpy (memif_conn_args.interface_name, IF_NAME0,
275            sizeof (memif_conn_args.interface_name));
276   memif_conn_args.is_master = is_master;
277
278   err = memif_create (&intf0.conn, &memif_conn_args, on_connect, on_disconnect,
279                       on_interrupt, (void *) &intf0);
280   if (err != MEMIF_ERR_SUCCESS)
281     {
282       INFO ("memif_create_socket: %s", memif_strerror (err));
283       return err;
284     }
285
286   memif_conn_args.interface_id = id1;
287   strncpy (memif_conn_args.interface_name, IF_NAME1,
288            sizeof (memif_conn_args.interface_name));
289
290   err = memif_create (&intf1.conn, &memif_conn_args, on_connect, on_disconnect,
291                       on_interrupt, (void *) &intf1);
292   if (err != MEMIF_ERR_SUCCESS)
293     {
294       INFO ("memif_create_socket: %s", memif_strerror (err));
295       return err;
296     }
297
298   do
299     {
300       err = memif_poll_event (memif_socket, -1);
301     }
302   while (err == MEMIF_ERR_SUCCESS);
303
304   return 0;
305
306 error:
307   ret = -1;
308 done:
309   free_memif_buffers (&intf0);
310   free_memif_buffers (&intf1);
311   memif_delete (&intf0.conn);
312   memif_delete (&intf1.conn);
313   memif_delete_socket (&memif_socket);
314   return ret;
315 }