aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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