[ovs-dev] [PATCH v4 4/4] netdev-linux: Read packet auxdata to obtain vlan_tid

Ben Pfaff blp at nicira.com
Wed Jan 8 18:49:24 UTC 2014


It's orthogonal I think.

On Wed, Jan 08, 2014 at 11:44:43AM +0200, Philip Roth wrote:
> this could also be used for 802.1AD right?
> 
> 
> On Tue, Jan 7, 2014 at 7:33 AM, Simon Horman <horms at verge.net.au> wrote:
> 
> > If VLAN acceleration is used when the kernel receives a packet
> > then the outer-most VLAN tag will not be present in the packet
> > when it is received by netdev-linux. Rather, it will be present
> > in auxdata.
> >
> > This patch uses recvmsg() instead of recv() to read auxdata for
> > each packet and if the vlan_tid is set then it is added to the packet.
> >
> > Adding the vlan_tid makes use of headroom available
> > in the buffer parameter of rx_recv.
> >
> > Signed-off-by: Simon Horman <horms at verge.net.au>
> >
> > --
> > v4
> > * Rebase
> > * Make aux variable in netdev_linux_rx_recv_sock() const
> > * Use TP_STATUS_VLAN_VALID to allow vlan 0 to be read from auxdata
> > * Use TP_STATUS_VLAN_TPID_VALID allow the VLAN tpid to be read
> >   from auxdata
> >
> > v3
> > * Remove unnecessary cast
> > * On success return 0 and increment buffer->size
> > * Limit to reading one VLAN from auxdata
> >
> > v2
> > * Updated to use eth_push_vlan() to efficiently push the VLAN
> >   if headroom is available. This is possible now there
> >   is a struct ofpbuf * to work with.
> > * Various cleanups
> > ---
> >  include/sparse/sys/socket.h |   1 +
> >  lib/netdev-linux.c          | 152
> > +++++++++++++++++++++++++++++++++++++++-----
> >  2 files changed, 136 insertions(+), 17 deletions(-)
> >
> > diff --git a/include/sparse/sys/socket.h b/include/sparse/sys/socket.h
> > index 75ee43c..3212bf4 100644
> > --- a/include/sparse/sys/socket.h
> > +++ b/include/sparse/sys/socket.h
> > @@ -87,6 +87,7 @@ enum {
> >  };
> >
> >  enum {
> > +    SOL_PACKET,
> >      SOL_SOCKET
> >  };
> >
> > diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> > index 106c18a..444369e 100644
> > --- a/lib/netdev-linux.c
> > +++ b/lib/netdev-linux.c
> > @@ -20,11 +20,11 @@
> >
> >  #include <errno.h>
> >  #include <fcntl.h>
> > -#include <arpa/inet.h>
> >  #include <inttypes.h>
> >  #include <linux/filter.h>
> >  #include <linux/gen_stats.h>
> >  #include <linux/if_ether.h>
> > +#include <linux/if_packet.h>
> >  #include <linux/if_tun.h>
> >  #include <linux/types.h>
> >  #include <linux/ethtool.h>
> > @@ -37,10 +37,8 @@
> >  #include <sys/types.h>
> >  #include <sys/ioctl.h>
> >  #include <sys/socket.h>
> > -#include <netpacket/packet.h>
> >  #include <net/if.h>
> >  #include <net/if_arp.h>
> > -#include <net/if_packet.h>
> >  #include <net/route.h>
> >  #include <netinet/in.h>
> >  #include <poll.h>
> > @@ -763,7 +761,7 @@ netdev_linux_rx_construct(struct netdev_rx *rx_)
> >          rx->fd = netdev->tap_fd;
> >      } else {
> >          struct sockaddr_ll sll;
> > -        int ifindex;
> > +        int ifindex, val;
> >          /* Result of tcpdump -dd inbound */
> >          static const struct sock_filter filt[] = {
> >              { 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */
> > @@ -783,6 +781,16 @@ netdev_linux_rx_construct(struct netdev_rx *rx_)
> >              goto error;
> >          }
> >
> > +        val = 1;
> > +        error = setsockopt(rx->fd, SOL_PACKET, PACKET_AUXDATA,
> > +                           &val, sizeof val);
> > +        if (error) {
> > +            error = errno;
> > +            VLOG_ERR("%s: failed to mark socket for auxdata (%s)",
> > +                     netdev_get_name(netdev_), ovs_strerror(error));
> > +            goto error;
> > +        }
> > +
> >          /* Set non-blocking mode. */
> >          error = set_nonblocking(rx->fd);
> >          if (error) {
> > @@ -799,7 +807,7 @@ netdev_linux_rx_construct(struct netdev_rx *rx_)
> >          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);
> > +        sll.sll_protocol = htons(ETH_P_ALL);
> >          if (bind(rx->fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
> >              error = errno;
> >              VLOG_ERR("%s: failed to bind raw socket (%s)",
> > @@ -847,31 +855,141 @@ netdev_linux_rx_dealloc(struct netdev_rx *rx_)
> >      free(rx);
> >  }
> >
> > +#ifdef TP_STATUS_VLAN_TPID_VALID
> > +static ovs_be16
> > +auxdata_to_vlan_tpid(const struct tpacket_auxdata *aux)
> > +{
> > +    if (aux->tp_status & TP_STATUS_VLAN_TPID_VALID) {
> > +        return htons(aux->tp_vlan_tpid);
> > +    } else {
> > +        return htons(ETH_TYPE_VLAN);
> > +    }
> > +}
> > +#else
> > +static ovs_be16
> > +auxdata_to_vlan_tpid(const struct tpacket_auxdata *aux OVS_UNUSED)
> > +{
> > +    return htons(ETH_TYPE_VLAN);
> > +}
> > +#endif
> > +
> > +/* TP_STATUS_VLAN_VALID was added to v3.0 of the linux kernel
> > + * to allow VLAN 0 to be accessed via auxdata. Use it if it
> > + * is available */
> > +#ifdef TP_STATUS_VLAN_VALID
> > +static bool
> > +auxdata_has_vlan_tci(const struct tpacket_auxdata *aux)
> > +{
> > +    return aux->tp_vlan_tci || (aux->tp_status & TP_STATUS_VLAN_VALID);
> > +}
> > +#else
> > +static bool
> > +auxdata_has_vlan_tci(const struct tpacket_auxdata *aux)
> > +{
> > +    return aux->tp_vlan_tci;
> > +}
> > +#endif
> > +
> >  static int
> > -netdev_linux_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
> > +netdev_linux_rx_recv_sock(int fd, struct ofpbuf *buffer)
> >  {
> > -    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
> > +    size_t size;
> >      ssize_t retval;
> > -    size_t size = ofpbuf_tailroom(buffer);
> > +    struct iovec iov;
> > +    struct cmsghdr *cmsg;
> > +    union {
> > +        struct cmsghdr cmsg;
> > +        char buffer[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
> > +    } cmsg_buffer;
> > +    struct msghdr msgh;
> > +
> > +    /* Reserve headroom for a single VLAN tag */
> > +    ofpbuf_reserve(buffer, VLAN_HEADER_LEN);
> > +    size = ofpbuf_tailroom(buffer);
> > +
> > +    iov.iov_base = buffer->data;
> > +    iov.iov_len = size;
> > +    msgh.msg_name = NULL;
> > +    msgh.msg_namelen = 0;
> > +    msgh.msg_iov = &iov;
> > +    msgh.msg_iovlen = 1;
> > +    msgh.msg_control = &cmsg_buffer;
> > +    msgh.msg_controllen = sizeof cmsg_buffer;
> > +    msgh.msg_flags = 0;
> >
> >      do {
> > -        retval = (rx->is_tap
> > -                  ? read(rx->fd, buffer->data, size)
> > -                  : recv(rx->fd, buffer->data, size, MSG_TRUNC));
> > +        retval = recvmsg(fd, &msgh, MSG_TRUNC);
> >      } while (retval < 0 && errno == EINTR);
> >
> >      if (retval < 0) {
> > -        if (errno != EAGAIN) {
> > -            VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
> > -                         ovs_strerror(errno), netdev_rx_get_name(rx_));
> > +        return errno;
> > +    } else if (retval > size) {
> > +        return EMSGSIZE;
> > +    }
> > +
> > +    buffer->size += retval;
> > +
> > +    for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,
> > cmsg)) {
> > +        const struct tpacket_auxdata *aux;
> > +
> > +        if (cmsg->cmsg_level != SOL_PACKET
> > +            || cmsg->cmsg_type != PACKET_AUXDATA
> > +            || cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)))
> > {
> > +            continue;
> >          }
> > +
> > +        aux = (struct tpacket_auxdata *)(void *)CMSG_DATA(cmsg);
> > +        if (auxdata_has_vlan_tci(aux)) {
> > +            if (retval < ETH_ADDR_LEN) {
> > +                return EINVAL;
> > +            }
> > +
> > +            eth_push_vlan(buffer, auxdata_to_vlan_tpid(aux),
> > +                          htons(aux->tp_vlan_tci));
> > +            break;
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int
> > +netdev_linux_rx_recv_tap(int fd, struct ofpbuf *buffer)
> > +{
> > +    ssize_t retval;
> > +    size_t size = ofpbuf_tailroom(buffer);
> > +
> > +    do {
> > +        retval = read(fd, buffer->data, size);
> > +    } while (retval < 0 && errno == EINTR);
> > +
> > +    if (retval < 0) {
> >          return errno;
> >      } else if (retval > size) {
> >          return EMSGSIZE;
> > -    } else {
> > -        buffer->size += retval;
> > -        return 0;
> >      }
> > +
> > +    buffer->size += retval;
> > +    return 0;
> > +}
> > +
> > +static int
> > +netdev_linux_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
> > +{
> > +    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
> > +    int retval;
> > +
> > +    retval = (rx->is_tap
> > +              ? netdev_linux_rx_recv_tap(rx->fd, buffer)
> > +              : netdev_linux_rx_recv_sock(rx->fd, buffer));
> > +    if (retval) {
> > +        if (retval != EAGAIN && retval != EMSGSIZE) {
> > +            VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
> > +                         ovs_strerror(errno), netdev_rx_get_name(rx_));
> > +        }
> > +    }
> > +
> > +    return retval;
> >  }
> >
> >  static void
> > --
> > 1.8.4
> >
> > _______________________________________________
> > dev mailing list
> > dev at openvswitch.org
> > http://openvswitch.org/mailman/listinfo/dev
> >



More information about the dev mailing list