diff options
Diffstat (limited to 'net/ipv4/fib_rules.c')
-rw-r--r-- | net/ipv4/fib_rules.c | 113 |
1 files changed, 68 insertions, 45 deletions
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 0dd4d06e456d..7b462a39aa4d 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c | |||
@@ -40,6 +40,8 @@ | |||
40 | #include <linux/skbuff.h> | 40 | #include <linux/skbuff.h> |
41 | #include <linux/netlink.h> | 41 | #include <linux/netlink.h> |
42 | #include <linux/init.h> | 42 | #include <linux/init.h> |
43 | #include <linux/list.h> | ||
44 | #include <linux/rcupdate.h> | ||
43 | 45 | ||
44 | #include <net/ip.h> | 46 | #include <net/ip.h> |
45 | #include <net/protocol.h> | 47 | #include <net/protocol.h> |
@@ -52,7 +54,7 @@ | |||
52 | 54 | ||
53 | struct fib_rule | 55 | struct fib_rule |
54 | { | 56 | { |
55 | struct fib_rule *r_next; | 57 | struct hlist_node hlist; |
56 | atomic_t r_clntref; | 58 | atomic_t r_clntref; |
57 | u32 r_preference; | 59 | u32 r_preference; |
58 | unsigned char r_table; | 60 | unsigned char r_table; |
@@ -75,6 +77,7 @@ struct fib_rule | |||
75 | #endif | 77 | #endif |
76 | char r_ifname[IFNAMSIZ]; | 78 | char r_ifname[IFNAMSIZ]; |
77 | int r_dead; | 79 | int r_dead; |
80 | struct rcu_head rcu; | ||
78 | }; | 81 | }; |
79 | 82 | ||
80 | static struct fib_rule default_rule = { | 83 | static struct fib_rule default_rule = { |
@@ -85,7 +88,6 @@ static struct fib_rule default_rule = { | |||
85 | }; | 88 | }; |
86 | 89 | ||
87 | static struct fib_rule main_rule = { | 90 | static struct fib_rule main_rule = { |
88 | .r_next = &default_rule, | ||
89 | .r_clntref = ATOMIC_INIT(2), | 91 | .r_clntref = ATOMIC_INIT(2), |
90 | .r_preference = 0x7FFE, | 92 | .r_preference = 0x7FFE, |
91 | .r_table = RT_TABLE_MAIN, | 93 | .r_table = RT_TABLE_MAIN, |
@@ -93,23 +95,24 @@ static struct fib_rule main_rule = { | |||
93 | }; | 95 | }; |
94 | 96 | ||
95 | static struct fib_rule local_rule = { | 97 | static struct fib_rule local_rule = { |
96 | .r_next = &main_rule, | ||
97 | .r_clntref = ATOMIC_INIT(2), | 98 | .r_clntref = ATOMIC_INIT(2), |
98 | .r_table = RT_TABLE_LOCAL, | 99 | .r_table = RT_TABLE_LOCAL, |
99 | .r_action = RTN_UNICAST, | 100 | .r_action = RTN_UNICAST, |
100 | }; | 101 | }; |
101 | 102 | ||
102 | static struct fib_rule *fib_rules = &local_rule; | 103 | struct hlist_head fib_rules; |
103 | static DEFINE_RWLOCK(fib_rules_lock); | 104 | |
105 | /* writer func called from netlink -- rtnl_sem hold*/ | ||
104 | 106 | ||
105 | int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | 107 | int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) |
106 | { | 108 | { |
107 | struct rtattr **rta = arg; | 109 | struct rtattr **rta = arg; |
108 | struct rtmsg *rtm = NLMSG_DATA(nlh); | 110 | struct rtmsg *rtm = NLMSG_DATA(nlh); |
109 | struct fib_rule *r, **rp; | 111 | struct fib_rule *r; |
112 | struct hlist_node *node; | ||
110 | int err = -ESRCH; | 113 | int err = -ESRCH; |
111 | 114 | ||
112 | for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) { | 115 | hlist_for_each_entry(r, node, &fib_rules, hlist) { |
113 | if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) && | 116 | if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) && |
114 | rtm->rtm_src_len == r->r_src_len && | 117 | rtm->rtm_src_len == r->r_src_len && |
115 | rtm->rtm_dst_len == r->r_dst_len && | 118 | rtm->rtm_dst_len == r->r_dst_len && |
@@ -126,10 +129,8 @@ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
126 | if (r == &local_rule) | 129 | if (r == &local_rule) |
127 | break; | 130 | break; |
128 | 131 | ||
129 | write_lock_bh(&fib_rules_lock); | 132 | hlist_del_rcu(&r->hlist); |
130 | *rp = r->r_next; | ||
131 | r->r_dead = 1; | 133 | r->r_dead = 1; |
132 | write_unlock_bh(&fib_rules_lock); | ||
133 | fib_rule_put(r); | 134 | fib_rule_put(r); |
134 | err = 0; | 135 | err = 0; |
135 | break; | 136 | break; |
@@ -150,21 +151,30 @@ static struct fib_table *fib_empty_table(void) | |||
150 | return NULL; | 151 | return NULL; |
151 | } | 152 | } |
152 | 153 | ||
154 | static inline void fib_rule_put_rcu(struct rcu_head *head) | ||
155 | { | ||
156 | struct fib_rule *r = container_of(head, struct fib_rule, rcu); | ||
157 | kfree(r); | ||
158 | } | ||
159 | |||
153 | void fib_rule_put(struct fib_rule *r) | 160 | void fib_rule_put(struct fib_rule *r) |
154 | { | 161 | { |
155 | if (atomic_dec_and_test(&r->r_clntref)) { | 162 | if (atomic_dec_and_test(&r->r_clntref)) { |
156 | if (r->r_dead) | 163 | if (r->r_dead) |
157 | kfree(r); | 164 | call_rcu(&r->rcu, fib_rule_put_rcu); |
158 | else | 165 | else |
159 | printk("Freeing alive rule %p\n", r); | 166 | printk("Freeing alive rule %p\n", r); |
160 | } | 167 | } |
161 | } | 168 | } |
162 | 169 | ||
170 | /* writer func called from netlink -- rtnl_sem hold*/ | ||
171 | |||
163 | int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | 172 | int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) |
164 | { | 173 | { |
165 | struct rtattr **rta = arg; | 174 | struct rtattr **rta = arg; |
166 | struct rtmsg *rtm = NLMSG_DATA(nlh); | 175 | struct rtmsg *rtm = NLMSG_DATA(nlh); |
167 | struct fib_rule *r, *new_r, **rp; | 176 | struct fib_rule *r, *new_r, *last = NULL; |
177 | struct hlist_node *node = NULL; | ||
168 | unsigned char table_id; | 178 | unsigned char table_id; |
169 | 179 | ||
170 | if (rtm->rtm_src_len > 32 || rtm->rtm_dst_len > 32 || | 180 | if (rtm->rtm_src_len > 32 || rtm->rtm_dst_len > 32 || |
@@ -188,6 +198,7 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
188 | if (!new_r) | 198 | if (!new_r) |
189 | return -ENOMEM; | 199 | return -ENOMEM; |
190 | memset(new_r, 0, sizeof(*new_r)); | 200 | memset(new_r, 0, sizeof(*new_r)); |
201 | |||
191 | if (rta[RTA_SRC-1]) | 202 | if (rta[RTA_SRC-1]) |
192 | memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4); | 203 | memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4); |
193 | if (rta[RTA_DST-1]) | 204 | if (rta[RTA_DST-1]) |
@@ -220,28 +231,28 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
220 | if (rta[RTA_FLOW-1]) | 231 | if (rta[RTA_FLOW-1]) |
221 | memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4); | 232 | memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4); |
222 | #endif | 233 | #endif |
234 | r = container_of(fib_rules.first, struct fib_rule, hlist); | ||
223 | 235 | ||
224 | rp = &fib_rules; | ||
225 | if (!new_r->r_preference) { | 236 | if (!new_r->r_preference) { |
226 | r = fib_rules; | 237 | if (r && r->hlist.next != NULL) { |
227 | if (r && (r = r->r_next) != NULL) { | 238 | r = container_of(r->hlist.next, struct fib_rule, hlist); |
228 | rp = &fib_rules->r_next; | ||
229 | if (r->r_preference) | 239 | if (r->r_preference) |
230 | new_r->r_preference = r->r_preference - 1; | 240 | new_r->r_preference = r->r_preference - 1; |
231 | } | 241 | } |
232 | } | 242 | } |
233 | 243 | ||
234 | while ( (r = *rp) != NULL ) { | 244 | hlist_for_each_entry(r, node, &fib_rules, hlist) { |
235 | if (r->r_preference > new_r->r_preference) | 245 | if (r->r_preference > new_r->r_preference) |
236 | break; | 246 | break; |
237 | rp = &r->r_next; | 247 | last = r; |
238 | } | 248 | } |
239 | |||
240 | new_r->r_next = r; | ||
241 | atomic_inc(&new_r->r_clntref); | 249 | atomic_inc(&new_r->r_clntref); |
242 | write_lock_bh(&fib_rules_lock); | 250 | |
243 | *rp = new_r; | 251 | if (last) |
244 | write_unlock_bh(&fib_rules_lock); | 252 | hlist_add_after_rcu(&last->hlist, &new_r->hlist); |
253 | else | ||
254 | hlist_add_before_rcu(&new_r->hlist, &r->hlist); | ||
255 | |||
245 | return 0; | 256 | return 0; |
246 | } | 257 | } |
247 | 258 | ||
@@ -254,30 +265,30 @@ u32 fib_rules_tclass(struct fib_result *res) | |||
254 | } | 265 | } |
255 | #endif | 266 | #endif |
256 | 267 | ||
268 | /* callers should hold rtnl semaphore */ | ||
257 | 269 | ||
258 | static void fib_rules_detach(struct net_device *dev) | 270 | static void fib_rules_detach(struct net_device *dev) |
259 | { | 271 | { |
272 | struct hlist_node *node; | ||
260 | struct fib_rule *r; | 273 | struct fib_rule *r; |
261 | 274 | ||
262 | for (r=fib_rules; r; r=r->r_next) { | 275 | hlist_for_each_entry(r, node, &fib_rules, hlist) { |
263 | if (r->r_ifindex == dev->ifindex) { | 276 | if (r->r_ifindex == dev->ifindex) |
264 | write_lock_bh(&fib_rules_lock); | ||
265 | r->r_ifindex = -1; | 277 | r->r_ifindex = -1; |
266 | write_unlock_bh(&fib_rules_lock); | 278 | |
267 | } | ||
268 | } | 279 | } |
269 | } | 280 | } |
270 | 281 | ||
282 | /* callers should hold rtnl semaphore */ | ||
283 | |||
271 | static void fib_rules_attach(struct net_device *dev) | 284 | static void fib_rules_attach(struct net_device *dev) |
272 | { | 285 | { |
286 | struct hlist_node *node; | ||
273 | struct fib_rule *r; | 287 | struct fib_rule *r; |
274 | 288 | ||
275 | for (r=fib_rules; r; r=r->r_next) { | 289 | hlist_for_each_entry(r, node, &fib_rules, hlist) { |
276 | if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) { | 290 | if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) |
277 | write_lock_bh(&fib_rules_lock); | ||
278 | r->r_ifindex = dev->ifindex; | 291 | r->r_ifindex = dev->ifindex; |
279 | write_unlock_bh(&fib_rules_lock); | ||
280 | } | ||
281 | } | 292 | } |
282 | } | 293 | } |
283 | 294 | ||
@@ -286,14 +297,17 @@ int fib_lookup(const struct flowi *flp, struct fib_result *res) | |||
286 | int err; | 297 | int err; |
287 | struct fib_rule *r, *policy; | 298 | struct fib_rule *r, *policy; |
288 | struct fib_table *tb; | 299 | struct fib_table *tb; |
300 | struct hlist_node *node; | ||
289 | 301 | ||
290 | u32 daddr = flp->fl4_dst; | 302 | u32 daddr = flp->fl4_dst; |
291 | u32 saddr = flp->fl4_src; | 303 | u32 saddr = flp->fl4_src; |
292 | 304 | ||
293 | FRprintk("Lookup: %u.%u.%u.%u <- %u.%u.%u.%u ", | 305 | FRprintk("Lookup: %u.%u.%u.%u <- %u.%u.%u.%u ", |
294 | NIPQUAD(flp->fl4_dst), NIPQUAD(flp->fl4_src)); | 306 | NIPQUAD(flp->fl4_dst), NIPQUAD(flp->fl4_src)); |
295 | read_lock(&fib_rules_lock); | 307 | |
296 | for (r = fib_rules; r; r=r->r_next) { | 308 | rcu_read_lock(); |
309 | |||
310 | hlist_for_each_entry_rcu(r, node, &fib_rules, hlist) { | ||
297 | if (((saddr^r->r_src) & r->r_srcmask) || | 311 | if (((saddr^r->r_src) & r->r_srcmask) || |
298 | ((daddr^r->r_dst) & r->r_dstmask) || | 312 | ((daddr^r->r_dst) & r->r_dstmask) || |
299 | (r->r_tos && r->r_tos != flp->fl4_tos) || | 313 | (r->r_tos && r->r_tos != flp->fl4_tos) || |
@@ -309,14 +323,14 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action); | |||
309 | policy = r; | 323 | policy = r; |
310 | break; | 324 | break; |
311 | case RTN_UNREACHABLE: | 325 | case RTN_UNREACHABLE: |
312 | read_unlock(&fib_rules_lock); | 326 | rcu_read_unlock(); |
313 | return -ENETUNREACH; | 327 | return -ENETUNREACH; |
314 | default: | 328 | default: |
315 | case RTN_BLACKHOLE: | 329 | case RTN_BLACKHOLE: |
316 | read_unlock(&fib_rules_lock); | 330 | rcu_read_unlock(); |
317 | return -EINVAL; | 331 | return -EINVAL; |
318 | case RTN_PROHIBIT: | 332 | case RTN_PROHIBIT: |
319 | read_unlock(&fib_rules_lock); | 333 | rcu_read_unlock(); |
320 | return -EACCES; | 334 | return -EACCES; |
321 | } | 335 | } |
322 | 336 | ||
@@ -327,16 +341,16 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action); | |||
327 | res->r = policy; | 341 | res->r = policy; |
328 | if (policy) | 342 | if (policy) |
329 | atomic_inc(&policy->r_clntref); | 343 | atomic_inc(&policy->r_clntref); |
330 | read_unlock(&fib_rules_lock); | 344 | rcu_read_unlock(); |
331 | return 0; | 345 | return 0; |
332 | } | 346 | } |
333 | if (err < 0 && err != -EAGAIN) { | 347 | if (err < 0 && err != -EAGAIN) { |
334 | read_unlock(&fib_rules_lock); | 348 | rcu_read_unlock(); |
335 | return err; | 349 | return err; |
336 | } | 350 | } |
337 | } | 351 | } |
338 | FRprintk("FAILURE\n"); | 352 | FRprintk("FAILURE\n"); |
339 | read_unlock(&fib_rules_lock); | 353 | rcu_read_unlock(); |
340 | return -ENETUNREACH; | 354 | return -ENETUNREACH; |
341 | } | 355 | } |
342 | 356 | ||
@@ -414,20 +428,25 @@ rtattr_failure: | |||
414 | return -1; | 428 | return -1; |
415 | } | 429 | } |
416 | 430 | ||
431 | /* callers should hold rtnl semaphore */ | ||
432 | |||
417 | int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) | 433 | int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) |
418 | { | 434 | { |
419 | int idx; | 435 | int idx = 0; |
420 | int s_idx = cb->args[0]; | 436 | int s_idx = cb->args[0]; |
421 | struct fib_rule *r; | 437 | struct fib_rule *r; |
438 | struct hlist_node *node; | ||
439 | |||
440 | rcu_read_lock(); | ||
441 | hlist_for_each_entry(r, node, &fib_rules, hlist) { | ||
422 | 442 | ||
423 | read_lock(&fib_rules_lock); | ||
424 | for (r=fib_rules, idx=0; r; r = r->r_next, idx++) { | ||
425 | if (idx < s_idx) | 443 | if (idx < s_idx) |
426 | continue; | 444 | continue; |
427 | if (inet_fill_rule(skb, r, cb, NLM_F_MULTI) < 0) | 445 | if (inet_fill_rule(skb, r, cb, NLM_F_MULTI) < 0) |
428 | break; | 446 | break; |
447 | idx++; | ||
429 | } | 448 | } |
430 | read_unlock(&fib_rules_lock); | 449 | rcu_read_unlock(); |
431 | cb->args[0] = idx; | 450 | cb->args[0] = idx; |
432 | 451 | ||
433 | return skb->len; | 452 | return skb->len; |
@@ -435,5 +454,9 @@ int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) | |||
435 | 454 | ||
436 | void __init fib_rules_init(void) | 455 | void __init fib_rules_init(void) |
437 | { | 456 | { |
457 | INIT_HLIST_HEAD(&fib_rules); | ||
458 | hlist_add_head(&local_rule.hlist, &fib_rules); | ||
459 | hlist_add_after(&local_rule.hlist, &main_rule.hlist); | ||
460 | hlist_add_after(&main_rule.hlist, &default_rule.hlist); | ||
438 | register_netdevice_notifier(&fib_rules_notifier); | 461 | register_netdevice_notifier(&fib_rules_notifier); |
439 | } | 462 | } |