Blame view

net/ipv4/fib_frontend.c 38.7 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: FIB frontend.
   *
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
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include <linux/module.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
12
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/bitops.h>
4fc268d24   Randy Dunlap   [PATCH] capable/c...
14
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
  #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...
24
  #include <linux/inetdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #include <linux/netdevice.h>
1823730fb   Thomas Graf   [IPv4]: Move inte...
26
  #include <linux/if_addr.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
  #include <linux/if_arp.h>
  #include <linux/skbuff.h>
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
29
  #include <linux/cache.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #include <linux/init.h>
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
31
  #include <linux/list.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
32
  #include <linux/slab.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>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
  #include <net/arp.h>
  #include <net/ip_fib.h>
5481d73f8   David Ahern   ipv4: Use accesso...
41
  #include <net/nexthop.h>
63f3444fb   Thomas Graf   [IPv4]: Use rtnl ...
42
  #include <net/rtnetlink.h>
990078afb   Michael Smith   Disable rp_filter...
43
  #include <net/xfrm.h>
385add906   David Ahern   net: Replace vrf_...
44
  #include <net/l3mdev.h>
9ed59592e   David Ahern   lwtunnel: fix aut...
45
  #include <net/lwtunnel.h>
f6d3c1927   David Ahern   net: FIB tracepoints
46
  #include <trace/events/fib.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  #ifndef CONFIG_IP_MULTIPLE_TABLES
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
49
  static int __net_init fib4_rules_init(struct net *net)
c3e9a353d   Pavel Emelyanov   [IPV4]: Compact s...
50
  {
93456b6d7   Denis V. Lunev   [IPV4]: Unify acc...
51
  	struct fib_table *local_table, *main_table;
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
52
  	main_table  = fib_trie_table(RT_TABLE_MAIN, NULL);
51456b291   Ian Morris   ipv4: coding styl...
53
  	if (!main_table)
61f0d861f   Alexander Duyck   fib_trie: Fix uni...
54
  		return -ENOMEM;
dbb50165b   Denis V. Lunev   [IPV4]: Check fib...
55

0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
56
  	local_table = fib_trie_table(RT_TABLE_LOCAL, main_table);
51456b291   Ian Morris   ipv4: coding styl...
57
  	if (!local_table)
61f0d861f   Alexander Duyck   fib_trie: Fix uni...
58
  		goto fail;
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
59

93456b6d7   Denis V. Lunev   [IPV4]: Unify acc...
60
  	hlist_add_head_rcu(&local_table->tb_hlist,
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
61
  				&net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]);
93456b6d7   Denis V. Lunev   [IPV4]: Unify acc...
62
  	hlist_add_head_rcu(&main_table->tb_hlist,
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
63
  				&net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]);
dbb50165b   Denis V. Lunev   [IPV4]: Check fib...
64
65
66
  	return 0;
  
  fail:
61f0d861f   Alexander Duyck   fib_trie: Fix uni...
67
  	fib_free_table(main_table);
dbb50165b   Denis V. Lunev   [IPV4]: Check fib...
68
  	return -ENOMEM;
c3e9a353d   Pavel Emelyanov   [IPV4]: Compact s...
69
  }
032a48020   Paolo Abeni   ipv4: fix validat...
70
71
72
73
74
  
  static bool fib4_has_custom_rules(struct net *net)
  {
  	return false;
  }
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
75
  #else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76

8ad4942cd   Denis V. Lunev   [NETNS]: Add netn...
77
  struct fib_table *fib_new_table(struct net *net, u32 id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
  {
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
79
  	struct fib_table *tb, *alias = NULL;
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
80
  	unsigned int h;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81

1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
82
83
  	if (id == 0)
  		id = RT_TABLE_MAIN;
8ad4942cd   Denis V. Lunev   [NETNS]: Add netn...
84
  	tb = fib_get_table(net, id);
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
85
86
  	if (tb)
  		return tb;
7f9b80529   Stephen Hemminger   [IPV4]: fib hash|...
87

5350d54f6   Alexander Duyck   ipv4: Do not allo...
88
  	if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules)
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
89
90
91
  		alias = fib_new_table(net, RT_TABLE_MAIN);
  
  	tb = fib_trie_table(id, alias);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
  	if (!tb)
  		return NULL;
f4530fa57   David S. Miller   ipv4: Avoid overh...
94
95
  
  	switch (id) {
f4530fa57   David S. Miller   ipv4: Avoid overh...
96
  	case RT_TABLE_MAIN:
a7e535312   Alexander Duyck   fib_trie: Make fi...
97
  		rcu_assign_pointer(net->ipv4.fib_main, tb);
f4530fa57   David S. Miller   ipv4: Avoid overh...
98
  		break;
f4530fa57   David S. Miller   ipv4: Avoid overh...
99
  	case RT_TABLE_DEFAULT:
a7e535312   Alexander Duyck   fib_trie: Make fi...
100
  		rcu_assign_pointer(net->ipv4.fib_default, tb);
f4530fa57   David S. Miller   ipv4: Avoid overh...
101
  		break;
f4530fa57   David S. Miller   ipv4: Avoid overh...
102
103
104
  	default:
  		break;
  	}
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
105
  	h = id & (FIB_TABLE_HASHSZ - 1);
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
106
  	hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
  	return tb;
  }
b3b4663c9   David Ahern   net: vrf: Create ...
109
  EXPORT_SYMBOL_GPL(fib_new_table);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110

345e9b542   Alexander Duyck   fib_trie: Push rc...
111
  /* caller must hold either rtnl or rcu read lock */
8ad4942cd   Denis V. Lunev   [NETNS]: Add netn...
112
  struct fib_table *fib_get_table(struct net *net, u32 id)
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
113
114
  {
  	struct fib_table *tb;
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
115
  	struct hlist_head *head;
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
116
  	unsigned int h;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117

1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
118
119
120
  	if (id == 0)
  		id = RT_TABLE_MAIN;
  	h = id & (FIB_TABLE_HASHSZ - 1);
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
121

e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
122
  	head = &net->ipv4.fib_table_hash[h];
7fd69b0ba   Joel Fernandes (Google)   ipv4: Add lockdep...
123
124
  	hlist_for_each_entry_rcu(tb, head, tb_hlist,
  				 lockdep_rtnl_is_held()) {
345e9b542   Alexander Duyck   fib_trie: Push rc...
125
  		if (tb->tb_id == id)
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
126
  			return tb;
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
127
  	}
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
128
129
  	return NULL;
  }
032a48020   Paolo Abeni   ipv4: fix validat...
130
131
132
133
134
  
  static bool fib4_has_custom_rules(struct net *net)
  {
  	return net->ipv4.fib_has_custom_rules;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  #endif /* CONFIG_IP_MULTIPLE_TABLES */
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
136
137
138
139
140
  static void fib_replace_table(struct net *net, struct fib_table *old,
  			      struct fib_table *new)
  {
  #ifdef CONFIG_IP_MULTIPLE_TABLES
  	switch (new->tb_id) {
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  	case RT_TABLE_MAIN:
  		rcu_assign_pointer(net->ipv4.fib_main, new);
  		break;
  	case RT_TABLE_DEFAULT:
  		rcu_assign_pointer(net->ipv4.fib_default, new);
  		break;
  	default:
  		break;
  	}
  
  #endif
  	/* replace the old table in the hlist */
  	hlist_replace_rcu(&old->tb_hlist, &new->tb_hlist);
  }
  
  int fib_unmerge(struct net *net)
  {
3b7093346   Alexander Duyck   ipv4: Restore fib...
158
  	struct fib_table *old, *new, *main_table;
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
159

3c9e9f732   Alexander Duyck   fib_trie: Avoid N...
160
  	/* attempt to fetch local table if it has been allocated */
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
161
  	old = fib_get_table(net, RT_TABLE_LOCAL);
3c9e9f732   Alexander Duyck   fib_trie: Avoid N...
162
163
  	if (!old)
  		return 0;
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
164

3c9e9f732   Alexander Duyck   fib_trie: Avoid N...
165
  	new = fib_trie_unmerge(old);
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
166
167
  	if (!new)
  		return -ENOMEM;
3b7093346   Alexander Duyck   ipv4: Restore fib...
168
169
170
  	/* table is already unmerged */
  	if (new == old)
  		return 0;
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
171
  	/* replace merged table with clean table */
3b7093346   Alexander Duyck   ipv4: Restore fib...
172
173
174
175
176
177
178
179
180
181
  	fib_replace_table(net, old, new);
  	fib_free_table(old);
  
  	/* attempt to fetch main table if it has been allocated */
  	main_table = fib_get_table(net, RT_TABLE_MAIN);
  	if (!main_table)
  		return 0;
  
  	/* flush local entries from main table */
  	fib_table_flush_external(main_table);
0ddcf43d5   Alexander Duyck   ipv4: FIB Local/M...
182
183
184
  
  	return 0;
  }
9bd836679   David Ahern   ipv4: export fib_...
185
  void fib_flush(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
  {
  	int flushed = 0;
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
188
  	unsigned int h;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189

1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
190
  	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
a7e535312   Alexander Duyck   fib_trie: Make fi...
191
192
193
194
195
  		struct hlist_head *head = &net->ipv4.fib_table_hash[h];
  		struct hlist_node *tmp;
  		struct fib_table *tb;
  
  		hlist_for_each_entry_safe(tb, tmp, head, tb_hlist)
f97f4dd8b   Ido Schimmel   net: ipv4: Fix me...
196
  			flushed += fib_table_flush(net, tb, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
  
  	if (flushed)
4ccfe6d41   Nicolas Dichtel   ipv4/route: arg d...
200
  		rt_cache_flush(net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  }
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
202
203
204
205
  /*
   * Find address type as if only "dev" was present in the system. If
   * on_dev is NULL then all interfaces are taken into consideration.
   */
95c961747   Eric Dumazet   net: cleanup unsi...
206
207
  static inline unsigned int __inet_dev_addr_type(struct net *net,
  						const struct net_device *dev,
9b8ff5182   David Ahern   net: Make table i...
208
  						__be32 addr, u32 tb_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  {
9ade22861   David S. Miller   ipv4: Use flowi4 ...
210
  	struct flowi4		fl4 = { .daddr = addr };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  	struct fib_result	res;
95c961747   Eric Dumazet   net: cleanup unsi...
212
  	unsigned int ret = RTN_BROADCAST;
15be405eb   David Ahern   net: Add inet_add...
213
  	struct fib_table *table;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214

1e637c74b   Jan Engelhardt   [IPV4]: Enable us...
215
  	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  		return RTN_BROADCAST;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
217
  	if (ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  		return RTN_MULTICAST;
345e9b542   Alexander Duyck   fib_trie: Push rc...
219
  	rcu_read_lock();
15be405eb   David Ahern   net: Add inet_add...
220
221
  	table = fib_get_table(net, tb_id);
  	if (table) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  		ret = RTN_UNICAST;
15be405eb   David Ahern   net: Add inet_add...
223
  		if (!fib_table_lookup(table, &fl4, &res, FIB_LOOKUP_NOREF)) {
dcb1ecb50   David Ahern   ipv4: Prepare for...
224
  			struct fib_nh_common *nhc = fib_info_nhc(res.fi, 0);
5481d73f8   David Ahern   ipv4: Use accesso...
225

dcb1ecb50   David Ahern   ipv4: Prepare for...
226
  			if (!dev || dev == nhc->nhc_dev)
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
227
  				ret = res.type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
  		}
  	}
345e9b542   Alexander Duyck   fib_trie: Push rc...
230
231
  
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
  	return ret;
  }
9b8ff5182   David Ahern   net: Make table i...
234
  unsigned int inet_addr_type_table(struct net *net, __be32 addr, u32 tb_id)
15be405eb   David Ahern   net: Add inet_add...
235
236
237
238
  {
  	return __inet_dev_addr_type(net, NULL, addr, tb_id);
  }
  EXPORT_SYMBOL(inet_addr_type_table);
6b175b26c   Eric W. Biederman   [NETNS]: Add netn...
239
  unsigned int inet_addr_type(struct net *net, __be32 addr)
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
240
  {
15be405eb   David Ahern   net: Add inet_add...
241
  	return __inet_dev_addr_type(net, NULL, addr, RT_TABLE_LOCAL);
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
242
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
243
  EXPORT_SYMBOL(inet_addr_type);
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
244

6b175b26c   Eric W. Biederman   [NETNS]: Add netn...
245
246
  unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
  				__be32 addr)
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
247
  {
3236b0042   David Ahern   net: Replace vrf_...
248
  	u32 rt_table = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL;
15be405eb   David Ahern   net: Add inet_add...
249
250
  
  	return __inet_dev_addr_type(net, dev, addr, rt_table);
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
251
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
252
  EXPORT_SYMBOL(inet_dev_addr_type);
055381161   Laszlo Attila Toth   [IPV4]: Add inet_...
253

30bbaa195   David Ahern   net: Fix up inet_...
254
255
256
257
258
259
260
  /* inet_addr_type with dev == NULL but using the table from a dev
   * if one is associated
   */
  unsigned int inet_addr_type_dev_table(struct net *net,
  				      const struct net_device *dev,
  				      __be32 addr)
  {
3236b0042   David Ahern   net: Replace vrf_...
261
  	u32 rt_table = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL;
30bbaa195   David Ahern   net: Fix up inet_...
262
263
264
265
  
  	return __inet_dev_addr_type(net, NULL, addr, rt_table);
  }
  EXPORT_SYMBOL(inet_addr_type_dev_table);
35ebf65e8   David S. Miller   ipv4: Create and ...
266
267
268
269
270
  __be32 fib_compute_spec_dst(struct sk_buff *skb)
  {
  	struct net_device *dev = skb->dev;
  	struct in_device *in_dev;
  	struct fib_result res;
a207a4b2e   David S. Miller   ipv4: Fix bugs in...
271
  	struct rtable *rt;
35ebf65e8   David S. Miller   ipv4: Create and ...
272
  	struct net *net;
a207a4b2e   David S. Miller   ipv4: Fix bugs in...
273
  	int scope;
35ebf65e8   David S. Miller   ipv4: Create and ...
274

a207a4b2e   David S. Miller   ipv4: Fix bugs in...
275
  	rt = skb_rtable(skb);
0cc535a29   Julian Anastasov   ipv4: fix address...
276
277
  	if ((rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST | RTCF_LOCAL)) ==
  	    RTCF_LOCAL)
35ebf65e8   David S. Miller   ipv4: Create and ...
278
279
280
  		return ip_hdr(skb)->daddr;
  
  	in_dev = __in_dev_get_rcu(dev);
35ebf65e8   David S. Miller   ipv4: Create and ...
281
282
  
  	net = dev_net(dev);
a207a4b2e   David S. Miller   ipv4: Fix bugs in...
283
284
285
  
  	scope = RT_SCOPE_UNIVERSE;
  	if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) {
9fc12023d   Lorenzo Bianconi   ipv4: remove BUG_...
286
  		bool vmark = in_dev && IN_DEV_SRC_VMARK(in_dev);
4cfc86f3d   Lance Richardson   ipv4: initialize ...
287
288
  		struct flowi4 fl4 = {
  			.flowi4_iif = LOOPBACK_IFINDEX,
e7372197e   David Ahern   net/ipv4: Set oif...
289
  			.flowi4_oif = l3mdev_master_ifindex_rcu(dev),
4cfc86f3d   Lance Richardson   ipv4: initialize ...
290
291
292
  			.daddr = ip_hdr(skb)->saddr,
  			.flowi4_tos = RT_TOS(ip_hdr(skb)->tos),
  			.flowi4_scope = scope,
9fc12023d   Lorenzo Bianconi   ipv4: remove BUG_...
293
  			.flowi4_mark = vmark ? skb->mark : 0,
4cfc86f3d   Lance Richardson   ipv4: initialize ...
294
  		};
0eeb075fa   Andy Gospodarek   net: ipv4 sysctl ...
295
  		if (!fib_lookup(net, &fl4, &res, 0))
eba618aba   David Ahern   ipv4: Add fib_nh_...
296
  			return fib_result_prefsrc(net, &res);
a207a4b2e   David S. Miller   ipv4: Fix bugs in...
297
298
299
300
301
  	} else {
  		scope = RT_SCOPE_LINK;
  	}
  
  	return inet_select_addr(dev, ip_hdr(skb)->saddr, scope);
35ebf65e8   David S. Miller   ipv4: Create and ...
302
  }
78f2756c5   David Ahern   net/ipv4: Move de...
303
304
305
  bool fib_info_nh_uses_dev(struct fib_info *fi, const struct net_device *dev)
  {
  	bool dev_match = false;
075e264fa   Eric Dumazet   net/ipv4: avoid c...
306
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
78f2756c5   David Ahern   net/ipv4: Move de...
307
  	int ret;
5481d73f8   David Ahern   ipv4: Use accesso...
308
  	for (ret = 0; ret < fib_info_num_path(fi); ret++) {
dcb1ecb50   David Ahern   ipv4: Prepare for...
309
  		const struct fib_nh_common *nhc = fib_info_nhc(fi, ret);
78f2756c5   David Ahern   net/ipv4: Move de...
310

dcb1ecb50   David Ahern   ipv4: Prepare for...
311
  		if (nhc->nhc_dev == dev) {
78f2756c5   David Ahern   net/ipv4: Move de...
312
313
  			dev_match = true;
  			break;
dcb1ecb50   David Ahern   ipv4: Prepare for...
314
  		} else if (l3mdev_master_ifindex_rcu(nhc->nhc_dev) == dev->ifindex) {
78f2756c5   David Ahern   net/ipv4: Move de...
315
316
317
318
319
  			dev_match = true;
  			break;
  		}
  	}
  #else
dcb1ecb50   David Ahern   ipv4: Prepare for...
320
  	if (fib_info_nhc(fi, 0)->nhc_dev == dev)
78f2756c5   David Ahern   net/ipv4: Move de...
321
322
323
324
325
326
  		dev_match = true;
  #endif
  
  	return dev_match;
  }
  EXPORT_SYMBOL_GPL(fib_info_nh_uses_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  /* Given (packet source, input interface) and optional (dst, oif, tos):
6a31d2a97   Eric Dumazet   fib: cleanups
328
329
330
331
332
   * - (main) check, that source is valid i.e. not broadcast or our local
   *   address.
   * - figure out what "logical" interface this packet arrived
   *   and calculate "specific destination" address.
   * - check, that packet arrived from expected physical interface.
ebc0ffae5   Eric Dumazet   fib: RCU conversi...
333
   * called with rcu_read_lock()
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
   */
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
335
336
337
  static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
  				 u8 tos, int oif, struct net_device *dev,
  				 int rpf, struct in_device *idev, u32 *itag)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
  {
5a847a6e1   David Ahern   net/ipv4: Initial...
339
340
  	struct net *net = dev_net(dev);
  	struct flow_keys flkeys;
1dced6a85   Sébastien Barré   ipv4: Restore acc...
341
  	int ret, no_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  	struct fib_result res;
9e56e3800   David S. Miller   ipv4: Adjust in_d...
343
  	struct flowi4 fl4;
9e56e3800   David S. Miller   ipv4: Adjust in_d...
344
  	bool dev_match;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

9ade22861   David S. Miller   ipv4: Use flowi4 ...
346
  	fl4.flowi4_oif = 0;
385add906   David Ahern   net: Replace vrf_...
347
  	fl4.flowi4_iif = l3mdev_master_ifindex_rcu(dev);
cd2fbe1b6   David Ahern   net: Use VRF devi...
348
349
  	if (!fl4.flowi4_iif)
  		fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX;
9ade22861   David S. Miller   ipv4: Use flowi4 ...
350
351
352
353
  	fl4.daddr = src;
  	fl4.saddr = dst;
  	fl4.flowi4_tos = tos;
  	fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
1b7179d3a   Thomas Graf   route: Extend flo...
354
  	fl4.flowi4_tun_key.tun_id = 0;
b84f78782   David Ahern   net: Initialize f...
355
  	fl4.flowi4_flags = 0;
8bcfd0925   Julian Anastasov   ipv4: add missing...
356
  	fl4.flowi4_uid = sock_net_uid(net, NULL);
cc7e17ea0   David S. Miller   ipv4: Optimize fl...
357

9e56e3800   David S. Miller   ipv4: Adjust in_d...
358
  	no_addr = idev->ifa_list == NULL;
990078afb   Michael Smith   Disable rp_filter...
359

9e56e3800   David S. Miller   ipv4: Adjust in_d...
360
  	fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0;
5a847a6e1   David Ahern   net/ipv4: Initial...
361
362
363
364
365
  	if (!fib4_rules_early_flow_dissect(net, skb, &fl4, &flkeys)) {
  		fl4.flowi4_proto = 0;
  		fl4.fl4_sport = 0;
  		fl4.fl4_dport = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366

0eeb075fa   Andy Gospodarek   net: ipv4 sysctl ...
367
  	if (fib_lookup(net, &fl4, &res, 0))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  		goto last_resort;
1dced6a85   Sébastien Barré   ipv4: Restore acc...
369
370
371
  	if (res.type != RTN_UNICAST &&
  	    (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
  		goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  	fib_combine_itag(itag, &res);
6f86b3251   David S. Miller   ipv4: Fix reverse...
373

78f2756c5   David Ahern   net/ipv4: Move de...
374
  	dev_match = fib_info_nh_uses_dev(res.fi, dev);
66f820954   Cong Wang   fib: relax source...
375
376
377
378
379
  	/* This is not common, loopback packets retain skb_dst so normally they
  	 * would not even hit this slow path.
  	 */
  	dev_match = dev_match || (res.type == RTN_LOCAL &&
  				  dev == net->loopback_dev);
6f86b3251   David S. Miller   ipv4: Fix reverse...
380
  	if (dev_match) {
eba618aba   David Ahern   ipv4: Add fib_nh_...
381
  		ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_HOST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
  		return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
  	if (no_addr)
  		goto last_resort;
c1cf8422f   Stephen Hemminger   ip: add loose rev...
386
  	if (rpf == 1)
b5f7e7554   Eric Dumazet   ipv4: add LINUX_M...
387
  		goto e_rpf;
9ade22861   David S. Miller   ipv4: Use flowi4 ...
388
  	fl4.flowi4_oif = dev->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
  
  	ret = 0;
0eeb075fa   Andy Gospodarek   net: ipv4 sysctl ...
391
  	if (fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE) == 0) {
41347dcdd   David S. Miller   ipv4: Kill rt->rt...
392
  		if (res.type == RTN_UNICAST)
eba618aba   David Ahern   ipv4: Add fib_nh_...
393
  			ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_HOST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
  	}
  	return ret;
  
  last_resort:
  	if (rpf)
b5f7e7554   Eric Dumazet   ipv4: add LINUX_M...
399
  		goto e_rpf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
  	*itag = 0;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
  e_inval:
  	return -EINVAL;
b5f7e7554   Eric Dumazet   ipv4: add LINUX_M...
404
405
  e_rpf:
  	return -EXDEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  }
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
407
408
409
410
411
412
  /* Ignore rp_filter for packets protected by IPsec. */
  int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
  			u8 tos, int oif, struct net_device *dev,
  			struct in_device *idev, u32 *itag)
  {
  	int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev);
6e617de84   Paolo Abeni   net: avoid a full...
413
  	struct net *net = dev_net(dev);
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
414

6e617de84   Paolo Abeni   net: avoid a full...
415
  	if (!r && !fib_num_tclassid_users(net) &&
e81da0e11   Julian Anastasov   ipv4: fix sending...
416
  	    (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) {
6e617de84   Paolo Abeni   net: avoid a full...
417
418
  		if (IN_DEV_ACCEPT_LOCAL(idev))
  			goto ok;
032a48020   Paolo Abeni   ipv4: fix validat...
419
420
421
  		/* with custom local routes in place, checking local addresses
  		 * only will be too optimistic, with custom rules, checking
  		 * local addresses only can be too strict, e.g. due to vrf
6e617de84   Paolo Abeni   net: avoid a full...
422
  		 */
032a48020   Paolo Abeni   ipv4: fix validat...
423
424
  		if (net->ipv4.fib_has_custom_local_routes ||
  		    fib4_has_custom_rules(net))
6e617de84   Paolo Abeni   net: avoid a full...
425
426
427
428
429
  			goto full_check;
  		if (inet_lookup_ifaddr_rcu(net, src))
  			return -EINVAL;
  
  ok:
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
430
431
432
  		*itag = 0;
  		return 0;
  	}
6e617de84   Paolo Abeni   net: avoid a full...
433
434
  
  full_check:
7a9bc9b81   David S. Miller   ipv4: Elide fib_v...
435
436
  	return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag);
  }
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
437
  static inline __be32 sk_extract_addr(struct sockaddr *addr)
4e902c574   Thomas Graf   [IPv4]: FIB confi...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  {
  	return ((struct sockaddr_in *) addr)->sin_addr.s_addr;
  }
  
  static int put_rtax(struct nlattr *mx, int len, int type, u32 value)
  {
  	struct nlattr *nla;
  
  	nla = (struct nlattr *) ((char *) mx + len);
  	nla->nla_type = type;
  	nla->nla_len = nla_attr_size(4);
  	*(u32 *) nla_data(nla) = value;
  
  	return len + nla_total_size(4);
  }
4b5d47d4d   Denis V. Lunev   [NETNS]: Correctl...
453
  static int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt,
4e902c574   Thomas Graf   [IPv4]: FIB confi...
454
455
  				 struct fib_config *cfg)
  {
6d85c10ab   Al Viro   [IPV4]: struct fi...
456
  	__be32 addr;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
457
458
459
  	int plen;
  
  	memset(cfg, 0, sizeof(*cfg));
4b5d47d4d   Denis V. Lunev   [NETNS]: Correctl...
460
  	cfg->fc_nlinfo.nl_net = net;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
  
  	if (rt->rt_dst.sa_family != AF_INET)
  		return -EAFNOSUPPORT;
  
  	/*
  	 * Check mask for validity:
  	 * a) it must be contiguous.
  	 * b) destination must have all host bits clear.
  	 * c) if application forgot to set correct family (AF_INET),
  	 *    reject request unless it is absolutely clear i.e.
  	 *    both family and mask are zero.
  	 */
  	plen = 32;
  	addr = sk_extract_addr(&rt->rt_dst);
  	if (!(rt->rt_flags & RTF_HOST)) {
81f7bf6cb   Al Viro   [IPV4]: net/ipv4/...
476
  		__be32 mask = sk_extract_addr(&rt->rt_genmask);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  
  		if (rt->rt_genmask.sa_family != AF_INET) {
  			if (mask || rt->rt_genmask.sa_family)
  				return -EAFNOSUPPORT;
  		}
  
  		if (bad_mask(mask, addr))
  			return -EINVAL;
  
  		plen = inet_mask_len(mask);
  	}
  
  	cfg->fc_dst_len = plen;
  	cfg->fc_dst = addr;
  
  	if (cmd != SIOCDELRT) {
  		cfg->fc_nlflags = NLM_F_CREATE;
  		cfg->fc_protocol = RTPROT_BOOT;
  	}
  
  	if (rt->rt_metric)
  		cfg->fc_priority = rt->rt_metric - 1;
  
  	if (rt->rt_flags & RTF_REJECT) {
  		cfg->fc_scope = RT_SCOPE_HOST;
  		cfg->fc_type = RTN_UNREACHABLE;
  		return 0;
  	}
  
  	cfg->fc_scope = RT_SCOPE_NOWHERE;
  	cfg->fc_type = RTN_UNICAST;
  
  	if (rt->rt_dev) {
  		char *colon;
  		struct net_device *dev;
  		char devname[IFNAMSIZ];
  
  		if (copy_from_user(devname, rt->rt_dev, IFNAMSIZ-1))
  			return -EFAULT;
  
  		devname[IFNAMSIZ-1] = 0;
  		colon = strchr(devname, ':');
  		if (colon)
  			*colon = 0;
4b5d47d4d   Denis V. Lunev   [NETNS]: Correctl...
521
  		dev = __dev_get_by_name(net, devname);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
522
523
524
  		if (!dev)
  			return -ENODEV;
  		cfg->fc_oif = dev->ifindex;
5a56a0b3a   Mark Tomlinson   net: Don't delete...
525
  		cfg->fc_table = l3mdev_fib_table(dev);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
526
  		if (colon) {
cd5a411db   Florian Westphal   net: use new in_d...
527
528
529
530
  			const struct in_ifaddr *ifa;
  			struct in_device *in_dev;
  
  			in_dev = __in_dev_get_rtnl(dev);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
531
532
  			if (!in_dev)
  				return -ENODEV;
cd5a411db   Florian Westphal   net: use new in_d...
533

4e902c574   Thomas Graf   [IPv4]: FIB confi...
534
  			*colon = ':';
cd5a411db   Florian Westphal   net: use new in_d...
535
536
537
  
  			rcu_read_lock();
  			in_dev_for_each_ifa_rcu(ifa, in_dev) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
538
539
  				if (strcmp(ifa->ifa_label, devname) == 0)
  					break;
cd5a411db   Florian Westphal   net: use new in_d...
540
541
  			}
  			rcu_read_unlock();
51456b291   Ian Morris   ipv4: coding styl...
542
  			if (!ifa)
4e902c574   Thomas Graf   [IPv4]: FIB confi...
543
544
545
546
547
548
549
  				return -ENODEV;
  			cfg->fc_prefsrc = ifa->ifa_local;
  		}
  	}
  
  	addr = sk_extract_addr(&rt->rt_gateway);
  	if (rt->rt_gateway.sa_family == AF_INET && addr) {
30bbaa195   David Ahern   net: Fix up inet_...
550
  		unsigned int addr_type;
f35b794b3   David Ahern   ipv4: Prepare fib...
551
552
  		cfg->fc_gw4 = addr;
  		cfg->fc_gw_family = AF_INET;
30bbaa195   David Ahern   net: Fix up inet_...
553
  		addr_type = inet_addr_type_table(net, addr, cfg->fc_table);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
554
  		if (rt->rt_flags & RTF_GATEWAY &&
30bbaa195   David Ahern   net: Fix up inet_...
555
  		    addr_type == RTN_UNICAST)
4e902c574   Thomas Graf   [IPv4]: FIB confi...
556
557
558
559
560
  			cfg->fc_scope = RT_SCOPE_UNIVERSE;
  	}
  
  	if (cmd == SIOCDELRT)
  		return 0;
f35b794b3   David Ahern   ipv4: Prepare fib...
561
  	if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw_family)
4e902c574   Thomas Graf   [IPv4]: FIB confi...
562
563
564
565
566
567
568
569
  		return -EINVAL;
  
  	if (cfg->fc_scope == RT_SCOPE_NOWHERE)
  		cfg->fc_scope = RT_SCOPE_LINK;
  
  	if (rt->rt_flags & (RTF_MTU | RTF_WINDOW | RTF_IRTT)) {
  		struct nlattr *mx;
  		int len = 0;
6396bb221   Kees Cook   treewide: kzalloc...
570
  		mx = kcalloc(3, nla_total_size(4), GFP_KERNEL);
51456b291   Ian Morris   ipv4: coding styl...
571
  		if (!mx)
4e902c574   Thomas Graf   [IPv4]: FIB confi...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  			return -ENOMEM;
  
  		if (rt->rt_flags & RTF_MTU)
  			len = put_rtax(mx, len, RTAX_ADVMSS, rt->rt_mtu - 40);
  
  		if (rt->rt_flags & RTF_WINDOW)
  			len = put_rtax(mx, len, RTAX_WINDOW, rt->rt_window);
  
  		if (rt->rt_flags & RTF_IRTT)
  			len = put_rtax(mx, len, RTAX_RTT, rt->rt_irtt << 3);
  
  		cfg->fc_mx = mx;
  		cfg->fc_mx_len = len;
  	}
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
  /*
6a31d2a97   Eric Dumazet   fib: cleanups
590
591
   * Handle IP routing ioctl calls.
   * These are used to manipulate the routing tables
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
   */
ca25c3004   Al Viro   ip_rt_ioctl(): ta...
593
  int ip_rt_ioctl(struct net *net, unsigned int cmd, struct rtentry *rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
  {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
595
  	struct fib_config cfg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
599
600
  
  	switch (cmd) {
  	case SIOCADDRT:		/* Add a route */
  	case SIOCDELRT:		/* Delete a route */
52e804c6d   Eric W. Biederman   net: Allow userns...
601
  		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  			return -EPERM;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
603

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  		rtnl_lock();
ca25c3004   Al Viro   ip_rt_ioctl(): ta...
605
  		err = rtentry_to_fib_config(net, cmd, rt, &cfg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
  		if (err == 0) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
607
  			struct fib_table *tb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  			if (cmd == SIOCDELRT) {
1bad118a3   Denis V. Lunev   [NETNS]: Pass nam...
609
  				tb = fib_get_table(net, cfg.fc_table);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
  				if (tb)
780559989   David Ahern   net: ipv4: Add ex...
611
612
  					err = fib_table_delete(net, tb, &cfg,
  							       NULL);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
613
614
  				else
  					err = -ESRCH;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
  			} else {
1bad118a3   Denis V. Lunev   [NETNS]: Pass nam...
616
  				tb = fib_new_table(net, cfg.fc_table);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  				if (tb)
6d8422a17   David Ahern   net: ipv4: Plumb ...
618
619
  					err = fib_table_insert(net, tb,
  							       &cfg, NULL);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
620
621
  				else
  					err = -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
  			}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
623
624
625
  
  			/* allocated by rtentry_to_fib_config() */
  			kfree(cfg.fc_mx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
627
628
629
630
631
  		}
  		rtnl_unlock();
  		return err;
  	}
  	return -EINVAL;
  }
6a31d2a97   Eric Dumazet   fib: cleanups
632
  const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = {
75425657f   David Ahern   net: Set strict_s...
633
  	[RTA_UNSPEC]		= { .strict_start_type = RTA_DPORT + 1 },
4e902c574   Thomas Graf   [IPv4]: FIB confi...
634
635
636
637
638
639
640
641
  	[RTA_DST]		= { .type = NLA_U32 },
  	[RTA_SRC]		= { .type = NLA_U32 },
  	[RTA_IIF]		= { .type = NLA_U32 },
  	[RTA_OIF]		= { .type = NLA_U32 },
  	[RTA_GATEWAY]		= { .type = NLA_U32 },
  	[RTA_PRIORITY]		= { .type = NLA_U32 },
  	[RTA_PREFSRC]		= { .type = NLA_U32 },
  	[RTA_METRICS]		= { .type = NLA_NESTED },
5176f91ea   Thomas Graf   [NETLINK]: Make u...
642
  	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
4e902c574   Thomas Graf   [IPv4]: FIB confi...
643
  	[RTA_FLOW]		= { .type = NLA_U32 },
571e72267   Roopa Prabhu   ipv4: support for...
644
645
  	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
  	[RTA_ENCAP]		= { .type = NLA_NESTED },
622ec2c9d   Lorenzo Colitti   net: core: add UI...
646
  	[RTA_UID]		= { .type = NLA_U32 },
3b45a4106   Liping Zhang   net: route: add m...
647
  	[RTA_MARK]		= { .type = NLA_U32 },
2eabd764c   Roopa Prabhu   net: ipv4: add mi...
648
  	[RTA_TABLE]		= { .type = NLA_U32 },
404eb77ea   Roopa Prabhu   ipv4: support spo...
649
650
651
  	[RTA_IP_PROTO]		= { .type = NLA_U8 },
  	[RTA_SPORT]		= { .type = NLA_U16 },
  	[RTA_DPORT]		= { .type = NLA_U16 },
493ced1ac   David Ahern   ipv4: Allow route...
652
  	[RTA_NH_ID]		= { .type = NLA_U32 },
4e902c574   Thomas Graf   [IPv4]: FIB confi...
653
  };
d15662682   David Ahern   ipv4: Allow ipv6 ...
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  int fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla,
  		    struct netlink_ext_ack *extack)
  {
  	struct rtvia *via;
  	int alen;
  
  	if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) {
  		NL_SET_ERR_MSG(extack, "Invalid attribute length for RTA_VIA");
  		return -EINVAL;
  	}
  
  	via = nla_data(nla);
  	alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr);
  
  	switch (via->rtvia_family) {
  	case AF_INET:
  		if (alen != sizeof(__be32)) {
  			NL_SET_ERR_MSG(extack, "Invalid IPv4 address in RTA_VIA");
  			return -EINVAL;
  		}
  		cfg->fc_gw_family = AF_INET;
  		cfg->fc_gw4 = *((__be32 *)via->rtvia_addr);
  		break;
  	case AF_INET6:
  #ifdef CONFIG_IPV6
  		if (alen != sizeof(struct in6_addr)) {
  			NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_VIA");
  			return -EINVAL;
  		}
  		cfg->fc_gw_family = AF_INET6;
  		cfg->fc_gw6 = *((struct in6_addr *)via->rtvia_addr);
  #else
  		NL_SET_ERR_MSG(extack, "IPv6 support not enabled in kernel");
  		return -EINVAL;
  #endif
  		break;
  	default:
  		NL_SET_ERR_MSG(extack, "Unsupported address family in RTA_VIA");
  		return -EINVAL;
  	}
  
  	return 0;
  }
4b5d47d4d   Denis V. Lunev   [NETNS]: Correctl...
697
  static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
6d8422a17   David Ahern   net: ipv4: Plumb ...
698
699
  			     struct nlmsghdr *nlh, struct fib_config *cfg,
  			     struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
  {
d15662682   David Ahern   ipv4: Allow ipv6 ...
701
  	bool has_gw = false, has_via = false;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
702
703
704
  	struct nlattr *attr;
  	int err, remaining;
  	struct rtmsg *rtm;
8cb081746   Johannes Berg   netlink: make val...
705
706
  	err = nlmsg_validate_deprecated(nlh, sizeof(*rtm), RTA_MAX,
  					rtm_ipv4_policy, extack);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
707
708
709
710
711
712
  	if (err < 0)
  		goto errout;
  
  	memset(cfg, 0, sizeof(*cfg));
  
  	rtm = nlmsg_data(nlh);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
713
  	cfg->fc_dst_len = rtm->rtm_dst_len;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
714
715
716
717
718
719
720
  	cfg->fc_tos = rtm->rtm_tos;
  	cfg->fc_table = rtm->rtm_table;
  	cfg->fc_protocol = rtm->rtm_protocol;
  	cfg->fc_scope = rtm->rtm_scope;
  	cfg->fc_type = rtm->rtm_type;
  	cfg->fc_flags = rtm->rtm_flags;
  	cfg->fc_nlflags = nlh->nlmsg_flags;
15e473046   Eric W. Biederman   netlink: Rename p...
721
  	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
722
  	cfg->fc_nlinfo.nlh = nlh;
4b5d47d4d   Denis V. Lunev   [NETNS]: Correctl...
723
  	cfg->fc_nlinfo.nl_net = net;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
724

a0ee18b9b   Thomas Graf   [IPv4] fib: Fix o...
725
  	if (cfg->fc_type > RTN_MAX) {
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
726
  		NL_SET_ERR_MSG(extack, "Invalid route type");
a0ee18b9b   Thomas Graf   [IPv4] fib: Fix o...
727
728
729
  		err = -EINVAL;
  		goto errout;
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
730
  	nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), remaining) {
8f4c1f9b0   Thomas Graf   [NETLINK]: Introd...
731
  		switch (nla_type(attr)) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
732
  		case RTA_DST:
17fb2c643   Al Viro   [IPV4]: RTA_{DST,...
733
  			cfg->fc_dst = nla_get_be32(attr);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
734
  			break;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
735
736
737
738
  		case RTA_OIF:
  			cfg->fc_oif = nla_get_u32(attr);
  			break;
  		case RTA_GATEWAY:
d15662682   David Ahern   ipv4: Allow ipv6 ...
739
  			has_gw = true;
f35b794b3   David Ahern   ipv4: Prepare fib...
740
  			cfg->fc_gw4 = nla_get_be32(attr);
d73f80f92   David Ahern   ipv4: Handle RTA_...
741
742
  			if (cfg->fc_gw4)
  				cfg->fc_gw_family = AF_INET;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
743
  			break;
b6e9e5df4   David Ahern   ipv4: Return erro...
744
  		case RTA_VIA:
d15662682   David Ahern   ipv4: Allow ipv6 ...
745
746
747
748
749
  			has_via = true;
  			err = fib_gw_from_via(cfg, attr, extack);
  			if (err)
  				goto errout;
  			break;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
750
751
752
753
  		case RTA_PRIORITY:
  			cfg->fc_priority = nla_get_u32(attr);
  			break;
  		case RTA_PREFSRC:
17fb2c643   Al Viro   [IPV4]: RTA_{DST,...
754
  			cfg->fc_prefsrc = nla_get_be32(attr);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
755
756
757
758
759
760
  			break;
  		case RTA_METRICS:
  			cfg->fc_mx = nla_data(attr);
  			cfg->fc_mx_len = nla_len(attr);
  			break;
  		case RTA_MULTIPATH:
9ed59592e   David Ahern   lwtunnel: fix aut...
761
  			err = lwtunnel_valid_encap_type_attr(nla_data(attr),
c255bd681   David Ahern   net: lwtunnel: Ad...
762
763
  							     nla_len(attr),
  							     extack);
9ed59592e   David Ahern   lwtunnel: fix aut...
764
765
  			if (err < 0)
  				goto errout;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
766
767
768
769
770
771
  			cfg->fc_mp = nla_data(attr);
  			cfg->fc_mp_len = nla_len(attr);
  			break;
  		case RTA_FLOW:
  			cfg->fc_flow = nla_get_u32(attr);
  			break;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
772
773
774
  		case RTA_TABLE:
  			cfg->fc_table = nla_get_u32(attr);
  			break;
571e72267   Roopa Prabhu   ipv4: support for...
775
776
777
778
779
  		case RTA_ENCAP:
  			cfg->fc_encap = attr;
  			break;
  		case RTA_ENCAP_TYPE:
  			cfg->fc_encap_type = nla_get_u16(attr);
c255bd681   David Ahern   net: lwtunnel: Ad...
780
781
  			err = lwtunnel_valid_encap_type(cfg->fc_encap_type,
  							extack);
9ed59592e   David Ahern   lwtunnel: fix aut...
782
783
  			if (err < 0)
  				goto errout;
571e72267   Roopa Prabhu   ipv4: support for...
784
  			break;
493ced1ac   David Ahern   ipv4: Allow route...
785
786
787
788
789
790
791
792
793
794
795
796
  		case RTA_NH_ID:
  			cfg->fc_nh_id = nla_get_u32(attr);
  			break;
  		}
  	}
  
  	if (cfg->fc_nh_id) {
  		if (cfg->fc_oif || cfg->fc_gw_family ||
  		    cfg->fc_encap || cfg->fc_mp) {
  			NL_SET_ERR_MSG(extack,
  				       "Nexthop specification and nexthop id are mutually exclusive");
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797
798
  		}
  	}
4e902c574   Thomas Graf   [IPv4]: FIB confi...
799

d15662682   David Ahern   ipv4: Allow ipv6 ...
800
801
802
803
804
  	if (has_gw && has_via) {
  		NL_SET_ERR_MSG(extack,
  			       "Nexthop configuration can not contain both GATEWAY and VIA");
  		goto errout;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
  	return 0;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
806
807
  errout:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
  }
c21ef3e34   David Ahern   net: rtnetlink: p...
809
810
  static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
  			     struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
812
  	struct net *net = sock_net(skb->sk);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
813
814
815
  	struct fib_config cfg;
  	struct fib_table *tb;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816

6d8422a17   David Ahern   net: ipv4: Plumb ...
817
  	err = rtm_to_fib_config(net, skb, nlh, &cfg, extack);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
818
819
  	if (err < 0)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820

493ced1ac   David Ahern   ipv4: Allow route...
821
822
823
824
825
  	if (cfg.fc_nh_id && !nexthop_find_by_id(net, cfg.fc_nh_id)) {
  		NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
  		err = -EINVAL;
  		goto errout;
  	}
8ad4942cd   Denis V. Lunev   [NETNS]: Add netn...
826
  	tb = fib_get_table(net, cfg.fc_table);
51456b291   Ian Morris   ipv4: coding styl...
827
  	if (!tb) {
c3ab2b4ec   David Ahern   net: ipv4: Add ex...
828
  		NL_SET_ERR_MSG(extack, "FIB table does not exist");
4e902c574   Thomas Graf   [IPv4]: FIB confi...
829
830
831
  		err = -ESRCH;
  		goto errout;
  	}
780559989   David Ahern   net: ipv4: Add ex...
832
  	err = fib_table_delete(net, tb, &cfg, extack);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
833
834
  errout:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
  }
c21ef3e34   David Ahern   net: rtnetlink: p...
836
837
  static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
  			     struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
839
  	struct net *net = sock_net(skb->sk);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
840
841
842
  	struct fib_config cfg;
  	struct fib_table *tb;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843

6d8422a17   David Ahern   net: ipv4: Plumb ...
844
  	err = rtm_to_fib_config(net, skb, nlh, &cfg, extack);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
845
846
  	if (err < 0)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
847

226b0b4a5   Denis V. Lunev   [NETNS]: Replace ...
848
  	tb = fib_new_table(net, cfg.fc_table);
51456b291   Ian Morris   ipv4: coding styl...
849
  	if (!tb) {
4e902c574   Thomas Graf   [IPv4]: FIB confi...
850
851
852
  		err = -ENOBUFS;
  		goto errout;
  	}
6d8422a17   David Ahern   net: ipv4: Plumb ...
853
  	err = fib_table_insert(net, tb, &cfg, extack);
6e617de84   Paolo Abeni   net: avoid a full...
854
855
  	if (!err && cfg.fc_type == RTN_LOCAL)
  		net->ipv4.fib_has_custom_local_routes = true;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
856
857
  errout:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  }
4724676d5   David Ahern   net: Add struct f...
859
860
  int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
  			  struct fib_dump_filter *filter,
effe67926   David Ahern   net: Enable kerne...
861
  			  struct netlink_callback *cb)
e8ba330ac   David Ahern   rtnetlink: Update...
862
  {
effe67926   David Ahern   net: Enable kerne...
863
864
  	struct netlink_ext_ack *extack = cb->extack;
  	struct nlattr *tb[RTA_MAX + 1];
e8ba330ac   David Ahern   rtnetlink: Update...
865
  	struct rtmsg *rtm;
effe67926   David Ahern   net: Enable kerne...
866
867
868
  	int err, i;
  
  	ASSERT_RTNL();
e8ba330ac   David Ahern   rtnetlink: Update...
869
870
871
872
873
874
875
876
  
  	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
  		NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request");
  		return -EINVAL;
  	}
  
  	rtm = nlmsg_data(nlh);
  	if (rtm->rtm_dst_len || rtm->rtm_src_len  || rtm->rtm_tos   ||
effe67926   David Ahern   net: Enable kerne...
877
  	    rtm->rtm_scope) {
e8ba330ac   David Ahern   rtnetlink: Update...
878
879
880
  		NL_SET_ERR_MSG(extack, "Invalid values in header for FIB dump request");
  		return -EINVAL;
  	}
564c91f7e   Stefano Brivio   fib_frontend, ip6...
881

e8ba330ac   David Ahern   rtnetlink: Update...
882
883
884
885
  	if (rtm->rtm_flags & ~(RTM_F_CLONED | RTM_F_PREFIX)) {
  		NL_SET_ERR_MSG(extack, "Invalid flags for FIB dump request");
  		return -EINVAL;
  	}
564c91f7e   Stefano Brivio   fib_frontend, ip6...
886
887
888
889
  	if (rtm->rtm_flags & RTM_F_CLONED)
  		filter->dump_routes = false;
  	else
  		filter->dump_exceptions = false;
e8ba330ac   David Ahern   rtnetlink: Update...
890

ae677bbb4   David Ahern   net: Don't return...
891
  	filter->dump_all_families = (rtm->rtm_family == AF_UNSPEC);
effe67926   David Ahern   net: Enable kerne...
892
893
894
895
  	filter->flags    = rtm->rtm_flags;
  	filter->protocol = rtm->rtm_protocol;
  	filter->rt_type  = rtm->rtm_type;
  	filter->table_id = rtm->rtm_table;
8cb081746   Johannes Berg   netlink: make val...
896
897
  	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
  					    rtm_ipv4_policy, extack);
effe67926   David Ahern   net: Enable kerne...
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
  	if (err < 0)
  		return err;
  
  	for (i = 0; i <= RTA_MAX; ++i) {
  		int ifindex;
  
  		if (!tb[i])
  			continue;
  
  		switch (i) {
  		case RTA_TABLE:
  			filter->table_id = nla_get_u32(tb[i]);
  			break;
  		case RTA_OIF:
  			ifindex = nla_get_u32(tb[i]);
  			filter->dev = __dev_get_by_index(net, ifindex);
  			if (!filter->dev)
  				return -ENODEV;
  			break;
  		default:
  			NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request");
  			return -EINVAL;
  		}
  	}
  
  	if (filter->flags || filter->protocol || filter->rt_type ||
  	    filter->table_id || filter->dev) {
  		filter->filter_set = 1;
  		cb->answer_flags = NLM_F_DUMP_FILTERED;
e8ba330ac   David Ahern   rtnetlink: Update...
927
928
929
930
931
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(ip_valid_fib_dump_req);
63f3444fb   Thomas Graf   [IPv4]: Use rtnl ...
932
  static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
  {
564c91f7e   Stefano Brivio   fib_frontend, ip6...
934
935
  	struct fib_dump_filter filter = { .dump_routes = true,
  					  .dump_exceptions = true };
e8ba330ac   David Ahern   rtnetlink: Update...
936
  	const struct nlmsghdr *nlh = cb->nlh;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
937
  	struct net *net = sock_net(skb->sk);
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
938
939
  	unsigned int h, s_h;
  	unsigned int e = 0, s_e;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940
  	struct fib_table *tb;
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
941
  	struct hlist_head *head;
f6c5775ff   David Ahern   net: Improve hand...
942
  	int dumped = 0, err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943

e8ba330ac   David Ahern   rtnetlink: Update...
944
  	if (cb->strict_check) {
effe67926   David Ahern   net: Enable kerne...
945
  		err = ip_valid_fib_dump_req(net, nlh, &filter, cb);
e8ba330ac   David Ahern   rtnetlink: Update...
946
947
  		if (err < 0)
  			return err;
e4e92fb16   David Ahern   net/ipv4: Bail ea...
948
949
950
951
  	} else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) {
  		struct rtmsg *rtm = nlmsg_data(nlh);
  
  		filter.flags = rtm->rtm_flags & (RTM_F_PREFIX | RTM_F_CLONED);
e8ba330ac   David Ahern   rtnetlink: Update...
952
  	}
b597ca6e8   Stefano Brivio   ipv4/fib_frontend...
953
954
  	/* ipv4 does not use prefix flag */
  	if (filter.flags & RTM_F_PREFIX)
0b8c7f6f2   Li RongQing   ipv4: remove ip_r...
955
  		return skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956

18a8021a7   David Ahern   net/ipv4: Plumb s...
957
958
959
  	if (filter.table_id) {
  		tb = fib_get_table(net, filter.table_id);
  		if (!tb) {
ae677bbb4   David Ahern   net: Don't return...
960
961
  			if (filter.dump_all_families)
  				return skb->len;
18a8021a7   David Ahern   net/ipv4: Plumb s...
962
963
964
965
966
967
968
  			NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist");
  			return -ENOENT;
  		}
  
  		err = fib_table_dump(tb, skb, cb, &filter);
  		return skb->len ? : err;
  	}
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
969
970
  	s_h = cb->args[0];
  	s_e = cb->args[1];
a7e535312   Alexander Duyck   fib_trie: Make fi...
971
  	rcu_read_lock();
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
972
973
  	for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
  		e = 0;
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
974
  		head = &net->ipv4.fib_table_hash[h];
a7e535312   Alexander Duyck   fib_trie: Make fi...
975
  		hlist_for_each_entry_rcu(tb, head, tb_hlist) {
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
976
977
978
979
  			if (e < s_e)
  				goto next;
  			if (dumped)
  				memset(&cb->args[2], 0, sizeof(cb->args) -
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
980
  						 2 * sizeof(cb->args[0]));
18a8021a7   David Ahern   net/ipv4: Plumb s...
981
  			err = fib_table_dump(tb, skb, cb, &filter);
f6c5775ff   David Ahern   net: Improve hand...
982
983
984
985
986
987
  			if (err < 0) {
  				if (likely(skb->len))
  					goto out;
  
  				goto out_err;
  			}
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
988
989
990
991
  			dumped = 1;
  next:
  			e++;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
  	}
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
993
  out:
f6c5775ff   David Ahern   net: Improve hand...
994
995
  	err = skb->len;
  out_err:
a7e535312   Alexander Duyck   fib_trie: Make fi...
996
  	rcu_read_unlock();
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
997
998
  	cb->args[1] = e;
  	cb->args[0] = h;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999

f6c5775ff   David Ahern   net: Improve hand...
1000
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
1003
  }
  
  /* Prepare and feed intra-kernel routing request.
6a31d2a97   Eric Dumazet   fib: cleanups
1004
1005
1006
1007
   * Really, it should be netlink message, but :-( netlink
   * can be not configured, so that we feed it directly
   * to fib engine. It is legal, because all events occur
   * only when netlink is already locked.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
   */
af4d768ad   David Ahern   net/ipv4: Add sup...
1009
1010
  static void fib_magic(int cmd, int type, __be32 dst, int dst_len,
  		      struct in_ifaddr *ifa, u32 rt_priority)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
  {
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1012
  	struct net *net = dev_net(ifa->ifa_dev->dev);
3236b0042   David Ahern   net: Replace vrf_...
1013
  	u32 tb_id = l3mdev_fib_table(ifa->ifa_dev->dev);
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1014
1015
1016
1017
1018
1019
  	struct fib_table *tb;
  	struct fib_config cfg = {
  		.fc_protocol = RTPROT_KERNEL,
  		.fc_type = type,
  		.fc_dst = dst,
  		.fc_dst_len = dst_len,
af4d768ad   David Ahern   net/ipv4: Add sup...
1020
  		.fc_priority = rt_priority,
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1021
1022
1023
  		.fc_prefsrc = ifa->ifa_local,
  		.fc_oif = ifa->ifa_dev->dev->ifindex,
  		.fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
4d1169c1e   Denis V. Lunev   [NETNS]: Add netn...
1024
  		.fc_nlinfo = {
4b5d47d4d   Denis V. Lunev   [NETNS]: Correctl...
1025
  			.nl_net = net,
4d1169c1e   Denis V. Lunev   [NETNS]: Add netn...
1026
  		},
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1027
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1028

021dd3b8a   David Ahern   net: Add routes t...
1029
1030
  	if (!tb_id)
  		tb_id = (type == RTN_UNICAST) ? RT_TABLE_MAIN : RT_TABLE_LOCAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031

021dd3b8a   David Ahern   net: Add routes t...
1032
  	tb = fib_new_table(net, tb_id);
51456b291   Ian Morris   ipv4: coding styl...
1033
  	if (!tb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1034
  		return;
4e902c574   Thomas Graf   [IPv4]: FIB confi...
1035
  	cfg.fc_table = tb->tb_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036

4e902c574   Thomas Graf   [IPv4]: FIB confi...
1037
1038
1039
1040
  	if (type != RTN_LOCAL)
  		cfg.fc_scope = RT_SCOPE_LINK;
  	else
  		cfg.fc_scope = RT_SCOPE_HOST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1041
1042
  
  	if (cmd == RTM_NEWROUTE)
6d8422a17   David Ahern   net: ipv4: Plumb ...
1043
  		fib_table_insert(net, tb, &cfg, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
  	else
780559989   David Ahern   net: ipv4: Add ex...
1045
  		fib_table_delete(net, tb, &cfg, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1046
  }
0ff60a456   Jamal Hadi Salim   [IPV4]: Fix secon...
1047
  void fib_add_ifaddr(struct in_ifaddr *ifa)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
1049
1050
1051
  {
  	struct in_device *in_dev = ifa->ifa_dev;
  	struct net_device *dev = in_dev->dev;
  	struct in_ifaddr *prim = ifa;
a144ea4b7   Al Viro   [IPV4]: annotate ...
1052
1053
  	__be32 mask = ifa->ifa_mask;
  	__be32 addr = ifa->ifa_local;
6a31d2a97   Eric Dumazet   fib: cleanups
1054
  	__be32 prefix = ifa->ifa_address & mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1055

6a31d2a97   Eric Dumazet   fib: cleanups
1056
  	if (ifa->ifa_flags & IFA_F_SECONDARY) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
  		prim = inet_ifa_byprefix(in_dev, prefix, mask);
51456b291   Ian Morris   ipv4: coding styl...
1058
  		if (!prim) {
058bd4d2a   Joe Perches   net: Convert prin...
1059
1060
  			pr_warn("%s: bug: prim == NULL
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1061
1062
1063
  			return;
  		}
  	}
af4d768ad   David Ahern   net/ipv4: Add sup...
1064
  	fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065

6a31d2a97   Eric Dumazet   fib: cleanups
1066
  	if (!(dev->flags & IFF_UP))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1067
1068
1069
  		return;
  
  	/* Add broadcast address, if it is explicitly assigned. */
a144ea4b7   Al Viro   [IPV4]: annotate ...
1070
  	if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
af4d768ad   David Ahern   net/ipv4: Add sup...
1071
1072
  		fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32,
  			  prim, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073

6a31d2a97   Eric Dumazet   fib: cleanups
1074
  	if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1075
  	    (prefix != addr || ifa->ifa_prefixlen < 32)) {
7b1311807   Paolo Abeni   ipv4: implement s...
1076
1077
1078
  		if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))
  			fib_magic(RTM_NEWROUTE,
  				  dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
af4d768ad   David Ahern   net/ipv4: Add sup...
1079
1080
  				  prefix, ifa->ifa_prefixlen, prim,
  				  ifa->ifa_rt_priority);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1081
1082
1083
  
  		/* Add network specific broadcasts, when it takes a sense */
  		if (ifa->ifa_prefixlen < 31) {
af4d768ad   David Ahern   net/ipv4: Add sup...
1084
1085
  			fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32,
  				  prim, 0);
6a31d2a97   Eric Dumazet   fib: cleanups
1086
  			fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask,
af4d768ad   David Ahern   net/ipv4: Add sup...
1087
  				  32, prim, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
1089
1090
  		}
  	}
  }
af4d768ad   David Ahern   net/ipv4: Add sup...
1091
1092
1093
1094
1095
1096
1097
1098
1099
  void fib_modify_prefix_metric(struct in_ifaddr *ifa, u32 new_metric)
  {
  	__be32 prefix = ifa->ifa_address & ifa->ifa_mask;
  	struct in_device *in_dev = ifa->ifa_dev;
  	struct net_device *dev = in_dev->dev;
  
  	if (!(dev->flags & IFF_UP) ||
  	    ifa->ifa_flags & (IFA_F_SECONDARY | IFA_F_NOPREFIXROUTE) ||
  	    ipv4_is_zeronet(prefix) ||
0b834ba00   Paolo Abeni   ipv4: fix route u...
1100
  	    (prefix == ifa->ifa_local && ifa->ifa_prefixlen == 32))
af4d768ad   David Ahern   net/ipv4: Add sup...
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
  		return;
  
  	/* add the new */
  	fib_magic(RTM_NEWROUTE,
  		  dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
  		  prefix, ifa->ifa_prefixlen, ifa, new_metric);
  
  	/* delete the old */
  	fib_magic(RTM_DELROUTE,
  		  dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
  		  prefix, ifa->ifa_prefixlen, ifa, ifa->ifa_rt_priority);
  }
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1113
1114
1115
1116
1117
1118
  /* Delete primary or secondary address.
   * Optionally, on secondary address promotion consider the addresses
   * from subnet iprim as deleted, even if they are in device list.
   * In this case the secondary ifa can be in device list.
   */
  void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1119
1120
1121
1122
  {
  	struct in_device *in_dev = ifa->ifa_dev;
  	struct net_device *dev = in_dev->dev;
  	struct in_ifaddr *ifa1;
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1123
  	struct in_ifaddr *prim = ifa, *prim1 = NULL;
6a31d2a97   Eric Dumazet   fib: cleanups
1124
1125
  	__be32 brd = ifa->ifa_address | ~ifa->ifa_mask;
  	__be32 any = ifa->ifa_address & ifa->ifa_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1126
1127
1128
1129
  #define LOCAL_OK	1
  #define BRD_OK		2
  #define BRD0_OK		4
  #define BRD1_OK		8
95c961747   Eric Dumazet   net: cleanup unsi...
1130
  	unsigned int ok = 0;
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1131
1132
1133
  	int subnet = 0;		/* Primary network */
  	int gone = 1;		/* Address is missing */
  	int same_prefsrc = 0;	/* Another primary with same IP */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1134

e6abbaa27   Julian Anastasov   ipv4: fix route d...
1135
  	if (ifa->ifa_flags & IFA_F_SECONDARY) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1136
  		prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
51456b291   Ian Morris   ipv4: coding styl...
1137
  		if (!prim) {
391a20333   Paolo Abeni   ipv4/fib: don't w...
1138
1139
1140
1141
1142
1143
  			/* if the device has been deleted, we don't perform
  			 * address promotion
  			 */
  			if (!in_dev->dead)
  				pr_warn("%s: bug: prim == NULL
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1144
1145
  			return;
  		}
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1146
  		if (iprim && iprim != prim) {
058bd4d2a   Joe Perches   net: Convert prin...
1147
1148
  			pr_warn("%s: bug: iprim != prim
  ", __func__);
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1149
1150
1151
1152
  			return;
  		}
  	} else if (!ipv4_is_zeronet(any) &&
  		   (any != ifa->ifa_local || ifa->ifa_prefixlen < 32)) {
7b1311807   Paolo Abeni   ipv4: implement s...
1153
1154
1155
  		if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))
  			fib_magic(RTM_DELROUTE,
  				  dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
af4d768ad   David Ahern   net/ipv4: Add sup...
1156
  				  any, ifa->ifa_prefixlen, prim, 0);
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1157
  		subnet = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1158
  	}
fbd40ea01   David S. Miller   ipv4: Don't do ex...
1159
1160
  	if (in_dev->dead)
  		goto no_promotions;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1161
  	/* Deletion is more complicated than add.
6a31d2a97   Eric Dumazet   fib: cleanups
1162
1163
1164
  	 * We should take care of not to delete too much :-)
  	 *
  	 * Scan address list to be sure that addresses are really gone.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165
  	 */
cd5a411db   Florian Westphal   net: use new in_d...
1166
1167
  	rcu_read_lock();
  	in_dev_for_each_ifa_rcu(ifa1, in_dev) {
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
  		if (ifa1 == ifa) {
  			/* promotion, keep the IP */
  			gone = 0;
  			continue;
  		}
  		/* Ignore IFAs from our subnet */
  		if (iprim && ifa1->ifa_mask == iprim->ifa_mask &&
  		    inet_ifa_match(ifa1->ifa_address, iprim))
  			continue;
  
  		/* Ignore ifa1 if it uses different primary IP (prefsrc) */
  		if (ifa1->ifa_flags & IFA_F_SECONDARY) {
  			/* Another address from our subnet? */
  			if (ifa1->ifa_mask == prim->ifa_mask &&
  			    inet_ifa_match(ifa1->ifa_address, prim))
  				prim1 = prim;
  			else {
  				/* We reached the secondaries, so
  				 * same_prefsrc should be determined.
  				 */
  				if (!same_prefsrc)
  					continue;
  				/* Search new prim1 if ifa1 is not
  				 * using the current prim1
  				 */
  				if (!prim1 ||
  				    ifa1->ifa_mask != prim1->ifa_mask ||
  				    !inet_ifa_match(ifa1->ifa_address, prim1))
  					prim1 = inet_ifa_byprefix(in_dev,
  							ifa1->ifa_address,
  							ifa1->ifa_mask);
  				if (!prim1)
  					continue;
  				if (prim1->ifa_local != prim->ifa_local)
  					continue;
  			}
  		} else {
  			if (prim->ifa_local != ifa1->ifa_local)
  				continue;
  			prim1 = ifa1;
  			if (prim != prim1)
  				same_prefsrc = 1;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1211
1212
1213
1214
1215
1216
1217
1218
  		if (ifa->ifa_local == ifa1->ifa_local)
  			ok |= LOCAL_OK;
  		if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
  			ok |= BRD_OK;
  		if (brd == ifa1->ifa_broadcast)
  			ok |= BRD1_OK;
  		if (any == ifa1->ifa_broadcast)
  			ok |= BRD0_OK;
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
  		/* primary has network specific broadcasts */
  		if (prim1 == ifa1 && ifa1->ifa_prefixlen < 31) {
  			__be32 brd1 = ifa1->ifa_address | ~ifa1->ifa_mask;
  			__be32 any1 = ifa1->ifa_address & ifa1->ifa_mask;
  
  			if (!ipv4_is_zeronet(any1)) {
  				if (ifa->ifa_broadcast == brd1 ||
  				    ifa->ifa_broadcast == any1)
  					ok |= BRD_OK;
  				if (brd == brd1 || brd == any1)
  					ok |= BRD1_OK;
  				if (any == brd1 || any == any1)
  					ok |= BRD0_OK;
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1234
  	}
cd5a411db   Florian Westphal   net: use new in_d...
1235
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1236

fbd40ea01   David S. Miller   ipv4: Don't do ex...
1237
  no_promotions:
6a31d2a97   Eric Dumazet   fib: cleanups
1238
  	if (!(ok & BRD_OK))
af4d768ad   David Ahern   net/ipv4: Add sup...
1239
1240
  		fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32,
  			  prim, 0);
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1241
1242
  	if (subnet && ifa->ifa_prefixlen < 31) {
  		if (!(ok & BRD1_OK))
af4d768ad   David Ahern   net/ipv4: Add sup...
1243
1244
  			fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32,
  				  prim, 0);
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1245
  		if (!(ok & BRD0_OK))
af4d768ad   David Ahern   net/ipv4: Add sup...
1246
1247
  			fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32,
  				  prim, 0);
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1248
  	}
6a31d2a97   Eric Dumazet   fib: cleanups
1249
  	if (!(ok & LOCAL_OK)) {
30bbaa195   David Ahern   net: Fix up inet_...
1250
  		unsigned int addr_type;
af4d768ad   David Ahern   net/ipv4: Add sup...
1251
  		fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1252
1253
  
  		/* Check, that this local address finally disappeared. */
30bbaa195   David Ahern   net: Fix up inet_...
1254
1255
1256
  		addr_type = inet_addr_type_dev_table(dev_net(dev), dev,
  						     ifa->ifa_local);
  		if (gone && addr_type != RTN_LOCAL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1257
  			/* And the last, but not the least thing.
6a31d2a97   Eric Dumazet   fib: cleanups
1258
1259
1260
1261
1262
  			 * We must flush stray FIB entries.
  			 *
  			 * First of all, we scan fib_info list searching
  			 * for stray nexthop entries, then ignite fib_flush.
  			 */
5a56a0b3a   Mark Tomlinson   net: Don't delete...
1263
  			if (fib_sync_down_addr(dev, ifa->ifa_local))
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1264
  				fib_flush(dev_net(dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1265
1266
1267
1268
1269
1270
1271
  		}
  	}
  #undef LOCAL_OK
  #undef BRD_OK
  #undef BRD0_OK
  #undef BRD1_OK
  }
345e9b542   Alexander Duyck   fib_trie: Push rc...
1272
  static void nl_fib_lookup(struct net *net, struct fib_result_nl *frn)
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1273
  {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1274

246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1275
  	struct fib_result       res;
9ade22861   David S. Miller   ipv4: Use flowi4 ...
1276
1277
1278
1279
1280
  	struct flowi4           fl4 = {
  		.flowi4_mark = frn->fl_mark,
  		.daddr = frn->fl_addr,
  		.flowi4_tos = frn->fl_tos,
  		.flowi4_scope = frn->fl_scope,
6a31d2a97   Eric Dumazet   fib: cleanups
1281
  	};
345e9b542   Alexander Duyck   fib_trie: Push rc...
1282
1283
1284
1285
1286
  	struct fib_table *tb;
  
  	rcu_read_lock();
  
  	tb = fib_get_table(net, frn->tb_id_in);
1194ed0a3   Alexey Kuznetsov   [NETLINK]: Infini...
1287
1288
  
  	frn->err = -ENOENT;
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1289
1290
1291
1292
  	if (tb) {
  		local_bh_disable();
  
  		frn->tb_id = tb->tb_id;
9ade22861   David S. Miller   ipv4: Use flowi4 ...
1293
  		frn->err = fib_table_lookup(tb, &fl4, &res, FIB_LOOKUP_NOREF);
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1294
1295
1296
1297
1298
1299
1300
1301
1302
  
  		if (!frn->err) {
  			frn->prefixlen = res.prefixlen;
  			frn->nh_sel = res.nh_sel;
  			frn->type = res.type;
  			frn->scope = res.scope;
  		}
  		local_bh_enable();
  	}
345e9b542   Alexander Duyck   fib_trie: Push rc...
1303
1304
  
  	rcu_read_unlock();
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1305
  }
28f7b0360   David S. Miller   [NETLINK]: fib_fr...
1306
  static void nl_fib_input(struct sk_buff *skb)
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1307
  {
6bd48fcf7   Denis V. Lunev   [NETNS]: Provide ...
1308
  	struct net *net;
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1309
  	struct fib_result_nl *frn;
28f7b0360   David S. Miller   [NETLINK]: fib_fr...
1310
  	struct nlmsghdr *nlh;
15e473046   Eric W. Biederman   netlink: Rename p...
1311
  	u32 portid;
1194ed0a3   Alexey Kuznetsov   [NETLINK]: Infini...
1312

3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1313
  	net = sock_net(skb->sk);
b529ccf27   Arnaldo Carvalho de Melo   [NETLINK]: Introd...
1314
  	nlh = nlmsg_hdr(skb);
c64c0b3ca   Eric Dumazet   ipv4: provide str...
1315
1316
  	if (skb->len < nlmsg_total_size(sizeof(*frn)) ||
  	    skb->len < nlh->nlmsg_len ||
573ce260b   Hong zhi guo   net-next: replace...
1317
  	    nlmsg_len(nlh) < sizeof(*frn))
ea86575ea   Thomas Graf   [NETLINK]: Fix pr...
1318
  		return;
d883a0367   Denis V. Lunev   [IPV4]: OOPS with...
1319

3a36515f7   Pablo Neira   netlink: fix spla...
1320
  	skb = netlink_skb_clone(skb, GFP_KERNEL);
51456b291   Ian Morris   ipv4: coding styl...
1321
  	if (!skb)
d883a0367   Denis V. Lunev   [IPV4]: OOPS with...
1322
1323
  		return;
  	nlh = nlmsg_hdr(skb);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1324

573ce260b   Hong zhi guo   net-next: replace...
1325
  	frn = (struct fib_result_nl *) nlmsg_data(nlh);
345e9b542   Alexander Duyck   fib_trie: Push rc...
1326
  	nl_fib_lookup(net, frn);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1327

28a28283f   Rami Rosen   ipv4: fib: fix a ...
1328
  	portid = NETLINK_CB(skb).portid;      /* netlink portid */
15e473046   Eric W. Biederman   netlink: Rename p...
1329
  	NETLINK_CB(skb).portid = 0;        /* from kernel */
ac6d439d2   Patrick McHardy   [NETLINK]: Conver...
1330
  	NETLINK_CB(skb).dst_group = 0;  /* unicast */
15e473046   Eric W. Biederman   netlink: Rename p...
1331
  	netlink_unicast(net->ipv4.fibnl, skb, portid, MSG_DONTWAIT);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1332
  }
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1333

2c8c1e729   Alexey Dobriyan   net: spread __net...
1334
  static int __net_init nl_fib_lookup_init(struct net *net)
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1335
  {
6bd48fcf7   Denis V. Lunev   [NETNS]: Provide ...
1336
  	struct sock *sk;
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
1337
1338
1339
  	struct netlink_kernel_cfg cfg = {
  		.input	= nl_fib_input,
  	};
9f00d9776   Pablo Neira Ayuso   netlink: hide str...
1340
  	sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, &cfg);
51456b291   Ian Morris   ipv4: coding styl...
1341
  	if (!sk)
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1342
  		return -EAFNOSUPPORT;
6bd48fcf7   Denis V. Lunev   [NETNS]: Provide ...
1343
  	net->ipv4.fibnl = sk;
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1344
1345
1346
1347
1348
  	return 0;
  }
  
  static void nl_fib_lookup_exit(struct net *net)
  {
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
1349
  	netlink_kernel_release(net->ipv4.fibnl);
775516bfa   Denis V. Lunev   [NETNS]: Namespac...
1350
  	net->ipv4.fibnl = NULL;
246955fe4   Robert Olsson   [NETLINK]: fib_lo...
1351
  }
4f823defd   Julian Anastasov   ipv4: fix to not ...
1352
1353
  static void fib_disable_ip(struct net_device *dev, unsigned long event,
  			   bool force)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1354
  {
4f823defd   Julian Anastasov   ipv4: fix to not ...
1355
  	if (fib_sync_down_dev(dev, event, force))
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1356
  		fib_flush(dev_net(dev));
06b4fc520   Gao Feng   net: fib: Decreas...
1357
1358
  	else
  		rt_cache_flush(dev_net(dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1359
1360
1361
1362
1363
  	arp_ifdown(dev);
  }
  
  static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
  {
6ed2533e5   Jianjun Kong   net: clean up net...
1364
  	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
76e6ebfb4   Denis V. Lunev   netns: add namesp...
1365
  	struct net_device *dev = ifa->ifa_dev->dev;
436c3b66e   David S. Miller   ipv4: Invalidate ...
1366
  	struct net *net = dev_net(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1367
1368
1369
1370
1371
  
  	switch (event) {
  	case NETDEV_UP:
  		fib_add_ifaddr(ifa);
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
8a3d03166   Andy Gospodarek   net: track link-s...
1372
  		fib_sync_up(dev, RTNH_F_DEAD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1373
  #endif
436c3b66e   David S. Miller   ipv4: Invalidate ...
1374
  		atomic_inc(&net->ipv4.dev_addr_genid);
4ccfe6d41   Nicolas Dichtel   ipv4/route: arg d...
1375
  		rt_cache_flush(dev_net(dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1376
1377
  		break;
  	case NETDEV_DOWN:
e6abbaa27   Julian Anastasov   ipv4: fix route d...
1378
  		fib_del_ifaddr(ifa, NULL);
436c3b66e   David S. Miller   ipv4: Invalidate ...
1379
  		atomic_inc(&net->ipv4.dev_addr_genid);
51456b291   Ian Morris   ipv4: coding styl...
1380
  		if (!ifa->ifa_dev->ifa_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381
  			/* Last address was deleted from this interface.
6a31d2a97   Eric Dumazet   fib: cleanups
1382
  			 * Disable IP.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
  			 */
4f823defd   Julian Anastasov   ipv4: fix to not ...
1384
  			fib_disable_ip(dev, event, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1385
  		} else {
4ccfe6d41   Nicolas Dichtel   ipv4/route: arg d...
1386
  			rt_cache_flush(dev_net(dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1387
1388
1389
1390
1391
1392
1393
1394
  		}
  		break;
  	}
  	return NOTIFY_DONE;
  }
  
  static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
  {
351638e7d   Jiri Pirko   net: pass info st...
1395
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1396
1397
  	struct netdev_notifier_changeupper_info *upper_info = ptr;
  	struct netdev_notifier_info_ext *info_ext = ptr;
0115e8e30   Eric Dumazet   net: remove delay...
1398
  	struct in_device *in_dev;
436c3b66e   David S. Miller   ipv4: Invalidate ...
1399
  	struct net *net = dev_net(dev);
cd5a411db   Florian Westphal   net: use new in_d...
1400
  	struct in_ifaddr *ifa;
8a3d03166   Andy Gospodarek   net: track link-s...
1401
  	unsigned int flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1402
1403
  
  	if (event == NETDEV_UNREGISTER) {
4f823defd   Julian Anastasov   ipv4: fix to not ...
1404
  		fib_disable_ip(dev, event, true);
caacf05e5   David S. Miller   ipv4: Properly pu...
1405
  		rt_flush_dev(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1406
1407
  		return NOTIFY_DONE;
  	}
0115e8e30   Eric Dumazet   net: remove delay...
1408
  	in_dev = __in_dev_get_rtnl(dev);
a0065f266   Oliver Hartkopp   fib_frontend: fix...
1409
1410
  	if (!in_dev)
  		return NOTIFY_DONE;
0115e8e30   Eric Dumazet   net: remove delay...
1411

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1412
1413
  	switch (event) {
  	case NETDEV_UP:
cd5a411db   Florian Westphal   net: use new in_d...
1414
  		in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1415
  			fib_add_ifaddr(ifa);
cd5a411db   Florian Westphal   net: use new in_d...
1416
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1417
  #ifdef CONFIG_IP_ROUTE_MULTIPATH
8a3d03166   Andy Gospodarek   net: track link-s...
1418
  		fib_sync_up(dev, RTNH_F_DEAD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1419
  #endif
436c3b66e   David S. Miller   ipv4: Invalidate ...
1420
  		atomic_inc(&net->ipv4.dev_addr_genid);
4ccfe6d41   Nicolas Dichtel   ipv4/route: arg d...
1421
  		rt_cache_flush(net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422
1423
  		break;
  	case NETDEV_DOWN:
4f823defd   Julian Anastasov   ipv4: fix to not ...
1424
  		fib_disable_ip(dev, event, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1426
  	case NETDEV_CHANGE:
8a3d03166   Andy Gospodarek   net: track link-s...
1427
1428
1429
1430
  		flags = dev_get_flags(dev);
  		if (flags & (IFF_RUNNING | IFF_LOWER_UP))
  			fib_sync_up(dev, RTNH_F_LINKDOWN);
  		else
4f823defd   Julian Anastasov   ipv4: fix to not ...
1431
  			fib_sync_down_dev(dev, event, false);
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1432
1433
  		rt_cache_flush(net);
  		break;
8a3d03166   Andy Gospodarek   net: track link-s...
1434
  	case NETDEV_CHANGEMTU:
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1435
  		fib_sync_mtu(dev, info_ext->ext.mtu);
4ccfe6d41   Nicolas Dichtel   ipv4/route: arg d...
1436
  		rt_cache_flush(net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1437
  		break;
7f49e7a38   David Ahern   net: Flush local ...
1438
  	case NETDEV_CHANGEUPPER:
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1439
  		upper_info = ptr;
7f49e7a38   David Ahern   net: Flush local ...
1440
1441
1442
  		/* flush all routes if dev is linked to or unlinked from
  		 * an L3 master device (e.g., VRF)
  		 */
af7d6cce5   Sabrina Dubroca   net: ipv4: update...
1443
1444
  		if (upper_info->upper_dev &&
  		    netif_is_l3_master(upper_info->upper_dev))
7f49e7a38   David Ahern   net: Flush local ...
1445
1446
  			fib_disable_ip(dev, NETDEV_DOWN, true);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1447
1448
1449
1450
1451
  	}
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block fib_inetaddr_notifier = {
6ed2533e5   Jianjun Kong   net: clean up net...
1452
  	.notifier_call = fib_inetaddr_event,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1453
1454
1455
  };
  
  static struct notifier_block fib_netdev_notifier = {
6ed2533e5   Jianjun Kong   net: clean up net...
1456
  	.notifier_call = fib_netdev_event,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1457
  };
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1458
  static int __net_init ip_fib_net_init(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1459
  {
dce5cbeec   Denis V. Lunev   [IPV4]: Fix memor...
1460
  	int err;
10da66f75   Eric Dumazet   fib: avoid false ...
1461
  	size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ;
04b1d4e50   Ido Schimmel   net: core: Make t...
1462
1463
1464
  	err = fib4_notifier_init(net);
  	if (err)
  		return err;
cacaad11f   Ido Schimmel   ipv4: fib: Allow ...
1465

10da66f75   Eric Dumazet   fib: avoid false ...
1466
1467
  	/* Avoid false sharing : Use at least a full cache line */
  	size = max_t(size_t, size, L1_CACHE_BYTES);
1af5a8c4a   Patrick McHardy   [IPV4]: Increase ...
1468

10da66f75   Eric Dumazet   fib: avoid false ...
1469
  	net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL);
04b1d4e50   Ido Schimmel   net: core: Make t...
1470
1471
1472
1473
  	if (!net->ipv4.fib_table_hash) {
  		err = -ENOMEM;
  		goto err_table_hash_alloc;
  	}
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
1474

dce5cbeec   Denis V. Lunev   [IPV4]: Fix memor...
1475
1476
  	err = fib4_rules_init(net);
  	if (err < 0)
04b1d4e50   Ido Schimmel   net: core: Make t...
1477
  		goto err_rules_init;
dce5cbeec   Denis V. Lunev   [IPV4]: Fix memor...
1478
  	return 0;
04b1d4e50   Ido Schimmel   net: core: Make t...
1479
  err_rules_init:
dce5cbeec   Denis V. Lunev   [IPV4]: Fix memor...
1480
  	kfree(net->ipv4.fib_table_hash);
04b1d4e50   Ido Schimmel   net: core: Make t...
1481
1482
  err_table_hash_alloc:
  	fib4_notifier_exit(net);
dce5cbeec   Denis V. Lunev   [IPV4]: Fix memor...
1483
  	return err;
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1484
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1485

2c8c1e729   Alexey Dobriyan   net: spread __net...
1486
  static void ip_fib_net_exit(struct net *net)
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1487
  {
b4681c282   Ido Schimmel   ipv4: Fix use-aft...
1488
  	int i;
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1489

6dede75b7   Sabrina Dubroca   fib_trie: call fi...
1490
  	rtnl_lock();
6e47d6caf   Alexander Duyck   fib_trie: Cleanup...
1491
  #ifdef CONFIG_IP_MULTIPLE_TABLES
6e47d6caf   Alexander Duyck   fib_trie: Cleanup...
1492
1493
1494
  	RCU_INIT_POINTER(net->ipv4.fib_main, NULL);
  	RCU_INIT_POINTER(net->ipv4.fib_default, NULL);
  #endif
b4681c282   Ido Schimmel   ipv4: Fix use-aft...
1495
1496
1497
1498
1499
1500
  	/* Destroy the tables in reverse order to guarantee that the
  	 * local table, ID 255, is destroyed before the main table, ID
  	 * 254. This is necessary as the local table may contain
  	 * references to data contained in the main table.
  	 */
  	for (i = FIB_TABLE_HASHSZ - 1; i >= 0; i--) {
a7e535312   Alexander Duyck   fib_trie: Make fi...
1501
  		struct hlist_head *head = &net->ipv4.fib_table_hash[i];
b67bfe0d4   Sasha Levin   hlist: drop the n...
1502
  		struct hlist_node *tmp;
a7e535312   Alexander Duyck   fib_trie: Make fi...
1503
  		struct fib_table *tb;
b67bfe0d4   Sasha Levin   hlist: drop the n...
1504
1505
  		hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) {
  			hlist_del(&tb->tb_hlist);
f97f4dd8b   Ido Schimmel   net: ipv4: Fix me...
1506
  			fib_table_flush(net, tb, true);
4aa2c466a   Pavel Emelyanov   fib: Fix fib zone...
1507
  			fib_free_table(tb);
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1508
1509
  		}
  	}
ad88d0513   Alexander Duyck   fib_trie: Fix war...
1510
1511
1512
1513
  
  #ifdef CONFIG_IP_MULTIPLE_TABLES
  	fib4_rules_exit(net);
  #endif
e2666f849   Eric Dumazet   fib: add rtnl loc...
1514
  	rtnl_unlock();
e4aef8aea   Denis V. Lunev   [NETNS]: Place fi...
1515
  	kfree(net->ipv4.fib_table_hash);
04b1d4e50   Ido Schimmel   net: core: Make t...
1516
  	fib4_notifier_exit(net);
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1517
1518
1519
1520
1521
  }
  
  static int __net_init fib_net_init(struct net *net)
  {
  	int error;
f4530fa57   David S. Miller   ipv4: Avoid overh...
1522
1523
1524
  #ifdef CONFIG_IP_ROUTE_CLASSID
  	net->ipv4.fib_num_tclassid_users = 0;
  #endif
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
  	error = ip_fib_net_init(net);
  	if (error < 0)
  		goto out;
  	error = nl_fib_lookup_init(net);
  	if (error < 0)
  		goto out_nlfl;
  	error = fib_proc_init(net);
  	if (error < 0)
  		goto out_proc;
  out:
  	return error;
  
  out_proc:
  	nl_fib_lookup_exit(net);
  out_nlfl:
  	ip_fib_net_exit(net);
  	goto out;
  }
  
  static void __net_exit fib_net_exit(struct net *net)
  {
  	fib_proc_exit(net);
  	nl_fib_lookup_exit(net);
  	ip_fib_net_exit(net);
  }
  
  static struct pernet_operations fib_net_ops = {
  	.init = fib_net_init,
  	.exit = fib_net_exit,
  };
  
  void __init ip_fib_init(void)
  {
8799a221f   Mahesh Bandewar   ipv4: initialize ...
1558
  	fib_trie_init();
7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1559
1560
  
  	register_pernet_subsys(&fib_net_ops);
8799a221f   Mahesh Bandewar   ipv4: initialize ...
1561

7b1a74fdb   Denis V. Lunev   [NETNS]: Refactor...
1562
1563
  	register_netdevice_notifier(&fib_netdev_notifier);
  	register_inetaddr_notifier(&fib_inetaddr_notifier);
7f9b80529   Stephen Hemminger   [IPV4]: fib hash|...
1564

b97bac64a   Florian Westphal   rtnetlink: make r...
1565
1566
1567
  	rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, 0);
  	rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, 0);
  	rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1568
  }