aboutsummaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorIlya Maximets <i.maximets@samsung.com>2015-12-10 18:32:03 +0300
committerMaxim Uvarov <maxim.uvarov@linaro.org>2015-12-14 11:57:03 +0300
commit758d7a4b6d62b6b7577d168a18c3456d18cf7e95 (patch)
tree719a858a11ca9033986b0f0f6f395106d2589a31 /platform
parent47982bcaa7e71298964351ba9041c34e5d427241 (diff)
linux-generic: pktio: add tap pktio type
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.maximets@samsung.com> Reviewed-and-tested-by: Stuart Haslam <stuart.haslam@linaro.org> Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
Diffstat (limited to 'platform')
-rw-r--r--platform/linux-generic/Makefile.am2
-rw-r--r--platform/linux-generic/include/odp_packet_io_internal.h3
-rw-r--r--platform/linux-generic/include/odp_packet_tap.h21
-rw-r--r--platform/linux-generic/pktio/io_ops.c1
-rw-r--r--platform/linux-generic/pktio/tap.c327
5 files changed, 354 insertions, 0 deletions
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 70bd8fe85..4639ebcc0 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 1a1118c83..de295578f 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 000000000..787758674
--- /dev/null
+++ b/platform/linux-generic/include/odp_packet_tap.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2015, Ilya Maximets <i.maximets@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 3b344e6ad..1933abcf8 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 000000000..7ecb3002d
--- /dev/null
+++ b/platform/linux-generic/pktio/tap.c
@@ -0,0 +1,327 @@
+/* Copyright (c) 2015, Ilya Maximets <i.maximets@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, n;
+ 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;
+ return -1;
+ }
+ 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) {
+ if (i == 0 && SOCK_ERR_REPORT(errno)) {
+ __odp_errno = errno;
+ ODP_ERR("write(): %s\n", strerror(errno));
+ return -1;
+ }
+ break;
+ } else if (retval != pkt_len) {
+ ODP_ERR("sent partial ethernet packet\n");
+ if (i == 0) {
+ __odp_errno = EMSGSIZE;
+ return -1;
+ }
+ break;
+ }
+ }
+
+ for (n = 0; n < i; n++)
+ odp_packet_free(pkts[n]);
+
+ return i;
+}
+
+static int tap_mtu_get(pktio_entry_t *pktio_entry)
+{
+ int ret;
+
+ ret = mtu_get_fd(pktio_entry->s.pkt_tap.skfd,
+ pktio_entry->s.name + 4);
+ if (ret > 0)
+ pktio_entry->s.pkt_tap.mtu = ret;
+
+ return ret;
+}
+
+static int tap_promisc_mode_set(pktio_entry_t *pktio_entry,
+ odp_bool_t enable)
+{
+ return promisc_mode_set_fd(pktio_entry->s.pkt_tap.skfd,
+ pktio_entry->s.name + 4, enable);
+}
+
+static int tap_promisc_mode_get(pktio_entry_t *pktio_entry)
+{
+ return promisc_mode_get_fd(pktio_entry->s.pkt_tap.skfd,
+ pktio_entry->s.name + 4);
+}
+
+static int tap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
+{
+ memcpy(mac_addr, pktio_entry->s.pkt_tap.if_mac, ETH_ALEN);
+ return ETH_ALEN;
+}
+
+const pktio_if_ops_t tap_pktio_ops = {
+ .init = NULL,
+ .term = NULL,
+ .open = tap_pktio_open,
+ .close = tap_pktio_close,
+ .start = NULL,
+ .stop = NULL,
+ .recv = tap_pktio_recv,
+ .send = tap_pktio_send,
+ .mtu_get = tap_mtu_get,
+ .promisc_mode_set = tap_promisc_mode_set,
+ .promisc_mode_get = tap_promisc_mode_get,
+ .mac_get = tap_mac_addr_get
+};