Commit 660b26dc authored by Nicolas Dichtel's avatar Nicolas Dichtel Committed by David S. Miller

mcast: add multicast proxy support (IPv4 and IPv6)

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: default avatarNicolas Dichtel <nicolas.dichtel@6wind.com>
Acked-by: default avatarDavid L Stevens <dlstevens@us.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bbb923a4
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
#define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */ #define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */
#define MRT_PIM (MRT_BASE+8) /* enable PIM code */ #define MRT_PIM (MRT_BASE+8) /* enable PIM code */
#define MRT_TABLE (MRT_BASE+9) /* Specify mroute table ID */ #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 SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */
#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1) #define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
......
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
#define MRT6_ASSERT (MRT6_BASE+7) /* Activate PIM assert mode */ #define MRT6_ASSERT (MRT6_BASE+7) /* Activate PIM assert mode */
#define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */ #define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */
#define MRT6_TABLE (MRT6_BASE+9) /* Specify mroute table ID */ #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 SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */
#define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1) #define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1)
......
...@@ -828,6 +828,49 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, ...@@ -828,6 +828,49 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
return NULL; 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 * Allocate a multicast cache entry
*/ */
...@@ -1053,7 +1096,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb) ...@@ -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 * 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; int line;
struct mfc_cache *c, *next; struct mfc_cache *c, *next;
...@@ -1062,7 +1105,8 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) ...@@ -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) { list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr && 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); list_del_rcu(&c->list);
mroute_netlink_event(mrt, c, RTM_DELROUTE); mroute_netlink_event(mrt, c, RTM_DELROUTE);
ipmr_cache_free(c); ipmr_cache_free(c);
...@@ -1073,7 +1117,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) ...@@ -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, 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; bool found = false;
int line; int line;
...@@ -1086,7 +1130,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, ...@@ -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) { list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr && 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; found = true;
break; break;
} }
...@@ -1103,7 +1148,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, ...@@ -1103,7 +1148,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
return 0; 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; return -EINVAL;
c = ipmr_cache_alloc(); c = ipmr_cache_alloc();
...@@ -1218,7 +1264,7 @@ static void mrtsock_destruct(struct sock *sk) ...@@ -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 ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
{ {
int ret; int ret, parent = 0;
struct vifctl vif; struct vifctl vif;
struct mfcctl mfc; struct mfcctl mfc;
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
...@@ -1287,16 +1333,22 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi ...@@ -1287,16 +1333,22 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
*/ */
case MRT_ADD_MFC: case MRT_ADD_MFC:
case MRT_DEL_MFC: case MRT_DEL_MFC:
parent = -1;
case MRT_ADD_MFC_PROXY:
case MRT_DEL_MFC_PROXY:
if (optlen != sizeof(mfc)) if (optlen != sizeof(mfc))
return -EINVAL; return -EINVAL;
if (copy_from_user(&mfc, optval, sizeof(mfc))) if (copy_from_user(&mfc, optval, sizeof(mfc)))
return -EFAULT; return -EFAULT;
if (parent == 0)
parent = mfc.mfcc_parent;
rtnl_lock(); rtnl_lock();
if (optname == MRT_DEL_MFC) if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
ret = ipmr_mfc_delete(mrt, &mfc); ret = ipmr_mfc_delete(mrt, &mfc, parent);
else else
ret = ipmr_mfc_add(net, mrt, &mfc, ret = ipmr_mfc_add(net, mrt, &mfc,
sk == rtnl_dereference(mrt->mroute_sk)); sk == rtnl_dereference(mrt->mroute_sk),
parent);
rtnl_unlock(); rtnl_unlock();
return ret; return ret;
/* /*
...@@ -1749,17 +1801,28 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, ...@@ -1749,17 +1801,28 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
{ {
int psend = -1; int psend = -1;
int vif, ct; int vif, ct;
int true_vifi = ipmr_find_vif(mrt, skb->dev);
vif = cache->mfc_parent; vif = cache->mfc_parent;
cache->mfc_un.res.pkt++; cache->mfc_un.res.pkt++;
cache->mfc_un.res.bytes += skb->len; 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. * Wrong interface: drop packet and (maybe) send PIM assert.
*/ */
if (mrt->vif_table[vif].dev != skb->dev) { if (mrt->vif_table[vif].dev != skb->dev) {
int true_vifi;
if (rt_is_output_route(skb_rtable(skb))) { if (rt_is_output_route(skb_rtable(skb))) {
/* It is our own packet, looped back. /* It is our own packet, looped back.
* Very complicated situation... * Very complicated situation...
...@@ -1776,7 +1839,6 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, ...@@ -1776,7 +1839,6 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
} }
cache->mfc_un.res.wrong_if++; cache->mfc_un.res.wrong_if++;
true_vifi = ipmr_find_vif(mrt, skb->dev);
if (true_vifi >= 0 && mrt->mroute_do_assert && if (true_vifi >= 0 && mrt->mroute_do_assert &&
/* pimsm uses asserts, when switching from RPT to SPT, /* 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, ...@@ -1794,15 +1856,33 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
goto dont_forward; goto dont_forward;
} }
forward:
mrt->vif_table[vif].pkt_in++; mrt->vif_table[vif].pkt_in++;
mrt->vif_table[vif].bytes_in += skb->len; mrt->vif_table[vif].bytes_in += skb->len;
/* /*
* Forward the frame * 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; for (ct = cache->mfc_un.res.maxvif - 1;
ct >= cache->mfc_un.res.minvif; ct--) { 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) { if (psend != -1) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 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, ...@@ -1813,6 +1893,7 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
psend = ct; psend = ct;
} }
} }
last_forward:
if (psend != -1) { if (psend != -1) {
if (local) { if (local) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
...@@ -1902,6 +1983,13 @@ int ip_mr_input(struct sk_buff *skb) ...@@ -1902,6 +1983,13 @@ int ip_mr_input(struct sk_buff *skb)
/* already under rcu_read_lock() */ /* already under rcu_read_lock() */
cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 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 * No usable cache entry
...@@ -2107,7 +2195,12 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb, ...@@ -2107,7 +2195,12 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
rcu_read_lock(); rcu_read_lock();
cache = ipmr_cache_find(mrt, saddr, daddr); 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) { if (cache == NULL) {
struct sk_buff *skb2; struct sk_buff *skb2;
struct iphdr *iph; struct iphdr *iph;
......
...@@ -1017,6 +1017,50 @@ static struct mfc6_cache *ip6mr_cache_find(struct mr6_table *mrt, ...@@ -1017,6 +1017,50 @@ static struct mfc6_cache *ip6mr_cache_find(struct mr6_table *mrt,
return NULL; 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 * Allocate a multicast cache entry
*/ */
...@@ -1247,7 +1291,8 @@ ip6mr_cache_unresolved(struct mr6_table *mrt, mifi_t mifi, struct sk_buff *skb) ...@@ -1247,7 +1291,8 @@ ip6mr_cache_unresolved(struct mr6_table *mrt, mifi_t mifi, struct sk_buff *skb)
* MFC6 cache manipulation by user space * 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; int line;
struct mfc6_cache *c, *next; struct mfc6_cache *c, *next;
...@@ -1256,7 +1301,9 @@ static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc) ...@@ -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) { 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) && 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); write_lock_bh(&mrt_lock);
list_del(&c->list); list_del(&c->list);
write_unlock_bh(&mrt_lock); write_unlock_bh(&mrt_lock);
...@@ -1391,7 +1438,7 @@ void ip6_mr_cleanup(void) ...@@ -1391,7 +1438,7 @@ void ip6_mr_cleanup(void)
} }
static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, 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; bool found = false;
int line; int line;
...@@ -1413,7 +1460,9 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, ...@@ -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) { list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) {
if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) && 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; found = true;
break; break;
} }
...@@ -1430,7 +1479,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, ...@@ -1430,7 +1479,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt,
return 0; 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; return -EINVAL;
c = ip6mr_cache_alloc(); c = ip6mr_cache_alloc();
...@@ -1596,7 +1646,7 @@ struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) ...@@ -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 ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
{ {
int ret; int ret, parent = 0;
struct mif6ctl vif; struct mif6ctl vif;
struct mf6cctl mfc; struct mf6cctl mfc;
mifi_t mifi; mifi_t mifi;
...@@ -1653,15 +1703,21 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns ...@@ -1653,15 +1703,21 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
*/ */
case MRT6_ADD_MFC: case MRT6_ADD_MFC:
case MRT6_DEL_MFC: case MRT6_DEL_MFC:
parent = -1;
case MRT6_ADD_MFC_PROXY:
case MRT6_DEL_MFC_PROXY:
if (optlen < sizeof(mfc)) if (optlen < sizeof(mfc))
return -EINVAL; return -EINVAL;
if (copy_from_user(&mfc, optval, sizeof(mfc))) if (copy_from_user(&mfc, optval, sizeof(mfc)))
return -EFAULT; return -EFAULT;
if (parent == 0)
parent = mfc.mf6cc_parent;
rtnl_lock(); rtnl_lock();
if (optname == MRT6_DEL_MFC) if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
ret = ip6mr_mfc_delete(mrt, &mfc); ret = ip6mr_mfc_delete(mrt, &mfc, parent);
else 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(); rtnl_unlock();
return ret; return ret;
...@@ -2015,19 +2071,29 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, ...@@ -2015,19 +2071,29 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt,
{ {
int psend = -1; int psend = -1;
int vif, ct; int vif, ct;
int true_vifi = ip6mr_find_vif(mrt, skb->dev);
vif = cache->mf6c_parent; vif = cache->mf6c_parent;
cache->mfc_un.res.pkt++; cache->mfc_un.res.pkt++;
cache->mfc_un.res.bytes += skb->len; 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. * Wrong interface: drop packet and (maybe) send PIM assert.
*/ */
if (mrt->vif6_table[vif].dev != skb->dev) { if (mrt->vif6_table[vif].dev != skb->dev) {
int true_vifi;
cache->mfc_un.res.wrong_if++; cache->mfc_un.res.wrong_if++;
true_vifi = ip6mr_find_vif(mrt, skb->dev);
if (true_vifi >= 0 && mrt->mroute_do_assert && if (true_vifi >= 0 && mrt->mroute_do_assert &&
/* pimsm uses asserts, when switching from RPT to SPT, /* 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, ...@@ -2045,14 +2111,32 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt,
goto dont_forward; goto dont_forward;
} }
forward:
mrt->vif6_table[vif].pkt_in++; mrt->vif6_table[vif].pkt_in++;
mrt->vif6_table[vif].bytes_in += skb->len; mrt->vif6_table[vif].bytes_in += skb->len;
/* /*
* Forward the frame * 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--) { 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) { if (psend != -1) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) if (skb2)
...@@ -2061,6 +2145,7 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, ...@@ -2061,6 +2145,7 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt,
psend = ct; psend = ct;
} }
} }
last_forward:
if (psend != -1) { if (psend != -1) {
ip6mr_forward2(net, mrt, skb, cache, psend); ip6mr_forward2(net, mrt, skb, cache, psend);
return 0; return 0;
...@@ -2096,6 +2181,14 @@ int ip6_mr_input(struct sk_buff *skb) ...@@ -2096,6 +2181,14 @@ int ip6_mr_input(struct sk_buff *skb)
read_lock(&mrt_lock); read_lock(&mrt_lock);
cache = ip6mr_cache_find(mrt, cache = ip6mr_cache_find(mrt,
&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr); &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 * No usable cache entry
...@@ -2183,6 +2276,13 @@ int ip6mr_get_route(struct net *net, ...@@ -2183,6 +2276,13 @@ int ip6mr_get_route(struct net *net,
read_lock(&mrt_lock); read_lock(&mrt_lock);
cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); 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) { if (!cache) {
struct sk_buff *skb2; struct sk_buff *skb2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment