Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-block.git] / net / ipv6 / ip6_tunnel.c
index c9015fad8d657b1f403f9ae92dfcd187994eae22..6af3fcfdcbbdaff85f669c28617775cba4e7a6b7 100644 (file)
@@ -252,7 +252,7 @@ static void ip6_dev_free(struct net_device *dev)
 }
 
 /**
- * ip6_tnl_create() - create a new tunnel
+ * ip6_tnl_create - create a new tunnel
  *   @p: tunnel parameters
  *   @pt: pointer to new tunnel
  *
@@ -684,24 +684,50 @@ static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
                IP6_ECN_set_ce(ipv6_hdr(skb));
 }
 
+static __u32 ip6_tnl_get_cap(struct ip6_tnl *t,
+                            const struct in6_addr *laddr,
+                            const struct in6_addr *raddr)
+{
+       struct ip6_tnl_parm *p = &t->parms;
+       int ltype = ipv6_addr_type(laddr);
+       int rtype = ipv6_addr_type(raddr);
+       __u32 flags = 0;
+
+       if (ltype == IPV6_ADDR_ANY || rtype == IPV6_ADDR_ANY) {
+               flags = IP6_TNL_F_CAP_PER_PACKET;
+       } else if (ltype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) &&
+                  rtype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) &&
+                  !((ltype|rtype) & IPV6_ADDR_LOOPBACK) &&
+                  (!((ltype|rtype) & IPV6_ADDR_LINKLOCAL) || p->link)) {
+               if (ltype&IPV6_ADDR_UNICAST)
+                       flags |= IP6_TNL_F_CAP_XMIT;
+               if (rtype&IPV6_ADDR_UNICAST)
+                       flags |= IP6_TNL_F_CAP_RCV;
+       }
+       return flags;
+}
+
 /* called with rcu_read_lock() */
-static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t)
+static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
+                                 const struct in6_addr *laddr,
+                                 const struct in6_addr *raddr)
 {
        struct ip6_tnl_parm *p = &t->parms;
        int ret = 0;
        struct net *net = dev_net(t->dev);
 
-       if (p->flags & IP6_TNL_F_CAP_RCV) {
+       if ((p->flags & IP6_TNL_F_CAP_RCV) ||
+           ((p->flags & IP6_TNL_F_CAP_PER_PACKET) &&
+            (ip6_tnl_get_cap(t, laddr, raddr) & IP6_TNL_F_CAP_RCV))) {
                struct net_device *ldev = NULL;
 
                if (p->link)
                        ldev = dev_get_by_index_rcu(net, p->link);
 
-               if ((ipv6_addr_is_multicast(&p->laddr) ||
-                    likely(ipv6_chk_addr(net, &p->laddr, ldev, 0))) &&
-                   likely(!ipv6_chk_addr(net, &p->raddr, NULL, 0)))
+               if ((ipv6_addr_is_multicast(laddr) ||
+                    likely(ipv6_chk_addr(net, laddr, ldev, 0))) &&
+                   likely(!ipv6_chk_addr(net, raddr, NULL, 0)))
                        ret = 1;
-
        }
        return ret;
 }
@@ -740,7 +766,7 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
                        goto discard;
                }
 
-               if (!ip6_tnl_rcv_ctl(t)) {
+               if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) {
                        t->dev->stats.rx_dropped++;
                        rcu_read_unlock();
                        goto discard;
@@ -1114,25 +1140,6 @@ tx_err:
        return NETDEV_TX_OK;
 }
 
-static void ip6_tnl_set_cap(struct ip6_tnl *t)
-{
-       struct ip6_tnl_parm *p = &t->parms;
-       int ltype = ipv6_addr_type(&p->laddr);
-       int rtype = ipv6_addr_type(&p->raddr);
-
-       p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV);
-
-       if (ltype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) &&
-           rtype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) &&
-           !((ltype|rtype) & IPV6_ADDR_LOOPBACK) &&
-           (!((ltype|rtype) & IPV6_ADDR_LINKLOCAL) || p->link)) {
-               if (ltype&IPV6_ADDR_UNICAST)
-                       p->flags |= IP6_TNL_F_CAP_XMIT;
-               if (rtype&IPV6_ADDR_UNICAST)
-                       p->flags |= IP6_TNL_F_CAP_RCV;
-       }
-}
-
 static void ip6_tnl_link_config(struct ip6_tnl *t)
 {
        struct net_device *dev = t->dev;
@@ -1153,7 +1160,8 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
        if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
                fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;
 
-       ip6_tnl_set_cap(t);
+       p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET);
+       p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
 
        if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV)
                dev->flags |= IFF_POINTOPOINT;
@@ -1438,6 +1446,9 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev)
 
        t->parms.proto = IPPROTO_IPV6;
        dev_hold(dev);
+
+       ip6_tnl_link_config(t);
+
        rcu_assign_pointer(ip6n->tnls_wc[0], t);
        return 0;
 }