Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=e1701c68c1d1aeb3213d7016593ea9a1d4309417
Commit:     e1701c68c1d1aeb3213d7016593ea9a1d4309417
Parent:     5f85813c33ddbf6d11ccfdbcc01f176e24a76bd2
Author:     Thomas Graf <[EMAIL PROTECTED]>
AuthorDate: Sat Mar 24 12:46:02 2007 -0700
Committer:  David S. Miller <[EMAIL PROTECTED]>
CommitDate: Sun Mar 25 18:48:00 2007 -0700

    [NET]: Fix fib_rules compatibility breakage
    
    Based upon a patch from Patrick McHardy.
    
    The fib_rules netlink attribute policy introduced in 2.6.19 broke
    userspace compatibilty. When specifying a rule with "from all"
    or "to all", iproute adds a zero byte long netlink attribute,
    but the policy requires all addresses to have a size equal to
    sizeof(struct in_addr)/sizeof(struct in6_addr), resulting in a
    validation error.
    
    Check attribute length of FRA_SRC/FRA_DST in the generic framework
    by letting the family specific rules implementation provide the
    length of an address. Report an error if address length is non
    zero but no address attribute is provided. Fix actual bug by
    checking address length for non-zero instead of relying on
    availability of attribute.
    
    Signed-off-by: Thomas Graf <[EMAIL PROTECTED]>
    Signed-off-by: Patrick McHardy <[EMAIL PROTECTED]>
    Signed-off-by: David S. Miller <[EMAIL PROTECTED]>
---
 include/net/fib_rules.h |    1 +
 net/core/fib_rules.c    |   30 ++++++++++++++++++++++++++++++
 net/decnet/dn_rules.c   |   13 ++++++-------
 net/ipv4/fib_rules.c    |   14 ++++++--------
 net/ipv6/fib6_rules.c   |   14 +++++---------
 5 files changed, 48 insertions(+), 24 deletions(-)

diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index bc3c264..d585ea9 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -34,6 +34,7 @@ struct fib_rules_ops
        int                     family;
        struct list_head        list;
        int                     rule_size;
+       int                     addr_size;
 
        int                     (*action)(struct fib_rule *,
                                          struct flowi *, int,
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index 3aea4e8..d011819 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -152,6 +152,28 @@ out:
 
 EXPORT_SYMBOL_GPL(fib_rules_lookup);
 
+static int validate_rulemsg(struct fib_rule_hdr *frh, struct nlattr **tb,
+                           struct fib_rules_ops *ops)
+{
+       int err = -EINVAL;
+
+       if (frh->src_len)
+               if (tb[FRA_SRC] == NULL ||
+                   frh->src_len > (ops->addr_size * 8) ||
+                   nla_len(tb[FRA_SRC]) != ops->addr_size)
+                       goto errout;
+
+       if (frh->dst_len)
+               if (tb[FRA_DST] == NULL ||
+                   frh->dst_len > (ops->addr_size * 8) ||
+                   nla_len(tb[FRA_DST]) != ops->addr_size)
+                       goto errout;
+
+       err = 0;
+errout:
+       return err;
+}
+
 int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
        struct fib_rule_hdr *frh = nlmsg_data(nlh);
@@ -173,6 +195,10 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* 
nlh, void *arg)
        if (err < 0)
                goto errout;
 
+       err = validate_rulemsg(frh, tb, ops);
+       if (err < 0)
+               goto errout;
+
        rule = kzalloc(ops->rule_size, GFP_KERNEL);
        if (rule == NULL) {
                err = -ENOMEM;
@@ -260,6 +286,10 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* 
nlh, void *arg)
        if (err < 0)
                goto errout;
 
+       err = validate_rulemsg(frh, tb, ops);
+       if (err < 0)
+               goto errout;
+
        list_for_each_entry(rule, ops->rules_list, list) {
                if (frh->action && (frh->action != rule->action))
                        continue;
diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c
index b6c98ac..5e86dd5 100644
--- a/net/decnet/dn_rules.c
+++ b/net/decnet/dn_rules.c
@@ -109,8 +109,6 @@ errout:
 
 static struct nla_policy dn_fib_rule_policy[FRA_MAX+1] __read_mostly = {
        FRA_GENERIC_POLICY,
-       [FRA_SRC]       = { .type = NLA_U16 },
-       [FRA_DST]       = { .type = NLA_U16 },
 };
 
 static int dn_fib_rule_match(struct fib_rule *rule, struct flowi *fl, int 
flags)
@@ -133,7 +131,7 @@ static int dn_fib_rule_configure(struct fib_rule *rule, 
struct sk_buff *skb,
        int err = -EINVAL;
        struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
 
-       if (frh->src_len > 16 || frh->dst_len > 16 || frh->tos)
+       if (frh->tos)
                goto  errout;
 
        if (rule->table == RT_TABLE_UNSPEC) {
@@ -150,10 +148,10 @@ static int dn_fib_rule_configure(struct fib_rule *rule, 
struct sk_buff *skb,
                }
        }
 
-       if (tb[FRA_SRC])
+       if (frh->src_len)
                r->src = nla_get_le16(tb[FRA_SRC]);
 
-       if (tb[FRA_DST])
+       if (frh->dst_len)
                r->dst = nla_get_le16(tb[FRA_DST]);
 
        r->src_len = frh->src_len;
@@ -176,10 +174,10 @@ static int dn_fib_rule_compare(struct fib_rule *rule, 
struct fib_rule_hdr *frh,
        if (frh->dst_len && (r->dst_len != frh->dst_len))
                return 0;
 
-       if (tb[FRA_SRC] && (r->src != nla_get_le16(tb[FRA_SRC])))
+       if (frh->src_len && (r->src != nla_get_le16(tb[FRA_SRC])))
                return 0;
 
-       if (tb[FRA_DST] && (r->dst != nla_get_le16(tb[FRA_DST])))
+       if (frh->dst_len && (r->dst != nla_get_le16(tb[FRA_DST])))
                return 0;
 
        return 1;
@@ -249,6 +247,7 @@ int dn_fib_dump_rules(struct sk_buff *skb, struct 
netlink_callback *cb)
 static struct fib_rules_ops dn_fib_rules_ops = {
        .family         = AF_DECnet,
        .rule_size      = sizeof(struct dn_fib_rule),
+       .addr_size      = sizeof(u16),
        .action         = dn_fib_rule_action,
        .match          = dn_fib_rule_match,
        .configure      = dn_fib_rule_configure,
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index b837c33..c660c07 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -171,8 +171,6 @@ static struct fib_table *fib_empty_table(void)
 
 static struct nla_policy fib4_rule_policy[FRA_MAX+1] __read_mostly = {
        FRA_GENERIC_POLICY,
-       [FRA_SRC]       = { .type = NLA_U32 },
-       [FRA_DST]       = { .type = NLA_U32 },
        [FRA_FLOW]      = { .type = NLA_U32 },
 };
 
@@ -183,8 +181,7 @@ static int fib4_rule_configure(struct fib_rule *rule, 
struct sk_buff *skb,
        int err = -EINVAL;
        struct fib4_rule *rule4 = (struct fib4_rule *) rule;
 
-       if (frh->src_len > 32 || frh->dst_len > 32 ||
-           (frh->tos & ~IPTOS_TOS_MASK))
+       if (frh->tos & ~IPTOS_TOS_MASK)
                goto errout;
 
        if (rule->table == RT_TABLE_UNSPEC) {
@@ -201,10 +198,10 @@ static int fib4_rule_configure(struct fib_rule *rule, 
struct sk_buff *skb,
                }
        }
 
-       if (tb[FRA_SRC])
+       if (frh->src_len)
                rule4->src = nla_get_be32(tb[FRA_SRC]);
 
-       if (tb[FRA_DST])
+       if (frh->dst_len)
                rule4->dst = nla_get_be32(tb[FRA_DST]);
 
 #ifdef CONFIG_NET_CLS_ROUTE
@@ -242,10 +239,10 @@ static int fib4_rule_compare(struct fib_rule *rule, 
struct fib_rule_hdr *frh,
                return 0;
 #endif
 
-       if (tb[FRA_SRC] && (rule4->src != nla_get_be32(tb[FRA_SRC])))
+       if (frh->src_len && (rule4->src != nla_get_be32(tb[FRA_SRC])))
                return 0;
 
-       if (tb[FRA_DST] && (rule4->dst != nla_get_be32(tb[FRA_DST])))
+       if (frh->dst_len && (rule4->dst != nla_get_be32(tb[FRA_DST])))
                return 0;
 
        return 1;
@@ -309,6 +306,7 @@ static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule)
 static struct fib_rules_ops fib4_rules_ops = {
        .family         = AF_INET,
        .rule_size      = sizeof(struct fib4_rule),
+       .addr_size      = sizeof(u32),
        .action         = fib4_rule_action,
        .match          = fib4_rule_match,
        .configure      = fib4_rule_configure,
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index 0862809..ea3035b 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -131,8 +131,6 @@ static int fib6_rule_match(struct fib_rule *rule, struct 
flowi *fl, int flags)
 
 static struct nla_policy fib6_rule_policy[FRA_MAX+1] __read_mostly = {
        FRA_GENERIC_POLICY,
-       [FRA_SRC]       = { .len = sizeof(struct in6_addr) },
-       [FRA_DST]       = { .len = sizeof(struct in6_addr) },
 };
 
 static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
@@ -142,9 +140,6 @@ static int fib6_rule_configure(struct fib_rule *rule, 
struct sk_buff *skb,
        int err = -EINVAL;
        struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 
-       if (frh->src_len > 128 || frh->dst_len > 128)
-               goto errout;
-
        if (rule->action == FR_ACT_TO_TBL) {
                if (rule->table == RT6_TABLE_UNSPEC)
                        goto errout;
@@ -155,11 +150,11 @@ static int fib6_rule_configure(struct fib_rule *rule, 
struct sk_buff *skb,
                }
        }
 
-       if (tb[FRA_SRC])
+       if (frh->src_len)
                nla_memcpy(&rule6->src.addr, tb[FRA_SRC],
                           sizeof(struct in6_addr));
 
-       if (tb[FRA_DST])
+       if (frh->dst_len)
                nla_memcpy(&rule6->dst.addr, tb[FRA_DST],
                           sizeof(struct in6_addr));
 
@@ -186,11 +181,11 @@ static int fib6_rule_compare(struct fib_rule *rule, 
struct fib_rule_hdr *frh,
        if (frh->tos && (rule6->tclass != frh->tos))
                return 0;
 
-       if (tb[FRA_SRC] &&
+       if (frh->src_len &&
            nla_memcmp(tb[FRA_SRC], &rule6->src.addr, sizeof(struct in6_addr)))
                return 0;
 
-       if (tb[FRA_DST] &&
+       if (frh->dst_len &&
            nla_memcmp(tb[FRA_DST], &rule6->dst.addr, sizeof(struct in6_addr)))
                return 0;
 
@@ -240,6 +235,7 @@ static size_t fib6_rule_nlmsg_payload(struct fib_rule *rule)
 static struct fib_rules_ops fib6_rules_ops = {
        .family                 = AF_INET6,
        .rule_size              = sizeof(struct fib6_rule),
+       .addr_size              = sizeof(struct in6_addr),
        .action                 = fib6_rule_action,
        .match                  = fib6_rule_match,
        .configure              = fib6_rule_configure,
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to