|
@@ -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)
|
|
* 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)
|
|
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
|
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,
|
|
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)
|
|
|
|
|
|
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
|
|
*/
|
|
*/
|
|
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)
|
|
|
|
- ret = ipmr_mfc_delete(mrt, &mfc);
|
|
|
|
|
|
+ if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
|
|
|
|
+ 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,
|
|
{
|
|
{
|
|
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,
|
|
}
|
|
}
|
|
|
|
|
|
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,
|
|
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,
|
|
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)
|
|
|
|
|
|
/* 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,
|
|
|
|
|
|
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;
|