#include "fatal-signal.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
+#include "ovs-thread.h"
#include "packets.h"
#include "poll-loop.h"
#include "socket-util.h"
int fd;
};
-static const struct netdev_rx_class netdev_rx_bsd_class;
-
struct netdev_bsd {
struct netdev up;
unsigned int cache_valid;
VALID_CARRIER = 1 << 5
};
-#if defined(__NetBSD__)
-/* AF_LINK socket used for netdev_bsd_get_stats and set_etheraddr */
-static int af_link_sock = -1;
-#endif /* defined(__NetBSD__) */
-
#define PCAP_SNAPLEN 2048
static int ifr_get_flags(const struct ifreq *);
static void ifr_set_flags(struct ifreq *, int flags);
-static int netdev_bsd_init(void);
+#ifdef __NetBSD__
+static int af_link_ioctl(int command, const void *arg);
+#endif
+
+static void netdev_bsd_run(void);
static bool
is_netdev_bsd_class(const struct netdev_class *netdev_class)
{
- return netdev_class->init == netdev_bsd_init;
+ return netdev_class->run == netdev_bsd_run;
}
static struct netdev_bsd *
static struct netdev_rx_bsd *
netdev_rx_bsd_cast(const struct netdev_rx *rx)
{
- netdev_rx_assert_class(rx, &netdev_rx_bsd_class);
+ ovs_assert(is_netdev_bsd_class(netdev_get_class(rx->netdev)));
return CONTAINER_OF(rx, struct netdev_rx_bsd, up);
}
return netdev_bsd_cast(netdev)->kernel_name;
}
-/* Initialize the AF_INET socket used for ioctl operations */
-static int
-netdev_bsd_init(void)
-{
-#if defined(__NetBSD__)
- static int status = -1;
-
- if (status >= 0) { /* already initialized */
- return status;
- }
-
- af_link_sock = socket(AF_LINK, SOCK_DGRAM, 0);
- status = af_link_sock >= 0 ? 0 : errno;
- if (status) {
- VLOG_ERR("failed to create link socket: %s", ovs_strerror(status));
- }
-
- return status;
-#else
- return 0;
-#endif /* defined(__NetBSD__) */
-}
-
/*
* Perform periodic work needed by netdev. In BSD netdevs it checks for any
* interface status changes, and eventually calls all the user callbacks.
return 0;
}
-/* Allocate a netdev_bsd structure */
+static struct netdev *
+netdev_bsd_alloc(void)
+{
+ struct netdev_bsd *netdev = xzalloc(sizeof *netdev);
+ return &netdev->up;
+}
+
static int
-netdev_bsd_create_system(const struct netdev_class *class, const char *name,
- struct netdev **netdevp)
+netdev_bsd_construct_system(struct netdev *netdev_)
{
- struct netdev_bsd *netdev;
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
enum netdev_flags flags;
int error;
return error;
}
- netdev = xzalloc(sizeof *netdev);
netdev->change_seq = 1;
- netdev_init(&netdev->up, name, class);
netdev->tap_fd = -1;
- netdev->kernel_name = xstrdup(name);
+ netdev->kernel_name = xstrdup(netdev_->name);
/* Verify that the netdev really exists by attempting to read its flags */
- error = netdev_get_flags(&netdev->up, &flags);
+ error = netdev_get_flags(netdev_, &flags);
if (error == ENXIO) {
free(netdev->kernel_name);
- netdev_uninit(&netdev->up, false);
- free(netdev);
cache_notifier_unref();
return error;
}
- *netdevp = &netdev->up;
return 0;
}
-/*
- * Allocate a netdev_bsd structure with 'tap' class.
- */
static int
-netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
- struct netdev **netdevp)
+netdev_bsd_construct_tap(struct netdev *netdev_)
{
- struct netdev_bsd *netdev = NULL;
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+ const char *name = netdev_->name;
int error = 0;
struct ifreq ifr;
char *kernel_name = NULL;
goto error;
}
- /* allocate the device structure and set the internal flag */
- netdev = xzalloc(sizeof *netdev);
-
memset(&ifr, 0, sizeof(ifr));
/* Create a tap device by opening /dev/tap. The TAPGIFNAME ioctl is used
goto error_unref_notifier;
}
- /* initialize the device structure and
- * link the structure to its netdev */
- netdev_init(&netdev->up, name, class);
netdev->kernel_name = kernel_name;
- *netdevp = &netdev->up;
return 0;
error_unref_notifier:
cache_notifier_unref();
error:
- free(netdev);
free(kernel_name);
return error;
}
static void
-netdev_bsd_destroy(struct netdev *netdev_)
+netdev_bsd_destruct(struct netdev *netdev_)
{
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
pcap_close(netdev->pcap);
}
free(netdev->kernel_name);
+}
+
+static void
+netdev_bsd_dealloc(struct netdev *netdev_)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
free(netdev);
}
return error;
}
+static struct netdev_rx *
+netdev_bsd_rx_alloc(void)
+{
+ struct netdev_rx_bsd *rx = xzalloc(sizeof *rx);
+ return &rx->up;
+}
+
static int
-netdev_bsd_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
+netdev_bsd_rx_construct(struct netdev_rx *rx_)
{
+ struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
+ struct netdev *netdev_ = rx->up.netdev;
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
- struct netdev_rx_bsd *rx;
- pcap_t *pcap;
- int fd;
-
if (!strcmp(netdev_get_type(netdev_), "tap")) {
- pcap = NULL;
- fd = netdev->tap_fd;
+ rx->pcap_handle = NULL;
+ rx->fd = netdev->tap_fd;
} else {
int error = netdev_bsd_open_pcap(netdev_get_kernel_name(netdev_),
- &pcap, &fd);
+ &rx->pcap_handle, &rx->fd);
if (error) {
return error;
}
netdev_bsd_changed(netdev);
}
- rx = xmalloc(sizeof *rx);
- netdev_rx_init(&rx->up, netdev_, &netdev_rx_bsd_class);
- rx->pcap_handle = pcap;
- rx->fd = fd;
-
- *rxp = &rx->up;
return 0;
}
static void
-netdev_rx_bsd_destroy(struct netdev_rx *rx_)
+netdev_bsd_rx_destruct(struct netdev_rx *rx_)
{
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
if (rx->pcap_handle) {
pcap_close(rx->pcap_handle);
}
+}
+
+static void
+netdev_bsd_rx_dealloc(struct netdev_rx *rx_)
+{
+ struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
+
free(rx);
}
}
}
-
static int
-netdev_rx_bsd_recv(struct netdev_rx *rx_, void *data, size_t size)
+netdev_bsd_rx_recv(struct netdev_rx *rx_, void *data, size_t size)
{
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
* when a packet is ready to be received with netdev_rx_recv() on 'rx'.
*/
static void
-netdev_rx_bsd_wait(struct netdev_rx *rx_)
+netdev_bsd_rx_wait(struct netdev_rx *rx_)
{
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
/* Discards all packets waiting to be received from 'rx'. */
static int
-netdev_rx_bsd_drain(struct netdev_rx *rx_)
+netdev_bsd_rx_drain(struct netdev_rx *rx_)
{
struct ifreq ifr;
struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
return 0;
#elif defined(__NetBSD__)
struct ifdatareq ifdr;
- int saved_errno;
- int ret;
+ int error;
memset(&ifdr, 0, sizeof(ifdr));
strncpy(ifdr.ifdr_name, netdev_get_kernel_name(netdev_),
sizeof(ifdr.ifdr_name));
- ret = ioctl(af_link_sock, SIOCGIFDATA, &ifdr);
- saved_errno = errno;
- if (ret == -1) {
- return saved_errno;
+ error = af_link_ioctl(SIOCGIFDATA, &ifdr);
+ if (!error) {
+ convert_stats(stats, &ifdr.ifdr_data);
}
- convert_stats(stats, &ifdr.ifdr_data);
- return 0;
+ return error;
#else
#error not implemented
#endif
const struct netdev_class netdev_bsd_class = {
"system",
- netdev_bsd_init,
+ NULL, /* init */
netdev_bsd_run,
netdev_bsd_wait,
- netdev_bsd_create_system,
- netdev_bsd_destroy,
+ netdev_bsd_alloc,
+ netdev_bsd_construct_system,
+ netdev_bsd_destruct,
+ netdev_bsd_dealloc,
NULL, /* get_config */
NULL, /* set_config */
NULL, /* get_tunnel_config */
- netdev_bsd_rx_open,
-
netdev_bsd_send,
netdev_bsd_send_wait,
netdev_bsd_update_flags,
- netdev_bsd_change_seq
+ netdev_bsd_change_seq,
+
+ netdev_bsd_rx_alloc,
+ netdev_bsd_rx_construct,
+ netdev_bsd_rx_destruct,
+ netdev_bsd_rx_dealloc,
+ netdev_bsd_rx_recv,
+ netdev_bsd_rx_wait,
+ netdev_bsd_rx_drain,
};
const struct netdev_class netdev_tap_class = {
"tap",
- netdev_bsd_init,
+ NULL, /* init */
netdev_bsd_run,
netdev_bsd_wait,
- netdev_bsd_create_tap,
- netdev_bsd_destroy,
+ netdev_bsd_alloc,
+ netdev_bsd_construct_tap,
+ netdev_bsd_destruct,
+ netdev_bsd_dealloc,
NULL, /* get_config */
NULL, /* set_config */
NULL, /* get_tunnel_config */
- netdev_bsd_rx_open,
-
netdev_bsd_send,
netdev_bsd_send_wait,
netdev_bsd_update_flags,
- netdev_bsd_change_seq
-};
+ netdev_bsd_change_seq,
-static const struct netdev_rx_class netdev_rx_bsd_class = {
- netdev_rx_bsd_destroy,
- netdev_rx_bsd_recv,
- netdev_rx_bsd_wait,
- netdev_rx_bsd_drain,
+ netdev_bsd_rx_alloc,
+ netdev_bsd_rx_construct,
+ netdev_bsd_rx_destruct,
+ netdev_bsd_rx_dealloc,
+ netdev_bsd_rx_recv,
+ netdev_bsd_rx_wait,
+ netdev_bsd_rx_drain,
};
\f
struct if_laddrreq req;
struct sockaddr_dl *sdl;
struct sockaddr_storage oldaddr;
- int ret;
+ int error;
/*
* get the old address, add new one, and then remove old one.
req.addr.ss_family = hwaddr_family;
sdl = (struct sockaddr_dl *)&req.addr;
sdl->sdl_alen = hwaddr_len;
- ret = ioctl(af_link_sock, SIOCGLIFADDR, &req);
- if (ret == -1) {
- return errno;
+
+ error = af_link_ioctl(SIOCGLIFADDR, &req);
+ if (error) {
+ return error;
}
if (!memcmp(&sdl->sdl_data[sdl->sdl_nlen], mac, hwaddr_len)) {
return 0;
sdl->sdl_alen = hwaddr_len;
sdl->sdl_family = hwaddr_family;
memcpy(sdl->sdl_data, mac, hwaddr_len);
- ret = ioctl(af_link_sock, SIOCALIFADDR, &req);
- if (ret == -1) {
- return errno;
+ error = af_link_ioctl(SIOCALIFADDR, &req);
+ if (error) {
+ return error;
}
memset(&req, 0, sizeof(req));
strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
req.addr = oldaddr;
- ret = ioctl(af_link_sock, SIOCDLIFADDR, &req);
- if (ret == -1) {
- return errno;
- }
- return 0;
+ return af_link_ioctl(SIOCDLIFADDR, &req);
#else
#error not implemented
#endif
ifr->ifr_flagshigh = flags >> 16;
#endif
}
+
+/* Calls ioctl() on an AF_LINK sock, passing the specified 'command' and
+ * 'arg'. Returns 0 if successful, otherwise a positive errno value. */
+int
+af_link_ioctl(int command, const void *arg)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static int sock;
+
+ if (ovsthread_once_start(&once)) {
+ sock = socket(AF_LINK, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ sock = -errno;
+ VLOG_ERR("failed to create link socket: %s", ovs_strerror(errno));
+ }
+ ovsthread_once_done(&once);
+ }
+
+ return (sock < 0 ? -sock
+ : ioctl(sock, command, arg) == -1 ? errno
+ : 0);
+}
bool listening;
};
-static const struct netdev_rx_class netdev_rx_dummy_class;
-
static unixctl_cb_func netdev_dummy_set_admin_state;
-static int netdev_dummy_create(const struct netdev_class *, const char *,
- struct netdev **);
+static int netdev_dummy_construct(struct netdev *);
static void netdev_dummy_poll_notify(struct netdev_dummy *);
static void netdev_dummy_queue_packet(struct netdev_dummy *, struct ofpbuf *);
static bool
is_dummy_class(const struct netdev_class *class)
{
- return class->create == netdev_dummy_create;
+ return class->construct == netdev_dummy_construct;
}
static struct netdev_dummy *
static struct netdev_rx_dummy *
netdev_rx_dummy_cast(const struct netdev_rx *rx)
{
- netdev_rx_assert_class(rx, &netdev_rx_dummy_class);
+ ovs_assert(is_dummy_class(netdev_get_class(rx->netdev)));
return CONTAINER_OF(rx, struct netdev_rx_dummy, up);
}
shash_destroy(&dummy_netdevs);
}
+static struct netdev *
+netdev_dummy_alloc(void)
+{
+ struct netdev_dummy *netdev = xzalloc(sizeof *netdev);
+ return &netdev->up;
+}
+
static int
-netdev_dummy_create(const struct netdev_class *class, const char *name,
- struct netdev **netdevp)
+netdev_dummy_construct(struct netdev *netdev_)
{
static atomic_uint next_n = ATOMIC_VAR_INIT(0xaa550000);
- struct netdev_dummy *netdev;
+ struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
unsigned int n;
atomic_add(&next_n, 1, &n);
- netdev = xzalloc(sizeof *netdev);
- netdev_init(&netdev->up, name, class);
netdev->hwaddr[0] = 0xaa;
netdev->hwaddr[1] = 0x55;
netdev->hwaddr[2] = n >> 24;
list_init(&netdev->rxes);
- *netdevp = &netdev->up;
-
return 0;
}
static void
-netdev_dummy_destroy(struct netdev *netdev_)
+netdev_dummy_destruct(struct netdev *netdev_)
{
struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
size_t i;
dummy_stream_close(&netdev->streams[i]);
}
free(netdev->streams);
+}
+
+static void
+netdev_dummy_dealloc(struct netdev *netdev_)
+{
+ struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
+
free(netdev);
}
return 0;
}
+static struct netdev_rx *
+netdev_dummy_rx_alloc(void)
+{
+ struct netdev_rx_dummy *rx = xzalloc(sizeof *rx);
+ return &rx->up;
+}
+
static int
-netdev_dummy_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
+netdev_dummy_rx_construct(struct netdev_rx *rx_)
{
- struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
- struct netdev_rx_dummy *rx;
+ struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
+ struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev);
- rx = xmalloc(sizeof *rx);
- netdev_rx_init(&rx->up, &netdev->up, &netdev_rx_dummy_class);
list_push_back(&netdev->rxes, &rx->node);
list_init(&rx->recv_queue);
rx->recv_queue_len = 0;
- *rxp = &rx->up;
return 0;
}
+static void
+netdev_dummy_rx_destruct(struct netdev_rx *rx_)
+{
+ struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
+
+ list_remove(&rx->node);
+ ofpbuf_list_delete(&rx->recv_queue);
+}
+
+static void
+netdev_dummy_rx_dealloc(struct netdev_rx *rx_)
+{
+ struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
+
+ free(rx);
+}
+
static int
-netdev_rx_dummy_recv(struct netdev_rx *rx_, void *buffer, size_t size)
+netdev_dummy_rx_recv(struct netdev_rx *rx_, void *buffer, size_t size)
{
struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
struct ofpbuf *packet;
}
static void
-netdev_rx_dummy_destroy(struct netdev_rx *rx_)
-{
- struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
- list_remove(&rx->node);
- ofpbuf_list_delete(&rx->recv_queue);
- free(rx);
-}
-
-static void
-netdev_rx_dummy_wait(struct netdev_rx *rx_)
+netdev_dummy_rx_wait(struct netdev_rx *rx_)
{
struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
if (!list_is_empty(&rx->recv_queue)) {
}
static int
-netdev_rx_dummy_drain(struct netdev_rx *rx_)
+netdev_dummy_rx_drain(struct netdev_rx *rx_)
{
struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
ofpbuf_list_delete(&rx->recv_queue);
netdev_dummy_run,
netdev_dummy_wait,
- netdev_dummy_create,
- netdev_dummy_destroy,
+ netdev_dummy_alloc,
+ netdev_dummy_construct,
+ netdev_dummy_destruct,
+ netdev_dummy_dealloc,
netdev_dummy_get_config,
netdev_dummy_set_config,
NULL, /* get_tunnel_config */
- netdev_dummy_rx_open,
-
netdev_dummy_send, /* send */
NULL, /* send_wait */
netdev_dummy_update_flags,
- netdev_dummy_change_seq
-};
+ netdev_dummy_change_seq,
-static const struct netdev_rx_class netdev_rx_dummy_class = {
- netdev_rx_dummy_destroy,
- netdev_rx_dummy_recv,
- netdev_rx_dummy_wait,
- netdev_rx_dummy_drain,
+ netdev_dummy_rx_alloc,
+ netdev_dummy_rx_construct,
+ netdev_dummy_rx_destruct,
+ netdev_dummy_rx_dealloc,
+ netdev_dummy_rx_recv,
+ netdev_dummy_rx_wait,
+ netdev_dummy_rx_drain,
};
static struct ofpbuf *
int fd;
};
-static const struct netdev_rx_class netdev_rx_linux_class;
-
/* This is set pretty low because we probably won't learn anything from the
* additional log messages. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
static struct netdev_rx_linux *
netdev_rx_linux_cast(const struct netdev_rx *rx)
{
- netdev_rx_assert_class(rx, &netdev_rx_linux_class);
+ ovs_assert(is_netdev_linux_class(netdev_get_class(rx->netdev)));
return CONTAINER_OF(rx, struct netdev_rx_linux, up);
}
\f
}
}
+static struct netdev *
+netdev_linux_alloc(void)
+{
+ struct netdev_linux *netdev = xzalloc(sizeof *netdev);
+ return &netdev->up;
+}
+
+static int
+netdev_linux_common_construct(struct netdev_linux *netdev)
+{
+ netdev->change_seq = 1;
+
+ return cache_notifier_ref();
+}
+
/* Creates system and internal devices. */
static int
-netdev_linux_create(const struct netdev_class *class, const char *name,
- struct netdev **netdevp)
+netdev_linux_construct(struct netdev *netdev_)
{
- struct netdev_linux *netdev;
+ struct netdev_linux *netdev = netdev_linux_cast(netdev_);
int error;
- error = cache_notifier_ref();
+ error = netdev_linux_common_construct(netdev);
if (error) {
return error;
}
- netdev = xzalloc(sizeof *netdev);
- netdev->change_seq = 1;
- netdev_init(&netdev->up, name, class);
error = get_flags(&netdev->up, &netdev->ifi_flags);
if (error == ENODEV) {
- if (class != &netdev_internal_class) {
+ if (netdev->up.netdev_class != &netdev_internal_class) {
/* The device does not exist, so don't allow it to be opened. */
- netdev_uninit(&netdev->up, false);
cache_notifier_unref();
- free(netdev);
return ENODEV;
} else {
/* "Internal" netdevs have to be created as netdev objects before
}
}
- *netdevp = &netdev->up;
return 0;
}
* buffers, across all readers. Therefore once data is read it will
* be unavailable to other reads for tap devices. */
static int
-netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
- const char *name, struct netdev **netdevp)
+netdev_linux_construct_tap(struct netdev *netdev_)
{
- struct netdev_linux *netdev;
+ struct netdev_linux *netdev = netdev_linux_cast(netdev_);
static const char tap_dev[] = "/dev/net/tun";
+ const char *name = netdev_->name;
struct ifreq ifr;
int error;
- netdev = xzalloc(sizeof *netdev);
- netdev->change_seq = 1;
-
- error = cache_notifier_ref();
+ error = netdev_linux_common_construct(netdev);
if (error) {
goto error;
}
goto error_close;
}
- netdev_init(&netdev->up, name, &netdev_tap_class);
- *netdevp = &netdev->up;
return 0;
error_close:
error_unref_notifier:
cache_notifier_unref();
error:
- free(netdev);
return error;
}
static void
-netdev_linux_destroy(struct netdev *netdev_)
+netdev_linux_destruct(struct netdev *netdev_)
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
{
close(netdev->tap_fd);
}
- free(netdev);
cache_notifier_unref();
}
+static void
+netdev_linux_dealloc(struct netdev *netdev_)
+{
+ struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+ free(netdev);
+}
+
+static struct netdev_rx *
+netdev_linux_rx_alloc(void)
+{
+ struct netdev_rx_linux *rx = xzalloc(sizeof *rx);
+ return &rx->up;
+}
+
static int
-netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
+netdev_linux_rx_construct(struct netdev_rx *rx_)
{
+ struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+ struct netdev *netdev_ = rx->up.netdev;
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
- bool is_tap = is_tap_netdev(netdev_);
- struct netdev_rx_linux *rx;
int error;
- int fd;
- if (is_tap) {
- fd = netdev->tap_fd;
+ rx->is_tap = is_tap_netdev(netdev_);
+ if (rx->is_tap) {
+ rx->fd = netdev->tap_fd;
} else {
struct sockaddr_ll sll;
int ifindex;
};
/* Create file descriptor. */
- fd = socket(PF_PACKET, SOCK_RAW, 0);
- if (fd < 0) {
+ rx->fd = socket(PF_PACKET, SOCK_RAW, 0);
+ if (rx->fd < 0) {
error = errno;
VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error));
goto error;
}
/* Set non-blocking mode. */
- error = set_nonblocking(fd);
+ error = set_nonblocking(rx->fd);
if (error) {
goto error;
}
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
sll.sll_protocol = (OVS_FORCE unsigned short int) htons(ETH_P_ALL);
- if (bind(fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
+ if (bind(rx->fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
error = errno;
VLOG_ERR("%s: failed to bind raw socket (%s)",
netdev_get_name(netdev_), ovs_strerror(error));
}
/* Filter for only inbound packets. */
- error = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog,
+ error = setsockopt(rx->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog,
sizeof fprog);
if (error) {
error = errno;
}
}
- rx = xmalloc(sizeof *rx);
- netdev_rx_init(&rx->up, netdev_, &netdev_rx_linux_class);
- rx->is_tap = is_tap;
- rx->fd = fd;
-
- *rxp = &rx->up;
return 0;
error:
- if (fd >= 0) {
- close(fd);
+ if (rx->fd >= 0) {
+ close(rx->fd);
}
return error;
}
static void
-netdev_rx_linux_destroy(struct netdev_rx *rx_)
+netdev_linux_rx_destruct(struct netdev_rx *rx_)
{
struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
if (!rx->is_tap) {
close(rx->fd);
}
+}
+
+static void
+netdev_linux_rx_dealloc(struct netdev_rx *rx_)
+{
+ struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+
free(rx);
}
static int
-netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size)
+netdev_linux_rx_recv(struct netdev_rx *rx_, void *data, size_t size)
{
struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
ssize_t retval;
}
static void
-netdev_rx_linux_wait(struct netdev_rx *rx_)
+netdev_linux_rx_wait(struct netdev_rx *rx_)
{
struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
poll_fd_wait(rx->fd, POLLIN);
}
static int
-netdev_rx_linux_drain(struct netdev_rx *rx_)
+netdev_linux_rx_drain(struct netdev_rx *rx_)
{
struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
if (rx->is_tap) {
return netdev_linux_cast(netdev)->change_seq;
}
-#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS, \
+#define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS, \
GET_FEATURES, GET_STATUS) \
{ \
NAME, \
netdev_linux_run, \
netdev_linux_wait, \
\
- CREATE, \
- netdev_linux_destroy, \
+ netdev_linux_alloc, \
+ CONSTRUCT, \
+ netdev_linux_destruct, \
+ netdev_linux_dealloc, \
NULL, /* get_config */ \
NULL, /* set_config */ \
NULL, /* get_tunnel_config */ \
\
- netdev_linux_rx_open, \
- \
netdev_linux_send, \
netdev_linux_send_wait, \
\
\
netdev_linux_update_flags, \
\
- netdev_linux_change_seq \
+ netdev_linux_change_seq, \
+ \
+ netdev_linux_rx_alloc, \
+ netdev_linux_rx_construct, \
+ netdev_linux_rx_destruct, \
+ netdev_linux_rx_dealloc, \
+ netdev_linux_rx_recv, \
+ netdev_linux_rx_wait, \
+ netdev_linux_rx_drain, \
}
const struct netdev_class netdev_linux_class =
NETDEV_LINUX_CLASS(
"system",
- netdev_linux_create,
+ netdev_linux_construct,
netdev_linux_get_stats,
NULL, /* set_stats */
netdev_linux_get_features,
const struct netdev_class netdev_tap_class =
NETDEV_LINUX_CLASS(
"tap",
- netdev_linux_create_tap,
+ netdev_linux_construct_tap,
netdev_tap_get_stats,
NULL, /* set_stats */
netdev_linux_get_features,
const struct netdev_class netdev_internal_class =
NETDEV_LINUX_CLASS(
"internal",
- netdev_linux_create,
+ netdev_linux_construct,
netdev_internal_get_stats,
netdev_internal_set_stats,
NULL, /* get_features */
netdev_internal_get_status);
-
-static const struct netdev_rx_class netdev_rx_linux_class = {
- netdev_rx_linux_destroy,
- netdev_rx_linux_recv,
- netdev_rx_linux_wait,
- netdev_rx_linux_drain,
-};
\f
/* HTB traffic control class. */
/* A network device (e.g. an Ethernet device).
*
- * Network device implementations should treat this structure as opaque. */
+ * Network device implementations may read these members but should not modify
+ * them. */
struct netdev {
char *name; /* Name of network device. */
const struct netdev_class *netdev_class; /* Functions to control
struct list saved_flags_list; /* Contains "struct netdev_saved_flags". */
};
-void netdev_init(struct netdev *, const char *name,
- const struct netdev_class *);
-void netdev_uninit(struct netdev *, bool destroy);
const char *netdev_get_type(const struct netdev *);
const struct netdev_class *netdev_get_class(const struct netdev *);
const char *netdev_get_name(const struct netdev *);
void netdev_get_devices(const struct netdev_class *,
struct shash *device_list);
+/* A data structure for capturing packets received by a network device.
+ *
+ * Network device implementations may read these members but should not modify
+ * them.
+ *
+ * None of these members change during the lifetime of a struct netdev_rx. */
+struct netdev_rx {
+ struct netdev *netdev; /* Owns a reference to the netdev. */
+};
+
+struct netdev *netdev_rx_get_netdev(const struct netdev_rx *);
+
/* Network device class structure, to be defined by each implementation of a
* network device.
*
* These functions return 0 if successful or a positive errno value on failure,
- * except where otherwise noted. */
+ * except where otherwise noted.
+ *
+ *
+ * Data Structures
+ * ===============
+ *
+ * These functions work primarily with two different kinds of data structures:
+ *
+ * - "struct netdev", which represents a network device.
+ *
+ * - "struct netdev_rx", which represents a handle for capturing packets
+ * received on a network device
+ *
+ * Each of these data structures contains all of the implementation-independent
+ * generic state for the respective concept, called the "base" state. None of
+ * them contains any extra space for implementations to use. Instead, each
+ * implementation is expected to declare its own data structure that contains
+ * an instance of the generic data structure plus additional
+ * implementation-specific members, called the "derived" state. The
+ * implementation can use casts or (preferably) the CONTAINER_OF macro to
+ * obtain access to derived state given only a pointer to the embedded generic
+ * data structure.
+ *
+ *
+ * Life Cycle
+ * ==========
+ *
+ * Four stylized functions accompany each of these data structures:
+ *
+ * "alloc" "construct" "destruct" "dealloc"
+ * ------------ ---------------- --------------- --------------
+ * netdev ->alloc ->construct ->destruct ->dealloc
+ * netdev_rx ->rx_alloc ->rx_construct ->rx_destruct ->rx_dealloc
+ *
+ * Any instance of a given data structure goes through the following life
+ * cycle:
+ *
+ * 1. The client calls the "alloc" function to obtain raw memory. If "alloc"
+ * fails, skip all the other steps.
+ *
+ * 2. The client initializes all of the data structure's base state. If this
+ * fails, skip to step 7.
+ *
+ * 3. The client calls the "construct" function. The implementation
+ * initializes derived state. It may refer to the already-initialized
+ * base state. If "construct" fails, skip to step 6.
+ *
+ * 4. The data structure is now initialized and in use.
+ *
+ * 5. When the data structure is no longer needed, the client calls the
+ * "destruct" function. The implementation uninitializes derived state.
+ * The base state has not been uninitialized yet, so the implementation
+ * may still refer to it.
+ *
+ * 6. The client uninitializes all of the data structure's base state.
+ *
+ * 7. The client calls the "dealloc" to free the raw memory. The
+ * implementation must not refer to base or derived state in the data
+ * structure, because it has already been uninitialized.
+ *
+ * Each "alloc" function allocates and returns a new instance of the respective
+ * data structure. The "alloc" function is not given any information about the
+ * use of the new data structure, so it cannot perform much initialization.
+ * Its purpose is just to ensure that the new data structure has enough room
+ * for base and derived state. It may return a null pointer if memory is not
+ * available, in which case none of the other functions is called.
+ *
+ * Each "construct" function initializes derived state in its respective data
+ * structure. When "construct" is called, all of the base state has already
+ * been initialized, so the "construct" function may refer to it. The
+ * "construct" function is allowed to fail, in which case the client calls the
+ * "dealloc" function (but not the "destruct" function).
+ *
+ * Each "destruct" function uninitializes and frees derived state in its
+ * respective data structure. When "destruct" is called, the base state has
+ * not yet been uninitialized, so the "destruct" function may refer to it. The
+ * "destruct" function is not allowed to fail.
+ *
+ * Each "dealloc" function frees raw memory that was allocated by the the
+ * "alloc" function. The memory's base and derived members might not have ever
+ * been initialized (but if "construct" returned successfully, then it has been
+ * "destruct"ed already). The "dealloc" function is not allowed to fail. */
struct netdev_class {
/* Type of netdevs in this class, e.g. "system", "tap", "gre", etc.
*
* the system. */
const char *type;
+/* ## ------------------- ## */
+/* ## Top-Level Functions ## */
+/* ## ------------------- ## */
+
/* Called when the netdev provider is registered, typically at program
* startup. Returning an error from this function will prevent any network
* device in this class from being opened.
* needed here. */
void (*wait)(void);
- /* Attempts to create a network device named 'name' in 'netdev_class'. On
- * success sets 'netdevp' to the newly created device. */
- int (*create)(const struct netdev_class *netdev_class, const char *name,
- struct netdev **netdevp);
+/* ## ---------------- ## */
+/* ## netdev Functions ## */
+/* ## ---------------- ## */
- /* Destroys 'netdev'.
- *
- * Netdev devices maintain a reference count that is incremented on
- * netdev_open() and decremented on netdev_close(). If 'netdev'
- * has a non-zero reference count, then this function will not be
- * called. */
- void (*destroy)(struct netdev *netdev);
+ /* Life-cycle functions for a netdev. See the large comment above on
+ * struct netdev_class. */
+ struct netdev *(*alloc)(void);
+ int (*construct)(struct netdev *);
+ void (*destruct)(struct netdev *);
+ void (*dealloc)(struct netdev *);
/* Fetches the device 'netdev''s configuration, storing it in 'args'.
* The caller owns 'args' and pre-initializes it to an empty smap.
const struct netdev_tunnel_config *
(*get_tunnel_config)(const struct netdev *netdev);
- /* Attempts to open a netdev_rx for receiving packets from 'netdev'.
- * Returns 0 if successful, otherwise a positive errno value. Returns
- * EOPNOTSUPP to indicate that the network device does not implement packet
- * reception through this interface. This function may be set to null if
- * it would always return EOPNOTSUPP anyhow. (This will prevent the
- * network device from being usefully used by the netdev-based "userspace
- * datapath".)
- *
- * On success, the implementation must set '*rxp' to a 'netdev_rx' for
- * 'netdev' that it has already initialized (with netdev_rx_init()). */
- int (*rx_open)(struct netdev *netdev, struct netdev_rx **rxp);
-
/* Sends the 'size'-byte packet in 'buffer' on 'netdev'. Returns 0 if
* successful, otherwise a positive errno value. Returns EAGAIN without
* blocking if the packet cannot be queued immediately. Returns EMSGSIZE
* returned sequence number is allowed to change even when 'netdev' doesn't
* change, although implementations should try to avoid this. */
unsigned int (*change_seq)(const struct netdev *netdev);
-};
-\f
-/* A data structure for capturing packets received by a network device.
- *
- * This structure should be treated as opaque by network device
- * implementations. */
-struct netdev_rx {
- const struct netdev_rx_class *rx_class;
- struct netdev *netdev;
-};
-void netdev_rx_init(struct netdev_rx *, struct netdev *,
- const struct netdev_rx_class *);
-void netdev_rx_uninit(struct netdev_rx *);
-struct netdev *netdev_rx_get_netdev(const struct netdev_rx *);
+/* ## ------------------- ## */
+/* ## netdev_rx Functions ## */
+/* ## ------------------- ## */
+
+/* If a particular netdev class does not support receiving packets, all these
+ * function pointers must be NULL. */
-struct netdev_rx_class {
- /* Destroys 'rx'. */
- void (*destroy)(struct netdev_rx *rx);
+ /* Life-cycle functions for a netdev_rx. See the large comment above on
+ * struct netdev_class. */
+ struct netdev_rx *(*rx_alloc)(void);
+ int (*rx_construct)(struct netdev_rx *);
+ void (*rx_destruct)(struct netdev_rx *);
+ void (*rx_dealloc)(struct netdev_rx *);
/* Attempts to receive a packet from 'rx' into the 'size' bytes in
* 'buffer'. If successful, returns the number of bytes in the received
* if no packet is ready to be received.
*
* Must return -EMSGSIZE, and discard the packet, if the received packet
- * is longer than 'size' bytes. */
- int (*recv)(struct netdev_rx *rx, void *buffer, size_t size);
+ * is longer than 'size' bytes.
+ *
+ * Specify NULL if this */
+ int (*rx_recv)(struct netdev_rx *rx, void *buffer, size_t size);
/* Registers with the poll loop to wake up from the next call to
* poll_block() when a packet is ready to be received with netdev_rx_recv()
* on 'rx'. */
- void (*wait)(struct netdev_rx *rx);
+ void (*rx_wait)(struct netdev_rx *rx);
/* Discards all packets waiting to be received from 'rx'. */
- int (*drain)(struct netdev_rx *rx);
+ int (*rx_drain)(struct netdev_rx *rx);
};
-static inline void netdev_rx_assert_class(const struct netdev_rx *rx,
- const struct netdev_rx_class *class_)
-{
- ovs_assert(rx->rx_class == class_);
-}
-
int netdev_register_provider(const struct netdev_class *);
int netdev_unregister_provider(const char *type);
const struct netdev_class *netdev_lookup_provider(const char *type);
struct netdev_class netdev_class;
};
-static int netdev_vport_create(const struct netdev_class *, const char *,
- struct netdev **);
+static int netdev_vport_construct(struct netdev *);
static int get_patch_config(const struct netdev *, struct smap *args);
static int get_tunnel_config(const struct netdev *, struct smap *args);
static void netdev_vport_poll_notify(struct netdev_vport *);
static bool
is_vport_class(const struct netdev_class *class)
{
- return class->create == netdev_vport_create;
+ return class->construct == netdev_vport_construct;
}
static const struct vport_class *
sizeof namebuf));
}
+static struct netdev *
+netdev_vport_alloc(void)
+{
+ struct netdev_vport *netdev = xzalloc(sizeof *netdev);
+ return &netdev->up;
+}
+
static int
-netdev_vport_create(const struct netdev_class *netdev_class, const char *name,
- struct netdev **netdevp)
+netdev_vport_construct(struct netdev *netdev_)
{
- struct netdev_vport *dev;
+ struct netdev_vport *netdev = netdev_vport_cast(netdev_);
- dev = xzalloc(sizeof *dev);
- netdev_init(&dev->up, name, netdev_class);
- dev->change_seq = 1;
- eth_addr_random(dev->etheraddr);
+ netdev->change_seq = 1;
+ eth_addr_random(netdev->etheraddr);
- *netdevp = &dev->up;
route_table_register();
return 0;
}
static void
-netdev_vport_destroy(struct netdev *netdev_)
+netdev_vport_destruct(struct netdev *netdev_)
{
struct netdev_vport *netdev = netdev_vport_cast(netdev_);
route_table_unregister();
free(netdev->peer);
+}
+
+static void
+netdev_vport_dealloc(struct netdev *netdev_)
+{
+ struct netdev_vport *netdev = netdev_vport_cast(netdev_);
free(netdev);
}
netdev_vport_run, \
netdev_vport_wait, \
\
- netdev_vport_create, \
- netdev_vport_destroy, \
+ netdev_vport_alloc, \
+ netdev_vport_construct, \
+ netdev_vport_destruct, \
+ netdev_vport_dealloc, \
GET_CONFIG, \
SET_CONFIG, \
GET_TUNNEL_CONFIG, \
\
- NULL, /* rx_open */ \
- \
NULL, /* send */ \
NULL, /* send_wait */ \
\
\
netdev_vport_update_flags, \
\
- netdev_vport_change_seq
+ netdev_vport_change_seq, \
+ \
+ NULL, /* rx_alloc */ \
+ NULL, /* rx_construct */ \
+ NULL, /* rx_destruct */ \
+ NULL, /* rx_dealloc */ \
+ NULL, /* rx_recv */ \
+ NULL, /* rx_wait */ \
+ NULL, /* rx_drain */
#define TUNNEL_CLASS(NAME, DPIF_PORT) \
{ DPIF_PORT, \
struct netdev *netdev;
int error;
- *netdevp = NULL;
netdev_initialize();
netdev = shash_find_data(&netdev_shash, name);
const struct netdev_class *class;
class = netdev_lookup_provider(type);
- if (!class) {
+ if (class) {
+ netdev = class->alloc();
+ if (netdev) {
+ memset(netdev, 0, sizeof *netdev);
+ netdev->netdev_class = class;
+ netdev->name = xstrdup(name);
+ netdev->node = shash_add(&netdev_shash, name, netdev);
+ list_init(&netdev->saved_flags_list);
+
+ error = class->construct(netdev);
+ if (error) {
+ class->dealloc(netdev);
+ }
+ } else {
+ error = ENOMEM;
+ }
+ } else {
VLOG_WARN("could not create netdev %s of unknown type %s",
name, type);
- return EAFNOSUPPORT;
- }
- error = class->create(class, name, &netdev);
- if (error) {
- return error;
+ error = EAFNOSUPPORT;
}
- ovs_assert(netdev->netdev_class == class);
-
+ } else {
+ error = 0;
}
- netdev->ref_cnt++;
- *netdevp = netdev;
- return 0;
+ if (!error) {
+ netdev->ref_cnt++;
+ *netdevp = netdev;
+ } else {
+ *netdevp = NULL;
+ }
+ return error;
}
/* Returns a reference to 'netdev_' for the caller to own. Returns null if
{
ovs_assert(dev->ref_cnt);
if (!--dev->ref_cnt) {
- netdev_uninit(dev, true);
+ dev->netdev_class->destruct(dev);
+
+ shash_delete(&netdev_shash, dev->node);
+ free(dev->name);
+ dev->netdev_class->dealloc(dev);
}
}
{
int error;
- error = (netdev->netdev_class->rx_open
- ? netdev->netdev_class->rx_open(netdev, rxp)
- : EOPNOTSUPP);
- if (!error) {
- ovs_assert((*rxp)->netdev == netdev);
- netdev->ref_cnt++;
+ if (netdev->netdev_class->rx_alloc) {
+ struct netdev_rx *rx = netdev->netdev_class->rx_alloc();
+ if (rx) {
+ rx->netdev = netdev;
+ error = netdev->netdev_class->rx_construct(rx);
+ if (!error) {
+ netdev->ref_cnt++;
+ *rxp = rx;
+ return 0;
+ }
+ netdev->netdev_class->rx_dealloc(rx);
+ } else {
+ error = ENOMEM;
+ }
} else {
- *rxp = NULL;
+ error = EOPNOTSUPP;
}
+
+ *rxp = NULL;
return error;
}
netdev_rx_close(struct netdev_rx *rx)
{
if (rx) {
- struct netdev *dev = rx->netdev;
-
- rx->rx_class->destroy(rx);
- netdev_unref(dev);
+ struct netdev *netdev = rx->netdev;
+ netdev->netdev_class->rx_destruct(rx);
+ netdev->netdev_class->rx_dealloc(rx);
+ netdev_close(netdev);
}
}
ovs_assert(buffer->size == 0);
ovs_assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN);
- retval = rx->rx_class->recv(rx, buffer->data, ofpbuf_tailroom(buffer));
+ retval = rx->netdev->netdev_class->rx_recv(rx, buffer->data,
+ ofpbuf_tailroom(buffer));
if (retval >= 0) {
COVERAGE_INC(netdev_received);
buffer->size += retval;
void
netdev_rx_wait(struct netdev_rx *rx)
{
- rx->rx_class->wait(rx);
+ rx->netdev->netdev_class->rx_wait(rx);
}
int
netdev_rx_drain(struct netdev_rx *rx)
{
- return rx->rx_class->drain ? rx->rx_class->drain(rx) : 0;
+ return (rx->netdev->netdev_class->rx_drain
+ ? rx->netdev->netdev_class->rx_drain(rx)
+ : 0);
}
/* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive
return netdev->netdev_class->change_seq(netdev);
}
\f
-/* Initializes 'netdev' as a netdev device named 'name' of the specified
- * 'netdev_class'. This function is ordinarily called from a netdev provider's
- * 'create' function.
- *
- * This function adds 'netdev' to a netdev-owned shash, so it is very important
- * that 'netdev' only be freed after calling netdev_uninit(). */
-void
-netdev_init(struct netdev *netdev, const char *name,
- const struct netdev_class *netdev_class)
-{
- ovs_assert(!shash_find(&netdev_shash, name));
-
- memset(netdev, 0, sizeof *netdev);
- netdev->netdev_class = netdev_class;
- netdev->name = xstrdup(name);
- netdev->node = shash_add(&netdev_shash, name, netdev);
- list_init(&netdev->saved_flags_list);
-}
-
-/* Undoes the results of initialization.
- *
- * Normally this function does not need to be called as netdev_close has
- * the same effect when the refcount drops to zero.
- * However, it may be called by providers due to an error on creation
- * that occurs after initialization. It this case netdev_close() would
- * never be called. */
-void
-netdev_uninit(struct netdev *netdev, bool destroy)
-{
- char *name = netdev->name;
-
- ovs_assert(!netdev->ref_cnt);
- ovs_assert(list_is_empty(&netdev->saved_flags_list));
-
- shash_delete(&netdev_shash, netdev->node);
-
- if (destroy) {
- netdev->netdev_class->destroy(netdev);
- }
- free(name);
-}
-
/* Returns the class type of 'netdev'.
*
* The caller must not free the returned value. */
return type;
}
\f
-void
-netdev_rx_init(struct netdev_rx *rx, struct netdev *netdev,
- const struct netdev_rx_class *class)
-{
- ovs_assert(netdev->ref_cnt > 0);
- rx->rx_class = class;
- rx->netdev = netdev;
-}
-
-void
-netdev_rx_uninit(struct netdev_rx *rx OVS_UNUSED)
-{
- /* Nothing to do. */
-}
-
struct netdev *
netdev_rx_get_netdev(const struct netdev_rx *rx)
{