On Tue, Dec 08, 2015 at 02:51:40PM +0300, Ilya Maximets wrote: > On 08.12.2015 14:24, Stuart Haslam wrote: > > On Mon, Dec 07, 2015 at 01:55:31PM +0300, Ilya Maximets wrote: > >> Creates a new pktio type that allows for creating and > >> sending/receiving packets through TAP interface. > >> It is intended for use as a simple conventional communication > >> method between applications that use kernel network stack > >> (ping, ssh, iperf, etc.) and ODP applications for the purpose > >> of functional testing and can be used as it is with some > >> of the existing example applications. > >> > >> To use this interface the name passed to odp_pktio_open() must > >> begin with "tap:" and be in the format: > >> > >> tap:iface > >> > >> iface the name of TAP device to be created. > >> > >> TUN/TAP kernel module should be loaded to use this pktio. > >> There should be no device named 'iface' in the system. > >> The total length of the 'iface' is limited by IF_NAMESIZE. > >> > >> Signed-off-by: Ilya Maximets <i.maxim...@samsung.com> > >> --- > >> platform/linux-generic/Makefile.am | 2 + > >> .../linux-generic/include/odp_packet_io_internal.h | 3 + > >> platform/linux-generic/include/odp_packet_tap.h | 21 ++ > >> platform/linux-generic/pktio/io_ops.c | 1 + > >> platform/linux-generic/pktio/tap.c | 317 > >> +++++++++++++++++++++ > >> 5 files changed, 344 insertions(+) > >> create mode 100644 platform/linux-generic/include/odp_packet_tap.h > >> create mode 100644 platform/linux-generic/pktio/tap.c > >> > >> diff --git a/platform/linux-generic/Makefile.am > >> b/platform/linux-generic/Makefile.am > >> index 70bd8fe..4639ebc 100644 > >> --- a/platform/linux-generic/Makefile.am > >> +++ b/platform/linux-generic/Makefile.am > >> @@ -92,6 +92,7 @@ noinst_HEADERS = \ > >> ${srcdir}/include/odp_packet_io_queue.h \ > >> ${srcdir}/include/odp_packet_netmap.h \ > >> ${srcdir}/include/odp_packet_socket.h \ > >> + ${srcdir}/include/odp_packet_tap.h \ > >> ${srcdir}/include/odp_pool_internal.h \ > >> ${srcdir}/include/odp_queue_internal.h \ > >> ${srcdir}/include/odp_schedule_internal.h \ > >> @@ -120,6 +121,7 @@ __LIB__libodp_la_SOURCES = \ > >> pktio/netmap.c \ > >> pktio/socket.c \ > >> pktio/socket_mmap.c \ > >> + pktio/tap.c \ > >> odp_pool.c \ > >> odp_queue.c \ > >> odp_rwlock.c \ > >> diff --git a/platform/linux-generic/include/odp_packet_io_internal.h > >> b/platform/linux-generic/include/odp_packet_io_internal.h > >> index 1a1118c..de29557 100644 > >> --- a/platform/linux-generic/include/odp_packet_io_internal.h > >> +++ b/platform/linux-generic/include/odp_packet_io_internal.h > >> @@ -22,6 +22,7 @@ extern "C" { > >> #include <odp/ticketlock.h> > >> #include <odp_packet_socket.h> > >> #include <odp_packet_netmap.h> > >> +#include <odp_packet_tap.h> > >> #include <odp_classification_datamodel.h> > >> #include <odp_align_internal.h> > >> #include <odp_debug_internal.h> > >> @@ -78,6 +79,7 @@ struct pktio_entry { > >> #ifdef HAVE_PCAP > >> pkt_pcap_t pkt_pcap; /**< Using pcap for IO */ > >> #endif > >> + pkt_tap_t pkt_tap; /**< using TAP for IO */ > >> }; > >> enum { > >> STATE_START = 0, > >> @@ -157,6 +159,7 @@ extern const pktio_if_ops_t loopback_pktio_ops; > >> #ifdef HAVE_PCAP > >> extern const pktio_if_ops_t pcap_pktio_ops; > >> #endif > >> +extern const pktio_if_ops_t tap_pktio_ops; > >> extern const pktio_if_ops_t * const pktio_if_ops[]; > >> > >> #ifdef __cplusplus > >> diff --git a/platform/linux-generic/include/odp_packet_tap.h > >> b/platform/linux-generic/include/odp_packet_tap.h > >> new file mode 100644 > >> index 0000000..7877586 > >> --- /dev/null > >> +++ b/platform/linux-generic/include/odp_packet_tap.h > >> @@ -0,0 +1,21 @@ > >> +/* Copyright (c) 2015, Ilya Maximets <i.maxim...@samsung.com> > >> + * All rights reserved. > >> + * > >> + * SPDX-License-Identifier: BSD-3-Clause > >> + */ > >> + > >> +#ifndef ODP_PACKET_TAP_H_ > >> +#define ODP_PACKET_TAP_H_ > >> + > >> +#include <odp/pool.h> > >> + > >> +typedef struct { > >> + int fd; /**< file descriptor for tap interface*/ > >> + int skfd; /**< socket descriptor */ > >> + uint32_t mtu; /**< cached mtu */ > >> + unsigned char if_mac[ETH_ALEN]; /**< MAC address of pktio side (not a > >> + MAC address of kernel interface)*/ > >> + odp_pool_t pool; /**< pool to alloc packets from */ > >> +} pkt_tap_t; > >> + > >> +#endif > >> diff --git a/platform/linux-generic/pktio/io_ops.c > >> b/platform/linux-generic/pktio/io_ops.c > >> index 3b344e6..1933abc 100644 > >> --- a/platform/linux-generic/pktio/io_ops.c > >> +++ b/platform/linux-generic/pktio/io_ops.c > >> @@ -18,6 +18,7 @@ const pktio_if_ops_t * const pktio_if_ops[] = { > >> #ifdef HAVE_PCAP > >> &pcap_pktio_ops, > >> #endif > >> + &tap_pktio_ops, > >> &sock_mmap_pktio_ops, > >> &sock_mmsg_pktio_ops, > >> NULL > >> diff --git a/platform/linux-generic/pktio/tap.c > >> b/platform/linux-generic/pktio/tap.c > >> new file mode 100644 > >> index 0000000..b11e64f > >> --- /dev/null > >> +++ b/platform/linux-generic/pktio/tap.c > >> @@ -0,0 +1,317 @@ > >> +/* Copyright (c) 2015, Ilya Maximets <i.maxim...@samsung.com> > >> + * All rights reserved. > >> + * > >> + * SPDX-License-Identifier: BSD-3-Clause > >> + */ > >> + > >> +/** > >> + * @file > >> + * > >> + * TAP pktio type > >> + * > >> + * This file provides a pktio interface that allows for creating and > >> + * send/receive packets through TAP interface. It is intended for use > >> + * as a simple conventional communication method between applications > >> + * that use kernel network stack (ping, ssh, iperf, etc.) and ODP > >> + * applications for the purpose of functional testing. > >> + * > >> + * To use this interface the name passed to odp_pktio_open() must begin > >> + * with "tap:" and be in the format: > >> + * > >> + * tap:iface > >> + * > >> + * iface the name of TAP device to be created. > >> + * > >> + * TUN/TAP kernel module should be loaded to use this pktio. > >> + * There should be no device named 'iface' in the system. > >> + * The total length of the 'iface' is limited by IF_NAMESIZE. > >> + */ > >> + > >> +#ifndef _GNU_SOURCE > >> +#define _GNU_SOURCE > >> +#endif > >> + > >> +#include <errno.h> > >> +#include <fcntl.h> > >> +#include <unistd.h> > >> +#include <stdio.h> > >> +#include <sys/ioctl.h> > >> +#include <sys/socket.h> > >> +#include <sys/types.h> > >> +#include <linux/if_tun.h> > >> + > >> +#include <odp.h> > >> +#include <odp_packet_socket.h> > >> +#include <odp_packet_internal.h> > >> +#include <odp_packet_io_internal.h> > >> + > >> +#define BUF_SIZE 65536 > >> + > >> +static int gen_random_mac(unsigned char *mac) > >> +{ > >> + mac[0] = 0x7a; /* not multicast and local assignment bit is set */ > >> + if (odp_random_data(mac + 1, 5, false) < 5) { > >> + ODP_ERR("odp_random_data failed.\n"); > >> + return -1; > >> + } > >> + return 0; > >> +} > >> + > >> +static int tap_pktio_open(odp_pktio_t id ODP_UNUSED, > >> + pktio_entry_t *pktio_entry, > >> + const char *devname, odp_pool_t pool) > >> +{ > >> + int fd, skfd, flags, mtu; > >> + struct ifreq ifr; > >> + pkt_tap_t *tap = &pktio_entry->s.pkt_tap; > >> + > >> + if (strncmp(devname, "tap:", 4) != 0) > >> + return -1; > >> + > >> + /* Init pktio entry */ > >> + memset(tap, 0, sizeof(*tap)); > >> + tap->fd = -1; > >> + tap->skfd = -1; > >> + > >> + if (pool == ODP_POOL_INVALID) > >> + return -1; > >> + > >> + fd = open("/dev/net/tun", O_RDWR); > >> + if (fd < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("failed to open /dev/net/tun: %s\n", strerror(errno)); > >> + return -1; > >> + } > >> + > >> + memset(&ifr, 0, sizeof(ifr)); > >> + /* Flags: IFF_TUN - TUN device (no Ethernet headers) > >> + * IFF_TAP - TAP device > >> + * > >> + * IFF_NO_PI - Do not provide packet information > >> + */ > >> + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; > >> + snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", devname + 4); > >> + > >> + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("%s: creating tap device failed: %s\n", > >> + ifr.ifr_name, strerror(errno)); > >> + goto tap_err; > >> + } > >> + > >> + /* Set nonblocking mode on interface. */ > >> + flags = fcntl(fd, F_GETFL, 0); > >> + if (flags < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("fcntl(F_GETFL) failed: %s\n", strerror(errno)); > >> + goto tap_err; > >> + } > >> + > >> + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("fcntl(F_SETFL) failed: %s\n", strerror(errno)); > >> + goto tap_err; > >> + } > >> + > >> + if (gen_random_mac(tap->if_mac) < 0) > >> + goto tap_err; > >> + > >> + /* Create AF_INET socket for network interface related operations. */ > >> + skfd = socket(AF_INET, SOCK_DGRAM, 0); > >> + if (skfd < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("socket creation failed: %s\n", strerror(errno)); > >> + goto tap_err; > >> + } > >> + > >> + mtu = mtu_get_fd(skfd, devname + 4); > >> + if (mtu < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("mtu_get_fd failed: %s\n", strerror(errno)); > >> + goto sock_err; > >> + } > >> + > >> + /* Up interface by default. */ > >> + if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("ioctl(SIOCGIFFLAGS) failed: %s\n", strerror(errno)); > >> + goto sock_err; > >> + } > >> + > >> + ifr.ifr_flags |= IFF_UP; > >> + ifr.ifr_flags |= IFF_RUNNING; > >> + > >> + if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("failed to come up: %s\n", strerror(errno)); > >> + goto sock_err; > >> + } > >> + > >> + tap->fd = fd; > >> + tap->skfd = skfd; > >> + tap->mtu = mtu; > >> + tap->pool = pool; > >> + return 0; > >> +sock_err: > >> + close(skfd); > >> +tap_err: > >> + close(fd); > >> + ODP_ERR("Tap device alloc failed.\n"); > >> + return -1; > >> +} > >> + > >> +static int tap_pktio_close(pktio_entry_t *pktio_entry) > >> +{ > >> + int ret = 0; > >> + pkt_tap_t *tap = &pktio_entry->s.pkt_tap; > >> + > >> + if (tap->fd != -1 && close(tap->fd) != 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("close(tap->fd): %s\n", strerror(errno)); > >> + ret = -1; > >> + } > >> + > >> + if (tap->skfd != -1 && close(tap->skfd) != 0) { > >> + __odp_errno = errno; > >> + ODP_ERR("close(tap->skfd): %s\n", strerror(errno)); > >> + ret = -1; > >> + } > >> + > >> + return ret; > >> +} > >> + > >> +static odp_packet_t pack_odp_pkt(odp_pool_t pool, > >> + const void *data, > >> + unsigned int len) > >> +{ > >> + odp_packet_t pkt; > >> + > >> + pkt = packet_alloc(pool, len, 1); > >> + > >> + if (pkt == ODP_PACKET_INVALID) > >> + return pkt; > >> + > >> + if (odp_packet_copydata_in(pkt, 0, len, data) < 0) { > >> + ODP_ERR("failed to copy packet data\n"); > >> + odp_packet_free(pkt); > >> + return ODP_PACKET_INVALID; > >> + } > >> + > >> + packet_parse_l2(odp_packet_hdr(pkt)); > >> + > >> + return pkt; > >> +} > >> + > >> +static int tap_pktio_recv(pktio_entry_t *pktio_entry, odp_packet_t pkts[], > >> + unsigned len) > >> +{ > >> + ssize_t retval; > >> + unsigned i; > >> + uint8_t buf[BUF_SIZE]; > >> + pkt_tap_t *tap = &pktio_entry->s.pkt_tap; > >> + > >> + for (i = 0; i < len; i++) { > >> + do { > >> + retval = read(tap->fd, buf, BUF_SIZE); > >> + } while (retval < 0 && errno == EINTR); > >> + > >> + if (retval < 0) { > >> + __odp_errno = errno; > >> + break; > >> + } > >> + > >> + pkts[i] = pack_odp_pkt(tap->pool, buf, retval); > >> + if (pkts[i] == ODP_PACKET_INVALID) > >> + break; > >> + } > >> + > >> + return i; > >> +} > >> + > >> +static int tap_pktio_send(pktio_entry_t *pktio_entry, odp_packet_t pkts[], > >> + unsigned len) > >> +{ > >> + ssize_t retval; > >> + unsigned i; > >> + uint32_t pkt_len; > >> + uint8_t buf[BUF_SIZE]; > >> + pkt_tap_t *tap = &pktio_entry->s.pkt_tap; > >> + > >> + for (i = 0; i < len; i++) { > >> + pkt_len = odp_packet_len(pkts[i]); > >> + > >> + if (pkt_len > tap->mtu) { > >> + if (i == 0) > >> + __odp_errno = EMSGSIZE; > >> + break; > >> + } > >> + > >> + if (odp_packet_copydata_out(pkts[i], 0, pkt_len, buf) < 0) { > >> + ODP_ERR("failed to copy packet data\n"); > >> + break; > >> + } > >> + > >> + do { > >> + retval = write(tap->fd, buf, pkt_len); > >> + } while (retval < 0 && errno == EINTR); > >> + > >> + if (retval < 0) { > >> + __odp_errno = (errno == ENOBUFS) ? EAGAIN : errno; > >> + break; > >> + } else if (retval != pkt_len) { > >> + __odp_errno = EMSGSIZE; > >> + ODP_ERR("sent partial ethernet packet\n"); > >> + break; > >> + } > >> + > >> + odp_packet_free(pkts[i]); > >> + } > >> + > >> + return (i != 0 || len == 0) ? (int)i : -1; > >> +} > > > > It looks like this will return -1 if an ENOBUFS error occurs the first > > time around (no packets received yet). That should be a 0 return value > > as -1 indicates the caller needs to take some action to recover, which > > isn't required in this case. > > > > You talking about tap_pktio_send or tap_pktio_recv?
tap_pktio_send > In case of recv, -1 can't be returned, and, in case of send, ENOBUFS means > nothing was sent. > At least, socket_mmap returns -1 in case of ENOBUFS on send. So it does, I think that's a bug and ENOBUFS should be filtered out by SOCK_ERR_REPORT(). > > Best regards, Ilya Maximets. -- Stuart. _______________________________________________ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp