]> Pileus Git - ~andy/linux/blobdiff - net/core/rtnetlink.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[~andy/linux] / net / core / rtnetlink.c
index b65441da74abd85cc8deacc7650c583013e9dcfb..589d0abb34a0b8c7f84f7b07208963c5c660871f 100644 (file)
@@ -517,32 +517,6 @@ out:
        return err;
 }
 
-static const int rtm_min[RTM_NR_FAMILIES] =
-{
-       [RTM_FAM(RTM_NEWLINK)]      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-       [RTM_FAM(RTM_NEWADDR)]      = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
-       [RTM_FAM(RTM_NEWROUTE)]     = NLMSG_LENGTH(sizeof(struct rtmsg)),
-       [RTM_FAM(RTM_NEWRULE)]      = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)),
-       [RTM_FAM(RTM_NEWQDISC)]     = NLMSG_LENGTH(sizeof(struct tcmsg)),
-       [RTM_FAM(RTM_NEWTCLASS)]    = NLMSG_LENGTH(sizeof(struct tcmsg)),
-       [RTM_FAM(RTM_NEWTFILTER)]   = NLMSG_LENGTH(sizeof(struct tcmsg)),
-       [RTM_FAM(RTM_NEWACTION)]    = NLMSG_LENGTH(sizeof(struct tcamsg)),
-       [RTM_FAM(RTM_GETMULTICAST)] = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-       [RTM_FAM(RTM_GETANYCAST)]   = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-};
-
-static const int rta_max[RTM_NR_FAMILIES] =
-{
-       [RTM_FAM(RTM_NEWLINK)]      = IFLA_MAX,
-       [RTM_FAM(RTM_NEWADDR)]      = IFA_MAX,
-       [RTM_FAM(RTM_NEWROUTE)]     = RTA_MAX,
-       [RTM_FAM(RTM_NEWRULE)]      = FRA_MAX,
-       [RTM_FAM(RTM_NEWQDISC)]     = TCA_MAX,
-       [RTM_FAM(RTM_NEWTCLASS)]    = TCA_MAX,
-       [RTM_FAM(RTM_NEWTFILTER)]   = TCA_MAX,
-       [RTM_FAM(RTM_NEWACTION)]    = TCAA_MAX,
-};
-
 int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo)
 {
        struct sock *rtnl = net->rtnl;
@@ -1539,7 +1513,7 @@ errout:
        return err;
 }
 
-static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -1580,7 +1554,7 @@ errout:
        return err;
 }
 
-static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        const struct rtnl_link_ops *ops;
@@ -1711,7 +1685,7 @@ static int rtnl_group_changelink(struct net *net, int group,
        return 0;
 }
 
-static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        const struct rtnl_link_ops *ops;
@@ -1866,7 +1840,7 @@ out:
        }
 }
 
-static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -1957,8 +1931,11 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
                if (rtnl_msg_handlers[idx] == NULL ||
                    rtnl_msg_handlers[idx][type].dumpit == NULL)
                        continue;
-               if (idx > s_idx)
+               if (idx > s_idx) {
                        memset(&cb->args[0], 0, sizeof(cb->args));
+                       cb->prev_seq = 0;
+                       cb->seq = 0;
+               }
                if (rtnl_msg_handlers[idx][type].dumpit(skb, cb))
                        break;
        }
@@ -2051,7 +2028,39 @@ errout:
        rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
 }
 
-static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+/**
+ * ndo_dflt_fdb_add - default netdevice operation to add an FDB entry
+ */
+int ndo_dflt_fdb_add(struct ndmsg *ndm,
+                    struct nlattr *tb[],
+                    struct net_device *dev,
+                    const unsigned char *addr,
+                    u16 flags)
+{
+       int err = -EINVAL;
+
+       /* If aging addresses are supported device will need to
+        * implement its own handler for this.
+        */
+       if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
+               pr_info("%s: FDB only supports static addresses\n", dev->name);
+               return err;
+       }
+
+       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
+               err = dev_uc_add_excl(dev, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_add_excl(dev, addr);
+
+       /* Only return duplicate errors if NLM_F_EXCL is set */
+       if (err == -EEXIST && !(flags & NLM_F_EXCL))
+               err = 0;
+
+       return err;
+}
+EXPORT_SYMBOL(ndo_dflt_fdb_add);
+
+static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
@@ -2082,7 +2091,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        }
 
        addr = nla_data(tb[NDA_LLADDR]);
-       if (!is_valid_ether_addr(addr)) {
+       if (is_zero_ether_addr(addr)) {
                pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n");
                return -EINVAL;
        }
@@ -2103,10 +2112,13 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        }
 
        /* Embedded bridge, macvlan, and any other device support */
-       if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_add) {
-               err = dev->netdev_ops->ndo_fdb_add(ndm, tb,
-                                                  dev, addr,
-                                                  nlh->nlmsg_flags);
+       if ((ndm->ndm_flags & NTF_SELF)) {
+               if (dev->netdev_ops->ndo_fdb_add)
+                       err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr,
+                                                          nlh->nlmsg_flags);
+               else
+                       err = ndo_dflt_fdb_add(ndm, tb, dev, addr,
+                                              nlh->nlmsg_flags);
 
                if (!err) {
                        rtnl_fdb_notify(dev, addr, RTM_NEWNEIGH);
@@ -2117,7 +2129,36 @@ out:
        return err;
 }
 
-static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+/**
+ * ndo_dflt_fdb_del - default netdevice operation to delete an FDB entry
+ */
+int ndo_dflt_fdb_del(struct ndmsg *ndm,
+                    struct nlattr *tb[],
+                    struct net_device *dev,
+                    const unsigned char *addr)
+{
+       int err = -EOPNOTSUPP;
+
+       /* If aging addresses are supported device will need to
+        * implement its own handler for this.
+        */
+       if (ndm->ndm_state & NUD_PERMANENT) {
+               pr_info("%s: FDB only supports static addresses\n", dev->name);
+               return -EINVAL;
+       }
+
+       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
+               err = dev_uc_del(dev, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_del(dev, addr);
+       else
+               err = -EINVAL;
+
+       return err;
+}
+EXPORT_SYMBOL(ndo_dflt_fdb_del);
+
+static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
@@ -2174,8 +2215,11 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        }
 
        /* Embedded bridge, macvlan, and any other device support */
-       if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) {
-               err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
+       if (ndm->ndm_flags & NTF_SELF) {
+               if (dev->netdev_ops->ndo_fdb_del)
+                       err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
+               else
+                       err = ndo_dflt_fdb_del(ndm, tb, dev, addr);
 
                if (!err) {
                        rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
@@ -2220,7 +2264,7 @@ skip:
  * @dev: netdevice
  *
  * Default netdevice operation to dump the existing unicast address list.
- * Returns zero on success.
+ * Returns number of addresses from list put in skb.
  */
 int ndo_dflt_fdb_dump(struct sk_buff *skb,
                      struct netlink_callback *cb,
@@ -2260,6 +2304,8 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
                if (dev->netdev_ops->ndo_fdb_dump)
                        idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx);
+               else
+                       idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
        }
        rcu_read_unlock();
 
@@ -2411,8 +2457,7 @@ errout:
        return err;
 }
 
-static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
-                              void *arg)
+static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -2482,8 +2527,7 @@ out:
        return err;
 }
 
-static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
-                              void *arg)
+static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct ifinfomsg *ifm;
@@ -2553,10 +2597,6 @@ out:
        return err;
 }
 
-/* Protected by RTNL sempahore.  */
-static struct rtattr **rta_buf;
-static int rtattr_max;
-
 /* Process one rtnetlink message. */
 
 static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -2564,7 +2604,6 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct net *net = sock_net(skb->sk);
        rtnl_doit_func doit;
        int sz_idx, kind;
-       int min_len;
        int family;
        int type;
        int err;
@@ -2576,10 +2615,10 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        type -= RTM_BASE;
 
        /* All the messages must have at least 1 byte length */
-       if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg)))
+       if (nlmsg_len(nlh) < sizeof(struct rtgenmsg))
                return 0;
 
-       family = ((struct rtgenmsg *)NLMSG_DATA(nlh))->rtgen_family;
+       family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
        sz_idx = type>>2;
        kind = type&3;
 
@@ -2612,32 +2651,11 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                return err;
        }
 
-       memset(rta_buf, 0, (rtattr_max * sizeof(struct rtattr *)));
-
-       min_len = rtm_min[sz_idx];
-       if (nlh->nlmsg_len < min_len)
-               return -EINVAL;
-
-       if (nlh->nlmsg_len > min_len) {
-               int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
-               struct rtattr *attr = (void *)nlh + NLMSG_ALIGN(min_len);
-
-               while (RTA_OK(attr, attrlen)) {
-                       unsigned int flavor = attr->rta_type & NLA_TYPE_MASK;
-                       if (flavor) {
-                               if (flavor > rta_max[sz_idx])
-                                       return -EINVAL;
-                               rta_buf[flavor-1] = attr;
-                       }
-                       attr = RTA_NEXT(attr, attrlen);
-               }
-       }
-
        doit = rtnl_get_doit(family, type);
        if (doit == NULL)
                return -EOPNOTSUPP;
 
-       return doit(skb, nlh, (void *)&rta_buf[0]);
+       return doit(skb, nlh);
 }
 
 static void rtnetlink_rcv(struct sk_buff *skb)
@@ -2707,16 +2725,6 @@ static struct pernet_operations rtnetlink_net_ops = {
 
 void __init rtnetlink_init(void)
 {
-       int i;
-
-       rtattr_max = 0;
-       for (i = 0; i < ARRAY_SIZE(rta_max); i++)
-               if (rta_max[i] > rtattr_max)
-                       rtattr_max = rta_max[i];
-       rta_buf = kmalloc(rtattr_max * sizeof(struct rtattr *), GFP_KERNEL);
-       if (!rta_buf)
-               panic("rtnetlink_init: cannot allocate rta_buf\n");
-
        if (register_pernet_subsys(&rtnetlink_net_ops))
                panic("rtnetlink_init: cannot initialize rtnetlink\n");