ip_tunnels: extend iptunnel_xmit()
[firefly-linux-kernel-4.4.55.git] / drivers / net / vxlan.c
index 8111565c35fceec89b3da0afcf0e8861a19eed40..284c6c00c3539c1ddb584b8eb99cbd3f46ccdd6b 100644 (file)
@@ -603,18 +603,22 @@ skip:
 
 /* Watch incoming packets to learn mapping between Ethernet address
  * and Tunnel endpoint.
+ * Return true if packet is bogus and should be droppped.
  */
-static void vxlan_snoop(struct net_device *dev,
+static bool vxlan_snoop(struct net_device *dev,
                        __be32 src_ip, const u8 *src_mac)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_fdb *f;
-       int err;
 
        f = vxlan_find_mac(vxlan, src_mac);
        if (likely(f)) {
                if (likely(f->remote.remote_ip == src_ip))
-                       return;
+                       return false;
+
+               /* Don't migrate static entries, drop packets */
+               if (f->state & NUD_NOARP)
+                       return true;
 
                if (net_ratelimit())
                        netdev_info(dev,
@@ -626,14 +630,19 @@ static void vxlan_snoop(struct net_device *dev,
        } else {
                /* learned new entry */
                spin_lock(&vxlan->hash_lock);
-               err = vxlan_fdb_create(vxlan, src_mac, src_ip,
-                                      NUD_REACHABLE,
-                                      NLM_F_EXCL|NLM_F_CREATE,
-                                      vxlan->dst_port,
-                                      vxlan->default_dst.remote_vni,
-                                      0, NTF_SELF);
+
+               /* close off race between vxlan_flush and incoming packets */
+               if (netif_running(dev))
+                       vxlan_fdb_create(vxlan, src_mac, src_ip,
+                                        NUD_REACHABLE,
+                                        NLM_F_EXCL|NLM_F_CREATE,
+                                        vxlan->dst_port,
+                                        vxlan->default_dst.remote_vni,
+                                        0, NTF_SELF);
                spin_unlock(&vxlan->hash_lock);
        }
+
+       return false;
 }
 
 
@@ -766,8 +775,9 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
                               vxlan->dev->dev_addr) == 0)
                goto drop;
 
-       if (vxlan->flags & VXLAN_F_LEARN)
-               vxlan_snoop(skb->dev, oip->saddr, eth_hdr(skb)->h_source);
+       if ((vxlan->flags & VXLAN_F_LEARN) &&
+           vxlan_snoop(skb->dev, oip->saddr, eth_hdr(skb)->h_source))
+               goto drop;
 
        __skb_tunnel_rx(skb, vxlan->dev);
        skb_reset_network_header(skb);
@@ -1011,7 +1021,6 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct rtable *rt;
        const struct iphdr *old_iph;
-       struct iphdr *iph;
        struct vxlanhdr *vxh;
        struct udphdr *uh;
        struct flowi4 fl4;
@@ -1020,6 +1029,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
         u32 vni;
        __be16 df = 0;
        __u8 tos, ttl;
+       int err;
 
        dst_port = rdst->remote_port ? rdst->remote_port : vxlan->dst_port;
        vni = rdst->remote_vni;
@@ -1087,13 +1097,6 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                vxlan_encap_bypass(skb, vxlan, dst_vxlan);
                return NETDEV_TX_OK;
        }
-
-       memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-       IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
-                             IPSKB_REROUTED);
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
        vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
        vxh->vx_flags = htonl(VXLAN_FLAGS);
        vxh->vx_vni = htonl(vni << 8);
@@ -1108,27 +1111,18 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
        uh->len = htons(skb->len);
        uh->check = 0;
 
-       __skb_push(skb, sizeof(*iph));
-       skb_reset_network_header(skb);
-       iph             = ip_hdr(skb);
-       iph->version    = 4;
-       iph->ihl        = sizeof(struct iphdr) >> 2;
-       iph->frag_off   = df;
-       iph->protocol   = IPPROTO_UDP;
-       iph->tos        = ip_tunnel_ecn_encap(tos, old_iph, skb);
-       iph->daddr      = dst;
-       iph->saddr      = fl4.saddr;
-       iph->ttl        = ttl ? : ip4_dst_hoplimit(&rt->dst);
-       tunnel_ip_select_ident(skb, old_iph, &rt->dst);
-
-       nf_reset(skb);
-
        vxlan_set_owner(dev, skb);
 
        if (handle_offloads(skb))
                goto drop;
 
-       iptunnel_xmit(skb, dev);
+       tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+       ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
+
+       err = iptunnel_xmit(dev_net(dev), rt, skb, fl4.saddr, dst,
+                           IPPROTO_UDP, tos, ttl, df);
+       iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+
        return NETDEV_TX_OK;
 
 drop:
@@ -1190,9 +1184,11 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
                struct sk_buff *skb1;
 
                skb1 = skb_clone(skb, GFP_ATOMIC);
-               rc1 = vxlan_xmit_one(skb1, dev, rdst, did_rsc);
-               if (rc == NETDEV_TX_OK)
-                       rc = rc1;
+               if (skb1) {
+                       rc1 = vxlan_xmit_one(skb1, dev, rdst, did_rsc);
+                       if (rc == NETDEV_TX_OK)
+                               rc = rc1;
+               }
        }
 
        rc1 = vxlan_xmit_one(skb, dev, rdst0, did_rsc);