Blame view

net/ipv4/fib_semantics.c 52.2 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
  /*
   * 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
9
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
   */
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
11
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
19
20
21
22
  #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...
23
  #include <linux/inetdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
  #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
28
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
29
  #include <linux/slab.h>
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
30
  #include <linux/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31

14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
32
  #include <net/arp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
  #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>
717a8f5b2   David Ahern   ipv4: Add fib_che...
39
  #include <net/ip6_fib.h>
5481d73f8   David Ahern   ipv4: Use accesso...
40
  #include <net/nexthop.h>
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
41
  #include <net/netlink.h>
3c618c1db   David Ahern   net: Rename net/n...
42
  #include <net/rtnh.h>
571e72267   Roopa Prabhu   ipv4: support for...
43
  #include <net/lwtunnel.h>
04b1d4e50   Ido Schimmel   net: core: Make t...
44
  #include <net/fib_notifier.h>
c0a720770   David Ahern   ipv6: Flip to fib...
45
  #include <net/addrconf.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
  
  #include "fib_lookup.h"
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
48
  static DEFINE_SPINLOCK(fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
  static struct hlist_head *fib_info_hash;
  static struct hlist_head *fib_info_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
51
  static unsigned int fib_info_hash_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
  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];
dcb1ecb50   David Ahern   ipv4: Prepare for...
57
58
59
  /* for_nexthops and change_nexthops only used when nexthop object
   * is not set in a fib_info. The logic within can reference fib_nh.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61

6a31d2a97   Eric Dumazet   fib: cleanups
62
63
64
  #define for_nexthops(fi) {						\
  	int nhsel; const struct fib_nh *nh;				\
  	for (nhsel = 0, nh = (fi)->fib_nh;				\
5481d73f8   David Ahern   ipv4: Use accesso...
65
  	     nhsel < fib_info_num_path((fi));				\
6a31d2a97   Eric Dumazet   fib: cleanups
66
67
68
69
70
  	     nh++, nhsel++)
  
  #define change_nexthops(fi) {						\
  	int nhsel; struct fib_nh *nexthop_nh;				\
  	for (nhsel = 0,	nexthop_nh = (struct fib_nh *)((fi)->fib_nh);	\
5481d73f8   David Ahern   ipv4: Use accesso...
71
  	     nhsel < fib_info_num_path((fi));				\
6a31d2a97   Eric Dumazet   fib: cleanups
72
  	     nexthop_nh++, nhsel++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
  
  #else /* CONFIG_IP_ROUTE_MULTIPATH */
  
  /* Hope, that gcc will optimize it to get rid of dummy loop */
6a31d2a97   Eric Dumazet   fib: cleanups
77
78
79
  #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
80

6a31d2a97   Eric Dumazet   fib: cleanups
81
82
83
84
  #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
85
86
87
88
  
  #endif /* CONFIG_IP_ROUTE_MULTIPATH */
  
  #define endfor_nexthops(fi) }
3be0686b6   David S. Miller   ipv4: Inline fib_...
89
  const struct fib_prop fib_props[RTN_MAX + 1] = {
6a31d2a97   Eric Dumazet   fib: cleanups
90
  	[RTN_UNSPEC] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
  		.error	= 0,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
93
94
  	},
  	[RTN_UNICAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
  		.error	= 0,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
97
98
  	},
  	[RTN_LOCAL] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
  		.error	= 0,
  		.scope	= RT_SCOPE_HOST,
6a31d2a97   Eric Dumazet   fib: cleanups
101
102
  	},
  	[RTN_BROADCAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
  		.error	= 0,
  		.scope	= RT_SCOPE_LINK,
6a31d2a97   Eric Dumazet   fib: cleanups
105
106
  	},
  	[RTN_ANYCAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
  		.error	= 0,
  		.scope	= RT_SCOPE_LINK,
6a31d2a97   Eric Dumazet   fib: cleanups
109
110
  	},
  	[RTN_MULTICAST] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
  		.error	= 0,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
113
114
  	},
  	[RTN_BLACKHOLE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
117
118
  	},
  	[RTN_UNREACHABLE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
  		.error	= -EHOSTUNREACH,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
121
122
  	},
  	[RTN_PROHIBIT] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
  		.error	= -EACCES,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
125
126
  	},
  	[RTN_THROW] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
  		.error	= -EAGAIN,
  		.scope	= RT_SCOPE_UNIVERSE,
6a31d2a97   Eric Dumazet   fib: cleanups
129
130
  	},
  	[RTN_NAT] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
133
134
  	},
  	[RTN_XRESOLVE] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
  		.error	= -EINVAL,
  		.scope	= RT_SCOPE_NOWHERE,
6a31d2a97   Eric Dumazet   fib: cleanups
137
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  };
c5038a832   David S. Miller   ipv4: Cache route...
139
140
141
142
143
144
145
146
147
148
149
  static void rt_fibinfo_free(struct rtable __rcu **rtp)
  {
  	struct rtable *rt = rcu_dereference_protected(*rtp, 1);
  
  	if (!rt)
  		return;
  
  	/* Not even needed : RCU_INIT_POINTER(*rtp, NULL);
  	 * because we waited an RCU grace period before calling
  	 * free_fib_info_rcu()
  	 */
95c47f9cf   Wei Wang   ipv4: call dst_de...
150
  	dst_dev_put(&rt->dst);
b838d5e1c   Wei Wang   ipv4: mark DST_NO...
151
  	dst_release_immediate(&rt->dst);
c5038a832   David S. Miller   ipv4: Cache route...
152
  }
a5995e710   David Ahern   ipv4: Move except...
153
  static void free_nh_exceptions(struct fib_nh_common *nhc)
4895c771c   David S. Miller   ipv4: Add FIB nex...
154
  {
caa415270   Eric Dumazet   ipv4: fix a race ...
155
  	struct fnhe_hash_bucket *hash;
4895c771c   David S. Miller   ipv4: Add FIB nex...
156
  	int i;
a5995e710   David Ahern   ipv4: Move except...
157
  	hash = rcu_dereference_protected(nhc->nhc_exceptions, 1);
caa415270   Eric Dumazet   ipv4: fix a race ...
158
159
  	if (!hash)
  		return;
4895c771c   David S. Miller   ipv4: Add FIB nex...
160
161
  	for (i = 0; i < FNHE_HASH_SIZE; i++) {
  		struct fib_nh_exception *fnhe;
5abf7f7e0   Eric Dumazet   ipv4: fix rcu splat
162
  		fnhe = rcu_dereference_protected(hash[i].chain, 1);
4895c771c   David S. Miller   ipv4: Add FIB nex...
163
164
  		while (fnhe) {
  			struct fib_nh_exception *next;
82695b30f   Stephen Hemminger   inet: whitespace ...
165

5abf7f7e0   Eric Dumazet   ipv4: fix rcu splat
166
  			next = rcu_dereference_protected(fnhe->fnhe_next, 1);
c5038a832   David S. Miller   ipv4: Cache route...
167

2ffae99d1   Timo Teräs   ipv4: use next ho...
168
169
  			rt_fibinfo_free(&fnhe->fnhe_rth_input);
  			rt_fibinfo_free(&fnhe->fnhe_rth_output);
c5038a832   David S. Miller   ipv4: Cache route...
170

4895c771c   David S. Miller   ipv4: Add FIB nex...
171
172
173
174
175
176
177
  			kfree(fnhe);
  
  			fnhe = next;
  		}
  	}
  	kfree(hash);
  }
c5038a832   David S. Miller   ipv4: Cache route...
178
  static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp)
d26b3a7c4   Eric Dumazet   ipv4: percpu nh_r...
179
180
181
182
183
184
185
186
187
188
  {
  	int cpu;
  
  	if (!rtp)
  		return;
  
  	for_each_possible_cpu(cpu) {
  		struct rtable *rt;
  
  		rt = rcu_dereference_protected(*per_cpu_ptr(rtp, cpu), 1);
0830106c5   Wei Wang   ipv4: take dst->_...
189
  		if (rt) {
95c47f9cf   Wei Wang   ipv4: call dst_de...
190
  			dst_dev_put(&rt->dst);
b838d5e1c   Wei Wang   ipv4: mark DST_NO...
191
  			dst_release_immediate(&rt->dst);
0830106c5   Wei Wang   ipv4: take dst->_...
192
  		}
d26b3a7c4   Eric Dumazet   ipv4: percpu nh_r...
193
194
195
  	}
  	free_percpu(rtp);
  }
979e276eb   David Ahern   net: Use common n...
196
197
198
199
200
201
  void fib_nh_common_release(struct fib_nh_common *nhc)
  {
  	if (nhc->nhc_dev)
  		dev_put(nhc->nhc_dev);
  
  	lwtstate_put(nhc->nhc_lwtstate);
0f457a366   David Ahern   ipv4: Move cached...
202
203
  	rt_fibinfo_free_cpus(nhc->nhc_pcpu_rth_output);
  	rt_fibinfo_free(&nhc->nhc_rth_input);
a5995e710   David Ahern   ipv4: Move except...
204
  	free_nh_exceptions(nhc);
979e276eb   David Ahern   net: Use common n...
205
206
  }
  EXPORT_SYMBOL_GPL(fib_nh_common_release);
faa041a40   David Ahern   ipv4: Create clea...
207
208
209
210
211
212
  void fib_nh_release(struct net *net, struct fib_nh *fib_nh)
  {
  #ifdef CONFIG_IP_ROUTE_CLASSID
  	if (fib_nh->nh_tclassid)
  		net->ipv4.fib_num_tclassid_users--;
  #endif
979e276eb   David Ahern   net: Use common n...
213
  	fib_nh_common_release(&fib_nh->nh_common);
faa041a40   David Ahern   ipv4: Create clea...
214
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  /* Release a nexthop info record */
19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
216
217
218
  static void free_fib_info_rcu(struct rcu_head *head)
  {
  	struct fib_info *fi = container_of(head, struct fib_info, rcu);
4c7e8084f   David Ahern   ipv4: Plumb suppo...
219
220
221
222
223
224
225
  	if (fi->nh) {
  		nexthop_put(fi->nh);
  	} else {
  		change_nexthops(fi) {
  			fib_nh_release(fi->fib_net, nexthop_nh);
  		} endfor_nexthops(fi);
  	}
e49cc0da7   Yanmin Zhang   ipv4: fix the rcu...
226

cc5f0eb21   David Ahern   net: Move free of...
227
  	ip_fib_metrics_put(fi->fib_metrics);
19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
228
229
  	kfree(fi);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
233
  
  void free_fib_info(struct fib_info *fi)
  {
  	if (fi->fib_dead == 0) {
058bd4d2a   Joe Perches   net: Convert prin...
234
235
  		pr_warn("Freeing alive fib_info %p
  ", fi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
  		return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  	fib_info_cnt--;
faa041a40   David Ahern   ipv4: Create clea...
239

19c1ea14c   Yan, Zheng   ipv4: Fix fib_inf...
240
  	call_rcu(&fi->rcu, free_fib_info_rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  }
b423cb108   Ido Schimmel   ipv4: fib: Export...
242
  EXPORT_SYMBOL_GPL(free_fib_info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
  
  void fib_release_info(struct fib_info *fi)
  {
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
246
  	spin_lock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
250
  	if (fi && --fi->fib_treeref == 0) {
  		hlist_del(&fi->fib_hash);
  		if (fi->fib_prefsrc)
  			hlist_del(&fi->fib_lhash);
4c7e8084f   David Ahern   ipv4: Plumb suppo...
251
252
253
254
255
256
257
258
259
  		if (fi->nh) {
  			list_del(&fi->nh_list);
  		} else {
  			change_nexthops(fi) {
  				if (!nexthop_nh->fib_nh_dev)
  					continue;
  				hlist_del(&nexthop_nh->nh_hash);
  			} endfor_nexthops(fi)
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
  		fi->fib_dead = 1;
  		fib_info_put(fi);
  	}
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
263
  	spin_unlock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  }
5481d73f8   David Ahern   ipv4: Use accesso...
265
  static inline int nh_comp(struct fib_info *fi, struct fib_info *ofi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
  {
5481d73f8   David Ahern   ipv4: Use accesso...
267
  	const struct fib_nh *onh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268

4c7e8084f   David Ahern   ipv4: Plumb suppo...
269
270
271
272
273
  	if (fi->nh || ofi->nh)
  		return nexthop_cmp(fi->nh, ofi->nh) ? 0 : -1;
  
  	if (ofi->fib_nhs == 0)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  	for_nexthops(fi) {
5481d73f8   David Ahern   ipv4: Use accesso...
275
  		onh = fib_info_nh(ofi, nhsel);
b75ed8b1a   David Ahern   ipv4: Rename fib_...
276
  		if (nh->fib_nh_oif != onh->fib_nh_oif ||
a4ea5d43c   David Ahern   ipv4: Add support...
277
  		    nh->fib_nh_gw_family != onh->fib_nh_gw_family ||
b75ed8b1a   David Ahern   ipv4: Rename fib_...
278
  		    nh->fib_nh_scope != onh->fib_nh_scope ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
b75ed8b1a   David Ahern   ipv4: Rename fib_...
280
  		    nh->fib_nh_weight != onh->fib_nh_weight ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  #endif
c7066f70d   Patrick McHardy   netfilter: fix Kc...
282
  #ifdef CONFIG_IP_ROUTE_CLASSID
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
  		    nh->nh_tclassid != onh->nh_tclassid ||
  #endif
b75ed8b1a   David Ahern   ipv4: Rename fib_...
285
286
  		    lwtunnel_cmp_encap(nh->fib_nh_lws, onh->fib_nh_lws) ||
  		    ((nh->fib_nh_flags ^ onh->fib_nh_flags) & ~RTNH_COMPARE_MASK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  			return -1;
a4ea5d43c   David Ahern   ipv4: Add support...
288
289
290
291
292
293
294
295
  
  		if (nh->fib_nh_gw_family == AF_INET &&
  		    nh->fib_nh_gw4 != onh->fib_nh_gw4)
  			return -1;
  
  		if (nh->fib_nh_gw_family == AF_INET6 &&
  		    ipv6_addr_cmp(&nh->fib_nh_gw6, &onh->fib_nh_gw6))
  			return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
  	} endfor_nexthops(fi);
  	return 0;
  }
88ebc72f6   David S. Miller   [IPV4] FIB: Inclu...
299
300
301
302
303
304
305
306
  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;
  }
6c48ea5fe   David Ahern   ipv4: Optimizatio...
307
308
309
310
311
312
313
314
315
316
317
318
319
  static unsigned int fib_info_hashfn_1(int init_val, u8 protocol, u8 scope,
  				      u32 prefsrc, u32 priority)
  {
  	unsigned int val = init_val;
  
  	val ^= (protocol << 8) | scope;
  	val ^= prefsrc;
  	val ^= priority;
  
  	return val;
  }
  
  static unsigned int fib_info_hashfn_result(unsigned int val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  {
123b9731b   David S. Miller   ipv4: Rename fib_...
321
  	unsigned int mask = (fib_info_hash_size - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322

6c48ea5fe   David Ahern   ipv4: Optimizatio...
323
324
325
326
327
328
329
330
331
332
  	return (val ^ (val >> 7) ^ (val >> 12)) & mask;
  }
  
  static inline unsigned int fib_info_hashfn(struct fib_info *fi)
  {
  	unsigned int val;
  
  	val = fib_info_hashfn_1(fi->fib_nhs, fi->fib_protocol,
  				fi->fib_scope, (__force u32)fi->fib_prefsrc,
  				fi->fib_priority);
4c7e8084f   David Ahern   ipv4: Plumb suppo...
333
334
335
336
337
338
339
340
  
  	if (fi->nh) {
  		val ^= fib_devindex_hashfn(fi->nh->id);
  	} else {
  		for_nexthops(fi) {
  			val ^= fib_devindex_hashfn(nh->fib_nh_oif);
  		} endfor_nexthops(fi)
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341

6c48ea5fe   David Ahern   ipv4: Optimizatio...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  	return fib_info_hashfn_result(val);
  }
  
  /* no metrics, only nexthop id */
  static struct fib_info *fib_find_info_nh(struct net *net,
  					 const struct fib_config *cfg)
  {
  	struct hlist_head *head;
  	struct fib_info *fi;
  	unsigned int hash;
  
  	hash = fib_info_hashfn_1(fib_devindex_hashfn(cfg->fc_nh_id),
  				 cfg->fc_protocol, cfg->fc_scope,
  				 (__force u32)cfg->fc_prefsrc,
  				 cfg->fc_priority);
  	hash = fib_info_hashfn_result(hash);
  	head = &fib_info_hash[hash];
  
  	hlist_for_each_entry(fi, head, fib_hash) {
  		if (!net_eq(fi->fib_net, net))
  			continue;
  		if (!fi->nh || fi->nh->id != cfg->fc_nh_id)
  			continue;
  		if (cfg->fc_protocol == fi->fib_protocol &&
  		    cfg->fc_scope == fi->fib_scope &&
  		    cfg->fc_prefsrc == fi->fib_prefsrc &&
  		    cfg->fc_priority == fi->fib_priority &&
  		    cfg->fc_type == fi->fib_type &&
  		    cfg->fc_table == fi->fib_tb_id &&
  		    !((cfg->fc_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK))
  			return fi;
  	}
  
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  }
5481d73f8   David Ahern   ipv4: Use accesso...
377
  static struct fib_info *fib_find_info(struct fib_info *nfi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
  {
  	struct hlist_head *head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
  	struct fib_info *fi;
  	unsigned int hash;
  
  	hash = fib_info_hashfn(nfi);
  	head = &fib_info_hash[hash];
b67bfe0d4   Sasha Levin   hlist: drop the n...
385
  	hlist_for_each_entry(fi, head, fib_hash) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
386
  		if (!net_eq(fi->fib_net, nfi->fib_net))
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
387
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
390
  		if (fi->fib_nhs != nfi->fib_nhs)
  			continue;
  		if (nfi->fib_protocol == fi->fib_protocol &&
37e826c51   David S. Miller   ipv4: Fix nexthop...
391
  		    nfi->fib_scope == fi->fib_scope &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
  		    nfi->fib_prefsrc == fi->fib_prefsrc &&
  		    nfi->fib_priority == fi->fib_priority &&
f4ef85bbd   Eric Dumazet   ipv4: add a fib_t...
394
  		    nfi->fib_type == fi->fib_type &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
  		    memcmp(nfi->fib_metrics, fi->fib_metrics,
fcd13f42c   Eric Dumazet   ipv4: fix fib met...
396
  			   sizeof(u32) * RTAX_MAX) == 0 &&
8a3d03166   Andy Gospodarek   net: track link-s...
397
  		    !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) &&
4c7e8084f   David Ahern   ipv4: Plumb suppo...
398
  		    nh_comp(fi, nfi) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
400
401
402
403
  			return fi;
  	}
  
  	return NULL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  /* Check, that the gateway is already configured.
6a31d2a97   Eric Dumazet   fib: cleanups
405
   * Used only by redirect accept routine.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
   */
d878e72e4   Al Viro   [IPV4]: ip_fib_ch...
407
  int ip_fib_check_default(__be32 gw, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
  {
  	struct hlist_head *head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
  	struct fib_nh *nh;
  	unsigned int hash;
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
412
  	spin_lock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
  
  	hash = fib_devindex_hashfn(dev->ifindex);
  	head = &fib_info_devhash[hash];
b67bfe0d4   Sasha Levin   hlist: drop the n...
416
  	hlist_for_each_entry(nh, head, nh_hash) {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
417
418
419
  		if (nh->fib_nh_dev == dev &&
  		    nh->fib_nh_gw4 == gw &&
  		    !(nh->fib_nh_flags & RTNH_F_DEAD)) {
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
420
  			spin_unlock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
  			return 0;
  		}
  	}
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
424
  	spin_unlock(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
  
  	return -1;
  }
339bf98ff   Thomas Graf   [NETLINK]: Do pre...
428
429
430
431
432
433
  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 */
ea6976399   Daniel Borkmann   net: tcp: add RTA...
434
435
  			 + nla_total_size(4) /* RTA_PREFSRC */
  			 + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */
5481d73f8   David Ahern   ipv4: Use accesso...
436
  	unsigned int nhs = fib_info_num_path(fi);
339bf98ff   Thomas Graf   [NETLINK]: Do pre...
437
438
439
  
  	/* space for nested metrics */
  	payload += nla_total_size((RTAX_MAX * nla_total_size(4)));
4c7e8084f   David Ahern   ipv4: Plumb suppo...
440
441
  	if (fi->nh)
  		payload += nla_total_size(4); /* RTA_NH_ID */
5481d73f8   David Ahern   ipv4: Use accesso...
442
  	if (nhs) {
571e72267   Roopa Prabhu   ipv4: support for...
443
  		size_t nh_encapsize = 0;
5481d73f8   David Ahern   ipv4: Use accesso...
444
  		/* Also handles the special case nhs == 1 */
339bf98ff   Thomas Graf   [NETLINK]: Do pre...
445
446
447
  
  		/* each nexthop is packed in an attribute */
  		size_t nhsize = nla_total_size(sizeof(struct rtnexthop));
dcb1ecb50   David Ahern   ipv4: Prepare for...
448
  		unsigned int i;
339bf98ff   Thomas Graf   [NETLINK]: Do pre...
449
450
451
  
  		/* may contain flow and gateway attribute */
  		nhsize += 2 * nla_total_size(4);
571e72267   Roopa Prabhu   ipv4: support for...
452
  		/* grab encap info */
dcb1ecb50   David Ahern   ipv4: Prepare for...
453
454
455
456
  		for (i = 0; i < fib_info_num_path(fi); i++) {
  			struct fib_nh_common *nhc = fib_info_nhc(fi, i);
  
  			if (nhc->nhc_lwtstate) {
571e72267   Roopa Prabhu   ipv4: support for...
457
458
  				/* RTA_ENCAP_TYPE */
  				nh_encapsize += lwtunnel_get_encap_size(
dcb1ecb50   David Ahern   ipv4: Prepare for...
459
  						nhc->nhc_lwtstate);
571e72267   Roopa Prabhu   ipv4: support for...
460
461
462
  				/* RTA_ENCAP */
  				nh_encapsize +=  nla_total_size(2);
  			}
dcb1ecb50   David Ahern   ipv4: Prepare for...
463
  		}
571e72267   Roopa Prabhu   ipv4: support for...
464

339bf98ff   Thomas Graf   [NETLINK]: Do pre...
465
  		/* all nexthops are packed in a nested attribute */
5481d73f8   David Ahern   ipv4: Use accesso...
466
  		payload += nla_total_size((nhs * nhsize) + nh_encapsize);
571e72267   Roopa Prabhu   ipv4: support for...
467

339bf98ff   Thomas Graf   [NETLINK]: Do pre...
468
469
470
471
  	}
  
  	return payload;
  }
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
472
  void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
9877b2538   Joe Perches   fib: Use const st...
473
  	       int dst_len, u32 tb_id, const struct nl_info *info,
b8f558313   Milan Kocian   [RTNETLINK]: Fix ...
474
  	       unsigned int nlm_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
  {
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
476
  	struct fib_rt_info fri;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
  	struct sk_buff *skb;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
478
  	u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
479
  	int err = -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480

339bf98ff   Thomas Graf   [NETLINK]: Do pre...
481
  	skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL);
51456b291   Ian Morris   ipv4: coding styl...
482
  	if (!skb)
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
483
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484

1e301fd04   Ido Schimmel   ipv4: Encapsulate...
485
486
487
488
489
490
  	fri.fi = fa->fa_info;
  	fri.tb_id = tb_id;
  	fri.dst = key;
  	fri.dst_len = dst_len;
  	fri.tos = fa->fa_tos;
  	fri.type = fa->fa_type;
90b93f1b3   Ido Schimmel   ipv4: Add "offloa...
491
492
  	fri.offload = fa->offload;
  	fri.trap = fa->trap;
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
493
  	err = fib_dump_info(skb, info->portid, seq, event, &fri, nlm_flags);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
494
495
496
497
498
499
  	if (err < 0) {
  		/* -EMSGSIZE implies BUG in fib_nlmsg_size() */
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
500
  	rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV4_ROUTE,
1ce85fe40   Pablo Neira Ayuso   netlink: change n...
501
502
  		    info->nlh, GFP_KERNEL);
  	return;
f21c7bc5f   Thomas Graf   [IPv4] route: Con...
503
504
  errout:
  	if (err < 0)
4d1169c1e   Denis V. Lunev   [NETNS]: Add netn...
505
  		rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
  }
c9cb6b6ec   Stephen Hemminger   ipv4: make fib_de...
507
508
509
  static int fib_detect_death(struct fib_info *fi, int order,
  			    struct fib_info **last_resort, int *last_idx,
  			    int dflt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
  {
619d18262   David Ahern   ipv4: Handle ipv6...
511
  	const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
  	struct neighbour *n;
  	int state = NUD_NONE;
619d18262   David Ahern   ipv4: Handle ipv6...
514
515
516
517
518
519
520
  	if (likely(nhc->nhc_gw_family == AF_INET))
  		n = neigh_lookup(&arp_tbl, &nhc->nhc_gw.ipv4, nhc->nhc_dev);
  	else if (nhc->nhc_gw_family == AF_INET6)
  		n = neigh_lookup(ipv6_stub->nd_tbl, &nhc->nhc_gw.ipv6,
  				 nhc->nhc_dev);
  	else
  		n = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
523
  	if (n) {
  		state = n->nud_state;
  		neigh_release(n);
88f643203   Julian Anastasov   ipv4: be more agg...
524
525
  	} else {
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  	}
d9319100c   Jianjun Kong   net: clean up net...
527
  	if (state == NUD_REACHABLE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  		return 0;
6a31d2a97   Eric Dumazet   fib: cleanups
529
  	if ((state & NUD_VALID) && order != dflt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
  		return 0;
6a31d2a97   Eric Dumazet   fib: cleanups
531
  	if ((state & NUD_VALID) ||
88f643203   Julian Anastasov   ipv4: be more agg...
532
  	    (*last_idx < 0 && order > dflt && state != NUD_INCOMPLETE)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
537
  		*last_resort = fi;
  		*last_idx = order;
  	}
  	return 1;
  }
faee67694   Alexander Aring   net: add net avai...
538
539
540
  int fib_nh_common_init(struct net *net, struct fib_nh_common *nhc,
  		       struct nlattr *encap, u16 encap_type,
  		       void *cfg, gfp_t gfp_flags,
979e276eb   David Ahern   net: Use common n...
541
542
  		       struct netlink_ext_ack *extack)
  {
0f457a366   David Ahern   ipv4: Move cached...
543
544
545
546
547
548
  	int err;
  
  	nhc->nhc_pcpu_rth_output = alloc_percpu_gfp(struct rtable __rcu *,
  						    gfp_flags);
  	if (!nhc->nhc_pcpu_rth_output)
  		return -ENOMEM;
979e276eb   David Ahern   net: Use common n...
549
550
  	if (encap) {
  		struct lwtunnel_state *lwtstate;
979e276eb   David Ahern   net: Use common n...
551
552
553
  
  		if (encap_type == LWTUNNEL_ENCAP_NONE) {
  			NL_SET_ERR_MSG(extack, "LWT encap type not specified");
0f457a366   David Ahern   ipv4: Move cached...
554
555
  			err = -EINVAL;
  			goto lwt_failure;
979e276eb   David Ahern   net: Use common n...
556
  		}
faee67694   Alexander Aring   net: add net avai...
557
558
559
  		err = lwtunnel_build_state(net, encap_type, encap,
  					   nhc->nhc_family, cfg, &lwtstate,
  					   extack);
979e276eb   David Ahern   net: Use common n...
560
  		if (err)
0f457a366   David Ahern   ipv4: Move cached...
561
  			goto lwt_failure;
979e276eb   David Ahern   net: Use common n...
562
563
564
565
566
  
  		nhc->nhc_lwtstate = lwtstate_get(lwtstate);
  	}
  
  	return 0;
0f457a366   David Ahern   ipv4: Move cached...
567
568
569
570
571
  
  lwt_failure:
  	rt_fibinfo_free_cpus(nhc->nhc_pcpu_rth_output);
  	nhc->nhc_pcpu_rth_output = NULL;
  	return err;
979e276eb   David Ahern   net: Use common n...
572
573
  }
  EXPORT_SYMBOL_GPL(fib_nh_common_init);
e4516ef65   David Ahern   ipv4: Create init...
574
575
576
577
  int fib_nh_init(struct net *net, struct fib_nh *nh,
  		struct fib_config *cfg, int nh_weight,
  		struct netlink_ext_ack *extack)
  {
0f457a366   David Ahern   ipv4: Move cached...
578
  	int err;
e4516ef65   David Ahern   ipv4: Create init...
579

f1741730d   David Ahern   net: Add fib_nh_c...
580
  	nh->fib_nh_family = AF_INET;
faee67694   Alexander Aring   net: add net avai...
581
  	err = fib_nh_common_init(net, &nh->nh_common, cfg->fc_encap,
979e276eb   David Ahern   net: Use common n...
582
583
  				 cfg->fc_encap_type, cfg, GFP_KERNEL, extack);
  	if (err)
0f457a366   David Ahern   ipv4: Move cached...
584
  		return err;
e4516ef65   David Ahern   ipv4: Create init...
585

b75ed8b1a   David Ahern   ipv4: Rename fib_...
586
  	nh->fib_nh_oif = cfg->fc_oif;
a4ea5d43c   David Ahern   ipv4: Add support...
587
588
  	nh->fib_nh_gw_family = cfg->fc_gw_family;
  	if (cfg->fc_gw_family == AF_INET)
f35b794b3   David Ahern   ipv4: Prepare fib...
589
  		nh->fib_nh_gw4 = cfg->fc_gw4;
a4ea5d43c   David Ahern   ipv4: Add support...
590
591
  	else if (cfg->fc_gw_family == AF_INET6)
  		nh->fib_nh_gw6 = cfg->fc_gw6;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
592
  	nh->fib_nh_flags = cfg->fc_flags;
e4516ef65   David Ahern   ipv4: Create init...
593
594
595
596
597
598
599
  
  #ifdef CONFIG_IP_ROUTE_CLASSID
  	nh->nh_tclassid = cfg->fc_flow;
  	if (nh->nh_tclassid)
  		net->ipv4.fib_num_tclassid_users++;
  #endif
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
b75ed8b1a   David Ahern   ipv4: Rename fib_...
600
  	nh->fib_nh_weight = nh_weight;
e4516ef65   David Ahern   ipv4: Create init...
601
602
  #endif
  	return 0;
e4516ef65   David Ahern   ipv4: Create init...
603
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
6d8422a17   David Ahern   net: ipv4: Plumb ...
605
606
  static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining,
  			      struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
  {
  	int nhs = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609

4e902c574   Thomas Graf   [IPv4]: FIB confi...
610
  	while (rtnh_ok(rtnh, remaining)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  		nhs++;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
612
613
614
615
  		rtnh = rtnh_next(rtnh, &remaining);
  	}
  
  	/* leftover implies invalid nexthop configuration, discard it */
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
616
617
618
619
620
621
622
  	if (remaining > 0) {
  		NL_SET_ERR_MSG(extack,
  			       "Invalid nexthop configuration - extra data after nexthops");
  		nhs = 0;
  	}
  
  	return nhs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  }
4c7e8084f   David Ahern   ipv4: Plumb suppo...
624
  /* only called when fib_nh is integrated into fib_info */
4e902c574   Thomas Graf   [IPv4]: FIB confi...
625
  static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
6d8422a17   David Ahern   net: ipv4: Plumb ...
626
627
  		       int remaining, struct fib_config *cfg,
  		       struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
  {
e4516ef65   David Ahern   ipv4: Create init...
629
630
  	struct net *net = fi->fib_net;
  	struct fib_config fib_cfg;
5481d73f8   David Ahern   ipv4: Use accesso...
631
  	struct fib_nh *nh;
571e72267   Roopa Prabhu   ipv4: support for...
632
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
  	change_nexthops(fi) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
634
  		int attrlen;
e4516ef65   David Ahern   ipv4: Create init...
635
  		memset(&fib_cfg, 0, sizeof(fib_cfg));
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
636
637
638
  		if (!rtnh_ok(rtnh, remaining)) {
  			NL_SET_ERR_MSG(extack,
  				       "Invalid nexthop configuration - extra data after nexthop");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  			return -EINVAL;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
640
  		}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
641

c3ab2b4ec   David Ahern   net: ipv4: Add ex...
642
643
644
  		if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) {
  			NL_SET_ERR_MSG(extack,
  				       "Invalid flags for nexthop - can not contain DEAD or LINKDOWN");
80610229e   Julian Anastasov   ipv4: reject RTNH...
645
  			return -EINVAL;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
646
  		}
80610229e   Julian Anastasov   ipv4: reject RTNH...
647

e4516ef65   David Ahern   ipv4: Create init...
648
649
  		fib_cfg.fc_flags = (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags;
  		fib_cfg.fc_oif = rtnh->rtnh_ifindex;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
650
651
652
  
  		attrlen = rtnh_attrlen(rtnh);
  		if (attrlen > 0) {
d15662682   David Ahern   ipv4: Allow ipv6 ...
653
  			struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
654
655
  
  			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
d15662682   David Ahern   ipv4: Allow ipv6 ...
656
657
658
659
660
661
  			nlav = nla_find(attrs, attrlen, RTA_VIA);
  			if (nla && nlav) {
  				NL_SET_ERR_MSG(extack,
  					       "Nexthop configuration can not contain both GATEWAY and VIA");
  				return -EINVAL;
  			}
f35b794b3   David Ahern   ipv4: Prepare fib...
662
  			if (nla) {
f35b794b3   David Ahern   ipv4: Prepare fib...
663
  				fib_cfg.fc_gw4 = nla_get_in_addr(nla);
d73f80f92   David Ahern   ipv4: Handle RTA_...
664
665
  				if (fib_cfg.fc_gw4)
  					fib_cfg.fc_gw_family = AF_INET;
d15662682   David Ahern   ipv4: Allow ipv6 ...
666
667
668
669
  			} else if (nlav) {
  				ret = fib_gw_from_via(&fib_cfg, nlav, extack);
  				if (ret)
  					goto errout;
f35b794b3   David Ahern   ipv4: Prepare fib...
670
  			}
e4516ef65   David Ahern   ipv4: Create init...
671

4e902c574   Thomas Graf   [IPv4]: FIB confi...
672
  			nla = nla_find(attrs, attrlen, RTA_FLOW);
e4516ef65   David Ahern   ipv4: Create init...
673
674
675
676
677
678
679
  			if (nla)
  				fib_cfg.fc_flow = nla_get_u32(nla);
  
  			fib_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
  			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
  			if (nla)
  				fib_cfg.fc_encap_type = nla_get_u16(nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
  		}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
681

e4516ef65   David Ahern   ipv4: Create init...
682
683
684
685
  		ret = fib_nh_init(net, nexthop_nh, &fib_cfg,
  				  rtnh->rtnh_hops + 1, extack);
  		if (ret)
  			goto errout;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
686
  		rtnh = rtnh_next(rtnh, &remaining);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
  	} endfor_nexthops(fi);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
688

571e72267   Roopa Prabhu   ipv4: support for...
689
  	ret = -EINVAL;
5481d73f8   David Ahern   ipv4: Use accesso...
690
691
  	nh = fib_info_nh(fi, 0);
  	if (cfg->fc_oif && nh->fib_nh_oif != cfg->fc_oif) {
e4516ef65   David Ahern   ipv4: Create init...
692
693
694
695
  		NL_SET_ERR_MSG(extack,
  			       "Nexthop device index does not match RTA_OIF");
  		goto errout;
  	}
f35b794b3   David Ahern   ipv4: Prepare fib...
696
  	if (cfg->fc_gw_family) {
5481d73f8   David Ahern   ipv4: Use accesso...
697
  		if (cfg->fc_gw_family != nh->fib_nh_gw_family ||
f35b794b3   David Ahern   ipv4: Prepare fib...
698
  		    (cfg->fc_gw_family == AF_INET &&
5481d73f8   David Ahern   ipv4: Use accesso...
699
  		     nh->fib_nh_gw4 != cfg->fc_gw4) ||
a4ea5d43c   David Ahern   ipv4: Add support...
700
  		    (cfg->fc_gw_family == AF_INET6 &&
5481d73f8   David Ahern   ipv4: Use accesso...
701
  		     ipv6_addr_cmp(&nh->fib_nh_gw6, &cfg->fc_gw6))) {
f35b794b3   David Ahern   ipv4: Prepare fib...
702
  			NL_SET_ERR_MSG(extack,
a4ea5d43c   David Ahern   ipv4: Add support...
703
  				       "Nexthop gateway does not match RTA_GATEWAY or RTA_VIA");
f35b794b3   David Ahern   ipv4: Prepare fib...
704
705
  			goto errout;
  		}
e4516ef65   David Ahern   ipv4: Create init...
706
707
  	}
  #ifdef CONFIG_IP_ROUTE_CLASSID
5481d73f8   David Ahern   ipv4: Use accesso...
708
  	if (cfg->fc_flow && nh->nh_tclassid != cfg->fc_flow) {
e4516ef65   David Ahern   ipv4: Create init...
709
710
711
712
713
714
  		NL_SET_ERR_MSG(extack,
  			       "Nexthop class id does not match RTA_FLOW");
  		goto errout;
  	}
  #endif
  	ret = 0;
571e72267   Roopa Prabhu   ipv4: support for...
715
716
  errout:
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  }
4c7e8084f   David Ahern   ipv4: Plumb suppo...
718
  /* only called when fib_nh is integrated into fib_info */
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
719
720
721
722
  static void fib_rebalance(struct fib_info *fi)
  {
  	int total;
  	int w;
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
723

5481d73f8   David Ahern   ipv4: Use accesso...
724
  	if (fib_info_num_path(fi) < 2)
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
725
726
727
728
  		return;
  
  	total = 0;
  	for_nexthops(fi) {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
729
  		if (nh->fib_nh_flags & RTNH_F_DEAD)
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
730
  			continue;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
731
732
  		if (ip_ignore_linkdown(nh->fib_nh_dev) &&
  		    nh->fib_nh_flags & RTNH_F_LINKDOWN)
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
733
  			continue;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
734
  		total += nh->fib_nh_weight;
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
735
736
737
738
739
  	} endfor_nexthops(fi);
  
  	w = 0;
  	change_nexthops(fi) {
  		int upper_bound;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
740
  		if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
741
  			upper_bound = -1;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
742
743
  		} else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) &&
  			   nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) {
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
744
745
  			upper_bound = -1;
  		} else {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
746
  			w += nexthop_nh->fib_nh_weight;
0a837fe47   Peter Nørlund   ipv4: Fix compila...
747
748
  			upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31,
  							    total) - 1;
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
749
  		}
b75ed8b1a   David Ahern   ipv4: Rename fib_...
750
  		atomic_set(&nexthop_nh->fib_nh_upper_bound, upper_bound);
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
751
  	} endfor_nexthops(fi);
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
752
  }
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
753
  #else /* CONFIG_IP_ROUTE_MULTIPATH */
8373c6c84   David Ahern   ipv4: Define fib_...
754
755
756
757
758
759
760
761
  static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
  		       int remaining, struct fib_config *cfg,
  		       struct netlink_ext_ack *extack)
  {
  	NL_SET_ERR_MSG(extack, "Multipath support not enabled in kernel");
  
  	return -EINVAL;
  }
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
762
  #define fib_rebalance(fi) do { } while (0)
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
763
764
  
  #endif /* CONFIG_IP_ROUTE_MULTIPATH */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765

faee67694   Alexander Aring   net: add net avai...
766
  static int fib_encap_match(struct net *net, u16 encap_type,
e01286ef0   Ying Xue   ipv4: Make fib_en...
767
  			   struct nlattr *encap,
30357d7d8   David Ahern   lwtunnel: remove ...
768
  			   const struct fib_nh *nh,
9ae287274   David Ahern   net: add extack a...
769
770
  			   const struct fib_config *cfg,
  			   struct netlink_ext_ack *extack)
571e72267   Roopa Prabhu   ipv4: support for...
771
772
  {
  	struct lwtunnel_state *lwtstate;
df383e624   Jiri Benc   lwtunnel: fix mem...
773
  	int ret, result = 0;
571e72267   Roopa Prabhu   ipv4: support for...
774
775
776
  
  	if (encap_type == LWTUNNEL_ENCAP_NONE)
  		return 0;
faee67694   Alexander Aring   net: add net avai...
777
  	ret = lwtunnel_build_state(net, encap_type, encap, AF_INET,
9ae287274   David Ahern   net: add extack a...
778
  				   cfg, &lwtstate, extack);
df383e624   Jiri Benc   lwtunnel: fix mem...
779
  	if (!ret) {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
780
  		result = lwtunnel_cmp_encap(lwtstate, nh->fib_nh_lws);
df383e624   Jiri Benc   lwtunnel: fix mem...
781
782
  		lwtstate_free(lwtstate);
  	}
571e72267   Roopa Prabhu   ipv4: support for...
783

df383e624   Jiri Benc   lwtunnel: fix mem...
784
  	return result;
571e72267   Roopa Prabhu   ipv4: support for...
785
  }
faee67694   Alexander Aring   net: add net avai...
786
  int fib_nh_match(struct net *net, struct fib_config *cfg, struct fib_info *fi,
9ae287274   David Ahern   net: add extack a...
787
  		 struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  {
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
790
791
  	struct rtnexthop *rtnh;
  	int remaining;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
  #endif
4e902c574   Thomas Graf   [IPv4]: FIB confi...
793
  	if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
  		return 1;
493ced1ac   David Ahern   ipv4: Allow route...
795
796
797
798
799
  	if (cfg->fc_nh_id) {
  		if (fi->nh && cfg->fc_nh_id == fi->nh->id)
  			return 0;
  		return 1;
  	}
f35b794b3   David Ahern   ipv4: Prepare fib...
800
  	if (cfg->fc_oif || cfg->fc_gw_family) {
5481d73f8   David Ahern   ipv4: Use accesso...
801
  		struct fib_nh *nh = fib_info_nh(fi, 0);
571e72267   Roopa Prabhu   ipv4: support for...
802
  		if (cfg->fc_encap) {
faee67694   Alexander Aring   net: add net avai...
803
804
  			if (fib_encap_match(net, cfg->fc_encap_type,
  					    cfg->fc_encap, nh, cfg, extack))
9ae287274   David Ahern   net: add extack a...
805
  				return 1;
571e72267   Roopa Prabhu   ipv4: support for...
806
  		}
a8c6db1df   Stefano Brivio   fib_semantics: Do...
807
808
  #ifdef CONFIG_IP_ROUTE_CLASSID
  		if (cfg->fc_flow &&
5481d73f8   David Ahern   ipv4: Use accesso...
809
  		    cfg->fc_flow != nh->nh_tclassid)
a8c6db1df   Stefano Brivio   fib_semantics: Do...
810
811
  			return 1;
  #endif
5481d73f8   David Ahern   ipv4: Use accesso...
812
  		if ((cfg->fc_oif && cfg->fc_oif != nh->fib_nh_oif) ||
f35b794b3   David Ahern   ipv4: Prepare fib...
813
  		    (cfg->fc_gw_family &&
5481d73f8   David Ahern   ipv4: Use accesso...
814
  		     cfg->fc_gw_family != nh->fib_nh_gw_family))
f35b794b3   David Ahern   ipv4: Prepare fib...
815
816
817
  			return 1;
  
  		if (cfg->fc_gw_family == AF_INET &&
5481d73f8   David Ahern   ipv4: Use accesso...
818
  		    cfg->fc_gw4 != nh->fib_nh_gw4)
f35b794b3   David Ahern   ipv4: Prepare fib...
819
  			return 1;
a4ea5d43c   David Ahern   ipv4: Add support...
820
  		if (cfg->fc_gw_family == AF_INET6 &&
5481d73f8   David Ahern   ipv4: Use accesso...
821
  		    ipv6_addr_cmp(&cfg->fc_gw6, &nh->fib_nh_gw6))
a4ea5d43c   David Ahern   ipv4: Add support...
822
  			return 1;
f35b794b3   David Ahern   ipv4: Prepare fib...
823
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
825
826
  	}
  
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
51456b291   Ian Morris   ipv4: coding styl...
827
  	if (!cfg->fc_mp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
  		return 0;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
829
830
831
  
  	rtnh = cfg->fc_mp;
  	remaining = cfg->fc_mp_len;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
832

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

4e902c574   Thomas Graf   [IPv4]: FIB confi...
836
  		if (!rtnh_ok(rtnh, remaining))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  			return -EINVAL;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
838

b75ed8b1a   David Ahern   ipv4: Rename fib_...
839
  		if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->fib_nh_oif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
  			return 1;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
841
842
  
  		attrlen = rtnh_attrlen(rtnh);
f76936d07   Jiri Pirko   ipv4: fix nexthop...
843
  		if (attrlen > 0) {
d15662682   David Ahern   ipv4: Allow ipv6 ...
844
  			struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
845
846
  
  			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
d15662682   David Ahern   ipv4: Allow ipv6 ...
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
  			nlav = nla_find(attrs, attrlen, RTA_VIA);
  			if (nla && nlav) {
  				NL_SET_ERR_MSG(extack,
  					       "Nexthop configuration can not contain both GATEWAY and VIA");
  				return -EINVAL;
  			}
  
  			if (nla) {
  				if (nh->fib_nh_gw_family != AF_INET ||
  				    nla_get_in_addr(nla) != nh->fib_nh_gw4)
  					return 1;
  			} else if (nlav) {
  				struct fib_config cfg2;
  				int err;
  
  				err = fib_gw_from_via(&cfg2, nlav, extack);
  				if (err)
  					return err;
  
  				switch (nh->fib_nh_gw_family) {
  				case AF_INET:
  					if (cfg2.fc_gw_family != AF_INET ||
  					    cfg2.fc_gw4 != nh->fib_nh_gw4)
  						return 1;
  					break;
  				case AF_INET6:
  					if (cfg2.fc_gw_family != AF_INET6 ||
  					    ipv6_addr_cmp(&cfg2.fc_gw6,
  							  &nh->fib_nh_gw6))
  						return 1;
  					break;
  				}
  			}
c7066f70d   Patrick McHardy   netfilter: fix Kc...
880
  #ifdef CONFIG_IP_ROUTE_CLASSID
4e902c574   Thomas Graf   [IPv4]: FIB confi...
881
882
  			nla = nla_find(attrs, attrlen, RTA_FLOW);
  			if (nla && nla_get_u32(nla) != nh->nh_tclassid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
884
885
  				return 1;
  #endif
  		}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
886
887
  
  		rtnh = rtnh_next(rtnh, &remaining);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
891
  	} endfor_nexthops(fi);
  #endif
  	return 0;
  }
5f9ae3d9e   Xin Long   ipv4: do metrics ...
892
893
894
895
896
897
898
899
900
901
  bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi)
  {
  	struct nlattr *nla;
  	int remaining;
  
  	if (!cfg->fc_mx)
  		return true;
  
  	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
  		int type = nla_type(nla);
d03a45572   Phil Sutter   ipv4: fib: Fix me...
902
  		u32 fi_val, val;
5f9ae3d9e   Xin Long   ipv4: do metrics ...
903
904
905
906
907
908
909
910
911
912
913
  
  		if (!type)
  			continue;
  		if (type > RTAX_MAX)
  			return false;
  
  		if (type == RTAX_CC_ALGO) {
  			char tmp[TCP_CA_NAME_MAX];
  			bool ecn_ca = false;
  
  			nla_strlcpy(tmp, nla, sizeof(tmp));
6670e1524   Stephen Hemminger   tcp: Namespace-if...
914
  			val = tcp_ca_get_key_by_name(fi->fib_net, tmp, &ecn_ca);
5f9ae3d9e   Xin Long   ipv4: do metrics ...
915
  		} else {
5b5e7a0de   Eric Dumazet   net: metrics: add...
916
917
  			if (nla_len(nla) != sizeof(u32))
  				return false;
5f9ae3d9e   Xin Long   ipv4: do metrics ...
918
919
  			val = nla_get_u32(nla);
  		}
d03a45572   Phil Sutter   ipv4: fib: Fix me...
920
921
922
923
924
  		fi_val = fi->fib_metrics->metrics[type - 1];
  		if (type == RTAX_FEATURES)
  			fi_val &= ~DST_FEATURE_ECN_CA;
  
  		if (fi_val != val)
5f9ae3d9e   Xin Long   ipv4: do metrics ...
925
926
927
928
929
  			return false;
  	}
  
  	return true;
  }
717a8f5b2   David Ahern   ipv4: Add fib_che...
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
  static int fib_check_nh_v6_gw(struct net *net, struct fib_nh *nh,
  			      u32 table, struct netlink_ext_ack *extack)
  {
  	struct fib6_config cfg = {
  		.fc_table = table,
  		.fc_flags = nh->fib_nh_flags | RTF_GATEWAY,
  		.fc_ifindex = nh->fib_nh_oif,
  		.fc_gateway = nh->fib_nh_gw6,
  	};
  	struct fib6_nh fib6_nh = {};
  	int err;
  
  	err = ipv6_stub->fib6_nh_init(net, &fib6_nh, &cfg, GFP_KERNEL, extack);
  	if (!err) {
  		nh->fib_nh_dev = fib6_nh.fib_nh_dev;
  		dev_hold(nh->fib_nh_dev);
  		nh->fib_nh_oif = nh->fib_nh_dev->ifindex;
  		nh->fib_nh_scope = RT_SCOPE_LINK;
  
  		ipv6_stub->fib6_nh_release(&fib6_nh);
  	}
  
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
954
955
  
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
   * 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
997
   */
448d72481   David Ahern   ipv4: Refactor fi...
998
999
  static int fib_check_nh_v4_gw(struct net *net, struct fib_nh *nh, u32 table,
  			      u8 scope, struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
  {
6a31d2a97   Eric Dumazet   fib: cleanups
1001
  	struct net_device *dev;
448d72481   David Ahern   ipv4: Refactor fi...
1002
  	struct fib_result res;
c3fee640b   Enrico Weigelt   net: ipv4: fib_se...
1003
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004

448d72481   David Ahern   ipv4: Refactor fi...
1005
1006
  	if (nh->fib_nh_flags & RTNH_F_ONLINK) {
  		unsigned int addr_type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007

448d72481   David Ahern   ipv4: Refactor fi...
1008
1009
1010
  		if (scope >= RT_SCOPE_LINK) {
  			NL_SET_ERR_MSG(extack, "Nexthop has invalid scope");
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
  		}
448d72481   David Ahern   ipv4: Refactor fi...
1012
1013
1014
1015
  		dev = __dev_get_by_index(net, nh->fib_nh_oif);
  		if (!dev) {
  			NL_SET_ERR_MSG(extack, "Nexthop device required for onlink");
  			return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
  		}
448d72481   David Ahern   ipv4: Refactor fi...
1017
1018
1019
  		if (!(dev->flags & IFF_UP)) {
  			NL_SET_ERR_MSG(extack, "Nexthop device is not up");
  			return -ENETDOWN;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1020
  		}
448d72481   David Ahern   ipv4: Refactor fi...
1021
1022
1023
1024
  		addr_type = inet_addr_type_dev_table(net, dev, nh->fib_nh_gw4);
  		if (addr_type != RTN_UNICAST) {
  			NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
  			return -EINVAL;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1025
  		}
8a3d03166   Andy Gospodarek   net: track link-s...
1026
  		if (!netif_carrier_ok(dev))
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1027
  			nh->fib_nh_flags |= RTNH_F_LINKDOWN;
448d72481   David Ahern   ipv4: Refactor fi...
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
  		nh->fib_nh_dev = dev;
  		dev_hold(dev);
  		nh->fib_nh_scope = RT_SCOPE_LINK;
  		return 0;
  	}
  	rcu_read_lock();
  	{
  		struct fib_table *tbl = NULL;
  		struct flowi4 fl4 = {
  			.daddr = nh->fib_nh_gw4,
  			.flowi4_scope = scope + 1,
  			.flowi4_oif = nh->fib_nh_oif,
  			.flowi4_iif = LOOPBACK_IFINDEX,
  		};
  
  		/* It is not necessary, but requires a bit of thinking */
  		if (fl4.flowi4_scope < RT_SCOPE_LINK)
  			fl4.flowi4_scope = RT_SCOPE_LINK;
5eea3a63f   guodeqing   net: Fix the arp ...
1046
  		if (table && table != RT_TABLE_MAIN)
448d72481   David Ahern   ipv4: Refactor fi...
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
  			tbl = fib_get_table(net, table);
  
  		if (tbl)
  			err = fib_table_lookup(tbl, &fl4, &res,
  					       FIB_LOOKUP_IGNORE_LINKSTATE |
  					       FIB_LOOKUP_NOREF);
  
  		/* on error or if no table given do full lookup. This
  		 * is needed for example when nexthops are in the local
  		 * table rather than the given table
  		 */
  		if (!tbl || err) {
  			err = fib_lookup(net, &fl4, &res,
  					 FIB_LOOKUP_IGNORE_LINKSTATE);
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1061
  		}
448d72481   David Ahern   ipv4: Refactor fi...
1062
1063
1064
  
  		if (err) {
  			NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
8723e1b4a   Eric Dumazet   inet: RCU changes...
1065
  			goto out;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1066
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1067
  	}
448d72481   David Ahern   ipv4: Refactor fi...
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
  
  	err = -EINVAL;
  	if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) {
  		NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
  		goto out;
  	}
  	nh->fib_nh_scope = res.scope;
  	nh->fib_nh_oif = FIB_RES_OIF(res);
  	nh->fib_nh_dev = dev = FIB_RES_DEV(res);
  	if (!dev) {
  		NL_SET_ERR_MSG(extack,
  			       "No egress device for nexthop gateway");
  		goto out;
  	}
  	dev_hold(dev);
  	if (!netif_carrier_ok(dev))
  		nh->fib_nh_flags |= RTNH_F_LINKDOWN;
  	err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN;
8723e1b4a   Eric Dumazet   inet: RCU changes...
1086
1087
1088
  out:
  	rcu_read_unlock();
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
  }
448d72481   David Ahern   ipv4: Refactor fi...
1090
1091
1092
1093
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
  
  static int fib_check_nh_nongw(struct net *net, struct fib_nh *nh,
  			      struct netlink_ext_ack *extack)
  {
  	struct in_device *in_dev;
  	int err;
  
  	if (nh->fib_nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) {
  		NL_SET_ERR_MSG(extack,
  			       "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set");
  		return -EINVAL;
  	}
  
  	rcu_read_lock();
  
  	err = -ENODEV;
  	in_dev = inetdev_by_index(net, nh->fib_nh_oif);
  	if (!in_dev)
  		goto out;
  	err = -ENETDOWN;
  	if (!(in_dev->dev->flags & IFF_UP)) {
  		NL_SET_ERR_MSG(extack, "Device for nexthop is not up");
  		goto out;
  	}
  
  	nh->fib_nh_dev = in_dev->dev;
  	dev_hold(nh->fib_nh_dev);
  	nh->fib_nh_scope = RT_SCOPE_HOST;
  	if (!netif_carrier_ok(nh->fib_nh_dev))
  		nh->fib_nh_flags |= RTNH_F_LINKDOWN;
  	err = 0;
  out:
  	rcu_read_unlock();
  	return err;
  }
ac1fab2d1   David Ahern   ipv4: export fib_...
1125
1126
  int fib_check_nh(struct net *net, struct fib_nh *nh, u32 table, u8 scope,
  		 struct netlink_ext_ack *extack)
448d72481   David Ahern   ipv4: Refactor fi...
1127
  {
448d72481   David Ahern   ipv4: Refactor fi...
1128
1129
1130
  	int err;
  
  	if (nh->fib_nh_gw_family == AF_INET)
ac1fab2d1   David Ahern   ipv4: export fib_...
1131
  		err = fib_check_nh_v4_gw(net, nh, table, scope, extack);
717a8f5b2   David Ahern   ipv4: Add fib_che...
1132
1133
  	else if (nh->fib_nh_gw_family == AF_INET6)
  		err = fib_check_nh_v6_gw(net, nh, table, extack);
448d72481   David Ahern   ipv4: Refactor fi...
1134
1135
1136
1137
1138
  	else
  		err = fib_check_nh_nongw(net, nh, extack);
  
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1139

81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
1140
  static inline unsigned int fib_laddr_hashfn(__be32 val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1141
  {
123b9731b   David S. Miller   ipv4: Rename fib_...
1142
  	unsigned int mask = (fib_info_hash_size - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1143

6a31d2a97   Eric Dumazet   fib: cleanups
1144
1145
1146
  	return ((__force u32)val ^
  		((__force u32)val >> 7) ^
  		((__force u32)val >> 14)) & mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1147
  }
123b9731b   David S. Miller   ipv4: Rename fib_...
1148
  static struct hlist_head *fib_info_hash_alloc(int bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1149
1150
  {
  	if (bytes <= PAGE_SIZE)
88f834916   Joonwoo Park   [IPV4] fib_semant...
1151
  		return kzalloc(bytes, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1152
1153
  	else
  		return (struct hlist_head *)
6a31d2a97   Eric Dumazet   fib: cleanups
1154
1155
  			__get_free_pages(GFP_KERNEL | __GFP_ZERO,
  					 get_order(bytes));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1156
  }
123b9731b   David S. Miller   ipv4: Rename fib_...
1157
  static void fib_info_hash_free(struct hlist_head *hash, int bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1158
1159
1160
1161
1162
1163
1164
1165
1166
  {
  	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_...
1167
1168
1169
  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
1170
  {
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
1171
  	struct hlist_head *old_info_hash, *old_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
1172
  	unsigned int old_size = fib_info_hash_size;
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
1173
  	unsigned int i, bytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1174

832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
1175
  	spin_lock_bh(&fib_info_lock);
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
1176
1177
  	old_info_hash = fib_info_hash;
  	old_laddrhash = fib_info_laddrhash;
123b9731b   David S. Miller   ipv4: Rename fib_...
1178
  	fib_info_hash_size = new_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1179
1180
1181
  
  	for (i = 0; i < old_size; i++) {
  		struct hlist_head *head = &fib_info_hash[i];
b67bfe0d4   Sasha Levin   hlist: drop the n...
1182
  		struct hlist_node *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1183
  		struct fib_info *fi;
b67bfe0d4   Sasha Levin   hlist: drop the n...
1184
  		hlist_for_each_entry_safe(fi, n, head, fib_hash) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1185
1186
  			struct hlist_head *dest;
  			unsigned int new_hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1187
1188
1189
1190
1191
1192
1193
1194
1195
  			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];
b67bfe0d4   Sasha Levin   hlist: drop the n...
1196
  		struct hlist_node *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1197
  		struct fib_info *fi;
b67bfe0d4   Sasha Levin   hlist: drop the n...
1198
  		hlist_for_each_entry_safe(fi, n, lhead, fib_lhash) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1199
1200
  			struct hlist_head *ldest;
  			unsigned int new_hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1201
1202
1203
1204
1205
1206
  			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...
1207
  	spin_unlock_bh(&fib_info_lock);
b7656e7f2   David S. Miller   [IPV4]: Fix memor...
1208
1209
  
  	bytes = old_size * sizeof(struct hlist_head *);
123b9731b   David S. Miller   ipv4: Rename fib_...
1210
1211
  	fib_info_hash_free(old_info_hash, bytes);
  	fib_info_hash_free(old_laddrhash, bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1212
  }
dcb1ecb50   David Ahern   ipv4: Prepare for...
1213
1214
  __be32 fib_info_update_nhc_saddr(struct net *net, struct fib_nh_common *nhc,
  				 unsigned char scope)
436c3b66e   David S. Miller   ipv4: Invalidate ...
1215
  {
dcb1ecb50   David Ahern   ipv4: Prepare for...
1216
1217
1218
1219
1220
1221
  	struct fib_nh *nh;
  
  	if (nhc->nhc_family != AF_INET)
  		return inet_select_addr(nhc->nhc_dev, 0, scope);
  
  	nh = container_of(nhc, struct fib_nh, nh_common);
c3669486b   David Ahern   ipv4: export fib_...
1222
  	nh->nh_saddr = inet_select_addr(nh->fib_nh_dev, nh->fib_nh_gw4, scope);
436c3b66e   David S. Miller   ipv4: Invalidate ...
1223
1224
1225
1226
  	nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid);
  
  	return nh->nh_saddr;
  }
eba618aba   David Ahern   ipv4: Add fib_nh_...
1227
1228
1229
  __be32 fib_result_prefsrc(struct net *net, struct fib_result *res)
  {
  	struct fib_nh_common *nhc = res->nhc;
eba618aba   David Ahern   ipv4: Add fib_nh_...
1230
1231
1232
  
  	if (res->fi->fib_prefsrc)
  		return res->fi->fib_prefsrc;
dcb1ecb50   David Ahern   ipv4: Prepare for...
1233
1234
  	if (nhc->nhc_family == AF_INET) {
  		struct fib_nh *nh;
eba618aba   David Ahern   ipv4: Add fib_nh_...
1235

dcb1ecb50   David Ahern   ipv4: Prepare for...
1236
1237
1238
1239
1240
1241
  		nh = container_of(nhc, struct fib_nh, nh_common);
  		if (nh->nh_saddr_genid == atomic_read(&net->ipv4.dev_addr_genid))
  			return nh->nh_saddr;
  	}
  
  	return fib_info_update_nhc_saddr(net, nhc, res->fi->fib_scope);
eba618aba   David Ahern   ipv4: Add fib_nh_...
1242
  }
021dd3b8a   David Ahern   net: Add routes t...
1243
1244
1245
1246
  static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc)
  {
  	if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst ||
  	    fib_prefsrc != cfg->fc_dst) {
9b8ff5182   David Ahern   net: Make table i...
1247
  		u32 tb_id = cfg->fc_table;
e1b8d903c   David Ahern   net: Fix prefsrc ...
1248
  		int rc;
021dd3b8a   David Ahern   net: Add routes t...
1249
1250
1251
  
  		if (tb_id == RT_TABLE_MAIN)
  			tb_id = RT_TABLE_LOCAL;
e1b8d903c   David Ahern   net: Fix prefsrc ...
1252
1253
1254
1255
1256
1257
  		rc = inet_addr_type_table(cfg->fc_nlinfo.nl_net,
  					  fib_prefsrc, tb_id);
  
  		if (rc != RTN_LOCAL && tb_id != RT_TABLE_LOCAL) {
  			rc = inet_addr_type_table(cfg->fc_nlinfo.nl_net,
  						  fib_prefsrc, RT_TABLE_LOCAL);
021dd3b8a   David Ahern   net: Add routes t...
1258
  		}
e1b8d903c   David Ahern   net: Fix prefsrc ...
1259
1260
1261
  
  		if (rc != RTN_LOCAL)
  			return false;
021dd3b8a   David Ahern   net: Add routes t...
1262
1263
1264
  	}
  	return true;
  }
6d8422a17   David Ahern   net: ipv4: Plumb ...
1265
1266
  struct fib_info *fib_create_info(struct fib_config *cfg,
  				 struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1267
1268
1269
  {
  	int err;
  	struct fib_info *fi = NULL;
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1270
  	struct nexthop *nh = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1271
  	struct fib_info *ofi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1272
  	int nhs = 1;
7462bd744   Denis V. Lunev   [NETNS]: Add a na...
1273
  	struct net *net = cfg->fc_nlinfo.nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1274

4c8237cd7   David S. Miller   ipv4: Validate ro...
1275
1276
  	if (cfg->fc_type > RTN_MAX)
  		goto err_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1277
  	/* Fast check to catch the most weird cases */
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1278
1279
  	if (fib_props[cfg->fc_type].scope > cfg->fc_scope) {
  		NL_SET_ERR_MSG(extack, "Invalid scope");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1280
  		goto err_inval;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1281
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1282

c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1283
1284
1285
  	if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) {
  		NL_SET_ERR_MSG(extack,
  			       "Invalid rtm_flags - can not contain DEAD or LINKDOWN");
80610229e   Julian Anastasov   ipv4: reject RTNH...
1286
  		goto err_inval;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1287
  	}
80610229e   Julian Anastasov   ipv4: reject RTNH...
1288

493ced1ac   David Ahern   ipv4: Allow route...
1289
  	if (cfg->fc_nh_id) {
6c48ea5fe   David Ahern   ipv4: Optimizatio...
1290
1291
1292
1293
1294
1295
1296
  		if (!cfg->fc_mx) {
  			fi = fib_find_info_nh(net, cfg);
  			if (fi) {
  				fi->fib_treeref++;
  				return fi;
  			}
  		}
493ced1ac   David Ahern   ipv4: Allow route...
1297
1298
1299
1300
1301
1302
1303
  		nh = nexthop_find_by_id(net, cfg->fc_nh_id);
  		if (!nh) {
  			NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
  			goto err_inval;
  		}
  		nhs = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1304
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1305
  	if (cfg->fc_mp) {
6d8422a17   David Ahern   net: ipv4: Plumb ...
1306
  		nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len, extack);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1307
1308
1309
1310
  		if (nhs == 0)
  			goto err_inval;
  	}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1311
1312
  
  	err = -ENOBUFS;
123b9731b   David S. Miller   ipv4: Rename fib_...
1313
1314
  	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
1315
1316
1317
1318
1319
  		struct hlist_head *new_info_hash;
  		struct hlist_head *new_laddrhash;
  		unsigned int bytes;
  
  		if (!new_size)
d94ce9b28   Eric Dumazet   ipv4: 16 slots in...
1320
  			new_size = 16;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1321
  		bytes = new_size * sizeof(struct hlist_head *);
123b9731b   David S. Miller   ipv4: Rename fib_...
1322
1323
  		new_info_hash = fib_info_hash_alloc(bytes);
  		new_laddrhash = fib_info_hash_alloc(bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1324
  		if (!new_info_hash || !new_laddrhash) {
123b9731b   David S. Miller   ipv4: Rename fib_...
1325
1326
  			fib_info_hash_free(new_info_hash, bytes);
  			fib_info_hash_free(new_laddrhash, bytes);
88f834916   Joonwoo Park   [IPV4] fib_semant...
1327
  		} else
123b9731b   David S. Miller   ipv4: Rename fib_...
1328
  			fib_info_hash_move(new_info_hash, new_laddrhash, new_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1329

123b9731b   David S. Miller   ipv4: Rename fib_...
1330
  		if (!fib_info_hash_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1331
1332
  			goto failure;
  	}
1f533ba6d   Gustavo A. R. Silva   ipv4: fib: use st...
1333
  	fi = kzalloc(struct_size(fi, fib_nh, nhs), GFP_KERNEL);
51456b291   Ian Morris   ipv4: coding styl...
1334
  	if (!fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1335
  		goto failure;
767a22175   David Ahern   net: common metri...
1336
  	fi->fib_metrics = ip_fib_metrics_init(fi->fib_net, cfg->fc_mx,
d7e774f35   David Ahern   net: Add extack a...
1337
  					      cfg->fc_mx_len, extack);
88e235b80   Enrico Weigelt   net: ipv4: drop u...
1338
  	if (IS_ERR(fi->fib_metrics)) {
767a22175   David Ahern   net: common metri...
1339
1340
1341
  		err = PTR_ERR(fi->fib_metrics);
  		kfree(fi);
  		return ERR_PTR(err);
187e5b3ac   Eric Dumazet   ipv4: fix NULL de...
1342
  	}
767a22175   David Ahern   net: common metri...
1343

187e5b3ac   Eric Dumazet   ipv4: fix NULL de...
1344
  	fib_info_cnt++;
efd7ef1c1   Eric W. Biederman   net: Kill hold_ne...
1345
  	fi->fib_net = net;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1346
  	fi->fib_protocol = cfg->fc_protocol;
37e826c51   David S. Miller   ipv4: Fix nexthop...
1347
  	fi->fib_scope = cfg->fc_scope;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1348
1349
1350
  	fi->fib_flags = cfg->fc_flags;
  	fi->fib_priority = cfg->fc_priority;
  	fi->fib_prefsrc = cfg->fc_prefsrc;
f4ef85bbd   Eric Dumazet   ipv4: add a fib_t...
1351
  	fi->fib_type = cfg->fc_type;
5a56a0b3a   Mark Tomlinson   net: Don't delete...
1352
  	fi->fib_tb_id = cfg->fc_table;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1353
1354
  
  	fi->fib_nhs = nhs;
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
  	if (nh) {
  		if (!nexthop_get(nh)) {
  			NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
  			err = -EINVAL;
  		} else {
  			err = 0;
  			fi->nh = nh;
  		}
  	} else {
  		change_nexthops(fi) {
  			nexthop_nh->nh_parent = fi;
  		} endfor_nexthops(fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1367

4c7e8084f   David Ahern   ipv4: Plumb suppo...
1368
1369
1370
1371
1372
1373
  		if (cfg->fc_mp)
  			err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg,
  					  extack);
  		else
  			err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack);
  	}
571e72267   Roopa Prabhu   ipv4: support for...
1374

e4516ef65   David Ahern   ipv4: Create init...
1375
1376
  	if (err != 0)
  		goto failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1377

4e902c574   Thomas Graf   [IPv4]: FIB confi...
1378
  	if (fib_props[cfg->fc_type].error) {
f35b794b3   David Ahern   ipv4: Prepare fib...
1379
  		if (cfg->fc_gw_family || cfg->fc_oif || cfg->fc_mp) {
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1380
1381
  			NL_SET_ERR_MSG(extack,
  				       "Gateway, device and multipath can not be specified for this route type");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1382
  			goto err_inval;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1383
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1384
  		goto link_it;
4c8237cd7   David S. Miller   ipv4: Validate ro...
1385
1386
1387
1388
1389
1390
1391
1392
1393
  	} else {
  		switch (cfg->fc_type) {
  		case RTN_UNICAST:
  		case RTN_LOCAL:
  		case RTN_BROADCAST:
  		case RTN_ANYCAST:
  		case RTN_MULTICAST:
  			break;
  		default:
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1394
  			NL_SET_ERR_MSG(extack, "Invalid route type");
4c8237cd7   David S. Miller   ipv4: Validate ro...
1395
1396
  			goto err_inval;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1397
  	}
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1398
1399
  	if (cfg->fc_scope > RT_SCOPE_HOST) {
  		NL_SET_ERR_MSG(extack, "Invalid scope");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1400
  		goto err_inval;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1401
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1402

4c7e8084f   David Ahern   ipv4: Plumb suppo...
1403
1404
1405
1406
1407
  	if (fi->nh) {
  		err = fib_check_nexthop(fi->nh, cfg->fc_scope, extack);
  		if (err)
  			goto failure;
  	} else if (cfg->fc_scope == RT_SCOPE_HOST) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1408
1409
1410
  		struct fib_nh *nh = fi->fib_nh;
  
  		/* Local address is added. */
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1411
1412
1413
  		if (nhs != 1) {
  			NL_SET_ERR_MSG(extack,
  				       "Route with host scope can not have multiple nexthops");
6d8422a17   David Ahern   net: ipv4: Plumb ...
1414
  			goto err_inval;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1415
  		}
bdf004677   David Ahern   net: Replace nhc_...
1416
  		if (nh->fib_nh_gw_family) {
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1417
1418
  			NL_SET_ERR_MSG(extack,
  				       "Route with host scope can not have a gateway");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1419
  			goto err_inval;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1420
  		}
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1421
  		nh->fib_nh_scope = RT_SCOPE_NOWHERE;
5481d73f8   David Ahern   ipv4: Use accesso...
1422
  		nh->fib_nh_dev = dev_get_by_index(net, nh->fib_nh_oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1423
  		err = -ENODEV;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1424
  		if (!nh->fib_nh_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
1426
  			goto failure;
  	} else {
8a3d03166   Andy Gospodarek   net: track link-s...
1427
  		int linkdown = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1428
  		change_nexthops(fi) {
ac1fab2d1   David Ahern   ipv4: export fib_...
1429
1430
1431
  			err = fib_check_nh(cfg->fc_nlinfo.nl_net, nexthop_nh,
  					   cfg->fc_table, cfg->fc_scope,
  					   extack);
6a31d2a97   Eric Dumazet   fib: cleanups
1432
  			if (err != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
  				goto failure;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1434
  			if (nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN)
8a3d03166   Andy Gospodarek   net: track link-s...
1435
  				linkdown++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1436
  		} endfor_nexthops(fi)
8a3d03166   Andy Gospodarek   net: track link-s...
1437
1438
  		if (linkdown == fi->fib_nhs)
  			fi->fib_flags |= RTNH_F_LINKDOWN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1439
  	}
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1440
1441
  	if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) {
  		NL_SET_ERR_MSG(extack, "Invalid prefsrc address");
021dd3b8a   David Ahern   net: Add routes t...
1442
  		goto err_inval;
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
1443
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1444

4c7e8084f   David Ahern   ipv4: Plumb suppo...
1445
1446
1447
1448
1449
1450
1451
  	if (!fi->nh) {
  		change_nexthops(fi) {
  			fib_info_update_nhc_saddr(net, &nexthop_nh->nh_common,
  						  fi->fib_scope);
  			if (nexthop_nh->fib_nh_gw_family == AF_INET6)
  				fi->fib_nh_is_v6 = true;
  		} endfor_nexthops(fi)
1fc050a13   David S. Miller   ipv4: Cache sourc...
1452

4c7e8084f   David Ahern   ipv4: Plumb suppo...
1453
1454
  		fib_rebalance(fi);
  	}
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
1455

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1456
  link_it:
6a31d2a97   Eric Dumazet   fib: cleanups
1457
1458
  	ofi = fib_find_info(fi);
  	if (ofi) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1459
1460
1461
1462
1463
1464
1465
  		fi->fib_dead = 1;
  		free_fib_info(fi);
  		ofi->fib_treeref++;
  		return ofi;
  	}
  
  	fi->fib_treeref++;
0029c0deb   Reshetova, Elena   net, ipv4: conver...
1466
  	refcount_set(&fi->fib_clntref, 1);
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
1467
  	spin_lock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1468
1469
1470
1471
1472
1473
1474
1475
  	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);
  	}
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1476
1477
1478
1479
1480
1481
  	if (fi->nh) {
  		list_add(&fi->nh_list, &nh->fi_list);
  	} else {
  		change_nexthops(fi) {
  			struct hlist_head *head;
  			unsigned int hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1482

4c7e8084f   David Ahern   ipv4: Plumb suppo...
1483
1484
1485
1486
1487
1488
1489
  			if (!nexthop_nh->fib_nh_dev)
  				continue;
  			hash = fib_devindex_hashfn(nexthop_nh->fib_nh_dev->ifindex);
  			head = &fib_info_devhash[hash];
  			hlist_add_head(&nexthop_nh->nh_hash, head);
  		} endfor_nexthops(fi)
  	}
832b4c5e1   Stephen Hemminger   [IPV4] fib: conve...
1490
  	spin_unlock_bh(&fib_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1491
1492
1493
1494
1495
1496
  	return fi;
  
  err_inval:
  	err = -EINVAL;
  
  failure:
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1497
  	if (fi) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1498
1499
1500
  		fi->fib_dead = 1;
  		free_fib_info(fi);
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1501
1502
  
  	return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1503
  }
c0a720770   David Ahern   ipv6: Flip to fib...
1504
  int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh_common *nhc,
7bdf4de12   Donald Sharp   net: Properly upd...
1505
  		     u8 rt_family, unsigned char *flags, bool skip_oif)
b0f601936   David Ahern   ipv4: Refactor ne...
1506
  {
c23641998   David Ahern   ipv4: Change fib_...
1507
  	if (nhc->nhc_flags & RTNH_F_DEAD)
b0f601936   David Ahern   ipv4: Refactor ne...
1508
  		*flags |= RTNH_F_DEAD;
c23641998   David Ahern   ipv4: Change fib_...
1509
  	if (nhc->nhc_flags & RTNH_F_LINKDOWN) {
b0f601936   David Ahern   ipv4: Refactor ne...
1510
1511
1512
  		*flags |= RTNH_F_LINKDOWN;
  
  		rcu_read_lock();
c23641998   David Ahern   ipv4: Change fib_...
1513
1514
1515
1516
1517
  		switch (nhc->nhc_family) {
  		case AF_INET:
  			if (ip_ignore_linkdown(nhc->nhc_dev))
  				*flags |= RTNH_F_DEAD;
  			break;
c0a720770   David Ahern   ipv6: Flip to fib...
1518
1519
1520
1521
  		case AF_INET6:
  			if (ip6_ignore_linkdown(nhc->nhc_dev))
  				*flags |= RTNH_F_DEAD;
  			break;
c23641998   David Ahern   ipv4: Change fib_...
1522
  		}
b0f601936   David Ahern   ipv4: Refactor ne...
1523
1524
  		rcu_read_unlock();
  	}
bdf004677   David Ahern   net: Replace nhc_...
1525
1526
1527
1528
1529
1530
  	switch (nhc->nhc_gw_family) {
  	case AF_INET:
  		if (nla_put_in_addr(skb, RTA_GATEWAY, nhc->nhc_gw.ipv4))
  			goto nla_put_failure;
  		break;
  	case AF_INET6:
d15662682   David Ahern   ipv4: Allow ipv6 ...
1531
1532
1533
  		/* if gateway family does not match nexthop family
  		 * gateway is encoded as RTA_VIA
  		 */
7bdf4de12   Donald Sharp   net: Properly upd...
1534
  		if (rt_family != nhc->nhc_gw_family) {
d15662682   David Ahern   ipv4: Allow ipv6 ...
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
  			int alen = sizeof(struct in6_addr);
  			struct nlattr *nla;
  			struct rtvia *via;
  
  			nla = nla_reserve(skb, RTA_VIA, alen + 2);
  			if (!nla)
  				goto nla_put_failure;
  
  			via = nla_data(nla);
  			via->rtvia_family = AF_INET6;
  			memcpy(via->rtvia_addr, &nhc->nhc_gw.ipv6, alen);
  		} else if (nla_put_in6_addr(skb, RTA_GATEWAY,
  					    &nhc->nhc_gw.ipv6) < 0) {
bdf004677   David Ahern   net: Replace nhc_...
1548
  			goto nla_put_failure;
d15662682   David Ahern   ipv4: Allow ipv6 ...
1549
  		}
bdf004677   David Ahern   net: Replace nhc_...
1550
  		break;
c23641998   David Ahern   ipv4: Change fib_...
1551
  	}
b0f601936   David Ahern   ipv4: Refactor ne...
1552

c23641998   David Ahern   ipv4: Change fib_...
1553
1554
  	*flags |= (nhc->nhc_flags & RTNH_F_ONLINK);
  	if (nhc->nhc_flags & RTNH_F_OFFLOAD)
b0f601936   David Ahern   ipv4: Refactor ne...
1555
  		*flags |= RTNH_F_OFFLOAD;
c23641998   David Ahern   ipv4: Change fib_...
1556
1557
  	if (!skip_oif && nhc->nhc_dev &&
  	    nla_put_u32(skb, RTA_OIF, nhc->nhc_dev->ifindex))
b0f601936   David Ahern   ipv4: Refactor ne...
1558
  		goto nla_put_failure;
c23641998   David Ahern   ipv4: Change fib_...
1559
  	if (nhc->nhc_lwtstate &&
ffa8ce54b   David Ahern   lwtunnel: Pass en...
1560
1561
  	    lwtunnel_fill_encap(skb, nhc->nhc_lwtstate,
  				RTA_ENCAP, RTA_ENCAP_TYPE) < 0)
b0f601936   David Ahern   ipv4: Refactor ne...
1562
1563
1564
1565
1566
1567
1568
  		goto nla_put_failure;
  
  	return 0;
  
  nla_put_failure:
  	return -EMSGSIZE;
  }
c0a720770   David Ahern   ipv6: Flip to fib...
1569
  EXPORT_SYMBOL_GPL(fib_nexthop_info);
b0f601936   David Ahern   ipv4: Refactor ne...
1570

c0a720770   David Ahern   ipv6: Flip to fib...
1571
1572
  #if IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) || IS_ENABLED(CONFIG_IPV6)
  int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh_common *nhc,
7bdf4de12   Donald Sharp   net: Properly upd...
1573
  		    int nh_weight, u8 rt_family)
b0f601936   David Ahern   ipv4: Refactor ne...
1574
  {
c23641998   David Ahern   ipv4: Change fib_...
1575
  	const struct net_device *dev = nhc->nhc_dev;
b0f601936   David Ahern   ipv4: Refactor ne...
1576
  	struct rtnexthop *rtnh;
ecc5663cc   David Ahern   net: Change nhc_f...
1577
  	unsigned char flags = 0;
b0f601936   David Ahern   ipv4: Refactor ne...
1578
1579
1580
1581
  
  	rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
  	if (!rtnh)
  		goto nla_put_failure;
c23641998   David Ahern   ipv4: Change fib_...
1582
  	rtnh->rtnh_hops = nh_weight - 1;
b0f601936   David Ahern   ipv4: Refactor ne...
1583
  	rtnh->rtnh_ifindex = dev ? dev->ifindex : 0;
7bdf4de12   Donald Sharp   net: Properly upd...
1584
  	if (fib_nexthop_info(skb, nhc, rt_family, &flags, true) < 0)
b0f601936   David Ahern   ipv4: Refactor ne...
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
  		goto nla_put_failure;
  
  	rtnh->rtnh_flags = flags;
  
  	/* length of rtnetlink header + attributes */
  	rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
  
  	return 0;
  
  nla_put_failure:
  	return -EMSGSIZE;
  }
c0a720770   David Ahern   ipv6: Flip to fib...
1597
  EXPORT_SYMBOL_GPL(fib_add_nexthop);
c23641998   David Ahern   ipv4: Change fib_...
1598
  #endif
b0f601936   David Ahern   ipv4: Refactor ne...
1599

c23641998   David Ahern   ipv4: Change fib_...
1600
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
b0f601936   David Ahern   ipv4: Refactor ne...
1601
1602
1603
  static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi)
  {
  	struct nlattr *mp;
ae0be8de9   Michal Kubecek   netlink: make nla...
1604
  	mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
b0f601936   David Ahern   ipv4: Refactor ne...
1605
1606
  	if (!mp)
  		goto nla_put_failure;
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1607
  	if (unlikely(fi->nh)) {
7bdf4de12   Donald Sharp   net: Properly upd...
1608
  		if (nexthop_mpath_fill_node(skb, fi->nh, AF_INET) < 0)
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1609
1610
1611
  			goto nla_put_failure;
  		goto mp_end;
  	}
b0f601936   David Ahern   ipv4: Refactor ne...
1612
  	for_nexthops(fi) {
7bdf4de12   Donald Sharp   net: Properly upd...
1613
1614
  		if (fib_add_nexthop(skb, &nh->nh_common, nh->fib_nh_weight,
  				    AF_INET) < 0)
b0f601936   David Ahern   ipv4: Refactor ne...
1615
1616
1617
1618
1619
1620
1621
  			goto nla_put_failure;
  #ifdef CONFIG_IP_ROUTE_CLASSID
  		if (nh->nh_tclassid &&
  		    nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid))
  			goto nla_put_failure;
  #endif
  	} endfor_nexthops(fi);
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1622
  mp_end:
b0f601936   David Ahern   ipv4: Refactor ne...
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
  	nla_nest_end(skb, mp);
  
  	return 0;
  
  nla_put_failure:
  	return -EMSGSIZE;
  }
  #else
  static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi)
  {
  	return 0;
  }
  #endif
15e473046   Eric W. Biederman   netlink: Rename p...
1636
  int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
1637
  		  struct fib_rt_info *fri, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1638
  {
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
1639
1640
1641
  	unsigned int nhs = fib_info_num_path(fri->fi);
  	struct fib_info *fi = fri->fi;
  	u32 tb_id = fri->tb_id;
be403ea18   Thomas Graf   [IPv4]: Convert F...
1642
  	struct nlmsghdr *nlh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1643
  	struct rtmsg *rtm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1644

15e473046   Eric W. Biederman   netlink: Rename p...
1645
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags);
51456b291   Ian Morris   ipv4: coding styl...
1646
  	if (!nlh)
26932566a   Patrick McHardy   [NETLINK]: Don't ...
1647
  		return -EMSGSIZE;
be403ea18   Thomas Graf   [IPv4]: Convert F...
1648
1649
  
  	rtm = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1650
  	rtm->rtm_family = AF_INET;
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
1651
  	rtm->rtm_dst_len = fri->dst_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1652
  	rtm->rtm_src_len = 0;
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
1653
  	rtm->rtm_tos = fri->tos;
709772e6e   Krzysztof Piotr Oledzki   net: Fix routing ...
1654
1655
1656
1657
  	if (tb_id < 256)
  		rtm->rtm_table = tb_id;
  	else
  		rtm->rtm_table = RT_TABLE_COMPAT;
f3756b79e   David S. Miller   ipv4: Stop using ...
1658
1659
  	if (nla_put_u32(skb, RTA_TABLE, tb_id))
  		goto nla_put_failure;
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
1660
  	rtm->rtm_type = fri->type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1661
  	rtm->rtm_flags = fi->fib_flags;
37e826c51   David S. Miller   ipv4: Fix nexthop...
1662
  	rtm->rtm_scope = fi->fib_scope;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1663
  	rtm->rtm_protocol = fi->fib_protocol;
be403ea18   Thomas Graf   [IPv4]: Convert F...
1664

f3756b79e   David S. Miller   ipv4: Stop using ...
1665
  	if (rtm->rtm_dst_len &&
1e301fd04   Ido Schimmel   ipv4: Encapsulate...
1666
  	    nla_put_in_addr(skb, RTA_DST, fri->dst))
f3756b79e   David S. Miller   ipv4: Stop using ...
1667
1668
1669
1670
  		goto nla_put_failure;
  	if (fi->fib_priority &&
  	    nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority))
  		goto nla_put_failure;
3fb07daff   Eric Dumazet   ipv4: add referen...
1671
  	if (rtnetlink_put_metrics(skb, fi->fib_metrics->metrics) < 0)
be403ea18   Thomas Graf   [IPv4]: Convert F...
1672
  		goto nla_put_failure;
f3756b79e   David S. Miller   ipv4: Stop using ...
1673
  	if (fi->fib_prefsrc &&
930345ea6   Jiri Benc   netlink: implemen...
1674
  	    nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc))
f3756b79e   David S. Miller   ipv4: Stop using ...
1675
  		goto nla_put_failure;
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1676
1677
1678
1679
1680
1681
  
  	if (fi->nh) {
  		if (nla_put_u32(skb, RTA_NH_ID, fi->nh->id))
  			goto nla_put_failure;
  		if (nexthop_is_blackhole(fi->nh))
  			rtm->rtm_type = RTN_BLACKHOLE;
4f80116d3   Roopa Prabhu   net: ipv4: add sy...
1682
1683
  		if (!fi->fib_net->ipv4.sysctl_nexthop_compat_mode)
  			goto offload;
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1684
  	}
5481d73f8   David Ahern   ipv4: Use accesso...
1685
  	if (nhs == 1) {
dcb1ecb50   David Ahern   ipv4: Prepare for...
1686
  		const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
ecc5663cc   David Ahern   net: Change nhc_f...
1687
  		unsigned char flags = 0;
b0f601936   David Ahern   ipv4: Refactor ne...
1688

7bdf4de12   Donald Sharp   net: Properly upd...
1689
  		if (fib_nexthop_info(skb, nhc, AF_INET, &flags, false) < 0)
f3756b79e   David S. Miller   ipv4: Stop using ...
1690
  			goto nla_put_failure;
b0f601936   David Ahern   ipv4: Refactor ne...
1691
1692
  
  		rtm->rtm_flags = flags;
c7066f70d   Patrick McHardy   netfilter: fix Kc...
1693
  #ifdef CONFIG_IP_ROUTE_CLASSID
dcb1ecb50   David Ahern   ipv4: Prepare for...
1694
1695
1696
1697
1698
1699
1700
1701
  		if (nhc->nhc_family == AF_INET) {
  			struct fib_nh *nh;
  
  			nh = container_of(nhc, struct fib_nh, nh_common);
  			if (nh->nh_tclassid &&
  			    nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid))
  				goto nla_put_failure;
  		}
8265abc08   Patrick McHardy   [IPV4]: Fix nexth...
1702
  #endif
b0f601936   David Ahern   ipv4: Refactor ne...
1703
1704
  	} else {
  		if (fib_add_multipath(skb, fi) < 0)
ea7a80858   David Ahern   net: lwtunnel: Ha...
1705
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1706
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1707

4f80116d3   Roopa Prabhu   net: ipv4: add sy...
1708
  offload:
90b93f1b3   Ido Schimmel   ipv4: Add "offloa...
1709
1710
1711
1712
  	if (fri->offload)
  		rtm->rtm_flags |= RTM_F_OFFLOAD;
  	if (fri->trap)
  		rtm->rtm_flags |= RTM_F_TRAP;
053c095a8   Johannes Berg   netlink: make nlm...
1713
1714
  	nlmsg_end(skb, nlh);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1715

be403ea18   Thomas Graf   [IPv4]: Convert F...
1716
  nla_put_failure:
26932566a   Patrick McHardy   [NETLINK]: Don't ...
1717
1718
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1719
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1720
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
1721
1722
1723
1724
   * 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
1725
   */
5a56a0b3a   Mark Tomlinson   net: Don't delete...
1726
  int fib_sync_down_addr(struct net_device *dev, __be32 local)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1727
1728
  {
  	int ret = 0;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1729
1730
  	unsigned int hash = fib_laddr_hashfn(local);
  	struct hlist_head *head = &fib_info_laddrhash[hash];
e0a312629   David Ahern   ipv4: Fix table i...
1731
  	int tb_id = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
5a56a0b3a   Mark Tomlinson   net: Don't delete...
1732
  	struct net *net = dev_net(dev);
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1733
  	struct fib_info *fi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1734

51456b291   Ian Morris   ipv4: coding styl...
1735
  	if (!fib_info_laddrhash || local == 0)
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1736
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1737

b67bfe0d4   Sasha Levin   hlist: drop the n...
1738
  	hlist_for_each_entry(fi, head, fib_lhash) {
5a56a0b3a   Mark Tomlinson   net: Don't delete...
1739
1740
  		if (!net_eq(fi->fib_net, net) ||
  		    fi->fib_tb_id != tb_id)
4814bdbd5   Denis V. Lunev   [NETNS]: Lookup i...
1741
  			continue;
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1742
1743
1744
  		if (fi->fib_prefsrc == local) {
  			fi->fib_flags |= RTNH_F_DEAD;
  			ret++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1745
1746
  		}
  	}
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1747
1748
  	return ret;
  }
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1749
  static int call_fib_nh_notifiers(struct fib_nh *nh,
982acb975   Ido Schimmel   ipv4: fib: Notify...
1750
1751
  				 enum fib_event_type event_type)
  {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1752
  	bool ignore_link_down = ip_ignore_linkdown(nh->fib_nh_dev);
982acb975   Ido Schimmel   ipv4: fib: Notify...
1753
  	struct fib_nh_notifier_info info = {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1754
  		.fib_nh = nh,
982acb975   Ido Schimmel   ipv4: fib: Notify...
1755
1756
1757
1758
  	};
  
  	switch (event_type) {
  	case FIB_EVENT_NH_ADD:
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1759
  		if (nh->fib_nh_flags & RTNH_F_DEAD)
982acb975   Ido Schimmel   ipv4: fib: Notify...
1760
  			break;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1761
  		if (ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN)
982acb975   Ido Schimmel   ipv4: fib: Notify...
1762
  			break;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1763
  		return call_fib4_notifiers(dev_net(nh->fib_nh_dev), event_type,
04b1d4e50   Ido Schimmel   net: core: Make t...
1764
  					   &info.info);
982acb975   Ido Schimmel   ipv4: fib: Notify...
1765
  	case FIB_EVENT_NH_DEL:
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1766
1767
1768
  		if ((ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN) ||
  		    (nh->fib_nh_flags & RTNH_F_DEAD))
  			return call_fib4_notifiers(dev_net(nh->fib_nh_dev),
04b1d4e50   Ido Schimmel   net: core: Make t...
1769
  						   event_type, &info.info);
982acb975   Ido Schimmel   ipv4: fib: Notify...
1770
1771
1772
1773
1774
1775
  	default:
  		break;
  	}
  
  	return NOTIFY_DONE;
  }
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
  /* Update the PMTU of exceptions when:
   * - the new MTU of the first hop becomes smaller than the PMTU
   * - the old MTU was the same as the PMTU, and it limited discovery of
   *   larger MTUs on the path. With that limit raised, we can now
   *   discover larger MTUs
   * A special case is locked exceptions, for which the PMTU is smaller
   * than the minimal accepted PMTU:
   * - if the new MTU is greater than the PMTU, don't make any change
   * - otherwise, unlock and set PMTU
   */
06c77c3e6   David Ahern   ipv4: Rename and ...
1786
  void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig)
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1787
1788
1789
  {
  	struct fnhe_hash_bucket *bucket;
  	int i;
a5995e710   David Ahern   ipv4: Move except...
1790
  	bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1);
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
  	if (!bucket)
  		return;
  
  	for (i = 0; i < FNHE_HASH_SIZE; i++) {
  		struct fib_nh_exception *fnhe;
  
  		for (fnhe = rcu_dereference_protected(bucket[i].chain, 1);
  		     fnhe;
  		     fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) {
  			if (fnhe->fnhe_mtu_locked) {
  				if (new <= fnhe->fnhe_pmtu) {
  					fnhe->fnhe_pmtu = new;
  					fnhe->fnhe_mtu_locked = false;
  				}
  			} else if (new < fnhe->fnhe_pmtu ||
  				   orig == fnhe->fnhe_pmtu) {
  				fnhe->fnhe_pmtu = new;
  			}
  		}
  	}
  }
  
  void fib_sync_mtu(struct net_device *dev, u32 orig_mtu)
  {
  	unsigned int hash = fib_devindex_hashfn(dev->ifindex);
  	struct hlist_head *head = &fib_info_devhash[hash];
  	struct fib_nh *nh;
  
  	hlist_for_each_entry(nh, head, nh_hash) {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1820
  		if (nh->fib_nh_dev == dev)
06c77c3e6   David Ahern   ipv4: Rename and ...
1821
  			fib_nhc_update_mtu(&nh->nh_common, dev->mtu, orig_mtu);
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1822
1823
  	}
  }
4f823defd   Julian Anastasov   ipv4: fix to not ...
1824
1825
1826
1827
1828
  /* Event              force Flags           Description
   * NETDEV_CHANGE      0     LINKDOWN        Carrier OFF, not for scope host
   * NETDEV_DOWN        0     LINKDOWN|DEAD   Link down, not for scope host
   * NETDEV_DOWN        1     LINKDOWN|DEAD   Last address removed
   * NETDEV_UNREGISTER  1     LINKDOWN|DEAD   Device removed
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1829
1830
   *
   * only used when fib_nh is built into fib_info
4f823defd   Julian Anastasov   ipv4: fix to not ...
1831
1832
   */
  int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force)
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1833
1834
1835
1836
1837
1838
  {
  	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];
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1839
  	struct fib_nh *nh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1840

4f823defd   Julian Anastasov   ipv4: fix to not ...
1841
  	if (force)
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1842
  		scope = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1843

b67bfe0d4   Sasha Levin   hlist: drop the n...
1844
  	hlist_for_each_entry(nh, head, nh_hash) {
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1845
1846
  		struct fib_info *fi = nh->nh_parent;
  		int dead;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1847

85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1848
  		BUG_ON(!fi->fib_nhs);
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1849
  		if (nh->fib_nh_dev != dev || fi == prev_fi)
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1850
1851
1852
1853
  			continue;
  		prev_fi = fi;
  		dead = 0;
  		change_nexthops(fi) {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1854
  			if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD)
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1855
  				dead++;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1856
1857
  			else if (nexthop_nh->fib_nh_dev == dev &&
  				 nexthop_nh->fib_nh_scope != scope) {
8a3d03166   Andy Gospodarek   net: track link-s...
1858
1859
1860
  				switch (event) {
  				case NETDEV_DOWN:
  				case NETDEV_UNREGISTER:
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1861
  					nexthop_nh->fib_nh_flags |= RTNH_F_DEAD;
a8eceea84   Joe Perches   inet: Use fallthr...
1862
  					fallthrough;
8a3d03166   Andy Gospodarek   net: track link-s...
1863
  				case NETDEV_CHANGE:
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1864
  					nexthop_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
8a3d03166   Andy Gospodarek   net: track link-s...
1865
1866
  					break;
  				}
982acb975   Ido Schimmel   ipv4: fib: Notify...
1867
1868
  				call_fib_nh_notifiers(nexthop_nh,
  						      FIB_EVENT_NH_DEL);
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1869
1870
  				dead++;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1871
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
8a3d03166   Andy Gospodarek   net: track link-s...
1872
  			if (event == NETDEV_UNREGISTER &&
b75ed8b1a   David Ahern   ipv4: Rename fib_...
1873
  			    nexthop_nh->fib_nh_dev == dev) {
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1874
1875
  				dead = fi->fib_nhs;
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1876
  			}
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1877
1878
1879
  #endif
  		} endfor_nexthops(fi)
  		if (dead == fi->fib_nhs) {
8a3d03166   Andy Gospodarek   net: track link-s...
1880
1881
1882
1883
  			switch (event) {
  			case NETDEV_DOWN:
  			case NETDEV_UNREGISTER:
  				fi->fib_flags |= RTNH_F_DEAD;
a8eceea84   Joe Perches   inet: Use fallthr...
1884
  				fallthrough;
8a3d03166   Andy Gospodarek   net: track link-s...
1885
1886
1887
1888
  			case NETDEV_CHANGE:
  				fi->fib_flags |= RTNH_F_LINKDOWN;
  				break;
  			}
85326fa54   Denis V. Lunev   [IPV4]: fib_sync_...
1889
  			ret++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1890
  		}
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
1891
1892
  
  		fib_rebalance(fi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1893
1894
1895
1896
  	}
  
  	return ret;
  }
0c838ff1a   David S. Miller   ipv4: Consolidate...
1897
  /* Must be invoked inside of an RCU protected region.  */
c7b371e34   David Ahern   net: ipv4: make f...
1898
  static void fib_select_default(const struct flowi4 *flp, struct fib_result *res)
0c838ff1a   David S. Miller   ipv4: Consolidate...
1899
1900
  {
  	struct fib_info *fi = NULL, *last_resort = NULL;
56315f9e6   Alexander Duyck   fib_trie: Convert...
1901
  	struct hlist_head *fa_head = res->fa_head;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1902
  	struct fib_table *tb = res->table;
18a912e9a   Julian Anastasov   ipv4: fib_select_...
1903
  	u8 slen = 32 - res->prefixlen;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1904
  	int order = -1, last_idx = -1;
2392debc2   Julian Anastasov   ipv4: consider TO...
1905
1906
1907
  	struct fib_alias *fa, *fa1 = NULL;
  	u32 last_prio = res->fi->fib_priority;
  	u8 last_tos = 0;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1908

56315f9e6   Alexander Duyck   fib_trie: Convert...
1909
  	hlist_for_each_entry_rcu(fa, fa_head, fa_list) {
0c838ff1a   David S. Miller   ipv4: Consolidate...
1910
  		struct fib_info *next_fi = fa->fa_info;
7c74b0bec   David Ahern   ipv4: Update fib_...
1911
  		struct fib_nh_common *nhc;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1912

18a912e9a   Julian Anastasov   ipv4: fib_select_...
1913
1914
  		if (fa->fa_slen != slen)
  			continue;
2392debc2   Julian Anastasov   ipv4: consider TO...
1915
1916
  		if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos)
  			continue;
18a912e9a   Julian Anastasov   ipv4: fib_select_...
1917
1918
  		if (fa->tb_id != tb->tb_id)
  			continue;
2392debc2   Julian Anastasov   ipv4: consider TO...
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
  		if (next_fi->fib_priority > last_prio &&
  		    fa->fa_tos == last_tos) {
  			if (last_tos)
  				continue;
  			break;
  		}
  		if (next_fi->fib_flags & RTNH_F_DEAD)
  			continue;
  		last_tos = fa->fa_tos;
  		last_prio = next_fi->fib_priority;
37e826c51   David S. Miller   ipv4: Fix nexthop...
1929
  		if (next_fi->fib_scope != res->scope ||
0c838ff1a   David S. Miller   ipv4: Consolidate...
1930
1931
  		    fa->fa_type != RTN_UNICAST)
  			continue;
5481d73f8   David Ahern   ipv4: Use accesso...
1932

7c74b0bec   David Ahern   ipv4: Update fib_...
1933
1934
  		nhc = fib_info_nhc(next_fi, 0);
  		if (!nhc->nhc_gw_family || nhc->nhc_scope != RT_SCOPE_LINK)
0c838ff1a   David S. Miller   ipv4: Consolidate...
1935
1936
1937
  			continue;
  
  		fib_alias_accessed(fa);
51456b291   Ian Morris   ipv4: coding styl...
1938
  		if (!fi) {
0c838ff1a   David S. Miller   ipv4: Consolidate...
1939
1940
  			if (next_fi != res->fi)
  				break;
2392debc2   Julian Anastasov   ipv4: consider TO...
1941
  			fa1 = fa;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1942
  		} else if (!fib_detect_death(fi, order, &last_resort,
2392debc2   Julian Anastasov   ipv4: consider TO...
1943
  					     &last_idx, fa1->fa_default)) {
0c838ff1a   David S. Miller   ipv4: Consolidate...
1944
  			fib_result_assign(res, fi);
2392debc2   Julian Anastasov   ipv4: consider TO...
1945
  			fa1->fa_default = order;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1946
1947
1948
1949
1950
  			goto out;
  		}
  		fi = next_fi;
  		order++;
  	}
51456b291   Ian Morris   ipv4: coding styl...
1951
  	if (order <= 0 || !fi) {
2392debc2   Julian Anastasov   ipv4: consider TO...
1952
1953
  		if (fa1)
  			fa1->fa_default = -1;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1954
1955
1956
1957
  		goto out;
  	}
  
  	if (!fib_detect_death(fi, order, &last_resort, &last_idx,
2392debc2   Julian Anastasov   ipv4: consider TO...
1958
  			      fa1->fa_default)) {
0c838ff1a   David S. Miller   ipv4: Consolidate...
1959
  		fib_result_assign(res, fi);
2392debc2   Julian Anastasov   ipv4: consider TO...
1960
  		fa1->fa_default = order;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1961
1962
1963
1964
1965
  		goto out;
  	}
  
  	if (last_idx >= 0)
  		fib_result_assign(res, last_resort);
2392debc2   Julian Anastasov   ipv4: consider TO...
1966
  	fa1->fa_default = last_idx;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1967
  out:
31d409373   Eric Dumazet   ipv4: fix rcu loc...
1968
  	return;
0c838ff1a   David S. Miller   ipv4: Consolidate...
1969
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1970
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
1971
1972
   * Dead device goes up. We wake up dead nexthops.
   * It takes sense only on multipath routes.
4c7e8084f   David Ahern   ipv4: Plumb suppo...
1973
1974
   *
   * only used when fib_nh is built into fib_info
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1975
   */
ecc5663cc   David Ahern   net: Change nhc_f...
1976
  int fib_sync_up(struct net_device *dev, unsigned char nh_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1977
1978
1979
1980
  {
  	struct fib_info *prev_fi;
  	unsigned int hash;
  	struct hlist_head *head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1981
1982
  	struct fib_nh *nh;
  	int ret;
6a31d2a97   Eric Dumazet   fib: cleanups
1983
  	if (!(dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1984
  		return 0;
c9b3292ee   Julian Anastasov   ipv4: update RTNH...
1985
1986
1987
1988
1989
1990
  	if (nh_flags & RTNH_F_DEAD) {
  		unsigned int flags = dev_get_flags(dev);
  
  		if (flags & (IFF_RUNNING | IFF_LOWER_UP))
  			nh_flags |= RTNH_F_LINKDOWN;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1991
1992
1993
1994
  	prev_fi = NULL;
  	hash = fib_devindex_hashfn(dev->ifindex);
  	head = &fib_info_devhash[hash];
  	ret = 0;
b67bfe0d4   Sasha Levin   hlist: drop the n...
1995
  	hlist_for_each_entry(nh, head, nh_hash) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1996
1997
1998
1999
  		struct fib_info *fi = nh->nh_parent;
  		int alive;
  
  		BUG_ON(!fi->fib_nhs);
b75ed8b1a   David Ahern   ipv4: Rename fib_...
2000
  		if (nh->fib_nh_dev != dev || fi == prev_fi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2001
2002
2003
2004
2005
  			continue;
  
  		prev_fi = fi;
  		alive = 0;
  		change_nexthops(fi) {
b75ed8b1a   David Ahern   ipv4: Rename fib_...
2006
  			if (!(nexthop_nh->fib_nh_flags & nh_flags)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2007
2008
2009
  				alive++;
  				continue;
  			}
b75ed8b1a   David Ahern   ipv4: Rename fib_...
2010
2011
  			if (!nexthop_nh->fib_nh_dev ||
  			    !(nexthop_nh->fib_nh_dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2012
  				continue;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
2013
  			if (nexthop_nh->fib_nh_dev != dev ||
71fceff0e   David S. Miller   ipv4: Use less co...
2014
  			    !__in_dev_get_rtnl(dev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2015
2016
  				continue;
  			alive++;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
2017
  			nexthop_nh->fib_nh_flags &= ~nh_flags;
982acb975   Ido Schimmel   ipv4: fib: Notify...
2018
  			call_fib_nh_notifiers(nexthop_nh, FIB_EVENT_NH_ADD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2019
2020
2021
  		} endfor_nexthops(fi)
  
  		if (alive > 0) {
8a3d03166   Andy Gospodarek   net: track link-s...
2022
  			fi->fib_flags &= ~nh_flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2023
2024
  			ret++;
  		}
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
2025
2026
  
  		fib_rebalance(fi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2027
2028
2029
2030
  	}
  
  	return ret;
  }
8a3d03166   Andy Gospodarek   net: track link-s...
2031
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
a6db4494d   David Ahern   net: ipv4: Consid...
2032
2033
2034
  static bool fib_good_nh(const struct fib_nh *nh)
  {
  	int state = NUD_REACHABLE;
b75ed8b1a   David Ahern   ipv4: Rename fib_...
2035
  	if (nh->fib_nh_scope == RT_SCOPE_LINK) {
a6db4494d   David Ahern   net: ipv4: Consid...
2036
2037
2038
  		struct neighbour *n;
  
  		rcu_read_lock_bh();
1a38c43d3   David Ahern   ipv4: Handle ipv6...
2039
2040
2041
2042
2043
2044
2045
2046
  		if (likely(nh->fib_nh_gw_family == AF_INET))
  			n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev,
  						   (__force u32)nh->fib_nh_gw4);
  		else if (nh->fib_nh_gw_family == AF_INET6)
  			n = __ipv6_neigh_lookup_noref_stub(nh->fib_nh_dev,
  							   &nh->fib_nh_gw6);
  		else
  			n = NULL;
a6db4494d   David Ahern   net: ipv4: Consid...
2047
2048
2049
2050
2051
2052
2053
2054
  		if (n)
  			state = n->nud_state;
  
  		rcu_read_unlock_bh();
  	}
  
  	return !!(state & NUD_VALID);
  }
8a3d03166   Andy Gospodarek   net: track link-s...
2055

0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
2056
  void fib_select_multipath(struct fib_result *res, int hash)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2057
2058
  {
  	struct fib_info *fi = res->fi;
a6db4494d   David Ahern   net: ipv4: Consid...
2059
2060
  	struct net *net = fi->fib_net;
  	bool first = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2061

4c7e8084f   David Ahern   ipv4: Plumb suppo...
2062
2063
2064
2065
  	if (unlikely(res->fi->nh)) {
  		nexthop_path_fib_result(res, hash);
  		return;
  	}
eba618aba   David Ahern   ipv4: Add fib_nh_...
2066
  	change_nexthops(fi) {
6174a30df   Xin Long   route: check sysc...
2067
  		if (net->ipv4.sysctl_fib_multipath_use_neigh) {
eba618aba   David Ahern   ipv4: Add fib_nh_...
2068
  			if (!fib_good_nh(nexthop_nh))
6174a30df   Xin Long   route: check sysc...
2069
2070
2071
  				continue;
  			if (!first) {
  				res->nh_sel = nhsel;
eba618aba   David Ahern   ipv4: Add fib_nh_...
2072
  				res->nhc = &nexthop_nh->nh_common;
6174a30df   Xin Long   route: check sysc...
2073
2074
2075
  				first = true;
  			}
  		}
eba618aba   David Ahern   ipv4: Add fib_nh_...
2076
  		if (hash > atomic_read(&nexthop_nh->fib_nh_upper_bound))
0e884c78e   Peter Nørlund   ipv4: L3 hash-bas...
2077
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2078

6174a30df   Xin Long   route: check sysc...
2079
  		res->nh_sel = nhsel;
eba618aba   David Ahern   ipv4: Add fib_nh_...
2080
  		res->nhc = &nexthop_nh->nh_common;
6174a30df   Xin Long   route: check sysc...
2081
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2082
  	} endfor_nexthops(fi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2083
2084
  }
  #endif
3ce58d843   David Ahern   net: Refactor pat...
2085
2086
  
  void fib_select_path(struct net *net, struct fib_result *res,
bf4e0a3db   Nikolay Aleksandrov   net: ipv4: add su...
2087
  		     struct flowi4 *fl4, const struct sk_buff *skb)
3ce58d843   David Ahern   net: Refactor pat...
2088
  {
0d876f2c6   David Ahern   net/ipv4: Simplif...
2089
2090
  	if (fl4->flowi4_oif && !(fl4->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF))
  		goto check_saddr;
7a18c5b9f   David Ahern   net: ipv4: Fix mu...
2091

3ce58d843   David Ahern   net: Refactor pat...
2092
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
5481d73f8   David Ahern   ipv4: Use accesso...
2093
  	if (fib_info_num_path(res->fi) > 1) {
7efc0b6b6   David Ahern   net/ipv4: Pass ne...
2094
  		int h = fib_multipath_hash(net, fl4, skb, NULL);
9920e48b8   Paolo Abeni   ipv4: use l4 hash...
2095

bf4e0a3db   Nikolay Aleksandrov   net: ipv4: add su...
2096
  		fib_select_multipath(res, h);
3ce58d843   David Ahern   net: Refactor pat...
2097
2098
2099
2100
2101
  	}
  	else
  #endif
  	if (!res->prefixlen &&
  	    res->table->tb_num_default > 1 &&
0d876f2c6   David Ahern   net/ipv4: Simplif...
2102
  	    res->type == RTN_UNICAST)
3ce58d843   David Ahern   net: Refactor pat...
2103
  		fib_select_default(fl4, res);
0d876f2c6   David Ahern   net/ipv4: Simplif...
2104
  check_saddr:
3ce58d843   David Ahern   net: Refactor pat...
2105
  	if (!fl4->saddr)
eba618aba   David Ahern   ipv4: Add fib_nh_...
2106
  		fl4->saddr = fib_result_prefsrc(net, res);
3ce58d843   David Ahern   net: Refactor pat...
2107
  }