On 08.12.2015 15:35, Stuart Haslam wrote: > On Tue, Dec 08, 2015 at 03:25:01PM +0300, Ilya Maximets wrote: >> >> >> On 08.12.2015 15:16, Stuart Haslam wrote: >>> 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(). >>> >> >> I agree. Would you prepare that patch? > > Will do. > >> OK, I think, I can change this function and use SOCK_ERR_REPORT() macro here >> like in other pktios. >> > > OK, sounds good. > New version posted.
Best regards, Ilya Maximets. _______________________________________________ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp