Blame view

net/ipv4/fib_semantics.c 30.3 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
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
22
23
24
25
26
  #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...
27
  #include <linux/inetdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
  #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
32
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
33
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
35
  #include <net/arp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
  #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...
42
  #include <net/netlink.h>
4e902c574   Thomas Graf   [IPv4]: FIB confi...
43
  #include <net/nexthop.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
  
  #include "fib_lookup.h"
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
46
  static DEFINE_SPINLOCK(fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
  static struct hlist_head *fib_info_hash;
  static struct hlist_head *fib_info_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
49
  static unsigned int fib_info_hash_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
  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
59
60
61
62
63
64
65
66
67
68
69
  #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
70
71
72
73
  
  #else /* CONFIG_IP_ROUTE_MULTIPATH */
  
  /* Hope, that gcc will optimize it to get rid of dummy loop */
6a31d2a97   Eric Dumazet   fib: cleanups
74
75
76
  #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
77

6a31d2a97   Eric Dumazet   fib: cleanups
78
79
80
81
  #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
82
83
84
85
  
  #endif /* CONFIG_IP_ROUTE_MULTIPATH */
  
  #define endfor_nexthops(fi) }
3be0686b6   David S. Miller   ipv4: Inline fib_...
86
  const struct fib_prop fib_props[RTN_MAX + 1] = {
6a31d2a97   Eric Dumazet   fib: cleanups
87
  	[RTN_UNSPEC] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
  		.error	= 0,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
90
91
  	},
  	[RTN_UNICAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
  		.error	= 0,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
94
95
  	},
  	[RTN_LOCAL] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
  		.error	= 0,
  		.scope	= RT_SCOPE_HOST,
6a31d2a97   Eric Dumazet   fib: cleanups
98
99
  	},
  	[RTN_BROADCAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
  		.error	= 0,
  		.scope	= RT_SCOPE_LINK,
6a31d2a97   Eric Dumazet   fib: cleanups
102
103
  	},
  	[RTN_ANYCAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
  		.error	= 0,
  		.scope	= RT_SCOPE_LINK,
6a31d2a97   Eric Dumazet   fib: cleanups
106
107
  	},
  	[RTN_MULTICAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  		.error	= 0,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
110
111
  	},
  	[RTN_BLACKHOLE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
114
115
  	},
  	[RTN_UNREACHABLE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
  		.error	= -EHOSTUNREACH,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
118
119
  	},
  	[RTN_PROHIBIT] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
  		.error	= -EACCES,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
122
123
  	},
  	[RTN_THROW] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
  		.error	= -EAGAIN,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
126
127
  	},
  	[RTN_NAT] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
130
131
  	},
  	[RTN_XRESOLVE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
134
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  };
4895c771c   David S. Miller   ipv4: Add FIB nex...
136
137
138
139
140
141
142
  static void free_nh_exceptions(struct fib_nh *nh)
  {
  	struct fnhe_hash_bucket *hash = nh->nh_exceptions;
  	int i;
  
  	for (i = 0; i < FNHE_HASH_SIZE; i++) {
  		struct fib_nh_exception *fnhe;
5abf7f7e0   Eric Dumazet   ipv4: fix rcu splat
143
  		fnhe = rcu_dereference_protected(hash[i].chain, 1);
4895c771c   David S. Miller   ipv4: Add FIB nex...
144
145
146
  		while (fnhe) {
  			struct fib_nh_exception *next;
  			
5abf7f7e0   Eric Dumazet   ipv4: fix rcu splat
147
  			next = rcu_dereference_protected(fnhe->fnhe_next, 1);
4895c771c   David S. Miller   ipv4: Add FIB nex...
148
149
150
151
152
153
154
  			kfree(fnhe);
  
  			fnhe = next;
  		}
  	}
  	kfree(hash);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  /* Release a nexthop info record */
19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
156
157
158
  static void free_fib_info_rcu(struct rcu_head *head)
  {
  	struct fib_info *fi = container_of(head, struct fib_info, rcu);
e49cc0da7   Yanmin Zhang   ipv4: fix the rcu...
159
160
161
  	change_nexthops(fi) {
  		if (nexthop_nh->nh_dev)
  			dev_put(nexthop_nh->nh_dev);
4895c771c   David S. Miller   ipv4: Add FIB nex...
162
163
  		if (nexthop_nh->nh_exceptions)
  			free_nh_exceptions(nexthop_nh);
f2bb4bedf   David S. Miller   ipv4: Cache outpu...
164
165
  		if (nexthop_nh->nh_rth_output)
  			dst_release(&nexthop_nh->nh_rth_output->dst);
d2d68ba9f   David S. Miller   ipv4: Cache input...
166
167
  		if (nexthop_nh->nh_rth_input)
  			dst_release(&nexthop_nh->nh_rth_input->dst);
e49cc0da7   Yanmin Zhang   ipv4: fix the rcu...
168
169
170
  	} endfor_nexthops(fi);
  
  	release_net(fi->fib_net);
19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
171
172
173
174
  	if (fi->fib_metrics != (u32 *) dst_default_metrics)
  		kfree(fi->fib_metrics);
  	kfree(fi);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
  
  void free_fib_info(struct fib_info *fi)
  {
  	if (fi->fib_dead == 0) {
058bd4d2a   Joe Perches   net: Convert prin...
179
180
  		pr_warn("Freeing alive fib_info %p
  ", fi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  		return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  	fib_info_cnt--;
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
184
185
186
  #ifdef CONFIG_IP_ROUTE_CLASSID
  	change_nexthops(fi) {
  		if (nexthop_nh->nh_tclassid)
f4530fa57   David S. Miller   ipv4: Avoid overh...
187
  			fi->fib_net->ipv4.fib_num_tclassid_users--;
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
188
189
  	} endfor_nexthops(fi);
  #endif
19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
190
  	call_rcu(&fi->rcu, free_fib_info_rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
  }
  
  void fib_release_info(struct fib_info *fi)
  {
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
195
  	spin_lock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
  	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...
201
  			if (!nexthop_nh->nh_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  				continue;
71fceff0e   David S. Miller   ipv4: Use less co...
203
  			hlist_del(&nexthop_nh->nh_hash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
206
207
  		} endfor_nexthops(fi)
  		fi->fib_dead = 1;
  		fib_info_put(fi);
  	}
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
208
  	spin_unlock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  }
6a31d2a97   Eric Dumazet   fib: cleanups
210
  static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
220
  {
  	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...
221
  #ifdef CONFIG_IP_ROUTE_CLASSID
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
  		    nh->nh_tclassid != onh->nh_tclassid ||
  #endif
6a31d2a97   Eric Dumazet   fib: cleanups
224
  		    ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_F_DEAD))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
  			return -1;
  		onh++;
  	} endfor_nexthops(fi);
  	return 0;
  }
88ebc72f6   David S. Miller   [IPV4] FIB: Inclu...
230
231
232
233
234
235
236
237
  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
238
239
  static inline unsigned int fib_info_hashfn(const struct fib_info *fi)
  {
123b9731b   David S. Miller   ipv4: Rename fib_...
240
  	unsigned int mask = (fib_info_hash_size - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  	unsigned int val = fi->fib_nhs;
37e826c51   David S. Miller   ipv4: Fix nexthop...
242
  	val ^= (fi->fib_protocol << 8) | fi->fib_scope;
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
243
  	val ^= (__force u32)fi->fib_prefsrc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  	val ^= fi->fib_priority;
88ebc72f6   David S. Miller   [IPV4] FIB: Inclu...
245
246
247
  	for_nexthops(fi) {
  		val ^= fib_devindex_hashfn(nh->nh_oif);
  	} endfor_nexthops(fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  
  	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...
263
  		if (!net_eq(fi->fib_net, nfi->fib_net))
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
264
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
  		if (fi->fib_nhs != nfi->fib_nhs)
  			continue;
  		if (nfi->fib_protocol == fi->fib_protocol &&
37e826c51   David S. Miller   ipv4: Fix nexthop...
268
  		    nfi->fib_scope == fi->fib_scope &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
  		    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...
272
  			   sizeof(u32) * RTAX_MAX) == 0 &&
6a31d2a97   Eric Dumazet   fib: cleanups
273
  		    ((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
279
  		    (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
  			return fi;
  	}
  
  	return NULL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  /* Check, that the gateway is already configured.
6a31d2a97   Eric Dumazet   fib: cleanups
281
   * Used only by redirect accept routine.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
   */
d878e72e4   Al Viro   [IPV4]: ip_fib_ch...
283
  int ip_fib_check_default(__be32 gw, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
288
  {
  	struct hlist_head *head;
  	struct hlist_node *node;
  	struct fib_nh *nh;
  	unsigned int hash;
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
289
  	spin_lock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
295
  
  	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
296
  		    !(nh->nh_flags & RTNH_F_DEAD)) {
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
297
  			spin_unlock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
  			return 0;
  		}
  	}
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
301
  	spin_unlock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
  
  	return -1;
  }
339bf98ff   Thomas Graf   [NETLINK]: Do pre...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  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/...
331
  void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
b8f558313   Milan Kocian   [RTNETLINK]: Fix ...
332
333
  	       int dst_len, u32 tb_id, struct nl_info *info,
  	       unsigned int nlm_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
  {
  	struct sk_buff *skb;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
336
  	u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
337
  	int err = -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338

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

4e902c574   Thomas Graf   [IPv4]: FIB confi...
343
  	err = fib_dump_info(skb, info->pid, seq, event, tb_id,
37e826c51   David S. Miller   ipv4: Fix nexthop...
344
  			    fa->fa_type, key, dst_len,
b8f558313   Milan Kocian   [RTNETLINK]: Fix ...
345
  			    fa->fa_tos, fa->fa_info, nlm_flags);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
346
347
348
349
350
351
  	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...
352
353
354
  	rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE,
  		    info->nlh, GFP_KERNEL);
  	return;
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
355
356
  errout:
  	if (err < 0)
4d1169c1e   Denis V. Lunev   [NETNS]: Add netn...
357
  		rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  }
  
  /* 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...
379
  		     struct fib_info **last_resort, int *last_idx, int dflt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
386
387
388
  {
  	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...
389
  	if (state == NUD_REACHABLE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
  		return 0;
6a31d2a97   Eric Dumazet   fib: cleanups
391
  	if ((state & NUD_VALID) && order != dflt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  		return 0;
6a31d2a97   Eric Dumazet   fib: cleanups
393
394
  	if ((state & NUD_VALID) ||
  	    (*last_idx < 0 && order > dflt)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
396
397
398
399
400
401
  		*last_resort = fi;
  		*last_idx = order;
  	}
  	return 1;
  }
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
402
  static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
  {
  	int nhs = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405

4e902c574   Thomas Graf   [IPv4]: FIB confi...
406
  	while (rtnh_ok(rtnh, remaining)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
  		nhs++;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
408
409
410
411
412
  		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
413
  }
4e902c574   Thomas Graf   [IPv4]: FIB confi...
414
415
  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
416
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
  	change_nexthops(fi) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
418
419
420
  		int attrlen;
  
  		if (!rtnh_ok(rtnh, remaining))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
  			return -EINVAL;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
422

71fceff0e   David S. Miller   ipv4: Use less co...
423
424
425
426
  		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...
427
428
429
430
431
432
  
  		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...
433
  			nexthop_nh->nh_gw = nla ? nla_get_be32(nla) : 0;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
434
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
435
  			nla = nla_find(attrs, attrlen, RTA_FLOW);
71fceff0e   David S. Miller   ipv4: Use less co...
436
  			nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0;
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
437
  			if (nexthop_nh->nh_tclassid)
f4530fa57   David S. Miller   ipv4: Avoid overh...
438
  				fi->fib_net->ipv4.fib_num_tclassid_users++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
  #endif
  		}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
441
442
  
  		rtnh = rtnh_next(rtnh, &remaining);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  	} endfor_nexthops(fi);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
444

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
447
448
  	return 0;
  }
  
  #endif
4e902c574   Thomas Graf   [IPv4]: FIB confi...
449
  int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
  {
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
452
453
  	struct rtnexthop *rtnh;
  	int remaining;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
  #endif
4e902c574   Thomas Graf   [IPv4]: FIB confi...
455
  	if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  		return 1;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
457
458
459
  	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
460
461
462
463
464
  			return 0;
  		return 1;
  	}
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
465
  	if (cfg->fc_mp == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
  		return 0;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
467
468
469
  
  	rtnh = cfg->fc_mp;
  	remaining = cfg->fc_mp_len;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
470

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

4e902c574   Thomas Graf   [IPv4]: FIB confi...
474
  		if (!rtnh_ok(rtnh, remaining))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
  			return -EINVAL;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
476
477
  
  		if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->nh_oif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
  			return 1;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
479
480
481
482
483
484
  
  		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,...
485
  			if (nla && nla_get_be32(nla) != nh->nh_gw)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  				return 1;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
487
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
488
489
  			nla = nla_find(attrs, attrlen, RTA_FLOW);
  			if (nla && nla_get_u32(nla) != nh->nh_tclassid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
  				return 1;
  #endif
  		}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
493
494
  
  		rtnh = rtnh_next(rtnh, &remaining);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
500
501
  	} endfor_nexthops(fi);
  #endif
  	return 0;
  }
  
  
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
   * 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
543
   */
4e902c574   Thomas Graf   [IPv4]: FIB confi...
544
545
  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
546
547
  {
  	int err;
86167a377   Denis V. Lunev   [NETNS]: Pass cor...
548
  	struct net *net;
6a31d2a97   Eric Dumazet   fib: cleanups
549
  	struct net_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550

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

4e902c574   Thomas Graf   [IPv4]: FIB confi...
556
  			if (cfg->fc_scope >= RT_SCOPE_LINK)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
  				return -EINVAL;
86167a377   Denis V. Lunev   [NETNS]: Pass cor...
558
  			if (inet_addr_type(net, nh->nh_gw) != RTN_UNICAST)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  				return -EINVAL;
6a31d2a97   Eric Dumazet   fib: cleanups
560
561
  			dev = __dev_get_by_index(net, nh->nh_oif);
  			if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
  				return -ENODEV;
6a31d2a97   Eric Dumazet   fib: cleanups
563
  			if (!(dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
569
  				return -ENETDOWN;
  			nh->nh_dev = dev;
  			dev_hold(dev);
  			nh->nh_scope = RT_SCOPE_LINK;
  			return 0;
  		}
ebc0ffae5   Eric Dumazet   fib: RCU conversi...
570
  		rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
  		{
9ade22861   David S. Miller   ipv4: Use flowi4 ...
572
573
574
575
  			struct flowi4 fl4 = {
  				.daddr = nh->nh_gw,
  				.flowi4_scope = cfg->fc_scope + 1,
  				.flowi4_oif = nh->nh_oif,
4e902c574   Thomas Graf   [IPv4]: FIB confi...
576
  			};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
  
  			/* It is not necessary, but requires a bit of thinking */
9ade22861   David S. Miller   ipv4: Use flowi4 ...
579
580
581
  			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...
582
583
  			if (err) {
  				rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
  				return err;
ebc0ffae5   Eric Dumazet   fib: RCU conversi...
585
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
589
590
591
  		}
  		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
592
593
  		nh->nh_dev = dev = FIB_RES_DEV(res);
  		if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
  			goto out;
6a31d2a97   Eric Dumazet   fib: cleanups
595
  		dev_hold(dev);
8723e1b4a   Eric Dumazet   inet: RCU changes...
596
  		err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
  	} else {
  		struct in_device *in_dev;
6a31d2a97   Eric Dumazet   fib: cleanups
599
  		if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
  			return -EINVAL;
8723e1b4a   Eric Dumazet   inet: RCU changes...
601
602
  		rcu_read_lock();
  		err = -ENODEV;
86167a377   Denis V. Lunev   [NETNS]: Pass cor...
603
  		in_dev = inetdev_by_index(net, nh->nh_oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  		if (in_dev == NULL)
8723e1b4a   Eric Dumazet   inet: RCU changes...
605
606
607
608
  			goto out;
  		err = -ENETDOWN;
  		if (!(in_dev->dev->flags & IFF_UP))
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
611
  		nh->nh_dev = in_dev->dev;
  		dev_hold(nh->nh_dev);
  		nh->nh_scope = RT_SCOPE_HOST;
8723e1b4a   Eric Dumazet   inet: RCU changes...
612
  		err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  	}
8723e1b4a   Eric Dumazet   inet: RCU changes...
614
615
616
  out:
  	rcu_read_unlock();
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  }
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
618
  static inline unsigned int fib_laddr_hashfn(__be32 val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
  {
123b9731b   David S. Miller   ipv4: Rename fib_...
620
  	unsigned int mask = (fib_info_hash_size - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621

6a31d2a97   Eric Dumazet   fib: cleanups
622
623
624
  	return ((__force u32)val ^
  		((__force u32)val >> 7) ^
  		((__force u32)val >> 14)) & mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
  }
123b9731b   David S. Miller   ipv4: Rename fib_...
626
  static struct hlist_head *fib_info_hash_alloc(int bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
  {
  	if (bytes <= PAGE_SIZE)
88f834916   Joonwoo Park   [IPV4] fib_semant...
629
  		return kzalloc(bytes, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
  	else
  		return (struct hlist_head *)
6a31d2a97   Eric Dumazet   fib: cleanups
632
633
  			__get_free_pages(GFP_KERNEL | __GFP_ZERO,
  					 get_order(bytes));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  }
123b9731b   David S. Miller   ipv4: Rename fib_...
635
  static void fib_info_hash_free(struct hlist_head *hash, int bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
640
641
642
643
644
  {
  	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_...
645
646
647
  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
648
  {
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
649
  	struct hlist_head *old_info_hash, *old_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
650
  	unsigned int old_size = fib_info_hash_size;
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
651
  	unsigned int i, bytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652

832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
653
  	spin_lock_bh(&fib_info_lock);
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
654
655
  	old_info_hash = fib_info_hash;
  	old_laddrhash = fib_info_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
656
  	fib_info_hash_size = new_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
  
  	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...
693
  	spin_unlock_bh(&fib_info_lock);
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
694
695
  
  	bytes = old_size * sizeof(struct hlist_head *);
123b9731b   David S. Miller   ipv4: Rename fib_...
696
697
  	fib_info_hash_free(old_info_hash, bytes);
  	fib_info_hash_free(old_laddrhash, bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
  }
436c3b66e   David S. Miller   ipv4: Invalidate ...
699
700
701
702
  __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...
703
  					nh->nh_parent->fib_scope);
436c3b66e   David S. Miller   ipv4: Invalidate ...
704
705
706
707
  	nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid);
  
  	return nh->nh_saddr;
  }
4e902c574   Thomas Graf   [IPv4]: FIB confi...
708
  struct fib_info *fib_create_info(struct fib_config *cfg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
712
  {
  	int err;
  	struct fib_info *fi = NULL;
  	struct fib_info *ofi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
  	int nhs = 1;
7462bd744   Denis V. Lunev   [NETNS]: Add a na...
714
  	struct net *net = cfg->fc_nlinfo.nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715

4c8237cd7   David S. Miller   ipv4: Validate ro...
716
717
  	if (cfg->fc_type > RTN_MAX)
  		goto err_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
  	/* Fast check to catch the most weird cases */
4e902c574   Thomas Graf   [IPv4]: FIB confi...
719
  	if (fib_props[cfg->fc_type].scope > cfg->fc_scope)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
  		goto err_inval;
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
723
724
  	if (cfg->fc_mp) {
  		nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
726
727
728
  		if (nhs == 0)
  			goto err_inval;
  	}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
730
  
  	err = -ENOBUFS;
123b9731b   David S. Miller   ipv4: Rename fib_...
731
732
  	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
733
734
735
736
737
738
739
  		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_...
740
741
  		new_info_hash = fib_info_hash_alloc(bytes);
  		new_laddrhash = fib_info_hash_alloc(bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
  		if (!new_info_hash || !new_laddrhash) {
123b9731b   David S. Miller   ipv4: Rename fib_...
743
744
  			fib_info_hash_free(new_info_hash, bytes);
  			fib_info_hash_free(new_laddrhash, bytes);
88f834916   Joonwoo Park   [IPV4] fib_semant...
745
  		} else
123b9731b   David S. Miller   ipv4: Rename fib_...
746
  			fib_info_hash_move(new_info_hash, new_laddrhash, new_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747

123b9731b   David S. Miller   ipv4: Rename fib_...
748
  		if (!fib_info_hash_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
750
  			goto failure;
  	}
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
751
  	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
  	if (fi == NULL)
  		goto failure;
725d1e1b4   David S. Miller   ipv4: Attach FIB ...
754
755
756
757
758
759
  	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
760
  	fib_info_cnt++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761

57d7a6009   Denis V. Lunev   [NETNS]: Add netn...
762
  	fi->fib_net = hold_net(net);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
763
  	fi->fib_protocol = cfg->fc_protocol;
37e826c51   David S. Miller   ipv4: Fix nexthop...
764
  	fi->fib_scope = cfg->fc_scope;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
765
766
767
  	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
768
769
770
  
  	fi->fib_nhs = nhs;
  	change_nexthops(fi) {
71fceff0e   David S. Miller   ipv4: Use less co...
771
  		nexthop_nh->nh_parent = fi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
  	} endfor_nexthops(fi)
4e902c574   Thomas Graf   [IPv4]: FIB confi...
773
774
775
776
777
  	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...
778
  			int type = nla_type(nla);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
779
780
  
  			if (type) {
6fac26252   David S. Miller   ipv4: Cap ADVMSS ...
781
  				u32 val;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
782
  				if (type > RTAX_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
  					goto err_inval;
6fac26252   David S. Miller   ipv4: Cap ADVMSS ...
784
785
786
  				val = nla_get_u32(nla);
  				if (type == RTAX_ADVMSS && val > 65535 - 40)
  					val = 65535 - 40;
710ab6c03   David S. Miller   ipv4: Enforce max...
787
788
  				if (type == RTAX_MTU && val > 65535 - 15)
  					val = 65535 - 15;
6fac26252   David S. Miller   ipv4: Cap ADVMSS ...
789
  				fi->fib_metrics[type - 1] = val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793

4e902c574   Thomas Graf   [IPv4]: FIB confi...
794
  	if (cfg->fc_mp) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
796
797
  		err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);
  		if (err != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
  			goto failure;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
799
  		if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
  			goto err_inval;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
801
  		if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
  			goto err_inval;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
803
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
804
  		if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
806
807
808
809
810
811
  			goto err_inval;
  #endif
  #else
  		goto err_inval;
  #endif
  	} else {
  		struct fib_nh *nh = fi->fib_nh;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
812
813
814
815
  
  		nh->nh_oif = cfg->fc_oif;
  		nh->nh_gw = cfg->fc_gw;
  		nh->nh_flags = cfg->fc_flags;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
816
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
817
  		nh->nh_tclassid = cfg->fc_flow;
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
818
  		if (nh->nh_tclassid)
f4530fa57   David S. Miller   ipv4: Avoid overh...
819
  			fi->fib_net->ipv4.fib_num_tclassid_users++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
822
823
824
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  		nh->nh_weight = 1;
  #endif
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
825
826
  	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
827
828
  			goto err_inval;
  		goto link_it;
4c8237cd7   David S. Miller   ipv4: Validate ro...
829
830
831
832
833
834
835
836
837
838
839
  	} 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
840
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
841
  	if (cfg->fc_scope > RT_SCOPE_HOST)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
  		goto err_inval;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
843
  	if (cfg->fc_scope == RT_SCOPE_HOST) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
845
846
847
848
849
  		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...
850
  		nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
852
853
854
855
  		err = -ENODEV;
  		if (nh->nh_dev == NULL)
  			goto failure;
  	} else {
  		change_nexthops(fi) {
6a31d2a97   Eric Dumazet   fib: cleanups
856
857
  			err = fib_check_nh(cfg, fi, nexthop_nh);
  			if (err != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
859
860
861
862
  				goto failure;
  		} endfor_nexthops(fi)
  	}
  
  	if (fi->fib_prefsrc) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
863
864
  		if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst ||
  		    fi->fib_prefsrc != cfg->fc_dst)
7462bd744   Denis V. Lunev   [NETNS]: Add a na...
865
  			if (inet_addr_type(net, fi->fib_prefsrc) != RTN_LOCAL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
  				goto err_inval;
  	}
1fc050a13   David S. Miller   ipv4: Cache sourc...
868
  	change_nexthops(fi) {
436c3b66e   David S. Miller   ipv4: Invalidate ...
869
  		fib_info_update_nh_saddr(net, nexthop_nh);
1fc050a13   David S. Miller   ipv4: Cache sourc...
870
  	} endfor_nexthops(fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
  link_it:
6a31d2a97   Eric Dumazet   fib: cleanups
872
873
  	ofi = fib_find_info(fi);
  	if (ofi) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
875
876
877
878
879
880
881
  		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...
882
  	spin_lock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
884
885
886
887
888
889
890
891
892
893
  	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...
894
  		if (!nexthop_nh->nh_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895
  			continue;
71fceff0e   David S. Miller   ipv4: Use less co...
896
  		hash = fib_devindex_hashfn(nexthop_nh->nh_dev->ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
  		head = &fib_info_devhash[hash];
71fceff0e   David S. Miller   ipv4: Use less co...
898
  		hlist_add_head(&nexthop_nh->nh_hash, head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
  	} endfor_nexthops(fi)
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
900
  	spin_unlock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
902
903
904
905
906
  	return fi;
  
  err_inval:
  	err = -EINVAL;
  
  failure:
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
907
  	if (fi) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
909
910
  		fi->fib_dead = 1;
  		free_fib_info(fi);
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
911
912
  
  	return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913
  }
be403ea18   Thomas Graf   [IPv4]: Convert F...
914
  int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
37e826c51   David S. Miller   ipv4: Fix nexthop...
915
  		  u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos,
be403ea18   Thomas Graf   [IPv4]: Convert F...
916
  		  struct fib_info *fi, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
  {
be403ea18   Thomas Graf   [IPv4]: Convert F...
918
  	struct nlmsghdr *nlh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
  	struct rtmsg *rtm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920

be403ea18   Thomas Graf   [IPv4]: Convert F...
921
922
  	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags);
  	if (nlh == NULL)
26932566a   Patrick McHardy   [NETLINK]: Don't ...
923
  		return -EMSGSIZE;
be403ea18   Thomas Graf   [IPv4]: Convert F...
924
925
  
  	rtm = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
927
928
929
  	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 ...
930
931
932
933
  	if (tb_id < 256)
  		rtm->rtm_table = tb_id;
  	else
  		rtm->rtm_table = RT_TABLE_COMPAT;
f3756b79e   David S. Miller   ipv4: Stop using ...
934
935
  	if (nla_put_u32(skb, RTA_TABLE, tb_id))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
936
937
  	rtm->rtm_type = type;
  	rtm->rtm_flags = fi->fib_flags;
37e826c51   David S. Miller   ipv4: Fix nexthop...
938
  	rtm->rtm_scope = fi->fib_scope;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939
  	rtm->rtm_protocol = fi->fib_protocol;
be403ea18   Thomas Graf   [IPv4]: Convert F...
940

f3756b79e   David S. Miller   ipv4: Stop using ...
941
942
943
944
945
946
  	if (rtm->rtm_dst_len &&
  	    nla_put_be32(skb, RTA_DST, dst))
  		goto nla_put_failure;
  	if (fi->fib_priority &&
  	    nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
  	if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0)
be403ea18   Thomas Graf   [IPv4]: Convert F...
948
  		goto nla_put_failure;
f3756b79e   David S. Miller   ipv4: Stop using ...
949
950
951
  	if (fi->fib_prefsrc &&
  	    nla_put_be32(skb, RTA_PREFSRC, fi->fib_prefsrc))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
  	if (fi->fib_nhs == 1) {
f3756b79e   David S. Miller   ipv4: Stop using ...
953
954
955
956
957
958
  		if (fi->fib_nh->nh_gw &&
  		    nla_put_be32(skb, RTA_GATEWAY, fi->fib_nh->nh_gw))
  			goto nla_put_failure;
  		if (fi->fib_nh->nh_oif &&
  		    nla_put_u32(skb, RTA_OIF, fi->fib_nh->nh_oif))
  			goto nla_put_failure;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
959
  #ifdef CONFIG_IP_ROUTE_CLASSID
f3756b79e   David S. Miller   ipv4: Stop using ...
960
961
962
  		if (fi->fib_nh[0].nh_tclassid &&
  		    nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid))
  			goto nla_put_failure;
8265abc08   Patrick McHardy   [IPV4]: Fix nexth...
963
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964
965
966
  	}
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  	if (fi->fib_nhs > 1) {
be403ea18   Thomas Graf   [IPv4]: Convert F...
967
968
969
970
971
972
  		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
973
974
  
  		for_nexthops(fi) {
be403ea18   Thomas Graf   [IPv4]: Convert F...
975
976
977
978
979
980
981
  			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;
f3756b79e   David S. Miller   ipv4: Stop using ...
982
983
984
  			if (nh->nh_gw &&
  			    nla_put_be32(skb, RTA_GATEWAY, nh->nh_gw))
  				goto nla_put_failure;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
985
  #ifdef CONFIG_IP_ROUTE_CLASSID
f3756b79e   David S. Miller   ipv4: Stop using ...
986
987
988
  			if (nh->nh_tclassid &&
  			    nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid))
  				goto nla_put_failure;
8265abc08   Patrick McHardy   [IPV4]: Fix nexth...
989
  #endif
be403ea18   Thomas Graf   [IPv4]: Convert F...
990
991
  			/* length of rtnetlink header + attributes */
  			rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
  		} endfor_nexthops(fi);
be403ea18   Thomas Graf   [IPv4]: Convert F...
993
994
  
  		nla_nest_end(skb, mp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
996
  	}
  #endif
be403ea18   Thomas Graf   [IPv4]: Convert F...
997
  	return nlmsg_end(skb, nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
998

be403ea18   Thomas Graf   [IPv4]: Convert F...
999
  nla_put_failure:
26932566a   Patrick McHardy   [NETLINK]: Don't ...
1000
1001
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
1004
1005
1006
1007
   * 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
1008
   */
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
1009
  int fib_sync_down_addr(struct net *net, __be32 local)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1010
1011
  {
  	int ret = 0;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1012
1013
1014
1015
  	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
1016

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

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1020
  	hlist_for_each_entry(fi, node, head, fib_lhash) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
1021
  		if (!net_eq(fi->fib_net, net))
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
1022
  			continue;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1023
1024
1025
  		if (fi->fib_prefsrc == local) {
  			fi->fib_flags |= RTNH_F_DEAD;
  			ret++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
1027
  		}
  	}
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
  	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
1040

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

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1044
1045
1046
  	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
1047

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1048
1049
1050
1051
1052
1053
  		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
1054
  			if (nexthop_nh->nh_flags & RTNH_F_DEAD)
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1055
  				dead++;
71fceff0e   David S. Miller   ipv4: Use less co...
1056
1057
1058
  			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
1059
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1060
  				spin_lock_bh(&fib_multipath_lock);
71fceff0e   David S. Miller   ipv4: Use less co...
1061
1062
  				fi->fib_power -= nexthop_nh->nh_power;
  				nexthop_nh->nh_power = 0;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1063
  				spin_unlock_bh(&fib_multipath_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064
  #endif
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1065
1066
  				dead++;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1067
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
71fceff0e   David S. Miller   ipv4: Use less co...
1068
  			if (force > 1 && nexthop_nh->nh_dev == dev) {
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1069
1070
  				dead = fi->fib_nhs;
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
  			}
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1072
1073
1074
1075
1076
  #endif
  		} endfor_nexthops(fi)
  		if (dead == fi->fib_nhs) {
  			fi->fib_flags |= RTNH_F_DEAD;
  			ret++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1077
1078
1079
1080
1081
  		}
  	}
  
  	return ret;
  }
0c838ff1a   David S. Miller   ipv4: Consolidate...
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
  /* 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...
1093
  		if (next_fi->fib_scope != res->scope ||
0c838ff1a   David S. Miller   ipv4: Consolidate...
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  		    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...
1134
  	return;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1135
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1136
1137
1138
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
  
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
1139
1140
   * Dead device goes up. We wake up dead nexthops.
   * It takes sense only on multipath routes.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1141
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1142
1143
1144
1145
1146
1147
1148
1149
  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
1150
  	if (!(dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
  		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
1169
  			if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1170
1171
1172
  				alive++;
  				continue;
  			}
71fceff0e   David S. Miller   ipv4: Use less co...
1173
  			if (nexthop_nh->nh_dev == NULL ||
6a31d2a97   Eric Dumazet   fib: cleanups
1174
  			    !(nexthop_nh->nh_dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1175
  				continue;
71fceff0e   David S. Miller   ipv4: Use less co...
1176
1177
  			if (nexthop_nh->nh_dev != dev ||
  			    !__in_dev_get_rtnl(dev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
1179
1180
  				continue;
  			alive++;
  			spin_lock_bh(&fib_multipath_lock);
71fceff0e   David S. Miller   ipv4: Use less co...
1181
1182
  			nexthop_nh->nh_power = 0;
  			nexthop_nh->nh_flags &= ~RTNH_F_DEAD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
  			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
1196
1197
   * The algorithm is suboptimal, but it provides really
   * fair weighted route distribution.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1198
   */
1b7fe5932   David S. Miller   ipv4: Kill flowi ...
1199
  void fib_select_multipath(struct fib_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1200
1201
1202
1203
1204
1205
1206
1207
  {
  	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
1208
  			if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) {
71fceff0e   David S. Miller   ipv4: Use less co...
1209
1210
  				power += nexthop_nh->nh_weight;
  				nexthop_nh->nh_power = nexthop_nh->nh_weight;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
  			}
  		} 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
1224
  	 * it is pretty bad approximation.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1225
1226
1227
1228
1229
  	 */
  
  	w = jiffies % fi->fib_power;
  
  	change_nexthops(fi) {
6a31d2a97   Eric Dumazet   fib: cleanups
1230
  		if (!(nexthop_nh->nh_flags & RTNH_F_DEAD) &&
71fceff0e   David S. Miller   ipv4: Use less co...
1231
  		    nexthop_nh->nh_power) {
6a31d2a97   Eric Dumazet   fib: cleanups
1232
1233
  			w -= nexthop_nh->nh_power;
  			if (w <= 0) {
71fceff0e   David S. Miller   ipv4: Use less co...
1234
  				nexthop_nh->nh_power--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
  				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