aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Graf <tgraf@suug.ch>2007-03-26 20:14:15 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-04-26 01:28:12 -0400
commit0947c9fe56d9cf7ad0bc3a03ccd30446cde698e4 (patch)
tree10d40d8d536d686b8226907f942b064c4bc59a4b
parent2f7826c02447480c7c1b5500b34fc783f1ed8145 (diff)
[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 <tgraf@suug.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/fib_rules.h5
-rw-r--r--include/net/fib_rules.h7
-rw-r--r--net/core/fib_rules.c88
3 files changed, 94 insertions, 6 deletions
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 @@
7/* rule is permanent, and cannot be deleted */ 7/* rule is permanent, and cannot be deleted */
8#define FIB_RULE_PERMANENT 1 8#define FIB_RULE_PERMANENT 1
9#define FIB_RULE_INVERT 2 9#define FIB_RULE_INVERT 2
10#define FIB_RULE_UNRESOLVED 4
10 11
11struct fib_rule_hdr 12struct fib_rule_hdr
12{ 13{
@@ -29,7 +30,7 @@ enum
29 FRA_DST, /* destination address */ 30 FRA_DST, /* destination address */
30 FRA_SRC, /* source address */ 31 FRA_SRC, /* source address */
31 FRA_IFNAME, /* interface name */ 32 FRA_IFNAME, /* interface name */
32 FRA_UNUSED1, 33 FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */
33 FRA_UNUSED2, 34 FRA_UNUSED2,
34 FRA_PRIORITY, /* priority/preference */ 35 FRA_PRIORITY, /* priority/preference */
35 FRA_UNUSED3, 36 FRA_UNUSED3,
@@ -51,7 +52,7 @@ enum
51{ 52{
52 FR_ACT_UNSPEC, 53 FR_ACT_UNSPEC,
53 FR_ACT_TO_TBL, /* Pass to fixed table */ 54 FR_ACT_TO_TBL, /* Pass to fixed table */
54 FR_ACT_RES1, 55 FR_ACT_GOTO, /* Jump to another rule */
55 FR_ACT_RES2, 56 FR_ACT_RES2,
56 FR_ACT_RES3, 57 FR_ACT_RES3,
57 FR_ACT_RES4, 58 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
19 u32 flags; 19 u32 flags;
20 u32 table; 20 u32 table;
21 u8 action; 21 u8 action;
22 u32 target;
23 struct fib_rule * ctarget;
22 struct rcu_head rcu; 24 struct rcu_head rcu;
23}; 25};
24 26
@@ -35,6 +37,8 @@ struct fib_rules_ops
35 struct list_head list; 37 struct list_head list;
36 int rule_size; 38 int rule_size;
37 int addr_size; 39 int addr_size;
40 int unresolved_rules;
41 int nr_goto_rules;
38 42
39 int (*action)(struct fib_rule *, 43 int (*action)(struct fib_rule *,
40 struct flowi *, int, 44 struct flowi *, int,
@@ -66,7 +70,8 @@ struct fib_rules_ops
66 [FRA_PRIORITY] = { .type = NLA_U32 }, \ 70 [FRA_PRIORITY] = { .type = NLA_U32 }, \
67 [FRA_FWMARK] = { .type = NLA_U32 }, \ 71 [FRA_FWMARK] = { .type = NLA_U32 }, \
68 [FRA_FWMASK] = { .type = NLA_U32 }, \ 72 [FRA_FWMASK] = { .type = NLA_U32 }, \
69 [FRA_TABLE] = { .type = NLA_U32 } 73 [FRA_TABLE] = { .type = NLA_U32 }, \
74 [FRA_GOTO] = { .type = NLA_U32 }
70 75
71static inline void fib_rule_get(struct fib_rule *rule) 76static inline void fib_rule_get(struct fib_rule *rule)
72{ 77{
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,
132 rcu_read_lock(); 132 rcu_read_lock();
133 133
134 list_for_each_entry_rcu(rule, ops->rules_list, list) { 134 list_for_each_entry_rcu(rule, ops->rules_list, list) {
135jumped:
135 if (!fib_rule_match(rule, ops, fl, flags)) 136 if (!fib_rule_match(rule, ops, fl, flags))
136 continue; 137 continue;
137 138
138 err = ops->action(rule, fl, flags, arg); 139 if (rule->action == FR_ACT_GOTO) {
140 struct fib_rule *target;
141
142 target = rcu_dereference(rule->ctarget);
143 if (target == NULL) {
144 continue;
145 } else {
146 rule = target;
147 goto jumped;
148 }
149 } else
150 err = ops->action(rule, fl, flags, arg);
151
139 if (err != -EAGAIN) { 152 if (err != -EAGAIN) {
140 fib_rule_get(rule); 153 fib_rule_get(rule);
141 arg->rule = rule; 154 arg->rule = rule;
@@ -180,7 +193,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
180 struct fib_rules_ops *ops = NULL; 193 struct fib_rules_ops *ops = NULL;
181 struct fib_rule *rule, *r, *last = NULL; 194 struct fib_rule *rule, *r, *last = NULL;
182 struct nlattr *tb[FRA_MAX+1]; 195 struct nlattr *tb[FRA_MAX+1];
183 int err = -EINVAL; 196 int err = -EINVAL, unresolved = 0;
184 197
185 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) 198 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
186 goto errout; 199 goto errout;
@@ -237,6 +250,28 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
237 if (!rule->pref && ops->default_pref) 250 if (!rule->pref && ops->default_pref)
238 rule->pref = ops->default_pref(); 251 rule->pref = ops->default_pref();
239 252
253 err = -EINVAL;
254 if (tb[FRA_GOTO]) {
255 if (rule->action != FR_ACT_GOTO)
256 goto errout_free;
257
258 rule->target = nla_get_u32(tb[FRA_GOTO]);
259 /* Backward jumps are prohibited to avoid endless loops */
260 if (rule->target <= rule->pref)
261 goto errout_free;
262
263 list_for_each_entry(r, ops->rules_list, list) {
264 if (r->pref == rule->target) {
265 rule->ctarget = r;
266 break;
267 }
268 }
269
270 if (rule->ctarget == NULL)
271 unresolved = 1;
272 } else if (rule->action == FR_ACT_GOTO)
273 goto errout_free;
274
240 err = ops->configure(rule, skb, nlh, frh, tb); 275 err = ops->configure(rule, skb, nlh, frh, tb);
241 if (err < 0) 276 if (err < 0)
242 goto errout_free; 277 goto errout_free;
@@ -249,6 +284,28 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
249 284
250 fib_rule_get(rule); 285 fib_rule_get(rule);
251 286
287 if (ops->unresolved_rules) {
288 /*
289 * There are unresolved goto rules in the list, check if
290 * any of them are pointing to this new rule.
291 */
292 list_for_each_entry(r, ops->rules_list, list) {
293 if (r->action == FR_ACT_GOTO &&
294 r->target == rule->pref) {
295 BUG_ON(r->ctarget != NULL);
296 rcu_assign_pointer(r->ctarget, rule);
297 if (--ops->unresolved_rules == 0)
298 break;
299 }
300 }
301 }
302
303 if (rule->action == FR_ACT_GOTO)
304 ops->nr_goto_rules++;
305
306 if (unresolved)
307 ops->unresolved_rules++;
308
252 if (last) 309 if (last)
253 list_add_rcu(&rule->list, &last->list); 310 list_add_rcu(&rule->list, &last->list);
254 else 311 else
@@ -269,7 +326,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
269{ 326{
270 struct fib_rule_hdr *frh = nlmsg_data(nlh); 327 struct fib_rule_hdr *frh = nlmsg_data(nlh);
271 struct fib_rules_ops *ops = NULL; 328 struct fib_rules_ops *ops = NULL;
272 struct fib_rule *rule; 329 struct fib_rule *rule, *tmp;
273 struct nlattr *tb[FRA_MAX+1]; 330 struct nlattr *tb[FRA_MAX+1];
274 int err = -EINVAL; 331 int err = -EINVAL;
275 332
@@ -322,6 +379,25 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
322 } 379 }
323 380
324 list_del_rcu(&rule->list); 381 list_del_rcu(&rule->list);
382
383 if (rule->action == FR_ACT_GOTO)
384 ops->nr_goto_rules--;
385
386 /*
387 * Check if this rule is a target to any of them. If so,
388 * disable them. As this operation is eventually very
389 * expensive, it is only performed if goto rules have
390 * actually been added.
391 */
392 if (ops->nr_goto_rules > 0) {
393 list_for_each_entry(tmp, ops->rules_list, list) {
394 if (tmp->ctarget == rule) {
395 rcu_assign_pointer(tmp->ctarget, NULL);
396 ops->unresolved_rules++;
397 }
398 }
399 }
400
325 synchronize_rcu(); 401 synchronize_rcu();
326 notify_rule_change(RTM_DELRULE, rule, ops, nlh, 402 notify_rule_change(RTM_DELRULE, rule, ops, nlh,
327 NETLINK_CB(skb).pid); 403 NETLINK_CB(skb).pid);
@@ -371,6 +447,9 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
371 frh->action = rule->action; 447 frh->action = rule->action;
372 frh->flags = rule->flags; 448 frh->flags = rule->flags;
373 449
450 if (rule->action == FR_ACT_GOTO && rule->ctarget == NULL)
451 frh->flags |= FIB_RULE_UNRESOLVED;
452
374 if (rule->ifname[0]) 453 if (rule->ifname[0])
375 NLA_PUT_STRING(skb, FRA_IFNAME, rule->ifname); 454 NLA_PUT_STRING(skb, FRA_IFNAME, rule->ifname);
376 455
@@ -383,6 +462,9 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
383 if (rule->mark_mask || rule->mark) 462 if (rule->mark_mask || rule->mark)
384 NLA_PUT_U32(skb, FRA_FWMASK, rule->mark_mask); 463 NLA_PUT_U32(skb, FRA_FWMASK, rule->mark_mask);
385 464
465 if (rule->target)
466 NLA_PUT_U32(skb, FRA_GOTO, rule->target);
467
386 if (ops->fill(rule, skb, nlh, frh) < 0) 468 if (ops->fill(rule, skb, nlh, frh) < 0)
387 goto nla_put_failure; 469 goto nla_put_failure;
388 470