vxlan: consolidate GBP handling even more
[cascardo/linux.git] / drivers / net / vxlan.c
index dfbb3cb..656a303 100644 (file)
@@ -246,6 +246,10 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, __be32 vni)
 {
        struct vxlan_dev *vxlan;
 
+       /* For flow based devices, map all packets to VNI 0 */
+       if (vs->flags & VXLAN_F_COLLECT_METADATA)
+               vni = 0;
+
        hlist_for_each_entry_rcu(vxlan, vni_head(vs, vni), hlist) {
                if (vxlan->default_dst.remote_vni == vni)
                        return vxlan;
@@ -1160,6 +1164,7 @@ out:
 }
 
 static void vxlan_parse_gbp_hdr(struct vxlanhdr *unparsed,
+                               struct sk_buff *skb, u32 vxflags,
                                struct vxlan_metadata *md,
                                struct metadata_dst *tun_dst)
 {
@@ -1179,32 +1184,24 @@ static void vxlan_parse_gbp_hdr(struct vxlanhdr *unparsed,
        if (gbp->policy_applied)
                md->gbp |= VXLAN_GBP_POLICY_APPLIED;
 
+       /* In flow-based mode, GBP is carried in dst_metadata */
+       if (!(vxflags & VXLAN_F_COLLECT_METADATA))
+               skb->mark = md->gbp;
 out:
        unparsed->vx_flags &= ~VXLAN_GBP_USED_BITS;
 }
 
-static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
-                     struct vxlan_metadata *md, __be32 vni,
+static void vxlan_rcv(struct vxlan_dev *vxlan, struct vxlan_sock *vs,
+                     struct sk_buff *skb, struct vxlan_metadata *md,
                      struct metadata_dst *tun_dst)
 {
        struct iphdr *oip = NULL;
        struct ipv6hdr *oip6 = NULL;
-       struct vxlan_dev *vxlan;
        struct pcpu_sw_netstats *stats;
        union vxlan_addr saddr;
        int err = 0;
 
-       /* For flow based devices, map all packets to VNI 0 */
-       if (vs->flags & VXLAN_F_COLLECT_METADATA)
-               vni = 0;
-
-       /* Is this VNI defined? */
-       vxlan = vxlan_vs_find_vni(vs, vni);
-       if (!vxlan)
-               goto drop;
-
        skb_reset_mac_header(skb);
-       skb_scrub_packet(skb, !net_eq(vxlan->net, dev_net(vxlan->dev)));
        skb->protocol = eth_type_trans(skb, vxlan->dev);
        skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
 
@@ -1235,9 +1232,6 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
                goto drop;
 
        skb_reset_network_header(skb);
-       /* In flow-based mode, GBP is carried in dst_metadata */
-       if (!(vs->flags & VXLAN_F_COLLECT_METADATA))
-               skb->mark = md->gbp;
 
        if (oip6)
                err = IP6_ECN_decapsulate(oip6, skb);
@@ -1281,6 +1275,7 @@ drop:
 static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
        struct metadata_dst *tun_dst = NULL;
+       struct vxlan_dev *vxlan;
        struct vxlan_sock *vs;
        struct vxlanhdr unparsed;
        struct vxlan_metadata _md;
@@ -1302,17 +1297,23 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
        unparsed.vx_flags &= ~VXLAN_HF_VNI;
        unparsed.vx_vni &= ~VXLAN_VNI_MASK;
 
-       if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB)))
-               goto drop;
-
        vs = rcu_dereference_sk_user_data(sk);
        if (!vs)
                goto drop;
 
+       vxlan = vxlan_vs_find_vni(vs, vxlan_vni(vxlan_hdr(skb)->vx_vni));
+       if (!vxlan)
+               goto drop;
+
+       if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB),
+                                !net_eq(vxlan->net, dev_net(vxlan->dev))))
+               goto drop;
+
        if (vxlan_collect_metadata(vs)) {
+               __be32 vni = vxlan_vni(vxlan_hdr(skb)->vx_vni);
+
                tun_dst = udp_tun_rx_dst(skb, vxlan_get_sk_family(vs), TUNNEL_KEY,
-                                        vxlan_vni(vxlan_hdr(skb)->vx_vni),
-                                        sizeof(*md));
+                                        vxlan_vni_to_tun_id(vni), sizeof(*md));
 
                if (!tun_dst)
                        goto drop;
@@ -1329,7 +1330,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
                if (!vxlan_remcsum(&unparsed, skb, vs->flags))
                        goto drop;
        if (vs->flags & VXLAN_F_GBP)
-               vxlan_parse_gbp_hdr(&unparsed, md, tun_dst);
+               vxlan_parse_gbp_hdr(&unparsed, skb, vs->flags, md, tun_dst);
 
        if (unparsed.vx_flags || unparsed.vx_vni) {
                /* If there are any unprocessed flags remaining treat
@@ -1343,7 +1344,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
                goto drop;
        }
 
-       vxlan_rcv(vs, skb, md, vxlan_vni(vxlan_hdr(skb)->vx_vni), tun_dst);
+       vxlan_rcv(vxlan, vs, skb, md, tun_dst);
        return 0;
 
 drop:
@@ -1957,13 +1958,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        goto drop;
                sk = vxlan->vn4_sock->sock->sk;
 
-               if (info) {
-                       if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT)
-                               df = htons(IP_DF);
-               } else {
-                       udp_sum = !!(flags & VXLAN_F_UDP_CSUM);
-               }
-
                rt = vxlan_get_route(vxlan, skb,
                                     rdst ? rdst->remote_ifindex : 0, tos,
                                     dst->sin.sin_addr.s_addr, &saddr,
@@ -1997,6 +1991,11 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        return;
                }
 
+               if (!info)
+                       udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM_TX);
+               else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT)
+                       df = htons(IP_DF);
+
                tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
                ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
                err = vxlan_build_skb(skb, &rt->dst, sizeof(struct iphdr),
@@ -2125,9 +2124,11 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 #endif
        }
 
-       if (vxlan->flags & VXLAN_F_COLLECT_METADATA &&
-           info && info->mode & IP_TUNNEL_INFO_TX) {
-               vxlan_xmit_one(skb, dev, NULL, false);
+       if (vxlan->flags & VXLAN_F_COLLECT_METADATA) {
+               if (info && info->mode & IP_TUNNEL_INFO_TX)
+                       vxlan_xmit_one(skb, dev, NULL, false);
+               else
+                       kfree_skb(skb);
                return NETDEV_TX_OK;
        }
 
@@ -2321,29 +2322,43 @@ static void vxlan_set_multicast_list(struct net_device *dev)
 {
 }
 
-static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
+static int __vxlan_change_mtu(struct net_device *dev,
+                             struct net_device *lowerdev,
+                             struct vxlan_rdst *dst, int new_mtu, bool strict)
 {
-       struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct vxlan_rdst *dst = &vxlan->default_dst;
-       struct net_device *lowerdev;
-       int max_mtu;
+       int max_mtu = IP_MAX_MTU;
 
-       lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
-       if (lowerdev == NULL)
-               return eth_change_mtu(dev, new_mtu);
+       if (lowerdev)
+               max_mtu = lowerdev->mtu;
 
        if (dst->remote_ip.sa.sa_family == AF_INET6)
-               max_mtu = lowerdev->mtu - VXLAN6_HEADROOM;
+               max_mtu -= VXLAN6_HEADROOM;
        else
-               max_mtu = lowerdev->mtu - VXLAN_HEADROOM;
+               max_mtu -= VXLAN_HEADROOM;
 
-       if (new_mtu < 68 || new_mtu > max_mtu)
+       if (new_mtu < 68)
                return -EINVAL;
 
+       if (new_mtu > max_mtu) {
+               if (strict)
+                       return -EINVAL;
+
+               new_mtu = max_mtu;
+       }
+
        dev->mtu = new_mtu;
        return 0;
 }
 
+static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_rdst *dst = &vxlan->default_dst;
+       struct net_device *lowerdev = __dev_get_by_index(vxlan->net,
+                                                        dst->remote_ifindex);
+       return __vxlan_change_mtu(dev, lowerdev, dst, new_mtu, true);
+}
+
 static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
@@ -2458,6 +2473,7 @@ static void vxlan_setup(struct net_device *dev)
        dev->hw_features |= NETIF_F_GSO_SOFTWARE;
        dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
        netif_keep_dst(dev);
+       dev->priv_flags &= ~IFF_TX_SKB_SHARING;
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
 
        INIT_LIST_HEAD(&vxlan->next);
@@ -2700,6 +2716,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
        int err;
        bool use_ipv6 = false;
        __be16 default_port = vxlan->cfg.dst_port;
+       struct net_device *lowerdev = NULL;
 
        vxlan->net = src_net;
 
@@ -2720,9 +2737,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
        }
 
        if (conf->remote_ifindex) {
-               struct net_device *lowerdev
-                        = __dev_get_by_index(src_net, conf->remote_ifindex);
-
+               lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex);
                dst->remote_ifindex = conf->remote_ifindex;
 
                if (!lowerdev) {
@@ -2746,6 +2761,12 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
                needed_headroom = lowerdev->hard_header_len;
        }
 
+       if (conf->mtu) {
+               err = __vxlan_change_mtu(dev, lowerdev, dst, conf->mtu, false);
+               if (err)
+                       return err;
+       }
+
        if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA)
                needed_headroom += VXLAN6_HEADROOM;
        else
@@ -2898,8 +2919,9 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev,
        if (data[IFLA_VXLAN_PORT])
                conf.dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
 
-       if (data[IFLA_VXLAN_UDP_CSUM] && nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
-               conf.flags |= VXLAN_F_UDP_CSUM;
+       if (data[IFLA_VXLAN_UDP_CSUM] &&
+           !nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
+               conf.flags |= VXLAN_F_UDP_ZERO_CSUM_TX;
 
        if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
            nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
@@ -3043,7 +3065,7 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
            nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->cfg.addrmax) ||
            nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->cfg.dst_port) ||
            nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,
-                       !!(vxlan->flags & VXLAN_F_UDP_CSUM)) ||
+                       !(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) ||
            nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
                        !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
            nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,