diff options
author | Ben Pfaff <blp@nicira.com> | 2013-05-10 14:39:19 -0700 |
---|---|---|
committer | Ben Pfaff <blp@nicira.com> | 2013-05-10 14:39:36 -0700 |
commit | 796223f5bc3a4896e6398733c798390158479400 (patch) | |
tree | 22bee34ee7f351bedd75b440b536d0865a48c430 /lib/netdev-linux.c | |
parent | 33ab38d9b861ab07fc4a291b77ce426bb3a1609e (diff) |
netdev: Add new "struct netdev_rx" for capturing packets from a netdev.
Separating packet capture from "struct netdev" means that there is no
remaining per-"struct netdev" state, which will allow us to get rid of
"struct netdev_dev" (by renaming it "struct netdev").
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
Diffstat (limited to 'lib/netdev-linux.c')
-rw-r--r-- | lib/netdev-linux.c | 216 |
1 files changed, 117 insertions, 99 deletions
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 30cd0f63..2e6feddf 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -122,7 +122,6 @@ enum { struct tap_state { int fd; - bool opened; }; /* Traffic control. */ @@ -400,9 +399,16 @@ struct netdev_dev_linux { 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. */ @@ -442,6 +448,12 @@ is_netdev_linux_class(const struct netdev_class *netdev_class) 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) { @@ -460,6 +472,13 @@ netdev_linux_cast(const struct netdev *netdev) 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); +} static int netdev_linux_init(void) @@ -729,7 +748,6 @@ netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp) /* 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. @@ -761,67 +779,65 @@ netdev_linux_close(struct netdev *netdev_) { 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: @@ -831,63 +847,64 @@ 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); } } @@ -903,11 +920,10 @@ netdev_linux_drain(struct netdev *netdev_) 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; @@ -945,11 +961,14 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) 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) { @@ -983,14 +1002,9 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) * 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(); } @@ -1018,7 +1032,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, } /* 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)) { @@ -2489,10 +2503,7 @@ netdev_linux_change_seq(const struct netdev *netdev) 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, \ @@ -2562,6 +2573,13 @@ const struct netdev_class netdev_internal_class = 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, +}; /* HTB traffic control class. */ |