Blame view

net/ipv4/fib_semantics.c 29 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   * INET		An implementation of the TCP/IP protocol suite for the LINUX
   *		operating system.  INET is implemented using the  BSD Socket
   *		interface as the means of communication with the user level.
   *
   *		IPv4 Forwarding Information Base: semantics.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
22
23
24
25
26
27
  #include <asm/uaccess.h>
  #include <asm/system.h>
  #include <linux/bitops.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/jiffies.h>
  #include <linux/mm.h>
  #include <linux/string.h>
  #include <linux/socket.h>
  #include <linux/sockios.h>
  #include <linux/errno.h>
  #include <linux/in.h>
  #include <linux/inet.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
28
  #include <linux/inetdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
  #include <linux/netdevice.h>
  #include <linux/if_arp.h>
  #include <linux/proc_fs.h>
  #include <linux/skbuff.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
34
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35

14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
36
  #include <net/arp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
  #include <net/ip.h>
  #include <net/protocol.h>
  #include <net/route.h>
  #include <net/tcp.h>
  #include <net/sock.h>
  #include <net/ip_fib.h>
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
43
  #include <net/netlink.h>
4e902c574   Thomas Graf   [IPv4]: FIB confi...
44
  #include <net/nexthop.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
  
  #include "fib_lookup.h"
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
47
  static DEFINE_SPINLOCK(fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  static struct hlist_head *fib_info_hash;
  static struct hlist_head *fib_info_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
50
  static unsigned int fib_info_hash_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
58
59
  static unsigned int fib_info_cnt;
  
  #define DEVINDEX_HASHBITS 8
  #define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS)
  static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE];
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  
  static DEFINE_SPINLOCK(fib_multipath_lock);
6a31d2a97   Eric Dumazet   fib: cleanups
60
61
62
63
64
65
66
67
68
69
70
  #define for_nexthops(fi) {						\
  	int nhsel; const struct fib_nh *nh;				\
  	for (nhsel = 0, nh = (fi)->fib_nh;				\
  	     nhsel < (fi)->fib_nhs;					\
  	     nh++, nhsel++)
  
  #define change_nexthops(fi) {						\
  	int nhsel; struct fib_nh *nexthop_nh;				\
  	for (nhsel = 0,	nexthop_nh = (struct fib_nh *)((fi)->fib_nh);	\
  	     nhsel < (fi)->fib_nhs;					\
  	     nexthop_nh++, nhsel++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
  
  #else /* CONFIG_IP_ROUTE_MULTIPATH */
  
  /* Hope, that gcc will optimize it to get rid of dummy loop */
6a31d2a97   Eric Dumazet   fib: cleanups
75
76
77
  #define for_nexthops(fi) {						\
  	int nhsel; const struct fib_nh *nh = (fi)->fib_nh;		\
  	for (nhsel = 0; nhsel < 1; nhsel++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78

6a31d2a97   Eric Dumazet   fib: cleanups
79
80
81
82
  #define change_nexthops(fi) {						\
  	int nhsel;							\
  	struct fib_nh *nexthop_nh = (struct fib_nh *)((fi)->fib_nh);	\
  	for (nhsel = 0; nhsel < 1; nhsel++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
  
  #endif /* CONFIG_IP_ROUTE_MULTIPATH */
  
  #define endfor_nexthops(fi) }
3be0686b6   David S. Miller   ipv4: Inline fib_...
87
  const struct fib_prop fib_props[RTN_MAX + 1] = {
6a31d2a97   Eric Dumazet   fib: cleanups
88
  	[RTN_UNSPEC] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
  		.error	= 0,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
91
92
  	},
  	[RTN_UNICAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
  		.error	= 0,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
95
96
  	},
  	[RTN_LOCAL] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
  		.error	= 0,
  		.scope	= RT_SCOPE_HOST,
6a31d2a97   Eric Dumazet   fib: cleanups
99
100
  	},
  	[RTN_BROADCAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
  		.error	= 0,
  		.scope	= RT_SCOPE_LINK,
6a31d2a97   Eric Dumazet   fib: cleanups
103
104
  	},
  	[RTN_ANYCAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
  		.error	= 0,
  		.scope	= RT_SCOPE_LINK,
6a31d2a97   Eric Dumazet   fib: cleanups
107
108
  	},
  	[RTN_MULTICAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
  		.error	= 0,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
111
112
  	},
  	[RTN_BLACKHOLE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
115
116
  	},
  	[RTN_UNREACHABLE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
  		.error	= -EHOSTUNREACH,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
119
120
  	},
  	[RTN_PROHIBIT] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
  		.error	= -EACCES,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
123
124
  	},
  	[RTN_THROW] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
  		.error	= -EAGAIN,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
127
128
  	},
  	[RTN_NAT] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
131
132
  	},
  	[RTN_XRESOLVE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
135
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  /* Release a nexthop info record */
19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
138
139
140
141
142
143
144
145
  static void free_fib_info_rcu(struct rcu_head *head)
  {
  	struct fib_info *fi = container_of(head, struct fib_info, rcu);
  
  	if (fi->fib_metrics != (u32 *) dst_default_metrics)
  		kfree(fi->fib_metrics);
  	kfree(fi);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
  
  void free_fib_info(struct fib_info *fi)
  {
  	if (fi->fib_dead == 0) {
6a31d2a97   Eric Dumazet   fib: cleanups
150
151
  		pr_warning("Freeing alive fib_info %p
  ", fi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
  		return;
  	}
  	change_nexthops(fi) {
71fceff0e   David S. Miller   ipv4: Use less co...
155
156
157
  		if (nexthop_nh->nh_dev)
  			dev_put(nexthop_nh->nh_dev);
  		nexthop_nh->nh_dev = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
  	} endfor_nexthops(fi);
  	fib_info_cnt--;
57d7a6009   Denis V. Lunev   [NETNS]: Add netn...
160
  	release_net(fi->fib_net);
19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
161
  	call_rcu(&fi->rcu, free_fib_info_rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
  }
  
  void fib_release_info(struct fib_info *fi)
  {
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
166
  	spin_lock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
170
171
  	if (fi && --fi->fib_treeref == 0) {
  		hlist_del(&fi->fib_hash);
  		if (fi->fib_prefsrc)
  			hlist_del(&fi->fib_lhash);
  		change_nexthops(fi) {
71fceff0e   David S. Miller   ipv4: Use less co...
172
  			if (!nexthop_nh->nh_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  				continue;
71fceff0e   David S. Miller   ipv4: Use less co...
174
  			hlist_del(&nexthop_nh->nh_hash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
  		} endfor_nexthops(fi)
  		fi->fib_dead = 1;
  		fib_info_put(fi);
  	}
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
179
  	spin_unlock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  }
6a31d2a97   Eric Dumazet   fib: cleanups
181
  static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
186
187
188
189
190
191
  {
  	const struct fib_nh *onh = ofi->fib_nh;
  
  	for_nexthops(fi) {
  		if (nh->nh_oif != onh->nh_oif ||
  		    nh->nh_gw  != onh->nh_gw ||
  		    nh->nh_scope != onh->nh_scope ||
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  		    nh->nh_weight != onh->nh_weight ||
  #endif
c7066f70d   Patrick McHardy   netfilter: fix Kc...
192
  #ifdef CONFIG_IP_ROUTE_CLASSID
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
  		    nh->nh_tclassid != onh->nh_tclassid ||
  #endif
6a31d2a97   Eric Dumazet   fib: cleanups
195
  		    ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_F_DEAD))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
  			return -1;
  		onh++;
  	} endfor_nexthops(fi);
  	return 0;
  }
88ebc72f6   David S. Miller   [IPV4] FIB: Inclu...
201
202
203
204
205
206
207
208
  static inline unsigned int fib_devindex_hashfn(unsigned int val)
  {
  	unsigned int mask = DEVINDEX_HASHSIZE - 1;
  
  	return (val ^
  		(val >> DEVINDEX_HASHBITS) ^
  		(val >> (DEVINDEX_HASHBITS * 2))) & mask;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
  static inline unsigned int fib_info_hashfn(const struct fib_info *fi)
  {
123b9731b   David S. Miller   ipv4: Rename fib_...
211
  	unsigned int mask = (fib_info_hash_size - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  	unsigned int val = fi->fib_nhs;
37e826c51   David S. Miller   ipv4: Fix nexthop...
213
  	val ^= (fi->fib_protocol << 8) | fi->fib_scope;
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
214
  	val ^= (__force u32)fi->fib_prefsrc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  	val ^= fi->fib_priority;
88ebc72f6   David S. Miller   [IPV4] FIB: Inclu...
216
217
218
  	for_nexthops(fi) {
  		val ^= fib_devindex_hashfn(nh->nh_oif);
  	} endfor_nexthops(fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
  
  	return (val ^ (val >> 7) ^ (val >> 12)) & mask;
  }
  
  static struct fib_info *fib_find_info(const struct fib_info *nfi)
  {
  	struct hlist_head *head;
  	struct hlist_node *node;
  	struct fib_info *fi;
  	unsigned int hash;
  
  	hash = fib_info_hashfn(nfi);
  	head = &fib_info_hash[hash];
  
  	hlist_for_each_entry(fi, node, head, fib_hash) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
234
  		if (!net_eq(fi->fib_net, nfi->fib_net))
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
235
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
  		if (fi->fib_nhs != nfi->fib_nhs)
  			continue;
  		if (nfi->fib_protocol == fi->fib_protocol &&
37e826c51   David S. Miller   ipv4: Fix nexthop...
239
  		    nfi->fib_scope == fi->fib_scope &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
  		    nfi->fib_prefsrc == fi->fib_prefsrc &&
  		    nfi->fib_priority == fi->fib_priority &&
  		    memcmp(nfi->fib_metrics, fi->fib_metrics,
fcd13f42c   Eric Dumazet   ipv4: fix fib met...
243
  			   sizeof(u32) * RTAX_MAX) == 0 &&
6a31d2a97   Eric Dumazet   fib: cleanups
244
  		    ((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
250
  		    (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
  			return fi;
  	}
  
  	return NULL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  /* Check, that the gateway is already configured.
6a31d2a97   Eric Dumazet   fib: cleanups
252
   * Used only by redirect accept routine.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
   */
d878e72e4   Al Viro   [IPV4]: ip_fib_ch...
254
  int ip_fib_check_default(__be32 gw, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
  {
  	struct hlist_head *head;
  	struct hlist_node *node;
  	struct fib_nh *nh;
  	unsigned int hash;
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
260
  	spin_lock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
266
  
  	hash = fib_devindex_hashfn(dev->ifindex);
  	head = &fib_info_devhash[hash];
  	hlist_for_each_entry(nh, node, head, nh_hash) {
  		if (nh->nh_dev == dev &&
  		    nh->nh_gw == gw &&
6a31d2a97   Eric Dumazet   fib: cleanups
267
  		    !(nh->nh_flags & RTNH_F_DEAD)) {
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
268
  			spin_unlock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
  			return 0;
  		}
  	}
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
272
  	spin_unlock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
  
  	return -1;
  }
339bf98ff   Thomas Graf   [NETLINK]: Do pre...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  static inline size_t fib_nlmsg_size(struct fib_info *fi)
  {
  	size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg))
  			 + nla_total_size(4) /* RTA_TABLE */
  			 + nla_total_size(4) /* RTA_DST */
  			 + nla_total_size(4) /* RTA_PRIORITY */
  			 + nla_total_size(4); /* RTA_PREFSRC */
  
  	/* space for nested metrics */
  	payload += nla_total_size((RTAX_MAX * nla_total_size(4)));
  
  	if (fi->fib_nhs) {
  		/* Also handles the special case fib_nhs == 1 */
  
  		/* each nexthop is packed in an attribute */
  		size_t nhsize = nla_total_size(sizeof(struct rtnexthop));
  
  		/* may contain flow and gateway attribute */
  		nhsize += 2 * nla_total_size(4);
  
  		/* all nexthops are packed in a nested attribute */
  		payload += nla_total_size(fi->fib_nhs * nhsize);
  	}
  
  	return payload;
  }
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
302
  void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
b8f558313   Milan Kocian   [RTNETLINK]: Fix ...
303
304
  	       int dst_len, u32 tb_id, struct nl_info *info,
  	       unsigned int nlm_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
306
  {
  	struct sk_buff *skb;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
307
  	u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
308
  	int err = -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309

339bf98ff   Thomas Graf   [NETLINK]: Do pre...
310
  	skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL);
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
311
312
  	if (skb == NULL)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313

4e902c574   Thomas Graf   [IPv4]: FIB confi...
314
  	err = fib_dump_info(skb, info->pid, seq, event, tb_id,
37e826c51   David S. Miller   ipv4: Fix nexthop...
315
  			    fa->fa_type, key, dst_len,
b8f558313   Milan Kocian   [RTNETLINK]: Fix ...
316
  			    fa->fa_tos, fa->fa_info, nlm_flags);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
317
318
319
320
321
322
  	if (err < 0) {
  		/* -EMSGSIZE implies BUG in fib_nlmsg_size() */
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
1ce85fe40   Pablo Neira Ayuso   netlink: change n...
323
324
325
  	rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE,
  		    info->nlh, GFP_KERNEL);
  	return;
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
326
327
  errout:
  	if (err < 0)
4d1169c1e   Denis V. Lunev   [NETNS]: Add netn...
328
  		rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  }
  
  /* Return the first fib alias matching TOS with
   * priority less than or equal to PRIO.
   */
  struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio)
  {
  	if (fah) {
  		struct fib_alias *fa;
  		list_for_each_entry(fa, fah, fa_list) {
  			if (fa->fa_tos > tos)
  				continue;
  			if (fa->fa_info->fib_priority >= prio ||
  			    fa->fa_tos < tos)
  				return fa;
  		}
  	}
  	return NULL;
  }
  
  int fib_detect_death(struct fib_info *fi, int order,
c17860a03   Denis V. Lunev   [IPV4]: no need p...
350
  		     struct fib_info **last_resort, int *last_idx, int dflt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
356
357
358
359
  {
  	struct neighbour *n;
  	int state = NUD_NONE;
  
  	n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev);
  	if (n) {
  		state = n->nud_state;
  		neigh_release(n);
  	}
d9319100c   Jianjun Kong   net: clean up net...
360
  	if (state == NUD_REACHABLE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  		return 0;
6a31d2a97   Eric Dumazet   fib: cleanups
362
  	if ((state & NUD_VALID) && order != dflt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  		return 0;
6a31d2a97   Eric Dumazet   fib: cleanups
364
365
  	if ((state & NUD_VALID) ||
  	    (*last_idx < 0 && order > dflt)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
369
370
371
372
  		*last_resort = fi;
  		*last_idx = order;
  	}
  	return 1;
  }
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
373
  static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
  {
  	int nhs = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376

4e902c574   Thomas Graf   [IPv4]: FIB confi...
377
  	while (rtnh_ok(rtnh, remaining)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
  		nhs++;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
379
380
381
382
383
  		rtnh = rtnh_next(rtnh, &remaining);
  	}
  
  	/* leftover implies invalid nexthop configuration, discard it */
  	return remaining > 0 ? 0 : nhs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  }
4e902c574   Thomas Graf   [IPv4]: FIB confi...
385
386
  static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
  		       int remaining, struct fib_config *cfg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
  	change_nexthops(fi) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
389
390
391
  		int attrlen;
  
  		if (!rtnh_ok(rtnh, remaining))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  			return -EINVAL;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
393

71fceff0e   David S. Miller   ipv4: Use less co...
394
395
396
397
  		nexthop_nh->nh_flags =
  			(cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags;
  		nexthop_nh->nh_oif = rtnh->rtnh_ifindex;
  		nexthop_nh->nh_weight = rtnh->rtnh_hops + 1;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
398
399
400
401
402
403
  
  		attrlen = rtnh_attrlen(rtnh);
  		if (attrlen > 0) {
  			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
  
  			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
71fceff0e   David S. Miller   ipv4: Use less co...
404
  			nexthop_nh->nh_gw = nla ? nla_get_be32(nla) : 0;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
405
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
406
  			nla = nla_find(attrs, attrlen, RTA_FLOW);
71fceff0e   David S. Miller   ipv4: Use less co...
407
  			nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
  #endif
  		}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
410
411
  
  		rtnh = rtnh_next(rtnh, &remaining);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  	} endfor_nexthops(fi);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
413

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
  	return 0;
  }
  
  #endif
4e902c574   Thomas Graf   [IPv4]: FIB confi...
418
  int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
  {
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
421
422
  	struct rtnexthop *rtnh;
  	int remaining;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
  #endif
4e902c574   Thomas Graf   [IPv4]: FIB confi...
424
  	if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  		return 1;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
426
427
428
  	if (cfg->fc_oif || cfg->fc_gw) {
  		if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) &&
  		    (!cfg->fc_gw  || cfg->fc_gw == fi->fib_nh->nh_gw))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
433
  			return 0;
  		return 1;
  	}
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
434
  	if (cfg->fc_mp == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  		return 0;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
436
437
438
  
  	rtnh = cfg->fc_mp;
  	remaining = cfg->fc_mp_len;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
439

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
  	for_nexthops(fi) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
441
  		int attrlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442

4e902c574   Thomas Graf   [IPv4]: FIB confi...
443
  		if (!rtnh_ok(rtnh, remaining))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
  			return -EINVAL;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
445
446
  
  		if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->nh_oif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  			return 1;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
448
449
450
451
452
453
  
  		attrlen = rtnh_attrlen(rtnh);
  		if (attrlen < 0) {
  			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
  
  			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
17fb2c643   Al Viro   [IPV4]: RTA_{DST,...
454
  			if (nla && nla_get_be32(nla) != nh->nh_gw)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  				return 1;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
456
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
457
458
  			nla = nla_find(attrs, attrlen, RTA_FLOW);
  			if (nla && nla_get_u32(nla) != nh->nh_tclassid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
  				return 1;
  #endif
  		}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
462
463
  
  		rtnh = rtnh_next(rtnh, &remaining);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
467
468
469
470
  	} endfor_nexthops(fi);
  #endif
  	return 0;
  }
  
  
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
   * Picture
   * -------
   *
   * Semantics of nexthop is very messy by historical reasons.
   * We have to take into account, that:
   * a) gateway can be actually local interface address,
   *    so that gatewayed route is direct.
   * b) gateway must be on-link address, possibly
   *    described not by an ifaddr, but also by a direct route.
   * c) If both gateway and interface are specified, they should not
   *    contradict.
   * d) If we use tunnel routes, gateway could be not on-link.
   *
   * Attempt to reconcile all of these (alas, self-contradictory) conditions
   * results in pretty ugly and hairy code with obscure logic.
   *
   * I chose to generalized it instead, so that the size
   * of code does not increase practically, but it becomes
   * much more general.
   * Every prefix is assigned a "scope" value: "host" is local address,
   * "link" is direct route,
   * [ ... "site" ... "interior" ... ]
   * and "universe" is true gateway route with global meaning.
   *
   * Every prefix refers to a set of "nexthop"s (gw, oif),
   * where gw must have narrower scope. This recursion stops
   * when gw has LOCAL scope or if "nexthop" is declared ONLINK,
   * which means that gw is forced to be on link.
   *
   * Code is still hairy, but now it is apparently logically
   * consistent and very flexible. F.e. as by-product it allows
   * to co-exists in peace independent exterior and interior
   * routing processes.
   *
   * Normally it looks as following.
   *
   * {universe prefix}  -> (gw, oif) [scope link]
   *		  |
   *		  |-> {link prefix} -> (gw, oif) [scope local]
   *					|
   *					|-> {local prefix} (terminal node)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
   */
4e902c574   Thomas Graf   [IPv4]: FIB confi...
513
514
  static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
  			struct fib_nh *nh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
  {
  	int err;
86167a377   Denis V. Lunev   [NETNS]: Pass cor...
517
  	struct net *net;
6a31d2a97   Eric Dumazet   fib: cleanups
518
  	struct net_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519

86167a377   Denis V. Lunev   [NETNS]: Pass cor...
520
  	net = cfg->fc_nlinfo.nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
  	if (nh->nh_gw) {
  		struct fib_result res;
6a31d2a97   Eric Dumazet   fib: cleanups
523
  		if (nh->nh_flags & RTNH_F_ONLINK) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524

4e902c574   Thomas Graf   [IPv4]: FIB confi...
525
  			if (cfg->fc_scope >= RT_SCOPE_LINK)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  				return -EINVAL;
86167a377   Denis V. Lunev   [NETNS]: Pass cor...
527
  			if (inet_addr_type(net, nh->nh_gw) != RTN_UNICAST)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  				return -EINVAL;
6a31d2a97   Eric Dumazet   fib: cleanups
529
530
  			dev = __dev_get_by_index(net, nh->nh_oif);
  			if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
  				return -ENODEV;
6a31d2a97   Eric Dumazet   fib: cleanups
532
  			if (!(dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
537
538
  				return -ENETDOWN;
  			nh->nh_dev = dev;
  			dev_hold(dev);
  			nh->nh_scope = RT_SCOPE_LINK;
  			return 0;
  		}
ebc0ffae5   Eric Dumazet   fib: RCU conversi...
539
  		rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
  		{
9ade22861   David S. Miller   ipv4: Use flowi4 ...
541
542
543
544
  			struct flowi4 fl4 = {
  				.daddr = nh->nh_gw,
  				.flowi4_scope = cfg->fc_scope + 1,
  				.flowi4_oif = nh->nh_oif,
4e902c574   Thomas Graf   [IPv4]: FIB confi...
545
  			};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
  
  			/* It is not necessary, but requires a bit of thinking */
9ade22861   David S. Miller   ipv4: Use flowi4 ...
548
549
550
  			if (fl4.flowi4_scope < RT_SCOPE_LINK)
  				fl4.flowi4_scope = RT_SCOPE_LINK;
  			err = fib_lookup(net, &fl4, &res);
ebc0ffae5   Eric Dumazet   fib: RCU conversi...
551
552
  			if (err) {
  				rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
  				return err;
ebc0ffae5   Eric Dumazet   fib: RCU conversi...
554
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
557
558
559
560
  		}
  		err = -EINVAL;
  		if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
  			goto out;
  		nh->nh_scope = res.scope;
  		nh->nh_oif = FIB_RES_OIF(res);
6a31d2a97   Eric Dumazet   fib: cleanups
561
562
  		nh->nh_dev = dev = FIB_RES_DEV(res);
  		if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  			goto out;
6a31d2a97   Eric Dumazet   fib: cleanups
564
  		dev_hold(dev);
8723e1b4a   Eric Dumazet   inet: RCU changes...
565
  		err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
  	} else {
  		struct in_device *in_dev;
6a31d2a97   Eric Dumazet   fib: cleanups
568
  		if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
  			return -EINVAL;
8723e1b4a   Eric Dumazet   inet: RCU changes...
570
571
  		rcu_read_lock();
  		err = -ENODEV;
86167a377   Denis V. Lunev   [NETNS]: Pass cor...
572
  		in_dev = inetdev_by_index(net, nh->nh_oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  		if (in_dev == NULL)
8723e1b4a   Eric Dumazet   inet: RCU changes...
574
575
576
577
  			goto out;
  		err = -ENETDOWN;
  		if (!(in_dev->dev->flags & IFF_UP))
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
580
  		nh->nh_dev = in_dev->dev;
  		dev_hold(nh->nh_dev);
  		nh->nh_scope = RT_SCOPE_HOST;
8723e1b4a   Eric Dumazet   inet: RCU changes...
581
  		err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  	}
8723e1b4a   Eric Dumazet   inet: RCU changes...
583
584
585
  out:
  	rcu_read_unlock();
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
  }
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
587
  static inline unsigned int fib_laddr_hashfn(__be32 val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  {
123b9731b   David S. Miller   ipv4: Rename fib_...
589
  	unsigned int mask = (fib_info_hash_size - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590

6a31d2a97   Eric Dumazet   fib: cleanups
591
592
593
  	return ((__force u32)val ^
  		((__force u32)val >> 7) ^
  		((__force u32)val >> 14)) & mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
  }
123b9731b   David S. Miller   ipv4: Rename fib_...
595
  static struct hlist_head *fib_info_hash_alloc(int bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
  {
  	if (bytes <= PAGE_SIZE)
88f834916   Joonwoo Park   [IPV4] fib_semant...
598
  		return kzalloc(bytes, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
600
  	else
  		return (struct hlist_head *)
6a31d2a97   Eric Dumazet   fib: cleanups
601
602
  			__get_free_pages(GFP_KERNEL | __GFP_ZERO,
  					 get_order(bytes));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
  }
123b9731b   David S. Miller   ipv4: Rename fib_...
604
  static void fib_info_hash_free(struct hlist_head *hash, int bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
609
610
611
612
613
  {
  	if (!hash)
  		return;
  
  	if (bytes <= PAGE_SIZE)
  		kfree(hash);
  	else
  		free_pages((unsigned long) hash, get_order(bytes));
  }
123b9731b   David S. Miller   ipv4: Rename fib_...
614
615
616
  static void fib_info_hash_move(struct hlist_head *new_info_hash,
  			       struct hlist_head *new_laddrhash,
  			       unsigned int new_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  {
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
618
  	struct hlist_head *old_info_hash, *old_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
619
  	unsigned int old_size = fib_info_hash_size;
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
620
  	unsigned int i, bytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621

832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
622
  	spin_lock_bh(&fib_info_lock);
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
623
624
  	old_info_hash = fib_info_hash;
  	old_laddrhash = fib_info_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
625
  	fib_info_hash_size = new_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
  
  	for (i = 0; i < old_size; i++) {
  		struct hlist_head *head = &fib_info_hash[i];
  		struct hlist_node *node, *n;
  		struct fib_info *fi;
  
  		hlist_for_each_entry_safe(fi, node, n, head, fib_hash) {
  			struct hlist_head *dest;
  			unsigned int new_hash;
  
  			hlist_del(&fi->fib_hash);
  
  			new_hash = fib_info_hashfn(fi);
  			dest = &new_info_hash[new_hash];
  			hlist_add_head(&fi->fib_hash, dest);
  		}
  	}
  	fib_info_hash = new_info_hash;
  
  	for (i = 0; i < old_size; i++) {
  		struct hlist_head *lhead = &fib_info_laddrhash[i];
  		struct hlist_node *node, *n;
  		struct fib_info *fi;
  
  		hlist_for_each_entry_safe(fi, node, n, lhead, fib_lhash) {
  			struct hlist_head *ldest;
  			unsigned int new_hash;
  
  			hlist_del(&fi->fib_lhash);
  
  			new_hash = fib_laddr_hashfn(fi->fib_prefsrc);
  			ldest = &new_laddrhash[new_hash];
  			hlist_add_head(&fi->fib_lhash, ldest);
  		}
  	}
  	fib_info_laddrhash = new_laddrhash;
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
662
  	spin_unlock_bh(&fib_info_lock);
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
663
664
  
  	bytes = old_size * sizeof(struct hlist_head *);
123b9731b   David S. Miller   ipv4: Rename fib_...
665
666
  	fib_info_hash_free(old_info_hash, bytes);
  	fib_info_hash_free(old_laddrhash, bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
  }
436c3b66e   David S. Miller   ipv4: Invalidate ...
668
669
670
671
  __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh)
  {
  	nh->nh_saddr = inet_select_addr(nh->nh_dev,
  					nh->nh_gw,
37e826c51   David S. Miller   ipv4: Fix nexthop...
672
  					nh->nh_parent->fib_scope);
436c3b66e   David S. Miller   ipv4: Invalidate ...
673
674
675
676
  	nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid);
  
  	return nh->nh_saddr;
  }
4e902c574   Thomas Graf   [IPv4]: FIB confi...
677
  struct fib_info *fib_create_info(struct fib_config *cfg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
680
681
  {
  	int err;
  	struct fib_info *fi = NULL;
  	struct fib_info *ofi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
  	int nhs = 1;
7462bd744   Denis V. Lunev   [NETNS]: Add a na...
683
  	struct net *net = cfg->fc_nlinfo.nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684

4c8237cd7   David S. Miller   ipv4: Validate ro...
685
686
  	if (cfg->fc_type > RTN_MAX)
  		goto err_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
  	/* Fast check to catch the most weird cases */
4e902c574   Thomas Graf   [IPv4]: FIB confi...
688
  	if (fib_props[cfg->fc_type].scope > cfg->fc_scope)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
690
691
  		goto err_inval;
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
692
693
  	if (cfg->fc_mp) {
  		nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
696
697
  		if (nhs == 0)
  			goto err_inval;
  	}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
  
  	err = -ENOBUFS;
123b9731b   David S. Miller   ipv4: Rename fib_...
700
701
  	if (fib_info_cnt >= fib_info_hash_size) {
  		unsigned int new_size = fib_info_hash_size << 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
703
704
705
706
707
708
  		struct hlist_head *new_info_hash;
  		struct hlist_head *new_laddrhash;
  		unsigned int bytes;
  
  		if (!new_size)
  			new_size = 1;
  		bytes = new_size * sizeof(struct hlist_head *);
123b9731b   David S. Miller   ipv4: Rename fib_...
709
710
  		new_info_hash = fib_info_hash_alloc(bytes);
  		new_laddrhash = fib_info_hash_alloc(bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  		if (!new_info_hash || !new_laddrhash) {
123b9731b   David S. Miller   ipv4: Rename fib_...
712
713
  			fib_info_hash_free(new_info_hash, bytes);
  			fib_info_hash_free(new_laddrhash, bytes);
88f834916   Joonwoo Park   [IPV4] fib_semant...
714
  		} else
123b9731b   David S. Miller   ipv4: Rename fib_...
715
  			fib_info_hash_move(new_info_hash, new_laddrhash, new_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716

123b9731b   David S. Miller   ipv4: Rename fib_...
717
  		if (!fib_info_hash_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
719
  			goto failure;
  	}
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
720
  	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
722
  	if (fi == NULL)
  		goto failure;
725d1e1b4   David S. Miller   ipv4: Attach FIB ...
723
724
725
726
727
728
  	if (cfg->fc_mx) {
  		fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
  		if (!fi->fib_metrics)
  			goto failure;
  	} else
  		fi->fib_metrics = (u32 *) dst_default_metrics;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
  	fib_info_cnt++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730

57d7a6009   Denis V. Lunev   [NETNS]: Add netn...
731
  	fi->fib_net = hold_net(net);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
732
  	fi->fib_protocol = cfg->fc_protocol;
37e826c51   David S. Miller   ipv4: Fix nexthop...
733
  	fi->fib_scope = cfg->fc_scope;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
734
735
736
  	fi->fib_flags = cfg->fc_flags;
  	fi->fib_priority = cfg->fc_priority;
  	fi->fib_prefsrc = cfg->fc_prefsrc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
738
739
  
  	fi->fib_nhs = nhs;
  	change_nexthops(fi) {
71fceff0e   David S. Miller   ipv4: Use less co...
740
  		nexthop_nh->nh_parent = fi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
  	} endfor_nexthops(fi)
4e902c574   Thomas Graf   [IPv4]: FIB confi...
742
743
744
745
746
  	if (cfg->fc_mx) {
  		struct nlattr *nla;
  		int remaining;
  
  		nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
8f4c1f9b0   Thomas Graf   [NETLINK]: Introd...
747
  			int type = nla_type(nla);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
748
749
750
  
  			if (type) {
  				if (type > RTAX_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
  					goto err_inval;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
752
  				fi->fib_metrics[type - 1] = nla_get_u32(nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
753
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756

4e902c574   Thomas Graf   [IPv4]: FIB confi...
757
  	if (cfg->fc_mp) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
759
760
  		err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);
  		if (err != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
  			goto failure;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
762
  		if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
  			goto err_inval;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
764
  		if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
  			goto err_inval;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
766
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
767
  		if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
769
770
771
772
773
774
  			goto err_inval;
  #endif
  #else
  		goto err_inval;
  #endif
  	} else {
  		struct fib_nh *nh = fi->fib_nh;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
775
776
777
778
  
  		nh->nh_oif = cfg->fc_oif;
  		nh->nh_gw = cfg->fc_gw;
  		nh->nh_flags = cfg->fc_flags;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
779
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
780
  		nh->nh_tclassid = cfg->fc_flow;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  		nh->nh_weight = 1;
  #endif
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
786
787
  	if (fib_props[cfg->fc_type].error) {
  		if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  			goto err_inval;
  		goto link_it;
4c8237cd7   David S. Miller   ipv4: Validate ro...
790
791
792
793
794
795
796
797
798
799
800
  	} else {
  		switch (cfg->fc_type) {
  		case RTN_UNICAST:
  		case RTN_LOCAL:
  		case RTN_BROADCAST:
  		case RTN_ANYCAST:
  		case RTN_MULTICAST:
  			break;
  		default:
  			goto err_inval;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
802
  	if (cfg->fc_scope > RT_SCOPE_HOST)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
  		goto err_inval;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
804
  	if (cfg->fc_scope == RT_SCOPE_HOST) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
806
807
808
809
810
  		struct fib_nh *nh = fi->fib_nh;
  
  		/* Local address is added. */
  		if (nhs != 1 || nh->nh_gw)
  			goto err_inval;
  		nh->nh_scope = RT_SCOPE_NOWHERE;
7462bd744   Denis V. Lunev   [NETNS]: Add a na...
811
  		nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
815
816
  		err = -ENODEV;
  		if (nh->nh_dev == NULL)
  			goto failure;
  	} else {
  		change_nexthops(fi) {
6a31d2a97   Eric Dumazet   fib: cleanups
817
818
  			err = fib_check_nh(cfg, fi, nexthop_nh);
  			if (err != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
821
822
823
  				goto failure;
  		} endfor_nexthops(fi)
  	}
  
  	if (fi->fib_prefsrc) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
824
825
  		if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst ||
  		    fi->fib_prefsrc != cfg->fc_dst)
7462bd744   Denis V. Lunev   [NETNS]: Add a na...
826
  			if (inet_addr_type(net, fi->fib_prefsrc) != RTN_LOCAL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
828
  				goto err_inval;
  	}
1fc050a13   David S. Miller   ipv4: Cache sourc...
829
  	change_nexthops(fi) {
436c3b66e   David S. Miller   ipv4: Invalidate ...
830
  		fib_info_update_nh_saddr(net, nexthop_nh);
1fc050a13   David S. Miller   ipv4: Cache sourc...
831
  	} endfor_nexthops(fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
  link_it:
6a31d2a97   Eric Dumazet   fib: cleanups
833
834
  	ofi = fib_find_info(fi);
  	if (ofi) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
836
837
838
839
840
841
842
  		fi->fib_dead = 1;
  		free_fib_info(fi);
  		ofi->fib_treeref++;
  		return ofi;
  	}
  
  	fi->fib_treeref++;
  	atomic_inc(&fi->fib_clntref);
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
843
  	spin_lock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
845
846
847
848
849
850
851
852
853
854
  	hlist_add_head(&fi->fib_hash,
  		       &fib_info_hash[fib_info_hashfn(fi)]);
  	if (fi->fib_prefsrc) {
  		struct hlist_head *head;
  
  		head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)];
  		hlist_add_head(&fi->fib_lhash, head);
  	}
  	change_nexthops(fi) {
  		struct hlist_head *head;
  		unsigned int hash;
71fceff0e   David S. Miller   ipv4: Use less co...
855
  		if (!nexthop_nh->nh_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
  			continue;
71fceff0e   David S. Miller   ipv4: Use less co...
857
  		hash = fib_devindex_hashfn(nexthop_nh->nh_dev->ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  		head = &fib_info_devhash[hash];
71fceff0e   David S. Miller   ipv4: Use less co...
859
  		hlist_add_head(&nexthop_nh->nh_hash, head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
  	} endfor_nexthops(fi)
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
861
  	spin_unlock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
864
865
866
867
  	return fi;
  
  err_inval:
  	err = -EINVAL;
  
  failure:
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
868
  	if (fi) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
871
  		fi->fib_dead = 1;
  		free_fib_info(fi);
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
872
873
  
  	return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
  }
be403ea18   Thomas Graf   [IPv4]: Convert F...
875
  int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
37e826c51   David S. Miller   ipv4: Fix nexthop...
876
  		  u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos,
be403ea18   Thomas Graf   [IPv4]: Convert F...
877
  		  struct fib_info *fi, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
  {
be403ea18   Thomas Graf   [IPv4]: Convert F...
879
  	struct nlmsghdr *nlh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
  	struct rtmsg *rtm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881

be403ea18   Thomas Graf   [IPv4]: Convert F...
882
883
  	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags);
  	if (nlh == NULL)
26932566a   Patrick McHardy   [NETLINK]: Don't ...
884
  		return -EMSGSIZE;
be403ea18   Thomas Graf   [IPv4]: Convert F...
885
886
  
  	rtm = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
889
890
  	rtm->rtm_family = AF_INET;
  	rtm->rtm_dst_len = dst_len;
  	rtm->rtm_src_len = 0;
  	rtm->rtm_tos = tos;
709772e6e   Krzysztof Piotr Oledzki   net: Fix routing ...
891
892
893
894
  	if (tb_id < 256)
  		rtm->rtm_table = tb_id;
  	else
  		rtm->rtm_table = RT_TABLE_COMPAT;
be403ea18   Thomas Graf   [IPv4]: Convert F...
895
  	NLA_PUT_U32(skb, RTA_TABLE, tb_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
897
  	rtm->rtm_type = type;
  	rtm->rtm_flags = fi->fib_flags;
37e826c51   David S. Miller   ipv4: Fix nexthop...
898
  	rtm->rtm_scope = fi->fib_scope;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
  	rtm->rtm_protocol = fi->fib_protocol;
be403ea18   Thomas Graf   [IPv4]: Convert F...
900
901
  
  	if (rtm->rtm_dst_len)
17fb2c643   Al Viro   [IPV4]: RTA_{DST,...
902
  		NLA_PUT_BE32(skb, RTA_DST, dst);
be403ea18   Thomas Graf   [IPv4]: Convert F...
903

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
904
  	if (fi->fib_priority)
be403ea18   Thomas Graf   [IPv4]: Convert F...
905
  		NLA_PUT_U32(skb, RTA_PRIORITY, fi->fib_priority);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
  	if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0)
be403ea18   Thomas Graf   [IPv4]: Convert F...
907
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
  	if (fi->fib_prefsrc)
17fb2c643   Al Viro   [IPV4]: RTA_{DST,...
909
  		NLA_PUT_BE32(skb, RTA_PREFSRC, fi->fib_prefsrc);
be403ea18   Thomas Graf   [IPv4]: Convert F...
910

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
  	if (fi->fib_nhs == 1) {
  		if (fi->fib_nh->nh_gw)
17fb2c643   Al Viro   [IPV4]: RTA_{DST,...
913
  			NLA_PUT_BE32(skb, RTA_GATEWAY, fi->fib_nh->nh_gw);
be403ea18   Thomas Graf   [IPv4]: Convert F...
914

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
  		if (fi->fib_nh->nh_oif)
be403ea18   Thomas Graf   [IPv4]: Convert F...
916
  			NLA_PUT_U32(skb, RTA_OIF, fi->fib_nh->nh_oif);
c7066f70d   Patrick McHardy   netfilter: fix Kc...
917
  #ifdef CONFIG_IP_ROUTE_CLASSID
8265abc08   Patrick McHardy   [IPV4]: Fix nexth...
918
  		if (fi->fib_nh[0].nh_tclassid)
be403ea18   Thomas Graf   [IPv4]: Convert F...
919
  			NLA_PUT_U32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid);
8265abc08   Patrick McHardy   [IPV4]: Fix nexth...
920
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
922
923
  	}
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  	if (fi->fib_nhs > 1) {
be403ea18   Thomas Graf   [IPv4]: Convert F...
924
925
926
927
928
929
  		struct rtnexthop *rtnh;
  		struct nlattr *mp;
  
  		mp = nla_nest_start(skb, RTA_MULTIPATH);
  		if (mp == NULL)
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
931
  
  		for_nexthops(fi) {
be403ea18   Thomas Graf   [IPv4]: Convert F...
932
933
934
935
936
937
938
  			rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
  			if (rtnh == NULL)
  				goto nla_put_failure;
  
  			rtnh->rtnh_flags = nh->nh_flags & 0xFF;
  			rtnh->rtnh_hops = nh->nh_weight - 1;
  			rtnh->rtnh_ifindex = nh->nh_oif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939
  			if (nh->nh_gw)
17fb2c643   Al Viro   [IPV4]: RTA_{DST,...
940
  				NLA_PUT_BE32(skb, RTA_GATEWAY, nh->nh_gw);
c7066f70d   Patrick McHardy   netfilter: fix Kc...
941
  #ifdef CONFIG_IP_ROUTE_CLASSID
8265abc08   Patrick McHardy   [IPV4]: Fix nexth...
942
  			if (nh->nh_tclassid)
be403ea18   Thomas Graf   [IPv4]: Convert F...
943
  				NLA_PUT_U32(skb, RTA_FLOW, nh->nh_tclassid);
8265abc08   Patrick McHardy   [IPV4]: Fix nexth...
944
  #endif
be403ea18   Thomas Graf   [IPv4]: Convert F...
945
946
  			/* length of rtnetlink header + attributes */
  			rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
  		} endfor_nexthops(fi);
be403ea18   Thomas Graf   [IPv4]: Convert F...
948
949
  
  		nla_nest_end(skb, mp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
951
  	}
  #endif
be403ea18   Thomas Graf   [IPv4]: Convert F...
952
  	return nlmsg_end(skb, nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953

be403ea18   Thomas Graf   [IPv4]: Convert F...
954
  nla_put_failure:
26932566a   Patrick McHardy   [NETLINK]: Don't ...
955
956
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
959
960
961
962
   * Update FIB if:
   * - local address disappeared -> we must delete all the entries
   *   referring to it.
   * - device went down -> we must shutdown all nexthops going via it.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
   */
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
964
  int fib_sync_down_addr(struct net *net, __be32 local)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
966
  {
  	int ret = 0;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
967
968
969
970
  	unsigned int hash = fib_laddr_hashfn(local);
  	struct hlist_head *head = &fib_info_laddrhash[hash];
  	struct hlist_node *node;
  	struct fib_info *fi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
972
973
  	if (fib_info_laddrhash == NULL || local == 0)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
975
  	hlist_for_each_entry(fi, node, head, fib_lhash) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
976
  		if (!net_eq(fi->fib_net, net))
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
977
  			continue;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
978
979
980
  		if (fi->fib_prefsrc == local) {
  			fi->fib_flags |= RTNH_F_DEAD;
  			ret++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
  		}
  	}
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
983
984
985
986
987
988
989
990
991
992
993
994
  	return ret;
  }
  
  int fib_sync_down_dev(struct net_device *dev, int force)
  {
  	int ret = 0;
  	int scope = RT_SCOPE_NOWHERE;
  	struct fib_info *prev_fi = NULL;
  	unsigned int hash = fib_devindex_hashfn(dev->ifindex);
  	struct hlist_head *head = &fib_info_devhash[hash];
  	struct hlist_node *node;
  	struct fib_nh *nh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
996
997
  	if (force)
  		scope = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
998

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
999
1000
1001
  	hlist_for_each_entry(nh, node, head, nh_hash) {
  		struct fib_info *fi = nh->nh_parent;
  		int dead;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1003
1004
1005
1006
1007
1008
  		BUG_ON(!fi->fib_nhs);
  		if (nh->nh_dev != dev || fi == prev_fi)
  			continue;
  		prev_fi = fi;
  		dead = 0;
  		change_nexthops(fi) {
6a31d2a97   Eric Dumazet   fib: cleanups
1009
  			if (nexthop_nh->nh_flags & RTNH_F_DEAD)
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1010
  				dead++;
71fceff0e   David S. Miller   ipv4: Use less co...
1011
1012
1013
  			else if (nexthop_nh->nh_dev == dev &&
  				 nexthop_nh->nh_scope != scope) {
  				nexthop_nh->nh_flags |= RTNH_F_DEAD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1015
  				spin_lock_bh(&fib_multipath_lock);
71fceff0e   David S. Miller   ipv4: Use less co...
1016
1017
  				fi->fib_power -= nexthop_nh->nh_power;
  				nexthop_nh->nh_power = 0;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1018
  				spin_unlock_bh(&fib_multipath_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1019
  #endif
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1020
1021
  				dead++;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
71fceff0e   David S. Miller   ipv4: Use less co...
1023
  			if (force > 1 && nexthop_nh->nh_dev == dev) {
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1024
1025
  				dead = fi->fib_nhs;
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
  			}
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1027
1028
1029
1030
1031
  #endif
  		} endfor_nexthops(fi)
  		if (dead == fi->fib_nhs) {
  			fi->fib_flags |= RTNH_F_DEAD;
  			ret++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032
1033
1034
1035
1036
  		}
  	}
  
  	return ret;
  }
0c838ff1a   David S. Miller   ipv4: Consolidate...
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
  /* Must be invoked inside of an RCU protected region.  */
  void fib_select_default(struct fib_result *res)
  {
  	struct fib_info *fi = NULL, *last_resort = NULL;
  	struct list_head *fa_head = res->fa_head;
  	struct fib_table *tb = res->table;
  	int order = -1, last_idx = -1;
  	struct fib_alias *fa;
  
  	list_for_each_entry_rcu(fa, fa_head, fa_list) {
  		struct fib_info *next_fi = fa->fa_info;
37e826c51   David S. Miller   ipv4: Fix nexthop...
1048
  		if (next_fi->fib_scope != res->scope ||
0c838ff1a   David S. Miller   ipv4: Consolidate...
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
  		    fa->fa_type != RTN_UNICAST)
  			continue;
  
  		if (next_fi->fib_priority > res->fi->fib_priority)
  			break;
  		if (!next_fi->fib_nh[0].nh_gw ||
  		    next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK)
  			continue;
  
  		fib_alias_accessed(fa);
  
  		if (fi == NULL) {
  			if (next_fi != res->fi)
  				break;
  		} else if (!fib_detect_death(fi, order, &last_resort,
  					     &last_idx, tb->tb_default)) {
  			fib_result_assign(res, fi);
  			tb->tb_default = order;
  			goto out;
  		}
  		fi = next_fi;
  		order++;
  	}
  
  	if (order <= 0 || fi == NULL) {
  		tb->tb_default = -1;
  		goto out;
  	}
  
  	if (!fib_detect_death(fi, order, &last_resort, &last_idx,
  				tb->tb_default)) {
  		fib_result_assign(res, fi);
  		tb->tb_default = order;
  		goto out;
  	}
  
  	if (last_idx >= 0)
  		fib_result_assign(res, last_resort);
  	tb->tb_default = last_idx;
  out:
31d409373   Eric Dumazet   ipv4: fix rcu loc...
1089
  	return;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1090
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1091
1092
1093
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
1094
1095
   * Dead device goes up. We wake up dead nexthops.
   * It takes sense only on multipath routes.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1096
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
1098
1099
1100
1101
1102
1103
1104
  int fib_sync_up(struct net_device *dev)
  {
  	struct fib_info *prev_fi;
  	unsigned int hash;
  	struct hlist_head *head;
  	struct hlist_node *node;
  	struct fib_nh *nh;
  	int ret;
6a31d2a97   Eric Dumazet   fib: cleanups
1105
  	if (!(dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
  		return 0;
  
  	prev_fi = NULL;
  	hash = fib_devindex_hashfn(dev->ifindex);
  	head = &fib_info_devhash[hash];
  	ret = 0;
  
  	hlist_for_each_entry(nh, node, head, nh_hash) {
  		struct fib_info *fi = nh->nh_parent;
  		int alive;
  
  		BUG_ON(!fi->fib_nhs);
  		if (nh->nh_dev != dev || fi == prev_fi)
  			continue;
  
  		prev_fi = fi;
  		alive = 0;
  		change_nexthops(fi) {
6a31d2a97   Eric Dumazet   fib: cleanups
1124
  			if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1125
1126
1127
  				alive++;
  				continue;
  			}
71fceff0e   David S. Miller   ipv4: Use less co...
1128
  			if (nexthop_nh->nh_dev == NULL ||
6a31d2a97   Eric Dumazet   fib: cleanups
1129
  			    !(nexthop_nh->nh_dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1130
  				continue;
71fceff0e   David S. Miller   ipv4: Use less co...
1131
1132
  			if (nexthop_nh->nh_dev != dev ||
  			    !__in_dev_get_rtnl(dev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1133
1134
1135
  				continue;
  			alive++;
  			spin_lock_bh(&fib_multipath_lock);
71fceff0e   David S. Miller   ipv4: Use less co...
1136
1137
  			nexthop_nh->nh_power = 0;
  			nexthop_nh->nh_flags &= ~RTNH_F_DEAD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
  			spin_unlock_bh(&fib_multipath_lock);
  		} endfor_nexthops(fi)
  
  		if (alive > 0) {
  			fi->fib_flags &= ~RTNH_F_DEAD;
  			ret++;
  		}
  	}
  
  	return ret;
  }
  
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
1151
1152
   * The algorithm is suboptimal, but it provides really
   * fair weighted route distribution.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1153
   */
1b7fe5932   David S. Miller   ipv4: Kill flowi ...
1154
  void fib_select_multipath(struct fib_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1155
1156
1157
1158
1159
1160
1161
1162
  {
  	struct fib_info *fi = res->fi;
  	int w;
  
  	spin_lock_bh(&fib_multipath_lock);
  	if (fi->fib_power <= 0) {
  		int power = 0;
  		change_nexthops(fi) {
6a31d2a97   Eric Dumazet   fib: cleanups
1163
  			if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) {
71fceff0e   David S. Miller   ipv4: Use less co...
1164
1165
  				power += nexthop_nh->nh_weight;
  				nexthop_nh->nh_power = nexthop_nh->nh_weight;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
  			}
  		} endfor_nexthops(fi);
  		fi->fib_power = power;
  		if (power <= 0) {
  			spin_unlock_bh(&fib_multipath_lock);
  			/* Race condition: route has just become dead. */
  			res->nh_sel = 0;
  			return;
  		}
  	}
  
  
  	/* w should be random number [0..fi->fib_power-1],
6a31d2a97   Eric Dumazet   fib: cleanups
1179
  	 * it is pretty bad approximation.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
1181
1182
1183
1184
  	 */
  
  	w = jiffies % fi->fib_power;
  
  	change_nexthops(fi) {
6a31d2a97   Eric Dumazet   fib: cleanups
1185
  		if (!(nexthop_nh->nh_flags & RTNH_F_DEAD) &&
71fceff0e   David S. Miller   ipv4: Use less co...
1186
  		    nexthop_nh->nh_power) {
6a31d2a97   Eric Dumazet   fib: cleanups
1187
1188
  			w -= nexthop_nh->nh_power;
  			if (w <= 0) {
71fceff0e   David S. Miller   ipv4: Use less co...
1189
  				nexthop_nh->nh_power--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
  				fi->fib_power--;
  				res->nh_sel = nhsel;
  				spin_unlock_bh(&fib_multipath_lock);
  				return;
  			}
  		}
  	} endfor_nexthops(fi);
  
  	/* Race condition: route has just become dead. */
  	res->nh_sel = 0;
  	spin_unlock_bh(&fib_multipath_lock);
  }
  #endif