diff options
Diffstat (limited to 'net/core/fib_rules.c')
-rw-r--r-- | net/core/fib_rules.c | 71 |
1 files changed, 62 insertions, 9 deletions
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 6b0e63cacd93..1df6cd4568d3 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c | |||
@@ -107,6 +107,22 @@ out: | |||
107 | 107 | ||
108 | EXPORT_SYMBOL_GPL(fib_rules_unregister); | 108 | EXPORT_SYMBOL_GPL(fib_rules_unregister); |
109 | 109 | ||
110 | static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, | ||
111 | struct flowi *fl, int flags) | ||
112 | { | ||
113 | int ret = 0; | ||
114 | |||
115 | if (rule->ifindex && (rule->ifindex != fl->iif)) | ||
116 | goto out; | ||
117 | |||
118 | if ((rule->mark ^ fl->mark) & rule->mark_mask) | ||
119 | goto out; | ||
120 | |||
121 | ret = ops->match(rule, fl, flags); | ||
122 | out: | ||
123 | return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; | ||
124 | } | ||
125 | |||
110 | int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, | 126 | int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, |
111 | int flags, struct fib_lookup_arg *arg) | 127 | int flags, struct fib_lookup_arg *arg) |
112 | { | 128 | { |
@@ -116,10 +132,7 @@ int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, | |||
116 | rcu_read_lock(); | 132 | rcu_read_lock(); |
117 | 133 | ||
118 | list_for_each_entry_rcu(rule, ops->rules_list, list) { | 134 | list_for_each_entry_rcu(rule, ops->rules_list, list) { |
119 | if (rule->ifindex && (rule->ifindex != fl->iif)) | 135 | if (!fib_rule_match(rule, ops, fl, flags)) |
120 | continue; | ||
121 | |||
122 | if (!ops->match(rule, fl, flags)) | ||
123 | continue; | 136 | continue; |
124 | 137 | ||
125 | err = ops->action(rule, fl, flags, arg); | 138 | err = ops->action(rule, fl, flags, arg); |
@@ -179,6 +192,18 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
179 | rule->ifindex = dev->ifindex; | 192 | rule->ifindex = dev->ifindex; |
180 | } | 193 | } |
181 | 194 | ||
195 | if (tb[FRA_FWMARK]) { | ||
196 | rule->mark = nla_get_u32(tb[FRA_FWMARK]); | ||
197 | if (rule->mark) | ||
198 | /* compatibility: if the mark value is non-zero all bits | ||
199 | * are compared unless a mask is explicitly specified. | ||
200 | */ | ||
201 | rule->mark_mask = 0xFFFFFFFF; | ||
202 | } | ||
203 | |||
204 | if (tb[FRA_FWMASK]) | ||
205 | rule->mark_mask = nla_get_u32(tb[FRA_FWMASK]); | ||
206 | |||
182 | rule->action = frh->action; | 207 | rule->action = frh->action; |
183 | rule->flags = frh->flags; | 208 | rule->flags = frh->flags; |
184 | rule->table = frh_get_table(frh, tb); | 209 | rule->table = frh_get_table(frh, tb); |
@@ -250,6 +275,14 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
250 | nla_strcmp(tb[FRA_IFNAME], rule->ifname)) | 275 | nla_strcmp(tb[FRA_IFNAME], rule->ifname)) |
251 | continue; | 276 | continue; |
252 | 277 | ||
278 | if (tb[FRA_FWMARK] && | ||
279 | (rule->mark != nla_get_u32(tb[FRA_FWMARK]))) | ||
280 | continue; | ||
281 | |||
282 | if (tb[FRA_FWMASK] && | ||
283 | (rule->mark_mask != nla_get_u32(tb[FRA_FWMASK]))) | ||
284 | continue; | ||
285 | |||
253 | if (!ops->compare(rule, frh, tb)) | 286 | if (!ops->compare(rule, frh, tb)) |
254 | continue; | 287 | continue; |
255 | 288 | ||
@@ -273,6 +306,22 @@ errout: | |||
273 | return err; | 306 | return err; |
274 | } | 307 | } |
275 | 308 | ||
309 | static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, | ||
310 | struct fib_rule *rule) | ||
311 | { | ||
312 | size_t payload = NLMSG_ALIGN(sizeof(struct fib_rule_hdr)) | ||
313 | + nla_total_size(IFNAMSIZ) /* FRA_IFNAME */ | ||
314 | + nla_total_size(4) /* FRA_PRIORITY */ | ||
315 | + nla_total_size(4) /* FRA_TABLE */ | ||
316 | + nla_total_size(4) /* FRA_FWMARK */ | ||
317 | + nla_total_size(4); /* FRA_FWMASK */ | ||
318 | |||
319 | if (ops->nlmsg_payload) | ||
320 | payload += ops->nlmsg_payload(rule); | ||
321 | |||
322 | return payload; | ||
323 | } | ||
324 | |||
276 | static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, | 325 | static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, |
277 | u32 pid, u32 seq, int type, int flags, | 326 | u32 pid, u32 seq, int type, int flags, |
278 | struct fib_rules_ops *ops) | 327 | struct fib_rules_ops *ops) |
@@ -298,6 +347,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, | |||
298 | if (rule->pref) | 347 | if (rule->pref) |
299 | NLA_PUT_U32(skb, FRA_PRIORITY, rule->pref); | 348 | NLA_PUT_U32(skb, FRA_PRIORITY, rule->pref); |
300 | 349 | ||
350 | if (rule->mark) | ||
351 | NLA_PUT_U32(skb, FRA_FWMARK, rule->mark); | ||
352 | |||
353 | if (rule->mark_mask || rule->mark) | ||
354 | NLA_PUT_U32(skb, FRA_FWMASK, rule->mark_mask); | ||
355 | |||
301 | if (ops->fill(rule, skb, nlh, frh) < 0) | 356 | if (ops->fill(rule, skb, nlh, frh) < 0) |
302 | goto nla_put_failure; | 357 | goto nla_put_failure; |
303 | 358 | ||
@@ -345,15 +400,13 @@ static void notify_rule_change(int event, struct fib_rule *rule, | |||
345 | struct sk_buff *skb; | 400 | struct sk_buff *skb; |
346 | int err = -ENOBUFS; | 401 | int err = -ENOBUFS; |
347 | 402 | ||
348 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | 403 | skb = nlmsg_new(fib_rule_nlmsg_size(ops, rule), GFP_KERNEL); |
349 | if (skb == NULL) | 404 | if (skb == NULL) |
350 | goto errout; | 405 | goto errout; |
351 | 406 | ||
352 | err = fib_nl_fill_rule(skb, rule, pid, nlh->nlmsg_seq, event, 0, ops); | 407 | err = fib_nl_fill_rule(skb, rule, pid, nlh->nlmsg_seq, event, 0, ops); |
353 | if (err < 0) { | 408 | /* failure implies BUG in fib_rule_nlmsg_size() */ |
354 | kfree_skb(skb); | 409 | BUG_ON(err < 0); |
355 | goto errout; | ||
356 | } | ||
357 | 410 | ||
358 | err = rtnl_notify(skb, pid, ops->nlgroup, nlh, GFP_KERNEL); | 411 | err = rtnl_notify(skb, pid, ops->nlgroup, nlh, GFP_KERNEL); |
359 | errout: | 412 | errout: |