]> Pileus Git - ~andy/linux/blobdiff - net/core/rtnetlink.c
Merge branch 'core-printk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / net / core / rtnetlink.c
index d7c4bb4b18208e894c78a4a858b0a9237dab1246..99d9e953fe3953adf96cc44cf85be380b6b3ff55 100644 (file)
 struct rtnl_link {
        rtnl_doit_func          doit;
        rtnl_dumpit_func        dumpit;
+       rtnl_calcit_func        calcit;
 };
 
 static DEFINE_MUTEX(rtnl_mutex);
+static u16 min_ifinfo_dump_size;
 
 void rtnl_lock(void)
 {
@@ -144,12 +146,28 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
        return tab ? tab[msgindex].dumpit : NULL;
 }
 
+static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex)
+{
+       struct rtnl_link *tab;
+
+       if (protocol <= RTNL_FAMILY_MAX)
+               tab = rtnl_msg_handlers[protocol];
+       else
+               tab = NULL;
+
+       if (tab == NULL || tab[msgindex].calcit == NULL)
+               tab = rtnl_msg_handlers[PF_UNSPEC];
+
+       return tab ? tab[msgindex].calcit : NULL;
+}
+
 /**
  * __rtnl_register - Register a rtnetlink message type
  * @protocol: Protocol family or PF_UNSPEC
  * @msgtype: rtnetlink message type
  * @doit: Function pointer called for each request message
  * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
+ * @calcit: Function pointer to calc size of dump message
  *
  * Registers the specified function pointers (at least one of them has
  * to be non-NULL) to be called whenever a request message for the
@@ -162,7 +180,8 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
  * Returns 0 on success or a negative error code.
  */
 int __rtnl_register(int protocol, int msgtype,
-                   rtnl_doit_func doit, rtnl_dumpit_func dumpit)
+                   rtnl_doit_func doit, rtnl_dumpit_func dumpit,
+                   rtnl_calcit_func calcit)
 {
        struct rtnl_link *tab;
        int msgindex;
@@ -185,6 +204,9 @@ int __rtnl_register(int protocol, int msgtype,
        if (dumpit)
                tab[msgindex].dumpit = dumpit;
 
+       if (calcit)
+               tab[msgindex].calcit = calcit;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(__rtnl_register);
@@ -199,9 +221,10 @@ EXPORT_SYMBOL_GPL(__rtnl_register);
  * of memory implies no sense in continuing.
  */
 void rtnl_register(int protocol, int msgtype,
-                  rtnl_doit_func doit, rtnl_dumpit_func dumpit)
+                  rtnl_doit_func doit, rtnl_dumpit_func dumpit,
+                  rtnl_calcit_func calcit)
 {
-       if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0)
+       if (__rtnl_register(protocol, msgtype, doit, dumpit, calcit) < 0)
                panic("Unable to register rtnetlink message handler, "
                      "protocol = %d, message type = %d\n",
                      protocol, msgtype);
@@ -850,6 +873,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
        struct nlattr *attr, *af_spec;
        struct rtnl_af_ops *af_ops;
 
+       ASSERT_RTNL();
        nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
        if (nlh == NULL)
                return -EMSGSIZE;
@@ -1007,10 +1031,13 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        s_h = cb->args[0];
        s_idx = cb->args[1];
 
+       rcu_read_lock();
+       cb->seq = net->dev_base_seq;
+
        for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
                idx = 0;
                head = &net->dev_index_head[h];
-               hlist_for_each_entry(dev, node, head, index_hlist) {
+               hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
                        if (idx < s_idx)
                                goto cont;
                        if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
@@ -1018,11 +1045,14 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
                                             cb->nlh->nlmsg_seq, 0,
                                             NLM_F_MULTI) <= 0)
                                goto out;
+
+                       nl_dump_check_consistent(cb, nlmsg_hdr(skb));
 cont:
                        idx++;
                }
        }
 out:
+       rcu_read_unlock();
        cb->args[1] = idx;
        cb->args[0] = h;
 
@@ -1043,6 +1073,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
        [IFLA_LINKMODE]         = { .type = NLA_U8 },
        [IFLA_LINKINFO]         = { .type = NLA_NESTED },
        [IFLA_NET_NS_PID]       = { .type = NLA_U32 },
+       [IFLA_NET_NS_FD]        = { .type = NLA_U32 },
        [IFLA_IFALIAS]          = { .type = NLA_STRING, .len = IFALIASZ-1 },
        [IFLA_VFINFO_LIST]      = {. type = NLA_NESTED },
        [IFLA_VF_PORTS]         = { .type = NLA_NESTED },
@@ -1091,6 +1122,8 @@ struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[])
         */
        if (tb[IFLA_NET_NS_PID])
                net = get_net_ns_by_pid(nla_get_u32(tb[IFLA_NET_NS_PID]));
+       else if (tb[IFLA_NET_NS_FD])
+               net = get_net_ns_by_fd(nla_get_u32(tb[IFLA_NET_NS_FD]));
        else
                net = get_net(src_net);
        return net;
@@ -1221,7 +1254,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
        int send_addr_notify = 0;
        int err;
 
-       if (tb[IFLA_NET_NS_PID]) {
+       if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD]) {
                struct net *net = rtnl_link_get_net(dev_net(dev), tb);
                if (IS_ERR(net)) {
                        err = PTR_ERR(net);
@@ -1499,6 +1532,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        char ifname[IFNAMSIZ];
        struct nlattr *tb[IFLA_MAX+1];
        int err;
+       LIST_HEAD(list_kill);
 
        err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
        if (err < 0)
@@ -1522,7 +1556,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        if (!ops)
                return -EOPNOTSUPP;
 
-       ops->dellink(dev, NULL);
+       ops->dellink(dev, &list_kill);
+       unregister_netdevice_many(&list_kill);
+       list_del(&list_kill);
        return 0;
 }
 
@@ -1570,12 +1606,6 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
        dev->rtnl_link_state = RTNL_LINK_INITIALIZING;
        dev->real_num_tx_queues = real_num_queues;
 
-       if (strchr(dev->name, '%')) {
-               err = dev_alloc_name(dev, dev->name);
-               if (err < 0)
-                       goto err_free;
-       }
-
        if (tb[IFLA_MTU])
                dev->mtu = nla_get_u32(tb[IFLA_MTU]);
        if (tb[IFLA_ADDRESS])
@@ -1595,8 +1625,6 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
 
        return dev;
 
-err_free:
-       free_netdev(dev);
 err:
        return ERR_PTR(err);
 }
@@ -1817,6 +1845,11 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        return err;
 }
 
+static u16 rtnl_calcit(struct sk_buff *skb)
+{
+       return min_ifinfo_dump_size;
+}
+
 static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
 {
        int idx;
@@ -1846,11 +1879,14 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
        struct net *net = dev_net(dev);
        struct sk_buff *skb;
        int err = -ENOBUFS;
+       size_t if_info_size;
 
-       skb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL);
+       skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL);
        if (skb == NULL)
                goto errout;
 
+       min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size);
+
        err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in if_nlmsg_size() */
@@ -1901,14 +1937,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
                struct sock *rtnl;
                rtnl_dumpit_func dumpit;
+               rtnl_calcit_func calcit;
+               u16 min_dump_alloc = 0;
 
                dumpit = rtnl_get_dumpit(family, type);
                if (dumpit == NULL)
                        return -EOPNOTSUPP;
+               calcit = rtnl_get_calcit(family, type);
+               if (calcit)
+                       min_dump_alloc = calcit(skb);
 
                __rtnl_unlock();
                rtnl = net->rtnl;
-               err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
+               err = netlink_dump_start(rtnl, skb, nlh, dumpit,
+                                        NULL, min_dump_alloc);
                rtnl_lock();
                return err;
        }
@@ -1963,6 +2005,8 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi
        case NETDEV_GOING_DOWN:
        case NETDEV_UNREGISTER:
        case NETDEV_UNREGISTER_BATCH:
+       case NETDEV_RELEASE:
+       case NETDEV_JOIN:
                break;
        default:
                rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
@@ -2016,12 +2060,13 @@ void __init rtnetlink_init(void)
        netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
        register_netdevice_notifier(&rtnetlink_dev_notifier);
 
-       rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo);
-       rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL);
-       rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL);
-       rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL);
+       rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink,
+                     rtnl_dump_ifinfo, rtnl_calcit);
+       rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL);
+       rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, NULL);
+       rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, NULL);
 
-       rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all);
-       rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all);
+       rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL);
+       rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL);
 }