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: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
79abc87505
commit
81f1983852
|
@ -344,14 +344,15 @@ out:
|
|||
|
||||
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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -381,10 +382,49 @@ cancel:
|
|||
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 {
|
||||
|
@ -422,21 +462,22 @@ err:
|
|||
|
||||
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;
|
||||
|
@ -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,
|
||||
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 = {
|
||||
|
@ -469,44 +512,45 @@ static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p,
|
|||
.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;
|
||||
|
@ -518,26 +562,6 @@ errout:
|
|||
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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue