Commit 3fcd550a authored by David S. Miller's avatar David S. Miller

Merge branch 'mcast'

Nicolas Dichtel says:

====================
The goal of this serie is to add the support of proxy multicast, ie being able
to build a static multicast tree. In other words, it adds the support of (*,G)
mf[6]c entries.

v2: use INADDR_ANY instead of 0 for IPv4 addresses
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 202dc3fc 660b26dc
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#ifdef CONFIG_IP_MROUTE #ifdef CONFIG_IP_MROUTE
static inline int ip_mroute_opt(int opt) static inline int ip_mroute_opt(int opt)
{ {
return (opt >= MRT_BASE) && (opt <= MRT_BASE + 10); return (opt >= MRT_BASE) && (opt <= MRT_MAX);
} }
#else #else
static inline int ip_mroute_opt(int opt) static inline int ip_mroute_opt(int opt)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#ifdef CONFIG_IPV6_MROUTE #ifdef CONFIG_IPV6_MROUTE
static inline int ip6_mroute_opt(int opt) static inline int ip6_mroute_opt(int opt)
{ {
return (opt >= MRT6_BASE) && (opt <= MRT6_BASE + 10); return (opt >= MRT6_BASE) && (opt <= MRT6_MAX);
} }
#else #else
static inline int ip6_mroute_opt(int opt) static inline int ip6_mroute_opt(int opt)
......
...@@ -259,17 +259,10 @@ struct in6_flowlabel_req { ...@@ -259,17 +259,10 @@ struct in6_flowlabel_req {
/* /*
* Multicast Routing: * Multicast Routing:
* see include/linux/mroute6.h. * see include/uapi/linux/mroute6.h.
* *
* MRT6_INIT 200 * MRT6_BASE 200
* MRT6_DONE 201 * ...
* MRT6_ADD_MIF 202 * MRT6_MAX
* MRT6_DEL_MIF 203
* MRT6_ADD_MFC 204
* MRT6_DEL_MFC 205
* MRT6_VERSION 206
* MRT6_ASSERT 207
* MRT6_PIM 208
* (reserved) 209
*/ */
#endif /* _UAPI_LINUX_IN6_H */ #endif /* _UAPI_LINUX_IN6_H */
...@@ -26,6 +26,9 @@ ...@@ -26,6 +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_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,6 +26,9 @@ ...@@ -26,6 +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_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