mcast: add multicast proxy support (IPv4 and IPv6)
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Mon, 21 Jan 2013 06:00:26 +0000 (06:00 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 21 Jan 2013 18:55:14 +0000 (13:55 -0500)
This patch add the support of proxy multicast, ie being able to build a static
multicast tree. It adds the support of (*,*) and (*,G) entries.

The user should define an (*,*) entry which is not used for real forwarding.
This entry defines the upstream in iif and contains all interfaces from the
static tree in its oifs. It will be used to forward packet upstream when they
come from an interface belonging to the static tree.
Hence, the user should define (*,G) entries to build its static tree. Note that
upstream interface must be part of oifs: packets are sent to all oifs
interfaces except the input interface. This ensures to always join the whole
static tree, even if the packet is not coming from the upstream interface.

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Acked-by: David L Stevens <dlstevens@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/mroute.h
include/uapi/linux/mroute6.h
net/ipv4/ipmr.c
net/ipv6/ip6mr.c

index 1c11004af5dba0e8069ac7e0b760067880480182..a382d2c04a42d061f758faa84f4b76820f135e48 100644 (file)
@@ -26,7 +26,9 @@
 #define MRT_ASSERT     (MRT_BASE+7)    /* Activate PIM assert mode             */
 #define MRT_PIM                (MRT_BASE+8)    /* enable PIM code                      */
 #define MRT_TABLE      (MRT_BASE+9)    /* Specify mroute table ID              */
-#define MRT_MAX                (MRT_BASE+9)
+#define MRT_ADD_MFC_PROXY      (MRT_BASE+10)   /* Add a (*,*|G) mfc entry      */
+#define MRT_DEL_MFC_PROXY      (MRT_BASE+11)   /* Del a (*,*|G) mfc entry      */
+#define MRT_MAX                (MRT_BASE+11)
 
 #define SIOCGETVIFCNT  SIOCPROTOPRIVATE        /* IP protocol privates */
 #define SIOCGETSGCNT   (SIOCPROTOPRIVATE+1)
index c206ae3a23274d3a2dcb5778abdd9e4739283e91..ce91215cf7e62cab5832264edaf78f76b08a8205 100644 (file)
@@ -26,7 +26,9 @@
 #define MRT6_ASSERT    (MRT6_BASE+7)   /* Activate PIM assert mode             */
 #define MRT6_PIM       (MRT6_BASE+8)   /* enable PIM code                      */
 #define MRT6_TABLE     (MRT6_BASE+9)   /* Specify mroute table ID              */
-#define MRT6_MAX       (MRT6_BASE+9)
+#define MRT6_ADD_MFC_PROXY     (MRT6_BASE+10)  /* Add a (*,*|G) mfc entry      */
+#define MRT6_DEL_MFC_PROXY     (MRT6_BASE+11)  /* Del a (*,*|G) mfc entry      */
+#define MRT6_MAX       (MRT6_BASE+11)
 
 #define SIOCGETMIFCNT_IN6      SIOCPROTOPRIVATE        /* IP protocol privates */
 #define SIOCGETSGCNT_IN6       (SIOCPROTOPRIVATE+1)
index a9454cbd953ce700139fdb010a333128b565b800..4b5e22670d44645f0cfac3e103ca9a4a3e08770a 100644 (file)
@@ -828,6 +828,49 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
        return NULL;
 }
 
+/* Look for a (*,*,oif) entry */
+static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt,
+                                                   int vifi)
+{
+       int line = MFC_HASH(INADDR_ANY, INADDR_ANY);
+       struct mfc_cache *c;
+
+       list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list)
+               if (c->mfc_origin == INADDR_ANY &&
+                   c->mfc_mcastgrp == INADDR_ANY &&
+                   c->mfc_un.res.ttls[vifi] < 255)
+                       return c;
+
+       return NULL;
+}
+
+/* Look for a (*,G) entry */
+static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt,
+                                            __be32 mcastgrp, int vifi)
+{
+       int line = MFC_HASH(mcastgrp, INADDR_ANY);
+       struct mfc_cache *c, *proxy;
+
+       if (mcastgrp == INADDR_ANY)
+               goto skip;
+
+       list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list)
+               if (c->mfc_origin == INADDR_ANY &&
+                   c->mfc_mcastgrp == mcastgrp) {
+                       if (c->mfc_un.res.ttls[vifi] < 255)
+                               return c;
+
+                       /* It's ok if the vifi is part of the static tree */
+                       proxy = ipmr_cache_find_any_parent(mrt,
+                                                          c->mfc_parent);
+                       if (proxy && proxy->mfc_un.res.ttls[vifi] < 255)
+                               return c;
+               }
+
+skip:
+       return ipmr_cache_find_any_parent(mrt, vifi);
+}
+
 /*
  *     Allocate a multicast cache entry
  */
@@ -1053,7 +1096,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
  *     MFC cache manipulation by user space mroute daemon
  */
 
-static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
+static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
 {
        int line;
        struct mfc_cache *c, *next;
@@ -1062,7 +1105,8 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
 
        list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
                if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
-                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
+                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr &&
+                   (parent == -1 || parent == c->mfc_parent)) {
                        list_del_rcu(&c->list);
                        mroute_netlink_event(mrt, c, RTM_DELROUTE);
                        ipmr_cache_free(c);
@@ -1073,7 +1117,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
 }
 
 static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
-                       struct mfcctl *mfc, int mrtsock)
+                       struct mfcctl *mfc, int mrtsock, int parent)
 {
        bool found = false;
        int line;
@@ -1086,7 +1130,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
 
        list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
                if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
-                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
+                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr &&
+                   (parent == -1 || parent == c->mfc_parent)) {
                        found = true;
                        break;
                }
@@ -1103,7 +1148,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
                return 0;
        }
 
-       if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
+       if (mfc->mfcc_mcastgrp.s_addr != INADDR_ANY &&
+           !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
                return -EINVAL;
 
        c = ipmr_cache_alloc();
@@ -1218,7 +1264,7 @@ static void mrtsock_destruct(struct sock *sk)
 
 int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
 {
-       int ret;
+       int ret, parent = 0;
        struct vifctl vif;
        struct mfcctl mfc;
        struct net *net = sock_net(sk);
@@ -1287,16 +1333,22 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
                 */
        case MRT_ADD_MFC:
        case MRT_DEL_MFC:
+               parent = -1;
+       case MRT_ADD_MFC_PROXY:
+       case MRT_DEL_MFC_PROXY:
                if (optlen != sizeof(mfc))
                        return -EINVAL;
                if (copy_from_user(&mfc, optval, sizeof(mfc)))
                        return -EFAULT;
+               if (parent == 0)
+                       parent = mfc.mfcc_parent;
                rtnl_lock();
-               if (optname == MRT_DEL_MFC)
-                       ret = ipmr_mfc_delete(mrt, &mfc);
+               if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
+                       ret = ipmr_mfc_delete(mrt, &mfc, parent);
                else
                        ret = ipmr_mfc_add(net, mrt, &mfc,
-                                          sk == rtnl_dereference(mrt->mroute_sk));
+                                          sk == rtnl_dereference(mrt->mroute_sk),
+                                          parent);
                rtnl_unlock();
                return ret;
                /*
@@ -1749,17 +1801,28 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
 {
        int psend = -1;
        int vif, ct;
+       int true_vifi = ipmr_find_vif(mrt, skb->dev);
 
        vif = cache->mfc_parent;
        cache->mfc_un.res.pkt++;
        cache->mfc_un.res.bytes += skb->len;
 
+       if (cache->mfc_origin == INADDR_ANY && true_vifi >= 0) {
+               struct mfc_cache *cache_proxy;
+
+               /* For an (*,G) entry, we only check that the incomming
+                * interface is part of the static tree.
+                */
+               cache_proxy = ipmr_cache_find_any_parent(mrt, vif);
+               if (cache_proxy &&
+                   cache_proxy->mfc_un.res.ttls[true_vifi] < 255)
+                       goto forward;
+       }
+
        /*
         * Wrong interface: drop packet and (maybe) send PIM assert.
         */
        if (mrt->vif_table[vif].dev != skb->dev) {
-               int true_vifi;
-
                if (rt_is_output_route(skb_rtable(skb))) {
                        /* It is our own packet, looped back.
                         * Very complicated situation...
@@ -1776,7 +1839,6 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
                }
 
                cache->mfc_un.res.wrong_if++;
-               true_vifi = ipmr_find_vif(mrt, skb->dev);
 
                if (true_vifi >= 0 && mrt->mroute_do_assert &&
                    /* pimsm uses asserts, when switching from RPT to SPT,
@@ -1794,15 +1856,33 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
                goto dont_forward;
        }
 
+forward:
        mrt->vif_table[vif].pkt_in++;
        mrt->vif_table[vif].bytes_in += skb->len;
 
        /*
         *      Forward the frame
         */
+       if (cache->mfc_origin == INADDR_ANY &&
+           cache->mfc_mcastgrp == INADDR_ANY) {
+               if (true_vifi >= 0 &&
+                   true_vifi != cache->mfc_parent &&
+                   ip_hdr(skb)->ttl >
+                               cache->mfc_un.res.ttls[cache->mfc_parent]) {
+                       /* It's an (*,*) entry and the packet is not coming from
+                        * the upstream: forward the packet to the upstream
+                        * only.
+                        */
+                       psend = cache->mfc_parent;
+                       goto last_forward;
+               }
+               goto dont_forward;
+       }
        for (ct = cache->mfc_un.res.maxvif - 1;
             ct >= cache->mfc_un.res.minvif; ct--) {
-               if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
+               /* For (*,G) entry, don't forward to the incoming interface */
+               if ((cache->mfc_origin != INADDR_ANY || ct != true_vifi) &&
+                   ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
                        if (psend != -1) {
                                struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1813,6 +1893,7 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
                        psend = ct;
                }
        }
+last_forward:
        if (psend != -1) {
                if (local) {
                        struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
@@ -1902,6 +1983,13 @@ int ip_mr_input(struct sk_buff *skb)
 
        /* already under rcu_read_lock() */
        cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
+       if (cache == NULL) {
+               int vif = ipmr_find_vif(mrt, skb->dev);
+
+               if (vif >= 0)
+                       cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr,
+                                                   vif);
+       }
 
        /*
         *      No usable cache entry
@@ -2107,7 +2195,12 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
 
        rcu_read_lock();
        cache = ipmr_cache_find(mrt, saddr, daddr);
+       if (cache == NULL && skb->dev) {
+               int vif = ipmr_find_vif(mrt, skb->dev);
 
+               if (vif >= 0)
+                       cache = ipmr_cache_find_any(mrt, daddr, vif);
+       }
        if (cache == NULL) {
                struct sk_buff *skb2;
                struct iphdr *iph;
index 26dcdec9e3a5f6cbbe799402c39bb5d09c9d561d..acc32494006a2ccf5a1993ba7f6c9b9c325aaf17 100644 (file)
@@ -1017,6 +1017,50 @@ static struct mfc6_cache *ip6mr_cache_find(struct mr6_table *mrt,
        return NULL;
 }
 
+/* Look for a (*,*,oif) entry */
+static struct mfc6_cache *ip6mr_cache_find_any_parent(struct mr6_table *mrt,
+                                                     mifi_t mifi)
+{
+       int line = MFC6_HASH(&in6addr_any, &in6addr_any);
+       struct mfc6_cache *c;
+
+       list_for_each_entry(c, &mrt->mfc6_cache_array[line], list)
+               if (ipv6_addr_any(&c->mf6c_origin) &&
+                   ipv6_addr_any(&c->mf6c_mcastgrp) &&
+                   (c->mfc_un.res.ttls[mifi] < 255))
+                       return c;
+
+       return NULL;
+}
+
+/* Look for a (*,G) entry */
+static struct mfc6_cache *ip6mr_cache_find_any(struct mr6_table *mrt,
+                                              struct in6_addr *mcastgrp,
+                                              mifi_t mifi)
+{
+       int line = MFC6_HASH(mcastgrp, &in6addr_any);
+       struct mfc6_cache *c, *proxy;
+
+       if (ipv6_addr_any(mcastgrp))
+               goto skip;
+
+       list_for_each_entry(c, &mrt->mfc6_cache_array[line], list)
+               if (ipv6_addr_any(&c->mf6c_origin) &&
+                   ipv6_addr_equal(&c->mf6c_mcastgrp, mcastgrp)) {
+                       if (c->mfc_un.res.ttls[mifi] < 255)
+                               return c;
+
+                       /* It's ok if the mifi is part of the static tree */
+                       proxy = ip6mr_cache_find_any_parent(mrt,
+                                                           c->mf6c_parent);
+                       if (proxy && proxy->mfc_un.res.ttls[mifi] < 255)
+                               return c;
+               }
+
+skip:
+       return ip6mr_cache_find_any_parent(mrt, mifi);
+}
+
 /*
  *     Allocate a multicast cache entry
  */
@@ -1247,7 +1291,8 @@ ip6mr_cache_unresolved(struct mr6_table *mrt, mifi_t mifi, struct sk_buff *skb)
  *     MFC6 cache manipulation by user space
  */
 
-static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc)
+static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc,
+                           int parent)
 {
        int line;
        struct mfc6_cache *c, *next;
@@ -1256,7 +1301,9 @@ static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc)
 
        list_for_each_entry_safe(c, next, &mrt->mfc6_cache_array[line], list) {
                if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) &&
-                   ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) {
+                   ipv6_addr_equal(&c->mf6c_mcastgrp,
+                                   &mfc->mf6cc_mcastgrp.sin6_addr) &&
+                   (parent == -1 || parent == c->mf6c_parent)) {
                        write_lock_bh(&mrt_lock);
                        list_del(&c->list);
                        write_unlock_bh(&mrt_lock);
@@ -1391,7 +1438,7 @@ void ip6_mr_cleanup(void)
 }
 
 static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt,
-                        struct mf6cctl *mfc, int mrtsock)
+                        struct mf6cctl *mfc, int mrtsock, int parent)
 {
        bool found = false;
        int line;
@@ -1413,7 +1460,9 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt,
 
        list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) {
                if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) &&
-                   ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) {
+                   ipv6_addr_equal(&c->mf6c_mcastgrp,
+                                   &mfc->mf6cc_mcastgrp.sin6_addr) &&
+                   (parent == -1 || parent == mfc->mf6cc_parent)) {
                        found = true;
                        break;
                }
@@ -1430,7 +1479,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt,
                return 0;
        }
 
-       if (!ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr))
+       if (!ipv6_addr_any(&mfc->mf6cc_mcastgrp.sin6_addr) &&
+           !ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr))
                return -EINVAL;
 
        c = ip6mr_cache_alloc();
@@ -1596,7 +1646,7 @@ struct sock *mroute6_socket(struct net *net, struct sk_buff *skb)
 
 int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
 {
-       int ret;
+       int ret, parent = 0;
        struct mif6ctl vif;
        struct mf6cctl mfc;
        mifi_t mifi;
@@ -1653,15 +1703,21 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
         */
        case MRT6_ADD_MFC:
        case MRT6_DEL_MFC:
+               parent = -1;
+       case MRT6_ADD_MFC_PROXY:
+       case MRT6_DEL_MFC_PROXY:
                if (optlen < sizeof(mfc))
                        return -EINVAL;
                if (copy_from_user(&mfc, optval, sizeof(mfc)))
                        return -EFAULT;
+               if (parent == 0)
+                       parent = mfc.mf6cc_parent;
                rtnl_lock();
-               if (optname == MRT6_DEL_MFC)
-                       ret = ip6mr_mfc_delete(mrt, &mfc);
+               if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
+                       ret = ip6mr_mfc_delete(mrt, &mfc, parent);
                else
-                       ret = ip6mr_mfc_add(net, mrt, &mfc, sk == mrt->mroute6_sk);
+                       ret = ip6mr_mfc_add(net, mrt, &mfc,
+                                           sk == mrt->mroute6_sk, parent);
                rtnl_unlock();
                return ret;
 
@@ -2015,19 +2071,29 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt,
 {
        int psend = -1;
        int vif, ct;
+       int true_vifi = ip6mr_find_vif(mrt, skb->dev);
 
        vif = cache->mf6c_parent;
        cache->mfc_un.res.pkt++;
        cache->mfc_un.res.bytes += skb->len;
 
+       if (ipv6_addr_any(&cache->mf6c_origin) && true_vifi >= 0) {
+               struct mfc6_cache *cache_proxy;
+
+               /* For an (*,G) entry, we only check that the incomming
+                * interface is part of the static tree.
+                */
+               cache_proxy = ip6mr_cache_find_any_parent(mrt, vif);
+               if (cache_proxy &&
+                   cache_proxy->mfc_un.res.ttls[true_vifi] < 255)
+                       goto forward;
+       }
+
        /*
         * Wrong interface: drop packet and (maybe) send PIM assert.
         */
        if (mrt->vif6_table[vif].dev != skb->dev) {
-               int true_vifi;
-
                cache->mfc_un.res.wrong_if++;
-               true_vifi = ip6mr_find_vif(mrt, skb->dev);
 
                if (true_vifi >= 0 && mrt->mroute_do_assert &&
                    /* pimsm uses asserts, when switching from RPT to SPT,
@@ -2045,14 +2111,32 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt,
                goto dont_forward;
        }
 
+forward:
        mrt->vif6_table[vif].pkt_in++;
        mrt->vif6_table[vif].bytes_in += skb->len;
 
        /*
         *      Forward the frame
         */
+       if (ipv6_addr_any(&cache->mf6c_origin) &&
+           ipv6_addr_any(&cache->mf6c_mcastgrp)) {
+               if (true_vifi >= 0 &&
+                   true_vifi != cache->mf6c_parent &&
+                   ipv6_hdr(skb)->hop_limit >
+                               cache->mfc_un.res.ttls[cache->mf6c_parent]) {
+                       /* It's an (*,*) entry and the packet is not coming from
+                        * the upstream: forward the packet to the upstream
+                        * only.
+                        */
+                       psend = cache->mf6c_parent;
+                       goto last_forward;
+               }
+               goto dont_forward;
+       }
        for (ct = cache->mfc_un.res.maxvif - 1; ct >= cache->mfc_un.res.minvif; ct--) {
-               if (ipv6_hdr(skb)->hop_limit > cache->mfc_un.res.ttls[ct]) {
+               /* For (*,G) entry, don't forward to the incoming interface */
+               if ((!ipv6_addr_any(&cache->mf6c_origin) || ct != true_vifi) &&
+                   ipv6_hdr(skb)->hop_limit > cache->mfc_un.res.ttls[ct]) {
                        if (psend != -1) {
                                struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
                                if (skb2)
@@ -2061,6 +2145,7 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt,
                        psend = ct;
                }
        }
+last_forward:
        if (psend != -1) {
                ip6mr_forward2(net, mrt, skb, cache, psend);
                return 0;
@@ -2096,6 +2181,14 @@ int ip6_mr_input(struct sk_buff *skb)
        read_lock(&mrt_lock);
        cache = ip6mr_cache_find(mrt,
                                 &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
+       if (cache == NULL) {
+               int vif = ip6mr_find_vif(mrt, skb->dev);
+
+               if (vif >= 0)
+                       cache = ip6mr_cache_find_any(mrt,
+                                                    &ipv6_hdr(skb)->daddr,
+                                                    vif);
+       }
 
        /*
         *      No usable cache entry
@@ -2183,6 +2276,13 @@ int ip6mr_get_route(struct net *net,
 
        read_lock(&mrt_lock);
        cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr);
+       if (!cache && skb->dev) {
+               int vif = ip6mr_find_vif(mrt, skb->dev);
+
+               if (vif >= 0)
+                       cache = ip6mr_cache_find_any(mrt, &rt->rt6i_dst.addr,
+                                                    vif);
+       }
 
        if (!cache) {
                struct sk_buff *skb2;