]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/geneve.c
rtlwifi: Fix kernel oops introduced with commit e49656147359
[karo-tx-linux.git] / drivers / net / geneve.c
index 42edd7b7902f16125f860fa37d3b1e45d2a4c1a2..45301cb98bc1c279a760e2b19cd1ed32b4659a9f 100644 (file)
@@ -43,43 +43,24 @@ struct geneve_net {
        struct list_head        sock_list;
 };
 
-static int geneve_net_id;
-
-union geneve_addr {
-       struct sockaddr_in sin;
-       struct sockaddr_in6 sin6;
-       struct sockaddr sa;
-};
-
-static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+static unsigned int geneve_net_id;
 
 /* Pseudo network device */
 struct geneve_dev {
        struct hlist_node  hlist;       /* vni hash table */
        struct net         *net;        /* netns for packet i/o */
        struct net_device  *dev;        /* netdev for geneve tunnel */
+       struct ip_tunnel_info info;
        struct geneve_sock __rcu *sock4;        /* IPv4 socket used for geneve tunnel */
 #if IS_ENABLED(CONFIG_IPV6)
        struct geneve_sock __rcu *sock6;        /* IPv6 socket used for geneve tunnel */
 #endif
-       u8                 vni[3];      /* virtual network ID for tunnel */
-       u8                 ttl;         /* TTL override */
-       u8                 tos;         /* TOS override */
-       union geneve_addr  remote;      /* IP address for link partner */
        struct list_head   next;        /* geneve's per namespace list */
-       __be32             label;       /* IPv6 flowlabel override */
-       __be16             dst_port;
-       bool               collect_md;
        struct gro_cells   gro_cells;
-       u32                flags;
-       struct dst_cache   dst_cache;
+       bool               collect_md;
+       bool               use_udp6_rx_checksums;
 };
 
-/* Geneve device flags */
-#define GENEVE_F_UDP_ZERO_CSUM_TX      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;
@@ -87,7 +68,6 @@ struct geneve_sock {
        struct rcu_head         rcu;
        int                     refcnt;
        struct hlist_head       vni_list[VNI_HASH_SIZE];
-       u32                     flags;
 };
 
 static inline __u32 geneve_net_vni_hash(u8 vni[3])
@@ -109,6 +89,31 @@ static __be64 vni_to_tunnel_id(const __u8 *vni)
 #endif
 }
 
+/* Convert 64 bit tunnel ID to 24 bit VNI. */
+static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
+{
+#ifdef __BIG_ENDIAN
+       vni[0] = (__force __u8)(tun_id >> 16);
+       vni[1] = (__force __u8)(tun_id >> 8);
+       vni[2] = (__force __u8)tun_id;
+#else
+       vni[0] = (__force __u8)((__force u64)tun_id >> 40);
+       vni[1] = (__force __u8)((__force u64)tun_id >> 48);
+       vni[2] = (__force __u8)((__force u64)tun_id >> 56);
+#endif
+}
+
+static bool eq_tun_id_and_vni(u8 *tun_id, u8 *vni)
+{
+#ifdef __BIG_ENDIAN
+       return (vni[0] == tun_id[2]) &&
+              (vni[1] == tun_id[1]) &&
+              (vni[2] == tun_id[0]);
+#else
+       return !memcmp(vni, &tun_id[5], 3);
+#endif
+}
+
 static sa_family_t geneve_get_sk_family(struct geneve_sock *gs)
 {
        return gs->sock->sk->sk_family;
@@ -125,8 +130,8 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
        hash = geneve_net_vni_hash(vni);
        vni_list_head = &gs->vni_list[hash];
        hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
-               if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-                   addr == geneve->remote.sin.sin_addr.s_addr)
+               if (eq_tun_id_and_vni((u8 *)&geneve->info.key.tun_id, vni) &&
+                   addr == geneve->info.key.u.ipv4.dst)
                        return geneve;
        }
        return NULL;
@@ -144,8 +149,8 @@ static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
        hash = geneve_net_vni_hash(vni);
        vni_list_head = &gs->vni_list[hash];
        hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
-               if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-                   ipv6_addr_equal(&addr6, &geneve->remote.sin6.sin6_addr))
+               if (eq_tun_id_and_vni((u8 *)&geneve->info.key.tun_id, vni) &&
+                   ipv6_addr_equal(&addr6, &geneve->info.key.u.ipv6.dst))
                        return geneve;
        }
        return NULL;
@@ -160,15 +165,12 @@ static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 static struct geneve_dev *geneve_lookup_skb(struct geneve_sock *gs,
                                            struct sk_buff *skb)
 {
-       u8 *vni;
-       __be32 addr;
        static u8 zero_vni[3];
-#if IS_ENABLED(CONFIG_IPV6)
-       static struct in6_addr zero_addr6;
-#endif
+       u8 *vni;
 
        if (geneve_get_sk_family(gs) == AF_INET) {
                struct iphdr *iph;
+               __be32 addr;
 
                iph = ip_hdr(skb); /* outer IP header... */
 
@@ -183,6 +185,7 @@ static struct geneve_dev *geneve_lookup_skb(struct geneve_sock *gs,
                return geneve_lookup(gs, addr, vni);
 #if IS_ENABLED(CONFIG_IPV6)
        } else if (geneve_get_sk_family(gs) == AF_INET6) {
+               static struct in6_addr zero_addr6;
                struct ipv6hdr *ip6h;
                struct in6_addr addr6;
 
@@ -305,13 +308,12 @@ static int geneve_init(struct net_device *dev)
                return err;
        }
 
-       err = dst_cache_init(&geneve->dst_cache, GFP_KERNEL);
+       err = dst_cache_init(&geneve->info.dst_cache, GFP_KERNEL);
        if (err) {
                free_percpu(dev->tstats);
                gro_cells_destroy(&geneve->gro_cells);
                return err;
        }
-
        return 0;
 }
 
@@ -319,7 +321,7 @@ static void geneve_uninit(struct net_device *dev)
 {
        struct geneve_dev *geneve = netdev_priv(dev);
 
-       dst_cache_destroy(&geneve->dst_cache);
+       dst_cache_destroy(&geneve->info.dst_cache);
        gro_cells_destroy(&geneve->gro_cells);
        free_percpu(dev->tstats);
 }
@@ -368,7 +370,7 @@ drop:
 }
 
 static struct socket *geneve_create_sock(struct net *net, bool ipv6,
-                                        __be16 port, u32 flags)
+                                        __be16 port, bool ipv6_rx_csum)
 {
        struct socket *sock;
        struct udp_port_cfg udp_conf;
@@ -379,8 +381,7 @@ 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);
+               udp_conf.use_udp6_rx_checksums = ipv6_rx_csum;
        } else {
                udp_conf.family = AF_INET;
                udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -491,7 +492,7 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb,
 
 /* Create new listen socket if needed */
 static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
-                                               bool ipv6, u32 flags)
+                                               bool ipv6, bool ipv6_rx_csum)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_sock *gs;
@@ -503,7 +504,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, flags);
+       sock = geneve_create_sock(net, ipv6, port, ipv6_rx_csum);
        if (IS_ERR(sock)) {
                kfree(gs);
                return ERR_CAST(sock);
@@ -579,21 +580,22 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
        struct net *net = geneve->net;
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_sock *gs;
+       __u8 vni[3];
        __u32 hash;
 
-       gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
+       gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->info.key.tp_dst);
        if (gs) {
                gs->refcnt++;
                goto out;
        }
 
-       gs = geneve_socket_create(net, geneve->dst_port, ipv6, geneve->flags);
+       gs = geneve_socket_create(net, geneve->info.key.tp_dst, ipv6,
+                                 geneve->use_udp6_rx_checksums);
        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)
                rcu_assign_pointer(geneve->sock6, gs);
@@ -601,7 +603,8 @@ out:
 #endif
                rcu_assign_pointer(geneve->sock4, gs);
 
-       hash = geneve_net_vni_hash(geneve->vni);
+       tunnel_id_to_vni(geneve->info.key.tun_id, vni);
+       hash = geneve_net_vni_hash(vni);
        hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
        return 0;
 }
@@ -609,7 +612,7 @@ out:
 static int geneve_open(struct net_device *dev)
 {
        struct geneve_dev *geneve = netdev_priv(dev);
-       bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
+       bool ipv6 = !!(geneve->info.mode & IP_TUNNEL_INFO_IPV6);
        bool metadata = geneve->collect_md;
        int ret = 0;
 
@@ -636,67 +639,34 @@ static int geneve_stop(struct net_device *dev)
 }
 
 static void geneve_build_header(struct genevehdr *geneveh,
-                               __be16 tun_flags, u8 vni[3],
-                               u8 options_len, u8 *options)
+                               const struct ip_tunnel_info *info)
 {
        geneveh->ver = GENEVE_VER;
-       geneveh->opt_len = options_len / 4;
-       geneveh->oam = !!(tun_flags & TUNNEL_OAM);
-       geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+       geneveh->opt_len = info->options_len / 4;
+       geneveh->oam = !!(info->key.tun_flags & TUNNEL_OAM);
+       geneveh->critical = !!(info->key.tun_flags & TUNNEL_CRIT_OPT);
        geneveh->rsvd1 = 0;
-       memcpy(geneveh->vni, vni, 3);
+       tunnel_id_to_vni(info->key.tun_id, geneveh->vni);
        geneveh->proto_type = htons(ETH_P_TEB);
        geneveh->rsvd2 = 0;
 
-       memcpy(geneveh->options, options, options_len);
+       ip_tunnel_info_opts_get(geneveh->options, info);
 }
 
-static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
-                           __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
-                           u32 flags, bool xnet)
+static int geneve_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+                           const struct ip_tunnel_info *info,
+                           bool xnet, int ip_hdr_len)
 {
+       bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM);
        struct genevehdr *gnvh;
        int min_headroom;
        int err;
-       bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM_TX);
-
-       skb_scrub_packet(skb, xnet);
-
-       min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
-                       + GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr);
-       err = skb_cow_head(skb, min_headroom);
-       if (unlikely(err))
-               goto free_rt;
-
-       err = udp_tunnel_handle_offloads(skb, udp_sum);
-       if (err)
-               goto free_rt;
-
-       gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-       geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
-
-       skb_set_inner_protocol(skb, htons(ETH_P_TEB));
-       return 0;
-
-free_rt:
-       ip_rt_put(rt);
-       return err;
-}
-
-#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,
-                            u32 flags, bool xnet)
-{
-       struct genevehdr *gnvh;
-       int min_headroom;
-       int err;
-       bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM6_TX);
 
+       skb_reset_mac_header(skb);
        skb_scrub_packet(skb, xnet);
 
-       min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
-                       + GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+       min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len +
+                      GENEVE_BASE_HLEN + info->options_len + ip_hdr_len;
        err = skb_cow_head(skb, min_headroom);
        if (unlikely(err))
                goto free_dst;
@@ -705,9 +675,9 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
        if (err)
                goto free_dst;
 
-       gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-       geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
-
+       gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) +
+                                                  info->options_len);
+       geneve_build_header(gnvh, info);
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
        return 0;
 
@@ -715,12 +685,11 @@ free_dst:
        dst_release(dst);
        return err;
 }
-#endif
 
 static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
                                       struct net_device *dev,
                                       struct flowi4 *fl4,
-                                      struct ip_tunnel_info *info)
+                                      const struct ip_tunnel_info *info)
 {
        bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
        struct geneve_dev *geneve = netdev_priv(dev);
@@ -734,32 +703,22 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
        memset(fl4, 0, sizeof(*fl4));
        fl4->flowi4_mark = skb->mark;
        fl4->flowi4_proto = IPPROTO_UDP;
+       fl4->daddr = info->key.u.ipv4.dst;
+       fl4->saddr = info->key.u.ipv4.src;
 
-       if (info) {
-               fl4->daddr = info->key.u.ipv4.dst;
-               fl4->saddr = info->key.u.ipv4.src;
-               fl4->flowi4_tos = RT_TOS(info->key.tos);
-               dst_cache = &info->dst_cache;
-       } else {
-               tos = geneve->tos;
-               if (tos == 1) {
-                       const struct iphdr *iip = ip_hdr(skb);
-
-                       tos = ip_tunnel_get_dsfield(iip, skb);
-                       use_cache = false;
-               }
-
-               fl4->flowi4_tos = RT_TOS(tos);
-               fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
-               dst_cache = &geneve->dst_cache;
+       tos = info->key.tos;
+       if ((tos == 1) && !geneve->collect_md) {
+               tos = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
+               use_cache = false;
        }
+       fl4->flowi4_tos = RT_TOS(tos);
 
+       dst_cache = (struct dst_cache *)&info->dst_cache;
        if (use_cache) {
                rt = dst_cache_get_ip4(dst_cache, &fl4->saddr);
                if (rt)
                        return rt;
        }
-
        rt = ip_route_output_key(geneve->net, fl4);
        if (IS_ERR(rt)) {
                netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr);
@@ -779,7 +738,7 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
 static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
                                           struct net_device *dev,
                                           struct flowi6 *fl6,
-                                          struct ip_tunnel_info *info)
+                                          const struct ip_tunnel_info *info)
 {
        bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
        struct geneve_dev *geneve = netdev_priv(dev);
@@ -795,34 +754,22 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
        memset(fl6, 0, sizeof(*fl6));
        fl6->flowi6_mark = skb->mark;
        fl6->flowi6_proto = IPPROTO_UDP;
-
-       if (info) {
-               fl6->daddr = info->key.u.ipv6.dst;
-               fl6->saddr = info->key.u.ipv6.src;
-               fl6->flowlabel = ip6_make_flowinfo(RT_TOS(info->key.tos),
-                                                  info->key.label);
-               dst_cache = &info->dst_cache;
-       } else {
-               prio = geneve->tos;
-               if (prio == 1) {
-                       const struct iphdr *iip = ip_hdr(skb);
-
-                       prio = ip_tunnel_get_dsfield(iip, skb);
-                       use_cache = false;
-               }
-
-               fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio),
-                                                  geneve->label);
-               fl6->daddr = geneve->remote.sin6.sin6_addr;
-               dst_cache = &geneve->dst_cache;
+       fl6->daddr = info->key.u.ipv6.dst;
+       fl6->saddr = info->key.u.ipv6.src;
+       prio = info->key.tos;
+       if ((prio == 1) && !geneve->collect_md) {
+               prio = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
+               use_cache = false;
        }
 
+       fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio),
+                                          info->key.label);
+       dst_cache = (struct dst_cache *)&info->dst_cache;
        if (use_cache) {
                dst = dst_cache_get_ip6(dst_cache, &fl6->saddr);
                if (dst)
                        return dst;
        }
-
        if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6)) {
                netdev_dbg(dev, "no route to %pI6\n", &fl6->daddr);
                return ERR_PTR(-ENETUNREACH);
@@ -839,205 +786,81 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 }
 #endif
 
-/* Convert 64 bit tunnel ID to 24 bit VNI. */
-static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
+static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+                          struct geneve_dev *geneve,
+                          const struct ip_tunnel_info *info)
 {
-#ifdef __BIG_ENDIAN
-       vni[0] = (__force __u8)(tun_id >> 16);
-       vni[1] = (__force __u8)(tun_id >> 8);
-       vni[2] = (__force __u8)tun_id;
-#else
-       vni[0] = (__force __u8)((__force u64)tun_id >> 40);
-       vni[1] = (__force __u8)((__force u64)tun_id >> 48);
-       vni[2] = (__force __u8)((__force u64)tun_id >> 56);
-#endif
-}
-
-static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
-                                  struct ip_tunnel_info *info)
-{
-       struct geneve_dev *geneve = netdev_priv(dev);
-       struct geneve_sock *gs4;
-       struct rtable *rt = NULL;
-       const struct iphdr *iip; /* interior IP header */
-       int err = -EINVAL;
+       bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+       struct geneve_sock *gs4 = rcu_dereference(geneve->sock4);
+       const struct ip_tunnel_key *key = &info->key;
+       struct rtable *rt;
        struct flowi4 fl4;
        __u8 tos, ttl;
        __be16 sport;
        __be16 df;
-       bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
-       u32 flags = geneve->flags;
-
-       gs4 = rcu_dereference(geneve->sock4);
-       if (!gs4)
-               goto tx_error;
-
-       if (geneve->collect_md) {
-               if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
-                       netdev_dbg(dev, "no tunnel metadata\n");
-                       goto tx_error;
-               }
-               if (info && ip_tunnel_info_af(info) != AF_INET)
-                       goto tx_error;
-       }
+       int err;
 
        rt = geneve_get_v4_rt(skb, dev, &fl4, info);
-       if (IS_ERR(rt)) {
-               err = PTR_ERR(rt);
-               goto tx_error;
-       }
+       if (IS_ERR(rt))
+               return PTR_ERR(rt);
 
        sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
-       skb_reset_mac_header(skb);
-
-       iip = ip_hdr(skb);
-
-       if (info) {
-               const struct ip_tunnel_key *key = &info->key;
-               u8 *opts = NULL;
-               u8 vni[3];
-
-               tunnel_id_to_vni(key->tun_id, vni);
-               if (info->options_len)
-                       opts = ip_tunnel_info_opts(info);
-
-               if (key->tun_flags & TUNNEL_CSUM)
-                       flags &= ~GENEVE_F_UDP_ZERO_CSUM_TX;
-               else
-                       flags |= GENEVE_F_UDP_ZERO_CSUM_TX;
-
-               err = geneve_build_skb(rt, skb, key->tun_flags, vni,
-                                      info->options_len, opts, flags, xnet);
-               if (unlikely(err))
-                       goto tx_error;
-
-               tos = ip_tunnel_ecn_encap(key->tos, iip, skb);
+       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 {
-               err = geneve_build_skb(rt, skb, 0, geneve->vni,
-                                      0, NULL, flags, xnet);
-               if (unlikely(err))
-                       goto tx_error;
-
-               tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb);
-               ttl = geneve->ttl;
-               if (!ttl && IN_MULTICAST(ntohl(fl4.daddr)))
-                       ttl = 1;
-               ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
-               df = 0;
+               tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb);
+               ttl = key->ttl ? : ip4_dst_hoplimit(&rt->dst);
        }
-       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)),
-                           !!(flags & GENEVE_F_UDP_ZERO_CSUM_TX));
-
-       return NETDEV_TX_OK;
-
-tx_error:
-       dev_kfree_skb(skb);
+       df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
 
-       if (err == -ELOOP)
-               dev->stats.collisions++;
-       else if (err == -ENETUNREACH)
-               dev->stats.tx_carrier_errors++;
+       err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr));
+       if (unlikely(err))
+               return err;
 
-       dev->stats.tx_errors++;
-       return NETDEV_TX_OK;
+       udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
+                           tos, ttl, df, sport, geneve->info.key.tp_dst,
+                           !net_eq(geneve->net, dev_net(geneve->dev)),
+                           !(info->key.tun_flags & TUNNEL_CSUM));
+       return 0;
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
-                                   struct ip_tunnel_info *info)
+static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+                           struct geneve_dev *geneve,
+                           const struct ip_tunnel_info *info)
 {
-       struct geneve_dev *geneve = netdev_priv(dev);
+       bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+       struct geneve_sock *gs6 = rcu_dereference(geneve->sock6);
+       const struct ip_tunnel_key *key = &info->key;
        struct dst_entry *dst = NULL;
-       const struct iphdr *iip; /* interior IP header */
-       struct geneve_sock *gs6;
-       int err = -EINVAL;
        struct flowi6 fl6;
        __u8 prio, ttl;
        __be16 sport;
-       __be32 label;
-       bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
-       u32 flags = geneve->flags;
-
-       gs6 = rcu_dereference(geneve->sock6);
-       if (!gs6)
-               goto tx_error;
-
-       if (geneve->collect_md) {
-               if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
-                       netdev_dbg(dev, "no tunnel metadata\n");
-                       goto tx_error;
-               }
-       }
+       int err;
 
        dst = geneve_get_v6_dst(skb, dev, &fl6, info);
-       if (IS_ERR(dst)) {
-               err = PTR_ERR(dst);
-               goto tx_error;
-       }
+       if (IS_ERR(dst))
+               return PTR_ERR(dst);
 
        sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
-       skb_reset_mac_header(skb);
-
-       iip = ip_hdr(skb);
-
-       if (info) {
-               const struct ip_tunnel_key *key = &info->key;
-               u8 *opts = NULL;
-               u8 vni[3];
-
-               tunnel_id_to_vni(key->tun_id, vni);
-               if (info->options_len)
-                       opts = ip_tunnel_info_opts(info);
-
-               if (key->tun_flags & TUNNEL_CSUM)
-                       flags &= ~GENEVE_F_UDP_ZERO_CSUM6_TX;
-               else
-                       flags |= GENEVE_F_UDP_ZERO_CSUM6_TX;
-
-               err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
-                                       info->options_len, opts,
-                                       flags, xnet);
-               if (unlikely(err))
-                       goto tx_error;
-
-               prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
+       if (geneve->collect_md) {
+               prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
                ttl = key->ttl;
-               label = info->key.label;
        } else {
-               err = geneve6_build_skb(dst, skb, 0, geneve->vni,
-                                       0, NULL, flags, xnet);
-               if (unlikely(err))
-                       goto tx_error;
-
                prio = ip_tunnel_ecn_encap(ip6_tclass(fl6.flowlabel),
-                                          iip, skb);
-               ttl = geneve->ttl;
-               if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
-                       ttl = 1;
-               ttl = ttl ? : ip6_dst_hoplimit(dst);
-               label = geneve->label;
+                                          ip_hdr(skb), skb);
+               ttl = key->ttl ? : ip6_dst_hoplimit(dst);
        }
+       err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct ipv6hdr));
+       if (unlikely(err))
+               return err;
 
        udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
-                            &fl6.saddr, &fl6.daddr, prio, ttl, label,
-                            sport, geneve->dst_port,
-                            !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX));
-       return NETDEV_TX_OK;
-
-tx_error:
-       dev_kfree_skb(skb);
-
-       if (err == -ELOOP)
-               dev->stats.collisions++;
-       else if (err == -ENETUNREACH)
-               dev->stats.tx_carrier_errors++;
-
-       dev->stats.tx_errors++;
-       return NETDEV_TX_OK;
+                            &fl6.saddr, &fl6.daddr, prio, ttl,
+                            info->key.label, sport, geneve->info.key.tp_dst,
+                            !(info->key.tun_flags & TUNNEL_CSUM));
+       return 0;
 }
 #endif
 
@@ -1045,63 +868,61 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct geneve_dev *geneve = netdev_priv(dev);
        struct ip_tunnel_info *info = NULL;
+       int err;
 
-       if (geneve->collect_md)
+       if (geneve->collect_md) {
                info = skb_tunnel_info(skb);
+               if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
+                       err = -EINVAL;
+                       netdev_dbg(dev, "no tunnel metadata\n");
+                       goto tx_error;
+               }
+       } else {
+               info = &geneve->info;
+       }
 
 #if IS_ENABLED(CONFIG_IPV6)
-       if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
-           (!info && geneve->remote.sa.sa_family == AF_INET6))
-               return geneve6_xmit_skb(skb, dev, info);
-#endif
-       return geneve_xmit_skb(skb, dev, info);
-}
-
-static int __geneve_change_mtu(struct net_device *dev, int new_mtu, bool strict)
-{
-       struct geneve_dev *geneve = netdev_priv(dev);
-       /* The max_mtu calculation does not take account of GENEVE
-        * options, to avoid excluding potentially valid
-        * configurations.
-        */
-       int max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - dev->hard_header_len;
-
-       if (geneve->remote.sa.sa_family == AF_INET6)
-               max_mtu -= sizeof(struct ipv6hdr);
+       if (info->mode & IP_TUNNEL_INFO_IPV6)
+               err = geneve6_xmit_skb(skb, dev, geneve, info);
        else
-               max_mtu -= sizeof(struct iphdr);
-
-       if (new_mtu < 68)
-               return -EINVAL;
+#endif
+               err = geneve_xmit_skb(skb, dev, geneve, info);
 
-       if (new_mtu > max_mtu) {
-               if (strict)
-                       return -EINVAL;
+       if (likely(!err))
+               return NETDEV_TX_OK;
+tx_error:
+       dev_kfree_skb(skb);
 
-               new_mtu = max_mtu;
-       }
+       if (err == -ELOOP)
+               dev->stats.collisions++;
+       else if (err == -ENETUNREACH)
+               dev->stats.tx_carrier_errors++;
 
-       dev->mtu = new_mtu;
-       return 0;
+       dev->stats.tx_errors++;
+       return NETDEV_TX_OK;
 }
 
 static int geneve_change_mtu(struct net_device *dev, int new_mtu)
 {
-       return __geneve_change_mtu(dev, new_mtu, true);
+       /* Only possible if called internally, ndo_change_mtu path's new_mtu
+        * is guaranteed to be between dev->min_mtu and dev->max_mtu.
+        */
+       if (new_mtu > dev->max_mtu)
+               new_mtu = dev->max_mtu;
+
+       dev->mtu = new_mtu;
+       return 0;
 }
 
 static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
 {
        struct ip_tunnel_info *info = skb_tunnel_info(skb);
        struct geneve_dev *geneve = netdev_priv(dev);
-       struct rtable *rt;
-       struct flowi4 fl4;
-#if IS_ENABLED(CONFIG_IPV6)
-       struct dst_entry *dst;
-       struct flowi6 fl6;
-#endif
 
        if (ip_tunnel_info_af(info) == AF_INET) {
+               struct rtable *rt;
+               struct flowi4 fl4;
+
                rt = geneve_get_v4_rt(skb, dev, &fl4, info);
                if (IS_ERR(rt))
                        return PTR_ERR(rt);
@@ -1110,6 +931,9 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
                info->key.u.ipv4.src = fl4.saddr;
 #if IS_ENABLED(CONFIG_IPV6)
        } else if (ip_tunnel_info_af(info) == AF_INET6) {
+               struct dst_entry *dst;
+               struct flowi6 fl6;
+
                dst = geneve_get_v6_dst(skb, dev, &fl6, info);
                if (IS_ERR(dst))
                        return PTR_ERR(dst);
@@ -1123,7 +947,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
 
        info->key.tp_src = udp_flow_src_port(geneve->net, skb,
                                             1, USHRT_MAX, true);
-       info->key.tp_dst = geneve->dst_port;
+       info->key.tp_dst = geneve->info.key.tp_dst;
        return 0;
 }
 
@@ -1193,6 +1017,14 @@ static void geneve_setup(struct net_device *dev)
        dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
        dev->hw_features |= NETIF_F_GSO_SOFTWARE;
 
+       /* MTU range: 68 - (something less than 65535) */
+       dev->min_mtu = ETH_MIN_MTU;
+       /* The max_mtu calculation does not take account of GENEVE
+        * options, to avoid excluding potentially valid
+        * configurations. This will be further reduced by IPvX hdr size.
+        */
+       dev->max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - dev->hard_header_len;
+
        netif_keep_dst(dev);
        dev->priv_flags &= ~IFF_TX_SKB_SHARING;
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
@@ -1237,81 +1069,75 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 }
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
-                                         __be16 dst_port,
-                                         union geneve_addr *remote,
-                                         u8 vni[],
+                                         const struct ip_tunnel_info *info,
                                          bool *tun_on_same_port,
                                          bool *tun_collect_md)
 {
-       struct geneve_dev *geneve, *t;
+       struct geneve_dev *geneve, *t = NULL;
 
        *tun_on_same_port = false;
        *tun_collect_md = false;
-       t = NULL;
        list_for_each_entry(geneve, &gn->geneve_list, next) {
-               if (geneve->dst_port == dst_port) {
+               if (info->key.tp_dst == geneve->info.key.tp_dst) {
                        *tun_collect_md = geneve->collect_md;
                        *tun_on_same_port = true;
                }
-               if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-                   !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
-                   dst_port == geneve->dst_port)
+               if (info->key.tun_id == geneve->info.key.tun_id &&
+                   info->key.tp_dst == geneve->info.key.tp_dst &&
+                   !memcmp(&info->key.u, &geneve->info.key.u, sizeof(info->key.u)))
                        t = geneve;
        }
        return t;
 }
 
+static bool is_all_zero(const u8 *fp, size_t size)
+{
+       int i;
+
+       for (i = 0; i < size; i++)
+               if (fp[i])
+                       return false;
+       return true;
+}
+
+static bool is_tnl_info_zero(const struct ip_tunnel_info *info)
+{
+       if (info->key.tun_id || info->key.tun_flags || info->key.tos ||
+           info->key.ttl || info->key.label || info->key.tp_src ||
+           !is_all_zero((const u8 *)&info->key.u, sizeof(info->key.u)))
+               return false;
+       else
+               return true;
+}
+
 static int geneve_configure(struct net *net, struct net_device *dev,
-                           union geneve_addr *remote,
-                           __u32 vni, __u8 ttl, __u8 tos, __be32 label,
-                           __be16 dst_port, bool metadata, u32 flags)
+                           const struct ip_tunnel_info *info,
+                           bool metadata, bool ipv6_rx_csum)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_dev *t, *geneve = netdev_priv(dev);
        bool tun_collect_md, tun_on_same_port;
        int err, encap_len;
 
-       if (!remote)
-               return -EINVAL;
-       if (metadata &&
-           (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl || label))
+       if (metadata && !is_tnl_info_zero(info))
                return -EINVAL;
 
        geneve->net = net;
        geneve->dev = dev;
 
-       geneve->vni[0] = (vni & 0x00ff0000) >> 16;
-       geneve->vni[1] = (vni & 0x0000ff00) >> 8;
-       geneve->vni[2] =  vni & 0x000000ff;
-
-       if ((remote->sa.sa_family == AF_INET &&
-            IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
-           (remote->sa.sa_family == AF_INET6 &&
-            ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
-               return -EINVAL;
-       if (label && remote->sa.sa_family != AF_INET6)
-               return -EINVAL;
-
-       geneve->remote = *remote;
-
-       geneve->ttl = ttl;
-       geneve->tos = tos;
-       geneve->label = label;
-       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);
+       t = geneve_find_dev(gn, info, &tun_on_same_port, &tun_collect_md);
        if (t)
                return -EBUSY;
 
        /* make enough headroom for basic scenario */
        encap_len = GENEVE_BASE_HLEN + ETH_HLEN;
-       if (remote->sa.sa_family == AF_INET)
+       if (ip_tunnel_info_af(info) == AF_INET) {
                encap_len += sizeof(struct iphdr);
-       else
+               dev->max_mtu -= sizeof(struct iphdr);
+       } else {
                encap_len += sizeof(struct ipv6hdr);
+               dev->max_mtu -= sizeof(struct ipv6hdr);
+       }
        dev->needed_headroom = encap_len + ETH_HLEN;
 
        if (metadata) {
@@ -1322,7 +1148,10 @@ static int geneve_configure(struct net *net, struct net_device *dev,
                        return -EPERM;
        }
 
-       dst_cache_reset(&geneve->dst_cache);
+       dst_cache_reset(&geneve->info.dst_cache);
+       geneve->info = *info;
+       geneve->collect_md = metadata;
+       geneve->use_udp6_rx_checksums = ipv6_rx_csum;
 
        err = register_netdevice(dev);
        if (err)
@@ -1332,74 +1161,99 @@ static int geneve_configure(struct net *net, struct net_device *dev,
        return 0;
 }
 
+static void init_tnl_info(struct ip_tunnel_info *info, __u16 dst_port)
+{
+       memset(info, 0, sizeof(*info));
+       info->key.tp_dst = htons(dst_port);
+}
+
 static int geneve_newlink(struct net *net, struct net_device *dev,
                          struct nlattr *tb[], struct nlattr *data[])
 {
-       __be16 dst_port = htons(GENEVE_UDP_PORT);
-       __u8 ttl = 0, tos = 0;
+       bool use_udp6_rx_checksums = false;
+       struct ip_tunnel_info info;
        bool metadata = false;
-       union geneve_addr remote = geneve_remote_unspec;
-       __be32 label = 0;
-       __u32 vni = 0;
-       u32 flags = 0;
+
+       init_tnl_info(&info, GENEVE_UDP_PORT);
 
        if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
                return -EINVAL;
 
        if (data[IFLA_GENEVE_REMOTE]) {
-               remote.sa.sa_family = AF_INET;
-               remote.sin.sin_addr.s_addr =
+               info.key.u.ipv4.dst =
                        nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+
+               if (IN_MULTICAST(ntohl(info.key.u.ipv4.dst))) {
+                       netdev_dbg(dev, "multicast remote is unsupported\n");
+                       return -EINVAL;
+               }
        }
 
        if (data[IFLA_GENEVE_REMOTE6]) {
-               if (!IS_ENABLED(CONFIG_IPV6))
-                       return -EPFNOSUPPORT;
-
-               remote.sa.sa_family = AF_INET6;
-               remote.sin6.sin6_addr =
+ #if IS_ENABLED(CONFIG_IPV6)
+               info.mode = IP_TUNNEL_INFO_IPV6;
+               info.key.u.ipv6.dst =
                        nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
 
-               if (ipv6_addr_type(&remote.sin6.sin6_addr) &
+               if (ipv6_addr_type(&info.key.u.ipv6.dst) &
                    IPV6_ADDR_LINKLOCAL) {
                        netdev_dbg(dev, "link-local remote is unsupported\n");
                        return -EINVAL;
                }
+               if (ipv6_addr_is_multicast(&info.key.u.ipv6.dst)) {
+                       netdev_dbg(dev, "multicast remote is unsupported\n");
+                       return -EINVAL;
+               }
+               info.key.tun_flags |= TUNNEL_CSUM;
+               use_udp6_rx_checksums = true;
+#else
+               return -EPFNOSUPPORT;
+#endif
        }
 
-       if (data[IFLA_GENEVE_ID])
+       if (data[IFLA_GENEVE_ID]) {
+               __u32 vni;
+               __u8 tvni[3];
+
                vni = nla_get_u32(data[IFLA_GENEVE_ID]);
+               tvni[0] = (vni & 0x00ff0000) >> 16;
+               tvni[1] = (vni & 0x0000ff00) >> 8;
+               tvni[2] =  vni & 0x000000ff;
 
+               info.key.tun_id = vni_to_tunnel_id(tvni);
+       }
        if (data[IFLA_GENEVE_TTL])
-               ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
+               info.key.ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
 
        if (data[IFLA_GENEVE_TOS])
-               tos = nla_get_u8(data[IFLA_GENEVE_TOS]);
+               info.key.tos = nla_get_u8(data[IFLA_GENEVE_TOS]);
 
-       if (data[IFLA_GENEVE_LABEL])
-               label = nla_get_be32(data[IFLA_GENEVE_LABEL]) &
-                       IPV6_FLOWLABEL_MASK;
+       if (data[IFLA_GENEVE_LABEL]) {
+               info.key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) &
+                                 IPV6_FLOWLABEL_MASK;
+               if (info.key.label && (!(info.mode & IP_TUNNEL_INFO_IPV6)))
+                       return -EINVAL;
+       }
 
        if (data[IFLA_GENEVE_PORT])
-               dst_port = nla_get_be16(data[IFLA_GENEVE_PORT]);
+               info.key.tp_dst = nla_get_be16(data[IFLA_GENEVE_PORT]);
 
        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_ZERO_CSUM_TX;
+               info.key.tun_flags |= TUNNEL_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;
+               info.key.tun_flags &= ~TUNNEL_CSUM;
 
        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;
+               use_udp6_rx_checksums = false;
 
-       return geneve_configure(net, dev, &remote, vni, ttl, tos, label,
-                               dst_port, metadata, flags);
+       return geneve_configure(net, dev, &info, metadata, use_udp6_rx_checksums);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -1428,45 +1282,52 @@ static size_t geneve_get_size(const struct net_device *dev)
 static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 {
        struct geneve_dev *geneve = netdev_priv(dev);
+       struct ip_tunnel_info *info = &geneve->info;
+       __u8 tmp_vni[3];
        __u32 vni;
 
-       vni = (geneve->vni[0] << 16) | (geneve->vni[1] << 8) | geneve->vni[2];
+       tunnel_id_to_vni(info->key.tun_id, tmp_vni);
+       vni = (tmp_vni[0] << 16) | (tmp_vni[1] << 8) | tmp_vni[2];
        if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
                goto nla_put_failure;
 
-       if (geneve->remote.sa.sa_family == AF_INET) {
+       if (ip_tunnel_info_af(info) == AF_INET) {
                if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-                                   geneve->remote.sin.sin_addr.s_addr))
+                                   info->key.u.ipv4.dst))
                        goto nla_put_failure;
+
+               if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM,
+                              !!(info->key.tun_flags & TUNNEL_CSUM)))
+                       goto nla_put_failure;
+
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
                if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
-                                    &geneve->remote.sin6.sin6_addr))
+                                    &info->key.u.ipv6.dst))
+                       goto nla_put_failure;
+
+               if (nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+                              !(info->key.tun_flags & TUNNEL_CSUM)))
+                       goto nla_put_failure;
+
+               if (nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+                              !geneve->use_udp6_rx_checksums))
                        goto nla_put_failure;
 #endif
        }
 
-       if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
-           nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos) ||
-           nla_put_be32(skb, IFLA_GENEVE_LABEL, geneve->label))
+       if (nla_put_u8(skb, IFLA_GENEVE_TTL, info->key.ttl) ||
+           nla_put_u8(skb, IFLA_GENEVE_TOS, info->key.tos) ||
+           nla_put_be32(skb, IFLA_GENEVE_LABEL, info->key.label))
                goto nla_put_failure;
 
-       if (nla_put_be16(skb, IFLA_GENEVE_PORT, geneve->dst_port))
+       if (nla_put_be16(skb, IFLA_GENEVE_PORT, info->key.tp_dst))
                goto nla_put_failure;
 
        if (geneve->collect_md) {
                if (nla_put_flag(skb, IFLA_GENEVE_COLLECT_METADATA))
                        goto nla_put_failure;
        }
-
-       if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM,
-                      !(geneve->flags & GENEVE_F_UDP_ZERO_CSUM_TX)) ||
-           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:
@@ -1490,6 +1351,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
                                        u8 name_assign_type, u16 dst_port)
 {
        struct nlattr *tb[IFLA_MAX + 1];
+       struct ip_tunnel_info info;
        struct net_device *dev;
        LIST_HEAD(list_kill);
        int err;
@@ -1500,9 +1362,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
        if (IS_ERR(dev))
                return dev;
 
-       err = geneve_configure(net, dev, &geneve_remote_unspec,
-                              0, 0, 0, 0, htons(dst_port), true,
-                              GENEVE_F_UDP_ZERO_CSUM6_RX);
+       init_tnl_info(&info, dst_port);
+       err = geneve_configure(net, dev, &info, true, true);
        if (err) {
                free_netdev(dev);
                return ERR_PTR(err);
@@ -1511,7 +1372,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
        /* openvswitch users expect packet sizes to be unrestricted,
         * so set the largest MTU we can.
         */
-       err = __geneve_change_mtu(dev, IP_MAX_MTU, false);
+       err = geneve_change_mtu(dev, IP_MAX_MTU);
        if (err)
                goto err;
 
@@ -1520,8 +1381,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
                goto err;
 
        return dev;
-
- err:
+err:
        geneve_dellink(dev, &list_kill);
        unregister_netdevice_many(&list_kill);
        return ERR_PTR(err);
@@ -1604,7 +1464,6 @@ static int __init geneve_init_module(void)
                goto out3;
 
        return 0;
-
 out3:
        unregister_netdevice_notifier(&geneve_notifier_block);
 out2: