From e74c04fc9fb2600470fe79a69d3ec6b0db95faec Mon Sep 17 00:00:00 2001 From: Jakub Grajciar Date: Mon, 4 Jan 2021 11:28:33 +0100 Subject: [PATCH] libmemif: refactor examples - icmp_responder: responds to ICMPv4 and ARP requests - loopback: connects two interfaces and sends a verification packet from master memif to slave memif where it is looped back - loopback (reverse path): reverses direction of packet in loopback application (slave memif to master memif) Type: refactor Signed-off-by: Jakub Grajciar Change-Id: Ie90aaa3367269408efb6c5d538ad5aa827432238 Signed-off-by: Dave Wallace --- docs/Makefile | 2 - docs/conf.py | 2 +- docs/interfacing/libmemif/example_setup_doc.rst | 1 - docs/interfacing/libmemif/index.rst | 3 +- extras/libmemif/dockerfile | 10 +- extras/libmemif/docs/buildinstructions_doc.rst | 74 ++-- extras/libmemif/examples/CMakeLists.txt | 15 +- extras/libmemif/examples/common/common.c | 192 +++++++++ extras/libmemif/examples/common/common.h | 116 +++++ extras/libmemif/examples/common/icmp_proto.c | 520 +++++++++++++++++++++++ extras/libmemif/examples/common/icmp_proto.h | 48 +++ extras/libmemif/examples/common/packet_handler.c | 65 +++ extras/libmemif/examples/common/responder.c | 172 ++++++++ extras/libmemif/examples/common/sender.c | 55 +++ extras/libmemif/examples/example_setup_doc.rst | 248 ----------- extras/libmemif/examples/examples_doc.rst | 101 +++-- extras/libmemif/examples/icmp_responder/main.c | 241 +++++++++++ extras/libmemif/examples/loopback/main.c | 307 +++++++++++++ extras/libmemif/libmemif_doc.rst | 5 +- 19 files changed, 1843 insertions(+), 334 deletions(-) delete mode 120000 docs/interfacing/libmemif/example_setup_doc.rst create mode 100644 extras/libmemif/examples/common/common.c create mode 100644 extras/libmemif/examples/common/common.h create mode 100644 extras/libmemif/examples/common/icmp_proto.c create mode 100644 extras/libmemif/examples/common/icmp_proto.h create mode 100644 extras/libmemif/examples/common/packet_handler.c create mode 100644 extras/libmemif/examples/common/responder.c create mode 100644 extras/libmemif/examples/common/sender.c delete mode 100644 extras/libmemif/examples/example_setup_doc.rst create mode 100644 extras/libmemif/examples/icmp_responder/main.c create mode 100644 extras/libmemif/examples/loopback/main.c diff --git a/docs/Makefile b/docs/Makefile index 8ccc8187f89..71ee034b331 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -67,8 +67,6 @@ ifeq ($(OS_ID),ubuntu) sudo apt-get update; \ sudo apt-get $(CONFIRM) $(FORCE) install $$inst; \ fi -else ifneq ("$(wildcard /etc/redhat-release)","") - @sudo yum install $(CONFIRM) $(DOC_RPM_DEPENDS) endif .PHONY: spell diff --git a/docs/conf.py b/docs/conf.py index ed4de0e7d72..26ccb3f0f7c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ import subprocess # -- Project information ----------------------------------------------------- project = u'The Vector Packet Processor' -copyright = u'2018-2021, Linux Foundation' +copyright = u'2018-2022, Linux Foundation' author = u'FD.io VPP Community' # The short X.Y version diff --git a/docs/interfacing/libmemif/example_setup_doc.rst b/docs/interfacing/libmemif/example_setup_doc.rst deleted file mode 120000 index 79e7dcfd672..00000000000 --- a/docs/interfacing/libmemif/example_setup_doc.rst +++ /dev/null @@ -1 +0,0 @@ -../../../extras/libmemif/examples/example_setup_doc.rst \ No newline at end of file diff --git a/docs/interfacing/libmemif/index.rst b/docs/interfacing/libmemif/index.rst index 4d4b24f36ce..41beb1da0ff 100644 --- a/docs/interfacing/libmemif/index.rst +++ b/docs/interfacing/libmemif/index.rst @@ -19,6 +19,5 @@ packets using libmemif API. libmemif_doc buildinstructions_doc - example_setup_doc - examples_doc gettingstarted_doc + examples_doc diff --git a/extras/libmemif/dockerfile b/extras/libmemif/dockerfile index 81012b0d5fd..c80ca5bdad6 100644 --- a/extras/libmemif/dockerfile +++ b/extras/libmemif/dockerfile @@ -1,20 +1,20 @@ FROM ubuntu:xenial RUN apt-get update && \ - apt-get install -y git build-essential autoconf pkg-config libtool sudo check + apt-get install -y git build-essential autoconf pkg-config libtool sudo RUN rm -rf /var/lib/apt/lists/* RUN mkdir /libmemif ADD . /libmemif WORKDIR /libmemif -RUN ./bootstrap -RUN ./configure +RUN mkdir build +RUN cmake .. +WORKDIR /libmemif/build RUN make -RUN make install RUN mkdir /run/vpp RUN ulimit -c unlimited -CMD ./.libs/icmpr-epoll +CMD ./examples/icmp_responder diff --git a/extras/libmemif/docs/buildinstructions_doc.rst b/extras/libmemif/docs/buildinstructions_doc.rst index 090b9d6ff75..f7770fc7fa4 100644 --- a/extras/libmemif/docs/buildinstructions_doc.rst +++ b/extras/libmemif/docs/buildinstructions_doc.rst @@ -8,54 +8,50 @@ Install dependencies :: - # sudo apt-get install -y git cmake autoconf pkg_config libtool + sudo apt-get install -y git cmake autoconf pkg_config libtool Libmemif is now part of VPP repository. Follow fd.io wiki to pull source code from VPP repository. https://wiki.fd.io/view/VPP/Pulling,_Building,_Running,_Hacking_and_Pushing_VPP_Code#Pushing_Patches -Libmemif is located under extras/libmemif. From extras/libmemif: +Libmemif is located under extras/libmemif. From the vpp workspace root directory:: -:: - - # mkdir build - # cd build - # cmake .. - # make install + mkdir -p extras/libmemif/build + cd extras/libmemif/build + cmake .. + make install Verify installation: -------------------- :: - build# ./examples/icmp_responder -? - -Use ``-?`` flag to display help: - -:: - - LIBMEMIF EXAMPLE APP: icmp_responder_example - ============================== - libmemif version: 4.0, memif version: 2.0 - ============================== - In this example, memif endpoint connects to an external application. - The example application can resolve ARP and reply to ICMPv4 packets. - The program will exit once the interface is disconnected. - ============================== - Usage: icmp_responder [OPTIONS] - - Options: - -r Interface role . Default: slave - -s Socket path. Supports abstract socket using @ before the path. Default: /run/vpp/memif.sock - -i Interface id. Default: 0 - -a IPv4 address. Default: 192.168.1.1 - -h Mac address. Default: aa:aa:aa:aa:aa:aa - -? Show help and exit. - -v Show libmemif and memif version information and exit. - -Examples --------- - -Once the library is built/installed, refer to :ref:`libmemif_examples_doc` -and :ref:`libmemif_gettingstarted_doc` for additional information on basic -use cases and API usage. + ./examples/icmp_responder -? + +Use ``-?`` flag to display help:: + + LIBMEMIF EXAMPLE APP: icmp_responder_example + ============================== + libmemif version: 4.0, memif version: 2.0 + ============================== + In this example, memif endpoint connects to an external application. + The example application can resolve ARP and reply to ICMPv4 packets. + The program will exit once the interface is disconnected. + ============================== + Usage: icmp_responder [OPTIONS] + + Options: + -r Interface role . Default: slave + -s Socket path. Supports abstract socket using @ before the path. Default: /run/vpp/memif.sock + -i Interface id. Default: 0 + -a IPv4 address. Default: 192.168.1.1 + -h Mac address. Default: aa:aa:aa:aa:aa:aa + -? Show help and exit. + -v Show libmemif and memif version information and exit. + +Use Cases +--------- + +Once the library is built/installed, refer to :ref:`libmemif_gettingstarted_doc` +and :ref:`libmemif_examples_doc` for additional information on basic use cases +and API usage. diff --git a/extras/libmemif/examples/CMakeLists.txt b/extras/libmemif/examples/CMakeLists.txt index 6adacaa3392..f6cfb882805 100644 --- a/extras/libmemif/examples/CMakeLists.txt +++ b/extras/libmemif/examples/CMakeLists.txt @@ -13,20 +13,25 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) -set(HEADERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(COMMON_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/common) set(COMMON_SOURCE_FILES - + common/common.c + common/sender.c + common/responder.c + common/packet_handler.c + common/icmp_proto.c ) list(APPEND EXAMPLES_LIST - + loopback/main.c + icmp_responder/main.c ) foreach (EXAMPLE_SRC ${EXAMPLES_LIST}) string(FIND ${EXAMPLE_SRC} "/" INDEX) string(SUBSTRING ${EXAMPLE_SRC} 0 ${INDEX} EXECUTABLE) - add_executable(${EXECUTABLE} ${COMMON_SOURCE_FILES} ${EXAMPLE_SRC}) - target_include_directories(${EXECUTABLE} PRIVATE $) + add_executable(${EXECUTABLE} ${COMMON_SOURCE_FILES} ${EXAMPLE_SRC} ) + target_include_directories(${EXECUTABLE} PRIVATE $) target_link_libraries(${EXECUTABLE} memif ${CMAKE_THREAD_LIBS_INIT}) endforeach() diff --git a/extras/libmemif/examples/common/common.c b/extras/libmemif/examples/common/common.c new file mode 100644 index 00000000000..5af42eaf63b --- /dev/null +++ b/extras/libmemif/examples/common/common.c @@ -0,0 +1,192 @@ +#include + +void +print_memif_ring_details (memif_connection_t *c, uint16_t qid, uint8_t is_rx) +{ + /* TODO: print memif shared memory details */ +} + +void +print_memif_rx_ring_details (memif_connection_t *c, uint16_t qid) +{ + print_memif_ring_details (c, qid, /* RX */ 1); +} + +void +print_memif_tx_ring_details (memif_connection_t *c, uint16_t qid) +{ + print_memif_ring_details (c, qid, /* TX */ 0); +} + +void +print_version () +{ + printf ("libmemif version: %s, memif version: %s\n", LIBMEMIF_VERSION, + memif_get_version_str ()); +} + +int +parse_ip4 (const char *input, uint8_t out[4]) +{ + char *ui, *end; + char *tmp = strdup (input); + + ui = strtok (tmp, "."); + if (ui == NULL) + return -1; + out[0] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + return -1; + out[1] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + return -1; + out[2] = strtol (ui, &end, 10); + + ui = strtok (NULL, "."); + if (ui == NULL) + return -1; + out[3] = strtol (ui, &end, 10); + + free (tmp); + + return 0; +} + +int +parse_mac (const char *input, uint8_t out[6]) +{ + char *ui, *end; + char *tmp = strdup (input); + + ui = strtok (tmp, ":"); + if (ui == NULL) + return -1; + out[0] = strtol (ui, &end, 16); + ui = strtok (NULL, ":"); + if (ui == NULL) + return -1; + out[1] = strtol (ui, &end, 16); + ui = strtok (NULL, ":"); + if (ui == NULL) + return -1; + out[2] = strtol (ui, &end, 16); + ui = strtok (NULL, ":"); + if (ui == NULL) + return -1; + out[3] = strtol (ui, &end, 16); + ui = strtok (NULL, ":"); + if (ui == NULL) + return -1; + out[4] = strtol (ui, &end, 16); + ui = strtok (NULL, ":"); + if (ui == NULL) + return -1; + out[5] = strtol (ui, &end, 16); + + free (tmp); + + return 0; +} + +void +alloc_memif_buffers (memif_connection_t *c) +{ + c->rx_bufs = + (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + c->rx_buf_num = 0; + c->tx_bufs = + (memif_buffer_t *) malloc (sizeof (memif_buffer_t) * MAX_MEMIF_BUFS); + c->tx_buf_num = 0; +} + +void +free_memif_buffers (memif_connection_t *c) +{ + if (c->rx_bufs != NULL) + free (c->rx_bufs); + c->rx_bufs = NULL; + c->rx_buf_num = 0; + if (c->tx_bufs != NULL) + free (c->tx_bufs); + c->tx_bufs = NULL; + c->tx_buf_num = 0; +} + +void +print_memif_details (memif_connection_t *c) +{ + printf ("MEMIF DETAILS\n"); + printf ("==============================\n"); + + memif_details_t md; + memset (&md, 0, sizeof (md)); + ssize_t buflen = 2048; + char *buf = (char *) 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 path: %s\n", (char *) md.socket_path); + 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); +} diff --git a/extras/libmemif/examples/common/common.h b/extras/libmemif/examples/common/common.h new file mode 100644 index 00000000000..ce4ead2d253 --- /dev/null +++ b/extras/libmemif/examples/common/common.h @@ -0,0 +1,116 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2020 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 _COMMON_H_ +#define _COMMON_H_ + +#include +#include +#include +#include +#include + +#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 + +struct memif_connection; + +typedef int (memif_packet_handler_t) (struct memif_connection *conn); + +typedef int (packet_generator_t) (struct memif_connection *c, + uint16_t num_pkts); + +typedef struct memif_connection +{ + uint16_t index; + /* memif conenction handle */ + memif_conn_handle_t conn; + uint8_t is_connected; + /* 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; + memif_packet_handler_t *packet_handler; + /* interface ip address */ + uint8_t ip_addr[4]; + /* interface hw address */ + uint8_t hw_addr[6]; +} memif_connection_t; + +void print_version (); + +int parse_ip4 (const char *input, uint8_t out[4]); + +int parse_mac (const char *input, uint8_t out[6]); + +void alloc_memif_buffers (memif_connection_t *c); + +void free_memif_buffers (memif_connection_t *c); + +void print_memif_details (memif_connection_t *c); + +void print_memif_rx_ring_details (memif_connection_t *c, uint16_t qid); + +void print_memif_tx_ring_details (memif_connection_t *c, uint16_t qid); + +int send_packets (memif_connection_t *conn, uint16_t qid, + packet_generator_t *gen, uint32_t num_pkts, + uint16_t max_pkt_size); + +/* Expect packets smaller than 2048b */ +int responder (memif_conn_handle_t conn, void *private_ctx, uint16_t qid); + +/* Expect packets smaller than 2048b */ +int responder_zero_copy (memif_conn_handle_t conn, void *private_ctx, + uint16_t qid); + +/* reply with the same data */ +int basic_packet_handler (memif_connection_t *conn); + +/* ICMPv4 and ARP handler */ +int icmp_packet_handler (memif_connection_t *conn); + +#endif /* COMMON_H */ \ No newline at end of file diff --git a/extras/libmemif/examples/common/icmp_proto.c b/extras/libmemif/examples/common/icmp_proto.c new file mode 100644 index 00000000000..fafc8e425b9 --- /dev/null +++ b/extras/libmemif/examples/common/icmp_proto.c @@ -0,0 +1,520 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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, uint8_t hw_addr[6]) +{ + struct ether_header *resp = (struct ether_header *) eth_resp; + memcpy (resp->ether_dhost, eth->ether_shost, 6); + + 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 = 0x0000; + 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 = 0x00; + 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], uint8_t hw_addr[6]) +{ + struct ether_header *eh; + struct ether_arp *eah; + struct iphdr *ip, *ip_out; + struct icmphdr *icmp; + *out_size = 0; + + if ((in_pck == NULL) || (out_pck == NULL)) + return -1; + + eh = (struct ether_header *) in_pck; + *out_size = resolve_eth (eh, out_pck, hw_addr); + + 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); + ip_out = (struct iphdr *) (out_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; + ip_out->tot_len = + __bswap_16 (*out_size - sizeof (struct ether_header)); + ip_out->check = cksum (ip_out, sizeof (struct iphdr)); + } + } + return 0; +} + +static ssize_t +generate_eth (struct ether_header *eh, uint8_t hw_daddr[6]) +{ + uint8_t hw_addr[6]; + int i; + for (i = 0; i < 6; i++) + { + hw_addr[i] = 'a'; + } + memcpy (eh->ether_shost, hw_addr, 6); + memcpy (eh->ether_dhost, hw_daddr, 6); + + eh->ether_type = 0x0008; + + return sizeof (struct ether_header); +} + +static ssize_t +generate_ip (struct iphdr *ip, uint8_t saddr[4], uint8_t daddr[4]) +{ + ip->ihl = 5; + ip->version = 4; + ip->tos = 0; + /*len updated later */ + ip->tot_len = 0x5400; + ip->id = 0; + ip->frag_off = 0; + ip->ttl = 0x40; + ip->protocol = 1; + /* saddr */ + ((uint8_t *) &ip->saddr)[0] = saddr[0]; + ((uint8_t *) &ip->saddr)[1] = saddr[1]; + ((uint8_t *) &ip->saddr)[2] = saddr[2]; + ((uint8_t *) &ip->saddr)[3] = saddr[3]; + /* daddr */ + ((uint8_t *) &ip->daddr)[0] = daddr[0]; + ((uint8_t *) &ip->daddr)[1] = daddr[1]; + ((uint8_t *) &ip->daddr)[2] = daddr[2]; + ((uint8_t *) &ip->daddr)[3] = daddr[3]; + + ip->check = cksum (ip, sizeof (struct iphdr)); + + return sizeof (struct iphdr); +} + +static ssize_t +generate_icmp (struct icmphdr *icmp, uint32_t seq) +{ + icmp->type = ICMP_ECHO; + icmp->code = 0; + icmp->un.echo.id = 0; + icmp->un.echo.sequence = seq; + + return sizeof (struct icmphdr); +} + +int +generate_packet (void *pck, uint32_t *size, uint8_t saddr[4], uint8_t daddr[4], + uint8_t hw_daddr[6], uint32_t seq) +{ + struct ether_header *eh; + struct iphdr *ip; + struct icmphdr *icmp; + + *size = 0; + + eh = (struct ether_header *) pck; + *size += generate_eth (eh, hw_daddr); + + ip = (struct iphdr *) (pck + *size); + *size += generate_ip (ip, saddr, daddr); + + icmp = (struct icmphdr *) (pck + *size); + *size += generate_icmp (icmp, seq); + + ((struct icmphdr *) (pck + *size - sizeof (struct icmphdr)))->checksum = + cksum (pck + *size - sizeof (struct icmphdr), sizeof (struct icmphdr)); + + ip->tot_len = __bswap_16 (*size - sizeof (struct ether_header)); + ip->check = 0; + ip->check = cksum (ip, sizeof (struct iphdr)); + + return 0; +} + +int +generate_packet2 (void *pck, uint32_t *size, uint8_t saddr[4], + uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq, + icmpr_flow_mode_t mode) +{ + struct ether_header *eh; + struct iphdr *ip; + struct icmphdr *icmp; + + *size = 0; + + if (mode == ICMPR_FLOW_MODE_ETH) + { + eh = (struct ether_header *) pck; + *size += generate_eth (eh, hw_daddr); + } + + ip = (struct iphdr *) (pck + *size); + *size += generate_ip (ip, saddr, daddr); + + icmp = (struct icmphdr *) (pck + *size); + *size += generate_icmp (icmp, seq); + + ((struct icmphdr *) (pck + *size - sizeof (struct icmphdr)))->checksum = + cksum (pck + *size - sizeof (struct icmphdr), sizeof (struct icmphdr)); + + ip->tot_len = __bswap_16 (*size - sizeof (struct ether_header)); + ip->check = 0; + ip->check = cksum (ip, sizeof (struct iphdr)); + + return 0; +} + +#define GET_HEADER(out, hdr, src, off) \ + do \ + { \ + out = (hdr *) (src + off); \ + off += sizeof (hdr); \ + } \ + while (0) + +int +resolve_packet_zero_copy (void *pck, uint32_t *size, uint8_t ip_addr[4], + uint8_t hw_addr[6]) +{ + struct ether_header *eh; + struct ether_arp *eah; + struct iphdr *ip; + struct icmphdr *icmp; + uint32_t offset = 0; + + if (pck == NULL) + return 0; + + GET_HEADER (eh, struct ether_header, pck, offset); + + memcpy (eh->ether_dhost, eh->ether_shost, 6); + memcpy (eh->ether_shost, hw_addr, 6); + + if (eh->ether_type == 0x0608) + { + GET_HEADER (eah, struct ether_arp, pck, offset); + struct arphdr *arp = &eah->ea_hdr; + + arp->ar_hrd = __bswap_16 (ARPHRD_ETHER); + arp->ar_pro = __bswap_16 (0x0800); + + arp->ar_hln = 6; + arp->ar_pln = 4; + + arp->ar_op = __bswap_16 (ARPOP_REPLY); + + memcpy (eah->arp_tha, eah->arp_sha, 6); + memcpy (eah->arp_tpa, eah->arp_spa, 4); + + memcpy (eah->arp_sha, eh->ether_shost, 6); + memcpy (eah->arp_spa, ip_addr, 4); + } + + else if (eh->ether_type == 0x0008) + { + GET_HEADER (ip, struct iphdr, pck, offset); + + if (ip->protocol == 1) + { + ip->ihl = 5; + ip->version = 4; + ip->tos = 0; + ip->tot_len = 0x0000; + ip->id = 0; + ip->frag_off = 0; + ip->ttl = 0x40; + ip->protocol = 1; + ip->check = 0x0000; + ip->daddr = ip->saddr; + ((uint8_t *) &ip->saddr)[0] = ip_addr[0]; + ((uint8_t *) &ip->saddr)[1] = ip_addr[1]; + ((uint8_t *) &ip->saddr)[2] = ip_addr[2]; + ((uint8_t *) &ip->saddr)[3] = ip_addr[3]; + + GET_HEADER (icmp, struct icmphdr, pck, offset); + + icmp->type = 0x00; + icmp->code = 0; + icmp->checksum = cksum (icmp, sizeof (struct icmphdr)); + + /* rest is payload */ + offset = *size; + + ip->tot_len = __bswap_16 (offset - sizeof (struct ether_header)); + ip->check = cksum (ip, sizeof (struct iphdr)); + } + } + + assert (offset == *size && "unsupported protocol"); + return 0; +} + +int +resolve_packet_zero_copy_add_encap (void **pck_, uint32_t *size, + uint8_t ip_addr[4]) +{ + struct ether_header *eh; + struct iphdr *ip; + struct icmphdr *icmp; + int32_t offset = 0; + uint16_t encap_size = sizeof (struct ether_header); + void *pck = *pck_; + + if (pck == NULL) + return 0; + + *pck_ -= encap_size; + offset -= encap_size; + + GET_HEADER (eh, struct ether_header, pck, offset); + + uint8_t hw_daddr[6]; + memset (hw_daddr, 0, sizeof (uint8_t) * 6); + + generate_eth (eh, hw_daddr); + + if (eh->ether_type == 0x0008) + { + GET_HEADER (ip, struct iphdr, pck, offset); + + if (ip->protocol == 1) + { + ip->ihl = 5; + ip->version = 4; + ip->tos = 0; + ip->tot_len = 0x0000; + ip->id = 0; + ip->frag_off = 0; + ip->ttl = 0x40; + ip->protocol = 1; + ip->check = 0x0000; + ip->daddr = ip->saddr; + ((uint8_t *) &ip->saddr)[0] = ip_addr[0]; + ((uint8_t *) &ip->saddr)[1] = ip_addr[1]; + ((uint8_t *) &ip->saddr)[2] = ip_addr[2]; + ((uint8_t *) &ip->saddr)[3] = ip_addr[3]; + + GET_HEADER (icmp, struct icmphdr, pck, offset); + + icmp->type = 0x00; + icmp->code = 0; + icmp->checksum = cksum (icmp, sizeof (struct icmphdr)); + + /* rest is payload */ + offset = *size; + + ip->tot_len = __bswap_16 (offset - sizeof (struct ether_header)); + ip->check = cksum (ip, sizeof (struct iphdr)); + } + } + + offset += encap_size; + + assert (offset != *size && + "new packet length must be increased by encap size"); + + /* overwrite packet size */ + *size = offset; + + return 0; +} diff --git a/extras/libmemif/examples/common/icmp_proto.h b/extras/libmemif/examples/common/icmp_proto.h new file mode 100644 index 00000000000..e346a3b13f7 --- /dev/null +++ b/extras/libmemif/examples/common/icmp_proto.h @@ -0,0 +1,48 @@ +/* + *------------------------------------------------------------------ + * 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_ + +typedef enum +{ + ICMPR_FLOW_MODE_ETH = 0, + ICMPR_FLOW_MODE_IP, +} icmpr_flow_mode_t; + +int resolve_packet (void *in_pck, ssize_t in_size, void *out_pck, + uint32_t *out_size, uint8_t ip_addr[4], + uint8_t hw_addr[6]); + +/* resolve packet in place */ +int resolve_packet_zero_copy (void *pck, uint32_t *size, uint8_t ip_addr[4], + uint8_t hw_addr[6]); + +/* resolve packet in place and add eth encap */ +int resolve_packet_zero_copy_add_encap (void **pck, uint32_t *size, + uint8_t ip_addr[4]); + +int generate_packet (void *pck, uint32_t *size, uint8_t saddr[4], + uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq); + +int generate_packet2 (void *pck, uint32_t *size, uint8_t saddr[4], + uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq, + icmpr_flow_mode_t); + +int print_packet (void *pck); + +#endif /* _ICMP_PROTO_H_ */ diff --git a/extras/libmemif/examples/common/packet_handler.c b/extras/libmemif/examples/common/packet_handler.c new file mode 100644 index 00000000000..705cc728a36 --- /dev/null +++ b/extras/libmemif/examples/common/packet_handler.c @@ -0,0 +1,65 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2020 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 +#include + +/* reply with the same data */ +int +basic_packet_handler (memif_connection_t *c) +{ + int i; + memif_buffer_t *dest, *src; + + /* in case of zero-copy the tx_buf_num will be zero, so the loop body won't + * execute */ + for (i = 0; i < c->tx_buf_num; i++) + { + memcpy (c->tx_bufs[i].data, c->rx_bufs[i].data, c->rx_bufs[i].len); + } + + return 0; +} + +/* ICMPv4 and ARP handler */ +int +icmp_packet_handler (memif_connection_t *c) +{ + int i; + memif_buffer_t *dest, *src; + + /* if tx_buf_num > 0 we use non-zero-copy mode */ + if (c->tx_buf_num > 0) + { + for (i = 0; i < c->tx_buf_num; i++) + { + resolve_packet (c->rx_bufs[i].data, c->rx_bufs[i].len, + c->tx_bufs[i].data, &c->tx_bufs[i].len, c->ip_addr, + c->hw_addr); + } + } + else + { + for (i = 0; i < c->rx_buf_num; i++) + { + resolve_packet_zero_copy (c->rx_bufs[i].data, &c->rx_bufs[i].len, + c->ip_addr, c->hw_addr); + } + } + + return 0; +} \ No newline at end of file diff --git a/extras/libmemif/examples/common/responder.c b/extras/libmemif/examples/common/responder.c new file mode 100644 index 00000000000..e0fa9e35b08 --- /dev/null +++ b/extras/libmemif/examples/common/responder.c @@ -0,0 +1,172 @@ +#include + +int +responder (memif_conn_handle_t conn, void *private_ctx, uint16_t qid) +{ + memif_connection_t *c = (memif_connection_t *) private_ctx; + int err, i; + uint16_t tx; + + /* receive packets from the shared memory */ + err = memif_rx_burst (conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &c->rx_buf_num); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_rx_burst: %s", memif_strerror (err)); + return err; + } + + do + { + /* allocate tx buffers */ + err = memif_buffer_alloc (conn, qid, c->tx_bufs, c->rx_buf_num, + &c->tx_buf_num, 2048); + /* suppress full ring error MEMIF_ERR_NOBUF_RING */ + if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING) + { + INFO ("memif_buffer_alloc: %s", memif_strerror (err)); + goto error; + } + + /* Process the packets */ + if (c->packet_handler == NULL) + { + INFO ("Missing packet handler"); + goto error; + } + err = c->packet_handler (c); + if (err != 0) + { + INFO ("packet handler error: %d", err); + goto error; + } + /* Done processing packets */ + + /* refill the queue */ + err = memif_refill_queue (conn, qid, c->tx_buf_num, 0); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_refill_queue: %s", memif_strerror (err)); + goto error; + } + c->rx_buf_num -= c->tx_buf_num; + + err = memif_tx_burst (conn, qid, c->tx_bufs, c->tx_buf_num, &tx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_tx_burst: %s", memif_strerror (err)); + goto error; + } + c->tx_buf_num -= tx; + + /* This should never happen */ + if (c->tx_buf_num != 0) + { + INFO ("memif_tx_burst failed to send all allocated buffers."); + goto error; + } + } + while (c->rx_buf_num > 0); + + return 0; + +error: + err = memif_refill_queue (conn, qid, c->rx_buf_num, 0); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_refill_queue: %s", memif_strerror (err)); + return err; + } + c->rx_buf_num = 0; + + return -1; +} + +int +responder_zero_copy (memif_conn_handle_t conn, void *private_ctx, uint16_t qid) +{ + memif_connection_t *c = (memif_connection_t *) private_ctx; + int err, i; + uint16_t tx, tx2; + + /* receive packets from the shared memory */ + err = memif_rx_burst (conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &c->rx_buf_num); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_rx_burst: %s", memif_strerror (err)); + return err; + } + + do + { + /* Note that in zero copy memif_buffer_alloc is not part of respond + process, + * instead rx buffers are used directly using memif_buffer_enq_tx. + * / + + /* Process the packets */ + if (c->packet_handler == NULL) + { + INFO ("Missing packet handler"); + goto error; + } + err = c->packet_handler (c); + if (err != 0) + { + INFO ("packet handler error: %d", err); + goto error; + } + /* Done processing packets */ + + /* Swap rx and tx buffers, swapped tx buffers are considered allocated + * and are ready to be transmitted. Notice that the buffers are swapped + * only in memif driver and locally remain in rx_bufs queue. + */ + err = memif_buffer_enq_tx (conn, qid, c->rx_bufs, c->rx_buf_num, &tx); + /* suppress full ring error MEMIF_ERR_NOBUF_RING */ + if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING) + { + INFO ("memif_buffer_alloc: %s", memif_strerror (err)); + goto error; + } + + /* refill the queue */ + err = memif_refill_queue (conn, qid, tx, 0); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_refill_queue: %s", memif_strerror (err)); + goto error; + } + c->rx_buf_num -= tx; + + /* Notice that we send from rx_bufs as the buffers were only swapped + * internally in memif driver */ + err = memif_tx_burst (conn, qid, c->rx_bufs, tx, &tx2); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_tx_burst: %s", memif_strerror (err)); + goto error; + } + tx -= tx2; + + /* This should never happen */ + if (tx != 0) + { + INFO ("memif_tx_burst failed to send all allocated buffers."); + goto error; + } + } + while (c->rx_buf_num > 0); + + return 0; + +error: + err = memif_refill_queue (conn, qid, c->rx_buf_num, 0); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_refill_queue: %s", memif_strerror (err)); + return err; + } + c->rx_buf_num = 0; + + return -1; +} \ No newline at end of file diff --git a/extras/libmemif/examples/common/sender.c b/extras/libmemif/examples/common/sender.c new file mode 100644 index 00000000000..bad926f7a16 --- /dev/null +++ b/extras/libmemif/examples/common/sender.c @@ -0,0 +1,55 @@ +#include + +int +send_packets (memif_connection_t *c, uint16_t qid, + packet_generator_t *generator, uint32_t num_pkts, + uint16_t max_pkt_size) +{ + int err, i; + uint16_t tx; + + do + { + err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, + num_pkts > MAX_MEMIF_BUFS ? MAX_MEMIF_BUFS : + num_pkts, + &c->tx_buf_num, max_pkt_size); + /* suppress full ring error MEMIF_ERR_NOBUF_RING */ + if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING) + { + INFO ("memif_buffer_alloc: %s", memif_strerror (err)); + goto error; + } + + /* generate packet inside allocated buffers */ + err = generator (c, num_pkts); + if (err != 0) + { + INFO ("paclet generator error: %d", err); + goto error; + } + + err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &tx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_tx_burst: %s", memif_strerror (err)); + goto error; + } + c->tx_buf_num -= tx; + + /* Should never happen... */ + if (c->tx_buf_num > 0) + { + INFO ("Failed to send allocated packets"); + goto error; + } + num_pkts -= tx; + } + while (num_pkts > 0); + + return 0; + +error: + /* TODO: free alloocated tx buffers */ + return -1; +} \ No newline at end of file diff --git a/extras/libmemif/examples/example_setup_doc.rst b/extras/libmemif/examples/example_setup_doc.rst deleted file mode 100644 index 76ce78dab72..00000000000 --- a/extras/libmemif/examples/example_setup_doc.rst +++ /dev/null @@ -1,248 +0,0 @@ -.. _libmemif_example_setup_doc: - -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 :ref:`libmemif_examples_doc` . - -VPP-side config: - -:: - - DBGvpp# create interface 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 successful, 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 ------------------------------------------- - - 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. - -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/examples_doc.rst b/extras/libmemif/examples/examples_doc.rst index 3fb8c3127f8..64029773378 100644 --- a/extras/libmemif/examples/examples_doc.rst +++ b/extras/libmemif/examples/examples_doc.rst @@ -1,42 +1,87 @@ .. _libmemif_examples_doc: -Examples -======== +Libmemif 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* folder. To run binaries directly, make -sure that libmemif library is installed. +Example source code is located in `.../vpp/extras/libmemif/examples/` directory. +The compiled binaries are located in `.../vpp/extras/libmemif/build/examples/`. -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: +ICMP Responder +-------------- +**Application Source Code**: `.../vpp/extras/libmemif/examples/icmp_responder` -:: +In this example, memif endpoint connects to an external application. The example +application can resolve ARP and reply to ICMPv4 packets. The program will exit +once the interface is disconnected Memif receive mode: interrupt. - # docker run -it --entrypoint=/bin/bash -i --rm --name icmp-responder --hostname icmp-responder --privileged -v "/run/vpp/:/run/vpp/" ligato/libmemif-sample-service +VPP (memif master) <--> icmp_responder app (memif slave) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Start VPP and configure memif interface:: -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. + make run + ... + DBGvpp# create interface memif id 0 master + DBGvpp# set int state memif0/0 up + DBGvpp# set int ip address memif0/0 192.168.1.2/24 -* ``extras/libmemif/examples/icmp_responder`` +Start icmp_responder example app:: -Simplest implementation. Event polling is handled by libmemif. -Single memif connection in slave mode is created (id 0). Use Ctrl + C to exit app. -Memif receive mode: interrupt. + ./examples/icmp_responder -* ``extras/libmemif/examples/icmp_responder-epoll`` (run in container by default) +Memif in slave mode will try to connect every 2 seconds. If connection +establishment is successful, the `memif connected` message will show:: -Supports multiple connections and master mode. User can create/delete connections, set ip addresses, -print connection information. :ref:`libmemif_example_setup_doc` contains instructions on basic -connection use cases setups. Memif receive mode: interrupt. App provides functionality to disable -interrupts for specified queue/s for testing purposes. Polling mode is not implemented in this example. + INFO: memif connected! -* ``extras/libmemif/examples/icmp_responder-mt`` +**Note**: Error messages like "unmatched interface id" are printed only in debug mode. -Multi-thread example, very similar to icmpr-epoll. Packets are handled in threads assigned to specific queues. Slave mode only. Memif receive mode: polling (memif_rx_poll function), interrupt (memif_rx_interrupt function). Receive modes differ per queue. +Verify that the memif is connected on VPP side:: + + 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 icmp_responder (Default IPv4: 192.168.1.1):: + + DBGvpp# ping 192.168.1.1 + 64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=.1888 ms + 64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=.1985 ms + 64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=.1813 ms + 64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=.1929 ms + + Statistics: 5 sent, 4 received, 20% packet loss + + +Loopback +-------- +**Application Source Code**: `.../vpp/extras/libmemif/examples/loopback` + +In this example, two memif endpoints are connected to create a loopback. +Once connected, a test packet is sent out the memif master interface to +the memif slave interface, which replies with the same packet in a +zero-copy way. +In reverse mode, the packet is sent from the slave interface and is +looped back by the master interface. + +Running The Loopback Application +++++++++++++++++++++++++++++++++ +Start the loopback example:: + + ./examples/loopback + +You should see the `Received correct data.` message:: + + INFO: Received correct data. + INFO: Stopping the program diff --git a/extras/libmemif/examples/icmp_responder/main.c b/extras/libmemif/examples/icmp_responder/main.c new file mode 100644 index 00000000000..32b8ed7579a --- /dev/null +++ b/extras/libmemif/examples/icmp_responder/main.c @@ -0,0 +1,241 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2020 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define APP_NAME "icmp_responder_example" + +#define IF_NAME "libmemif0" +#define IF_ID 0 +#define SOCKET_PATH "/run/vpp/memif.sock" +const uint8_t HW_ADDR[6] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; +const uint8_t IP_ADDR[4] = { 192, 168, 1, 1 }; + +memif_connection_t intf; +int epfd; + +/* informs user about connected status. private_ctx is used by user to identify + * connection */ +int +on_connect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif connected!"); + int err; + + memif_connection_t *c = (memif_connection_t *) private_ctx; + + c->is_connected = 1; + alloc_memif_buffers (c); + + err = memif_refill_queue (conn, 0, -1, 0); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_refill_queue: %s", memif_strerror (err)); + return err; + } + + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to + * identify connection */ +int +on_disconnect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif disconnected!"); + + memif_connection_t *c = (memif_connection_t *) private_ctx; + + c->is_connected = 0; + free_memif_buffers (c); + + /* stop event polling thread */ + int err = memif_cancel_poll_event (memif_get_socket_handle (conn)); + if (err != MEMIF_ERR_SUCCESS) + INFO ("We are doomed..."); + + return 0; +} + +void +print_help () +{ + printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME); +#ifdef ICMP_DBG + printf (" (debug)"); +#endif + printf ("\n"); + printf ("==============================\n"); + print_version (); + printf ("==============================\n"); + printf ( + "In this example, memif endpoint connects to an external application.\n"); + printf ( + "The example application can resolve ARP and reply to ICMPv4 packets.\n"); + printf ("The program will exit once the interface is disconnected.\n"); + printf ("==============================\n"); + printf ("Usage: icmp_responder [OPTIONS]\n\n"); + printf ("Options:\n"); + printf ("\t-r\tInterface role . Default: slave\n"); + printf ("\t-s\tSocket path. Supports abstract socket using @ before the " + "path. Default: /run/vpp/memif.sock\n"); + printf ("\t-i\tInterface id. Default: 0\n"); + printf ("\t-a\tIPv4 address. Default: 192.168.1.1\n"); + printf ("\t-h\tMac address. Default: aa:aa:aa:aa:aa:aa\n"); + printf ("\t-?\tShow help and exit.\n"); + printf ("\t-v\tShow libmemif and memif version information and exit.\n"); +} + +int +main (int argc, char *argv[]) +{ + memif_socket_args_t memif_socket_args = { 0 }; + memif_socket_handle_t memif_socket; + memif_conn_args_t memif_conn_args = { 0 }; + int opt, err, ret = 0; + uint8_t is_master = 0; + char socket_path[108]; + int id = IF_ID; + + strncpy (socket_path, SOCKET_PATH, strlen (SOCKET_PATH)); + + /* prepare the private data */ + memset (&intf, 0, sizeof (intf)); + intf.packet_handler = icmp_packet_handler; + memcpy (intf.ip_addr, IP_ADDR, 4); + memcpy (intf.hw_addr, HW_ADDR, 6); + + while ((opt = getopt (argc, argv, "r:s:i:a:h:?v")) != -1) + { + switch (opt) + { + case 'r': + if (strncmp (optarg, "master", sizeof (optarg)) == 0) + { + is_master = 1; + } + else if (strncmp (optarg, "slave", sizeof (optarg)) == 0) + { + is_master = 0; + } + else + { + INFO ("Invalid role value: '%s'", optarg); + return -1; + } + break; + case 's': + sprintf (socket_path, "%s", optarg); + break; + case 'i': + id = atoi (optarg); + break; + case 'a': + if (parse_ip4 (optarg, intf.ip_addr) != 0) + { + INFO ("Invalid ipv4 address: %s", optarg); + return -1; + } + break; + case 'h': + if (parse_mac (optarg, intf.hw_addr) != 0) + { + INFO ("Invalid mac address: %s", optarg); + return -1; + } + break; + case '?': + print_help (); + return 0; + case 'v': + print_version (); + return 0; + } + } + + /** Create memif socket + * + * Interfaces are internally stored in a database referenced by memif socket. + */ + sprintf (memif_socket_args.path, "%s", socket_path); + /* Set application name */ + strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME)); + + /* configure autoconnect timer */ + if (is_master == 0) + { + memif_socket_args.connection_request_timer.it_value.tv_sec = 2; + memif_socket_args.connection_request_timer.it_value.tv_nsec = 0; + memif_socket_args.connection_request_timer.it_interval.tv_sec = 2; + memif_socket_args.connection_request_timer.it_interval.tv_nsec = 0; + } + + err = memif_create_socket (&memif_socket, &memif_socket_args, NULL); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_create_socket: %s", memif_strerror (err)); + goto error; + } + + /** Create memif interfaces + * + * Both interaces are assigned the same socket and same id to create a + * loopback. + */ + + memif_conn_args.socket = memif_socket; + memif_conn_args.interface_id = id; + strncpy (memif_conn_args.interface_name, IF_NAME, + sizeof (memif_conn_args.interface_name)); + memif_conn_args.is_master = is_master; + + err = + memif_create (&intf.conn, &memif_conn_args, on_connect, on_disconnect, + is_master ? responder : responder_zero_copy, (void *) &intf); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_create_socket: %s", memif_strerror (err)); + return err; + } + + do + { + err = memif_poll_event (memif_socket, -1); + } + while (err == MEMIF_ERR_SUCCESS); + + return 0; + +error: + ret = -1; +done: + free_memif_buffers (&intf); + memif_delete (&intf.conn); + memif_delete_socket (&memif_socket); + return ret; +} diff --git a/extras/libmemif/examples/loopback/main.c b/extras/libmemif/examples/loopback/main.c new file mode 100644 index 00000000000..e68f69ddaf0 --- /dev/null +++ b/extras/libmemif/examples/loopback/main.c @@ -0,0 +1,307 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2020 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define APP_NAME "loopback_example" +#define IF0_NAME "lo0" +#define IF1_NAME "lo1" + +memif_connection_t intf0, intf1; +int is_reverse; +int epfd; + +int +packet_generator (memif_connection_t *c, uint16_t num_pkts) +{ + int i, bi = 0; + memif_buffer_t *mb; + + for (i = 0; (i < num_pkts) && (bi < c->tx_buf_num); i++) + { + mb = &c->tx_bufs[bi++]; + memset (mb->data, 1, mb->len); + } + + return 0; +} + +/* informs user about connected status. private_ctx is used by user to identify + * connection */ +int +on_connect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif connected!"); + int err; + + memif_connection_t *c = (memif_connection_t *) private_ctx; + + c->is_connected = 1; + alloc_memif_buffers (c); + + err = memif_refill_queue (conn, 0, -1, 0); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_refill_queue: %s", memif_strerror (err)); + return err; + } + + print_memif_details (c); + + /* Once both interfaces are connected send a test packet, master -> slave. + * Slave will use zero-copy method to reply the same pakcet back. + * (Configured by assigning responder_zero_copy as on_interrupt callback.) + */ + if ((intf0.is_connected == 1) && (intf1.is_connected == 1)) + { + send_packets (is_reverse ? &intf1 : &intf0, 0, packet_generator, 1, + 2048); + } + + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to + * identify connection */ +int +on_disconnect (memif_conn_handle_t conn, void *private_ctx) +{ + INFO ("memif disconnected!"); + + memif_connection_t *c = (memif_connection_t *) private_ctx; + + c->is_connected = 0; + free_memif_buffers (c); + + /* stop event polling thread */ + int err = memif_cancel_poll_event (memif_get_socket_handle (conn)); + if (err != MEMIF_ERR_SUCCESS) + INFO ("We are doomed..."); + + return 0; +} + +int +verify_packet (memif_conn_handle_t conn, void *private_ctx, uint16_t qid) +{ + memif_connection_t *c = (memif_connection_t *) private_ctx; + int err; + void *want; + + err = memif_rx_burst (conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &c->rx_buf_num); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("meif_rx_burst: %s", memif_strerror (err)); + return err; + } + + want = malloc (c->rx_bufs[0].len); + if (want == NULL) + { + INFO ("Out of memory"); + goto done; + } + + memset (want, 1, c->rx_bufs[0].len); + + err = memcmp (c->rx_bufs[0].data, want, c->rx_bufs[0].len); + if (err != 0) + { + INFO ("Received malformed data. ret: %d", err); + } + else + { + INFO ("Received correct data."); + } + +done: + err = memif_refill_queue (conn, qid, c->rx_buf_num, 0); + if (err != MEMIF_ERR_SUCCESS) + INFO ("memif_refill_queue: %s", memif_strerror (err)); + + /* stop polling and exit the program */ + INFO ("Stopping the program"); + err = memif_cancel_poll_event (memif_get_socket_handle (conn)); + if (err != MEMIF_ERR_SUCCESS) + INFO ("We are doomed..."); + + return err; +} + +int +create_memif_interface (memif_socket_handle_t memif_socket, + const char *if_name, int id, uint8_t is_master, + memif_connection_t *ctx) +{ + memif_conn_args_t memif_conn_args = { 0 }; + int err; + + memif_conn_args.socket = memif_socket; + memif_conn_args.interface_id = id; + strncpy (memif_conn_args.interface_name, if_name, + sizeof (memif_conn_args.interface_name)); + memif_conn_args.is_master = is_master; + + err = memif_create (&ctx->conn, &memif_conn_args, on_connect, on_disconnect, + is_master ? verify_packet : responder_zero_copy, + (void *) ctx); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_create_socket: %s", memif_strerror (err)); + return 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: %s\n", memif_get_version_str ()); + printf ("==============================\n"); + printf ("In this example, two memif endpoints are connected to create a " + "loopback.\n"); + printf ("Once connected, a test packet is sent out the memif master " + "interface to\n"); + printf ( + "the memif slave interface, which replies with the same packet in a\n"); + printf ("zero-copy way.\n"); + printf ( + "In reverse mode, the packet is sent from the slave interface and is\n"); + printf ("looped back by the master interface.\n"); + printf ("==============================\n"); + printf ("Usage: loopback [OPTIONS]\n\n"); + printf ("Options:\n"); + printf ("\t-r\tReverse mode, verification packet is sent by slave.\n"); + printf ("\t-?\tShow help and exit.\n"); + printf ("\t-v\tShow libmemif and memif version information and exit.\n"); +} + +int +main (int argc, char *argv[]) +{ + memif_socket_args_t memif_socket_args = { 0 }; + memif_socket_handle_t memif_socket; + int opt, err, ret = 0; + is_reverse = 0; + + while ((opt = getopt (argc, argv, "r?v")) != -1) + { + switch (opt) + { + case 'r': + is_reverse = 1; + break; + case '?': + print_help (); + return 0; + case 'v': + print_version (); + return 0; + } + } + + /** Create memif socket + * + * Interfaces are internally stored in a database referenced by memif socket. + */ + /* Abstract socket supported */ + memif_socket_args.path[0] = '@'; + strncpy (memif_socket_args.path + 1, APP_NAME, strlen (APP_NAME)); + /* Set application name */ + strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME)); + + err = memif_create_socket (&memif_socket, &memif_socket_args, NULL); + if (err != MEMIF_ERR_SUCCESS) + { + INFO ("memif_create_socket: %s", memif_strerror (err)); + goto error; + } + + /** Create memif interfaces + * + * Both interaces are assigned the same socket and same id to create a + * loopback. + */ + + /* prepare the private data */ + memset (&intf0, 0, sizeof (intf0)); + memset (&intf1, 0, sizeof (intf1)); + if (is_reverse) + { + intf0.packet_handler = basic_packet_handler; + } + else + { + intf1.packet_handler = basic_packet_handler; + } + + err = + create_memif_interface (memif_socket, IF0_NAME, 0, /* master */ 1, &intf0); + if (err != 0) + { + goto error; + } + + err = + create_memif_interface (memif_socket, IF1_NAME, 0, /* slave */ 0, &intf1); + if (err != 0) + { + goto error; + } + + do + { + err = memif_poll_event (memif_socket, -1); + } + while (err == MEMIF_ERR_SUCCESS); + + return 0; + +error: + ret = -1; +done: + free_memif_buffers (&intf0); + free_memif_buffers (&intf1); + memif_delete (&intf0.conn); + memif_delete (&intf1.conn); + memif_delete_socket (&memif_socket); + return ret; +} diff --git a/extras/libmemif/libmemif_doc.rst b/extras/libmemif/libmemif_doc.rst index a6478cfa89e..281c5487c90 100644 --- a/extras/libmemif/libmemif_doc.rst +++ b/extras/libmemif/libmemif_doc.rst @@ -62,6 +62,5 @@ The interface will by default connect to a master interface listening on ``/run/vpp/master.sock``. The example will handle ARP requests and respond to ICMPv4 requests to ``192.168.1.1``. -Continue with :ref:`libmemif_example_setup_doc` which contains instructions on -how to set up connection between icmpr-epoll example app and VPP-memif. - +Continue with :ref:`libmemif_examples_doc` which contains instructions on +how to set up connection between icmp_responder example app and VPP-memif. -- 2.16.6