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.

Reviewed-by: Sabrina Dubroca <s...@queasysnail.net>
Signed-off-by: Stefano Brivio <sbri...@redhat.com>
---
 drivers/net/geneve.c         | 52 +++++++++++++++++++++++++++++++-----
 include/uapi/linux/if_link.h |  9 +++++++
 2 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 8a69879d516a..cafdee06b5c8 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -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 {
@@ -898,7 +899,24 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct 
net_device *dev,
                        ttl = key->ttl;
                ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
        }
+
        df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
+       if (!df) {
+               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);
+                       }
+               }
+       }
 
        err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr));
        if (unlikely(err))
@@ -1145,6 +1163,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 +1199,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 +1254,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 +1304,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 +1324,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 +1412,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 +1533,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 +1542,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 +1610,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 +1626,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 +1659,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 +1708,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 +1762,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);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index efc588949431..f42c069d81db 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -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,
-- 
2.19.1

Reply via email to