diff options
Diffstat (limited to 'extras/libmemif/examples')
-rw-r--r-- | extras/libmemif/examples/ExampleSetup.md | 206 | ||||
-rw-r--r-- | extras/libmemif/examples/README.md | 16 | ||||
-rw-r--r-- | extras/libmemif/examples/icmp_responder-epoll/main.c | 791 | ||||
-rw-r--r-- | extras/libmemif/examples/icmp_responder-mt/main.c | 921 | ||||
-rw-r--r-- | extras/libmemif/examples/icmp_responder/icmp_proto.c | 246 | ||||
-rw-r--r-- | extras/libmemif/examples/icmp_responder/icmp_proto.h | 26 | ||||
-rw-r--r-- | extras/libmemif/examples/icmp_responder/main.c | 416 |
7 files changed, 2622 insertions, 0 deletions
diff --git a/extras/libmemif/examples/ExampleSetup.md b/extras/libmemif/examples/ExampleSetup.md new file mode 100644 index 00000000000..a02506dd0ca --- /dev/null +++ b/extras/libmemif/examples/ExampleSetup.md @@ -0,0 +1,206 @@ +## Example setup + +#### VPP-memif master icmp_responder slave + +> Libmemif example app(s) use memif default socket file: /run/vpp/memif.sock. + +Run VPP and icmpr-epoll example (default example when running in container). +> Other examples work similar to icmpr-epoll. Brief explanation can be found in [Examples readme](README.md) file. + +VPP-side config: +``` +DBGvpp# create memif id 0 master +DBGvpp# set int state memif0/0 up +DBGvpp# set int ip address memif0/0 192.168.1.1/24 +``` +icmpr-epoll: +``` +conn 0 0 +``` +Memif in slave mode will try to connect every 2 seconds. If connection establishment is successfull, a message will show. +``` +INFO: memif connected! +``` +> Error messages like "unmatched interface id" are printed only in debug mode. + +Check connected status. +Use show command in icmpr-epoll: +``` +show +MEMIF DETAILS +============================== +interface index: 0 + interface ip: 192.168.1.2 + interface name: memif_connection + app name: ICMP_Responder + remote interface name: memif0/0 + remote app name: VPP 17.10-rc0~132-g62f9cdd + id: 0 + secret: + role: slave + mode: ethernet + socket filename: /run/vpp/memif.sock + rx queues: + queue id: 0 + ring size: 1024 + buffer size: 2048 + tx queues: + queue id: 0 + ring size: 1024 + buffer size: 2048 + link: up +interface index: 1 + no connection + +``` +Use sh memif command in VPP: +``` +DBGvpp# sh memif +interface memif0/0 + remote-name "ICMP_Responder" + remote-interface "memif_connection" + id 0 mode ethernet file /run/vpp/memif.sock + flags admin-up connected + listener-fd 12 conn-fd 13 + num-s2m-rings 1 num-m2s-rings 1 buffer-size 0 + master-to-slave ring 0: + region 0 offset 32896 ring-size 1024 int-fd 16 + head 0 tail 0 flags 0x0000 interrupts 0 + master-to-slave ring 0: + region 0 offset 0 ring-size 1024 int-fd 15 + head 0 tail 0 flags 0x0001 interrupts 0 +``` + +Send ping from VPP to icmpr-epoll: +``` +DBGvpp# ping 192.168.1.2 +64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=.1888 ms +64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=.1985 ms +64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=.1813 ms +64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=.1929 ms + +Statistics: 5 sent, 4 received, 20% packet loss +``` +#### multiple queues VPP-memif slave icmp_responder master + +Run icmpr-epoll as in previous example setup. +Run VPP with startup conf, enabling 2 worker threads. +Example startup.conf: +``` +unix { + interactive + nodaemon + full-coredump +} + +cpu { + workers 2 +} +``` +VPP-side config: +``` +DBGvpp# create memif id 0 slave rx-queues 2 tx-queues 2 +DBGvpp# set int state memif0/0 up +DBGvpp# set int ip address memif0/0 192.168.1.1/24 +``` +icmpr-epoll: +``` +conn 0 1 +``` +When connection is established a message will print: +``` +INFO: memif connected! +``` +> Error messages like "unmatched interface id" are printed only in debug mode. + +Check connected status. +Use show command in icmpr-epoll: +``` +show +MEMIF DETAILS +============================== +interface index: 0 + interface ip: 192.168.1.2 + interface name: memif_connection + app name: ICMP_Responder + remote interface name: memif0/0 + remote app name: VPP 17.10-rc0~132-g62f9cdd + id: 0 + secret: + role: master + mode: ethernet + socket filename: /run/vpp/memif.sock + rx queues: + queue id: 0 + ring size: 1024 + buffer size: 2048 + queue id: 1 + ring size: 1024 + buffer size: 2048 + tx queues: + queue id: 0 + ring size: 1024 + buffer size: 2048 + queue id: 1 + ring size: 1024 + buffer size: 2048 + link: up +interface index: 1 + no connection + +``` +Use sh memif command in VPP: +``` +DBGvpp# sh memif +interface memif0/0 + remote-name "ICMP_Responder" + remote-interface "memif_connection" + id 0 mode ethernet file /run/vpp/memif.sock + flags admin-up slave connected + listener-fd -1 conn-fd 12 + num-s2m-rings 2 num-m2s-rings 2 buffer-size 2048 + slave-to-master ring 0: + region 0 offset 0 ring-size 1024 int-fd 14 + head 0 tail 0 flags 0x0000 interrupts 0 + slave-to-master ring 1: + region 0 offset 32896 ring-size 1024 int-fd 15 + head 0 tail 0 flags 0x0000 interrupts 0 + slave-to-master ring 0: + region 0 offset 65792 ring-size 1024 int-fd 16 + head 0 tail 0 flags 0x0001 interrupts 0 + slave-to-master ring 1: + region 0 offset 98688 ring-size 1024 int-fd 17 + head 0 tail 0 flags 0x0001 interrupts 0 + +``` +Send ping from VPP to icmpr-epoll: +``` +DBGvpp# ping 192.168.1.2 +64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=.1439 ms +64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=.2184 ms +64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=.1458 ms +64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=.1687 ms + +Statistics: 5 sent, 4 received, 20% packet loss +``` + +#### icmp_responder master icmp_responder slave + +> Example apps can only repond to ping. This setup creates connection between two applications using libmemif. Traffic functionality is the same as when connection to VPP. App can receive ARP/ICMP request and transmit response, but can not send ARP/ICMP request. + +Run two instances of icmpr-epoll example. +> If not running in container, make sure folder /run/vpp/ exists before creating memif master. +Instance 1 will be in master mode, instance 2 in slave mode. +instance 1: +``` +conn 0 1 +``` +instance 2: +``` +conn 0 0 +``` +In 2 seconds, both instances should print connected! message: +``` +INFO: memif connected! +``` +Check peer interface names using show command. diff --git a/extras/libmemif/examples/README.md b/extras/libmemif/examples/README.md new file mode 100644 index 00000000000..bbd663b99e2 --- /dev/null +++ b/extras/libmemif/examples/README.md @@ -0,0 +1,16 @@ +## Examples + +After build, root folder will contain scripts linking binary examples with library (same name as example apps). These scripts can be executed to run example apps without installing the library. Example apps binaries can be found in _libs_ filder. To run binaries directly, make sure that libmemif library is installed. + +#### Run in container +ligato/libmemif-sample-service image contains built and installed libmemf. To run different examples, override docker CMD to start container in bash: +``` +# docker run -it --entrypoint=/bin/bash -i --rm --name icmp-responder --hostname icmp-responder --privileged -v "/run/vpp/:/run/vpp/" ligato/libmemif-sample-service +``` +Current WORKDIR is set to root repository directory. Example apps can be run from this directory (a script linking binary with library), or browse to ./.libs folder and execute binary directly. + +Example app | Description +------------|------------ +[icmpr](../examples/icmp_responder/main.c) | Simplest implementaion. Event polling is handled by libmemif. Single memif conenction in slave mode is created (id 0). Use Ctrl + C to exit app. +[icmpr-epoll](../examples/icmp_responder-epoll/main.c) (run in container by default) | Supports multiple connections and master mode. User can create/delete connections, set ip addresses, print connection information. [Example setup](ExampleSetup.md) contains instructions on basic connection use cases setups. +[icmpr-mt](../examples/icmp_responder-mt/main.c) | Multi-thread example, very similar to icmpr-epoll. Packets are handled in threads assigned to specific queues. Slave mode only. diff --git a/extras/libmemif/examples/icmp_responder-epoll/main.c b/extras/libmemif/examples/icmp_responder-epoll/main.c new file mode 100644 index 00000000000..4172785f0ef --- /dev/null +++ b/extras/libmemif/examples/icmp_responder-epoll/main.c @@ -0,0 +1,791 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include <stdlib.h> +#include <stdint.h> +#include <net/if.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#include <netdb.h> +#include <linux/ip.h> +#include <linux/icmp.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <netinet/if_ether.h> +#include <net/if_arp.h> +#include <asm/byteorder.h> +#include <byteswap.h> +#include <string.h> +#include <sys/epoll.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> + +#include <libmemif.h> +#include <icmp_proto.h> + +#define APP_NAME "ICMP_Responder" +#define IF_NAME "memif_connection" + + +#ifdef ICMP_DBG +#define DBG(...) do { \ + printf (APP_NAME":%s:%d: ", __func__, __LINE__); \ + printf (__VA_ARGS__); \ + printf ("\n"); \ + } while (0) +#else +#define DBG(...) +#endif + +#define INFO(...) do { \ + printf ("INFO: "__VA_ARGS__); \ + printf ("\n"); \ + } while (0) + +/* maximum tx/rx memif buffers */ +#define MAX_MEMIF_BUFS 256 +#define MAX_CONNS 50 + +int epfd; + +typedef struct +{ + uint16_t index; + /* memif conenction handle */ + memif_conn_handle_t conn; + /* tx buffers */ + memif_buffer_t *tx_bufs; + /* allocated tx buffers counter */ + /* number of tx buffers pointing to shared memory */ + uint16_t tx_buf_num; + /* rx buffers */ + memif_buffer_t *rx_bufs; + /* allcoated rx buffers counter */ + /* number of rx buffers pointing to shared memory */ + uint16_t rx_buf_num; + /* interface ip address */ + uint8_t ip_addr[4]; +} memif_connection_t; + +memif_connection_t memif_connection[MAX_CONNS]; +long ctx[MAX_CONNS]; + +/* print details for all memif connections */ +static void +print_memif_details () +{ + memif_details_t md; + ssize_t buflen; + char *buf; + int err, i, e; + buflen = 2048; + buf = malloc (buflen); + printf ("MEMIF DETAILS\n"); + printf ("==============================\n"); + for (i = 0; i < MAX_CONNS; i++) + { + memif_connection_t *c = &memif_connection[i]; + + memset (&md, 0, sizeof (md)); + memset (buf, 0, buflen); + + err = memif_get_details (c->conn, &md, buf, buflen); + if (err != MEMIF_ERR_SUCCESS) + { + if (err != MEMIF_ERR_NOCONN) + INFO ("%s", memif_strerror (err)); + continue; + } + + printf ("interface index: %d\n", i); + + printf ("\tinterface ip: %u.%u.%u.%u\n", + c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]); + printf ("\tinterface name: %s\n", (char *) md.if_name); + printf ("\tapp name: %s\n", (char *) md.inst_name); + printf ("\tremote interface name: %s\n", (char *) md.remote_if_name); + printf ("\tremote app name: %s\n", (char *) md.remote_inst_name); + printf ("\tid: %u\n", md.id); + printf ("\tsecret: %s\n", (char *) md.secret); + printf ("\trole: "); + if (md.role) + printf ("slave\n"); + else + printf ("master\n"); + printf ("\tmode: "); + switch (md.mode) + { + case 0: + printf ("ethernet\n"); + break; + case 1: + printf ("ip\n"); + break; + case 2: + printf ("punt/inject\n"); + break; + default: + printf ("unknown\n"); + break; + } + printf ("\tsocket filename: %s\n", (char *) md.socket_filename); + printf ("\trx queues:\n"); + for (e = 0; e < md.rx_queues_num; e++) + { + printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid); + printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size); + printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size); + } + printf ("\ttx queues:\n"); + for (e = 0; e < md.tx_queues_num; e++) + { + printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid); + printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size); + printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size); + } + printf ("\tlink: "); + if (md.link_up_down) + printf ("up\n"); + else + printf ("down\n"); + } + free (buf); +} + +int +add_epoll_fd (int fd, uint32_t events) +{ + if (fd < 0) + { + DBG ("invalid fd %d", fd); + return -1; + } + struct epoll_event evt; + memset (&evt, 0, sizeof (evt)); + evt.events = events; + evt.data.fd = fd; + if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0) + { + DBG ("epoll_ctl: %s fd %d", strerror (errno), fd); + return -1; + } + DBG ("fd %d added to epoll", fd); + return 0; +} + +int +mod_epoll_fd (int fd, uint32_t events) +{ + if (fd < 0) + { + DBG ("invalid fd %d", fd); + return -1; + } + struct epoll_event evt; + memset (&evt, 0, sizeof (evt)); + evt.events = events; + evt.data.fd = fd; + if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0) + { + DBG ("epoll_ctl: %s fd %d", strerror (errno), fd); + return -1; + } + DBG ("fd %d moddified on epoll", fd); + return 0; +} + +int +del_epoll_fd (int fd) +{ + if (fd < 0) + { + DBG ("invalid fd %d", fd); + return -1; + } + struct epoll_event evt; + memset (&evt, 0, sizeof (evt)); + if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0) + { + DBG ("epoll_ctl: %s fd %d", strerror (errno), fd); + return -1; + } + DBG ("fd %d removed from epoll", fd); + return 0; +} + +/* informs user about connected status. private_ctx is used by user to identify connection + (multiple connections WIP) */ +int +on_connect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif connected!"); + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to identify connection + (multiple connections WIP) */ +int +on_disconnect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif disconnected!"); + return 0; +} + +/* user needs to watch new fd or stop watching fd that is about to be closed. + control fd will be modified during connection establishment to minimize CPU usage */ +int +control_fd_update (int fd, uint8_t events) +{ + /* convert memif event definitions to epoll events */ + if (events & MEMIF_FD_EVENT_DEL) + return del_epoll_fd (fd); + + uint32_t evt = 0; + if (events & MEMIF_FD_EVENT_READ) + evt |= EPOLLIN; + if (events & MEMIF_FD_EVENT_WRITE) + evt |= EPOLLOUT; + + if (events & MEMIF_FD_EVENT_MOD) + return mod_epoll_fd (fd, evt); + + return add_epoll_fd (fd, evt); +} + +int +icmpr_buffer_alloc (long index, long n, uint16_t qid) +{ + memif_connection_t *c = &memif_connection[index]; + int err; + uint16_t r; + /* set data pointer to shared memory and set buffer_len to shared mmeory buffer len */ + err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, n, &r); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_buffer_alloc: %s", memif_strerror (err)); + c->tx_buf_num += r; + return -1; + } + c->tx_buf_num += r; + DBG ("allocated %d/%ld buffers, %u free buffers", r, n, + MAX_MEMIF_BUFS - c->tx_buf_num); + return 0; +} + +int +icmpr_tx_burst (long index, uint16_t qid) +{ + memif_connection_t *c = &memif_connection[index]; + int err; + uint16_t r; + /* inform peer memif interface about data in shared memory buffers */ + /* mark memif buffers as free */ + err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_tx_burst: %s", memif_strerror (err)); + DBG ("tx: %d/%u", r, c->tx_buf_num); + c->tx_buf_num -= r; + return 0; +} + +/* called when event is polled on interrupt file descriptor. + there are packets in shared memory ready to be received */ +int +on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid) +{ + long index = *((long *) private_ctx); + memif_connection_t *c = &memif_connection[index]; + if (c->index != index) + { + INFO ("invalid context: %ld/%u", index, c->index); + return 0; + } + int err; + uint16_t rx; + uint16_t fb; + /* receive data from shared memory buffers */ + err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_rx_burst: %s", memif_strerror (err)); + c->rx_buf_num += rx; + goto error; + } + c->rx_buf_num += rx; + + DBG ("received %d buffers. %u/%u alloc/free buffers", + rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + + if (icmpr_buffer_alloc (index, rx, qid) < 0) + { + INFO ("buffer_alloc error"); + goto error; + } + int i; + for (i = 0; i < rx; i++) + { + resolve_packet ((void *) (c->rx_bufs + i)->data, + (c->rx_bufs + i)->data_len, + (void *) (c->tx_bufs + i)->data, + &(c->tx_bufs + i)->data_len, c->ip_addr); + } + + /* mark memif buffers and shared memory buffers as free */ + err = memif_buffer_free (c->conn, qid, c->rx_bufs, rx, &fb); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_buffer_free: %s", memif_strerror (err)); + c->rx_buf_num -= fb; + + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + + icmpr_tx_burst (index, qid); + + return 0; + +error: + err = memif_buffer_free (c->conn, qid, c->rx_bufs, rx, &fb); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_buffer_free: %s", memif_strerror (err)); + c->rx_buf_num -= fb; + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + return 0; +} + +int +icmpr_memif_create (long index, long mode) +{ + if (index >= MAX_CONNS) + { + INFO ("connection array overflow"); + return 0; + } + if (index < 0) + { + INFO ("don't even try..."); + return 0; + } + memif_connection_t *c = &memif_connection[index]; + + /* setting memif connection arguments */ + memif_conn_args_t args; + int fd = -1; + memset (&args, 0, sizeof (args)); + args.is_master = mode; + args.log2_ring_size = 10; + args.buffer_size = 2048; + args.num_s2m_rings = 2; + args.num_m2s_rings = 2; + strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME)); + strncpy ((char *) args.instance_name, APP_NAME, strlen (APP_NAME)); + args.mode = 0; + /* socket filename is not specified, because this app is supposed to + connect to VPP over memif. so default socket filename will be used */ + /* default socketfile = /run/vpp/memif.sock */ + + args.interface_id = index; + /* last argument for memif_create (void * private_ctx) is used by user + to identify connection. this context is returned with callbacks */ + int err = memif_create (&c->conn, + &args, on_connect, on_disconnect, on_interrupt, + &ctx[index]); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_create: %s", memif_strerror (err)); + return 0; + } + + c->index = index; + /* alloc memif buffers */ + c->rx_buf_num = 0; + c->rx_bufs = + (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + c->tx_buf_num = 0; + c->tx_bufs = + (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + + c->ip_addr[0] = 192; + c->ip_addr[1] = 168; + c->ip_addr[2] = c->index + 1; + c->ip_addr[3] = 2; + return 0; +} + +int +icmpr_memif_delete (long index) +{ + if (index >= MAX_CONNS) + { + INFO ("connection array overflow"); + return 0; + } + if (index < 0) + { + INFO ("don't even try..."); + return 0; + } + memif_connection_t *c = &memif_connection[index]; + + if (c->rx_bufs) + free (c->rx_bufs); + c->rx_bufs = NULL; + c->rx_buf_num = 0; + if (c->tx_bufs) + free (c->tx_bufs); + c->tx_bufs = NULL; + c->tx_buf_num = 0; + + int err; + /* disconenct then delete memif connection */ + err = memif_delete (&c->conn); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_delete: %s", memif_strerror (err)); + if (c->conn != NULL) + INFO ("memif delete fail"); + return 0; +} + +void +print_help () +{ + printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME); +#ifdef ICMP_DBG + printf (" (debug)"); +#endif + printf ("\n"); + printf ("==============================\n"); + printf ("libmemif version: %s", LIBMEMIF_VERSION); +#ifdef MEMIF_DBG + printf (" (debug)"); +#endif + printf ("\n"); + printf ("memif version: %d\n", MEMIF_VERSION); + printf ("commands:\n"); + printf ("\thelp - prints this help\n"); + printf ("\texit - exit app\n"); + printf + ("\tconn <index> <mode> - create memif. index is also used as interface id, mode 0 = slave 1 = master\n"); + printf ("\tdel <index> - delete memif\n"); + printf ("\tshow - show connection details\n"); + printf ("\tip-set <index> <ip-addr> - set interface ip address\n"); + printf + ("\trx-mode <index> <qid> <polling|interrupt> - set queue rx mode\n"); +} + +int +icmpr_free () +{ + /* application cleanup */ + int err; + long i; + for (i = 0; i < MAX_CONNS; i++) + { + memif_connection_t *c = &memif_connection[i]; + if (c->conn) + icmpr_memif_delete (i); + } + + err = memif_cleanup (); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_delete: %s", memif_strerror (err)); + + return 0; +} + +int +icmpr_set_ip (long index, char *ip) +{ + if (index >= MAX_CONNS) + { + INFO ("connection array overflow"); + return 0; + } + if (index < 0) + { + INFO ("don't even try..."); + return 0; + } + memif_connection_t *c = &memif_connection[index]; + if (c->conn == NULL) + { + INFO ("no connection at index %ld", index); + return 0; + } + + char *end; + char *ui; + uint8_t tmp[4]; + ui = strtok (ip, "."); + if (ui == NULL) + goto error; + tmp[0] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + goto error; + tmp[1] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + goto error; + tmp[2] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + goto error; + tmp[3] = strtol (ui, &end, 10); + + c->ip_addr[0] = tmp[0]; + c->ip_addr[1] = tmp[1]; + c->ip_addr[2] = tmp[2]; + c->ip_addr[3] = tmp[3]; + + INFO ("memif %ld ip address set to %u.%u.%u.%u", + index, c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]); + + return 0; + +error: + INFO ("invalid ip address"); + return 0; +} + +int +icmpr_set_rx_mode (long index, long qid, char *mode) +{ + if (index >= MAX_CONNS) + { + INFO ("connection array overflow"); + return 0; + } + if (index < 0) + { + INFO ("don't even try..."); + return 0; + } + memif_connection_t *c = &memif_connection[index]; + + if (c->conn == NULL) + { + INFO ("no connection at index %ld", index); + return 0; + } + + if (strncmp (mode, "interrupt", 9) == 0) + { + memif_set_rx_mode (c->conn, MEMIF_RX_MODE_INTERRUPT, qid); + } + + else if (strncmp (mode, "polling", 7) == 0) + { + memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, qid); + } + else + INFO ("expected rx mode <interrupt|polling>"); + return 0; +} + +int +user_input_handler () +{ + int i; + char *in = (char *) malloc (256); + char *ui = fgets (in, 256, stdin); + char *end; + long a; + if (in[0] == '\n') + goto done; + ui = strtok (in, " "); + if (strncmp (ui, "exit", 4) == 0) + { + free (in); + icmpr_free (); + exit (EXIT_SUCCESS); + } + else if (strncmp (ui, "help", 4) == 0) + { + print_help (); + goto done; + } + else if (strncmp (ui, "conn", 4) == 0) + { + ui = strtok (NULL, " "); + if (ui != NULL) + a = strtol (ui, &end, 10); + else + { + INFO ("expected id"); + goto done; + } + ui = strtok (NULL, " "); + if (ui != NULL) + icmpr_memif_create (a, strtol (ui, &end, 10)); + else + INFO ("expected mode <0|1>"); + goto done; + } + else if (strncmp (ui, "del", 3) == 0) + { + ui = strtok (NULL, " "); + if (ui != NULL) + icmpr_memif_delete (strtol (ui, &end, 10)); + else + INFO ("expected id"); + goto done; + } + else if (strncmp (ui, "show", 4) == 0) + { + print_memif_details (); + goto done; + } + else if (strncmp (ui, "ip-set", 6) == 0) + { + ui = strtok (NULL, " "); + if (ui != NULL) + icmpr_set_ip (strtol (ui, &end, 10), strtok (NULL, " ")); + else + INFO ("expected id"); + goto done; + } + else if (strncmp (ui, "rx-mode", 7) == 0) + { + ui = strtok (NULL, " "); + if (ui != NULL) + a = strtol (ui, &end, 10); + else + { + INFO ("expected id"); + goto done; + } + ui = strtok (NULL, " "); + if (ui != NULL) + icmpr_set_rx_mode (a, strtol (ui, &end, 10), strtok (NULL, " ")); + else + INFO ("expected qid"); + goto done; + } + else + { + DBG ("unknown command: %s", ui); + goto done; + } + + return 0; +done: + free (in); + return 0; +} + +int +poll_event (int timeout) +{ + struct epoll_event evt, *e; + int app_err = 0, memif_err = 0, en = 0; + int tmp, nfd; + uint32_t events = 0; + memset (&evt, 0, sizeof (evt)); + evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset (&sigset); + en = epoll_pwait (epfd, &evt, 1, timeout, &sigset); + if (en < 0) + { + DBG ("epoll_pwait: %s", strerror (errno)); + return -1; + } + if (en > 0) + { + /* this app does not use any other file descriptors than stds and memif control fds */ + if (evt.data.fd > 2) + { + /* event of memif control fd */ + /* convert epolle events to memif events */ + if (evt.events & EPOLLIN) + events |= MEMIF_FD_EVENT_READ; + if (evt.events & EPOLLOUT) + events |= MEMIF_FD_EVENT_WRITE; + if (evt.events & EPOLLERR) + events |= MEMIF_FD_EVENT_ERROR; + memif_err = memif_control_fd_handler (evt.data.fd, events); + if (memif_err != MEMIF_ERR_SUCCESS) + INFO ("memif_control_fd_handler: %s", memif_strerror (memif_err)); + } + else if (evt.data.fd == 0) + { + app_err = user_input_handler (); + } + else + { + DBG ("unexpected event at memif_epfd. fd %d", evt.data.fd); + } + } + + if ((app_err < 0) || (memif_err < 0)) + { + if (app_err < 0) + DBG ("user input handler error"); + if (memif_err < 0) + DBG ("memif control fd handler error"); + return -1; + } + + return 0; +} + +int +main () +{ + epfd = epoll_create (1); + add_epoll_fd (0, EPOLLIN); + + /* initialize memory interface */ + int err, i; + /* if valid callback is passed as argument, fd event polling will be done by user + all file descriptors and events will be passed to user in this callback */ + /* if callback is set to NULL libmemif will handle fd event polling */ + err = memif_init (control_fd_update, APP_NAME); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_init: %s", memif_strerror (err)); + + for (i = 0; i < MAX_CONNS; i++) + { + memif_connection[i].conn = NULL; + ctx[i] = i; + } + + print_help (); + + /* main loop */ + while (1) + { + if (poll_event (-1) < 0) + { + DBG ("poll_event error!"); + } + } +} diff --git a/extras/libmemif/examples/icmp_responder-mt/main.c b/extras/libmemif/examples/icmp_responder-mt/main.c new file mode 100644 index 00000000000..860569bc20b --- /dev/null +++ b/extras/libmemif/examples/icmp_responder-mt/main.c @@ -0,0 +1,921 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include <stdlib.h> +#include <stdint.h> +#include <net/if.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#include <netdb.h> +#include <linux/ip.h> +#include <linux/icmp.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <netinet/if_ether.h> +#include <net/if_arp.h> +#include <asm/byteorder.h> +#include <byteswap.h> +#include <string.h> +#include <sys/epoll.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <pthread.h> + +#include <libmemif.h> +#include <icmp_proto.h> + +#define APP_NAME "ICMP_Responder" +#define IF_NAME "memif_connection" + + +#ifdef ICMP_DBG +#define DBG(...) do { \ + printf (APP_NAME":%s:%d: ", __func__, __LINE__); \ + printf (__VA_ARGS__); \ + printf ("\n"); \ + } while (0) +#else +#define DBG(...) +#endif + +#define INFO(...) do { \ + printf ("INFO: "__VA_ARGS__); \ + printf ("\n"); \ + } while (0) + +/* maximum tx/rx memif buffers */ +#define MAX_MEMIF_BUFS 256 +#define MAX_CONNS 50 +#define MAX_QUEUES 2 +#define MAX_THREADS ((MAX_CONNS) * (MAX_QUEUES)) + +int main_epfd; + +typedef struct +{ + /* thread id */ + uint8_t id; + /* memif connection index */ + uint16_t index; + /* id of queue to be handled by thread */ + uint8_t qid; + uint8_t isRunning; + + uint16_t rx_buf_num; + uint16_t tx_buf_num; + memif_buffer_t *rx_bufs; + memif_buffer_t *tx_bufs; +} memif_thread_data_t; + +typedef struct +{ + uint16_t index; + /* memif conenction handle */ + memif_conn_handle_t conn; + /* interface ip address */ + uint8_t ip_addr[4]; + /* inform pthread about connection termination */ + uint8_t pending_del; +} memif_connection_t; + +memif_connection_t memif_connection[MAX_CONNS]; +long ctx[MAX_CONNS]; + +/* thread data specific for each thread */ +memif_thread_data_t thread_data[MAX_THREADS]; +pthread_t thread[MAX_THREADS]; + +void +user_signal_handler (int sig) +{ + sig = sig; +} + +static void +print_memif_details () +{ + memif_details_t md; + ssize_t buflen; + char *buf; + int err, i, e, ti; + buflen = 2048; + buf = malloc (buflen); + printf ("MEMIF DETAILS\n"); + printf ("==============================\n"); + for (i = 0; i < MAX_CONNS; i++) + { + memif_connection_t *c = &memif_connection[i]; + + memset (&md, 0, sizeof (md)); + memset (buf, 0, buflen); + + err = memif_get_details (c->conn, &md, buf, buflen); + if (err != MEMIF_ERR_SUCCESS) + { + if (err != MEMIF_ERR_NOCONN) + INFO ("%s", memif_strerror (err)); + continue; + } + + printf ("interface index: %d\n", i); + + printf ("\tinterface ip: %u.%u.%u.%u\n", + c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]); + printf ("\tinterface name: %s\n", (char *) md.if_name); + printf ("\tapp name: %s\n", (char *) md.inst_name); + printf ("\tremote interface name: %s\n", (char *) md.remote_if_name); + printf ("\tremote app name: %s\n", (char *) md.remote_inst_name); + printf ("\tid: %u\n", md.id); + printf ("\tsecret: %s\n", (char *) md.secret); + printf ("\trole: "); + if (md.role) + printf ("slave\n"); + else + printf ("master\n"); + printf ("\tmode: "); + switch (md.mode) + { + case 0: + printf ("ethernet\n"); + break; + case 1: + printf ("ip\n"); + break; + case 2: + printf ("punt/inject\n"); + break; + default: + printf ("unknown\n"); + break; + } + printf ("\tsocket filename: %s\n", (char *) md.socket_filename); + printf ("\trx queues:\n"); + for (e = 0; e < md.rx_queues_num; e++) + { + ti = (i * MAX_QUEUES) + e; + printf ("\tqueue id: %u\n", md.rx_queues[e].qid); + printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size); + printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size); + printf ("\t\tthread id: %u\n", thread_data[ti].id); + printf ("\t\tthread connection index: %u\n", thread_data[ti].index); + printf ("\t\tthread running: "); + if (thread_data[ti].isRunning) + printf ("yes\n"); + else + printf ("no"); + } + printf ("\ttx queues:\n"); + for (e = 0; e < md.tx_queues_num; e++) + { + printf ("\tqueue id: %u\n", md.tx_queues[e].qid); + printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size); + printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size); + } + printf ("\tlink: "); + if (md.link_up_down) + printf ("up\n"); + else + printf ("down\n"); + } + free (buf); +} + +int +add_epoll_fd (int epfd, int fd, uint32_t events) +{ + if (fd < 0) + { + DBG ("invalid fd %d", fd); + return -1; + } + struct epoll_event evt; + memset (&evt, 0, sizeof (evt)); + evt.events = events; + evt.data.fd = fd; + if (epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &evt) < 0) + { + DBG ("epoll_ctl: %s fd %d", strerror (errno), fd); + return -1; + } + DBG ("fd %d added to epoll", fd); + return 0; +} + +int +mod_epoll_fd (int epfd, int fd, uint32_t events) +{ + if (fd < 0) + { + DBG ("invalid fd %d", fd); + return -1; + } + struct epoll_event evt; + memset (&evt, 0, sizeof (evt)); + evt.events = events; + evt.data.fd = fd; + if (epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &evt) < 0) + { + DBG ("epoll_ctl: %s fd %d", strerror (errno), fd); + return -1; + } + DBG ("fd %d moddified on epoll", fd); + return 0; +} + +int +del_epoll_fd (int epfd, int fd) +{ + if (fd < 0) + { + DBG ("invalid fd %d", fd); + return -1; + } + struct epoll_event evt; + memset (&evt, 0, sizeof (evt)); + if (epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &evt) < 0) + { + DBG ("epoll_ctl: %s fd %d", strerror (errno), fd); + return -1; + } + DBG ("fd %d removed from epoll", fd); + return 0; +} + +void * +memif_rx_poll (void *ptr) +{ + memif_thread_data_t *data = (memif_thread_data_t *) ptr; + memif_connection_t *c = &memif_connection[data->index]; + int err; + uint16_t rx = 0, tx = 0, fb = 0; + + data->rx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + data->tx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + data->rx_buf_num = 0; + data->tx_buf_num = 0; + + data->isRunning = 1; + INFO ("pthread id %u starts in polling mode", data->id); + + while (1) + { + if (c->pending_del) + goto close; + + /* receive data from shared memory buffers */ + err = + memif_rx_burst (c->conn, data->qid, data->rx_bufs, MAX_MEMIF_BUFS, + &rx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_rx_burst: %s", memif_strerror (err)); + data->rx_buf_num += rx; + goto error; + } + data->rx_buf_num += rx; + if (rx == 0) + { + continue; + } + + DBG ("thread id: %u", data->id); + + DBG ("received %d buffers. %u/%u alloc/free buffers", + rx, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num); + + err = + memif_buffer_alloc (c->conn, data->qid, data->tx_bufs, + data->rx_buf_num, &tx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_buffer_alloc: %s", memif_strerror (err)); + data->tx_buf_num += tx; + goto error; + } + data->tx_buf_num += tx; + DBG ("allocated %d/%d buffers, %u free buffers", + tx, data->rx_buf_num, MAX_MEMIF_BUFS - data->tx_buf_num); + + int i; + for (i = 0; i < rx; i++) + { + resolve_packet ((void *) (data->rx_bufs + i)->data, + (data->rx_bufs + i)->data_len, + (void *) (data->tx_bufs + i)->data, + &(data->tx_bufs + i)->data_len, c->ip_addr); + } + + /* mark memif buffers and shared memory buffers as free */ + err = memif_buffer_free (c->conn, data->qid, data->rx_bufs, rx, &fb); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_buffer_free: %s", memif_strerror (err)); + data->rx_buf_num -= fb; + + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num); + + err = + memif_tx_burst (c->conn, data->qid, data->tx_bufs, data->tx_buf_num, + &tx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_tx_burst: %s", memif_strerror (err)); + goto error; + } + DBG ("tx: %d/%u", tx, data->tx_buf_num); + data->tx_buf_num -= tx; + } + +error: + INFO ("thread %u error!", data->id); + goto close; + +close: + err = memif_buffer_free (c->conn, data->qid, data->rx_bufs, rx, &fb); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_buffer_free: %s", memif_strerror (err)); + data->rx_buf_num -= fb; + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num); + free (data->rx_bufs); + free (data->tx_bufs); + data->isRunning = 0; + INFO ("pthread id %u exit", data->id); + pthread_exit (NULL); +} + +void * +memif_rx_interrupt (void *ptr) +{ + memif_thread_data_t *data = (memif_thread_data_t *) ptr; + memif_connection_t *c = &memif_connection[data->index]; + int err; + uint16_t rx = 0, tx = 0, fb = 0; + struct epoll_event evt, *e; + int en = 0; + uint32_t events = 0; + sigset_t sigset; + + signal (SIGUSR1, user_signal_handler); + + data->rx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + data->tx_bufs = malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + data->rx_buf_num = 0; + data->tx_buf_num = 0; + + data->isRunning = 1; + INFO ("pthread id %u starts in interrupt mode", data->id); + int thread_epfd = epoll_create (1); + + /* get interrupt queue id */ + int fd = -1; + err = memif_get_queue_efd (c->conn, data->qid, &fd); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_get_queue_efd: %s", memif_strerror (err)); + goto error; + } + add_epoll_fd (thread_epfd, fd, EPOLLIN); + + while (1) + { + memset (&evt, 0, sizeof (evt)); + evt.events = EPOLLIN | EPOLLOUT; + sigemptyset (&sigset); + en = epoll_pwait (thread_epfd, &evt, 1, -1, &sigset); + if (en < 0) + { + if (errno == EINTR) + goto close; + DBG ("epoll_pwait: %s", strerror (errno)); + goto error; + } + else if (en > 0) + { + /* receive data from shared memory buffers */ + err = + memif_rx_burst (c->conn, data->qid, data->rx_bufs, MAX_MEMIF_BUFS, + &rx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_rx_burst: %s", memif_strerror (err)); + data->rx_buf_num += rx; + goto error; + } + data->rx_buf_num += rx; + if (rx == 0) + { + continue; + } + + DBG ("thread id: %u", data->id); + + DBG ("received %d buffers. %u/%u alloc/free buffers", + rx, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num); + + err = + memif_buffer_alloc (c->conn, data->qid, data->tx_bufs, + data->rx_buf_num, &tx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_buffer_alloc: %s", memif_strerror (err)); + data->tx_buf_num += tx; + goto error; + } + data->tx_buf_num += tx; + DBG ("allocated %d/%d buffers, %u free buffers", + tx, data->rx_buf_num, MAX_MEMIF_BUFS - data->tx_buf_num); + + int i; + for (i = 0; i < rx; i++) + { + resolve_packet ((void *) (data->rx_bufs + i)->data, + (data->rx_bufs + i)->data_len, + (void *) (data->tx_bufs + i)->data, + &(data->tx_bufs + i)->data_len, c->ip_addr); + } + + /* mark memif buffers and shared memory buffers as free */ + err = + memif_buffer_free (c->conn, data->qid, data->rx_bufs, rx, &fb); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_buffer_free: %s", memif_strerror (err)); + data->rx_buf_num -= fb; + + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num); + + err = + memif_tx_burst (c->conn, data->qid, data->tx_bufs, + data->tx_buf_num, &tx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_tx_burst: %s", memif_strerror (err)); + goto error; + } + DBG ("tx: %d/%u", tx, data->tx_buf_num); + data->tx_buf_num -= tx; + } + } + +error: + INFO ("thread %u error!", data->id); + goto close; + +close: + err = memif_buffer_free (c->conn, data->qid, data->rx_bufs, rx, &fb); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_buffer_free: %s", memif_strerror (err)); + data->rx_buf_num -= fb; + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, data->rx_buf_num, MAX_MEMIF_BUFS - data->rx_buf_num); + free (data->rx_bufs); + free (data->tx_bufs); + data->isRunning = 0; + INFO ("pthread id %u exit", data->id); + pthread_exit (NULL); + +} + +/* informs user about connected status. private_ctx is used by user to identify connection + (multiple connections WIP) */ +int +on_connect (memif_conn_handle_t conn, void *private_ctx) +{ + long index = (*(long *) private_ctx); + int err, i, ti; + INFO ("memif connected! index %ld", index); + memif_connection_t *c = &memif_connection[index]; + c->pending_del = 0; + + for (i = 0; i < MAX_QUEUES; i++) + { + err = memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, i); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_set_rx_mode: %s qid: %u", memif_strerror (err), i); + else + { + ti = (index * MAX_QUEUES) + i; + if (thread_data[ti].isRunning) + { + INFO ("thread id: %d already running!", ti); + continue; + } + thread_data[ti].index = index; + thread_data[ti].qid = i; + thread_data[ti].id = ti; + if ((i % 2) == 0) + pthread_create (&thread[ti], + NULL, memif_rx_poll, (void *) &thread_data[ti]); + else + pthread_create (&thread[ti], + NULL, memif_rx_interrupt, + (void *) &thread_data[ti]); + } + + } + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to identify connection + (multiple connections WIP) */ +int +on_disconnect (memif_conn_handle_t conn, void *private_ctx) +{ + void *ptr; + long index = (*(long *) private_ctx); + memif_connection_t *c = &memif_connection[index]; + int i, ti; + INFO ("memif disconnected!"); + /* inform thread in polling mode about memif disconenction */ + c->pending_del = 1; + for (i = 0; i < MAX_QUEUES; i++) + { + ti = (index * MAX_QUEUES) + i; + if (!thread_data[ti].isRunning) + continue; + if ((i % 2) != 0) + pthread_kill (thread[ti], SIGUSR1); /* interrupt thread in interrupt mode */ + pthread_join (thread[ti], &ptr); + } + return 0; +} + +/* user needs to watch new fd or stop watching fd that is about to be closed. + control fd will be modified during connection establishment to minimize CPU usage */ +int +control_fd_update (int fd, uint8_t events) +{ + /* convert memif event definitions to epoll events */ + if (events & MEMIF_FD_EVENT_DEL) + return del_epoll_fd (main_epfd, fd); + + uint32_t evt = 0; + if (events & MEMIF_FD_EVENT_READ) + evt |= EPOLLIN; + if (events & MEMIF_FD_EVENT_WRITE) + evt |= EPOLLOUT; + + if (events & MEMIF_FD_EVENT_MOD) + return mod_epoll_fd (main_epfd, fd, evt); + + return add_epoll_fd (main_epfd, fd, evt); +} + +int +icmpr_memif_create (long index) +{ + if (index >= MAX_CONNS) + { + INFO ("connection array overflow"); + return 0; + } + if (index < 0) + { + INFO ("don't even try..."); + return 0; + } + memif_connection_t *c = &memif_connection[index]; + + /* setting memif connection arguments */ + memif_conn_args_t args; + int fd = -1; + memset (&args, 0, sizeof (args)); + args.is_master = 0; + args.log2_ring_size = 10; + args.buffer_size = 2048; + args.num_s2m_rings = 2; + args.num_m2s_rings = 2; + strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME)); + strncpy ((char *) args.instance_name, APP_NAME, strlen (APP_NAME)); + args.mode = 0; + /* socket filename is not specified, because this app is supposed to + connect to VPP over memif. so default socket filename will be used */ + /* default socketfile = /run/vpp/memif.sock */ + + args.interface_id = index; + /* last argument for memif_create (void * private_ctx) is used by user + to identify connection. this context is returned with callbacks */ + int err = memif_create (&c->conn, + &args, on_connect, on_disconnect, NULL, + &ctx[index]); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_create: %s", memif_strerror (err)); + return 0; + } + + c->index = index; + + c->ip_addr[0] = 192; + c->ip_addr[1] = 168; + c->ip_addr[2] = c->index + 1; + c->ip_addr[3] = 2; + return 0; +} + +int +icmpr_memif_delete (long index) +{ + if (index >= MAX_CONNS) + { + INFO ("connection array overflow"); + return 0; + } + if (index < 0) + { + INFO ("don't even try..."); + return 0; + } + memif_connection_t *c = &memif_connection[index]; + + int err; + /* disconenct then delete memif connection */ + err = memif_delete (&c->conn); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_delete: %s", memif_strerror (err)); + return 0; +} + +void +print_help () +{ + printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME); +#ifdef ICMP_DBG + printf (" (debug)"); +#endif + printf ("\n"); + printf ("==============================\n"); + printf ("libmemif version: %s", LIBMEMIF_VERSION); +#ifdef MEMIF_DBG + printf (" (debug)"); +#endif + printf ("\n"); + printf ("memif version: %d\n", MEMIF_VERSION); + printf ("commands:\n"); + printf ("\thelp - prints this help\n"); + printf ("\texit - exit app\n"); + printf ("\tconn <index> - create memif (slave-mode)\n"); + printf ("\tdel <index> - delete memif\n"); + printf ("\tshow - show connection details\n"); + printf ("\tip-set <index> <ip-addr> - set interface ip address\n"); +} + +int +icmpr_free () +{ + /* application cleanup */ + int err; + long i; + for (i = 0; i < MAX_CONNS; i++) + { + memif_connection_t *c = &memif_connection[i]; + if (c->conn) + icmpr_memif_delete (i); + } + + err = memif_cleanup (); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_delete: %s", memif_strerror (err)); + + return 0; +} + +int +icmpr_set_ip (long index, char *ip) +{ + if (index >= MAX_CONNS) + { + INFO ("connection array overflow"); + return 0; + } + if (index < 0) + { + INFO ("don't even try..."); + return 0; + } + memif_connection_t *c = &memif_connection[index]; + if (c->conn == NULL) + { + INFO ("no connection at index %ld", index); + return 0; + } + + char *end; + char *ui; + uint8_t tmp[4]; + ui = strtok (ip, "."); + if (ui == NULL) + goto error; + tmp[0] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + goto error; + tmp[1] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + goto error; + tmp[2] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + goto error; + tmp[3] = strtol (ui, &end, 10); + + c->ip_addr[0] = tmp[0]; + c->ip_addr[1] = tmp[1]; + c->ip_addr[2] = tmp[2]; + c->ip_addr[3] = tmp[3]; + + INFO ("memif %ld ip address set to %u.%u.%u.%u", + index, c->ip_addr[0], c->ip_addr[1], c->ip_addr[2], c->ip_addr[3]); + + return 0; + +error: + INFO ("invalid ip address"); + return 0; +} + + +int +user_input_handler () +{ + int i; + char *in = (char *) malloc (256); + char *ui = fgets (in, 256, stdin); + char *end; + if (in[0] == '\n') + goto done; + ui = strtok (in, " "); + if (strncmp (ui, "exit", 4) == 0) + { + free (in); + icmpr_free (); + exit (EXIT_SUCCESS); + } + else if (strncmp (ui, "help", 4) == 0) + { + print_help (); + goto done; + } + else if (strncmp (ui, "conn", 4) == 0) + { + ui = strtok (NULL, " "); + if (ui != NULL) + icmpr_memif_create (strtol (ui, &end, 10)); + else + INFO ("expected id"); + goto done; + } + else if (strncmp (ui, "del", 3) == 0) + { + ui = strtok (NULL, " "); + if (ui != NULL) + icmpr_memif_delete (strtol (ui, &end, 10)); + else + INFO ("expected id"); + goto done; + } + else if (strncmp (ui, "show", 4) == 0) + { + print_memif_details (); + goto done; + } + else if (strncmp (ui, "ip-set", 6) == 0) + { + ui = strtok (NULL, " "); + if (ui != NULL) + icmpr_set_ip (strtol (ui, &end, 10), strtok (NULL, " ")); + else + INFO ("expected id"); + goto done; + } + else + { + DBG ("unknown command: %s", ui); + goto done; + } + + return 0; +done: + free (in); + return 0; +} + +int +poll_event (int timeout) +{ + struct epoll_event evt, *e; + int app_err = 0, memif_err = 0, en = 0; + int tmp, nfd; + uint32_t events = 0; + memset (&evt, 0, sizeof (evt)); + evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset (&sigset); + en = epoll_pwait (main_epfd, &evt, 1, timeout, &sigset); + if (en < 0) + { + DBG ("epoll_pwait: %s", strerror (errno)); + return -1; + } + if (en > 0) + { + /* this app does not use any other file descriptors than stds and memif control fds */ + if (evt.data.fd > 2) + { + /* event of memif control fd */ + /* convert epolle events to memif events */ + if (evt.events & EPOLLIN) + events |= MEMIF_FD_EVENT_READ; + if (evt.events & EPOLLOUT) + events |= MEMIF_FD_EVENT_WRITE; + if (evt.events & EPOLLERR) + events |= MEMIF_FD_EVENT_ERROR; + memif_err = memif_control_fd_handler (evt.data.fd, events); + if (memif_err != MEMIF_ERR_SUCCESS) + INFO ("memif_control_fd_handler: %s", memif_strerror (memif_err)); + } + else if (evt.data.fd == 0) + { + app_err = user_input_handler (); + } + else + { + DBG ("unexpected event at memif_epfd. fd %d", evt.data.fd); + } + } + + if ((app_err < 0) || (memif_err < 0)) + { + if (app_err < 0) + DBG ("user input handler error"); + if (memif_err < 0) + DBG ("memif control fd handler error"); + return -1; + } + + return 0; +} + +int +main () +{ + main_epfd = epoll_create (1); + add_epoll_fd (main_epfd, 0, EPOLLIN); + + /* initialize memory interface */ + int err, i; + /* if valid callback is passed as argument, fd event polling will be done by user + all file descriptors and events will be passed to user in this callback */ + /* if callback is set to NULL libmemif will handle fd event polling */ + err = memif_init (control_fd_update, APP_NAME); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_init: %s", memif_strerror (err)); + + for (i = 0; i < MAX_CONNS; i++) + { + memif_connection[i].conn = NULL; + ctx[i] = i; + } + + memset (&thread_data, 0, sizeof (memif_thread_data_t) * MAX_THREADS); + + print_help (); + + /* main loop */ + while (1) + { + if (poll_event (-1) < 0) + { + DBG ("poll_event error!"); + } + } +} diff --git a/extras/libmemif/examples/icmp_responder/icmp_proto.c b/extras/libmemif/examples/icmp_responder/icmp_proto.c new file mode 100644 index 00000000000..a923f209126 --- /dev/null +++ b/extras/libmemif/examples/icmp_responder/icmp_proto.c @@ -0,0 +1,246 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include <stdint.h> +#include <net/if.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#include <netdb.h> +#include <linux/ip.h> +#include <linux/icmp.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <netinet/if_ether.h> +#include <net/if_arp.h> +#include <asm/byteorder.h> +#include <byteswap.h> + +#include <icmp_proto.h> + +static uint16_t +cksum (void *addr, ssize_t len) +{ + char *data = (char *) addr; + + uint32_t acc = 0xffff; + + ssize_t i; + for (i = 0; (i + 1) < len; i += 2) + { + uint16_t word; + memcpy (&word, data + i, 2); + acc += ntohs (word); + if (acc > 0xffff) + acc -= 0xffff; + } + + if (len & 1) + { + uint16_t word = 0; + memcpy (&word, data + len - 1, 1); + acc += ntohs (word); + if (acc > 0xffff) + acc -= 0xffff; + } + return htons (~acc); +} + +int +print_packet (void *pck) +{ + if (pck == NULL) + { + printf ("ICMP_PROTO: no data\n"); + return -1; + } + struct iphdr *ip; + struct icmphdr *icmp; + ip = (struct iphdr *) pck; + icmp = (struct icmphdr *) (pck + sizeof (struct iphdr)); + printf ("received packet:\n"); + printf ("\tiphdr:\n"); + printf ("\t\tihl: %u\n\t\tversion: %u\n\t\tlen: %u\n\t\tid: %u\n", + ip->ihl, ip->version, __bswap_16 (ip->tot_len), ip->id); + printf ("\t\tprotocol: %u\n", ip->protocol); + + printf ("\t\tsaddr: "); + int i; + for (i = 0; i < 4; i++) + { + printf ("%u.", ((uint8_t *) & ip->saddr)[i]); + } + printf ("\n"); + + printf ("\t\tdaddr: "); + for (i = 0; i < 4; i++) + { + printf ("%u.", ((uint8_t *) & ip->daddr)[i]); + } + printf ("\n"); + printf ("\ticmphdr:\n"); + printf ("\t\ttype: %s\n", + (icmp->type == ICMP_ECHO) ? "ICMP_ECHO" : "ICMP_ECHOREPLY"); + + return 0; +} + +static ssize_t +resolve_arp (void *arp) +{ + struct arphdr *resp = (struct arphdr *) arp; + + resp->ar_hrd = __bswap_16 (ARPHRD_ETHER); + + resp->ar_pro = __bswap_16 (0x0800); + + resp->ar_hln = 6; + resp->ar_pln = 4; + + resp->ar_op = __bswap_16 (ARPOP_REPLY); + + return sizeof (struct arphdr); +} + +static ssize_t +resolve_eth_arp (struct ether_arp *eth_arp, void *eth_arp_resp, + uint8_t ip_addr[4]) +{ + struct ether_arp *resp = (struct ether_arp *) eth_arp_resp; + + resolve_arp (&resp->ea_hdr); + + memcpy (resp->arp_tha, eth_arp->arp_sha, 6); + memcpy (resp->arp_tpa, eth_arp->arp_spa, 4); + + memcpy (resp->arp_sha, + (((struct ether_header *) (eth_arp_resp - + sizeof (struct ether_header)))-> + ether_shost), 6); + + memcpy (resp->arp_spa, ip_addr, 4); + + return sizeof (struct ether_arp); +} + +static ssize_t +resolve_eth (struct ether_header *eth, void *eth_resp) +{ + struct ether_header *resp = (struct ether_header *) eth_resp; + memcpy (resp->ether_dhost, eth->ether_shost, 6); + + uint8_t hw_addr[6]; + int i; + for (i = 0; i < 6; i++) + { + hw_addr[i] = 'a'; + } + memcpy (resp->ether_shost, hw_addr, 6); + + resp->ether_type = eth->ether_type; + + return sizeof (struct ether_header); +} + +static ssize_t +resolve_ip (struct iphdr *ip, void *ip_resp, uint8_t ip_addr[4]) +{ + struct iphdr *resp = (struct iphdr *) ip_resp; + resp->ihl = 5; + resp->version = 4; + resp->tos = 0; + /*len updated later */ + resp->tot_len = 0x5400; + resp->id = 0; + resp->frag_off = 0; + resp->ttl = 0x40; + resp->protocol = 1; + ((uint8_t *) & resp->saddr)[0] = ip_addr[0]; + ((uint8_t *) & resp->saddr)[1] = ip_addr[1]; + ((uint8_t *) & resp->saddr)[2] = ip_addr[2]; + ((uint8_t *) & resp->saddr)[3] = ip_addr[3]; + resp->daddr = ip->saddr; + + resp->check = cksum (resp, sizeof (struct iphdr)); + + return sizeof (struct iphdr); +} + +static ssize_t +resolve_icmp (struct icmphdr *icmp, void *icmp_resp) +{ + struct icmphdr *resp = (struct icmphdr *) icmp_resp; + resp->type = ICMP_ECHOREPLY; + resp->code = 0; + resp->un.echo.id = icmp->un.echo.id; + resp->un.echo.sequence = icmp->un.echo.sequence; + + /*resp->checksum = cksum (resp, sizeof (struct icmphdr)); */ + + return sizeof (struct icmphdr); +} + +int +resolve_packet (void *in_pck, ssize_t in_size, + void *out_pck, uint32_t * out_size, uint8_t ip_addr[4]) +{ + struct ether_header *eh; + struct ether_arp *eah; + struct iphdr *ip; + struct icmphdr *icmp; + *out_size = 0; + + eh = (struct ether_header *) in_pck; + *out_size = resolve_eth (eh, out_pck); + + if (eh->ether_type == 0x0608) + { + eah = (struct ether_arp *) (in_pck + *out_size); + *out_size += resolve_eth_arp (eah, out_pck + *out_size, ip_addr); + + } + else if (eh->ether_type == 0x0008) + { +#ifdef ICMP_DBG + print_packet (in_pck + *out_size); +#endif + ip = (struct iphdr *) (in_pck + *out_size); + *out_size += resolve_ip (ip, out_pck + *out_size, ip_addr); + if (ip->protocol == 1) + { + icmp = (struct icmphdr *) (in_pck + *out_size); + *out_size += resolve_icmp (icmp, out_pck + *out_size); + ((struct icmphdr *) (out_pck + *out_size - + sizeof (struct icmphdr)))->checksum = + cksum (out_pck + *out_size - sizeof (struct icmphdr), + sizeof (struct icmphdr)); + /* payload */ + memcpy (out_pck + *out_size, in_pck + *out_size, + in_size - *out_size); + *out_size = in_size; + } + } + return 0; +} diff --git a/extras/libmemif/examples/icmp_responder/icmp_proto.h b/extras/libmemif/examples/icmp_responder/icmp_proto.h new file mode 100644 index 00000000000..f2f22ac4629 --- /dev/null +++ b/extras/libmemif/examples/icmp_responder/icmp_proto.h @@ -0,0 +1,26 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#ifndef _ICMP_PROTO_H_ +#define _ICMP_PROTO_H_ + +int resolve_packet (void *in_pck, ssize_t in_size, void *out_pck, + uint32_t * out_size, uint8_t ip_addr[4]); + +int print_packet (void *pck); + +#endif /* _ICMP_PROTO_H_ */ diff --git a/extras/libmemif/examples/icmp_responder/main.c b/extras/libmemif/examples/icmp_responder/main.c new file mode 100644 index 00000000000..5351b6b8e3c --- /dev/null +++ b/extras/libmemif/examples/icmp_responder/main.c @@ -0,0 +1,416 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include <stdlib.h> +#include <stdint.h> +#include <net/if.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#include <netdb.h> +#include <linux/ip.h> +#include <linux/icmp.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <netinet/if_ether.h> +#include <net/if_arp.h> +#include <asm/byteorder.h> +#include <byteswap.h> +#include <string.h> +#include <sys/epoll.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> + +#include <libmemif.h> +#include <icmp_proto.h> + +#define APP_NAME "ICMP_Responder" +#define IF_NAME "memif_connection" + + +#ifdef ICMP_DBG +#define DBG(...) do { \ + printf (APP_NAME":%s:%d: ", __func__, __LINE__); \ + printf (__VA_ARGS__); \ + printf ("\n"); \ + } while (0) +#else +#define DBG(...) +#endif + +#define INFO(...) do { \ + printf ("INFO: "__VA_ARGS__); \ + printf ("\n"); \ + } while (0) + +/* maximum tx/rx memif buffers */ +#define MAX_MEMIF_BUFS 256 + +typedef struct +{ + uint16_t index; + /* memif conenction handle */ + memif_conn_handle_t conn; + /* transmit queue id */ + uint16_t tx_qid; + /* tx buffers */ + memif_buffer_t *tx_bufs; + /* allocated tx buffers counter */ + /* number of tx buffers pointing to shared memory */ + uint16_t tx_buf_num; + /* rx buffers */ + memif_buffer_t *rx_bufs; + /* allcoated rx buffers counter */ + /* number of rx buffers pointing to shared memory */ + uint16_t rx_buf_num; + /* interface ip address */ + uint8_t ip_addr[4]; +} memif_connection_t; + +memif_connection_t memif_connection; +int epfd; + +static void +print_memif_details () +{ + memif_connection_t *c = &memif_connection; + printf ("MEMIF DETAILS\n"); + printf ("==============================\n"); + + + memif_details_t md; + memset (&md, 0, sizeof (md)); + ssize_t buflen = 2048; + char *buf = malloc (buflen); + memset (buf, 0, buflen); + int err, e; + + err = memif_get_details (c->conn, &md, buf, buflen); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("%s", memif_strerror (err)); + if (err == MEMIF_ERR_NOCONN) + { + free (buf); + return; + } + } + + printf ("\tinterface name: %s\n", (char *) md.if_name); + printf ("\tapp name: %s\n", (char *) md.inst_name); + printf ("\tremote interface name: %s\n", (char *) md.remote_if_name); + printf ("\tremote app name: %s\n", (char *) md.remote_inst_name); + printf ("\tid: %u\n", md.id); + printf ("\tsecret: %s\n", (char *) md.secret); + printf ("\trole: "); + if (md.role) + printf ("slave\n"); + else + printf ("master\n"); + printf ("\tmode: "); + switch (md.mode) + { + case 0: + printf ("ethernet\n"); + break; + case 1: + printf ("ip\n"); + break; + case 2: + printf ("punt/inject\n"); + break; + default: + printf ("unknown\n"); + break; + } + printf ("\tsocket filename: %s\n", (char *) md.socket_filename); + printf ("\tsocket filename: %s\n", (char *) md.socket_filename); + printf ("\trx queues:\n"); + for (e = 0; e < md.rx_queues_num; e++) + { + printf ("\t\tqueue id: %u\n", md.rx_queues[e].qid); + printf ("\t\tring size: %u\n", md.rx_queues[e].ring_size); + printf ("\t\tbuffer size: %u\n", md.rx_queues[e].buffer_size); + } + printf ("\ttx queues:\n"); + for (e = 0; e < md.tx_queues_num; e++) + { + printf ("\t\tqueue id: %u\n", md.tx_queues[e].qid); + printf ("\t\tring size: %u\n", md.tx_queues[e].ring_size); + printf ("\t\tbuffer size: %u\n", md.tx_queues[e].buffer_size); + } + printf ("\tlink: "); + if (md.link_up_down) + printf ("up\n"); + else + printf ("down\n"); + + free (buf); +} + +/* informs user about connected status. private_ctx is used by user to identify connection + (multiple connections WIP) */ +int +on_connect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif connected!"); + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to identify connection + (multiple connections WIP) */ +int +on_disconnect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif disconnected!"); + return 0; +} + +int +icmpr_memif_delete () +{ + int err; + /* disconenct then delete memif connection */ + err = memif_delete (&(&memif_connection)->conn); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_delete: %s", memif_strerror (err)); + return 0; +} + +void +print_help () +{ + printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME); +#ifdef ICMP_DBG + printf (" (debug)"); +#endif + printf ("\n"); + printf ("==============================\n"); + printf ("libmemif version: %s", LIBMEMIF_VERSION); +#ifdef MEMIF_DBG + printf (" (debug)"); +#endif + printf ("\n"); + printf ("memif version: %d\n", MEMIF_VERSION); + printf ("\tuse CTRL+C to exit\n"); +} + +int +icmpr_buffer_alloc (long n, uint16_t qid) +{ + memif_connection_t *c = &memif_connection; + int err; + uint16_t r; + /* set data pointer to shared memory and set buffer_len to shared mmeory buffer len */ + err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, n, &r); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_buffer_alloc: %s", memif_strerror (err)); + c->tx_buf_num += r; + return -1; + } + c->tx_buf_num += r; + DBG ("allocated %d/%ld buffers, %u free buffers", r, n, + MAX_MEMIF_BUFS - c->tx_buf_num); + return 0; +} + +int +icmpr_tx_burst (uint16_t qid) +{ + memif_connection_t *c = &memif_connection; + int err; + uint16_t r; + /* inform peer memif interface about data in shared memory buffers */ + /* mark memif buffers as free */ + err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_tx_burst: %s", memif_strerror (err)); + DBG ("tx: %d/%u", r, c->tx_buf_num); + c->tx_buf_num -= r; + return 0; +} + +int +icmpr_free () +{ + /* application cleanup */ + int err; + memif_connection_t *c = &memif_connection; + free (c->tx_bufs); + c->tx_bufs = NULL; + free (c->rx_bufs); + c->rx_bufs = NULL; + + err = memif_cleanup (); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_delete: %s", memif_strerror (err)); + + return 0; +} + +void +icmpr_exit (int sig) +{ + printf ("\n"); + icmpr_memif_delete (); + icmpr_free (); + exit (EXIT_SUCCESS); +} + +/* called when event is polled on interrupt file descriptor. + there are packets in shared memory ready to be received */ +int +on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid) +{ + DBG ("interrupted"); + memif_connection_t *c = &memif_connection; + int err; + uint16_t rx; + /* receive data from shared memory buffers */ + err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); + c->rx_buf_num += rx; + + DBG ("received %d buffers. %u/%u alloc/free buffers", + rx, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + + if (icmpr_buffer_alloc (rx, c->tx_qid) < 0) + { + INFO ("buffer_alloc error"); + goto error; + } + int i; + for (i = 0; i < rx; i++) + { + resolve_packet ((void *) (c->rx_bufs + i)->data, + (c->rx_bufs + i)->data_len, + (void *) (c->tx_bufs + i)->data, + &(c->tx_bufs + i)->data_len, c->ip_addr); + } + + uint16_t fb; + /* mark memif buffers and shared memory buffers as free */ + err = memif_buffer_free (c->conn, qid, c->rx_bufs, rx, &fb); + c->rx_buf_num -= fb; + + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + + icmpr_tx_burst (c->tx_qid); + + return 0; + +error: + err = memif_buffer_free (c->conn, qid, c->rx_bufs, rx, &fb); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_buffer_free: %s", memif_strerror (err)); + c->rx_buf_num -= fb; + DBG ("freed %d buffers. %u/%u alloc/free buffers", + fb, c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + return 0; +} + +int +icmpr_memif_create (int is_master) +{ + /* setting memif connection arguments */ + memif_conn_args_t args; + int fd = -1; + memset (&args, 0, sizeof (args)); + args.is_master = is_master; + args.log2_ring_size = 10; + args.buffer_size = 2048; + args.num_s2m_rings = 2; + args.num_m2s_rings = 2; + strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME)); + strncpy ((char *) args.instance_name, APP_NAME, strlen (APP_NAME)); + args.mode = 0; + /* socket filename is not specified, because this app is supposed to + connect to VPP over memif. so default socket filename will be used */ + /* default socketfile = /run/vpp/memif.sock */ + + args.interface_id = 0; + /* last argument for memif_create (void * private_ctx) is used by user + to identify connection. this context is returned with callbacks */ + int err = memif_create (&(&memif_connection)->conn, + &args, on_connect, on_disconnect, on_interrupt, + NULL); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_create: %s", memif_strerror (err)); + return 0; +} + +int +main (int argc, char *argv[]) +{ + memif_connection_t *c = &memif_connection; + + signal (SIGINT, icmpr_exit); + + /* initialize global memif connection handle */ + c->conn = NULL; + if (argc == 1) + c->tx_qid = 0; + else + { + char *end; + c->tx_qid = strtol (argv[1], &end, 10); + } + INFO ("tx qid: %u", c->tx_qid); + /* alloc memif buffers */ + c->rx_buf_num = 0; + c->rx_bufs = + (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + c->tx_buf_num = 0; + c->tx_bufs = + (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + c->ip_addr[0] = 192; + c->ip_addr[1] = 168; + c->ip_addr[2] = 1; + c->ip_addr[3] = 2; + /* initialize memory interface */ + int err; + /* if valid callback is passed as argument, fd event polling will be done by user + all file descriptors and events will be passed to user in this callback */ + /* if callback is set to NULL libmemif will handle fd event polling */ + err = memif_init (NULL, APP_NAME); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_init: %s", memif_strerror (err)); + + print_help (); + + icmpr_memif_create (0); + print_memif_details (); + + /* main loop */ + while (1) + { + if (memif_poll_event (-1) < 0) + { + DBG ("poll_event error!"); + } + } +} |