{
struct dp_netdev_execute_aux aux = {dp, key};
- odp_execute_actions(&aux, packet, md, actions, actions_len, dp_execute_cb);
+ odp_execute_actions(&aux, packet, may_steal, md,
+ actions, actions_len, dp_execute_cb);
}
- NULL, \
- NULL, \
+#define DPIF_NETDEV_CLASS_FUNCTIONS \
+ dpif_netdev_enumerate, \
+ dpif_netdev_port_open_type, \
+ dpif_netdev_open, \
+ dpif_netdev_close, \
+ dpif_netdev_destroy, \
++ dpif_netdev_run, \
++ dpif_netdev_wait, \
+ dpif_netdev_get_stats, \
+ dpif_netdev_port_add, \
+ dpif_netdev_port_del, \
+ dpif_netdev_port_query_by_number, \
+ dpif_netdev_port_query_by_name, \
+ NULL, /* port_get_pid */ \
+ dpif_netdev_port_dump_start, \
+ dpif_netdev_port_dump_next, \
+ dpif_netdev_port_dump_done, \
+ dpif_netdev_port_poll, \
+ dpif_netdev_port_poll_wait, \
+ dpif_netdev_flow_get, \
+ dpif_netdev_flow_put, \
+ dpif_netdev_flow_del, \
+ dpif_netdev_flow_flush, \
+ dpif_netdev_flow_dump_state_init, \
+ dpif_netdev_flow_dump_start, \
+ dpif_netdev_flow_dump_next, \
+ NULL, \
+ dpif_netdev_flow_dump_done, \
+ dpif_netdev_flow_dump_state_uninit, \
+ dpif_netdev_execute, \
+ NULL, /* operate */ \
+ dpif_netdev_recv_set, \
++ dpif_netdev_handlers_set, \
+ dpif_netdev_queue_to_priority, \
+ dpif_netdev_recv, \
+ dpif_netdev_recv_wait, \
+ dpif_netdev_recv_purge, \
+
const struct dpif_class dpif_netdev_class = {
"netdev",
- dpif_netdev_enumerate,
- dpif_netdev_port_open_type,
- dpif_netdev_open,
- dpif_netdev_close,
- dpif_netdev_destroy,
- dpif_netdev_run,
- dpif_netdev_wait,
- dpif_netdev_get_stats,
- dpif_netdev_port_add,
- dpif_netdev_port_del,
- dpif_netdev_port_query_by_number,
- dpif_netdev_port_query_by_name,
- NULL, /* port_get_pid */
- dpif_netdev_port_dump_start,
- dpif_netdev_port_dump_next,
- dpif_netdev_port_dump_done,
- dpif_netdev_port_poll,
- dpif_netdev_port_poll_wait,
- dpif_netdev_flow_get,
- dpif_netdev_flow_put,
- dpif_netdev_flow_del,
- dpif_netdev_flow_flush,
- dpif_netdev_flow_dump_state_init,
- dpif_netdev_flow_dump_start,
- dpif_netdev_flow_dump_next,
- NULL,
- dpif_netdev_flow_dump_done,
- dpif_netdev_flow_dump_state_uninit,
- dpif_netdev_execute,
- NULL, /* operate */
- dpif_netdev_recv_set,
- dpif_netdev_handlers_set,
- dpif_netdev_queue_to_priority,
- dpif_netdev_recv,
- dpif_netdev_recv_wait,
- dpif_netdev_recv_purge,
+ DPIF_NETDEV_CLASS_FUNCTIONS
+};
+
+const struct dpif_class dpif_planetlab_class = {
+ "planetlab",
+ DPIF_NETDEV_CLASS_FUNCTIONS
};
static void
--- /dev/null
- struct netdev_rx_pltap {
- struct netdev_rx up;
+/*
+ * Copyright (c) 2012 Giuseppe Lettieri
+ *
+ * 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 <config.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <linux/if_tun.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include "flow.h"
+#include "list.h"
++#include "dpif-netdev.h"
+#include "netdev-provider.h"
+#include "odp-util.h"
+#include "ofp-print.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "shash.h"
+#include "sset.h"
+#include "unixctl.h"
+#include "socket-util.h"
+#include "vlog.h"
+#include "tunalloc.h"
+
+VLOG_DEFINE_THIS_MODULE(netdev_pltap);
+
+/* Protects 'sync_list'. */
+static struct ovs_mutex sync_list_mutex = OVS_MUTEX_INITIALIZER;
+
+static struct list sync_list OVS_GUARDED_BY(sync_list_mutex)
+ = LIST_INITIALIZER(&sync_list);
+
+struct netdev_pltap {
+ struct netdev up;
+
+ /* In sync_list. */
+ struct list sync_list OVS_GUARDED_BY(sync_list_mutex);
+
+ /* Protects all members below. */
+ struct ovs_mutex mutex OVS_ACQ_AFTER(sync_list_mutex);
+
+ char *real_name;
+ struct netdev_stats stats;
+ enum netdev_flags new_flags;
+ enum netdev_flags flags;
+ int fd;
+ struct sockaddr_in local_addr;
+ int local_netmask;
+ bool valid_local_ip;
+ bool valid_local_netmask;
+ bool sync_flags_needed;
+ unsigned int change_seq;
+};
+
+
- static struct netdev_rx_pltap*
- netdev_rx_pltap_cast(const struct netdev_rx *rx)
++struct netdev_rxq_pltap {
++ struct netdev_rxq up;
+ int fd;
+};
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+/* Protects 'pltap_netdevs' */
+static struct ovs_mutex pltap_netdevs_mutex = OVS_MUTEX_INITIALIZER;
+static struct shash pltap_netdevs OVS_GUARDED_BY(pltap_netdevs_mutex)
+ = SHASH_INITIALIZER(&pltap_netdevs);
+
+static int netdev_pltap_construct(struct netdev *netdev_);
+
+static void netdev_pltap_update_seq(struct netdev_pltap *)
+ OVS_REQUIRES(dev->mutex);
+static int get_flags(struct netdev_pltap *dev, enum netdev_flags *flags)
+ OVS_REQUIRES(dev->mutex);
+
+static bool
+netdev_pltap_finalized(struct netdev_pltap *dev)
+ OVS_REQUIRES(dev->mutex)
+{
+ return dev->valid_local_ip && dev->valid_local_netmask;
+}
+
+static bool
+is_netdev_pltap_class(const struct netdev_class *class)
+{
+ return class->construct == netdev_pltap_construct;
+}
+
+static struct netdev_pltap *
+netdev_pltap_cast(const struct netdev *netdev)
+{
+ ovs_assert(is_netdev_pltap_class(netdev_get_class(netdev)));
+ return CONTAINER_OF(netdev, struct netdev_pltap, up);
+}
+
- return CONTAINER_OF(rx, struct netdev_rx_pltap, up);
++static struct netdev_rxq_pltap*
++netdev_rxq_pltap_cast(const struct netdev_rxq *rx)
+{
+ ovs_assert(is_netdev_pltap_class(netdev_get_class(rx->netdev)));
- static struct netdev_rx *
- netdev_pltap_rx_alloc(void)
++ return CONTAINER_OF(rx, struct netdev_rxq_pltap, up);
+}
+
+static void sync_needed(struct netdev_pltap *dev)
+ OVS_REQUIRES(dev->mutex, sync_list_mutex)
+{
+ if (dev->sync_flags_needed)
+ return;
+
+ dev->sync_flags_needed = true;
+ list_insert(&sync_list, &dev->sync_list);
+}
+
+static void sync_done(struct netdev_pltap *dev)
+ OVS_REQUIRES(dev->mutex, sync_list_mutex)
+{
+ if (!dev->sync_flags_needed)
+ return;
+
+ (void) list_remove(&dev->sync_list);
+ dev->sync_flags_needed = false;
+}
+
+static struct netdev *
+netdev_pltap_alloc(void)
+{
+ struct netdev_pltap *netdev = xzalloc(sizeof *netdev);
+ return &netdev->up;
+}
+
+static int
+netdev_pltap_construct(struct netdev *netdev_)
+{
+ struct netdev_pltap *netdev = netdev_pltap_cast(netdev_);
+ int error;
+
+ ovs_mutex_init(&netdev->mutex);
+ netdev->real_name = xzalloc(IFNAMSIZ + 1);
+ memset(&netdev->local_addr, 0, sizeof(netdev->local_addr));
+ netdev->valid_local_ip = false;
+ netdev->valid_local_netmask = false;
+ netdev->flags = 0;
+ netdev->sync_flags_needed = false;
+ netdev->change_seq = 1;
+
+
+ /* Open tap device. */
+ netdev->fd = tun_alloc(IFF_TAP, netdev->real_name);
+ if (netdev->fd < 0) {
+ error = errno;
+ VLOG_WARN("tun_alloc(IFF_TAP, %s) failed: %s",
+ netdev_get_name(netdev_), ovs_strerror(error));
+ return error;
+ }
+ VLOG_DBG("real_name = %s", netdev->real_name);
+
+ /* Make non-blocking. */
+ error = set_nonblocking(netdev->fd);
+ if (error) {
+ return error;
+ }
+
+ ovs_mutex_lock(&pltap_netdevs_mutex);
+ shash_add(&pltap_netdevs, netdev_get_name(netdev_), netdev);
+ ovs_mutex_unlock(&pltap_netdevs_mutex);
+ return 0;
+}
+
+static void
+netdev_pltap_destruct(struct netdev *netdev_)
+{
+ struct netdev_pltap *netdev = netdev_pltap_cast(netdev_);
+
+ ovs_mutex_lock(&pltap_netdevs_mutex);
+ if (netdev->fd != -1)
+ close(netdev->fd);
+
+ if (netdev->sync_flags_needed) {
+ ovs_mutex_lock(&sync_list_mutex);
+ (void) list_remove(&netdev->sync_list);
+ ovs_mutex_unlock(&sync_list_mutex);
+ }
+
+ shash_find_and_delete(&pltap_netdevs,
+ netdev_get_name(netdev_));
+ ovs_mutex_unlock(&pltap_netdevs_mutex);
+ ovs_mutex_destroy(&netdev->mutex);
+}
+
+static void
+netdev_pltap_dealloc(struct netdev *netdev_)
+{
+ struct netdev_pltap *netdev = netdev_pltap_cast(netdev_);
+ free(netdev);
+}
+
+static int netdev_pltap_up(struct netdev_pltap *dev) OVS_REQUIRES(dev->mutex);
+
- struct netdev_rx_pltap *rx = xzalloc(sizeof *rx);
++static struct netdev_rxq *
++netdev_pltap_rxq_alloc(void)
+{
- netdev_pltap_rx_construct(struct netdev_rx *rx_)
++ struct netdev_rxq_pltap *rx = xzalloc(sizeof *rx);
+ return &rx->up;
+}
+
+static int
- struct netdev_rx_pltap *rx = netdev_rx_pltap_cast(rx_);
++netdev_pltap_rxq_construct(struct netdev_rxq *rx_)
+{
- netdev_pltap_rx_destruct(struct netdev_rx *rx_ OVS_UNUSED)
++ struct netdev_rxq_pltap *rx = netdev_rxq_pltap_cast(rx_);
+ struct netdev *netdev_ = rx->up.netdev;
+ struct netdev_pltap *netdev =
+ netdev_pltap_cast(netdev_);
+ int error = 0;
+
+ ovs_mutex_lock(&netdev->mutex);
+ rx->fd = netdev->fd;
+ if (!netdev_pltap_finalized(netdev))
+ goto out;
+ error = netdev_pltap_up(netdev);
+ if (error) {
+ goto out;
+ }
+out:
+ ovs_mutex_unlock(&netdev->mutex);
+ return error;
+}
+
+static void
- netdev_pltap_rx_dealloc(struct netdev_rx *rx_)
++netdev_pltap_rxq_destruct(struct netdev_rxq *rx_ OVS_UNUSED)
+{
+}
+
+static void
- struct netdev_rx_pltap *rx = netdev_rx_pltap_cast(rx_);
++netdev_pltap_rxq_dealloc(struct netdev_rxq *rx_)
+{
- netdev_pltap_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
++ struct netdev_rxq_pltap *rx = netdev_rxq_pltap_cast(rx_);
+
+ free(rx);
+}
+
+static int vsys_transaction(const char *script,
+ const char **preply, char *format, ...)
+{
+ char *msg = NULL, *reply = NULL;
+ const size_t reply_size = 1024;
+ int ifd = -1, ofd = -1, maxfd;
+ size_t bytes_to_write, bytes_to_read,
+ bytes_written = 0, bytes_read = 0;
+ int error = 0;
+ char *ofname = NULL, *ifname = NULL;
+ va_list args;
+
+ va_start(args, format);
+ msg = xvasprintf(format, args);
+ va_end(args);
+ reply = (char*)xmalloc(reply_size);
+ if (!msg || !reply) {
+ VLOG_ERR("Out of memory");
+ error = ENOMEM;
+ goto cleanup;
+ }
+
+ ofname = xasprintf("/vsys/%s.out", script);
+ ifname = xasprintf("/vsys/%s.in", script);
+ if (!ofname || !ifname) {
+ VLOG_ERR("Out of memory");
+ error = ENOMEM;
+ goto cleanup;
+ }
+
+ ofd = open(ofname, O_RDONLY | O_NONBLOCK);
+ if (ofd < 0) {
+ VLOG_ERR("Cannot open %s: %s", ofname, ovs_strerror(errno));
+ error = errno;
+ goto cleanup;
+ }
+ ifd = open(ifname, O_WRONLY | O_NONBLOCK);
+ if (ifd < 0) {
+ VLOG_ERR("Cannot open %s: %s", ifname, ovs_strerror(errno));
+ error = errno;
+ goto cleanup;
+ }
+ maxfd = (ifd < ofd) ? ofd : ifd;
+
+ bytes_to_write = strlen(msg);
+ bytes_to_read = reply_size;
+ while (bytes_to_write || bytes_to_read) {
+ fd_set readset, writeset, errorset;
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&errorset);
+ if (bytes_to_write) {
+ FD_SET(ifd, &writeset);
+ FD_SET(ifd, &errorset);
+ }
+ FD_SET(ofd, &readset);
+ FD_SET(ofd, &errorset);
+ if (select(maxfd + 1, &readset, &writeset, &errorset, NULL) < 0) {
+ if (errno == EINTR)
+ continue;
+ VLOG_ERR("selec error: %s", ovs_strerror(errno));
+ error = errno;
+ goto cleanup;
+ }
+ if (FD_ISSET(ifd, &errorset) || FD_ISSET(ofd, &errorset)) {
+ VLOG_ERR("error condition on ifd or ofd");
+ goto cleanup;
+ }
+ if (FD_ISSET(ifd, &writeset)) {
+ ssize_t n = write(ifd, msg + bytes_written, bytes_to_write);
+ if (n < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ VLOG_ERR("write on %s: %s", ifname, ovs_strerror(errno));
+ error = errno;
+ goto cleanup;
+ }
+ } else {
+ bytes_written += n;
+ bytes_to_write -= n;
+ if (bytes_to_write == 0)
+ close(ifd);
+ }
+ }
+ if (FD_ISSET(ofd, &readset)) {
+ ssize_t n = read(ofd, reply + bytes_read, bytes_to_read);
+ if (n < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ VLOG_ERR("read on %s: %s", ofname, ovs_strerror(errno));
+ error = errno;
+ goto cleanup;
+ }
+ } else if (n == 0) {
+ bytes_to_read = 0;
+ } else {
+ bytes_read += n;
+ bytes_to_read -= n;
+ }
+ }
+ }
+ if (bytes_read) {
+ reply[bytes_read] = '\0';
+ if (preply) {
+ *preply = reply;
+ reply = NULL; /* prevent freeing the reply msg */
+ } else {
+ VLOG_ERR("%s returned: %s", script, reply);
+ }
+ error = EAGAIN;
+ goto cleanup;
+ }
+
+cleanup:
+ free(msg);
+ free(reply);
+ free(ofname);
+ free(ifname);
+ close(ifd);
+ close(ofd);
+ return error;
+}
+
+static int
+netdev_pltap_up(struct netdev_pltap *dev)
+ OVS_REQUIRES(dev->mutex)
+{
+ if (!netdev_pltap_finalized(dev)) {
+ return 0;
+ }
+
+ return vsys_transaction("vif_up", NULL, "%s\n"IP_FMT"\n%d\n",
+ dev->real_name,
+ IP_ARGS(dev->local_addr.sin_addr.s_addr),
+ dev->local_netmask);
+}
+
+static int
+netdev_pltap_down(struct netdev_pltap *dev)
+ OVS_REQUIRES(dev->mutex)
+{
+ if (!netdev_pltap_finalized(dev)) {
+ return 0;
+ }
+
+ return vsys_transaction("vif_down", NULL, "%s\n", dev->real_name);
+}
+
+static int
+netdev_pltap_promisc(struct netdev_pltap *dev, bool promisc)
+ OVS_REQUIRES(dev-mutex)
+{
+ if (!netdev_pltap_finalized(dev)) {
+ return 0;
+ }
+
+ return vsys_transaction("promisc", NULL, "%s\n%s",
+ dev->real_name,
+ (promisc ? "" : "-\n"));
+}
+
+static void
+netdev_pltap_sync_flags(struct netdev_pltap *dev)
+ OVS_REQUIRES(sync_list_mutex)
+{
+
+ ovs_mutex_lock(&dev->mutex);
+
+ if (dev->fd < 0 || !netdev_pltap_finalized(dev)) {
+ goto out;
+ }
+
+ VLOG_DBG("sync_flags(%s): current: %s %s target: %s %s",
+ dev->real_name,
+ (dev->flags & NETDEV_UP ? "UP" : "-"),
+ (dev->flags & NETDEV_PROMISC ? "PROMISC" : "-"),
+ (dev->new_flags & NETDEV_UP ? "UP" : "-"),
+ (dev->new_flags & NETDEV_PROMISC ? "PROMISC" : "-"));
+
+ if ((dev->new_flags & NETDEV_UP) && !(dev->flags & NETDEV_UP)) {
+ (void) netdev_pltap_up(dev);
+ } else if (!(dev->new_flags & NETDEV_UP) && (dev->flags & NETDEV_UP)) {
+ (void) netdev_pltap_down(dev);
+ }
+
+ if ((dev->new_flags & NETDEV_PROMISC) ^ (dev->flags & NETDEV_PROMISC)) {
+ (void) netdev_pltap_promisc(dev, dev->new_flags & NETDEV_PROMISC);
+ }
+
+ netdev_pltap_update_seq(dev);
+
+out:
+ sync_done(dev);
+ ovs_mutex_unlock(&dev->mutex);
+}
+
+
+static int
+netdev_pltap_get_config(const struct netdev *dev_, struct smap *args)
+{
+ struct netdev_pltap *netdev = netdev_pltap_cast(dev_);
+
+ ovs_mutex_lock(&netdev->mutex);
+ if (netdev->valid_local_ip)
+ smap_add_format(args, "local_ip", IP_FMT,
+ IP_ARGS(netdev->local_addr.sin_addr.s_addr));
+ if (netdev->valid_local_netmask)
+ smap_add_format(args, "local_netmask", "%"PRIu32,
+ ntohs(netdev->local_netmask));
+ ovs_mutex_unlock(&netdev->mutex);
+ return 0;
+}
+
+static int
+netdev_pltap_set_config(struct netdev *dev_, const struct smap *args)
+{
+ struct netdev_pltap *netdev = netdev_pltap_cast(dev_);
+ struct shash_node *node;
+
+ ovs_mutex_lock(&sync_list_mutex);
+ ovs_mutex_lock(&netdev->mutex);
+ VLOG_DBG("pltap_set_config(%s)", netdev_get_name(dev_));
+ SMAP_FOR_EACH(node, args) {
+ VLOG_DBG("arg: %s->%s", node->name, (char*)node->data);
+ if (!strcmp(node->name, "local_ip")) {
+ struct in_addr addr;
+ if (lookup_ip(node->data, &addr)) {
+ VLOG_WARN("%s: bad 'local_ip'", node->name);
+ } else {
+ netdev->local_addr.sin_addr = addr;
+ netdev->valid_local_ip = true;
+ }
+ } else if (!strcmp(node->name, "local_netmask")) {
+ netdev->local_netmask = atoi(node->data);
+ // XXX check valididy
+ netdev->valid_local_netmask = true;
+ } else {
+ VLOG_WARN("%s: unknown argument '%s'",
+ netdev_get_name(dev_), node->name);
+ }
+ }
+ if (netdev_pltap_finalized(netdev)) {
+ netdev->new_flags |= NETDEV_UP;
+ sync_needed(netdev);
+ }
+ ovs_mutex_unlock(&netdev->mutex);
+ ovs_mutex_unlock(&sync_list_mutex);
+ return 0;
+}
+
+static int
- size_t size = ofpbuf_tailroom(buffer);
- struct netdev_rx_pltap *rx = netdev_rx_pltap_cast(rx_);
++netdev_pltap_rxq_recv(struct netdev_rxq *rx_, struct ofpbuf **packet, int *c)
+{
- { .iov_base = buffer->data, .iov_len = size }
++ struct netdev_rxq_pltap *rx = netdev_rxq_pltap_cast(rx_);
+ struct tun_pi pi;
+ struct iovec iov[2] = {
+ { .iov_base = &pi, .iov_len = sizeof(pi) },
- return 0;
+ };
++ struct ofpbuf *buffer = NULL;
++ size_t size;
++ int error = 0;
++
++ buffer = ofpbuf_new_with_headroom(VLAN_ETH_HEADER_LEN + ETH_PAYLOAD_MAX,
++ DP_NETDEV_HEADROOM);
++ size = ofpbuf_tailroom(buffer);
++ iov[1].iov_base = buffer->data;
++ iov[1].iov_len = size;
+ for (;;) {
+ ssize_t retval;
+ retval = readv(rx->fd, iov, 2);
+ if (retval >= 0) {
+ if (retval <= size) {
+ buffer->size += retval;
- return EMSGSIZE;
++ goto out;
+ } else {
- netdev_rx_get_name(rx_), ovs_strerror(errno));
++ error = EMSGSIZE;
++ goto out;
+ }
+ } else if (errno != EINTR) {
+ if (errno != EAGAIN) {
+ VLOG_WARN_RL(&rl, "error receiveing Ethernet packet on %s: %s",
- return errno;
++ netdev_rxq_get_name(rx_), ovs_strerror(errno));
+ }
- netdev_pltap_rx_wait(struct netdev_rx *rx_)
++ error = errno;
++ goto out;
+ }
+ }
++out:
++ if (error) {
++ ofpbuf_delete(buffer);
++ } else {
++ dp_packet_pad(buffer);
++ packet[0] = buffer;
++ *c = 1;
++ }
++
++ return error;
+}
+
+static void
- struct netdev_rx_pltap *rx = netdev_rx_pltap_cast(rx_);
++netdev_pltap_rxq_wait(struct netdev_rxq *rx_)
+{
- netdev_pltap_send(struct netdev *netdev_, const void *buffer, size_t size)
++ struct netdev_rxq_pltap *rx = netdev_rxq_pltap_cast(rx_);
+ struct netdev_pltap *netdev =
+ netdev_pltap_cast(rx->up.netdev);
+ if (rx->fd >= 0 && netdev_pltap_finalized(netdev)) {
+ poll_fd_wait(rx->fd, POLLIN);
+ }
+}
+
+static int
- netdev_pltap_cast(netdev_);
++netdev_pltap_send(struct netdev *netdev_, struct ofpbuf *pkt, bool may_steal)
+{
++ const void *buffer = pkt->data;
++ size_t size = pkt->size;
+ struct netdev_pltap *dev =
- { .iov_base = (char*) buffer, .iov_len = size }
++ netdev_pltap_cast(netdev_);
++ int error = 0;
+ struct tun_pi pi = { 0, 0x86 };
+ struct iovec iov[2] = {
+ { .iov_base = &pi, .iov_len = sizeof(pi) },
- if (dev->fd < 0)
- return EAGAIN;
++ { .iov_base = (char*) buffer, .iov_len = size }
+ };
- if (retval != size + 4) {
- VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIdSIZE" bytes of %"PRIuSIZE") on %s",
- retval, size + 4, netdev_get_name(netdev_));
- }
- return 0;
++ if (dev->fd < 0) {
++ error = EAGAIN;
++ goto out;
++ }
+ for (;;) {
+ ssize_t retval;
+ retval = writev(dev->fd, iov, 2);
+ if (retval >= 0) {
- netdev_get_name(netdev_), ovs_strerror(errno));
++ if (retval != size + 4) {
++ VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIdSIZE" bytes of %"PRIuSIZE") on %s",
++ retval, size + 4, netdev_get_name(netdev_));
++ }
++ goto out;
+ } else if (errno != EINTR) {
+ if (errno != EAGAIN) {
+ VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
- return errno;
++ netdev_get_name(netdev_), ovs_strerror(errno));
+ }
- netdev_pltap_rx_drain(struct netdev_rx *rx_)
++ error = errno;
++ goto out;
+ }
+ }
++out:
++ if (may_steal) {
++ ofpbuf_delete(pkt);
++ }
++ return error;
+}
+
+static void
+netdev_pltap_send_wait(struct netdev *netdev_)
+{
+ struct netdev_pltap *dev =
+ netdev_pltap_cast(netdev_);
+ if (dev->fd >= 0 && netdev_pltap_finalized(dev)) {
+ poll_fd_wait(dev->fd, POLLOUT);
+ }
+}
+
+static int
- struct netdev_rx_pltap *rx = netdev_rx_pltap_cast(rx_);
++netdev_pltap_rxq_drain(struct netdev_rxq *rx_)
+{
- netdev_pltap_rx_alloc,
- netdev_pltap_rx_construct,
- netdev_pltap_rx_destruct,
- netdev_pltap_rx_dealloc,
- netdev_pltap_rx_recv,
- netdev_pltap_rx_wait,
- netdev_pltap_rx_drain,
++ struct netdev_rxq_pltap *rx = netdev_rxq_pltap_cast(rx_);
+ char buffer[128];
+ int error;
+
+ if (rx->fd < 0)
+ return EAGAIN;
+ for (;;) {
+ error = recv(rx->fd, buffer, 128, MSG_TRUNC);
+ if (error) {
+ if (error == -EAGAIN)
+ break;
+ else if (error != -EMSGSIZE)
+ return error;
+ }
+ }
+ return 0;
+}
+
+static int
+netdev_pltap_set_etheraddr(struct netdev *netdevi OVS_UNUSED,
+ const uint8_t mac[ETH_ADDR_LEN] OVS_UNUSED)
+{
+ return ENOTSUP;
+}
+
+
+// XXX from netdev-linux.c
+static int
+get_etheraddr(struct netdev_pltap *dev, uint8_t ea[ETH_ADDR_LEN])
+ OVS_REQUIRES(dev->mutex)
+{
+ struct ifreq ifr;
+ int hwaddr_family;
+ int error;
+
+ memset(&ifr, 0, sizeof ifr);
+ ovs_strzcpy(ifr.ifr_name, dev->real_name, sizeof ifr.ifr_name);
+ error = af_inet_ifreq_ioctl(dev->real_name, &ifr,
+ SIOCGIFHWADDR, "SIOCGIFHWADDR");
+ if (error) {
+ return error;
+ }
+ hwaddr_family = ifr.ifr_hwaddr.sa_family;
+ if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
+ VLOG_WARN("%s device has unknown hardware address family %d",
+ dev->real_name, hwaddr_family);
+ }
+ memcpy(ea, ifr.ifr_hwaddr.sa_data, ETH_ADDR_LEN);
+ return 0;
+}
+
+static int
+get_flags(struct netdev_pltap *dev, enum netdev_flags *flags)
+ OVS_REQUIRES(dev->mutex)
+{
+ struct ifreq ifr;
+ int error;
+
+ error = af_inet_ifreq_ioctl(dev->real_name, &ifr,
+ SIOCGIFFLAGS, "SIOCGIFFLAGS");
+ if (error) {
+ return error;
+ }
+ *flags = 0;
+ if (ifr.ifr_flags & IFF_UP)
+ *flags |= NETDEV_UP;
+ if (ifr.ifr_flags & IFF_PROMISC)
+ *flags |= NETDEV_PROMISC;
+ return 0;
+}
+
+static int
+netdev_pltap_get_etheraddr(const struct netdev *netdev,
+ uint8_t mac[ETH_ADDR_LEN])
+{
+ struct netdev_pltap *dev =
+ netdev_pltap_cast(netdev);
+ int error = 0;
+
+ ovs_mutex_lock(&dev->mutex);
+ if (dev->fd < 0) {
+ error = EAGAIN;
+ goto out;
+ }
+ error = get_etheraddr(dev, mac);
+
+out:
+ ovs_mutex_unlock(&dev->mutex);
+ return error;
+}
+
+
+// XXX can we read stats in planetlab?
+static int
+netdev_pltap_get_stats(const struct netdev *netdev OVS_UNUSED, struct netdev_stats *stats OVS_UNUSED)
+{
+ return ENOTSUP;
+}
+
+static int
+netdev_pltap_set_stats(struct netdev *netdev OVS_UNUSED, const struct netdev_stats *stats OVS_UNUSED)
+{
+ return ENOTSUP;
+}
+
+
+static int
+netdev_pltap_update_flags(struct netdev *dev_,
+ enum netdev_flags off, enum netdev_flags on,
+ enum netdev_flags *old_flagsp)
+{
+ struct netdev_pltap *netdev =
+ netdev_pltap_cast(dev_);
+ int error = 0;
+
+ ovs_mutex_lock(&sync_list_mutex);
+ ovs_mutex_lock(&netdev->mutex);
+ if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (netdev_pltap_finalized(netdev)) {
+ error = get_flags(netdev, &netdev->flags);
+ }
+ *old_flagsp = netdev->flags;
+ netdev->new_flags |= on;
+ netdev->new_flags &= ~off;
+ if (netdev->flags != netdev->new_flags) {
+ /* we cannot sync here, since we may be in a signal handler */
+ sync_needed(netdev);
+ }
+
+out:
+ ovs_mutex_unlock(&netdev->mutex);
+ ovs_mutex_unlock(&sync_list_mutex);
+ return error;
+}
+
+static unsigned int
+netdev_pltap_change_seq(const struct netdev *netdev)
+{
+ struct netdev_pltap *dev =
+ netdev_pltap_cast(netdev);
+ unsigned int change_seq;
+
+ ovs_mutex_lock(&dev->mutex);
+ change_seq = dev->change_seq;
+ ovs_mutex_unlock(&dev->mutex);
+
+ return change_seq;
+}
+\f
+/* Helper functions. */
+
+static void
+netdev_pltap_update_seq(struct netdev_pltap *dev)
+ OVS_REQUIRES(dev->mutex)
+{
+ dev->change_seq++;
+ if (!dev->change_seq) {
+ dev->change_seq++;
+ }
+}
+
+static void
+netdev_pltap_get_real_name(struct unixctl_conn *conn,
+ int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED)
+{
+ struct netdev_pltap *pltap_dev;
+
+ ovs_mutex_lock(&pltap_netdevs_mutex);
+ pltap_dev = shash_find_data(&pltap_netdevs, argv[1]);
+ if (!pltap_dev) {
+ unixctl_command_reply_error(conn, "no such pltap netdev");
+ goto out;
+ }
+ if (pltap_dev->fd < 0) {
+ unixctl_command_reply_error(conn, "no real device attached");
+ goto out;
+ }
+
+ unixctl_command_reply(conn, pltap_dev->real_name);
+
+out:
+ ovs_mutex_unlock(&pltap_netdevs_mutex);
+}
+
+static int
+netdev_pltap_init(void)
+{
+ unixctl_command_register("netdev-pltap/get-tapname", "port",
+ 1, 1, netdev_pltap_get_real_name, NULL);
+ return 0;
+}
+
+static void
+netdev_pltap_run(void)
+{
+ struct netdev_pltap *iter, *next;
+ ovs_mutex_lock(&sync_list_mutex);
+ LIST_FOR_EACH_SAFE(iter, next, sync_list, &sync_list) {
+ netdev_pltap_sync_flags(iter);
+ }
+ ovs_mutex_unlock(&sync_list_mutex);
+}
+
+static void
+netdev_pltap_wait(void)
+{
+ ovs_mutex_lock(&sync_list_mutex);
+ if (!list_is_empty(&sync_list)) {
+ VLOG_DBG("netdev_pltap: scheduling sync");
+ poll_immediate_wake();
+ }
+ ovs_mutex_unlock(&sync_list_mutex);
+}
+
+const struct netdev_class netdev_pltap_class = {
+ "pltap",
+ netdev_pltap_init,
+ netdev_pltap_run,
+ netdev_pltap_wait,
+
+ netdev_pltap_alloc,
+ netdev_pltap_construct,
+ netdev_pltap_destruct,
+ netdev_pltap_dealloc,
+ netdev_pltap_get_config,
+ netdev_pltap_set_config,
+ NULL, /* get_tunnel_config */
+
+ netdev_pltap_send,
+ netdev_pltap_send_wait,
+
+ netdev_pltap_set_etheraddr,
+ netdev_pltap_get_etheraddr,
+ NULL, /* get_mtu */
+ NULL, /* set_mtu */
+ NULL, /* get_ifindex */
+ NULL, /* get_carrier */
+ NULL, /* get_carrier_resets */
+ NULL, /* get_miimon */
+ netdev_pltap_get_stats,
+ netdev_pltap_set_stats,
+
+ NULL, /* get_features */
+ NULL, /* set_advertisements */
+
+ NULL, /* set_policing */
+ NULL, /* get_qos_types */
+ NULL, /* get_qos_capabilities */
+ NULL, /* get_qos */
+ NULL, /* set_qos */
+ NULL, /* get_queue */
+ NULL, /* set_queue */
+ NULL, /* delete_queue */
+ NULL, /* get_queue_stats */
+ NULL, /* queue_dump_start */
+ NULL, /* queue_dump_next */
+ NULL, /* queue_dump_done */
+ NULL, /* dump_queue_stats */
+
+ NULL, /* get_in4 */
+ NULL, /* set_in4 */
+ NULL, /* get_in6 */
+ NULL, /* add_router */
+ NULL, /* get_next_hop */
+ NULL, /* get_drv_info */
+ NULL, /* arp_lookup */
+
+ netdev_pltap_update_flags,
+
++ netdev_pltap_rxq_alloc,
++ netdev_pltap_rxq_construct,
++ netdev_pltap_rxq_destruct,
++ netdev_pltap_rxq_dealloc,
++ netdev_pltap_rxq_recv,
++ netdev_pltap_rxq_wait,
++ netdev_pltap_rxq_drain,
+};
--- /dev/null
- struct netdev_rx_tunnel {
- struct netdev_rx up;
+/*
+ * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ *
+ * 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 <config.h>
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "flow.h"
+#include "list.h"
++#include "dpif-netdev.h"
+#include "netdev-provider.h"
+#include "odp-util.h"
+#include "ofp-print.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "shash.h"
+#include "sset.h"
+#include "unixctl.h"
+#include "socket-util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(netdev_tunnel);
+
+struct netdev_tunnel {
+ struct netdev up;
+
+ /* Protects all members below. */
+ struct ovs_mutex mutex;
+
+ uint8_t hwaddr[ETH_ADDR_LEN];
+ struct netdev_stats stats;
+ enum netdev_flags flags;
+ int sockfd;
+ struct sockaddr_storage local_addr;
+ struct sockaddr_storage remote_addr;
+ bool valid_remote_ip;
+ bool valid_remote_port;
+ bool connected;
+ unsigned int change_seq;
+};
+
- static struct netdev_rx_tunnel *
- netdev_rx_tunnel_cast(const struct netdev_rx *rx)
++struct netdev_rxq_tunnel {
++ struct netdev_rxq up;
+ int fd;
+};
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+static struct ovs_mutex tunnel_netdevs_mutex = OVS_MUTEX_INITIALIZER;
+static struct shash tunnel_netdevs OVS_GUARDED_BY(tunnel_netdevs_mutex)
+ = SHASH_INITIALIZER(&tunnel_netdevs);
+
+static int netdev_tunnel_construct(struct netdev *netdevp_);
+static void netdev_tunnel_update_seq(struct netdev_tunnel *);
+
+static bool
+is_netdev_tunnel_class(const struct netdev_class *class)
+{
+ return class->construct == netdev_tunnel_construct;
+}
+
+static struct netdev_tunnel *
+netdev_tunnel_cast(const struct netdev *netdev)
+{
+ ovs_assert(is_netdev_tunnel_class(netdev_get_class(netdev)));
+ return CONTAINER_OF(netdev, struct netdev_tunnel, up);
+}
+
- return CONTAINER_OF(rx, struct netdev_rx_tunnel, up);
++static struct netdev_rxq_tunnel *
++netdev_rxq_tunnel_cast(const struct netdev_rxq *rx)
+{
+ ovs_assert(is_netdev_tunnel_class(netdev_get_class(rx->netdev)));
- smap_add_format(args, "remote_ip", IP_FMT,
- IP_ARGS(sin->sin_addr.s_addr));
++ return CONTAINER_OF(rx, struct netdev_rxq_tunnel, up);
+}
+
+static struct netdev *
+netdev_tunnel_alloc(void)
+{
+ struct netdev_tunnel *netdev = xzalloc(sizeof *netdev);
+ return &netdev->up;
+}
+
+static int
+netdev_tunnel_construct(struct netdev *netdev_)
+{
+ static atomic_uint next_n = ATOMIC_VAR_INIT(0);
+ struct netdev_tunnel *netdev = netdev_tunnel_cast(netdev_);
+ unsigned int n;
+
+ atomic_add(&next_n, 1, &n);
+
+ ovs_mutex_init(&netdev->mutex);
+ netdev->hwaddr[0] = 0xfe;
+ netdev->hwaddr[1] = 0xff;
+ netdev->hwaddr[2] = 0xff;
+ netdev->hwaddr[3] = n >> 16;
+ netdev->hwaddr[4] = n >> 8;
+ netdev->hwaddr[5] = n;
+ netdev->flags = 0;
+ netdev->change_seq = 1;
+ memset(&netdev->remote_addr, 0, sizeof(netdev->remote_addr));
+ netdev->valid_remote_ip = false;
+ netdev->valid_remote_port = false;
+ netdev->connected = false;
+
+
+ netdev->sockfd = inet_open_passive(SOCK_DGRAM, "", 0,
+ &netdev->local_addr, 0);
+ if (netdev->sockfd < 0) {
+ return netdev->sockfd;
+ }
+
+
+ shash_add(&tunnel_netdevs, netdev_get_name(netdev_), netdev);
+
+ n++;
+
+ VLOG_DBG("tunnel_create: name=%s, fd=%d, port=%d",
+ netdev_get_name(netdev_), netdev->sockfd, ss_get_port(&netdev->local_addr));
+
+ return 0;
+
+}
+
+static void
+netdev_tunnel_destruct(struct netdev *netdev_)
+{
+ struct netdev_tunnel *netdev = netdev_tunnel_cast(netdev_);
+
+ ovs_mutex_lock(&tunnel_netdevs_mutex);
+
+ if (netdev->sockfd != -1)
+ close(netdev->sockfd);
+
+ shash_find_and_delete(&tunnel_netdevs,
+ netdev_get_name(netdev_));
+
+ ovs_mutex_destroy(&netdev->mutex);
+ ovs_mutex_unlock(&tunnel_netdevs_mutex);
+}
+
+static void
+netdev_tunnel_dealloc(struct netdev *netdev_)
+{
+ struct netdev_tunnel *netdev = netdev_tunnel_cast(netdev_);
+ free(netdev);
+}
+
+static int
+netdev_tunnel_get_config(const struct netdev *dev_, struct smap *args)
+{
+ struct netdev_tunnel *netdev = netdev_tunnel_cast(dev_);
+
+ ovs_mutex_lock(&netdev->mutex);
+ if (netdev->valid_remote_ip) {
+ const struct sockaddr_in *sin =
+ ALIGNED_CAST(const struct sockaddr_in *, &netdev->remote_addr);
- ss_get_port(&netdev->remote_addr));
++ smap_add_format(args, "remote_ip", IP_FMT,
++ IP_ARGS(sin->sin_addr.s_addr));
+ }
+ if (netdev->valid_remote_port)
+ smap_add_format(args, "remote_port", "%"PRIu16,
- ovs_strerror(errno));
++ ss_get_port(&netdev->remote_addr));
+ ovs_mutex_unlock(&netdev->mutex);
+ return 0;
+}
+
+static int
+netdev_tunnel_connect(struct netdev_tunnel *dev)
+ OVS_REQUIRES(dev->mutex)
+{
+ char buf[1024];
+ struct sockaddr_in *sin =
+ ALIGNED_CAST(struct sockaddr_in *, &dev->remote_addr);
+ if (dev->sockfd < 0)
+ return EBADF;
+ if (!dev->valid_remote_ip || !dev->valid_remote_port)
+ return 0;
+ if (connect(dev->sockfd, (struct sockaddr*) sin, sizeof(*sin)) < 0) {
+ VLOG_DBG("%s: connect returned %s", netdev_get_name(&dev->up),
- inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, 1024),
- ss_get_port(&dev->remote_addr));
++ ovs_strerror(errno));
+ return errno;
+ }
+ dev->connected = true;
+ netdev_tunnel_update_seq(dev);
+ VLOG_DBG("%s: connected to (%s, %d)", netdev_get_name(&dev->up),
- if (!strcmp(node->name, "remote_ip")) {
- struct in_addr addr;
- if (lookup_ip(node->data, &addr)) {
- VLOG_WARN("%s: bad 'remote_ip'", node->name);
- } else {
- sin->sin_family = AF_INET;
- sin->sin_addr = addr;
- netdev->valid_remote_ip = true;
- }
- } else if (!strcmp(node->name, "remote_port")) {
- sin->sin_port = htons(atoi(node->data));
- netdev->valid_remote_port = true;
- } else {
- VLOG_WARN("%s: unknown argument '%s'",
- netdev_get_name(dev_), node->name);
- }
++ inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, 1024),
++ ss_get_port(&dev->remote_addr));
+ return 0;
+}
+
+static int
+netdev_tunnel_set_config(struct netdev *dev_, const struct smap *args)
+{
+ struct netdev_tunnel *netdev = netdev_tunnel_cast(dev_);
+ struct shash_node *node;
+ int error;
+ struct sockaddr_in *sin =
+ ALIGNED_CAST(struct sockaddr_in *, &netdev->remote_addr);
+
+ ovs_mutex_lock(&netdev->mutex);
+ VLOG_DBG("tunnel_set_config(%s)", netdev_get_name(dev_));
+ SMAP_FOR_EACH(node, args) {
+ VLOG_DBG("arg: %s->%s", node->name, (char*)node->data);
- static struct netdev_rx *
- netdev_tunnel_rx_alloc(void)
++ if (!strcmp(node->name, "remote_ip")) {
++ struct in_addr addr;
++ if (lookup_ip(node->data, &addr)) {
++ VLOG_WARN("%s: bad 'remote_ip'", node->name);
++ } else {
++ sin->sin_family = AF_INET;
++ sin->sin_addr = addr;
++ netdev->valid_remote_ip = true;
++ }
++ } else if (!strcmp(node->name, "remote_port")) {
++ sin->sin_port = htons(atoi(node->data));
++ netdev->valid_remote_port = true;
++ } else {
++ VLOG_WARN("%s: unknown argument '%s'",
++ netdev_get_name(dev_), node->name);
++ }
+ }
+ error = netdev_tunnel_connect(netdev);
+ ovs_mutex_unlock(&netdev->mutex);
+ return error;
+}
+
- struct netdev_rx_tunnel *rx = xzalloc(sizeof *rx);
++static struct netdev_rxq *
++netdev_tunnel_rxq_alloc(void)
+{
- netdev_tunnel_rx_construct(struct netdev_rx *rx_)
++ struct netdev_rxq_tunnel *rx = xzalloc(sizeof *rx);
+ return &rx->up;
+}
+
+static int
- struct netdev_rx_tunnel *rx = netdev_rx_tunnel_cast(rx_);
++netdev_tunnel_rxq_construct(struct netdev_rxq *rx_)
+{
- netdev_tunnel_rx_destruct(struct netdev_rx *rx_ OVS_UNUSED)
++ struct netdev_rxq_tunnel *rx = netdev_rxq_tunnel_cast(rx_);
+ struct netdev *netdev_ = rx->up.netdev;
+ struct netdev_tunnel *netdev = netdev_tunnel_cast(netdev_);
+
+ ovs_mutex_lock(&netdev->mutex);
+ rx->fd = netdev->sockfd;
+ ovs_mutex_unlock(&netdev->mutex);
+ return 0;
+}
+
+static void
- netdev_tunnel_rx_dealloc(struct netdev_rx *rx_)
++netdev_tunnel_rxq_destruct(struct netdev_rxq *rx_ OVS_UNUSED)
+{
+}
+
+static void
- struct netdev_rx_tunnel *rx = netdev_rx_tunnel_cast(rx_);
++netdev_tunnel_rxq_dealloc(struct netdev_rxq *rx_)
+{
- netdev_tunnel_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
++ struct netdev_rxq_tunnel *rx = netdev_rxq_tunnel_cast(rx_);
+
+ free(rx);
+}
+
+static int
- size_t size = ofpbuf_tailroom(buffer);
- struct netdev_rx_tunnel *rx = netdev_rx_tunnel_cast(rx_);
++netdev_tunnel_rxq_recv(struct netdev_rxq *rx_, struct ofpbuf **packet, int *c)
+{
- VLOG_DBG("%s: recv(%"PRIxPTR", %"PRIuSIZE", MSG_TRUNC) = %"PRIdSIZE,
- netdev_rx_get_name(rx_), (uintptr_t)buffer->data, size, retval);
++ struct netdev_rxq_tunnel *rx = netdev_rxq_tunnel_cast(rx_);
+ struct netdev_tunnel *netdev =
+ netdev_tunnel_cast(rx_->netdev);
++ struct ofpbuf *buffer = NULL;
++ size_t size;
++ int error = 0;
++
+ if (!netdev->connected)
+ return EAGAIN;
++ buffer = ofpbuf_new_with_headroom(VLAN_ETH_HEADER_LEN + ETH_PAYLOAD_MAX,
++ DP_NETDEV_HEADROOM);
++ size = ofpbuf_tailroom(buffer);
++
+ for (;;) {
+ ssize_t retval;
+ retval = recv(rx->fd, buffer->data, size, MSG_TRUNC);
- netdev->stats.rx_packets++;
- netdev->stats.rx_bytes += retval;
++ VLOG_DBG("%s: recv(%"PRIxPTR", %"PRIuSIZE", MSG_TRUNC) = %"PRIdSIZE,
++ netdev_rxq_get_name(rx_), (uintptr_t)buffer->data, size, retval);
+ if (retval >= 0) {
- return 0;
++ netdev->stats.rx_packets++;
++ netdev->stats.rx_bytes += retval;
+ if (retval <= size) {
+ buffer->size += retval;
- return EMSGSIZE;
++ goto out;
+ } else {
+ netdev->stats.rx_errors++;
+ netdev->stats.rx_length_errors++;
- netdev_rx_get_name(rx_), ovs_strerror(errno));
- netdev->stats.rx_errors++;
++ error = EMSGSIZE;
++ goto out;
+ }
+ } else if (errno != EINTR) {
+ if (errno != EAGAIN) {
+ VLOG_WARN_RL(&rl, "error receiveing Ethernet packet on %s: %s",
- return errno;
++ netdev_rxq_get_name(rx_), ovs_strerror(errno));
++ netdev->stats.rx_errors++;
+ }
- netdev_tunnel_rx_wait(struct netdev_rx *rx_)
++ error = errno;
++ goto out;
+ }
+ }
++out:
++ if (error) {
++ ofpbuf_delete(buffer);
++ } else {
++ dp_packet_pad(buffer);
++ packet[0] = buffer;
++ *c = 1;
++ }
++
++ return error;
+}
+
+static void
- struct netdev_rx_tunnel *rx =
- netdev_rx_tunnel_cast(rx_);
++netdev_tunnel_rxq_wait(struct netdev_rxq *rx_)
+{
- netdev_tunnel_send(struct netdev *netdev_, const void *buffer, size_t size)
++ struct netdev_rxq_tunnel *rx =
++ netdev_rxq_tunnel_cast(rx_);
+ if (rx->fd >= 0) {
+ poll_fd_wait(rx->fd, POLLIN);
+ }
+}
+
+static int
- netdev_tunnel_cast(netdev_);
- if (!dev->connected)
- return EAGAIN;
++netdev_tunnel_send(struct netdev *netdev_, struct ofpbuf *pkt, bool may_steal)
+{
++ const void *buffer = pkt->data;
++ size_t size = pkt->size;
+ struct netdev_tunnel *dev =
- VLOG_DBG("%s: send(%"PRIxPTR", %"PRIuSIZE") = %"PRIdSIZE,
- netdev_get_name(netdev_), (uintptr_t)buffer, size, retval);
++ netdev_tunnel_cast(netdev_);
++ int error = 0;
++ if (!dev->connected) {
++ error = EAGAIN;
++ goto out;
++ }
+ for (;;) {
+ ssize_t retval;
+ retval = send(dev->sockfd, buffer, size, 0);
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += retval;
- if (retval != size) {
- VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIdSIZE" bytes of "
- "%"PRIuSIZE") on %s", retval, size, netdev_get_name(netdev_));
- dev->stats.tx_errors++;
- }
- return 0;
++ VLOG_DBG("%s: send(%"PRIxPTR", %"PRIuSIZE") = %"PRIdSIZE,
++ netdev_get_name(netdev_), (uintptr_t)buffer, size, retval);
+ if (retval >= 0) {
- netdev_get_name(netdev_), ovs_strerror(errno));
- dev->stats.tx_errors++;
++ dev->stats.tx_packets++;
++ dev->stats.tx_bytes += retval;
++ if (retval != size) {
++ VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIdSIZE" bytes of "
++ "%"PRIuSIZE") on %s", retval, size, netdev_get_name(netdev_));
++ dev->stats.tx_errors++;
++ }
++ goto out;
+ } else if (errno != EINTR) {
+ if (errno != EAGAIN) {
+ VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
- return errno;
++ netdev_get_name(netdev_), ovs_strerror(errno));
++ dev->stats.tx_errors++;
+ }
- netdev_tunnel_rx_drain(struct netdev_rx *rx_)
++ error = errno;
++ goto out;
+ }
+ }
++out:
++ if (may_steal) {
++ ofpbuf_delete(pkt);
++ }
++
++ return error;
+}
+
+static void
+netdev_tunnel_send_wait(struct netdev *netdev_)
+{
+ struct netdev_tunnel *dev = netdev_tunnel_cast(netdev_);
+ if (dev->sockfd >= 0) {
+ poll_fd_wait(dev->sockfd, POLLOUT);
+ }
+}
+
+static int
- struct netdev_rx_tunnel *rx =
- netdev_rx_tunnel_cast(rx_);
++netdev_tunnel_rxq_drain(struct netdev_rxq *rx_)
+{
+ struct netdev_tunnel *netdev =
+ netdev_tunnel_cast(rx_->netdev);
- return 0;
++ struct netdev_rxq_tunnel *rx =
++ netdev_rxq_tunnel_cast(rx_);
+ char buffer[128];
+ int error;
+
+ if (!netdev->connected)
- error = recv(rx->fd, buffer, 128, MSG_TRUNC);
- if (error) {
++ return 0;
+ for (;;) {
- break;
++ error = recv(rx->fd, buffer, 128, MSG_TRUNC);
++ if (error) {
+ if (error == -EAGAIN)
- return error;
- }
++ break;
+ else if (error != -EMSGSIZE)
- netdev_tunnel_rx_alloc,
- netdev_tunnel_rx_construct,
- netdev_tunnel_rx_destruct,
- netdev_tunnel_rx_dealloc,
- netdev_tunnel_rx_recv,
- netdev_tunnel_rx_wait,
- netdev_tunnel_rx_drain,
++ return error;
++ }
+ }
+ return 0;
+}
+
+static int
+netdev_tunnel_set_etheraddr(struct netdev *netdev,
+ const uint8_t mac[ETH_ADDR_LEN])
+{
+ struct netdev_tunnel *dev = netdev_tunnel_cast(netdev);
+
+ ovs_mutex_lock(&dev->mutex);
+ if (!eth_addr_equals(dev->hwaddr, mac)) {
+ memcpy(dev->hwaddr, mac, ETH_ADDR_LEN);
+ netdev_tunnel_update_seq(dev);
+ }
+ ovs_mutex_unlock(&dev->mutex);
+
+ return 0;
+}
+
+static int
+netdev_tunnel_get_etheraddr(const struct netdev *netdev,
+ uint8_t mac[ETH_ADDR_LEN])
+{
+ const struct netdev_tunnel *dev = netdev_tunnel_cast(netdev);
+
+ ovs_mutex_lock(&dev->mutex);
+ memcpy(mac, dev->hwaddr, ETH_ADDR_LEN);
+ ovs_mutex_unlock(&dev->mutex);
+ return 0;
+}
+
+
+static int
+netdev_tunnel_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+{
+ const struct netdev_tunnel *dev = netdev_tunnel_cast(netdev);
+
+ ovs_mutex_lock(&dev->mutex);
+ *stats = dev->stats;
+ ovs_mutex_unlock(&dev->mutex);
+ return 0;
+}
+
+static int
+netdev_tunnel_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
+{
+ struct netdev_tunnel *dev = netdev_tunnel_cast(netdev);
+
+ ovs_mutex_lock(&dev->mutex);
+ dev->stats = *stats;
+ ovs_mutex_unlock(&dev->mutex);
+ return 0;
+}
+
+static int
+netdev_tunnel_update_flags(struct netdev *dev_,
+ enum netdev_flags off, enum netdev_flags on,
+ enum netdev_flags *old_flagsp)
+{
+ struct netdev_tunnel *netdev =
+ netdev_tunnel_cast(dev_);
+ int error = 0;
+
+ ovs_mutex_lock(&netdev->mutex);
+ if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ // XXX should we actually do something with these flags?
+ *old_flagsp = netdev->flags;
+ netdev->flags |= on;
+ netdev->flags &= ~off;
+ if (*old_flagsp != netdev->flags) {
+ netdev_tunnel_update_seq(netdev);
+ }
+
+out:
+ ovs_mutex_unlock(&netdev->mutex);
+ return error;
+}
+
+\f
+/* Helper functions. */
+
+static void
+netdev_tunnel_update_seq(struct netdev_tunnel *dev)
+ OVS_REQUIRES(dev->mutex)
+{
+ dev->change_seq++;
+ if (!dev->change_seq) {
+ dev->change_seq++;
+ }
+}
+
+static void
+netdev_tunnel_get_port(struct unixctl_conn *conn,
+ int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED)
+{
+ struct netdev_tunnel *tunnel_dev;
+ char buf[6];
+
+ ovs_mutex_lock(&tunnel_netdevs_mutex);
+ tunnel_dev = shash_find_data(&tunnel_netdevs, argv[1]);
+ if (!tunnel_dev) {
+ unixctl_command_reply_error(conn, "no such tunnel netdev");
+ goto out;
+ }
+
+ ovs_mutex_lock(&tunnel_dev->mutex);
+ sprintf(buf, "%d", ss_get_port(&tunnel_dev->local_addr));
+ ovs_mutex_unlock(&tunnel_dev->mutex);
+
+ unixctl_command_reply(conn, buf);
+out:
+ ovs_mutex_unlock(&tunnel_netdevs_mutex);
+}
+
+static void
+netdev_tunnel_get_tx_bytes(struct unixctl_conn *conn,
+ int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED)
+{
+ struct netdev_tunnel *tunnel_dev;
+ char buf[128];
+
+ ovs_mutex_lock(&tunnel_netdevs_mutex);
+ tunnel_dev = shash_find_data(&tunnel_netdevs, argv[1]);
+ if (!tunnel_dev) {
+ unixctl_command_reply_error(conn, "no such tunnel netdev");
+ goto out;
+ }
+
+ ovs_mutex_lock(&tunnel_dev->mutex);
+ sprintf(buf, "%"PRIu64, tunnel_dev->stats.tx_bytes);
+ ovs_mutex_unlock(&tunnel_dev->mutex);
+ unixctl_command_reply(conn, buf);
+out:
+ ovs_mutex_unlock(&tunnel_netdevs_mutex);
+}
+
+static void
+netdev_tunnel_get_rx_bytes(struct unixctl_conn *conn,
+ int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED)
+{
+ struct netdev_tunnel *tunnel_dev;
+ char buf[128];
+
+ ovs_mutex_lock(&tunnel_netdevs_mutex);
+ tunnel_dev = shash_find_data(&tunnel_netdevs, argv[1]);
+ if (!tunnel_dev) {
+ unixctl_command_reply_error(conn, "no such tunnel netdev");
+ goto out;
+ }
+
+ sprintf(buf, "%"PRIu64, tunnel_dev->stats.rx_bytes);
+ unixctl_command_reply(conn, buf);
+out:
+ ovs_mutex_unlock(&tunnel_netdevs_mutex);
+}
+
+
+static int
+netdev_tunnel_init(void)
+{
+ unixctl_command_register("netdev-tunnel/get-port", "NAME",
+ 1, 1, netdev_tunnel_get_port, NULL);
+ unixctl_command_register("netdev-tunnel/get-tx-bytes", "NAME",
+ 1, 1, netdev_tunnel_get_tx_bytes, NULL);
+ unixctl_command_register("netdev-tunnel/get-rx-bytes", "NAME",
+ 1, 1, netdev_tunnel_get_rx_bytes, NULL);
+ return 0;
+}
+
+static void
+netdev_tunnel_run(void)
+{
+}
+
+static void
+netdev_tunnel_wait(void)
+{
+}
+
+const struct netdev_class netdev_tunnel_class = {
+ "tunnel",
+ netdev_tunnel_init,
+ netdev_tunnel_run,
+ netdev_tunnel_wait,
+
+ netdev_tunnel_alloc,
+ netdev_tunnel_construct,
+ netdev_tunnel_destruct,
+ netdev_tunnel_dealloc,
+ netdev_tunnel_get_config,
+ netdev_tunnel_set_config,
+ NULL, /* get_tunnel_config */
+
+ netdev_tunnel_send,
+ netdev_tunnel_send_wait,
+
+ netdev_tunnel_set_etheraddr,
+ netdev_tunnel_get_etheraddr,
+ NULL, /* get_mtu */
+ NULL, /* set_mtu */
+ NULL, /* get_ifindex */
+ NULL, /* get_carrier */
+ NULL, /* get_carrier_resets */
+ NULL, /* get_miimon */
+ netdev_tunnel_get_stats,
+ netdev_tunnel_set_stats,
+
+ NULL, /* get_features */
+ NULL, /* set_advertisements */
+
+ NULL, /* set_policing */
+ NULL, /* get_qos_types */
+ NULL, /* get_qos_capabilities */
+ NULL, /* get_qos */
+ NULL, /* set_qos */
+ NULL, /* get_queue */
+ NULL, /* set_queue */
+ NULL, /* delete_queue */
+ NULL, /* get_queue_stats */
+ NULL, /* queue_dump_start */
+ NULL, /* queue_dump_next */
+ NULL, /* queue_dump_done */
+ NULL, /* dump_queue_stats */
+
+ NULL, /* get_in4 */
+ NULL, /* set_in4 */
+ NULL, /* get_in6 */
+ NULL, /* add_router */
+ NULL, /* get_next_hop */
+ NULL, /* get_status */
+ NULL, /* arp_lookup */
+
+ netdev_tunnel_update_flags,
+
++ netdev_tunnel_rxq_alloc,
++ netdev_tunnel_rxq_construct,
++ netdev_tunnel_rxq_destruct,
++ netdev_tunnel_rxq_dealloc,
++ netdev_tunnel_rxq_recv,
++ netdev_tunnel_rxq_wait,
++ netdev_tunnel_rxq_drain,
+};
netdev_register_provider(&netdev_tap_class);
netdev_register_provider(&netdev_bsd_class);
#endif
+ netdev_register_provider(&netdev_tunnel_class);
+ netdev_register_provider(&netdev_pltap_class);
+ netdev_dpdk_register();
ovsthread_once_done(&once);
}