Merge tag 'please-pull-misc-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git...
[deliverable/linux.git] / net / ipv4 / ip_gre.c
index 205a2b8a5a84579c909a62fd4aafdd19fe64aa54..4d2025f7ec578b7e3f3fd342e82e734d36395068 100644 (file)
@@ -122,126 +122,6 @@ static int ipgre_tunnel_init(struct net_device *dev);
 static int ipgre_net_id __read_mostly;
 static int gre_tap_net_id __read_mostly;
 
-static int ip_gre_calc_hlen(__be16 o_flags)
-{
-       int addend = 4;
-
-       if (o_flags & TUNNEL_CSUM)
-               addend += 4;
-       if (o_flags & TUNNEL_KEY)
-               addend += 4;
-       if (o_flags & TUNNEL_SEQ)
-               addend += 4;
-       return addend;
-}
-
-static __be16 gre_flags_to_tnl_flags(__be16 flags)
-{
-       __be16 tflags = 0;
-
-       if (flags & GRE_CSUM)
-               tflags |= TUNNEL_CSUM;
-       if (flags & GRE_ROUTING)
-               tflags |= TUNNEL_ROUTING;
-       if (flags & GRE_KEY)
-               tflags |= TUNNEL_KEY;
-       if (flags & GRE_SEQ)
-               tflags |= TUNNEL_SEQ;
-       if (flags & GRE_STRICT)
-               tflags |= TUNNEL_STRICT;
-       if (flags & GRE_REC)
-               tflags |= TUNNEL_REC;
-       if (flags & GRE_VERSION)
-               tflags |= TUNNEL_VERSION;
-
-       return tflags;
-}
-
-static __be16 tnl_flags_to_gre_flags(__be16 tflags)
-{
-       __be16 flags = 0;
-
-       if (tflags & TUNNEL_CSUM)
-               flags |= GRE_CSUM;
-       if (tflags & TUNNEL_ROUTING)
-               flags |= GRE_ROUTING;
-       if (tflags & TUNNEL_KEY)
-               flags |= GRE_KEY;
-       if (tflags & TUNNEL_SEQ)
-               flags |= GRE_SEQ;
-       if (tflags & TUNNEL_STRICT)
-               flags |= GRE_STRICT;
-       if (tflags & TUNNEL_REC)
-               flags |= GRE_REC;
-       if (tflags & TUNNEL_VERSION)
-               flags |= GRE_VERSION;
-
-       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)
-{
-       const struct gre_base_hdr *greh;
-       __be32 *options;
-       int hdr_len;
-
-       if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
-               return -EINVAL;
-
-       greh = (struct gre_base_hdr *)skb_transport_header(skb);
-       if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
-               return -EINVAL;
-
-       tpi->flags = gre_flags_to_tnl_flags(greh->flags);
-       hdr_len = ip_gre_calc_hlen(tpi->flags);
-
-       if (!pskb_may_pull(skb, hdr_len))
-               return -EINVAL;
-
-       greh = (struct gre_base_hdr *)skb_transport_header(skb);
-       tpi->proto = greh->protocol;
-
-       options = (__be32 *)(greh + 1);
-       if (greh->flags & GRE_CSUM) {
-               if (skb_checksum_simple_validate(skb)) {
-                       *csum_err = true;
-                       return -EINVAL;
-               }
-
-               skb_checksum_try_convert(skb, IPPROTO_GRE, 0,
-                                        null_compute_pseudo);
-               options++;
-       }
-
-       if (greh->flags & GRE_KEY) {
-               tpi->key = *options;
-               options++;
-       } else {
-               tpi->key = 0;
-       }
-       if (unlikely(greh->flags & GRE_SEQ)) {
-               tpi->seq = *options;
-               options++;
-       } else {
-               tpi->seq = 0;
-       }
-       /* WCCP version 1 and 2 protocol decoding.
-        * - Change protocol to IP
-        * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
-        */
-       if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
-               tpi->proto = htons(ETH_P_IP);
-               if ((*(u8 *)options & 0xF0) != 0x40) {
-                       hdr_len += 4;
-                       if (!pskb_may_pull(skb, hdr_len))
-                               return -EINVAL;
-               }
-       }
-       return hdr_len;
-}
-
 static void ipgre_err(struct sk_buff *skb, u32 info,
                      const struct tnl_ptk_info *tpi)
 {
@@ -342,7 +222,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) < 0) {
+       if (gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP)) < 0) {
                if (!csum_err)          /* ignore csum errors. */
                        return;
        }
@@ -380,25 +260,26 @@ static __be32 tunnel_id_to_key(__be64 x)
 #endif
 }
 
-static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
+static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+                      struct ip_tunnel_net *itn, int hdr_len, bool raw_proto)
 {
-       struct net *net = dev_net(skb->dev);
        struct metadata_dst *tun_dst = NULL;
-       struct ip_tunnel_net *itn;
        const struct iphdr *iph;
        struct ip_tunnel *tunnel;
 
-       if (tpi->proto == htons(ETH_P_TEB))
-               itn = net_generic(net, gre_tap_net_id);
-       else
-               itn = net_generic(net, ipgre_net_id);
-
        iph = ip_hdr(skb);
        tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
                                  iph->saddr, iph->daddr, tpi->key);
 
        if (tunnel) {
-               skb_pop_mac_header(skb);
+               if (__iptunnel_pull_header(skb, hdr_len, tpi->proto,
+                                          raw_proto, false) < 0)
+                       goto drop;
+
+               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;
@@ -413,7 +294,34 @@ static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
                ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
                return PACKET_RCVD;
        }
-       return PACKET_REJECT;
+       return PACKET_NEXT;
+
+drop:
+       kfree_skb(skb);
+       return PACKET_RCVD;
+}
+
+static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+                    int hdr_len)
+{
+       struct net *net = dev_net(skb->dev);
+       struct ip_tunnel_net *itn;
+       int res;
+
+       if (tpi->proto == htons(ETH_P_TEB))
+               itn = net_generic(net, gre_tap_net_id);
+       else
+               itn = net_generic(net, ipgre_net_id);
+
+       res = __ipgre_rcv(skb, tpi, itn, hdr_len, false);
+       if (res == PACKET_NEXT && tpi->proto == htons(ETH_P_TEB)) {
+               /* ipgre tunnels in collect metadata mode should receive
+                * also ETH_P_TEB traffic.
+                */
+               itn = net_generic(net, ipgre_net_id);
+               res = __ipgre_rcv(skb, tpi, itn, hdr_len, true);
+       }
+       return res;
 }
 
 static int gre_rcv(struct sk_buff *skb)
@@ -430,13 +338,11 @@ static int gre_rcv(struct sk_buff *skb)
        }
 #endif
 
-       hdr_len = parse_gre_header(skb, &tpi, &csum_err);
+       hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP));
        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)
+       if (ipgre_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
                return 0;
 
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
@@ -445,49 +351,6 @@ drop:
        return 0;
 }
 
-static __sum16 gre_checksum(struct sk_buff *skb)
-{
-       __wsum csum;
-
-       if (skb->ip_summed == CHECKSUM_PARTIAL)
-               csum = lco_csum(skb);
-       else
-               csum = skb_checksum(skb, 0, skb->len, 0);
-       return csum_fold(csum);
-}
-
-static void build_header(struct sk_buff *skb, int hdr_len, __be16 flags,
-                        __be16 proto, __be32 key, __be32 seq)
-{
-       struct gre_base_hdr *greh;
-
-       skb_push(skb, hdr_len);
-
-       skb_reset_transport_header(skb);
-       greh = (struct gre_base_hdr *)skb->data;
-       greh->flags = tnl_flags_to_gre_flags(flags);
-       greh->protocol = proto;
-
-       if (flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) {
-               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
-
-               if (flags & TUNNEL_SEQ) {
-                       *ptr = seq;
-                       ptr--;
-               }
-               if (flags & TUNNEL_KEY) {
-                       *ptr = key;
-                       ptr--;
-               }
-               if (flags & TUNNEL_CSUM &&
-                   !(skb_shinfo(skb)->gso_type &
-                     (SKB_GSO_GRE | SKB_GSO_GRE_CSUM))) {
-                       *ptr = 0;
-                       *(__sum16 *)ptr = gre_checksum(skb);
-               }
-       }
-}
-
 static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
                       const struct iphdr *tnl_params,
                       __be16 proto)
@@ -498,15 +361,15 @@ static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
                tunnel->o_seqno++;
 
        /* Push GRE header. */
-       build_header(skb, tunnel->tun_hlen, tunnel->parms.o_flags,
-                    proto, tunnel->parms.o_key, htonl(tunnel->o_seqno));
+       gre_build_header(skb, tunnel->tun_hlen,
+                        tunnel->parms.o_flags, proto, tunnel->parms.o_key,
+                        htonl(tunnel->o_seqno));
 
        skb_set_inner_protocol(skb, proto);
        ip_tunnel_xmit(skb, dev, tnl_params, tnl_params->protocol);
 }
 
-static struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
-                                          bool csum)
+static int gre_handle_offloads(struct sk_buff *skb, bool csum)
 {
        return iptunnel_handle_offloads(skb, csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
 }
@@ -559,7 +422,7 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
                                          fl.saddr);
        }
 
-       tunnel_hlen = ip_gre_calc_hlen(key->tun_flags);
+       tunnel_hlen = gre_calc_hlen(key->tun_flags);
 
        min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
                        + tunnel_hlen + sizeof(struct iphdr);
@@ -574,15 +437,12 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
        }
 
        /* Push Tunnel header. */
-       skb = gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM));
-       if (IS_ERR(skb)) {
-               skb = NULL;
+       if (gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM)))
                goto err_free_rt;
-       }
 
        flags = tun_info->key.tun_flags & (TUNNEL_CSUM | TUNNEL_KEY);
-       build_header(skb, tunnel_hlen, flags, proto,
-                    tunnel_id_to_key(tun_info->key.tun_id), 0);
+       gre_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;
 
@@ -646,16 +506,14 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
                tnl_params = &tunnel->parms.iph;
        }
 
-       skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM));
-       if (IS_ERR(skb))
-               goto out;
+       if (gre_handle_offloads(skb, !!(tunnel->parms.o_flags & TUNNEL_CSUM)))
+               goto free_skb;
 
        __gre_xmit(skb, dev, tnl_params, skb->protocol);
        return NETDEV_TX_OK;
 
 free_skb:
        kfree_skb(skb);
-out:
        dev->stats.tx_dropped++;
        return NETDEV_TX_OK;
 }
@@ -670,9 +528,8 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
                return NETDEV_TX_OK;
        }
 
-       skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM));
-       if (IS_ERR(skb))
-               goto out;
+       if (gre_handle_offloads(skb, !!(tunnel->parms.o_flags & TUNNEL_CSUM)))
+               goto free_skb;
 
        if (skb_cow_head(skb, dev->needed_headroom))
                goto free_skb;
@@ -682,7 +539,6 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
 
 free_skb:
        kfree_skb(skb);
-out:
        dev->stats.tx_dropped++;
        return NETDEV_TX_OK;
 }
@@ -708,8 +564,8 @@ static int ipgre_tunnel_ioctl(struct net_device *dev,
        if (err)
                return err;
 
-       p.i_flags = tnl_flags_to_gre_flags(p.i_flags);
-       p.o_flags = tnl_flags_to_gre_flags(p.o_flags);
+       p.i_flags = gre_tnl_flags_to_gre_flags(p.i_flags);
+       p.o_flags = gre_tnl_flags_to_gre_flags(p.o_flags);
 
        if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
                return -EFAULT;
@@ -753,7 +609,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
 
        iph = (struct iphdr *)skb_push(skb, t->hlen + sizeof(*iph));
        greh = (struct gre_base_hdr *)(iph+1);
-       greh->flags = tnl_flags_to_gre_flags(t->parms.o_flags);
+       greh->flags = gre_tnl_flags_to_gre_flags(t->parms.o_flags);
        greh->protocol = htons(type);
 
        memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
@@ -854,7 +710,7 @@ static void __gre_tunnel_init(struct net_device *dev)
        int t_hlen;
 
        tunnel = netdev_priv(dev);
-       tunnel->tun_hlen = ip_gre_calc_hlen(tunnel->parms.o_flags);
+       tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
        tunnel->parms.iph.protocol = IPPROTO_GRE;
 
        tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
@@ -1031,6 +887,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;
        }
 }
 
@@ -1175,8 +1033,10 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
        struct ip_tunnel_parm *p = &t->parms;
 
        if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
-           nla_put_be16(skb, IFLA_GRE_IFLAGS, tnl_flags_to_gre_flags(p->i_flags)) ||
-           nla_put_be16(skb, IFLA_GRE_OFLAGS, tnl_flags_to_gre_flags(p->o_flags)) ||
+           nla_put_be16(skb, IFLA_GRE_IFLAGS,
+                        gre_tnl_flags_to_gre_flags(p->i_flags)) ||
+           nla_put_be16(skb, IFLA_GRE_OFLAGS,
+                        gre_tnl_flags_to_gre_flags(p->o_flags)) ||
            nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
            nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
            nla_put_in_addr(skb, IFLA_GRE_LOCAL, p->iph.saddr) ||
This page took 0.047445 seconds and 5 git commands to generate.