net: bridge: mcast: support for IGMPV3/MLDv2 CHANGE_TO_INCLUDE/EXCLUDE report
In order to process IGMPV3/MLDv2 CHANGE_TO_INCLUDE/EXCLUDE report types we need new helpers which allow us to mark entries based on their timer state and to query only marked entries. v3: add IPv6/MLDv2 support, fix other_query checks v2: directly do flag bit operations Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
e6231bca6a
commit
5bf1e00b68
|
@ -1244,6 +1244,86 @@ static int __grp_src_delete_marked(struct net_bridge_port_group *pg)
|
|||
return deleted;
|
||||
}
|
||||
|
||||
static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg)
|
||||
{
|
||||
struct bridge_mcast_other_query *other_query = NULL;
|
||||
struct net_bridge *br = pg->port->br;
|
||||
u32 lmqc = br->multicast_last_member_count;
|
||||
unsigned long lmqt, lmi, now = jiffies;
|
||||
struct net_bridge_group_src *ent;
|
||||
|
||||
if (!netif_running(br->dev) ||
|
||||
!br_opt_get(br, BROPT_MULTICAST_ENABLED))
|
||||
return;
|
||||
|
||||
if (pg->addr.proto == htons(ETH_P_IP))
|
||||
other_query = &br->ip4_other_query;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
else
|
||||
other_query = &br->ip6_other_query;
|
||||
#endif
|
||||
|
||||
lmqt = now + br_multicast_lmqt(br);
|
||||
hlist_for_each_entry(ent, &pg->src_list, node) {
|
||||
if (ent->flags & BR_SGRP_F_SEND) {
|
||||
ent->flags &= ~BR_SGRP_F_SEND;
|
||||
if (ent->timer.expires > lmqt) {
|
||||
if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
|
||||
other_query &&
|
||||
!timer_pending(&other_query->timer))
|
||||
ent->src_query_rexmit_cnt = lmqc;
|
||||
mod_timer(&ent->timer, lmqt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!br_opt_get(br, BROPT_MULTICAST_QUERIER) ||
|
||||
!other_query || timer_pending(&other_query->timer))
|
||||
return;
|
||||
|
||||
__br_multicast_send_query(br, pg->port, pg, &pg->addr,
|
||||
&pg->addr, true, 1, NULL);
|
||||
|
||||
lmi = now + br->multicast_last_member_interval;
|
||||
if (!timer_pending(&pg->rexmit_timer) ||
|
||||
time_after(pg->rexmit_timer.expires, lmi))
|
||||
mod_timer(&pg->rexmit_timer, lmi);
|
||||
}
|
||||
|
||||
static void __grp_send_query_and_rexmit(struct net_bridge_port_group *pg)
|
||||
{
|
||||
struct bridge_mcast_other_query *other_query = NULL;
|
||||
struct net_bridge *br = pg->port->br;
|
||||
unsigned long now = jiffies, lmi;
|
||||
|
||||
if (!netif_running(br->dev) ||
|
||||
!br_opt_get(br, BROPT_MULTICAST_ENABLED))
|
||||
return;
|
||||
|
||||
if (pg->addr.proto == htons(ETH_P_IP))
|
||||
other_query = &br->ip4_other_query;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
else
|
||||
other_query = &br->ip6_other_query;
|
||||
#endif
|
||||
|
||||
if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
|
||||
other_query && !timer_pending(&other_query->timer)) {
|
||||
lmi = now + br->multicast_last_member_interval;
|
||||
pg->grp_query_rexmit_cnt = br->multicast_last_member_count - 1;
|
||||
__br_multicast_send_query(br, pg->port, pg, &pg->addr,
|
||||
&pg->addr, false, 0, NULL);
|
||||
if (!timer_pending(&pg->rexmit_timer) ||
|
||||
time_after(pg->rexmit_timer.expires, lmi))
|
||||
mod_timer(&pg->rexmit_timer, lmi);
|
||||
}
|
||||
|
||||
if (pg->filter_mode == MCAST_EXCLUDE &&
|
||||
(!timer_pending(&pg->timer) ||
|
||||
time_after(pg->timer.expires, now + br_multicast_lmqt(br))))
|
||||
mod_timer(&pg->timer, now + br_multicast_lmqt(br));
|
||||
}
|
||||
|
||||
/* State Msg type New state Actions
|
||||
* INCLUDE (A) IS_IN (B) INCLUDE (A+B) (B)=GMI
|
||||
* INCLUDE (A) ALLOW (B) INCLUDE (A+B) (B)=GMI
|
||||
|
@ -1373,6 +1453,216 @@ static bool br_multicast_isexc(struct net_bridge_port_group *pg,
|
|||
return changed;
|
||||
}
|
||||
|
||||
/* State Msg type New state Actions
|
||||
* INCLUDE (A) TO_IN (B) INCLUDE (A+B) (B)=GMI
|
||||
* Send Q(G,A-B)
|
||||
*/
|
||||
static bool __grp_src_toin_incl(struct net_bridge_port_group *pg,
|
||||
void *srcs, u32 nsrcs, size_t src_size)
|
||||
{
|
||||
struct net_bridge *br = pg->port->br;
|
||||
u32 src_idx, to_send = pg->src_ents;
|
||||
struct net_bridge_group_src *ent;
|
||||
unsigned long now = jiffies;
|
||||
bool changed = false;
|
||||
struct br_ip src_ip;
|
||||
|
||||
hlist_for_each_entry(ent, &pg->src_list, node)
|
||||
ent->flags |= BR_SGRP_F_SEND;
|
||||
|
||||
memset(&src_ip, 0, sizeof(src_ip));
|
||||
src_ip.proto = pg->addr.proto;
|
||||
for (src_idx = 0; src_idx < nsrcs; src_idx++) {
|
||||
memcpy(&src_ip.u, srcs, src_size);
|
||||
ent = br_multicast_find_group_src(pg, &src_ip);
|
||||
if (ent) {
|
||||
ent->flags &= ~BR_SGRP_F_SEND;
|
||||
to_send--;
|
||||
} else {
|
||||
ent = br_multicast_new_group_src(pg, &src_ip);
|
||||
if (ent)
|
||||
changed = true;
|
||||
}
|
||||
if (ent)
|
||||
mod_timer(&ent->timer, now + br_multicast_gmi(br));
|
||||
srcs += src_size;
|
||||
}
|
||||
|
||||
if (to_send)
|
||||
__grp_src_query_marked_and_rexmit(pg);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* State Msg type New state Actions
|
||||
* EXCLUDE (X,Y) TO_IN (A) EXCLUDE (X+A,Y-A) (A)=GMI
|
||||
* Send Q(G,X-A)
|
||||
* Send Q(G)
|
||||
*/
|
||||
static bool __grp_src_toin_excl(struct net_bridge_port_group *pg,
|
||||
void *srcs, u32 nsrcs, size_t src_size)
|
||||
{
|
||||
struct net_bridge *br = pg->port->br;
|
||||
u32 src_idx, to_send = pg->src_ents;
|
||||
struct net_bridge_group_src *ent;
|
||||
unsigned long now = jiffies;
|
||||
bool changed = false;
|
||||
struct br_ip src_ip;
|
||||
|
||||
hlist_for_each_entry(ent, &pg->src_list, node)
|
||||
if (timer_pending(&ent->timer))
|
||||
ent->flags |= BR_SGRP_F_SEND;
|
||||
|
||||
memset(&src_ip, 0, sizeof(src_ip));
|
||||
src_ip.proto = pg->addr.proto;
|
||||
for (src_idx = 0; src_idx < nsrcs; src_idx++) {
|
||||
memcpy(&src_ip.u, srcs, src_size);
|
||||
ent = br_multicast_find_group_src(pg, &src_ip);
|
||||
if (ent) {
|
||||
if (timer_pending(&ent->timer)) {
|
||||
ent->flags &= ~BR_SGRP_F_SEND;
|
||||
to_send--;
|
||||
}
|
||||
} else {
|
||||
ent = br_multicast_new_group_src(pg, &src_ip);
|
||||
if (ent)
|
||||
changed = true;
|
||||
}
|
||||
if (ent)
|
||||
mod_timer(&ent->timer, now + br_multicast_gmi(br));
|
||||
srcs += src_size;
|
||||
}
|
||||
|
||||
if (to_send)
|
||||
__grp_src_query_marked_and_rexmit(pg);
|
||||
|
||||
__grp_send_query_and_rexmit(pg);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool br_multicast_toin(struct net_bridge_port_group *pg,
|
||||
void *srcs, u32 nsrcs, size_t src_size)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
switch (pg->filter_mode) {
|
||||
case MCAST_INCLUDE:
|
||||
changed = __grp_src_toin_incl(pg, srcs, nsrcs, src_size);
|
||||
break;
|
||||
case MCAST_EXCLUDE:
|
||||
changed = __grp_src_toin_excl(pg, srcs, nsrcs, src_size);
|
||||
break;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* State Msg type New state Actions
|
||||
* INCLUDE (A) TO_EX (B) EXCLUDE (A*B,B-A) (B-A)=0
|
||||
* Delete (A-B)
|
||||
* Send Q(G,A*B)
|
||||
* Group Timer=GMI
|
||||
*/
|
||||
static void __grp_src_toex_incl(struct net_bridge_port_group *pg,
|
||||
void *srcs, u32 nsrcs, size_t src_size)
|
||||
{
|
||||
struct net_bridge_group_src *ent;
|
||||
u32 src_idx, to_send = 0;
|
||||
struct br_ip src_ip;
|
||||
|
||||
hlist_for_each_entry(ent, &pg->src_list, node)
|
||||
ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
|
||||
|
||||
memset(&src_ip, 0, sizeof(src_ip));
|
||||
src_ip.proto = pg->addr.proto;
|
||||
for (src_idx = 0; src_idx < nsrcs; src_idx++) {
|
||||
memcpy(&src_ip.u, srcs, src_size);
|
||||
ent = br_multicast_find_group_src(pg, &src_ip);
|
||||
if (ent) {
|
||||
ent->flags = (ent->flags & ~BR_SGRP_F_DELETE) |
|
||||
BR_SGRP_F_SEND;
|
||||
to_send++;
|
||||
} else {
|
||||
br_multicast_new_group_src(pg, &src_ip);
|
||||
}
|
||||
srcs += src_size;
|
||||
}
|
||||
|
||||
__grp_src_delete_marked(pg);
|
||||
if (to_send)
|
||||
__grp_src_query_marked_and_rexmit(pg);
|
||||
}
|
||||
|
||||
/* State Msg type New state Actions
|
||||
* EXCLUDE (X,Y) TO_EX (A) EXCLUDE (A-Y,Y*A) (A-X-Y)=Group Timer
|
||||
* Delete (X-A)
|
||||
* Delete (Y-A)
|
||||
* Send Q(G,A-Y)
|
||||
* Group Timer=GMI
|
||||
*/
|
||||
static bool __grp_src_toex_excl(struct net_bridge_port_group *pg,
|
||||
void *srcs, u32 nsrcs, size_t src_size)
|
||||
{
|
||||
struct net_bridge_group_src *ent;
|
||||
u32 src_idx, to_send = 0;
|
||||
bool changed = false;
|
||||
struct br_ip src_ip;
|
||||
|
||||
hlist_for_each_entry(ent, &pg->src_list, node)
|
||||
ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
|
||||
|
||||
memset(&src_ip, 0, sizeof(src_ip));
|
||||
src_ip.proto = pg->addr.proto;
|
||||
for (src_idx = 0; src_idx < nsrcs; src_idx++) {
|
||||
memcpy(&src_ip.u, srcs, src_size);
|
||||
ent = br_multicast_find_group_src(pg, &src_ip);
|
||||
if (ent) {
|
||||
ent->flags &= ~BR_SGRP_F_DELETE;
|
||||
} else {
|
||||
ent = br_multicast_new_group_src(pg, &src_ip);
|
||||
if (ent) {
|
||||
mod_timer(&ent->timer, pg->timer.expires);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (ent && timer_pending(&ent->timer)) {
|
||||
ent->flags |= BR_SGRP_F_SEND;
|
||||
to_send++;
|
||||
}
|
||||
srcs += src_size;
|
||||
}
|
||||
|
||||
if (__grp_src_delete_marked(pg))
|
||||
changed = true;
|
||||
if (to_send)
|
||||
__grp_src_query_marked_and_rexmit(pg);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool br_multicast_toex(struct net_bridge_port_group *pg,
|
||||
void *srcs, u32 nsrcs, size_t src_size)
|
||||
{
|
||||
struct net_bridge *br = pg->port->br;
|
||||
bool changed = false;
|
||||
|
||||
switch (pg->filter_mode) {
|
||||
case MCAST_INCLUDE:
|
||||
__grp_src_toex_incl(pg, srcs, nsrcs, src_size);
|
||||
changed = true;
|
||||
break;
|
||||
case MCAST_EXCLUDE:
|
||||
__grp_src_toex_excl(pg, srcs, nsrcs, src_size);
|
||||
break;
|
||||
}
|
||||
|
||||
pg->filter_mode = MCAST_EXCLUDE;
|
||||
mod_timer(&pg->timer, jiffies + br_multicast_gmi(br));
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static struct net_bridge_port_group *
|
||||
br_multicast_find_port(struct net_bridge_mdb_entry *mp,
|
||||
struct net_bridge_port *p,
|
||||
|
@ -1478,6 +1768,14 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
|
|||
changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
|
||||
sizeof(__be32));
|
||||
break;
|
||||
case IGMPV3_CHANGE_TO_INCLUDE:
|
||||
changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
|
||||
sizeof(__be32));
|
||||
break;
|
||||
case IGMPV3_CHANGE_TO_EXCLUDE:
|
||||
changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
|
||||
sizeof(__be32));
|
||||
break;
|
||||
}
|
||||
if (changed)
|
||||
br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
|
||||
|
@ -1592,6 +1890,14 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
|
|||
changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
|
||||
sizeof(struct in6_addr));
|
||||
break;
|
||||
case MLD2_CHANGE_TO_INCLUDE:
|
||||
changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
|
||||
sizeof(struct in6_addr));
|
||||
break;
|
||||
case MLD2_CHANGE_TO_EXCLUDE:
|
||||
changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
|
||||
sizeof(struct in6_addr));
|
||||
break;
|
||||
}
|
||||
if (changed)
|
||||
br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
|
||||
|
|
Loading…
Reference in a new issue