Commit 81f19838 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by Jakub Kicinski

net: bridge: mdb: use mdb and port entries in notifications

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: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 79abc875
...@@ -344,14 +344,15 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -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, static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
struct br_mdb_entry *entry, u32 pid, struct net_bridge_mdb_entry *mp,
u32 seq, int type, unsigned int flags) struct net_bridge_port_group *pg,
int type)
{ {
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
struct br_port_msg *bpm; struct br_port_msg *bpm;
struct nlattr *nest, *nest2; 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) if (!nlh)
return -EMSGSIZE; return -EMSGSIZE;
...@@ -366,7 +367,7 @@ static int nlmsg_populate_mdb_fill(struct sk_buff *skb, ...@@ -366,7 +367,7 @@ static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
if (nest2 == NULL) if (nest2 == NULL)
goto end; goto end;
if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(*entry), entry)) if (__mdb_fill_info(skb, mp, pg))
goto end; goto end;
nla_nest_end(skb, nest2); nla_nest_end(skb, nest2);
...@@ -381,10 +382,49 @@ static int nlmsg_populate_mdb_fill(struct sk_buff *skb, ...@@ -381,10 +382,49 @@ static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
return -EMSGSIZE; 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)) size_t nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
+ nla_total_size(sizeof(struct br_mdb_entry)); 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 { struct br_mdb_complete_info {
...@@ -422,21 +462,22 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv) ...@@ -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, static void br_mdb_switchdev_host_port(struct net_device *dev,
struct net_device *lower_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 = { struct switchdev_obj_port_mdb mdb = {
.obj = { .obj = {
.id = SWITCHDEV_OBJ_ID_HOST_MDB, .id = SWITCHDEV_OBJ_ID_HOST_MDB,
.flags = SWITCHDEV_F_DEFER, .flags = SWITCHDEV_F_DEFER,
}, },
.vid = entry->vid, .vid = mp->addr.vid,
}; };
if (entry->addr.proto == htons(ETH_P_IP)) if (mp->addr.proto == htons(ETH_P_IP))
ip_eth_mc_map(entry->addr.u.ip4, mdb.addr); ip_eth_mc_map(mp->addr.u.ip4, mdb.addr);
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else else
ipv6_eth_mc_map(&entry->addr.u.ip6, mdb.addr); ipv6_eth_mc_map(&mp->addr.u.ip6, mdb.addr);
#endif #endif
mdb.obj.orig_dev = dev; mdb.obj.orig_dev = dev;
...@@ -451,17 +492,19 @@ static void br_mdb_switchdev_host_port(struct net_device *dev, ...@@ -451,17 +492,19 @@ static void br_mdb_switchdev_host_port(struct net_device *dev,
} }
static void br_mdb_switchdev_host(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 net_device *lower_dev;
struct list_head *iter; struct list_head *iter;
netdev_for_each_lower_dev(dev, lower_dev, 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, void br_mdb_notify(struct net_device *dev,
struct br_mdb_entry *entry, int type) struct net_bridge_mdb_entry *mp,
struct net_bridge_port_group *pg,
int type)
{ {
struct br_mdb_complete_info *complete_info; struct br_mdb_complete_info *complete_info;
struct switchdev_obj_port_mdb mdb = { struct switchdev_obj_port_mdb mdb = {
...@@ -469,44 +512,45 @@ static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p, ...@@ -469,44 +512,45 @@ static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p,
.id = SWITCHDEV_OBJ_ID_PORT_MDB, .id = SWITCHDEV_OBJ_ID_PORT_MDB,
.flags = SWITCHDEV_F_DEFER, .flags = SWITCHDEV_F_DEFER,
}, },
.vid = entry->vid, .vid = mp->addr.vid,
}; };
struct net_device *port_dev;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
struct sk_buff *skb; struct sk_buff *skb;
int err = -ENOBUFS; int err = -ENOBUFS;
port_dev = __dev_get_by_index(net, entry->ifindex); if (pg) {
if (entry->addr.proto == htons(ETH_P_IP)) if (mp->addr.proto == htons(ETH_P_IP))
ip_eth_mc_map(entry->addr.u.ip4, mdb.addr); ip_eth_mc_map(mp->addr.u.ip4, mdb.addr);
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else else
ipv6_eth_mc_map(&entry->addr.u.ip6, mdb.addr); ipv6_eth_mc_map(&mp->addr.u.ip6, mdb.addr);
#endif #endif
mdb.obj.orig_dev = pg->port->dev;
mdb.obj.orig_dev = port_dev; switch (type) {
if (p && port_dev && type == RTM_NEWMDB) { case RTM_NEWMDB:
complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC); complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC);
if (complete_info) { if (!complete_info)
complete_info->port = p; break;
__mdb_entry_to_br_ip(entry, &complete_info->ip); complete_info->port = pg->port;
complete_info->ip = mp->addr;
mdb.obj.complete_priv = complete_info; mdb.obj.complete_priv = complete_info;
mdb.obj.complete = br_mdb_complete; 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); 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) { } else {
switchdev_port_obj_del(port_dev, &mdb.obj); br_mdb_switchdev_host(dev, mp, type);
} }
if (!p) skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC);
br_mdb_switchdev_host(dev, entry, type);
skb = nlmsg_new(rtnl_mdb_nlmsg_size(), GFP_ATOMIC);
if (!skb) if (!skb)
goto errout; 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) { if (err < 0) {
kfree_skb(skb); kfree_skb(skb);
goto errout; goto errout;
...@@ -518,26 +562,6 @@ static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p, ...@@ -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); 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, static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
int ifindex, u32 pid, int ifindex, u32 pid,
...@@ -706,7 +730,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, ...@@ -706,7 +730,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
return -EEXIST; return -EEXIST;
br_multicast_host_join(mp, false); 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; return 0;
} }
...@@ -727,7 +751,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, ...@@ -727,7 +751,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
rcu_assign_pointer(*pp, p); rcu_assign_pointer(*pp, p);
if (entry->state == MDB_TEMPORARY) if (entry->state == MDB_TEMPORARY)
mod_timer(&p->timer, now + br->multicast_membership_interval); 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; return 0;
} }
...@@ -831,7 +855,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry) ...@@ -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) { if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) {
br_multicast_host_leave(mp, false); br_multicast_host_leave(mp, false);
err = 0; 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)) if (!mp->ports && netif_running(br->dev))
mod_timer(&mp->timer, jiffies); mod_timer(&mp->timer, jiffies);
goto unlock; goto unlock;
......
...@@ -188,7 +188,7 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp, ...@@ -188,7 +188,7 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
del_timer(&pg->rexmit_timer); del_timer(&pg->rexmit_timer);
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node) hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
br_multicast_del_group_src(ent); 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); kfree_rcu(pg, rcu);
if (!mp->ports && !mp->host_joined && netif_running(br->dev)) if (!mp->ports && !mp->host_joined && netif_running(br->dev))
...@@ -749,8 +749,7 @@ void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify) ...@@ -749,8 +749,7 @@ void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify)
if (!mp->host_joined) { if (!mp->host_joined) {
mp->host_joined = true; mp->host_joined = true;
if (notify) if (notify)
br_mdb_notify(mp->br->dev, NULL, &mp->addr, br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB);
RTM_NEWMDB, 0);
} }
mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval); mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval);
} }
...@@ -762,7 +761,7 @@ void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify) ...@@ -762,7 +761,7 @@ void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)
mp->host_joined = false; mp->host_joined = false;
if (notify) 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, static int br_multicast_add_group(struct net_bridge *br,
...@@ -805,10 +804,11 @@ static int br_multicast_add_group(struct net_bridge *br, ...@@ -805,10 +804,11 @@ static int br_multicast_add_group(struct net_bridge *br,
if (unlikely(!p)) if (unlikely(!p))
goto err; goto err;
rcu_assign_pointer(*pp, p); 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: found:
mod_timer(&p->timer, now + br->multicast_membership_interval); mod_timer(&p->timer, now + br->multicast_membership_interval);
out: out:
err = 0; err = 0;
......
...@@ -800,8 +800,8 @@ br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group, ...@@ -800,8 +800,8 @@ br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
u8 filter_mode); u8 filter_mode);
int br_mdb_hash_init(struct net_bridge *br); int br_mdb_hash_init(struct net_bridge *br);
void br_mdb_hash_fini(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, void br_mdb_notify(struct net_device *dev, struct net_bridge_mdb_entry *mp,
struct br_ip *group, int type, u8 flags); struct net_bridge_port_group *pg, int type);
void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
int type); int type);
void br_multicast_del_pg(struct net_bridge_mdb_entry *mp, void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
......
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