Commit cc47bcdf authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Paolo Abeni
Browse files

rtnetlink: Factorise do_setlink() path from __rtnl_newlink().



__rtnl_newlink() got too long to maintain.

For example, netdev_master_upper_dev_get()->rtnl_link_ops is fetched even
when IFLA_INFO_SLAVE_DATA is not specified.

Let's factorise the single dev do_setlink() path to a separate function.

Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@amazon.com>
Reviewed-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent a5838cf9
Loading
Loading
Loading
Loading
+74 −68
Original line number Diff line number Diff line
@@ -3505,6 +3505,78 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname,
}
EXPORT_SYMBOL(rtnl_create_link);

struct rtnl_newlink_tbs {
	struct nlattr *tb[IFLA_MAX + 1];
	struct nlattr *linkinfo[IFLA_INFO_MAX + 1];
	struct nlattr *attr[RTNL_MAX_TYPE + 1];
	struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
};

static int rtnl_changelink(const struct sk_buff *skb, struct nlmsghdr *nlh,
			   const struct rtnl_link_ops *ops,
			   struct net_device *dev,
			   struct rtnl_newlink_tbs *tbs,
			   struct nlattr **data,
			   struct netlink_ext_ack *extack)
{
	struct nlattr ** const linkinfo = tbs->linkinfo;
	struct nlattr ** const tb = tbs->tb;
	int status = 0;
	int err;

	if (nlh->nlmsg_flags & NLM_F_EXCL)
		return -EEXIST;

	if (nlh->nlmsg_flags & NLM_F_REPLACE)
		return -EOPNOTSUPP;

	if (linkinfo[IFLA_INFO_DATA]) {
		if (!ops || ops != dev->rtnl_link_ops || !ops->changelink)
			return -EOPNOTSUPP;

		err = ops->changelink(dev, tb, data, extack);
		if (err < 0)
			return err;

		status |= DO_SETLINK_NOTIFY;
	}

	if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
		const struct rtnl_link_ops *m_ops = NULL;
		struct nlattr **slave_data = NULL;
		struct net_device *master_dev;

		master_dev = netdev_master_upper_dev_get(dev);
		if (master_dev)
			m_ops = master_dev->rtnl_link_ops;

		if (!m_ops || !m_ops->slave_changelink)
			return -EOPNOTSUPP;

		if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
			return -EINVAL;

		if (m_ops->slave_maxtype) {
			err = nla_parse_nested_deprecated(tbs->slave_attr,
							  m_ops->slave_maxtype,
							  linkinfo[IFLA_INFO_SLAVE_DATA],
							  m_ops->slave_policy, extack);
			if (err < 0)
				return err;

			slave_data = tbs->slave_attr;
		}

		err = m_ops->slave_changelink(master_dev, dev, tb, slave_data, extack);
		if (err < 0)
			return err;

		status |= DO_SETLINK_NOTIFY;
	}

	return do_setlink(skb, dev, nlmsg_data(nlh), extack, tb, status);
}

static int rtnl_group_changelink(const struct sk_buff *skb,
		struct net *net, int group,
		struct ifinfomsg *ifm,
@@ -3617,24 +3689,14 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
	goto out;
}

struct rtnl_newlink_tbs {
	struct nlattr *tb[IFLA_MAX + 1];
	struct nlattr *linkinfo[IFLA_INFO_MAX + 1];
	struct nlattr *attr[RTNL_MAX_TYPE + 1];
	struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
};

static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
			  struct rtnl_newlink_tbs *tbs,
			  struct netlink_ext_ack *extack)
{
	struct nlattr ** const linkinfo = tbs->linkinfo;
	struct nlattr ** const tb = tbs->tb;
	const struct rtnl_link_ops *m_ops;
	struct net_device *master_dev;
	struct net *net = sock_net(skb->sk);
	const struct rtnl_link_ops *ops;
	struct nlattr **slave_data;
	char kind[MODULE_NAME_LEN];
	struct net_device *dev;
	struct ifinfomsg *ifm;
@@ -3669,14 +3731,6 @@ static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
		dev = NULL;
	}

	master_dev = NULL;
	m_ops = NULL;
	if (dev) {
		master_dev = netdev_master_upper_dev_get(dev);
		if (master_dev)
			m_ops = master_dev->rtnl_link_ops;
	}

	if (tb[IFLA_LINKINFO]) {
		err = nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX,
						  tb[IFLA_LINKINFO],
@@ -3715,56 +3769,8 @@ static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
		}
	}

	slave_data = NULL;
	if (m_ops) {
		if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
			return -EINVAL;

		if (m_ops->slave_maxtype &&
		    linkinfo[IFLA_INFO_SLAVE_DATA]) {
			err = nla_parse_nested_deprecated(tbs->slave_attr,
							  m_ops->slave_maxtype,
							  linkinfo[IFLA_INFO_SLAVE_DATA],
							  m_ops->slave_policy,
							  extack);
			if (err < 0)
				return err;
			slave_data = tbs->slave_attr;
		}
	}

	if (dev) {
		int status = 0;

		if (nlh->nlmsg_flags & NLM_F_EXCL)
			return -EEXIST;
		if (nlh->nlmsg_flags & NLM_F_REPLACE)
			return -EOPNOTSUPP;

		if (linkinfo[IFLA_INFO_DATA]) {
			if (!ops || ops != dev->rtnl_link_ops ||
			    !ops->changelink)
				return -EOPNOTSUPP;

			err = ops->changelink(dev, tb, data, extack);
			if (err < 0)
				return err;
			status |= DO_SETLINK_NOTIFY;
		}

		if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
			if (!m_ops || !m_ops->slave_changelink)
				return -EOPNOTSUPP;

			err = m_ops->slave_changelink(master_dev, dev, tb,
						      slave_data, extack);
			if (err < 0)
				return err;
			status |= DO_SETLINK_NOTIFY;
		}

		return do_setlink(skb, dev, ifm, extack, tb, status);
	}
	if (dev)
		return rtnl_changelink(skb, nlh, ops, dev, tbs, data, extack);

	if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
		/* No dev found and NLM_F_CREATE not set. Requested dev does not exist,