Merge remote-tracking branch 'asoc/topic/max98371' into asoc-next
[cascardo/linux.git] / net / ipv4 / ip_gre.c
index af5d1f3..4cc8421 100644 (file)
@@ -179,6 +179,7 @@ static __be16 tnl_flags_to_gre_flags(__be16 tflags)
        return flags;
 }
 
+/* Fills in tpi and returns header length to be pulled. */
 static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
                            bool *csum_err)
 {
@@ -238,7 +239,7 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
                                return -EINVAL;
                }
        }
-       return iptunnel_pull_header(skb, hdr_len, tpi->proto, false);
+       return hdr_len;
 }
 
 static void ipgre_err(struct sk_buff *skb, u32 info,
@@ -341,7 +342,7 @@ static void gre_err(struct sk_buff *skb, u32 info)
        struct tnl_ptk_info tpi;
        bool csum_err = false;
 
-       if (parse_gre_header(skb, &tpi, &csum_err)) {
+       if (parse_gre_header(skb, &tpi, &csum_err) < 0) {
                if (!csum_err)          /* ignore csum errors. */
                        return;
        }
@@ -397,7 +398,10 @@ static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
                                  iph->saddr, iph->daddr, tpi->key);
 
        if (tunnel) {
-               skb_pop_mac_header(skb);
+               if (tunnel->dev->type != ARPHRD_NONE)
+                       skb_pop_mac_header(skb);
+               else
+                       skb_reset_mac_header(skb);
                if (tunnel->collect_md) {
                        __be16 flags;
                        __be64 tun_id;
@@ -419,6 +423,7 @@ static int gre_rcv(struct sk_buff *skb)
 {
        struct tnl_ptk_info tpi;
        bool csum_err = false;
+       int hdr_len;
 
 #ifdef CONFIG_NET_IPGRE_BROADCAST
        if (ipv4_is_multicast(ip_hdr(skb)->daddr)) {
@@ -428,7 +433,10 @@ static int gre_rcv(struct sk_buff *skb)
        }
 #endif
 
-       if (parse_gre_header(skb, &tpi, &csum_err) < 0)
+       hdr_len = parse_gre_header(skb, &tpi, &csum_err);
+       if (hdr_len < 0)
+               goto drop;
+       if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false) < 0)
                goto drop;
 
        if (ipgre_rcv(skb, &tpi) == PACKET_RCVD)
@@ -523,7 +531,8 @@ static struct rtable *gre_get_rt(struct sk_buff *skb,
        return ip_route_output_key(net, fl);
 }
 
-static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
+static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
+                       __be16 proto)
 {
        struct ip_tunnel_info *tun_info;
        const struct ip_tunnel_key *key;
@@ -575,7 +584,7 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        flags = tun_info->key.tun_flags & (TUNNEL_CSUM | TUNNEL_KEY);
-       build_header(skb, tunnel_hlen, flags, htons(ETH_P_TEB),
+       build_header(skb, tunnel_hlen, flags, proto,
                     tunnel_id_to_key(tun_info->key.tun_id), 0);
 
        df = key->tun_flags & TUNNEL_DONT_FRAGMENT ?  htons(IP_DF) : 0;
@@ -616,7 +625,7 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
        const struct iphdr *tnl_params;
 
        if (tunnel->collect_md) {
-               gre_fb_xmit(skb, dev);
+               gre_fb_xmit(skb, dev, skb->protocol);
                return NETDEV_TX_OK;
        }
 
@@ -660,7 +669,7 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
        struct ip_tunnel *tunnel = netdev_priv(dev);
 
        if (tunnel->collect_md) {
-               gre_fb_xmit(skb, dev);
+               gre_fb_xmit(skb, dev, htons(ETH_P_TEB));
                return NETDEV_TX_OK;
        }
 
@@ -893,7 +902,7 @@ static int ipgre_tunnel_init(struct net_device *dev)
        netif_keep_dst(dev);
        dev->addr_len           = 4;
 
-       if (iph->daddr) {
+       if (iph->daddr && !tunnel->collect_md) {
 #ifdef CONFIG_NET_IPGRE_BROADCAST
                if (ipv4_is_multicast(iph->daddr)) {
                        if (!iph->saddr)
@@ -902,8 +911,9 @@ static int ipgre_tunnel_init(struct net_device *dev)
                        dev->header_ops = &ipgre_header_ops;
                }
 #endif
-       } else
+       } else if (!tunnel->collect_md) {
                dev->header_ops = &ipgre_header_ops;
+       }
 
        return ip_tunnel_init(dev);
 }
@@ -946,6 +956,11 @@ static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[])
        if (flags & (GRE_VERSION|GRE_ROUTING))
                return -EINVAL;
 
+       if (data[IFLA_GRE_COLLECT_METADATA] &&
+           data[IFLA_GRE_ENCAP_TYPE] &&
+           nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE)
+               return -EINVAL;
+
        return 0;
 }
 
@@ -1019,6 +1034,8 @@ static void ipgre_netlink_parms(struct net_device *dev,
                struct ip_tunnel *t = netdev_priv(dev);
 
                t->collect_md = true;
+               if (dev->type == ARPHRD_IPGRE)
+                       dev->type = ARPHRD_NONE;
        }
 }