struct tap_state {
int fd;
- bool opened;
};
\f
/* Traffic control. */
* Written only by TC implementation. */
};
+#define TC_INITIALIZER(TC, OPS) { OPS, HMAP_INITIALIZER(&(TC)->queues) }
+
/* One traffic control queue.
*
* Each TC implementation subclasses this with whatever additional data it
static const struct tc_ops tc_ops_default;
static const struct tc_ops tc_ops_other;
-static const struct tc_ops *tcs[] = {
+static const struct tc_ops *const tcs[] = {
&tc_ops_htb, /* Hierarchy token bucket (see tc-htb(8)). */
&tc_ops_hfsc, /* Hierarchical fair service curve. */
&tc_ops_default, /* Default qdisc (see tc-pfifo_fast(8)). */
struct netdev_linux {
struct netdev netdev;
+};
+
+struct netdev_rx_linux {
+ struct netdev_rx up;
+ bool is_tap;
int fd;
};
+static const struct netdev_rx_class netdev_rx_linux_class;
+
/* Sockets used for ioctl operations. */
static int af_inet_sock = -1; /* AF_INET, SOCK_DGRAM. */
static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *,
int cmd, const char *cmd_name);
static int get_flags(const struct netdev_dev *, unsigned int *flags);
-static int set_flags(struct netdev *, unsigned int flags);
+static int set_flags(const char *, unsigned int flags);
static int do_get_ifindex(const char *netdev_name);
static int get_ifindex(const struct netdev *, int *ifindexp);
static int do_set_addr(struct netdev *netdev,
return netdev_class->init == netdev_linux_init;
}
+static bool
+is_tap_netdev(const struct netdev *netdev)
+{
+ return netdev_dev_get_class(netdev_get_dev(netdev)) == &netdev_tap_class;
+}
+
static struct netdev_dev_linux *
netdev_dev_linux_cast(const struct netdev_dev *netdev_dev)
{
return CONTAINER_OF(netdev, struct netdev_linux, netdev);
}
+
+static struct netdev_rx_linux *
+netdev_rx_linux_cast(const struct netdev_rx *rx)
+{
+ netdev_rx_assert_class(rx, &netdev_rx_linux_class);
+ return CONTAINER_OF(rx, struct netdev_rx_linux, up);
+}
\f
static int
netdev_linux_init(void)
/* Allocate network device. */
netdev = xzalloc(sizeof *netdev);
- netdev->fd = -1;
netdev_init(&netdev->netdev, netdev_dev_);
/* Verify that the device really exists, by attempting to read its flags.
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
- if (netdev->fd > 0 && strcmp(netdev_get_type(netdev_), "tap")) {
- close(netdev->fd);
- }
free(netdev);
}
static int
-netdev_linux_listen(struct netdev *netdev_)
+netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
struct netdev_dev_linux *netdev_dev =
netdev_dev_linux_cast(netdev_get_dev(netdev_));
- struct sockaddr_ll sll;
- int ifindex;
+ bool is_tap = is_tap_netdev(netdev_);
+ struct netdev_rx_linux *rx;
int error;
int fd;
- if (netdev->fd >= 0) {
- return 0;
- }
+ if (is_tap) {
+ fd = netdev_dev->state.tap.fd;
+ } else {
+ struct sockaddr_ll sll;
+ int ifindex;
- if (!strcmp(netdev_get_type(netdev_), "tap")
- && !netdev_dev->state.tap.opened) {
- netdev->fd = netdev_dev->state.tap.fd;
- netdev_dev->state.tap.opened = true;
- return 0;
- }
+ /* Create file descriptor. */
+ fd = socket(PF_PACKET, SOCK_RAW, 0);
+ if (fd < 0) {
+ error = errno;
+ VLOG_ERR("failed to create raw socket (%s)", strerror(error));
+ goto error;
+ }
- /* Create file descriptor. */
- fd = socket(PF_PACKET, SOCK_RAW, 0);
- if (fd < 0) {
- error = errno;
- VLOG_ERR("failed to create raw socket (%s)", strerror(error));
- goto error;
- }
+ /* Set non-blocking mode. */
+ error = set_nonblocking(fd);
+ if (error) {
+ goto error;
+ }
- /* Set non-blocking mode. */
- error = set_nonblocking(fd);
- if (error) {
- goto error;
- }
+ /* Get ethernet device index. */
+ error = get_ifindex(&netdev->netdev, &ifindex);
+ if (error) {
+ goto error;
+ }
- /* Get ethernet device index. */
- error = get_ifindex(&netdev->netdev, &ifindex);
- if (error) {
- goto error;
+ /* Bind to specific ethernet device. */
+ memset(&sll, 0, sizeof sll);
+ 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) {
+ error = errno;
+ VLOG_ERR("%s: failed to bind raw socket (%s)",
+ netdev_get_name(netdev_), strerror(error));
+ goto error;
+ }
}
- /* Bind to specific ethernet device. */
- memset(&sll, 0, sizeof sll);
- 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) {
- error = errno;
- VLOG_ERR("%s: failed to bind raw socket (%s)",
- netdev_get_name(netdev_), strerror(error));
- goto error;
- }
+ rx = xmalloc(sizeof *rx);
+ netdev_rx_init(&rx->up, netdev_get_dev(netdev_), &netdev_rx_linux_class);
+ rx->is_tap = is_tap;
+ rx->fd = fd;
- netdev->fd = fd;
+ *rxp = &rx->up;
return 0;
error:
return error;
}
-static int
-netdev_linux_recv(struct netdev *netdev_, void *data, size_t size)
+static void
+netdev_rx_linux_destroy(struct netdev_rx *rx_)
{
- struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+ struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
- if (netdev->fd < 0) {
- /* Device is not listening. */
- return -EAGAIN;
+ if (!rx->is_tap) {
+ close(rx->fd);
}
+ free(rx);
+}
- for (;;) {
- ssize_t retval;
+static int
+netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size)
+{
+ struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+ ssize_t retval;
- retval = (netdev_->netdev_dev->netdev_class == &netdev_tap_class
- ? read(netdev->fd, data, size)
- : recv(netdev->fd, data, size, MSG_TRUNC));
- if (retval >= 0) {
- return retval <= size ? retval : -EMSGSIZE;
- } else if (errno != EINTR) {
- if (errno != EAGAIN) {
- VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
- strerror(errno), netdev_get_name(netdev_));
- }
- return -errno;
+ do {
+ retval = (rx->is_tap
+ ? read(rx->fd, data, size)
+ : recv(rx->fd, data, size, MSG_TRUNC));
+ } while (retval < 0 && errno == EINTR);
+
+ if (retval > size) {
+ return -EMSGSIZE;
+ } else if (retval >= 0) {
+ return retval;
+ } else {
+ if (errno != EAGAIN) {
+ VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
+ strerror(errno), netdev_rx_get_name(rx_));
}
+ return -errno;
}
}
-/* 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_recv() on 'netdev'. */
static void
-netdev_linux_recv_wait(struct netdev *netdev_)
+netdev_rx_linux_wait(struct netdev_rx *rx_)
{
- struct netdev_linux *netdev = netdev_linux_cast(netdev_);
- if (netdev->fd >= 0) {
- poll_fd_wait(netdev->fd, POLLIN);
- }
+ struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+ poll_fd_wait(rx->fd, POLLIN);
}
-/* Discards all packets waiting to be received from 'netdev'. */
static int
-netdev_linux_drain(struct netdev *netdev_)
+netdev_rx_linux_drain(struct netdev_rx *rx_)
{
- struct netdev_linux *netdev = netdev_linux_cast(netdev_);
- if (netdev->fd < 0) {
- return 0;
- } else if (!strcmp(netdev_get_type(netdev_), "tap")) {
+ struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+ if (rx->is_tap) {
struct ifreq ifr;
- int error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
+ int error = netdev_linux_do_ioctl(netdev_rx_get_name(rx_), &ifr,
SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
if (error) {
return error;
}
- drain_fd(netdev->fd, ifr.ifr_qlen);
+ drain_fd(rx->fd, ifr.ifr_qlen);
return 0;
} else {
- return drain_rcvbuf(netdev->fd);
+ return drain_rcvbuf(rx->fd);
}
}
static int
netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
{
- struct netdev_linux *netdev = netdev_linux_cast(netdev_);
for (;;) {
ssize_t retval;
- if (netdev->fd < 0) {
+ if (!is_tap_netdev(netdev_)) {
/* Use our AF_PACKET socket to send to this device. */
struct sockaddr_ll sll;
struct msghdr msg;
sock = af_packet_sock();
if (sock < 0) {
- return sock;
+ return -sock;
}
error = get_ifindex(netdev_, &ifindex);
retval = sendmsg(sock, &msg, 0);
} else {
- /* Use the netdev's own fd to send to this device. This is
- * essential for tap devices, because packets sent to a tap device
- * with an AF_PACKET socket will loop back to be *received* again
- * on the tap device. */
- retval = write(netdev->fd, data, size);
+ /* Use the tap fd to send to this device. This is essential for
+ * tap devices, because packets sent to a tap device with an
+ * AF_PACKET socket will loop back to be *received* again on the
+ * tap device. */
+ struct netdev_dev_linux *dev
+ = netdev_dev_linux_cast(netdev_get_dev(netdev_));
+
+ retval = write(dev->state.tap.fd, data, size);
}
if (retval < 0) {
* expected to do additional queuing of packets. Thus, this function is
* unlikely to ever be used. It is included for completeness. */
static void
-netdev_linux_send_wait(struct netdev *netdev_)
+netdev_linux_send_wait(struct netdev *netdev)
{
- struct netdev_linux *netdev = netdev_linux_cast(netdev_);
- if (netdev->fd < 0) {
- /* Nothing to do. */
- } else if (strcmp(netdev_get_type(netdev_), "tap")) {
- poll_fd_wait(netdev->fd, POLLOUT);
- } else {
+ if (is_tap_netdev(netdev)) {
/* TAP device always accepts packets.*/
poll_immediate_wake();
}
{
struct netdev_dev_linux *netdev_dev =
netdev_dev_linux_cast(netdev_get_dev(netdev_));
+ struct netdev_saved_flags *sf = NULL;
int error;
- bool up_again = false;
if (netdev_dev->cache_valid & VALID_ETHERADDR) {
if (netdev_dev->ether_addr_error) {
}
/* Tap devices must be brought down before setting the address. */
- if (!strcmp(netdev_get_type(netdev_), "tap")) {
+ if (is_tap_netdev(netdev_)) {
enum netdev_flags flags;
if (!netdev_get_flags(netdev_, &flags) && (flags & NETDEV_UP)) {
- netdev_turn_flags_off(netdev_, NETDEV_UP, false);
- up_again = true;
+ netdev_turn_flags_off(netdev_, NETDEV_UP, &sf);
}
}
error = set_etheraddr(netdev_get_name(netdev_), mac);
}
}
- if (up_again) {
- netdev_turn_flags_on(netdev_, NETDEV_UP, false);
- }
+ netdev_restore_flags(sf);
return error;
}
netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED,
struct sset *types)
{
- const struct tc_ops **opsp;
+ const struct tc_ops *const *opsp;
for (opsp = tcs; *opsp != NULL; opsp++) {
const struct tc_ops *ops = *opsp;
static const struct tc_ops *
tc_lookup_ovs_name(const char *name)
{
- const struct tc_ops **opsp;
+ const struct tc_ops *const *opsp;
for (opsp = tcs; *opsp != NULL; opsp++) {
const struct tc_ops *ops = *opsp;
static const struct tc_ops *
tc_lookup_linux_name(const char *name)
{
- const struct tc_ops **opsp;
+ const struct tc_ops *const *opsp;
for (opsp = tcs; *opsp != NULL; opsp++) {
const struct tc_ops *ops = *opsp;
}
static int
-netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off,
+netdev_linux_update_flags(struct netdev_dev *dev_, enum netdev_flags off,
enum netdev_flags on, enum netdev_flags *old_flagsp)
{
struct netdev_dev_linux *netdev_dev;
int old_flags, new_flags;
int error = 0;
- netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev));
+ netdev_dev = netdev_dev_linux_cast(dev_);
old_flags = netdev_dev->ifi_flags;
*old_flagsp = iff_to_nd_flags(old_flags);
new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
if (new_flags != old_flags) {
- error = set_flags(netdev, new_flags);
+ error = set_flags(netdev_dev_get_name(dev_), new_flags);
get_flags(&netdev_dev->netdev_dev, &netdev_dev->ifi_flags);
}
return error;
netdev_linux_open, \
netdev_linux_close, \
\
- netdev_linux_listen, \
- netdev_linux_recv, \
- netdev_linux_recv_wait, \
- netdev_linux_drain, \
+ netdev_linux_rx_open, \
\
netdev_linux_send, \
netdev_linux_send_wait, \
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. */
{
struct netdev_dev_linux *netdev_dev =
netdev_dev_linux_cast(netdev_get_dev(netdev));
- static struct tc *tc;
+ static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_default);
- if (!tc) {
- tc = xmalloc(sizeof *tc);
- tc_init(tc, &tc_ops_default);
- }
- netdev_dev->tc = tc;
+ /* Nothing but a tc class implementation is allowed to write to a tc. This
+ * class never does that, so we can legitimately use a const tc object. */
+ netdev_dev->tc = CONST_CAST(struct tc *, &tc);
}
static int
{
struct netdev_dev_linux *netdev_dev =
netdev_dev_linux_cast(netdev_get_dev(netdev));
- static struct tc *tc;
+ static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_other);
- if (!tc) {
- tc = xmalloc(sizeof *tc);
- tc_init(tc, &tc_ops_other);
- }
- netdev_dev->tc = tc;
+ /* Nothing but a tc class implementation is allowed to write to a tc. This
+ * class never does that, so we can legitimately use a const tc object. */
+ netdev_dev->tc = CONST_CAST(struct tc *, &tc);
return 0;
}
}
static int
-set_flags(struct netdev *netdev, unsigned int flags)
+set_flags(const char *name, unsigned int flags)
{
struct ifreq ifr;
ifr.ifr_flags = flags;
- return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCSIFFLAGS,
- "SIOCSIFFLAGS");
+ return netdev_linux_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
}
static int