Skip to content

Commit

Permalink
net: bridge: mdb: use mdb and port entries in notifications
Browse files Browse the repository at this point in the history
We have to use mdb and port entries when sending mdb notifications in
order to fill in all group attributes properly. Before this change we
would've used a fake br_mdb_entry struct to fill in only partial
information about the mdb. Now we can also reuse the mdb dump fill
function and thus have only a single central place which fills the mdb
attributes.

v3: add IPv6 support

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Nikolay Aleksandrov authored and Jakub Kicinski committed Sep 7, 2020
1 parent 79abc87 commit 81f1983
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 68 deletions.
146 changes: 85 additions & 61 deletions net/bridge/br_mdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,15 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)

static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
struct net_device *dev,
struct br_mdb_entry *entry, u32 pid,
u32 seq, int type, unsigned int flags)
struct net_bridge_mdb_entry *mp,
struct net_bridge_port_group *pg,
int type)
{
struct nlmsghdr *nlh;
struct br_port_msg *bpm;
struct nlattr *nest, *nest2;

nlh = nlmsg_put(skb, pid, seq, type, sizeof(*bpm), 0);
nlh = nlmsg_put(skb, 0, 0, type, sizeof(*bpm), 0);
if (!nlh)
return -EMSGSIZE;

Expand All @@ -366,7 +367,7 @@ static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
if (nest2 == NULL)
goto end;

if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(*entry), entry))
if (__mdb_fill_info(skb, mp, pg))
goto end;

nla_nest_end(skb, nest2);
Expand All @@ -381,10 +382,49 @@ static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
return -EMSGSIZE;
}

static inline size_t rtnl_mdb_nlmsg_size(void)
static size_t rtnl_mdb_nlmsg_size(struct net_bridge_port_group *pg)
{
return NLMSG_ALIGN(sizeof(struct br_port_msg))
+ nla_total_size(sizeof(struct br_mdb_entry));
size_t nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
nla_total_size(sizeof(struct br_mdb_entry)) +
nla_total_size(sizeof(u32));
struct net_bridge_group_src *ent;
size_t addr_size = 0;

if (!pg)
goto out;

switch (pg->addr.proto) {
case htons(ETH_P_IP):
if (pg->port->br->multicast_igmp_version == 2)
goto out;
addr_size = sizeof(__be32);
break;
#if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6):
if (pg->port->br->multicast_mld_version == 1)
goto out;
addr_size = sizeof(struct in6_addr);
break;
#endif
}

/* MDBA_MDB_EATTR_GROUP_MODE */
nlmsg_size += nla_total_size(sizeof(u8));

/* MDBA_MDB_EATTR_SRC_LIST nested attr */
if (!hlist_empty(&pg->src_list))
nlmsg_size += nla_total_size(0);

hlist_for_each_entry(ent, &pg->src_list, node) {
/* MDBA_MDB_SRCLIST_ENTRY nested attr +
* MDBA_MDB_SRCATTR_ADDRESS + MDBA_MDB_SRCATTR_TIMER
*/
nlmsg_size += nla_total_size(0) +
nla_total_size(addr_size) +
nla_total_size(sizeof(u32));
}
out:
return nlmsg_size;
}

struct br_mdb_complete_info {
Expand Down Expand Up @@ -422,21 +462,22 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)

static void br_mdb_switchdev_host_port(struct net_device *dev,
struct net_device *lower_dev,
struct br_mdb_entry *entry, int type)
struct net_bridge_mdb_entry *mp,
int type)
{
struct switchdev_obj_port_mdb mdb = {
.obj = {
.id = SWITCHDEV_OBJ_ID_HOST_MDB,
.flags = SWITCHDEV_F_DEFER,
},
.vid = entry->vid,
.vid = mp->addr.vid,
};

if (entry->addr.proto == htons(ETH_P_IP))
ip_eth_mc_map(entry->addr.u.ip4, mdb.addr);
if (mp->addr.proto == htons(ETH_P_IP))
ip_eth_mc_map(mp->addr.u.ip4, mdb.addr);
#if IS_ENABLED(CONFIG_IPV6)
else
ipv6_eth_mc_map(&entry->addr.u.ip6, mdb.addr);
ipv6_eth_mc_map(&mp->addr.u.ip6, mdb.addr);
#endif

mdb.obj.orig_dev = dev;
Expand All @@ -451,62 +492,65 @@ static void br_mdb_switchdev_host_port(struct net_device *dev,
}

static void br_mdb_switchdev_host(struct net_device *dev,
struct br_mdb_entry *entry, int type)
struct net_bridge_mdb_entry *mp, int type)
{
struct net_device *lower_dev;
struct list_head *iter;

netdev_for_each_lower_dev(dev, lower_dev, iter)
br_mdb_switchdev_host_port(dev, lower_dev, entry, type);
br_mdb_switchdev_host_port(dev, lower_dev, mp, type);
}

static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p,
struct br_mdb_entry *entry, int type)
void br_mdb_notify(struct net_device *dev,
struct net_bridge_mdb_entry *mp,
struct net_bridge_port_group *pg,
int type)
{
struct br_mdb_complete_info *complete_info;
struct switchdev_obj_port_mdb mdb = {
.obj = {
.id = SWITCHDEV_OBJ_ID_PORT_MDB,
.flags = SWITCHDEV_F_DEFER,
},
.vid = entry->vid,
.vid = mp->addr.vid,
};
struct net_device *port_dev;
struct net *net = dev_net(dev);
struct sk_buff *skb;
int err = -ENOBUFS;

port_dev = __dev_get_by_index(net, entry->ifindex);
if (entry->addr.proto == htons(ETH_P_IP))
ip_eth_mc_map(entry->addr.u.ip4, mdb.addr);
if (pg) {
if (mp->addr.proto == htons(ETH_P_IP))
ip_eth_mc_map(mp->addr.u.ip4, mdb.addr);
#if IS_ENABLED(CONFIG_IPV6)
else
ipv6_eth_mc_map(&entry->addr.u.ip6, mdb.addr);
else
ipv6_eth_mc_map(&mp->addr.u.ip6, mdb.addr);
#endif

mdb.obj.orig_dev = port_dev;
if (p && port_dev && type == RTM_NEWMDB) {
complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC);
if (complete_info) {
complete_info->port = p;
__mdb_entry_to_br_ip(entry, &complete_info->ip);
mdb.obj.orig_dev = pg->port->dev;
switch (type) {
case RTM_NEWMDB:
complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC);
if (!complete_info)
break;
complete_info->port = pg->port;
complete_info->ip = mp->addr;
mdb.obj.complete_priv = complete_info;
mdb.obj.complete = br_mdb_complete;
if (switchdev_port_obj_add(port_dev, &mdb.obj, NULL))
if (switchdev_port_obj_add(pg->port->dev, &mdb.obj, NULL))
kfree(complete_info);
break;
case RTM_DELMDB:
switchdev_port_obj_del(pg->port->dev, &mdb.obj);
break;
}
} else if (p && port_dev && type == RTM_DELMDB) {
switchdev_port_obj_del(port_dev, &mdb.obj);
} else {
br_mdb_switchdev_host(dev, mp, type);
}

if (!p)
br_mdb_switchdev_host(dev, entry, type);

skb = nlmsg_new(rtnl_mdb_nlmsg_size(), GFP_ATOMIC);
skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC);
if (!skb)
goto errout;

err = nlmsg_populate_mdb_fill(skb, dev, entry, 0, 0, type, NTF_SELF);
err = nlmsg_populate_mdb_fill(skb, dev, mp, pg, type);
if (err < 0) {
kfree_skb(skb);
goto errout;
Expand All @@ -518,26 +562,6 @@ static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p,
rtnl_set_sk_err(net, RTNLGRP_MDB, err);
}

void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
struct br_ip *group, int type, u8 flags)
{
struct br_mdb_entry entry;

memset(&entry, 0, sizeof(entry));
if (port)
entry.ifindex = port->dev->ifindex;
else
entry.ifindex = dev->ifindex;
entry.addr.proto = group->proto;
entry.addr.u.ip4 = group->u.ip4;
#if IS_ENABLED(CONFIG_IPV6)
entry.addr.u.ip6 = group->u.ip6;
#endif
entry.vid = group->vid;
__mdb_entry_fill_flags(&entry, flags);
__br_mdb_notify(dev, port, &entry, type);
}

static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
struct net_device *dev,
int ifindex, u32 pid,
Expand Down Expand Up @@ -706,7 +730,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
return -EEXIST;

br_multicast_host_join(mp, false);
__br_mdb_notify(br->dev, NULL, entry, RTM_NEWMDB);
br_mdb_notify(br->dev, mp, NULL, RTM_NEWMDB);

return 0;
}
Expand All @@ -727,7 +751,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
rcu_assign_pointer(*pp, p);
if (entry->state == MDB_TEMPORARY)
mod_timer(&p->timer, now + br->multicast_membership_interval);
__br_mdb_notify(br->dev, port, entry, RTM_NEWMDB);
br_mdb_notify(br->dev, mp, p, RTM_NEWMDB);

return 0;
}
Expand Down Expand Up @@ -831,7 +855,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) {
br_multicast_host_leave(mp, false);
err = 0;
__br_mdb_notify(br->dev, NULL, entry, RTM_DELMDB);
br_mdb_notify(br->dev, mp, NULL, RTM_DELMDB);
if (!mp->ports && netif_running(br->dev))
mod_timer(&mp->timer, jiffies);
goto unlock;
Expand Down
10 changes: 5 additions & 5 deletions net/bridge/br_multicast.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
del_timer(&pg->rexmit_timer);
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
br_multicast_del_group_src(ent);
br_mdb_notify(br->dev, pg->port, &pg->addr, RTM_DELMDB, pg->flags);
br_mdb_notify(br->dev, mp, pg, RTM_DELMDB);
kfree_rcu(pg, rcu);

if (!mp->ports && !mp->host_joined && netif_running(br->dev))
Expand Down Expand Up @@ -749,8 +749,7 @@ void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify)
if (!mp->host_joined) {
mp->host_joined = true;
if (notify)
br_mdb_notify(mp->br->dev, NULL, &mp->addr,
RTM_NEWMDB, 0);
br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB);
}
mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval);
}
Expand All @@ -762,7 +761,7 @@ void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)

mp->host_joined = false;
if (notify)
br_mdb_notify(mp->br->dev, NULL, &mp->addr, RTM_DELMDB, 0);
br_mdb_notify(mp->br->dev, mp, NULL, RTM_DELMDB);
}

static int br_multicast_add_group(struct net_bridge *br,
Expand Down Expand Up @@ -805,10 +804,11 @@ static int br_multicast_add_group(struct net_bridge *br,
if (unlikely(!p))
goto err;
rcu_assign_pointer(*pp, p);
br_mdb_notify(br->dev, port, group, RTM_NEWMDB, 0);
br_mdb_notify(br->dev, mp, p, RTM_NEWMDB);

found:
mod_timer(&p->timer, now + br->multicast_membership_interval);

out:
err = 0;

Expand Down
4 changes: 2 additions & 2 deletions net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -800,8 +800,8 @@ br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
u8 filter_mode);
int br_mdb_hash_init(struct net_bridge *br);
void br_mdb_hash_fini(struct net_bridge *br);
void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
struct br_ip *group, int type, u8 flags);
void br_mdb_notify(struct net_device *dev, struct net_bridge_mdb_entry *mp,
struct net_bridge_port_group *pg, int type);
void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
int type);
void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
Expand Down

0 comments on commit 81f1983

Please sign in to comment.