aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fib_rules.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/fib_rules.c')
-rw-r--r--net/ipv4/fib_rules.c113
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
53struct fib_rule 55struct 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
80static struct fib_rule default_rule = { 83static struct fib_rule default_rule = {
@@ -85,7 +88,6 @@ static struct fib_rule default_rule = {
85}; 88};
86 89
87static struct fib_rule main_rule = { 90static 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
95static struct fib_rule local_rule = { 97static 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
102static struct fib_rule *fib_rules = &local_rule; 103struct hlist_head fib_rules;
103static DEFINE_RWLOCK(fib_rules_lock); 104
105/* writer func called from netlink -- rtnl_sem hold*/
104 106
105int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 107int 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
154static 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
153void fib_rule_put(struct fib_rule *r) 160void 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
163int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 172int 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
258static void fib_rules_detach(struct net_device *dev) 270static 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
271static void fib_rules_attach(struct net_device *dev) 284static 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
293FRprintk("Lookup: %u.%u.%u.%u <- %u.%u.%u.%u ", 305FRprintk("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 }
338FRprintk("FAILURE\n"); 352FRprintk("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
417int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) 433int 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
436void __init fib_rules_init(void) 455void __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}