[PATCH] slab: remove SLAB_ATOMIC
[safe/jmp/linux-2.6] / net / core / fib_rules.c
index 6cdad24..1df6cd4 100644 (file)
@@ -8,7 +8,6 @@
  * Authors:    Thomas Graf <tgraf@suug.ch>
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
@@ -18,7 +17,8 @@ static LIST_HEAD(rules_ops);
 static DEFINE_SPINLOCK(rules_mod_lock);
 
 static void notify_rule_change(int event, struct fib_rule *rule,
-                              struct fib_rules_ops *ops);
+                              struct fib_rules_ops *ops, struct nlmsghdr *nlh,
+                              u32 pid);
 
 static struct fib_rules_ops *lookup_rules_ops(int family)
 {
@@ -107,6 +107,22 @@ out:
 
 EXPORT_SYMBOL_GPL(fib_rules_unregister);
 
+static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
+                         struct flowi *fl, int flags)
+{
+       int ret = 0;
+
+       if (rule->ifindex && (rule->ifindex != fl->iif))
+               goto out;
+
+       if ((rule->mark ^ fl->mark) & rule->mark_mask)
+               goto out;
+
+       ret = ops->match(rule, fl, flags);
+out:
+       return (rule->flags & FIB_RULE_INVERT) ? !ret : ret;
+}
+
 int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl,
                     int flags, struct fib_lookup_arg *arg)
 {
@@ -116,10 +132,7 @@ int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl,
        rcu_read_lock();
 
        list_for_each_entry_rcu(rule, ops->rules_list, list) {
-               if (rule->ifindex && (rule->ifindex != fl->iif))
-                       continue;
-
-               if (!ops->match(rule, fl, flags))
+               if (!fib_rule_match(rule, ops, fl, flags))
                        continue;
 
                err = ops->action(rule, fl, flags, arg);
@@ -160,9 +173,6 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        if (err < 0)
                goto errout;
 
-       if (tb[FRA_IFNAME] && nla_len(tb[FRA_IFNAME]) > IFNAMSIZ)
-               goto errout;
-
        rule = kzalloc(ops->rule_size, GFP_KERNEL);
        if (rule == NULL) {
                err = -ENOMEM;
@@ -176,18 +186,27 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                struct net_device *dev;
 
                rule->ifindex = -1;
-               if (nla_strlcpy(rule->ifname, tb[FRA_IFNAME],
-                               IFNAMSIZ) >= IFNAMSIZ)
-                       goto errout_free;
-
+               nla_strlcpy(rule->ifname, tb[FRA_IFNAME], IFNAMSIZ);
                dev = __dev_get_by_name(rule->ifname);
                if (dev)
                        rule->ifindex = dev->ifindex;
        }
 
+       if (tb[FRA_FWMARK]) {
+               rule->mark = nla_get_u32(tb[FRA_FWMARK]);
+               if (rule->mark)
+                       /* compatibility: if the mark value is non-zero all bits
+                        * are compared unless a mask is explicitly specified.
+                        */
+                       rule->mark_mask = 0xFFFFFFFF;
+       }
+
+       if (tb[FRA_FWMASK])
+               rule->mark_mask = nla_get_u32(tb[FRA_FWMASK]);
+
        rule->action = frh->action;
        rule->flags = frh->flags;
-       rule->table = frh->table;
+       rule->table = frh_get_table(frh, tb);
 
        if (!rule->pref && ops->default_pref)
                rule->pref = ops->default_pref();
@@ -209,7 +228,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        else
                list_add_rcu(&rule->list, ops->rules_list);
 
-       notify_rule_change(RTM_NEWRULE, rule, ops);
+       notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid);
        rules_ops_put(ops);
        return 0;
 
@@ -245,7 +264,7 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                if (frh->action && (frh->action != rule->action))
                        continue;
 
-               if (frh->table && (frh->table != rule->table))
+               if (frh->table && (frh_get_table(frh, tb) != rule->table))
                        continue;
 
                if (tb[FRA_PRIORITY] &&
@@ -256,6 +275,14 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                    nla_strcmp(tb[FRA_IFNAME], rule->ifname))
                        continue;
 
+               if (tb[FRA_FWMARK] &&
+                   (rule->mark != nla_get_u32(tb[FRA_FWMARK])))
+                       continue;
+
+               if (tb[FRA_FWMASK] &&
+                   (rule->mark_mask != nla_get_u32(tb[FRA_FWMASK])))
+                       continue;
+
                if (!ops->compare(rule, frh, tb))
                        continue;
 
@@ -266,7 +293,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 
                list_del_rcu(&rule->list);
                synchronize_rcu();
-               notify_rule_change(RTM_DELRULE, rule, ops);
+               notify_rule_change(RTM_DELRULE, rule, ops, nlh,
+                                  NETLINK_CB(skb).pid);
                fib_rule_put(rule);
                rules_ops_put(ops);
                return 0;
@@ -278,6 +306,22 @@ errout:
        return err;
 }
 
+static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
+                                        struct fib_rule *rule)
+{
+       size_t payload = NLMSG_ALIGN(sizeof(struct fib_rule_hdr))
+                        + nla_total_size(IFNAMSIZ) /* FRA_IFNAME */
+                        + nla_total_size(4) /* FRA_PRIORITY */
+                        + nla_total_size(4) /* FRA_TABLE */
+                        + nla_total_size(4) /* FRA_FWMARK */
+                        + nla_total_size(4); /* FRA_FWMASK */
+
+       if (ops->nlmsg_payload)
+               payload += ops->nlmsg_payload(rule);
+
+       return payload;
+}
+
 static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
                            u32 pid, u32 seq, int type, int flags,
                            struct fib_rules_ops *ops)
@@ -291,6 +335,7 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
 
        frh = nlmsg_data(nlh);
        frh->table = rule->table;
+       NLA_PUT_U32(skb, FRA_TABLE, rule->table);
        frh->res1 = 0;
        frh->res2 = 0;
        frh->action = rule->action;
@@ -302,6 +347,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
        if (rule->pref)
                NLA_PUT_U32(skb, FRA_PRIORITY, rule->pref);
 
+       if (rule->mark)
+               NLA_PUT_U32(skb, FRA_FWMARK, rule->mark);
+
+       if (rule->mark_mask || rule->mark)
+               NLA_PUT_U32(skb, FRA_FWMASK, rule->mark_mask);
+
        if (ops->fill(rule, skb, nlh, frh) < 0)
                goto nla_put_failure;
 
@@ -343,18 +394,24 @@ skip:
 EXPORT_SYMBOL_GPL(fib_rules_dump);
 
 static void notify_rule_change(int event, struct fib_rule *rule,
-                              struct fib_rules_ops *ops)
+                              struct fib_rules_ops *ops, struct nlmsghdr *nlh,
+                              u32 pid)
 {
-       int size = nlmsg_total_size(sizeof(struct fib_rule_hdr) + 128);
-       struct sk_buff *skb = alloc_skb(size, GFP_KERNEL);
+       struct sk_buff *skb;
+       int err = -ENOBUFS;
 
+       skb = nlmsg_new(fib_rule_nlmsg_size(ops, rule), GFP_KERNEL);
        if (skb == NULL)
-               netlink_set_err(rtnl, 0, ops->nlgroup, ENOBUFS);
-       else if (fib_nl_fill_rule(skb, rule, 0, 0, event, 0, ops) < 0) {
-               kfree_skb(skb);
-               netlink_set_err(rtnl, 0, ops->nlgroup, EINVAL);
-       } else
-               netlink_broadcast(rtnl, skb, 0, ops->nlgroup, GFP_KERNEL);
+               goto errout;
+
+       err = fib_nl_fill_rule(skb, rule, pid, nlh->nlmsg_seq, event, 0, ops);
+       /* failure implies BUG in fib_rule_nlmsg_size() */
+       BUG_ON(err < 0);
+
+       err = rtnl_notify(skb, pid, ops->nlgroup, nlh, GFP_KERNEL);
+errout:
+       if (err < 0)
+               rtnl_set_sk_err(ops->nlgroup, err);
 }
 
 static void attach_rules(struct list_head *rules, struct net_device *dev)