X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev.c;h=08afc390a2ceecfc9362bc46bf232cba16ebe77f;hb=9092532ddc84a4006d49b81db8be5ccc66b59181;hp=ba11b6abdb766e0e8cbda66a91cbc3a15a601ee5;hpb=9da88ee2ad791e259a8c88ba2fc116830293aaf5;p=sliver-openvswitch.git diff --git a/lib/netdev.c b/lib/netdev.c index ba11b6abd..08afc390a 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -17,7 +17,7 @@ * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * THE SOFTWRE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS @@ -36,8 +36,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -59,7 +61,7 @@ #include "fatal-signal.h" #include "list.h" #include "ofpbuf.h" -#include "openflow.h" +#include "openflow/openflow.h" #include "packets.h" #include "poll-loop.h" #include "socket-util.h" @@ -70,11 +72,21 @@ struct netdev { struct list node; char *name; + + /* File descriptors. For ordinary network devices, the two fds below are + * the same; for tap devices, they differ. */ + int netdev_fd; /* Network device. */ + int tap_fd; /* TAP character device, if any, otherwise the + * network device. */ + + /* Cached network device information. */ int ifindex; - int fd; uint8_t etheraddr[ETH_ADDR_LEN]; + struct in6_addr in6; int speed; int mtu; + int txqlen; + int hwaddr_family; /* Bitmaps of OFPPF_* that describe features. All bits disabled if * unsupported or unavailable. */ @@ -83,7 +95,6 @@ struct netdev { uint32_t supported; /* Features supported by the port. */ uint32_t peer; /* Features advertised by the peer. */ - struct in6_addr in6; int save_flags; /* Initial device flags. */ int changed_flags; /* Flags that we changed. */ }; @@ -99,9 +110,11 @@ static int af_inet_sock = -1; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); static void init_netdev(void); +static int do_open_netdev(const char *name, int ethertype, int tap_fd, + struct netdev **netdev_); static int restore_flags(struct netdev *netdev); -static int get_flags(const struct netdev *, int *flagsp); -static int set_flags(struct netdev *, int flags); +static int get_flags(const struct netdev *, int fd, int *flagsp); +static int set_flags(struct netdev *, int fd, int flags); /* Obtains the IPv6 address for 'name' into 'in6'. */ static void @@ -157,7 +170,7 @@ do_ethtool(struct netdev *netdev) memset(&ecmd, 0, sizeof ecmd); ecmd.cmd = ETHTOOL_GSET; - if (ioctl(netdev->fd, SIOCETHTOOL, &ifr) == 0) { + if (ioctl(netdev->netdev_fd, SIOCETHTOOL, &ifr) == 0) { if (ecmd.supported & SUPPORTED_10baseT_Half) { netdev->supported |= OFPPF_10MB_HD; } @@ -267,7 +280,7 @@ do_ethtool(struct netdev *netdev) } /* Opens the network device named 'name' (e.g. "eth0") and returns zero if - * successful, otherwise a positive errno value. On success, sets '*netdev' + * successful, otherwise a positive errno value. On success, sets '*netdevp' * to the new network device, otherwise to null. * * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order to @@ -275,34 +288,99 @@ do_ethtool(struct netdev *netdev) * the 'enum netdev_pseudo_ethertype' values to receive frames in one of those * categories. */ int -netdev_open(const char *name, int ethertype, struct netdev **netdev_) +netdev_open(const char *name, int ethertype, struct netdev **netdevp) { - int fd; + if (!strncmp(name, "tap:", 4)) { + return netdev_open_tap(name + 4, netdevp); + } else { + return do_open_netdev(name, ethertype, -1, netdevp); + } +} + +/* Opens a TAP virtual network device. If 'name' is a nonnull, non-empty + * string, attempts to assign that name to the TAP device (failing if the name + * is already in use); otherwise, a name is automatically assigned. Returns + * zero if successful, otherwise a positive errno value. On success, sets + * '*netdevp' to the new network device, otherwise to null. */ +int +netdev_open_tap(const char *name, struct netdev **netdevp) +{ + static const char tap_dev[] = "/dev/net/tun"; + struct ifreq ifr; + int error; + int tap_fd; + + tap_fd = open(tap_dev, O_RDWR); + if (tap_fd < 0) { + ofp_error(errno, "opening \"%s\" failed", tap_dev); + return errno; + } + + memset(&ifr, 0, sizeof ifr); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (name) { + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + } + if (ioctl(tap_fd, TUNSETIFF, &ifr) < 0) { + int error = errno; + ofp_error(error, "ioctl(TUNSETIFF) on \"%s\" failed", tap_dev); + close(tap_fd); + return error; + } + + error = set_nonblocking(tap_fd); + if (error) { + ofp_error(error, "set_nonblocking on \"%s\" failed", tap_dev); + close(tap_fd); + return error; + } + + error = do_open_netdev(ifr.ifr_name, NETDEV_ETH_TYPE_NONE, tap_fd, + netdevp); + if (error) { + close(tap_fd); + } + return error; +} + +static int +do_open_netdev(const char *name, int ethertype, int tap_fd, + struct netdev **netdev_) +{ + int netdev_fd; struct sockaddr_ll sll; struct ifreq ifr; unsigned int ifindex; uint8_t etheraddr[ETH_ADDR_LEN]; struct in6_addr in6; int mtu; + int txqlen; + int hwaddr_family; int error; struct netdev *netdev; - *netdev_ = NULL; init_netdev(); + *netdev_ = NULL; /* Create raw socket. */ - fd = socket(PF_PACKET, SOCK_RAW, - htons(ethertype == NETDEV_ETH_TYPE_NONE ? 0 - : ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL - : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2 - : ethertype)); - if (fd < 0) { + netdev_fd = socket(PF_PACKET, SOCK_RAW, + htons(ethertype == NETDEV_ETH_TYPE_NONE ? 0 + : ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL + : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2 + : ethertype)); + if (netdev_fd < 0) { return errno; } + /* Set non-blocking mode. */ + error = set_nonblocking(netdev_fd); + if (error) { + goto error_already_set; + } + /* Get ethernet device index. */ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + if (ioctl(netdev_fd, SIOCGIFINDEX, &ifr) < 0) { VLOG_ERR("ioctl(SIOCGIFINDEX) on %s device failed: %s", name, strerror(errno)); goto error; @@ -313,7 +391,7 @@ netdev_open(const char *name, int ethertype, struct netdev **netdev_) memset(&sll, 0, sizeof sll); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; - if (bind(fd, (struct sockaddr *) &sll, sizeof sll) < 0) { + if (bind(netdev_fd, (struct sockaddr *) &sll, sizeof sll) < 0) { VLOG_ERR("bind to %s failed: %s", name, strerror(errno)); goto error; } @@ -323,40 +401,51 @@ netdev_open(const char *name, int ethertype, struct netdev **netdev_) * packets of the requested type on all system interfaces. We do not * want to receive that data, but there is no way to avoid it. So we * must now drain out the receive queue. */ - error = drain_rcvbuf(fd); + error = drain_rcvbuf(netdev_fd); if (error) { goto error; } } /* Get MAC address. */ - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + if (ioctl(netdev_fd, SIOCGIFHWADDR, &ifr) < 0) { VLOG_ERR("ioctl(SIOCGIFHWADDR) on %s device failed: %s", name, strerror(errno)); goto error; } - if (ifr.ifr_hwaddr.sa_family != AF_UNSPEC - && ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + 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", - name, (int) ifr.ifr_hwaddr.sa_family); + name, hwaddr_family); } memcpy(etheraddr, ifr.ifr_hwaddr.sa_data, sizeof etheraddr); /* Get MTU. */ - if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { + if (ioctl(netdev_fd, SIOCGIFMTU, &ifr) < 0) { VLOG_ERR("ioctl(SIOCGIFMTU) on %s device failed: %s", name, strerror(errno)); goto error; } mtu = ifr.ifr_mtu; + /* Get TX queue length. */ + if (ioctl(netdev_fd, SIOCGIFTXQLEN, &ifr) < 0) { + VLOG_ERR("ioctl(SIOCGIFTXQLEN) on %s device failed: %s", + name, strerror(errno)); + goto error; + } + txqlen = ifr.ifr_qlen; + get_ipv6_address(name, &in6); /* Allocate network device. */ netdev = xmalloc(sizeof *netdev); netdev->name = xstrdup(name); netdev->ifindex = ifindex; - netdev->fd = fd; + netdev->txqlen = txqlen; + netdev->hwaddr_family = hwaddr_family; + netdev->netdev_fd = netdev_fd; + netdev->tap_fd = tap_fd < 0 ? netdev_fd : tap_fd; memcpy(netdev->etheraddr, etheraddr, sizeof etheraddr); netdev->mtu = mtu; netdev->in6 = in6; @@ -365,9 +454,9 @@ netdev_open(const char *name, int ethertype, struct netdev **netdev_) do_ethtool(netdev); /* Save flags to restore at close or exit. */ - error = get_flags(netdev, &netdev->save_flags); + error = get_flags(netdev, netdev_fd, &netdev->save_flags); if (error) { - goto preset_error; + goto error_already_set; } netdev->changed_flags = 0; fatal_signal_block(); @@ -380,8 +469,11 @@ netdev_open(const char *name, int ethertype, struct netdev **netdev_) error: error = errno; -preset_error: - close(fd); +error_already_set: + close(netdev_fd); + if (tap_fd >= 0) { + close(tap_fd); + } return error; } @@ -404,7 +496,10 @@ netdev_close(struct netdev *netdev) /* Free. */ free(netdev->name); - close(netdev->fd); + close(netdev->netdev_fd); + if (netdev->netdev_fd != netdev->tap_fd) { + close(netdev->tap_fd); + } free(netdev); } } @@ -415,8 +510,7 @@ static void pad_to_minimum_length(struct ofpbuf *buffer) { if (buffer->size < ETH_TOTAL_MIN) { - size_t shortage = ETH_TOTAL_MIN - buffer->size; - memset(ofpbuf_put_uninit(buffer, shortage), 0, shortage); + ofpbuf_put_zeros(buffer, ETH_TOTAL_MIN - buffer->size); } } @@ -440,9 +534,8 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) assert(buffer->size == 0); assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN); do { - n_bytes = recv(netdev->fd, - ofpbuf_tail(buffer), ofpbuf_tailroom(buffer), - MSG_DONTWAIT); + n_bytes = read(netdev->tap_fd, + ofpbuf_tail(buffer), ofpbuf_tailroom(buffer)); } while (n_bytes < 0 && errno == EINTR); if (n_bytes < 0) { if (errno != EAGAIN) { @@ -468,14 +561,19 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) void netdev_recv_wait(struct netdev *netdev) { - poll_fd_wait(netdev->fd, POLLIN); + poll_fd_wait(netdev->tap_fd, POLLIN); } /* Discards all packets waiting to be received from 'netdev'. */ -void +int netdev_drain(struct netdev *netdev) { - drain_rcvbuf(netdev->fd); + if (netdev->tap_fd != netdev->netdev_fd) { + drain_fd(netdev->tap_fd, netdev->txqlen); + return 0; + } else { + return drain_rcvbuf(netdev->netdev_fd); + } } /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive @@ -491,18 +589,9 @@ int netdev_send(struct netdev *netdev, const struct ofpbuf *buffer) { ssize_t n_bytes; - const struct eth_header *eh; - - /* Pull out the Ethernet header. */ - if (buffer->size < ETH_HEADER_LEN) { - VLOG_WARN_RL(&rl, "cannot send %zu-byte frame on %s", - buffer->size, netdev->name); - return EMSGSIZE; - } - eh = ofpbuf_at_assert(buffer, 0, sizeof *eh); do { - n_bytes = sendto(netdev->fd, buffer->data, buffer->size, 0, NULL, 0); + n_bytes = write(netdev->tap_fd, buffer->data, buffer->size); } while (n_bytes < 0 && errno == EINTR); if (n_bytes < 0) { @@ -536,7 +625,32 @@ netdev_send(struct netdev *netdev, const struct ofpbuf *buffer) void netdev_send_wait(struct netdev *netdev) { - poll_fd_wait(netdev->fd, POLLOUT); + if (netdev->tap_fd == netdev->netdev_fd) { + poll_fd_wait(netdev->tap_fd, POLLOUT); + } else { + /* TAP device always accepts packets.*/ + poll_immediate_wake(); + } +} + +/* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, + * otherwise a positive errno value. */ +int +netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN]) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); + ifr.ifr_hwaddr.sa_family = netdev->hwaddr_family; + memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN); + if (ioctl(netdev->netdev_fd, SIOCSIFHWADDR, &ifr) < 0) { + VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s", + netdev->name, strerror(errno)); + return errno; + } + memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN); + return 0; } /* Returns a pointer to 'netdev''s MAC address. The caller must not modify or @@ -578,7 +692,7 @@ netdev_get_link_status(const struct netdev *netdev) memset(&edata, 0, sizeof edata); edata.cmd = ETHTOOL_GLINK; - if (ioctl(netdev->fd, SIOCETHTOOL, &ifr) == 0) { + if (ioctl(netdev->netdev_fd, SIOCETHTOOL, &ifr) == 0) { if (edata.data) { return 1; } else { @@ -717,7 +831,7 @@ netdev_get_flags(const struct netdev *netdev, enum netdev_flags *flagsp) { int error, flags; - error = get_flags(netdev, &flags); + error = get_flags(netdev, netdev->netdev_fd, &flags); if (error) { return error; } @@ -750,13 +864,13 @@ nd_to_iff_flags(enum netdev_flags nd) * will be reverted when 'netdev' is closed or the program exits. Returns 0 if * successful, otherwise a positive errno value. */ static int -do_update_flags(struct netdev *netdev, enum netdev_flags off, +do_update_flags(struct netdev *netdev, int fd, enum netdev_flags off, enum netdev_flags on, bool permanent) { int old_flags, new_flags; int error; - error = get_flags(netdev, &old_flags); + error = get_flags(netdev, fd, &old_flags); if (error) { return error; } @@ -766,7 +880,7 @@ do_update_flags(struct netdev *netdev, enum netdev_flags off, netdev->changed_flags |= new_flags ^ old_flags; } if (new_flags != old_flags) { - error = set_flags(netdev, new_flags); + error = set_flags(netdev, fd, new_flags); } return error; } @@ -779,7 +893,7 @@ int netdev_set_flags(struct netdev *netdev, enum netdev_flags flags, bool permanent) { - return do_update_flags(netdev, -1, flags, permanent); + return do_update_flags(netdev, netdev->netdev_fd, -1, flags, permanent); } /* Turns on the specified 'flags' on 'netdev'. @@ -790,7 +904,7 @@ int netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags, bool permanent) { - return do_update_flags(netdev, 0, flags, permanent); + return do_update_flags(netdev, netdev->netdev_fd, 0, flags, permanent); } /* Turns off the specified 'flags' on 'netdev'. @@ -801,7 +915,7 @@ int netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags, bool permanent) { - return do_update_flags(netdev, flags, 0, permanent); + return do_update_flags(netdev, netdev->netdev_fd, flags, 0, permanent); } /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be @@ -865,7 +979,7 @@ restore_flags(struct netdev *netdev) /* Get current flags. */ strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); - if (ioctl(netdev->fd, SIOCGIFFLAGS, &ifr) < 0) { + if (ioctl(netdev->netdev_fd, SIOCGIFFLAGS, &ifr) < 0) { return errno; } @@ -874,7 +988,7 @@ restore_flags(struct netdev *netdev) if ((ifr.ifr_flags ^ netdev->save_flags) & restore_flags) { ifr.ifr_flags &= ~restore_flags; ifr.ifr_flags |= netdev->save_flags & restore_flags; - if (ioctl(netdev->fd, SIOCSIFFLAGS, &ifr) < 0) { + if (ioctl(netdev->netdev_fd, SIOCSIFFLAGS, &ifr) < 0) { return errno; } } @@ -894,11 +1008,11 @@ restore_all_flags(void *aux UNUSED) } static int -get_flags(const struct netdev *netdev, int *flags) +get_flags(const struct netdev *netdev, int fd, int *flags) { struct ifreq ifr; strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); - if (ioctl(netdev->fd, SIOCGIFFLAGS, &ifr) < 0) { + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { VLOG_ERR("ioctl(SIOCGIFFLAGS) on %s device failed: %s", netdev->name, strerror(errno)); return errno; @@ -908,12 +1022,12 @@ get_flags(const struct netdev *netdev, int *flags) } static int -set_flags(struct netdev *netdev, int flags) +set_flags(struct netdev *netdev, int fd, int flags) { struct ifreq ifr; strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); ifr.ifr_flags = flags; - if (ioctl(netdev->fd, SIOCSIFFLAGS, &ifr) < 0) { + if (ioctl(netdev->netdev_fd, SIOCSIFFLAGS, &ifr) < 0) { VLOG_ERR("ioctl(SIOCSIFFLAGS) on %s device failed: %s", netdev->name, strerror(errno)); return errno;