Commit cb453926 authored by Ido Schimmel's avatar Ido Schimmel Committed by Jakub Kicinski

bridge: mcast: Centralize netlink attribute parsing

Netlink attributes are currently passed deep in the MDB creation call
chain, making it difficult to add new attributes. In addition, some
validity checks are performed under the multicast lock although they can
be performed before it is ever acquired.

As a first step towards solving these issues, parse the RTM_{NEW,DEL}MDB
messages into a configuration structure, relieving other functions from
the need to handle raw netlink attributes.

Subsequent patches will convert the MDB code to use this configuration
structure.

This is consistent with how other rtnetlink objects are handled, such as
routes and nexthops.
Signed-off-by: default avatarIdo Schimmel <idosch@nvidia.com>
Acked-by: default avatarNikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 16dc16d9
...@@ -974,6 +974,116 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br, ...@@ -974,6 +974,116 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
return ret; return ret;
} }
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];
int err;
err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX, set_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],
cfg->entry->addr.proto, extack))
return -EINVAL;
__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(cfg->br, BROPT_MULTICAST_ENABLED)) {
NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled");
return -EINVAL;
}
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;
}
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 (cfg->p->br != cfg->br) {
NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device");
return -EINVAL;
}
}
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, static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
...@@ -984,9 +1094,14 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -984,9 +1094,14 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net_device *dev, *pdev; struct net_device *dev, *pdev;
struct br_mdb_entry *entry; struct br_mdb_entry *entry;
struct net_bridge_vlan *v; struct net_bridge_vlan *v;
struct br_mdb_config cfg;
struct net_bridge *br; struct net_bridge *br;
int err; int err;
err = br_mdb_config_init(net, nlh, &cfg, extack);
if (err)
return err;
err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
if (err < 0) if (err < 0)
return err; return err;
...@@ -1101,9 +1216,14 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1101,9 +1216,14 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net_device *dev, *pdev; struct net_device *dev, *pdev;
struct br_mdb_entry *entry; struct br_mdb_entry *entry;
struct net_bridge_vlan *v; struct net_bridge_vlan *v;
struct br_mdb_config cfg;
struct net_bridge *br; struct net_bridge *br;
int err; int err;
err = br_mdb_config_init(net, nlh, &cfg, extack);
if (err)
return err;
err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
if (err < 0) if (err < 0)
return err; return err;
......
...@@ -92,6 +92,13 @@ struct bridge_mcast_stats { ...@@ -92,6 +92,13 @@ struct bridge_mcast_stats {
struct br_mcast_stats mstats; struct br_mcast_stats mstats;
struct u64_stats_sync syncp; 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 #endif
/* net_bridge_mcast_port must be always defined due to forwarding stubs */ /* net_bridge_mcast_port must be always defined due to forwarding stubs */
......
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