aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
Diffstat (limited to 'net/core')
-rw-r--r--net/core/fib_rules.c88
1 files changed, 85 insertions, 3 deletions
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