diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1385de0..ff6e98c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1961,6 +1961,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) struct nlattr *tb[IFLA_MAX+1]; struct nlattr *linkinfo[IFLA_INFO_MAX+1]; unsigned char name_assign_type = NET_NAME_USER; + struct net *dest_net, *link_net = NULL; int err; #ifdef CONFIG_MODULES @@ -1976,11 +1977,26 @@ replay: ifname[0] = '\0'; ifm = nlmsg_data(nlh); + + dest_net = rtnl_link_get_net(net, tb); + if (IS_ERR(dest_net)) + return PTR_ERR(dest_net); + + if (tb[IFLA_LINK_NETNSID]) { + int id = nla_get_s32(tb[IFLA_LINK_NETNSID]); + + link_net = get_net_ns_by_id(dest_net, id); + if (!link_net) { + err = -EINVAL; + goto out; + } + } + if (ifm->ifi_index > 0) - dev = __dev_get_by_index(net, ifm->ifi_index); + dev = __dev_get_by_index(link_net ?: dest_net, ifm->ifi_index); else { if (ifname[0]) - dev = __dev_get_by_name(net, ifname); + dev = __dev_get_by_name(link_net ?: dest_net, ifname); else dev = NULL; } @@ -1993,13 +2009,13 @@ replay: err = validate_linkmsg(dev, tb); if (err < 0) - return err; + goto err; if (tb[IFLA_LINKINFO]) { err = nla_parse_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO], ifla_info_policy); if (err < 0) - return err; + goto err; } else memset(linkinfo, 0, sizeof(linkinfo)); @@ -2016,7 +2032,6 @@ replay: struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 1]; struct nlattr **data = NULL; struct nlattr **slave_data = NULL; - struct net *dest_net, *link_net = NULL; if (ops) { if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) { @@ -2024,13 +2039,13 @@ replay: linkinfo[IFLA_INFO_DATA], ops->policy); if (err < 0) - return err; + goto err; data = attr; } if (ops->validate) { err = ops->validate(tb, data); if (err < 0) - return err; + goto err; } } @@ -2042,59 +2057,71 @@ replay: linkinfo[IFLA_INFO_SLAVE_DATA], m_ops->slave_policy); if (err < 0) - return err; + goto err; slave_data = slave_attr; } if (m_ops->slave_validate) { err = m_ops->slave_validate(tb, slave_data); if (err < 0) - return err; + goto err; } } if (dev) { int status = 0; - if (nlh->nlmsg_flags & NLM_F_EXCL) - return -EEXIST; - if (nlh->nlmsg_flags & NLM_F_REPLACE) - return -EOPNOTSUPP; + if (nlh->nlmsg_flags & NLM_F_EXCL) { + err = -EEXIST; + goto err; + } + if (nlh->nlmsg_flags & NLM_F_REPLACE) { + err = -EOPNOTSUPP; + goto err; + } if (linkinfo[IFLA_INFO_DATA]) { if (!ops || ops != dev->rtnl_link_ops || - !ops->changelink) - return -EOPNOTSUPP; + !ops->changelink) { + err = -EOPNOTSUPP; + goto err; + } err = ops->changelink(dev, tb, data); if (err < 0) - return err; + goto err; status |= DO_SETLINK_NOTIFY; } if (linkinfo[IFLA_INFO_SLAVE_DATA]) { - if (!m_ops || !m_ops->slave_changelink) - return -EOPNOTSUPP; + if (!m_ops || !m_ops->slave_changelink) { + err = -EOPNOTSUPP; + goto err; + } err = m_ops->slave_changelink(master_dev, dev, tb, slave_data); if (err < 0) - return err; + goto err; status |= DO_SETLINK_NOTIFY; } - return do_setlink(skb, dev, ifm, tb, ifname, status); + err = do_setlink(skb, dev, ifm, tb, ifname, status); + goto err; } if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { + err = -ENODEV; if (ifm->ifi_index == 0 && tb[IFLA_GROUP]) - return rtnl_group_changelink(skb, net, + err = rtnl_group_changelink(skb, dest_net, nla_get_u32(tb[IFLA_GROUP]), ifm, tb); - return -ENODEV; + goto err; } - if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO]) - return -EOPNOTSUPP; + if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO]) { + err = -EOPNOTSUPP; + goto err; + } if (!ops) { #ifdef CONFIG_MODULES @@ -2103,40 +2130,33 @@ replay: request_module("rtnl-link-%s", kind); rtnl_lock(); ops = rtnl_link_ops_get(kind); - if (ops) + if (ops) { + if (link_net) + put_net(link_net); + put_net(dest_net); goto replay; + } } #endif - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + goto err; } - if (!ops->setup) - return -EOPNOTSUPP; + if (!ops->setup) { + err = -EOPNOTSUPP; + goto err; + } if (!ifname[0]) { snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind); name_assign_type = NET_NAME_ENUM; } - dest_net = rtnl_link_get_net(net, tb); - if (IS_ERR(dest_net)) - return PTR_ERR(dest_net); - - if (tb[IFLA_LINK_NETNSID]) { - int id = nla_get_s32(tb[IFLA_LINK_NETNSID]); - - link_net = get_net_ns_by_id(dest_net, id); - if (!link_net) { - err = -EINVAL; - goto out; - } - } - dev = rtnl_create_link(link_net ? : dest_net, ifname, name_assign_type, ops, tb); if (IS_ERR(dev)) { err = PTR_ERR(dev); - goto out; + goto err; } dev->ifindex = ifm->ifi_index; @@ -2151,13 +2171,13 @@ replay: /* If device is not registered at all, free it now */ if (dev->reg_state == NETREG_UNINITIALIZED) free_netdev(dev); - goto out; + goto err; } } else { err = register_netdevice(dev); if (err < 0) { free_netdev(dev); - goto out; + goto err; } } err = rtnl_configure_link(dev, ifm); @@ -2170,7 +2190,7 @@ replay: } else { unregister_netdevice(dev); } - goto out; + goto err; } if (link_net) { @@ -2178,12 +2198,14 @@ replay: if (err < 0) unregister_netdevice(dev); } -out: - if (link_net) - put_net(link_net); - put_net(dest_net); - return err; } + +err: + if (link_net) + put_net(link_net); +out: + put_net(dest_net); + return err; } static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh)