From 9d9e6a5819230b5a5cc036f213135cb123ab1e50 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 25 Mar 2007 23:20:05 -0700 Subject: [NET] rules: Use rtnl registration interface Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/net/fib_rules.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'include/net/fib_rules.h') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index d585ea9fa97d..b2b9ccdf32d6 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include struct fib_rule { @@ -99,10 +99,6 @@ extern int fib_rules_lookup(struct fib_rules_ops *, struct flowi *, int flags, struct fib_lookup_arg *); -extern int fib_nl_newrule(struct sk_buff *, - struct nlmsghdr *, void *); -extern int fib_nl_delrule(struct sk_buff *, - struct nlmsghdr *, void *); extern int fib_rules_dump(struct sk_buff *, struct netlink_callback *, int); #endif -- cgit v1.2.3 From c454673da7c1d6533f40ec2f788023df9af56ebf Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 25 Mar 2007 23:24:24 -0700 Subject: [NET] rules: Unified rules dumping Implements a unified, protocol independant rules dumping function which is capable of both, dumping a specific protocol family or all of them. This speeds up dumping as less lookups are required. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/net/fib_rules.h | 3 --- include/net/rtnetlink.h | 8 ++++++++ net/core/fib_rules.c | 47 +++++++++++++++++++++++++++++++++++++---------- net/decnet/dn_rules.c | 7 ------- net/ipv4/fib_rules.c | 7 ------- net/ipv6/fib6_rules.c | 7 ------- 6 files changed, 45 insertions(+), 34 deletions(-) (limited to 'include/net/fib_rules.h') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index b2b9ccdf32d6..ff3029fe9656 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -98,7 +98,4 @@ extern int fib_rules_unregister(struct fib_rules_ops *); extern int fib_rules_lookup(struct fib_rules_ops *, struct flowi *, int flags, struct fib_lookup_arg *); - -extern int fib_rules_dump(struct sk_buff *, - struct netlink_callback *, int); #endif diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index dce7072bd28c..086fa9e89509 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -15,4 +15,12 @@ extern int rtnl_unregister(int protocol, int msgtype); extern void rtnl_unregister_all(int protocol); extern int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb); +static inline int rtnl_msg_family(struct nlmsghdr *nlh) +{ + if (nlmsg_len(nlh) >= sizeof(struct rtgenmsg)) + return ((struct rtgenmsg *) nlmsg_data(nlh))->rtgen_family; + else + return AF_UNSPEC; +} + #endif diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index bf45f24cfea2..fdf05af16ba5 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -393,19 +393,15 @@ nla_put_failure: return -EMSGSIZE; } -int fib_rules_dump(struct sk_buff *skb, struct netlink_callback *cb, int family) +static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, + struct fib_rules_ops *ops) { int idx = 0; struct fib_rule *rule; - struct fib_rules_ops *ops; - - ops = lookup_rules_ops(family); - if (ops == NULL) - return -EAFNOSUPPORT; rcu_read_lock(); list_for_each_entry_rcu(rule, ops->rules_list, list) { - if (idx < cb->args[0]) + if (idx < cb->args[1]) goto skip; if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).pid, @@ -416,13 +412,44 @@ skip: idx++; } rcu_read_unlock(); - cb->args[0] = idx; + cb->args[1] = idx; rules_ops_put(ops); return skb->len; } -EXPORT_SYMBOL_GPL(fib_rules_dump); +static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct fib_rules_ops *ops; + int idx = 0, family; + + family = rtnl_msg_family(cb->nlh); + if (family != AF_UNSPEC) { + /* Protocol specific dump request */ + ops = lookup_rules_ops(family); + if (ops == NULL) + return -EAFNOSUPPORT; + + return dump_rules(skb, cb, ops); + } + + rcu_read_lock(); + list_for_each_entry_rcu(ops, &rules_ops, list) { + if (idx < cb->args[0] || !try_module_get(ops->owner)) + goto skip; + + if (dump_rules(skb, cb, ops) < 0) + break; + + cb->args[1] = 0; + skip: + idx++; + } + rcu_read_unlock(); + cb->args[0] = idx; + + return skb->len; +} static void notify_rule_change(int event, struct fib_rule *rule, struct fib_rules_ops *ops, struct nlmsghdr *nlh, @@ -503,7 +530,7 @@ static int __init fib_rules_init(void) { rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL); rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL); - rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, rtnl_dump_all); + rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule); return register_netdevice_notifier(&fib_rules_notifier); } diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index a7a7da9b35c3..fd0cc2aa316c 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -239,11 +239,6 @@ static u32 dn_fib_rule_default_pref(void) return 0; } -static int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) -{ - return fib_rules_dump(skb, cb, AF_DECnet); -} - static struct fib_rules_ops dn_fib_rules_ops = { .family = AF_DECnet, .rule_size = sizeof(struct dn_fib_rule), @@ -264,12 +259,10 @@ void __init dn_fib_rules_init(void) { list_add_tail(&default_rule.common.list, &dn_fib_rules); fib_rules_register(&dn_fib_rules_ops); - rtnl_register(PF_DECnet, RTM_GETRULE, NULL, dn_fib_dump_rules); } void __exit dn_fib_rules_cleanup(void) { - rtnl_unregister(PF_DECnet, RTM_GETRULE); fib_rules_unregister(&dn_fib_rules_ops); } diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index a7f931ddfaad..b021b3440ca3 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -274,11 +274,6 @@ nla_put_failure: return -ENOBUFS; } -static int fib4_rule_dump(struct sk_buff *skb, struct netlink_callback *cb) -{ - return fib_rules_dump(skb, cb, AF_INET); -} - static u32 fib4_rule_default_pref(void) { struct list_head *pos; @@ -327,6 +322,4 @@ void __init fib4_rules_init(void) list_add_tail(&default_rule.common.list, &fib4_rules); fib_rules_register(&fib4_rules_ops); - - rtnl_register(PF_INET, RTM_GETRULE, NULL, fib4_rule_dump); } diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index c74da4b6dd2f..dd9720e700ef 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -216,11 +216,6 @@ nla_put_failure: return -ENOBUFS; } -static int fib6_rules_dump(struct sk_buff *skb, struct netlink_callback *cb) -{ - return fib_rules_dump(skb, cb, AF_INET6); -} - static u32 fib6_rule_default_pref(void) { return 0x3FFF; @@ -255,11 +250,9 @@ void __init fib6_rules_init(void) list_add_tail(&main_rule.common.list, &fib6_rules); fib_rules_register(&fib6_rules_ops); - __rtnl_register(PF_INET6, RTM_GETRULE, NULL, fib6_rules_dump); } void fib6_rules_cleanup(void) { - rtnl_unregister(PF_INET6, RTM_GETRULE); fib_rules_unregister(&fib6_rules_ops); } -- cgit v1.2.3 From 0947c9fe56d9cf7ad0bc3a03ccd30446cde698e4 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Mon, 26 Mar 2007 17:14:15 -0700 Subject: [NET] fib_rules: goto rule action This patch adds a new rule action FR_ACT_GOTO which allows to skip a set of rules by jumping to another rule. The rule to jump to is specified via the FRA_GOTO attribute which carries a rule preference. Referring to a rule which doesn't exists is explicitely allowed. Such goto rules are marked with the flag FIB_RULE_UNRESOLVED and will act like a rule with a non-matching selector. The rule will become functional as soon as its target is present. The goto action enables performance optimizations by reducing the average number of rules that have to be passed per lookup. Example: 0: from all lookup local 40: not from all to 192.168.23.128 goto 32766 41: from all fwmark 0xa blackhole 42: from all fwmark 0xff blackhole 32766: from all lookup main Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/fib_rules.h | 5 +-- include/net/fib_rules.h | 7 +++- net/core/fib_rules.c | 88 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 6 deletions(-) (limited to 'include/net/fib_rules.h') diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 8270aac2aa5d..ec9c7b1d3e91 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -7,6 +7,7 @@ /* rule is permanent, and cannot be deleted */ #define FIB_RULE_PERMANENT 1 #define FIB_RULE_INVERT 2 +#define FIB_RULE_UNRESOLVED 4 struct fib_rule_hdr { @@ -29,7 +30,7 @@ enum FRA_DST, /* destination address */ FRA_SRC, /* source address */ FRA_IFNAME, /* interface name */ - FRA_UNUSED1, + FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */ FRA_UNUSED2, FRA_PRIORITY, /* priority/preference */ FRA_UNUSED3, @@ -51,7 +52,7 @@ enum { FR_ACT_UNSPEC, FR_ACT_TO_TBL, /* Pass to fixed table */ - FR_ACT_RES1, + FR_ACT_GOTO, /* Jump to another rule */ FR_ACT_RES2, FR_ACT_RES3, FR_ACT_RES4, diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index ff3029fe9656..08bab8b6e575 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -19,6 +19,8 @@ struct fib_rule u32 flags; u32 table; u8 action; + u32 target; + struct fib_rule * ctarget; struct rcu_head rcu; }; @@ -35,6 +37,8 @@ struct fib_rules_ops struct list_head list; int rule_size; int addr_size; + int unresolved_rules; + int nr_goto_rules; int (*action)(struct fib_rule *, struct flowi *, int, @@ -66,7 +70,8 @@ struct fib_rules_ops [FRA_PRIORITY] = { .type = NLA_U32 }, \ [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ - [FRA_TABLE] = { .type = NLA_U32 } + [FRA_TABLE] = { .type = NLA_U32 }, \ + [FRA_GOTO] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index fdf05af16ba5..0d8bb2efb0c1 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -132,10 +132,23 @@ 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) { +jumped: if (!fib_rule_match(rule, ops, fl, flags)) continue; - err = ops->action(rule, fl, flags, arg); + if (rule->action == FR_ACT_GOTO) { + struct fib_rule *target; + + target = rcu_dereference(rule->ctarget); + if (target == NULL) { + continue; + } else { + rule = target; + goto jumped; + } + } else + err = ops->action(rule, fl, flags, arg); + if (err != -EAGAIN) { fib_rule_get(rule); arg->rule = rule; @@ -180,7 +193,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) struct fib_rules_ops *ops = NULL; struct fib_rule *rule, *r, *last = NULL; struct nlattr *tb[FRA_MAX+1]; - int err = -EINVAL; + int err = -EINVAL, unresolved = 0; if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) goto errout; @@ -237,6 +250,28 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) if (!rule->pref && ops->default_pref) rule->pref = ops->default_pref(); + err = -EINVAL; + if (tb[FRA_GOTO]) { + if (rule->action != FR_ACT_GOTO) + goto errout_free; + + rule->target = nla_get_u32(tb[FRA_GOTO]); + /* Backward jumps are prohibited to avoid endless loops */ + if (rule->target <= rule->pref) + goto errout_free; + + list_for_each_entry(r, ops->rules_list, list) { + if (r->pref == rule->target) { + rule->ctarget = r; + break; + } + } + + if (rule->ctarget == NULL) + unresolved = 1; + } else if (rule->action == FR_ACT_GOTO) + goto errout_free; + err = ops->configure(rule, skb, nlh, frh, tb); if (err < 0) goto errout_free; @@ -249,6 +284,28 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) fib_rule_get(rule); + if (ops->unresolved_rules) { + /* + * There are unresolved goto rules in the list, check if + * any of them are pointing to this new rule. + */ + list_for_each_entry(r, ops->rules_list, list) { + if (r->action == FR_ACT_GOTO && + r->target == rule->pref) { + BUG_ON(r->ctarget != NULL); + rcu_assign_pointer(r->ctarget, rule); + if (--ops->unresolved_rules == 0) + break; + } + } + } + + if (rule->action == FR_ACT_GOTO) + ops->nr_goto_rules++; + + if (unresolved) + ops->unresolved_rules++; + if (last) list_add_rcu(&rule->list, &last->list); else @@ -269,7 +326,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct fib_rule_hdr *frh = nlmsg_data(nlh); struct fib_rules_ops *ops = NULL; - struct fib_rule *rule; + struct fib_rule *rule, *tmp; struct nlattr *tb[FRA_MAX+1]; int err = -EINVAL; @@ -322,6 +379,25 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) } list_del_rcu(&rule->list); + + if (rule->action == FR_ACT_GOTO) + ops->nr_goto_rules--; + + /* + * Check if this rule is a target to any of them. If so, + * disable them. As this operation is eventually very + * expensive, it is only performed if goto rules have + * actually been added. + */ + if (ops->nr_goto_rules > 0) { + list_for_each_entry(tmp, ops->rules_list, list) { + if (tmp->ctarget == rule) { + rcu_assign_pointer(tmp->ctarget, NULL); + ops->unresolved_rules++; + } + } + } + synchronize_rcu(); notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).pid); @@ -371,6 +447,9 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, frh->action = rule->action; frh->flags = rule->flags; + if (rule->action == FR_ACT_GOTO && rule->ctarget == NULL) + frh->flags |= FIB_RULE_UNRESOLVED; + if (rule->ifname[0]) NLA_PUT_STRING(skb, FRA_IFNAME, rule->ifname); @@ -383,6 +462,9 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, if (rule->mark_mask || rule->mark) NLA_PUT_U32(skb, FRA_FWMASK, rule->mark_mask); + if (rule->target) + NLA_PUT_U32(skb, FRA_GOTO, rule->target); + if (ops->fill(rule, skb, nlh, frh) < 0) goto nla_put_failure; -- cgit v1.2.3 From 73417f617a93cf30342c3ea41abc38927bd467aa Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 27 Mar 2007 13:56:52 -0700 Subject: [NET] fib_rules: Flush route cache after rule modifications The results of FIB rules lookups are cached in the routing cache except for IPv6 as no such cache exists. So far, it was the responsibility of the user to flush the cache after modifying any rules. This lead to many false bug reports due to misunderstanding of this concept. This patch automatically flushes the route cache after inserting or deleting a rule. Thanks to Muli Ben-Yehuda for catching a bug in the previous patch. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/net/fib_rules.h | 4 ++++ net/core/fib_rules.c | 8 ++++++++ net/decnet/dn_rules.c | 7 +++++++ net/ipv4/fib_rules.c | 6 ++++++ 4 files changed, 25 insertions(+) (limited to 'include/net/fib_rules.h') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 08bab8b6e575..ed3a8872c6ca 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -59,6 +59,10 @@ struct fib_rules_ops u32 (*default_pref)(void); size_t (*nlmsg_payload)(struct fib_rule *); + /* Called after modifications to the rules set, must flush + * the route cache if one exists. */ + void (*flush_cache)(void); + int nlgroup; struct nla_policy *policy; struct list_head *rules_list; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 5824b2644f26..cb2dae19531b 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -44,6 +44,12 @@ static void rules_ops_put(struct fib_rules_ops *ops) module_put(ops->owner); } +static void flush_route_cache(struct fib_rules_ops *ops) +{ + if (ops->flush_cache) + ops->flush_cache(); +} + int fib_rules_register(struct fib_rules_ops *ops) { int err = -EEXIST; @@ -314,6 +320,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) list_add_rcu(&rule->list, ops->rules_list); notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid); + flush_route_cache(ops); rules_ops_put(ops); return 0; @@ -404,6 +411,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).pid); fib_rule_put(rule); + flush_route_cache(ops); rules_ops_put(ops); return 0; } diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index fd0cc2aa316c..7f58b95b27d1 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -31,6 +31,7 @@ #include #include #include +#include static struct fib_rules_ops dn_fib_rules_ops; @@ -239,6 +240,11 @@ static u32 dn_fib_rule_default_pref(void) return 0; } +static void dn_fib_rule_flush_cache(void) +{ + dn_rt_cache_flush(0); +} + static struct fib_rules_ops dn_fib_rules_ops = { .family = AF_DECnet, .rule_size = sizeof(struct dn_fib_rule), @@ -249,6 +255,7 @@ static struct fib_rules_ops dn_fib_rules_ops = { .compare = dn_fib_rule_compare, .fill = dn_fib_rule_fill, .default_pref = dn_fib_rule_default_pref, + .flush_cache = dn_fib_rule_flush_cache, .nlgroup = RTNLGRP_DECnet_RULE, .policy = dn_fib_rule_policy, .rules_list = &dn_fib_rules, diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index b021b3440ca3..fe29b98d6c8f 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -298,6 +298,11 @@ static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule) + nla_total_size(4); /* flow */ } +static void fib4_rule_flush_cache(void) +{ + rt_cache_flush(0); +} + static struct fib_rules_ops fib4_rules_ops = { .family = AF_INET, .rule_size = sizeof(struct fib4_rule), @@ -309,6 +314,7 @@ static struct fib_rules_ops fib4_rules_ops = { .fill = fib4_rule_fill, .default_pref = fib4_rule_default_pref, .nlmsg_payload = fib4_rule_nlmsg_payload, + .flush_cache = fib4_rule_flush_cache, .nlgroup = RTNLGRP_IPV4_RULE, .policy = fib4_rule_policy, .rules_list = &fib4_rules, -- cgit v1.2.3