]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/ip6_gre.c
Merge branch 'for-3.7/core' of git://git.kernel.dk/linux-block
[~andy/linux] / net / ipv6 / ip6_gre.c
index 424d11a4e7ffdd6d42a5bd80b88ea8a3d5139ec1..0185679c5f536c1fa98d71ac90a2c24cd6481d8f 100644 (file)
 #include <net/ip6_tunnel.h>
 
 
+static bool log_ecn_error = true;
+module_param(log_ecn_error, bool, 0644);
+MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
+
 #define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
 #define IPV6_TCLASS_SHIFT 20
 
@@ -149,7 +153,9 @@ static struct rtnl_link_stats64 *ip6gre_get_stats64(struct net_device *dev,
        tot->rx_crc_errors = dev->stats.rx_crc_errors;
        tot->rx_fifo_errors = dev->stats.rx_fifo_errors;
        tot->rx_length_errors = dev->stats.rx_length_errors;
+       tot->rx_frame_errors = dev->stats.rx_frame_errors;
        tot->rx_errors = dev->stats.rx_errors;
+
        tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
        tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
        tot->tx_dropped = dev->stats.tx_dropped;
@@ -437,14 +443,12 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        ipv6h = (const struct ipv6hdr *)skb->data;
        p = (__be16 *)(skb->data + offset);
 
-       rcu_read_lock();
-
        t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
                                flags & GRE_KEY ?
                                *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
                                p[1]);
        if (t == NULL)
-               goto out;
+               return;
 
        switch (type) {
                __u32 teli;
@@ -489,30 +493,6 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        else
                t->err_count = 1;
        t->err_time = jiffies;
-out:
-       rcu_read_unlock();
-}
-
-static inline void ip6gre_ecn_decapsulate_ipv4(const struct ip6_tnl *t,
-               const struct ipv6hdr *ipv6h, struct sk_buff *skb)
-{
-       __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK;
-
-       if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
-               ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield);
-
-       if (INET_ECN_is_ce(dsfield))
-               IP_ECN_set_ce(ip_hdr(skb));
-}
-
-static inline void ip6gre_ecn_decapsulate_ipv6(const struct ip6_tnl *t,
-               const struct ipv6hdr *ipv6h, struct sk_buff *skb)
-{
-       if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
-               ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb));
-
-       if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))
-               IP6_ECN_set_ce(ipv6_hdr(skb));
 }
 
 static int ip6gre_rcv(struct sk_buff *skb)
@@ -526,9 +506,10 @@ static int ip6gre_rcv(struct sk_buff *skb)
        struct ip6_tnl *tunnel;
        int    offset = 4;
        __be16 gre_proto;
+       int err;
 
        if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
-               goto drop_nolock;
+               goto drop;
 
        ipv6h = ipv6_hdr(skb);
        h = skb->data;
@@ -539,7 +520,7 @@ static int ip6gre_rcv(struct sk_buff *skb)
                   - We do not support routing headers.
                 */
                if (flags&(GRE_VERSION|GRE_ROUTING))
-                       goto drop_nolock;
+                       goto drop;
 
                if (flags&GRE_CSUM) {
                        switch (skb->ip_summed) {
@@ -567,7 +548,6 @@ static int ip6gre_rcv(struct sk_buff *skb)
 
        gre_proto = *(__be16 *)(h + 2);
 
-       rcu_read_lock();
        tunnel = ip6gre_tunnel_lookup(skb->dev,
                                          &ipv6h->saddr, &ipv6h->daddr, key,
                                          gre_proto);
@@ -630,30 +610,36 @@ static int ip6gre_rcv(struct sk_buff *skb)
                        skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
                }
 
+               __skb_tunnel_rx(skb, tunnel->dev);
+
+               skb_reset_network_header(skb);
+
+               err = IP6_ECN_decapsulate(ipv6h, skb);
+               if (unlikely(err)) {
+                       if (log_ecn_error)
+                               net_info_ratelimited("non-ECT from %pI6 with dsfield=%#x\n",
+                                                    &ipv6h->saddr,
+                                                    ipv6_get_dsfield(ipv6h));
+                       if (err > 1) {
+                               ++tunnel->dev->stats.rx_frame_errors;
+                               ++tunnel->dev->stats.rx_errors;
+                               goto drop;
+                       }
+               }
+
                tstats = this_cpu_ptr(tunnel->dev->tstats);
                u64_stats_update_begin(&tstats->syncp);
                tstats->rx_packets++;
                tstats->rx_bytes += skb->len;
                u64_stats_update_end(&tstats->syncp);
 
-               __skb_tunnel_rx(skb, tunnel->dev);
-
-               skb_reset_network_header(skb);
-               if (skb->protocol == htons(ETH_P_IP))
-                       ip6gre_ecn_decapsulate_ipv4(tunnel, ipv6h, skb);
-               else if (skb->protocol == htons(ETH_P_IPV6))
-                       ip6gre_ecn_decapsulate_ipv6(tunnel, ipv6h, skb);
-
                netif_rx(skb);
 
-               rcu_read_unlock();
                return 0;
        }
        icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
 
 drop:
-       rcu_read_unlock();
-drop_nolock:
        kfree_skb(skb);
        return 0;
 }
@@ -1293,16 +1279,8 @@ static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
        return -t->hlen;
 }
 
-static int ip6gre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
-{
-       const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb_mac_header(skb);
-       memcpy(haddr, &ipv6h->saddr, sizeof(struct in6_addr));
-       return sizeof(struct in6_addr);
-}
-
 static const struct header_ops ip6gre_header_ops = {
        .create = ip6gre_header,
-       .parse  = ip6gre_header_parse,
 };
 
 static const struct net_device_ops ip6gre_netdev_ops = {