Merge branch 'bridge-mcast-preparations-for-evpn-extensions'
Ido Schimmel says: ==================== bridge: mcast: Preparations for EVPN extensions This patchset was split from [1] and includes non-functional changes aimed at making it easier to add additional netlink attributes later on. Future extensions are available here [2]. The idea behind these patches is to create an MDB configuration structure into which netlink messages are parsed into. The structure is then passed in the entry creation / deletion call chain instead of passing the netlink attributes themselves. The same pattern is used by other rtnetlink objects such as routes and nexthops. I initially tried to extend the current code, but it proved to be too difficult, which is why I decided to refactor it to the extensible and familiar pattern used by other rtnetlink objects. Tested using existing selftests and using a new selftest that will be submitted together with the planned extensions. [1] https://lore.kernel.org/netdev/20221018120420.561846-1-idosch@nvidia.com/ [2] https://github.com/idosch/linux/commits/submit/mdb_v1 ==================== Link: https://lore.kernel.org/r/20221206105809.363767-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Коммит
5955a948ac
|
@ -754,73 +754,6 @@ static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = {
|
|||
sizeof(struct in6_addr)),
|
||||
};
|
||||
|
||||
static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct net_device **pdev, struct br_mdb_entry **pentry,
|
||||
struct nlattr **mdb_attrs, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct br_mdb_entry *entry;
|
||||
struct br_port_msg *bpm;
|
||||
struct nlattr *tb[MDBA_SET_ENTRY_MAX+1];
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
|
||||
MDBA_SET_ENTRY_MAX, NULL, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
bpm = nlmsg_data(nlh);
|
||||
if (bpm->ifindex == 0) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev = __dev_get_by_index(net, bpm->ifindex);
|
||||
if (dev == NULL) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!netif_is_bridge_master(dev)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
*pdev = dev;
|
||||
|
||||
if (!tb[MDBA_SET_ENTRY]) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry = nla_data(tb[MDBA_SET_ENTRY]);
|
||||
if (!is_valid_mdb_entry(entry, extack))
|
||||
return -EINVAL;
|
||||
*pentry = entry;
|
||||
|
||||
if (tb[MDBA_SET_ENTRY_ATTRS]) {
|
||||
err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX,
|
||||
tb[MDBA_SET_ENTRY_ATTRS],
|
||||
br_mdbe_attrs_pol, extack);
|
||||
if (err)
|
||||
return err;
|
||||
if (mdb_attrs[MDBE_ATTR_SOURCE] &&
|
||||
!is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE],
|
||||
entry->addr.proto, extack))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
memset(mdb_attrs, 0,
|
||||
sizeof(struct nlattr *) * (MDBE_ATTR_MAX + 1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_bridge_mcast *
|
||||
__br_mdb_choose_context(struct net_bridge *br,
|
||||
const struct br_mdb_entry *entry,
|
||||
|
@ -853,44 +786,26 @@ out:
|
|||
return brmctx;
|
||||
}
|
||||
|
||||
static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
|
||||
struct br_mdb_entry *entry,
|
||||
struct nlattr **mdb_attrs,
|
||||
static int br_mdb_add_group(const struct br_mdb_config *cfg,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net_bridge_mdb_entry *mp, *star_mp;
|
||||
struct net_bridge_port_group __rcu **pp;
|
||||
struct br_mdb_entry *entry = cfg->entry;
|
||||
struct net_bridge_port *port = cfg->p;
|
||||
struct net_bridge *br = cfg->br;
|
||||
struct net_bridge_port_group *p;
|
||||
struct net_bridge_mcast *brmctx;
|
||||
struct br_ip group, star_group;
|
||||
struct br_ip group = cfg->group;
|
||||
unsigned long now = jiffies;
|
||||
unsigned char flags = 0;
|
||||
struct br_ip star_group;
|
||||
u8 filter_mode;
|
||||
|
||||
__mdb_entry_to_br_ip(entry, &group, mdb_attrs);
|
||||
|
||||
brmctx = __br_mdb_choose_context(br, entry, extack);
|
||||
if (!brmctx)
|
||||
return -EINVAL;
|
||||
|
||||
/* host join errors which can happen before creating the group */
|
||||
if (!port && !br_group_is_l2(&group)) {
|
||||
/* don't allow any flags for host-joined IP groups */
|
||||
if (entry->state) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!br_multicast_is_star_g(&group)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (br_group_is_l2(&group) && entry->state != MDB_PERMANENT) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mp = br_multicast_new_group(br, &group);
|
||||
if (IS_ERR(mp))
|
||||
return PTR_ERR(mp);
|
||||
|
@ -959,107 +874,197 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __br_mdb_add(struct net *net, struct net_bridge *br,
|
||||
struct net_bridge_port *p,
|
||||
struct br_mdb_entry *entry,
|
||||
struct nlattr **mdb_attrs,
|
||||
static int __br_mdb_add(const struct br_mdb_config *cfg,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_bh(&br->multicast_lock);
|
||||
ret = br_mdb_add_group(br, p, entry, mdb_attrs, extack);
|
||||
spin_unlock_bh(&br->multicast_lock);
|
||||
spin_lock_bh(&cfg->br->multicast_lock);
|
||||
ret = br_mdb_add_group(cfg, extack);
|
||||
spin_unlock_bh(&cfg->br->multicast_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
static int br_mdb_config_attrs_init(struct nlattr *set_attrs,
|
||||
struct br_mdb_config *cfg,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1];
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct net_bridge_vlan_group *vg;
|
||||
struct net_bridge_port *p = NULL;
|
||||
struct net_device *dev, *pdev;
|
||||
struct br_mdb_entry *entry;
|
||||
struct net_bridge_vlan *v;
|
||||
struct net_bridge *br;
|
||||
int err;
|
||||
|
||||
err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
|
||||
if (err < 0)
|
||||
err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX, set_attrs,
|
||||
br_mdbe_attrs_pol, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
br = netdev_priv(dev);
|
||||
if (mdb_attrs[MDBE_ATTR_SOURCE] &&
|
||||
!is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE],
|
||||
cfg->entry->addr.proto, extack))
|
||||
return -EINVAL;
|
||||
|
||||
if (!netif_running(br->dev)) {
|
||||
__mdb_entry_to_br_ip(cfg->entry, &cfg->group, mdb_attrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_mdb_config_init(struct net *net, const struct nlmsghdr *nlh,
|
||||
struct br_mdb_config *cfg,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nlattr *tb[MDBA_SET_ENTRY_MAX + 1];
|
||||
struct br_port_msg *bpm;
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
|
||||
MDBA_SET_ENTRY_MAX, NULL, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
|
||||
bpm = nlmsg_data(nlh);
|
||||
if (!bpm->ifindex) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev = __dev_get_by_index(net, bpm->ifindex);
|
||||
if (!dev) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!netif_is_bridge_master(dev)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
cfg->br = netdev_priv(dev);
|
||||
|
||||
if (!netif_running(cfg->br->dev)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Bridge device is not running");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
|
||||
if (!br_opt_get(cfg->br, BROPT_MULTICAST_ENABLED)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (entry->ifindex != br->dev->ifindex) {
|
||||
pdev = __dev_get_by_index(net, entry->ifindex);
|
||||
if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg->entry = nla_data(tb[MDBA_SET_ENTRY]);
|
||||
if (!is_valid_mdb_entry(cfg->entry, extack))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->entry->ifindex != cfg->br->dev->ifindex) {
|
||||
struct net_device *pdev;
|
||||
|
||||
pdev = __dev_get_by_index(net, cfg->entry->ifindex);
|
||||
if (!pdev) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Port net device doesn't exist");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
p = br_port_get_rtnl(pdev);
|
||||
if (!p) {
|
||||
cfg->p = br_port_get_rtnl(pdev);
|
||||
if (!cfg->p) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (p->br != br) {
|
||||
if (cfg->p->br != cfg->br) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (p->state == BR_STATE_DISABLED && entry->state != MDB_PERMANENT) {
|
||||
}
|
||||
|
||||
if (tb[MDBA_SET_ENTRY_ATTRS])
|
||||
return br_mdb_config_attrs_init(tb[MDBA_SET_ENTRY_ATTRS], cfg,
|
||||
extack);
|
||||
else
|
||||
__mdb_entry_to_br_ip(cfg->entry, &cfg->group, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct net_bridge_vlan_group *vg;
|
||||
struct net_bridge_vlan *v;
|
||||
struct br_mdb_config cfg;
|
||||
int err;
|
||||
|
||||
err = br_mdb_config_init(net, nlh, &cfg, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* host join errors which can happen before creating the group */
|
||||
if (!cfg.p && !br_group_is_l2(&cfg.group)) {
|
||||
/* don't allow any flags for host-joined IP groups */
|
||||
if (cfg.entry->state) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!br_multicast_is_star_g(&cfg.group)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (br_group_is_l2(&cfg.group) && cfg.entry->state != MDB_PERMANENT) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cfg.p) {
|
||||
if (cfg.p->state == BR_STATE_DISABLED && cfg.entry->state != MDB_PERMANENT) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent");
|
||||
return -EINVAL;
|
||||
}
|
||||
vg = nbp_vlan_group(p);
|
||||
vg = nbp_vlan_group(cfg.p);
|
||||
} else {
|
||||
vg = br_vlan_group(br);
|
||||
vg = br_vlan_group(cfg.br);
|
||||
}
|
||||
|
||||
/* If vlan filtering is enabled and VLAN is not specified
|
||||
* install mdb entry on all vlans configured on the port.
|
||||
*/
|
||||
if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
|
||||
if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) {
|
||||
list_for_each_entry(v, &vg->vlan_list, vlist) {
|
||||
entry->vid = v->vid;
|
||||
err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack);
|
||||
cfg.entry->vid = v->vid;
|
||||
cfg.group.vid = v->vid;
|
||||
err = __br_mdb_add(&cfg, extack);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack);
|
||||
err = __br_mdb_add(&cfg, extack);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry,
|
||||
struct nlattr **mdb_attrs)
|
||||
static int __br_mdb_del(const struct br_mdb_config *cfg)
|
||||
{
|
||||
struct br_mdb_entry *entry = cfg->entry;
|
||||
struct net_bridge *br = cfg->br;
|
||||
struct net_bridge_mdb_entry *mp;
|
||||
struct net_bridge_port_group *p;
|
||||
struct net_bridge_port_group __rcu **pp;
|
||||
struct br_ip ip;
|
||||
struct br_ip ip = cfg->group;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
|
||||
return -EINVAL;
|
||||
|
||||
__mdb_entry_to_br_ip(entry, &ip, mdb_attrs);
|
||||
|
||||
spin_lock_bh(&br->multicast_lock);
|
||||
mp = br_mdb_ip_get(br, &ip);
|
||||
if (!mp)
|
||||
|
@ -1094,51 +1099,32 @@ unlock:
|
|||
static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1];
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct net_bridge_vlan_group *vg;
|
||||
struct net_bridge_port *p = NULL;
|
||||
struct net_device *dev, *pdev;
|
||||
struct br_mdb_entry *entry;
|
||||
struct net_bridge_vlan *v;
|
||||
struct net_bridge *br;
|
||||
struct br_mdb_config cfg;
|
||||
int err;
|
||||
|
||||
err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
|
||||
if (err < 0)
|
||||
err = br_mdb_config_init(net, nlh, &cfg, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
br = netdev_priv(dev);
|
||||
|
||||
if (entry->ifindex != br->dev->ifindex) {
|
||||
pdev = __dev_get_by_index(net, entry->ifindex);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
p = br_port_get_rtnl(pdev);
|
||||
if (!p) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (p->br != br) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device");
|
||||
return -EINVAL;
|
||||
}
|
||||
vg = nbp_vlan_group(p);
|
||||
} else {
|
||||
vg = br_vlan_group(br);
|
||||
}
|
||||
if (cfg.p)
|
||||
vg = nbp_vlan_group(cfg.p);
|
||||
else
|
||||
vg = br_vlan_group(cfg.br);
|
||||
|
||||
/* If vlan filtering is enabled and VLAN is not specified
|
||||
* delete mdb entry on all vlans configured on the port.
|
||||
*/
|
||||
if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
|
||||
if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) {
|
||||
list_for_each_entry(v, &vg->vlan_list, vlist) {
|
||||
entry->vid = v->vid;
|
||||
err = __br_mdb_del(br, entry, mdb_attrs);
|
||||
cfg.entry->vid = v->vid;
|
||||
cfg.group.vid = v->vid;
|
||||
err = __br_mdb_del(&cfg);
|
||||
}
|
||||
} else {
|
||||
err = __br_mdb_del(br, entry, mdb_attrs);
|
||||
err = __br_mdb_del(&cfg);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
@ -1273,7 +1273,7 @@ br_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_i
|
|||
|
||||
struct net_bridge_port_group *br_multicast_new_port_group(
|
||||
struct net_bridge_port *port,
|
||||
struct br_ip *group,
|
||||
const struct br_ip *group,
|
||||
struct net_bridge_port_group __rcu *next,
|
||||
unsigned char flags,
|
||||
const unsigned char *src,
|
||||
|
|
|
@ -92,6 +92,13 @@ struct bridge_mcast_stats {
|
|||
struct br_mcast_stats mstats;
|
||||
struct u64_stats_sync syncp;
|
||||
};
|
||||
|
||||
struct br_mdb_config {
|
||||
struct net_bridge *br;
|
||||
struct net_bridge_port *p;
|
||||
struct br_mdb_entry *entry;
|
||||
struct br_ip group;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* net_bridge_mcast_port must be always defined due to forwarding stubs */
|
||||
|
@ -934,7 +941,8 @@ br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
|
|||
struct net_bridge_mdb_entry *
|
||||
br_multicast_new_group(struct net_bridge *br, struct br_ip *group);
|
||||
struct net_bridge_port_group *
|
||||
br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
|
||||
br_multicast_new_port_group(struct net_bridge_port *port,
|
||||
const struct br_ip *group,
|
||||
struct net_bridge_port_group __rcu *next,
|
||||
unsigned char flags, const unsigned char *src,
|
||||
u8 filter_mode, u8 rt_protocol);
|
||||
|
|
Загрузка…
Ссылка в новой задаче