]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/sit.c
net: Explicitly initialize u64_stats_sync structures for lockdep
[~andy/linux] / net / ipv6 / sit.c
index 7ee5cb96db348ab5c75946e77d3068c3c447ba17..365dc5473eb26a3faf02e5ab96a4b2e8ca979dc2 100644 (file)
@@ -566,6 +566,70 @@ static inline bool is_spoofed_6rd(struct ip_tunnel *tunnel, const __be32 v4addr,
        return false;
 }
 
+/* Checks if an address matches an address on the tunnel interface.
+ * Used to detect the NAT of proto 41 packets and let them pass spoofing test.
+ * Long story:
+ * This function is called after we considered the packet as spoofed
+ * in is_spoofed_6rd.
+ * We may have a router that is doing NAT for proto 41 packets
+ * for an internal station. Destination a.a.a.a/PREFIX:bbbb:bbbb
+ * will be translated to n.n.n.n/PREFIX:bbbb:bbbb. And is_spoofed_6rd
+ * function will return true, dropping the packet.
+ * But, we can still check if is spoofed against the IP
+ * addresses associated with the interface.
+ */
+static bool only_dnatted(const struct ip_tunnel *tunnel,
+       const struct in6_addr *v6dst)
+{
+       int prefix_len;
+
+#ifdef CONFIG_IPV6_SIT_6RD
+       prefix_len = tunnel->ip6rd.prefixlen + 32
+               - tunnel->ip6rd.relay_prefixlen;
+#else
+       prefix_len = 48;
+#endif
+       return ipv6_chk_custom_prefix(v6dst, prefix_len, tunnel->dev);
+}
+
+/* Returns true if a packet is spoofed */
+static bool packet_is_spoofed(struct sk_buff *skb,
+                             const struct iphdr *iph,
+                             struct ip_tunnel *tunnel)
+{
+       const struct ipv6hdr *ipv6h;
+
+       if (tunnel->dev->priv_flags & IFF_ISATAP) {
+               if (!isatap_chksrc(skb, iph, tunnel))
+                       return true;
+
+               return false;
+       }
+
+       if (tunnel->dev->flags & IFF_POINTOPOINT)
+               return false;
+
+       ipv6h = ipv6_hdr(skb);
+
+       if (unlikely(is_spoofed_6rd(tunnel, iph->saddr, &ipv6h->saddr))) {
+               net_warn_ratelimited("Src spoofed %pI4/%pI6c -> %pI4/%pI6c\n",
+                                    &iph->saddr, &ipv6h->saddr,
+                                    &iph->daddr, &ipv6h->daddr);
+               return true;
+       }
+
+       if (likely(!is_spoofed_6rd(tunnel, iph->daddr, &ipv6h->daddr)))
+               return false;
+
+       if (only_dnatted(tunnel, &ipv6h->daddr))
+               return false;
+
+       net_warn_ratelimited("Dst spoofed %pI4/%pI6c -> %pI4/%pI6c\n",
+                            &iph->saddr, &ipv6h->saddr,
+                            &iph->daddr, &ipv6h->daddr);
+       return true;
+}
+
 static int ipip6_rcv(struct sk_buff *skb)
 {
        const struct iphdr *iph = ip_hdr(skb);
@@ -586,19 +650,9 @@ static int ipip6_rcv(struct sk_buff *skb)
                IPCB(skb)->flags = 0;
                skb->protocol = htons(ETH_P_IPV6);
 
-               if (tunnel->dev->priv_flags & IFF_ISATAP) {
-                       if (!isatap_chksrc(skb, iph, tunnel)) {
-                               tunnel->dev->stats.rx_errors++;
-                               goto out;
-                       }
-               } else if (!(tunnel->dev->flags&IFF_POINTOPOINT)) {
-                       if (is_spoofed_6rd(tunnel, iph->saddr,
-                                          &ipv6_hdr(skb)->saddr) ||
-                           is_spoofed_6rd(tunnel, iph->daddr,
-                                          &ipv6_hdr(skb)->daddr)) {
-                               tunnel->dev->stats.rx_errors++;
-                               goto out;
-                       }
+               if (packet_is_spoofed(skb, iph, tunnel)) {
+                       tunnel->dev->stats.rx_errors++;
+                       goto out;
                }
 
                __skb_tunnel_rx(skb, tunnel->dev, tunnel->net);
@@ -748,7 +802,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
 
                if (neigh == NULL) {
-                       net_dbg_ratelimited("sit: nexthop == NULL\n");
+                       net_dbg_ratelimited("nexthop == NULL\n");
                        goto tx_error;
                }
 
@@ -777,7 +831,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
 
                if (neigh == NULL) {
-                       net_dbg_ratelimited("sit: nexthop == NULL\n");
+                       net_dbg_ratelimited("nexthop == NULL\n");
                        goto tx_error;
                }
 
@@ -1256,6 +1310,7 @@ static void ipip6_tunnel_setup(struct net_device *dev)
 static int ipip6_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
+       int i;
 
        tunnel->dev = dev;
        tunnel->net = dev_net(dev);
@@ -1268,6 +1323,12 @@ static int ipip6_tunnel_init(struct net_device *dev)
        if (!dev->tstats)
                return -ENOMEM;
 
+       for_each_possible_cpu(i) {
+               struct pcpu_tstats *ipip6_tunnel_stats;
+               ipip6_tunnel_stats = per_cpu_ptr(dev->tstats, i);
+               u64_stats_init(&ipip6_tunnel_stats->syncp);
+       }
+
        return 0;
 }
 
@@ -1277,6 +1338,7 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
        struct iphdr *iph = &tunnel->parms.iph;
        struct net *net = dev_net(dev);
        struct sit_net *sitn = net_generic(net, sit_net_id);
+       int i;
 
        tunnel->dev = dev;
        tunnel->net = dev_net(dev);
@@ -1290,6 +1352,13 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
        dev->tstats = alloc_percpu(struct pcpu_tstats);
        if (!dev->tstats)
                return -ENOMEM;
+
+       for_each_possible_cpu(i) {
+               struct pcpu_tstats *ipip6_fb_stats;
+               ipip6_fb_stats = per_cpu_ptr(dev->tstats, i);
+               u64_stats_init(&ipip6_fb_stats->syncp);
+       }
+
        dev_hold(dev);
        rcu_assign_pointer(sitn->tunnels_wc[0], tunnel);
        return 0;
@@ -1612,6 +1681,7 @@ static int __net_init sit_init_net(struct net *net)
                goto err_alloc_dev;
        }
        dev_net_set(sitn->fb_tunnel_dev, net);
+       sitn->fb_tunnel_dev->rtnl_link_ops = &sit_link_ops;
        /* FB netdevice is special: we have one, and only one per netns.
         * Allowing to move it to another netns is clearly unsafe.
         */
@@ -1646,7 +1716,6 @@ static void __net_exit sit_exit_net(struct net *net)
 
        rtnl_lock();
        sit_destroy_tunnels(sitn, &list);
-       unregister_netdevice_queue(sitn->fb_tunnel_dev, &list);
        unregister_netdevice_many(&list);
        rtnl_unlock();
 }