geneve: UDP checksum configuration via netlink
authorTom Herbert <tom@herbertland.com>
Thu, 10 Dec 2015 20:37:45 +0000 (12:37 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 14 Dec 2015 04:58:03 +0000 (23:58 -0500)
Add support to enable and disable UDP checksums via netlink. This is
similar to how VXLAN and GUE allow this. This includes support for
enabling the UDP zero checksum (for both TX and RX).

Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/geneve.c
include/uapi/linux/if_link.h

index de5c30c..0750d7a 100644 (file)
@@ -71,8 +71,14 @@ struct geneve_dev {
        __be16             dst_port;
        bool               collect_md;
        struct gro_cells   gro_cells;
+       u32                flags;
 };
 
+/* Geneve device flags */
+#define GENEVE_F_UDP_CSUM              BIT(0)
+#define GENEVE_F_UDP_ZERO_CSUM6_TX     BIT(1)
+#define GENEVE_F_UDP_ZERO_CSUM6_RX     BIT(2)
+
 struct geneve_sock {
        bool                    collect_md;
        struct list_head        list;
@@ -81,6 +87,7 @@ struct geneve_sock {
        int                     refcnt;
        struct udp_offload      udp_offloads;
        struct hlist_head       vni_list[VNI_HASH_SIZE];
+       u32                     flags;
 };
 
 static inline __u32 geneve_net_vni_hash(u8 vni[3])
@@ -343,7 +350,7 @@ error:
 }
 
 static struct socket *geneve_create_sock(struct net *net, bool ipv6,
-                                        __be16 port)
+                                        __be16 port, u32 flags)
 {
        struct socket *sock;
        struct udp_port_cfg udp_conf;
@@ -354,6 +361,8 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
        if (ipv6) {
                udp_conf.family = AF_INET6;
                udp_conf.ipv6_v6only = 1;
+               udp_conf.use_udp6_rx_checksums =
+                   !(flags & GENEVE_F_UDP_ZERO_CSUM6_RX);
        } else {
                udp_conf.family = AF_INET;
                udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -480,7 +489,7 @@ static int geneve_gro_complete(struct sk_buff *skb, int nhoff,
 
 /* Create new listen socket if needed */
 static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
-                                               bool ipv6)
+                                               bool ipv6, u32 flags)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_sock *gs;
@@ -492,7 +501,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
        if (!gs)
                return ERR_PTR(-ENOMEM);
 
-       sock = geneve_create_sock(net, ipv6, port);
+       sock = geneve_create_sock(net, ipv6, port, flags);
        if (IS_ERR(sock)) {
                kfree(gs);
                return ERR_CAST(sock);
@@ -575,12 +584,13 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
                goto out;
        }
 
-       gs = geneve_socket_create(net, geneve->dst_port, ipv6);
+       gs = geneve_socket_create(net, geneve->dst_port, ipv6, geneve->flags);
        if (IS_ERR(gs))
                return PTR_ERR(gs);
 
 out:
        gs->collect_md = geneve->collect_md;
+       gs->flags = geneve->flags;
 #if IS_ENABLED(CONFIG_IPV6)
        if (ipv6)
                geneve->sock6 = gs;
@@ -642,11 +652,12 @@ static void geneve_build_header(struct genevehdr *geneveh,
 
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
                            __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
-                           bool csum, bool xnet)
+                           u32 flags, bool xnet)
 {
        struct genevehdr *gnvh;
        int min_headroom;
        int err;
+       bool udp_sum = !!(flags & GENEVE_F_UDP_CSUM);
 
        skb_scrub_packet(skb, xnet);
 
@@ -658,7 +669,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
                goto free_rt;
        }
 
-       skb = udp_tunnel_handle_offloads(skb, csum);
+       skb = udp_tunnel_handle_offloads(skb, udp_sum);
        if (IS_ERR(skb)) {
                err = PTR_ERR(skb);
                goto free_rt;
@@ -678,11 +689,12 @@ free_rt:
 #if IS_ENABLED(CONFIG_IPV6)
 static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
                             __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
-                            bool csum, bool xnet)
+                            u32 flags, bool xnet)
 {
        struct genevehdr *gnvh;
        int min_headroom;
        int err;
+       bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM6_TX);
 
        skb_scrub_packet(skb, xnet);
 
@@ -694,7 +706,7 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
                goto free_dst;
        }
 
-       skb = udp_tunnel_handle_offloads(skb, csum);
+       skb = udp_tunnel_handle_offloads(skb, udp_sum);
        if (IS_ERR(skb)) {
                err = PTR_ERR(skb);
                goto free_dst;
@@ -824,9 +836,9 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        struct flowi4 fl4;
        __u8 tos, ttl;
        __be16 sport;
-       bool udp_csum;
        __be16 df;
        bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+       u32 flags = geneve->flags;
 
        if (geneve->collect_md) {
                if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
@@ -857,9 +869,13 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                if (key->tun_flags & TUNNEL_GENEVE_OPT)
                        opts = ip_tunnel_info_opts(info);
 
-               udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+               if (key->tun_flags & TUNNEL_CSUM)
+                       flags |= GENEVE_F_UDP_CSUM;
+               else
+                       flags &= ~GENEVE_F_UDP_CSUM;
+
                err = geneve_build_skb(rt, skb, key->tun_flags, vni,
-                                      info->options_len, opts, udp_csum, xnet);
+                                      info->options_len, opts, flags, xnet);
                if (unlikely(err))
                        goto err;
 
@@ -867,9 +883,8 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                ttl = key->ttl;
                df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
        } else {
-               udp_csum = false;
                err = geneve_build_skb(rt, skb, 0, geneve->vni,
-                                      0, NULL, udp_csum, xnet);
+                                      0, NULL, flags, xnet);
                if (unlikely(err))
                        goto err;
 
@@ -883,7 +898,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
                                  tos, ttl, df, sport, geneve->dst_port,
                                  !net_eq(geneve->net, dev_net(geneve->dev)),
-                                 !udp_csum);
+                                 !(flags & GENEVE_F_UDP_CSUM));
 
        iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
        return NETDEV_TX_OK;
@@ -912,8 +927,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        struct flowi6 fl6;
        __u8 prio, ttl;
        __be16 sport;
-       bool udp_csum;
        bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+       u32 flags = geneve->flags;
 
        if (geneve->collect_md) {
                if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
@@ -942,19 +957,22 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                if (key->tun_flags & TUNNEL_GENEVE_OPT)
                        opts = ip_tunnel_info_opts(info);
 
-               udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+               if (key->tun_flags & TUNNEL_CSUM)
+                       flags |= GENEVE_F_UDP_CSUM;
+               else
+                       flags &= ~GENEVE_F_UDP_CSUM;
+
                err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
                                        info->options_len, opts,
-                                       udp_csum, xnet);
+                                       flags, xnet);
                if (unlikely(err))
                        goto err;
 
                prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
                ttl = key->ttl;
        } else {
-               udp_csum = false;
                err = geneve6_build_skb(dst, skb, 0, geneve->vni,
-                                       0, NULL, udp_csum, xnet);
+                                       0, NULL, flags, xnet);
                if (unlikely(err))
                        goto err;
 
@@ -966,7 +984,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        }
        err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
                                   &fl6.saddr, &fl6.daddr, prio, ttl,
-                                  sport, geneve->dst_port, !udp_csum);
+                                  sport, geneve->dst_port,
+                                  !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX));
 
        iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
        return NETDEV_TX_OK;
@@ -1099,6 +1118,9 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
        [IFLA_GENEVE_TOS]               = { .type = NLA_U8 },
        [IFLA_GENEVE_PORT]              = { .type = NLA_U16 },
        [IFLA_GENEVE_COLLECT_METADATA]  = { .type = NLA_FLAG },
+       [IFLA_GENEVE_UDP_CSUM]          = { .type = NLA_U8 },
+       [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
+       [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
 };
 
 static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -1152,7 +1174,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 static int geneve_configure(struct net *net, struct net_device *dev,
                            union geneve_addr *remote,
                            __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
-                           bool metadata)
+                           bool metadata, u32 flags)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -1183,6 +1205,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
        geneve->tos = tos;
        geneve->dst_port = dst_port;
        geneve->collect_md = metadata;
+       geneve->flags = flags;
 
        t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
                            &tun_on_same_port, &tun_collect_md);
@@ -1213,6 +1236,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
        bool metadata = false;
        union geneve_addr remote = geneve_remote_unspec;
        __u32 vni = 0;
+       u32 flags = 0;
 
        if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
                return -EINVAL;
@@ -1253,8 +1277,20 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
        if (data[IFLA_GENEVE_COLLECT_METADATA])
                metadata = true;
 
+       if (data[IFLA_GENEVE_UDP_CSUM] &&
+           nla_get_u8(data[IFLA_GENEVE_UDP_CSUM]))
+               flags |= GENEVE_F_UDP_CSUM;
+
+       if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] &&
+           nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
+               flags |= GENEVE_F_UDP_ZERO_CSUM6_TX;
+
+       if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] &&
+           nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
+               flags |= GENEVE_F_UDP_ZERO_CSUM6_RX;
+
        return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
-                               metadata);
+                               metadata, flags);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -1273,6 +1309,9 @@ static size_t geneve_get_size(const struct net_device *dev)
                nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
                nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
                nla_total_size(0) +      /* IFLA_GENEVE_COLLECT_METADATA */
+               nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_CSUM */
+               nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_TX */
+               nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_RX */
                0;
 }
 
@@ -1309,6 +1348,14 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
                        goto nla_put_failure;
        }
 
+       if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM,
+                      !!(geneve->flags & GENEVE_F_UDP_CSUM)) ||
+           nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+                      !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_TX)) ||
+           nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+                      !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_RX)))
+               goto nla_put_failure;
+
        return 0;
 
 nla_put_failure:
@@ -1342,7 +1389,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
                return dev;
 
        err = geneve_configure(net, dev, &geneve_remote_unspec,
-                              0, 0, 0, htons(dst_port), true);
+                              0, 0, 0, htons(dst_port), true, 0);
        if (err) {
                free_netdev(dev);
                return ERR_PTR(err);
index 5ad5737..2be1dd5 100644 (file)
@@ -462,6 +462,9 @@ enum {
        IFLA_GENEVE_PORT,       /* destination port */
        IFLA_GENEVE_COLLECT_METADATA,
        IFLA_GENEVE_REMOTE6,
+       IFLA_GENEVE_UDP_CSUM,
+       IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+       IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
        __IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX        (__IFLA_GENEVE_MAX - 1)