Skip to content

Commit

Permalink
mcast: add multicast proxy support (IPv4 and IPv6)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Nicolas Dichtel authored and David S. Miller committed Jan 21, 2013
1 parent bbb923a commit 660b26d
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 28 deletions.
4 changes: 3 additions & 1 deletion include/uapi/linux/mroute.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion include/uapi/linux/mroute6.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
119 changes: 106 additions & 13 deletions net/ipv4/ipmr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
/*
Expand Down Expand Up @@ -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...
Expand All @@ -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,
Expand All @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 660b26d

Please sign in to comment.