From: Ben Pfaff Date: Fri, 1 Apr 2011 16:22:39 +0000 (-0700) Subject: netdev-linux: Support 'send' for netdevs opened with NETDEV_ETH_TYPE_NONE. X-Git-Tag: v1.2.0~516 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;ds=sidebyside;h=76c308b50d316a2186283467e12564c0234c7501;p=sliver-openvswitch.git netdev-linux: Support 'send' for netdevs opened with NETDEV_ETH_TYPE_NONE. The new implementation of the bonding code expects to be able to send packets using netdev_send(). This makes it possible for Linux netdevs. --- diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 6b0d0149f..eecaaa591 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -368,8 +368,9 @@ struct netdev_linux { int fd; }; -/* An AF_INET socket (used for ioctl operations). */ -static int af_inet_sock = -1; +/* Sockets used for ioctl operations. */ +static int af_inet_sock = -1; /* AF_INET, SOCK_DGRAM. */ +static int af_packet_sock = -1; /* AF_PACKET, SOCK_RAW. */ /* A Netlink routing socket that is not subscribed to any multicast groups. */ static struct nl_sock *rtnl_sock; @@ -443,6 +444,14 @@ netdev_linux_init(void) status = af_inet_sock >= 0 ? 0 : errno; if (status) { VLOG_ERR("failed to create inet socket: %s", strerror(status)); + } else { + /* Create AF_PACKET socket. */ + af_packet_sock = socket(AF_PACKET, SOCK_RAW, 0); + status = af_packet_sock >= 0 ? 0 : errno; + if (status) { + VLOG_ERR("failed to create packet socket: %s", + strerror(status)); + } } /* Create rtnetlink socket. */ @@ -819,16 +828,36 @@ 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_); + struct sockaddr_ll sll; + struct msghdr msg; + struct iovec iov; + int ifindex; + int error; - /* XXX should support sending even if 'ethertype' was NETDEV_ETH_TYPE_NONE. - */ - if (netdev->fd < 0) { - return EPIPE; + error = get_ifindex(netdev_, &ifindex); + if (error) { + return error; } + /* We don't bother setting most fields in sockaddr_ll because the kernel + * ignores them for SOCK_RAW. */ + memset(&sll, 0, sizeof sll); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = ifindex; + + iov.iov_base = (void *) data; + iov.iov_len = size; + + msg.msg_name = &sll; + msg.msg_namelen = sizeof sll; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + for (;;) { - ssize_t retval = write(netdev->fd, data, size); + ssize_t retval = sendmsg(af_packet_sock, &msg, 0); if (retval < 0) { /* The Linux AF_PACKET implementation never blocks waiting for room * for packets, instead returning ENOBUFS. Translate this into diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index c6ebd2a5e..b095779cd 100644 --- a/lib/netdev-provider.h +++ b/lib/netdev-provider.h @@ -210,7 +210,8 @@ struct netdev_class { * transmission 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".) */ + * datapath". It will also prevent the OVS implementation of bonding from + * working properly over 'netdev'.) */ int (*send)(struct netdev *netdev, const void *buffer, size_t size); /* Registers with the poll loop to wake up from the next call to