geneve: Allow configuration of DF behaviour
authorStefano Brivio <sbrivio@redhat.com>
Thu, 8 Nov 2018 11:19:19 +0000 (12:19 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 9 Nov 2018 01:13:08 +0000 (17:13 -0800)
draft-ietf-nvo3-geneve-08 says:

   It is strongly RECOMMENDED that Path MTU Discovery ([RFC1191],
   [RFC1981]) be used by setting the DF bit in the IP header when Geneve
   packets are transmitted over IPv4 (this is the default with IPv6).

Now that ICMP error handling is working for GENEVE, we can comply with
this recommendation.

Make this configurable, though, to avoid breaking existing setups. By
default, DF won't be set. It can be set or inherited from inner IPv4
packets. If it's configured to be inherited and we are encapsulating IPv6,
it will be set.

This only applies to non-lwt tunnels: if an external control plane is
used, tunnel key will still control the DF flag.

v2:
- DF behaviour configuration only applies for non-lwt tunnels, apply DF
  setting only if (!geneve->collect_md) in geneve_xmit_skb()
  (Stephen Hemminger)

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/geneve.c
include/uapi/linux/if_link.h

index 4dc4574897705d526c3115cd94272d046e3e51e6..7c53e06b31c32365440c6fbf0c3261e5a679937b 100644 (file)
@@ -70,6 +70,7 @@ struct geneve_dev {
        bool               collect_md;
        bool               use_udp6_rx_checksums;
        bool               ttl_inherit;
+       enum ifla_geneve_df df;
 };
 
 struct geneve_sock {
@@ -875,8 +876,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        struct rtable *rt;
        struct flowi4 fl4;
        __u8 tos, ttl;
+       __be16 df = 0;
        __be16 sport;
-       __be16 df;
        int err;
 
        rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info);
@@ -890,6 +891,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        if (geneve->collect_md) {
                tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
                ttl = key->ttl;
+
+               df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
        } else {
                tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb);
                if (geneve->ttl_inherit)
@@ -897,8 +900,22 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                else
                        ttl = key->ttl;
                ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
+
+               if (geneve->df == GENEVE_DF_SET) {
+                       df = htons(IP_DF);
+               } else if (geneve->df == GENEVE_DF_INHERIT) {
+                       struct ethhdr *eth = eth_hdr(skb);
+
+                       if (ntohs(eth->h_proto) == ETH_P_IPV6) {
+                               df = htons(IP_DF);
+                       } else if (ntohs(eth->h_proto) == ETH_P_IP) {
+                               struct iphdr *iph = ip_hdr(skb);
+
+                               if (iph->frag_off & htons(IP_DF))
+                                       df = htons(IP_DF);
+                       }
+               }
        }
-       df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
 
        err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr));
        if (unlikely(err))
@@ -1145,6 +1162,7 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
        [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
        [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
        [IFLA_GENEVE_TTL_INHERIT]       = { .type = NLA_U8 },
+       [IFLA_GENEVE_DF]                = { .type = NLA_U8 },
 };
 
 static int geneve_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -1180,6 +1198,16 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[],
                }
        }
 
+       if (data[IFLA_GENEVE_DF]) {
+               enum ifla_geneve_df df = nla_get_u8(data[IFLA_GENEVE_DF]);
+
+               if (df < 0 || df > GENEVE_DF_MAX) {
+                       NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_GENEVE_DF],
+                                           "Invalid DF attribute");
+                       return -EINVAL;
+               }
+       }
+
        return 0;
 }
 
@@ -1225,7 +1253,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
                            struct netlink_ext_ack *extack,
                            const struct ip_tunnel_info *info,
                            bool metadata, bool ipv6_rx_csum,
-                           bool ttl_inherit)
+                           bool ttl_inherit, enum ifla_geneve_df df)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -1275,6 +1303,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
        geneve->collect_md = metadata;
        geneve->use_udp6_rx_checksums = ipv6_rx_csum;
        geneve->ttl_inherit = ttl_inherit;
+       geneve->df = df;
 
        err = register_netdevice(dev);
        if (err)
@@ -1294,7 +1323,7 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[],
                          struct netlink_ext_ack *extack,
                          struct ip_tunnel_info *info, bool *metadata,
                          bool *use_udp6_rx_checksums, bool *ttl_inherit,
-                         bool changelink)
+                         enum ifla_geneve_df *df, bool changelink)
 {
        int attrtype;
 
@@ -1382,6 +1411,9 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[],
        if (data[IFLA_GENEVE_TOS])
                info->key.tos = nla_get_u8(data[IFLA_GENEVE_TOS]);
 
+       if (data[IFLA_GENEVE_DF])
+               *df = nla_get_u8(data[IFLA_GENEVE_DF]);
+
        if (data[IFLA_GENEVE_LABEL]) {
                info->key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) &
                                  IPV6_FLOWLABEL_MASK;
@@ -1500,6 +1532,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
                          struct nlattr *tb[], struct nlattr *data[],
                          struct netlink_ext_ack *extack)
 {
+       enum ifla_geneve_df df = GENEVE_DF_UNSET;
        bool use_udp6_rx_checksums = false;
        struct ip_tunnel_info info;
        bool ttl_inherit = false;
@@ -1508,12 +1541,12 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 
        init_tnl_info(&info, GENEVE_UDP_PORT);
        err = geneve_nl2info(tb, data, extack, &info, &metadata,
-                            &use_udp6_rx_checksums, &ttl_inherit, false);
+                            &use_udp6_rx_checksums, &ttl_inherit, &df, false);
        if (err)
                return err;
 
        err = geneve_configure(net, dev, extack, &info, metadata,
-                              use_udp6_rx_checksums, ttl_inherit);
+                              use_udp6_rx_checksums, ttl_inherit, df);
        if (err)
                return err;
 
@@ -1576,6 +1609,7 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[],
        struct ip_tunnel_info info;
        bool metadata;
        bool use_udp6_rx_checksums;
+       enum ifla_geneve_df df;
        bool ttl_inherit;
        int err;
 
@@ -1591,7 +1625,7 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[],
        use_udp6_rx_checksums = geneve->use_udp6_rx_checksums;
        ttl_inherit = geneve->ttl_inherit;
        err = geneve_nl2info(tb, data, extack, &info, &metadata,
-                            &use_udp6_rx_checksums, &ttl_inherit, true);
+                            &use_udp6_rx_checksums, &ttl_inherit, &df, true);
        if (err)
                return err;
 
@@ -1624,6 +1658,7 @@ static size_t geneve_get_size(const struct net_device *dev)
                nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
                nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
                nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
+               nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_DF */
                nla_total_size(sizeof(__be32)) +  /* IFLA_GENEVE_LABEL */
                nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
                nla_total_size(0) +      /* IFLA_GENEVE_COLLECT_METADATA */
@@ -1672,6 +1707,9 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
            nla_put_be32(skb, IFLA_GENEVE_LABEL, info->key.label))
                goto nla_put_failure;
 
+       if (nla_put_u8(skb, IFLA_GENEVE_DF, geneve->df))
+               goto nla_put_failure;
+
        if (nla_put_be16(skb, IFLA_GENEVE_PORT, info->key.tp_dst))
                goto nla_put_failure;
 
@@ -1723,7 +1761,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
                return dev;
 
        init_tnl_info(&info, dst_port);
-       err = geneve_configure(net, dev, NULL, &info, true, true, false);
+       err = geneve_configure(net, dev, NULL, &info,
+                              true, true, false, GENEVE_DF_UNSET);
        if (err) {
                free_netdev(dev);
                return ERR_PTR(err);
index efc588949431e288e1e03f8827a686ebefbd13b7..f42c069d81db353cffaf579a03722364545e26ef 100644 (file)
@@ -566,10 +566,19 @@ enum {
        IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
        IFLA_GENEVE_LABEL,
        IFLA_GENEVE_TTL_INHERIT,
+       IFLA_GENEVE_DF,
        __IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX        (__IFLA_GENEVE_MAX - 1)
 
+enum ifla_geneve_df {
+       GENEVE_DF_UNSET = 0,
+       GENEVE_DF_SET,
+       GENEVE_DF_INHERIT,
+       __GENEVE_DF_END,
+       GENEVE_DF_MAX = __GENEVE_DF_END - 1,
+};
+
 /* PPP section */
 enum {
        IFLA_PPP_UNSPEC,