bridge: fdb dumping takes a filter device
[deliverable/linux.git] / net / core / rtnetlink.c
index 2d8d8fcfa060c51e2f6cbe240aff3d01b3f964cc..90a906e7ac264caadc5cd67d67d36746d7808099 100644 (file)
@@ -299,7 +299,12 @@ int __rtnl_link_register(struct rtnl_link_ops *ops)
        if (rtnl_link_ops_get(ops->kind))
                return -EEXIST;
 
-       if (!ops->dellink)
+       /* The check for setup is here because if ops
+        * does not have that filled up, it is not possible
+        * to use the ops for creating device. So do not
+        * fill up dellink as well. That disables rtnl_dellink.
+        */
+       if (ops->setup && !ops->dellink)
                ops->dellink = unregister_netdevice_queue;
 
        list_add_tail(&ops->list, &link_ops);
@@ -798,8 +803,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
                size += num_vfs *
                        (nla_total_size(sizeof(struct ifla_vf_mac)) +
                         nla_total_size(sizeof(struct ifla_vf_vlan)) +
-                        nla_total_size(sizeof(struct ifla_vf_tx_rate)) +
-                        nla_total_size(sizeof(struct ifla_vf_spoofchk)));
+                        nla_total_size(sizeof(struct ifla_vf_spoofchk)) +
+                        nla_total_size(sizeof(struct ifla_vf_rate)));
                return size;
        } else
                return 0;
@@ -1065,6 +1070,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        struct ifla_vf_info ivi;
                        struct ifla_vf_mac vf_mac;
                        struct ifla_vf_vlan vf_vlan;
+                       struct ifla_vf_rate vf_rate;
                        struct ifla_vf_tx_rate vf_tx_rate;
                        struct ifla_vf_spoofchk vf_spoofchk;
                        struct ifla_vf_link_state vf_linkstate;
@@ -1085,6 +1091,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                                break;
                        vf_mac.vf =
                                vf_vlan.vf =
+                               vf_rate.vf =
                                vf_tx_rate.vf =
                                vf_spoofchk.vf =
                                vf_linkstate.vf = ivi.vf;
@@ -1092,7 +1099,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac));
                        vf_vlan.vlan = ivi.vlan;
                        vf_vlan.qos = ivi.qos;
-                       vf_tx_rate.rate = ivi.tx_rate;
+                       vf_tx_rate.rate = ivi.max_tx_rate;
+                       vf_rate.min_tx_rate = ivi.min_tx_rate;
+                       vf_rate.max_tx_rate = ivi.max_tx_rate;
                        vf_spoofchk.setting = ivi.spoofchk;
                        vf_linkstate.link_state = ivi.linkstate;
                        vf = nla_nest_start(skb, IFLA_VF_INFO);
@@ -1102,6 +1111,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        }
                        if (nla_put(skb, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac) ||
                            nla_put(skb, IFLA_VF_VLAN, sizeof(vf_vlan), &vf_vlan) ||
+                           nla_put(skb, IFLA_VF_RATE, sizeof(vf_rate),
+                                   &vf_rate) ||
                            nla_put(skb, IFLA_VF_TX_RATE, sizeof(vf_tx_rate),
                                    &vf_tx_rate) ||
                            nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk),
@@ -1208,6 +1219,10 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
                                    .len = sizeof(struct ifla_vf_tx_rate) },
        [IFLA_VF_SPOOFCHK]      = { .type = NLA_BINARY,
                                    .len = sizeof(struct ifla_vf_spoofchk) },
+       [IFLA_VF_RATE]          = { .type = NLA_BINARY,
+                                   .len = sizeof(struct ifla_vf_rate) },
+       [IFLA_VF_LINK_STATE]    = { .type = NLA_BINARY,
+                                   .len = sizeof(struct ifla_vf_link_state) },
 };
 
 static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
@@ -1234,6 +1249,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        struct nlattr *tb[IFLA_MAX+1];
        u32 ext_filter_mask = 0;
        int err;
+       int hdrlen;
 
        s_h = cb->args[0];
        s_idx = cb->args[1];
@@ -1241,8 +1257,17 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        rcu_read_lock();
        cb->seq = net->dev_base_seq;
 
-       if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-                       ifla_policy) >= 0) {
+       /* A hack to preserve kernel<->userspace interface.
+        * The correct header is ifinfomsg. It is consistent with rtnl_getlink.
+        * However, before Linux v3.9 the code here assumed rtgenmsg and that's
+        * what iproute2 < v3.9.0 used.
+        * We can detect the old iproute2. Even including the IFLA_EXT_MASK
+        * attribute, its netlink message is shorter than struct ifinfomsg.
+        */
+       hdrlen = nlmsg_len(cb->nlh) < sizeof(struct ifinfomsg) ?
+                sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
+
+       if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
 
                if (tb[IFLA_EXT_MASK])
                        ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
@@ -1367,11 +1392,29 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
                }
                case IFLA_VF_TX_RATE: {
                        struct ifla_vf_tx_rate *ivt;
+                       struct ifla_vf_info ivf;
                        ivt = nla_data(vf);
                        err = -EOPNOTSUPP;
-                       if (ops->ndo_set_vf_tx_rate)
-                               err = ops->ndo_set_vf_tx_rate(dev, ivt->vf,
-                                                             ivt->rate);
+                       if (ops->ndo_get_vf_config)
+                               err = ops->ndo_get_vf_config(dev, ivt->vf,
+                                                            &ivf);
+                       if (err)
+                               break;
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_rate)
+                               err = ops->ndo_set_vf_rate(dev, ivt->vf,
+                                                          ivf.min_tx_rate,
+                                                          ivt->rate);
+                       break;
+               }
+               case IFLA_VF_RATE: {
+                       struct ifla_vf_rate *ivt;
+                       ivt = nla_data(vf);
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_rate)
+                               err = ops->ndo_set_vf_rate(dev, ivt->vf,
+                                                          ivt->min_tx_rate,
+                                                          ivt->max_tx_rate);
                        break;
                }
                case IFLA_VF_SPOOFCHK: {
@@ -1739,12 +1782,11 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
                return -ENODEV;
 
        ops = dev->rtnl_link_ops;
-       if (!ops)
+       if (!ops || !ops->dellink)
                return -EOPNOTSUPP;
 
        ops->dellink(dev, &list_kill);
        unregister_netdevice_many(&list_kill);
-       list_del(&list_kill);
        return 0;
 }
 
@@ -2001,6 +2043,9 @@ replay:
                        return -EOPNOTSUPP;
                }
 
+               if (!ops->setup)
+                       return -EOPNOTSUPP;
+
                if (!ifname[0])
                        snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
 
@@ -2019,11 +2064,15 @@ replay:
                if (ops->newlink) {
                        err = ops->newlink(net, dev, tb, data);
                        /* Drivers should call free_netdev() in ->destructor
-                        * and unregister it on failure so that device could be
-                        * finally freed in rtnl_unlock.
+                        * and unregister it on failure after registration
+                        * so that device could be finally freed in rtnl_unlock.
                         */
-                       if (err < 0)
+                       if (err < 0) {
+                               /* If device is not registered at all, free it now */
+                               if (dev->reg_state == NETREG_UNINITIALIZED)
+                                       free_netdev(dev);
                                goto out;
+                       }
                } else {
                        err = register_netdevice(dev);
                        if (err < 0) {
@@ -2095,9 +2144,13 @@ static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct nlattr *tb[IFLA_MAX+1];
        u32 ext_filter_mask = 0;
        u16 min_ifinfo_dump_size = 0;
+       int hdrlen;
+
+       /* Same kernel<->userspace interface hack as in rtnl_dump_ifinfo. */
+       hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ?
+                sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
 
-       if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-                       ifla_policy) >= 0) {
+       if (nlmsg_parse(nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
                if (tb[IFLA_EXT_MASK])
                        ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
        }
@@ -2464,6 +2517,7 @@ skip:
 int ndo_dflt_fdb_dump(struct sk_buff *skb,
                      struct netlink_callback *cb,
                      struct net_device *dev,
+                     struct net_device *filter_dev,
                      int idx)
 {
        int err;
@@ -2494,13 +2548,15 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        br_dev = netdev_master_upper_dev_get(dev);
                        ops = br_dev->netdev_ops;
                        if (ops->ndo_fdb_dump)
-                               idx = ops->ndo_fdb_dump(skb, cb, dev, idx);
+                               idx = ops->ndo_fdb_dump(skb, cb, dev, NULL,
+                                                       idx);
                }
 
                if (dev->netdev_ops->ndo_fdb_dump)
-                       idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx);
+                       idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, NULL,
+                                                           idx);
                else
-                       idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
+                       idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx);
        }
        rcu_read_unlock();
 
This page took 0.030458 seconds and 5 git commands to generate.