blob: 6761639dd0e975a902d83bb15f0cc350790a63af [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * INET An implementation of the TCP/IP protocol suite for the LINUX
3 * operating system. INET is implemented using the BSD Socket
4 * interface as the means of communication with the user level.
5 *
6 * IPv4 Forwarding Information Base: FIB frontend.
7 *
8 * Version: $Id: fib_frontend.c,v 1.26 2001/10/31 21:55:54 davem Exp $
9 *
10 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/module.h>
19#include <asm/uaccess.h>
20#include <asm/system.h>
21#include <linux/bitops.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080022#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/types.h>
24#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <linux/mm.h>
26#include <linux/string.h>
27#include <linux/socket.h>
28#include <linux/sockios.h>
29#include <linux/errno.h>
30#include <linux/in.h>
31#include <linux/inet.h>
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020032#include <linux/inetdevice.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/netdevice.h>
Thomas Graf18237302006-08-04 23:04:54 -070034#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/if_arp.h>
36#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070038#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
40#include <net/ip.h>
41#include <net/protocol.h>
42#include <net/route.h>
43#include <net/tcp.h>
44#include <net/sock.h>
45#include <net/icmp.h>
46#include <net/arp.h>
47#include <net/ip_fib.h>
Thomas Graf63f34442007-03-22 11:55:17 -070048#include <net/rtnetlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#ifndef CONFIG_IP_MULTIPLE_TABLES
51
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -080052static int __net_init fib4_rules_init(struct net *net)
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -080053{
Denis V. Lunev93456b62008-01-10 03:23:38 -080054 struct fib_table *local_table, *main_table;
55
Stephen Hemminger7f9b8052008-01-14 23:14:20 -080056 local_table = fib_hash_table(RT_TABLE_LOCAL);
Denis V. Lunev93456b62008-01-10 03:23:38 -080057 if (local_table == NULL)
Denis V. Lunevdbb50162008-01-10 03:21:49 -080058 return -ENOMEM;
59
Stephen Hemminger7f9b8052008-01-14 23:14:20 -080060 main_table = fib_hash_table(RT_TABLE_MAIN);
Denis V. Lunev93456b62008-01-10 03:23:38 -080061 if (main_table == NULL)
Denis V. Lunevdbb50162008-01-10 03:21:49 -080062 goto fail;
63
Denis V. Lunev93456b62008-01-10 03:23:38 -080064 hlist_add_head_rcu(&local_table->tb_hlist,
Denis V. Luneve4aef8a2008-01-10 03:28:24 -080065 &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]);
Denis V. Lunev93456b62008-01-10 03:23:38 -080066 hlist_add_head_rcu(&main_table->tb_hlist,
Denis V. Luneve4aef8a2008-01-10 03:28:24 -080067 &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]);
Denis V. Lunevdbb50162008-01-10 03:21:49 -080068 return 0;
69
70fail:
Denis V. Lunev93456b62008-01-10 03:23:38 -080071 kfree(local_table);
Denis V. Lunevdbb50162008-01-10 03:21:49 -080072 return -ENOMEM;
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -080073}
Linus Torvalds1da177e2005-04-16 15:20:36 -070074#else
75
Denis V. Lunev8ad49422008-01-10 03:24:11 -080076struct fib_table *fib_new_table(struct net *net, u32 id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070079 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070081 if (id == 0)
82 id = RT_TABLE_MAIN;
Denis V. Lunev8ad49422008-01-10 03:24:11 -080083 tb = fib_get_table(net, id);
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070084 if (tb)
85 return tb;
Stephen Hemminger7f9b8052008-01-14 23:14:20 -080086
87 tb = fib_hash_table(id);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 if (!tb)
89 return NULL;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070090 h = id & (FIB_TABLE_HASHSZ - 1);
Denis V. Luneve4aef8a2008-01-10 03:28:24 -080091 hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 return tb;
93}
94
Denis V. Lunev8ad49422008-01-10 03:24:11 -080095struct fib_table *fib_get_table(struct net *net, u32 id)
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070096{
97 struct fib_table *tb;
98 struct hlist_node *node;
Denis V. Luneve4aef8a2008-01-10 03:28:24 -080099 struct hlist_head *head;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700100 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700102 if (id == 0)
103 id = RT_TABLE_MAIN;
104 h = id & (FIB_TABLE_HASHSZ - 1);
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800105
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700106 rcu_read_lock();
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800107 head = &net->ipv4.fib_table_hash[h];
108 hlist_for_each_entry_rcu(tb, node, head, tb_hlist) {
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700109 if (tb->tb_id == id) {
110 rcu_read_unlock();
111 return tb;
112 }
113 }
114 rcu_read_unlock();
115 return NULL;
116}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117#endif /* CONFIG_IP_MULTIPLE_TABLES */
118
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800119static void fib_flush(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120{
121 int flushed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700123 struct hlist_node *node;
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800124 struct hlist_head *head;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700125 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700127 for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800128 head = &net->ipv4.fib_table_hash[h];
129 hlist_for_each_entry(tb, node, head, tb_hlist)
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700130 flushed += tb->tb_flush(tb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 if (flushed)
134 rt_cache_flush(-1);
135}
136
137/*
138 * Find the first device with a given source address.
139 */
140
Al Viro60cad5d2006-09-26 22:17:09 -0700141struct net_device * ip_dev_find(__be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
143 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
144 struct fib_result res;
145 struct net_device *dev = NULL;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700146 struct fib_table *local_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
148#ifdef CONFIG_IP_MULTIPLE_TABLES
149 res.r = NULL;
150#endif
151
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800152 local_table = fib_get_table(&init_net, RT_TABLE_LOCAL);
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700153 if (!local_table || local_table->tb_lookup(local_table, &fl, &res))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 return NULL;
155 if (res.type != RTN_LOCAL)
156 goto out;
157 dev = FIB_RES_DEV(res);
158
159 if (dev)
160 dev_hold(dev);
161out:
162 fib_res_put(&res);
163 return dev;
164}
165
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800166/*
167 * Find address type as if only "dev" was present in the system. If
168 * on_dev is NULL then all interfaces are taken into consideration.
169 */
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800170static inline unsigned __inet_dev_addr_type(struct net *net,
171 const struct net_device *dev,
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800172 __be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173{
174 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
175 struct fib_result res;
176 unsigned ret = RTN_BROADCAST;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700177 struct fib_table *local_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
Jan Engelhardt1e637c72008-01-21 03:18:08 -0800179 if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 return RTN_BROADCAST;
Joe Perchesf97c1e02007-12-16 13:45:43 -0800181 if (ipv4_is_multicast(addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return RTN_MULTICAST;
183
184#ifdef CONFIG_IP_MULTIPLE_TABLES
185 res.r = NULL;
186#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900187
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800188 local_table = fib_get_table(net, RT_TABLE_LOCAL);
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700189 if (local_table) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 ret = RTN_UNICAST;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700191 if (!local_table->tb_lookup(local_table, &fl, &res)) {
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800192 if (!dev || dev == res.fi->fib_dev)
193 ret = res.type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 fib_res_put(&res);
195 }
196 }
197 return ret;
198}
199
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800200unsigned int inet_addr_type(struct net *net, __be32 addr)
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800201{
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800202 return __inet_dev_addr_type(net, NULL, addr);
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800203}
204
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800205unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
206 __be32 addr)
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800207{
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800208 return __inet_dev_addr_type(net, dev, addr);
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800209}
210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211/* Given (packet source, input interface) and optional (dst, oif, tos):
212 - (main) check, that source is valid i.e. not broadcast or our local
213 address.
214 - figure out what "logical" interface this packet arrived
215 and calculate "specific destination" address.
216 - check, that packet arrived from expected physical interface.
217 */
218
Al Virod9c9df82006-09-26 21:28:14 -0700219int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
220 struct net_device *dev, __be32 *spec_dst, u32 *itag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221{
222 struct in_device *in_dev;
223 struct flowi fl = { .nl_u = { .ip4_u =
224 { .daddr = src,
225 .saddr = dst,
226 .tos = tos } },
227 .iif = oif };
228 struct fib_result res;
229 int no_addr, rpf;
230 int ret;
Denis V. Lunev5b707aa2008-01-21 17:33:15 -0800231 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
233 no_addr = rpf = 0;
234 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700235 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 if (in_dev) {
237 no_addr = in_dev->ifa_list == NULL;
238 rpf = IN_DEV_RPFILTER(in_dev);
239 }
240 rcu_read_unlock();
241
242 if (in_dev == NULL)
243 goto e_inval;
244
Denis V. Lunev5b707aa2008-01-21 17:33:15 -0800245 net = dev->nd_net;
246 if (fib_lookup(net, &fl, &res))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 goto last_resort;
248 if (res.type != RTN_UNICAST)
249 goto e_inval_res;
250 *spec_dst = FIB_RES_PREFSRC(res);
251 fib_combine_itag(itag, &res);
252#ifdef CONFIG_IP_ROUTE_MULTIPATH
253 if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
254#else
255 if (FIB_RES_DEV(res) == dev)
256#endif
257 {
258 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
259 fib_res_put(&res);
260 return ret;
261 }
262 fib_res_put(&res);
263 if (no_addr)
264 goto last_resort;
265 if (rpf)
266 goto e_inval;
267 fl.oif = dev->ifindex;
268
269 ret = 0;
Denis V. Lunev5b707aa2008-01-21 17:33:15 -0800270 if (fib_lookup(net, &fl, &res) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 if (res.type == RTN_UNICAST) {
272 *spec_dst = FIB_RES_PREFSRC(res);
273 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
274 }
275 fib_res_put(&res);
276 }
277 return ret;
278
279last_resort:
280 if (rpf)
281 goto e_inval;
282 *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
283 *itag = 0;
284 return 0;
285
286e_inval_res:
287 fib_res_put(&res);
288e_inval:
289 return -EINVAL;
290}
291
Al Viro81f7bf62006-09-27 18:40:00 -0700292static inline __be32 sk_extract_addr(struct sockaddr *addr)
Thomas Graf4e902c52006-08-17 18:14:52 -0700293{
294 return ((struct sockaddr_in *) addr)->sin_addr.s_addr;
295}
296
297static int put_rtax(struct nlattr *mx, int len, int type, u32 value)
298{
299 struct nlattr *nla;
300
301 nla = (struct nlattr *) ((char *) mx + len);
302 nla->nla_type = type;
303 nla->nla_len = nla_attr_size(4);
304 *(u32 *) nla_data(nla) = value;
305
306 return len + nla_total_size(4);
307}
308
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800309static int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt,
Thomas Graf4e902c52006-08-17 18:14:52 -0700310 struct fib_config *cfg)
311{
Al Viro6d85c102006-09-26 22:15:46 -0700312 __be32 addr;
Thomas Graf4e902c52006-08-17 18:14:52 -0700313 int plen;
314
315 memset(cfg, 0, sizeof(*cfg));
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800316 cfg->fc_nlinfo.nl_net = net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700317
318 if (rt->rt_dst.sa_family != AF_INET)
319 return -EAFNOSUPPORT;
320
321 /*
322 * Check mask for validity:
323 * a) it must be contiguous.
324 * b) destination must have all host bits clear.
325 * c) if application forgot to set correct family (AF_INET),
326 * reject request unless it is absolutely clear i.e.
327 * both family and mask are zero.
328 */
329 plen = 32;
330 addr = sk_extract_addr(&rt->rt_dst);
331 if (!(rt->rt_flags & RTF_HOST)) {
Al Viro81f7bf62006-09-27 18:40:00 -0700332 __be32 mask = sk_extract_addr(&rt->rt_genmask);
Thomas Graf4e902c52006-08-17 18:14:52 -0700333
334 if (rt->rt_genmask.sa_family != AF_INET) {
335 if (mask || rt->rt_genmask.sa_family)
336 return -EAFNOSUPPORT;
337 }
338
339 if (bad_mask(mask, addr))
340 return -EINVAL;
341
342 plen = inet_mask_len(mask);
343 }
344
345 cfg->fc_dst_len = plen;
346 cfg->fc_dst = addr;
347
348 if (cmd != SIOCDELRT) {
349 cfg->fc_nlflags = NLM_F_CREATE;
350 cfg->fc_protocol = RTPROT_BOOT;
351 }
352
353 if (rt->rt_metric)
354 cfg->fc_priority = rt->rt_metric - 1;
355
356 if (rt->rt_flags & RTF_REJECT) {
357 cfg->fc_scope = RT_SCOPE_HOST;
358 cfg->fc_type = RTN_UNREACHABLE;
359 return 0;
360 }
361
362 cfg->fc_scope = RT_SCOPE_NOWHERE;
363 cfg->fc_type = RTN_UNICAST;
364
365 if (rt->rt_dev) {
366 char *colon;
367 struct net_device *dev;
368 char devname[IFNAMSIZ];
369
370 if (copy_from_user(devname, rt->rt_dev, IFNAMSIZ-1))
371 return -EFAULT;
372
373 devname[IFNAMSIZ-1] = 0;
374 colon = strchr(devname, ':');
375 if (colon)
376 *colon = 0;
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800377 dev = __dev_get_by_name(net, devname);
Thomas Graf4e902c52006-08-17 18:14:52 -0700378 if (!dev)
379 return -ENODEV;
380 cfg->fc_oif = dev->ifindex;
381 if (colon) {
382 struct in_ifaddr *ifa;
383 struct in_device *in_dev = __in_dev_get_rtnl(dev);
384 if (!in_dev)
385 return -ENODEV;
386 *colon = ':';
387 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
388 if (strcmp(ifa->ifa_label, devname) == 0)
389 break;
390 if (ifa == NULL)
391 return -ENODEV;
392 cfg->fc_prefsrc = ifa->ifa_local;
393 }
394 }
395
396 addr = sk_extract_addr(&rt->rt_gateway);
397 if (rt->rt_gateway.sa_family == AF_INET && addr) {
398 cfg->fc_gw = addr;
399 if (rt->rt_flags & RTF_GATEWAY &&
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800400 inet_addr_type(net, addr) == RTN_UNICAST)
Thomas Graf4e902c52006-08-17 18:14:52 -0700401 cfg->fc_scope = RT_SCOPE_UNIVERSE;
402 }
403
404 if (cmd == SIOCDELRT)
405 return 0;
406
407 if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw)
408 return -EINVAL;
409
410 if (cfg->fc_scope == RT_SCOPE_NOWHERE)
411 cfg->fc_scope = RT_SCOPE_LINK;
412
413 if (rt->rt_flags & (RTF_MTU | RTF_WINDOW | RTF_IRTT)) {
414 struct nlattr *mx;
415 int len = 0;
416
417 mx = kzalloc(3 * nla_total_size(4), GFP_KERNEL);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900418 if (mx == NULL)
Thomas Graf4e902c52006-08-17 18:14:52 -0700419 return -ENOMEM;
420
421 if (rt->rt_flags & RTF_MTU)
422 len = put_rtax(mx, len, RTAX_ADVMSS, rt->rt_mtu - 40);
423
424 if (rt->rt_flags & RTF_WINDOW)
425 len = put_rtax(mx, len, RTAX_WINDOW, rt->rt_window);
426
427 if (rt->rt_flags & RTF_IRTT)
428 len = put_rtax(mx, len, RTAX_RTT, rt->rt_irtt << 3);
429
430 cfg->fc_mx = mx;
431 cfg->fc_mx_len = len;
432 }
433
434 return 0;
435}
436
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437/*
438 * Handle IP routing ioctl calls. These are used to manipulate the routing tables
439 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900440
Denis V. Lunev1bad1182008-01-10 03:29:53 -0800441int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442{
Thomas Graf4e902c52006-08-17 18:14:52 -0700443 struct fib_config cfg;
444 struct rtentry rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
447 switch (cmd) {
448 case SIOCADDRT: /* Add a route */
449 case SIOCDELRT: /* Delete a route */
450 if (!capable(CAP_NET_ADMIN))
451 return -EPERM;
Thomas Graf4e902c52006-08-17 18:14:52 -0700452
453 if (copy_from_user(&rt, arg, sizeof(rt)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 return -EFAULT;
Thomas Graf4e902c52006-08-17 18:14:52 -0700455
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 rtnl_lock();
Denis V. Lunev1bad1182008-01-10 03:29:53 -0800457 err = rtentry_to_fib_config(net, cmd, &rt, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 if (err == 0) {
Thomas Graf4e902c52006-08-17 18:14:52 -0700459 struct fib_table *tb;
460
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 if (cmd == SIOCDELRT) {
Denis V. Lunev1bad1182008-01-10 03:29:53 -0800462 tb = fib_get_table(net, cfg.fc_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 if (tb)
Thomas Graf4e902c52006-08-17 18:14:52 -0700464 err = tb->tb_delete(tb, &cfg);
465 else
466 err = -ESRCH;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 } else {
Denis V. Lunev1bad1182008-01-10 03:29:53 -0800468 tb = fib_new_table(net, cfg.fc_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 if (tb)
Thomas Graf4e902c52006-08-17 18:14:52 -0700470 err = tb->tb_insert(tb, &cfg);
471 else
472 err = -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 }
Thomas Graf4e902c52006-08-17 18:14:52 -0700474
475 /* allocated by rtentry_to_fib_config() */
476 kfree(cfg.fc_mx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
478 rtnl_unlock();
479 return err;
480 }
481 return -EINVAL;
482}
483
Patrick McHardyef7c79e2007-06-05 12:38:30 -0700484const struct nla_policy rtm_ipv4_policy[RTA_MAX+1] = {
Thomas Graf4e902c52006-08-17 18:14:52 -0700485 [RTA_DST] = { .type = NLA_U32 },
486 [RTA_SRC] = { .type = NLA_U32 },
487 [RTA_IIF] = { .type = NLA_U32 },
488 [RTA_OIF] = { .type = NLA_U32 },
489 [RTA_GATEWAY] = { .type = NLA_U32 },
490 [RTA_PRIORITY] = { .type = NLA_U32 },
491 [RTA_PREFSRC] = { .type = NLA_U32 },
492 [RTA_METRICS] = { .type = NLA_NESTED },
Thomas Graf5176f912006-08-26 20:13:18 -0700493 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Thomas Graf4e902c52006-08-17 18:14:52 -0700494 [RTA_PROTOINFO] = { .type = NLA_U32 },
495 [RTA_FLOW] = { .type = NLA_U32 },
Thomas Graf4e902c52006-08-17 18:14:52 -0700496};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800498static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
499 struct nlmsghdr *nlh, struct fib_config *cfg)
Thomas Graf4e902c52006-08-17 18:14:52 -0700500{
501 struct nlattr *attr;
502 int err, remaining;
503 struct rtmsg *rtm;
504
505 err = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipv4_policy);
506 if (err < 0)
507 goto errout;
508
509 memset(cfg, 0, sizeof(*cfg));
510
511 rtm = nlmsg_data(nlh);
Thomas Graf4e902c52006-08-17 18:14:52 -0700512 cfg->fc_dst_len = rtm->rtm_dst_len;
Thomas Graf4e902c52006-08-17 18:14:52 -0700513 cfg->fc_tos = rtm->rtm_tos;
514 cfg->fc_table = rtm->rtm_table;
515 cfg->fc_protocol = rtm->rtm_protocol;
516 cfg->fc_scope = rtm->rtm_scope;
517 cfg->fc_type = rtm->rtm_type;
518 cfg->fc_flags = rtm->rtm_flags;
519 cfg->fc_nlflags = nlh->nlmsg_flags;
520
521 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
522 cfg->fc_nlinfo.nlh = nlh;
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800523 cfg->fc_nlinfo.nl_net = net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700524
Thomas Grafa0ee18b2007-03-24 20:32:54 -0700525 if (cfg->fc_type > RTN_MAX) {
526 err = -EINVAL;
527 goto errout;
528 }
529
Thomas Graf4e902c52006-08-17 18:14:52 -0700530 nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +0200531 switch (nla_type(attr)) {
Thomas Graf4e902c52006-08-17 18:14:52 -0700532 case RTA_DST:
Al Viro17fb2c62006-09-26 22:15:25 -0700533 cfg->fc_dst = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700534 break;
Thomas Graf4e902c52006-08-17 18:14:52 -0700535 case RTA_OIF:
536 cfg->fc_oif = nla_get_u32(attr);
537 break;
538 case RTA_GATEWAY:
Al Viro17fb2c62006-09-26 22:15:25 -0700539 cfg->fc_gw = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700540 break;
541 case RTA_PRIORITY:
542 cfg->fc_priority = nla_get_u32(attr);
543 break;
544 case RTA_PREFSRC:
Al Viro17fb2c62006-09-26 22:15:25 -0700545 cfg->fc_prefsrc = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700546 break;
547 case RTA_METRICS:
548 cfg->fc_mx = nla_data(attr);
549 cfg->fc_mx_len = nla_len(attr);
550 break;
551 case RTA_MULTIPATH:
552 cfg->fc_mp = nla_data(attr);
553 cfg->fc_mp_len = nla_len(attr);
554 break;
555 case RTA_FLOW:
556 cfg->fc_flow = nla_get_u32(attr);
557 break;
Thomas Graf4e902c52006-08-17 18:14:52 -0700558 case RTA_TABLE:
559 cfg->fc_table = nla_get_u32(attr);
560 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 }
562 }
Thomas Graf4e902c52006-08-17 18:14:52 -0700563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 return 0;
Thomas Graf4e902c52006-08-17 18:14:52 -0700565errout:
566 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567}
568
Thomas Graf63f34442007-03-22 11:55:17 -0700569static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100571 struct net *net = skb->sk->sk_net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700572 struct fib_config cfg;
573 struct fib_table *tb;
574 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800576 err = rtm_to_fib_config(net, skb, nlh, &cfg);
Thomas Graf4e902c52006-08-17 18:14:52 -0700577 if (err < 0)
578 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800580 tb = fib_get_table(net, cfg.fc_table);
Thomas Graf4e902c52006-08-17 18:14:52 -0700581 if (tb == NULL) {
582 err = -ESRCH;
583 goto errout;
584 }
585
586 err = tb->tb_delete(tb, &cfg);
587errout:
588 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590
Thomas Graf63f34442007-03-22 11:55:17 -0700591static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100593 struct net *net = skb->sk->sk_net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700594 struct fib_config cfg;
595 struct fib_table *tb;
596 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800598 err = rtm_to_fib_config(net, skb, nlh, &cfg);
Thomas Graf4e902c52006-08-17 18:14:52 -0700599 if (err < 0)
600 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
Denis V. Lunev226b0b4a52008-01-10 03:30:24 -0800602 tb = fib_new_table(net, cfg.fc_table);
Thomas Graf4e902c52006-08-17 18:14:52 -0700603 if (tb == NULL) {
604 err = -ENOBUFS;
605 goto errout;
606 }
607
608 err = tb->tb_insert(tb, &cfg);
609errout:
610 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
612
Thomas Graf63f34442007-03-22 11:55:17 -0700613static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100615 struct net *net = skb->sk->sk_net;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700616 unsigned int h, s_h;
617 unsigned int e = 0, s_e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700619 struct hlist_node *node;
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800620 struct hlist_head *head;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700621 int dumped = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
Thomas Grafbe403ea2006-08-17 18:15:17 -0700623 if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
624 ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 return ip_rt_dump(skb, cb);
626
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700627 s_h = cb->args[0];
628 s_e = cb->args[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700630 for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
631 e = 0;
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800632 head = &net->ipv4.fib_table_hash[h];
633 hlist_for_each_entry(tb, node, head, tb_hlist) {
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700634 if (e < s_e)
635 goto next;
636 if (dumped)
637 memset(&cb->args[2], 0, sizeof(cb->args) -
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900638 2 * sizeof(cb->args[0]));
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700639 if (tb->tb_dump(tb, skb, cb) < 0)
640 goto out;
641 dumped = 1;
642next:
643 e++;
644 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 }
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700646out:
647 cb->args[1] = e;
648 cb->args[0] = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
650 return skb->len;
651}
652
653/* Prepare and feed intra-kernel routing request.
654 Really, it should be netlink message, but :-( netlink
655 can be not configured, so that we feed it directly
656 to fib engine. It is legal, because all events occur
657 only when netlink is already locked.
658 */
659
Al Viro81f7bf62006-09-27 18:40:00 -0700660static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661{
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800662 struct net *net = ifa->ifa_dev->dev->nd_net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700663 struct fib_table *tb;
664 struct fib_config cfg = {
665 .fc_protocol = RTPROT_KERNEL,
666 .fc_type = type,
667 .fc_dst = dst,
668 .fc_dst_len = dst_len,
669 .fc_prefsrc = ifa->ifa_local,
670 .fc_oif = ifa->ifa_dev->dev->ifindex,
671 .fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800672 .fc_nlinfo = {
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800673 .nl_net = net,
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800674 },
Thomas Graf4e902c52006-08-17 18:14:52 -0700675 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
677 if (type == RTN_UNICAST)
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800678 tb = fib_new_table(net, RT_TABLE_MAIN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 else
Denis V. Lunev4b5d47d2008-01-10 03:29:23 -0800680 tb = fib_new_table(net, RT_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
682 if (tb == NULL)
683 return;
684
Thomas Graf4e902c52006-08-17 18:14:52 -0700685 cfg.fc_table = tb->tb_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Thomas Graf4e902c52006-08-17 18:14:52 -0700687 if (type != RTN_LOCAL)
688 cfg.fc_scope = RT_SCOPE_LINK;
689 else
690 cfg.fc_scope = RT_SCOPE_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
692 if (cmd == RTM_NEWROUTE)
Thomas Graf4e902c52006-08-17 18:14:52 -0700693 tb->tb_insert(tb, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 else
Thomas Graf4e902c52006-08-17 18:14:52 -0700695 tb->tb_delete(tb, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696}
697
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800698void fib_add_ifaddr(struct in_ifaddr *ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699{
700 struct in_device *in_dev = ifa->ifa_dev;
701 struct net_device *dev = in_dev->dev;
702 struct in_ifaddr *prim = ifa;
Al Viroa144ea42006-09-28 18:00:55 -0700703 __be32 mask = ifa->ifa_mask;
704 __be32 addr = ifa->ifa_local;
705 __be32 prefix = ifa->ifa_address&mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706
707 if (ifa->ifa_flags&IFA_F_SECONDARY) {
708 prim = inet_ifa_byprefix(in_dev, prefix, mask);
709 if (prim == NULL) {
Stephen Hemmingera6db9012008-01-12 20:58:35 -0800710 printk(KERN_WARNING "fib_add_ifaddr: bug: prim == NULL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 return;
712 }
713 }
714
715 fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
716
717 if (!(dev->flags&IFF_UP))
718 return;
719
720 /* Add broadcast address, if it is explicitly assigned. */
Al Viroa144ea42006-09-28 18:00:55 -0700721 if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
723
Joe Perchesf97c1e02007-12-16 13:45:43 -0800724 if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 (prefix != addr || ifa->ifa_prefixlen < 32)) {
726 fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
727 RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
728
729 /* Add network specific broadcasts, when it takes a sense */
730 if (ifa->ifa_prefixlen < 31) {
731 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
732 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
733 }
734 }
735}
736
737static void fib_del_ifaddr(struct in_ifaddr *ifa)
738{
739 struct in_device *in_dev = ifa->ifa_dev;
740 struct net_device *dev = in_dev->dev;
741 struct in_ifaddr *ifa1;
742 struct in_ifaddr *prim = ifa;
Al Viroa144ea42006-09-28 18:00:55 -0700743 __be32 brd = ifa->ifa_address|~ifa->ifa_mask;
744 __be32 any = ifa->ifa_address&ifa->ifa_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745#define LOCAL_OK 1
746#define BRD_OK 2
747#define BRD0_OK 4
748#define BRD1_OK 8
749 unsigned ok = 0;
750
751 if (!(ifa->ifa_flags&IFA_F_SECONDARY))
752 fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
753 RTN_UNICAST, any, ifa->ifa_prefixlen, prim);
754 else {
755 prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
756 if (prim == NULL) {
Stephen Hemmingera6db9012008-01-12 20:58:35 -0800757 printk(KERN_WARNING "fib_del_ifaddr: bug: prim == NULL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 return;
759 }
760 }
761
762 /* Deletion is more complicated than add.
763 We should take care of not to delete too much :-)
764
765 Scan address list to be sure that addresses are really gone.
766 */
767
768 for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) {
769 if (ifa->ifa_local == ifa1->ifa_local)
770 ok |= LOCAL_OK;
771 if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
772 ok |= BRD_OK;
773 if (brd == ifa1->ifa_broadcast)
774 ok |= BRD1_OK;
775 if (any == ifa1->ifa_broadcast)
776 ok |= BRD0_OK;
777 }
778
779 if (!(ok&BRD_OK))
780 fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
781 if (!(ok&BRD1_OK))
782 fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
783 if (!(ok&BRD0_OK))
784 fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
785 if (!(ok&LOCAL_OK)) {
786 fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
787
788 /* Check, that this local address finally disappeared. */
Denis V. Lunev226b0b4a52008-01-10 03:30:24 -0800789 if (inet_addr_type(dev->nd_net, ifa->ifa_local) != RTN_LOCAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 /* And the last, but not the least thing.
791 We must flush stray FIB entries.
792
793 First of all, we scan fib_info list searching
794 for stray nexthop entries, then ignite fib_flush.
795 */
796 if (fib_sync_down(ifa->ifa_local, NULL, 0))
Denis V. Lunev226b0b4a52008-01-10 03:30:24 -0800797 fib_flush(dev->nd_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 }
799 }
800#undef LOCAL_OK
801#undef BRD_OK
802#undef BRD0_OK
803#undef BRD1_OK
804}
805
Robert Olsson246955f2005-06-20 13:36:39 -0700806static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb )
807{
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900808
Robert Olsson246955f2005-06-20 13:36:39 -0700809 struct fib_result res;
Thomas Graf5f3008932006-11-09 15:21:41 -0800810 struct flowi fl = { .mark = frn->fl_mark,
Thomas Graf47dcf0c2006-11-09 15:20:38 -0800811 .nl_u = { .ip4_u = { .daddr = frn->fl_addr,
Robert Olsson246955f2005-06-20 13:36:39 -0700812 .tos = frn->fl_tos,
813 .scope = frn->fl_scope } } };
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700814
Sergey Vlasov912a41a2007-04-27 02:17:19 -0700815#ifdef CONFIG_IP_MULTIPLE_TABLES
816 res.r = NULL;
817#endif
818
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700819 frn->err = -ENOENT;
Robert Olsson246955f2005-06-20 13:36:39 -0700820 if (tb) {
821 local_bh_disable();
822
823 frn->tb_id = tb->tb_id;
824 frn->err = tb->tb_lookup(tb, &fl, &res);
825
826 if (!frn->err) {
827 frn->prefixlen = res.prefixlen;
828 frn->nh_sel = res.nh_sel;
829 frn->type = res.type;
830 frn->scope = res.scope;
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700831 fib_res_put(&res);
Robert Olsson246955f2005-06-20 13:36:39 -0700832 }
833 local_bh_enable();
834 }
835}
836
David S. Miller28f7b0362007-10-10 21:32:39 -0700837static void nl_fib_input(struct sk_buff *skb)
Robert Olsson246955f2005-06-20 13:36:39 -0700838{
Denis V. Lunev6bd48fc2008-01-10 03:28:55 -0800839 struct net *net;
Robert Olsson246955f2005-06-20 13:36:39 -0700840 struct fib_result_nl *frn;
David S. Miller28f7b0362007-10-10 21:32:39 -0700841 struct nlmsghdr *nlh;
Robert Olsson246955f2005-06-20 13:36:39 -0700842 struct fib_table *tb;
David S. Miller28f7b0362007-10-10 21:32:39 -0700843 u32 pid;
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700844
Denis V. Lunev6bd48fc2008-01-10 03:28:55 -0800845 net = skb->sk->sk_net;
Arnaldo Carvalho de Melob529ccf2007-04-25 19:08:35 -0700846 nlh = nlmsg_hdr(skb);
Thomas Grafea865752005-12-01 14:30:00 -0800847 if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len ||
Denis V. Lunevd883a032007-12-21 02:01:53 -0800848 nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*frn)))
Thomas Grafea865752005-12-01 14:30:00 -0800849 return;
Denis V. Lunevd883a032007-12-21 02:01:53 -0800850
851 skb = skb_clone(skb, GFP_KERNEL);
852 if (skb == NULL)
853 return;
854 nlh = nlmsg_hdr(skb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900855
Robert Olsson246955f2005-06-20 13:36:39 -0700856 frn = (struct fib_result_nl *) NLMSG_DATA(nlh);
Denis V. Lunev6bd48fc2008-01-10 03:28:55 -0800857 tb = fib_get_table(net, frn->tb_id_in);
Robert Olsson246955f2005-06-20 13:36:39 -0700858
859 nl_fib_lookup(frn, tb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900860
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700861 pid = NETLINK_CB(skb).pid; /* pid of sending process */
Robert Olsson246955f2005-06-20 13:36:39 -0700862 NETLINK_CB(skb).pid = 0; /* from kernel */
Patrick McHardyac6d4392005-08-14 19:29:52 -0700863 NETLINK_CB(skb).dst_group = 0; /* unicast */
Denis V. Lunev6bd48fc2008-01-10 03:28:55 -0800864 netlink_unicast(net->ipv4.fibnl, skb, pid, MSG_DONTWAIT);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900865}
Robert Olsson246955f2005-06-20 13:36:39 -0700866
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800867static int nl_fib_lookup_init(struct net *net)
Robert Olsson246955f2005-06-20 13:36:39 -0700868{
Denis V. Lunev6bd48fc2008-01-10 03:28:55 -0800869 struct sock *sk;
870 sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, 0,
871 nl_fib_input, NULL, THIS_MODULE);
872 if (sk == NULL)
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800873 return -EAFNOSUPPORT;
Denis V. Lunev6bd48fc2008-01-10 03:28:55 -0800874 net->ipv4.fibnl = sk;
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800875 return 0;
876}
877
878static void nl_fib_lookup_exit(struct net *net)
879{
Denis V. Lunevb7c6ba62008-01-28 14:41:19 -0800880 netlink_kernel_release(net->ipv4.fibnl);
Denis V. Lunev775516b2008-01-18 23:55:19 -0800881 net->ipv4.fibnl = NULL;
Robert Olsson246955f2005-06-20 13:36:39 -0700882}
883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884static void fib_disable_ip(struct net_device *dev, int force)
885{
886 if (fib_sync_down(0, dev, force))
Denis V. Lunev226b0b4a52008-01-10 03:30:24 -0800887 fib_flush(dev->nd_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 rt_cache_flush(0);
889 arp_ifdown(dev);
890}
891
892static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
893{
894 struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
895
896 switch (event) {
897 case NETDEV_UP:
898 fib_add_ifaddr(ifa);
899#ifdef CONFIG_IP_ROUTE_MULTIPATH
900 fib_sync_up(ifa->ifa_dev->dev);
901#endif
902 rt_cache_flush(-1);
903 break;
904 case NETDEV_DOWN:
905 fib_del_ifaddr(ifa);
Jayachandran C9fcc2e82005-10-27 15:10:01 -0700906 if (ifa->ifa_dev->ifa_list == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 /* Last address was deleted from this interface.
908 Disable IP.
909 */
910 fib_disable_ip(ifa->ifa_dev->dev, 1);
911 } else {
912 rt_cache_flush(-1);
913 }
914 break;
915 }
916 return NOTIFY_DONE;
917}
918
919static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
920{
921 struct net_device *dev = ptr;
Herbert Xue5ed6392005-10-03 14:35:55 -0700922 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
924 if (event == NETDEV_UNREGISTER) {
925 fib_disable_ip(dev, 2);
926 return NOTIFY_DONE;
927 }
928
929 if (!in_dev)
930 return NOTIFY_DONE;
931
932 switch (event) {
933 case NETDEV_UP:
934 for_ifa(in_dev) {
935 fib_add_ifaddr(ifa);
936 } endfor_ifa(in_dev);
937#ifdef CONFIG_IP_ROUTE_MULTIPATH
938 fib_sync_up(dev);
939#endif
940 rt_cache_flush(-1);
941 break;
942 case NETDEV_DOWN:
943 fib_disable_ip(dev, 0);
944 break;
945 case NETDEV_CHANGEMTU:
946 case NETDEV_CHANGE:
947 rt_cache_flush(0);
948 break;
949 }
950 return NOTIFY_DONE;
951}
952
953static struct notifier_block fib_inetaddr_notifier = {
954 .notifier_call =fib_inetaddr_event,
955};
956
957static struct notifier_block fib_netdev_notifier = {
958 .notifier_call =fib_netdev_event,
959};
960
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800961static int __net_init ip_fib_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962{
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700963 unsigned int i;
964
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800965 net->ipv4.fib_table_hash = kzalloc(
966 sizeof(struct hlist_head)*FIB_TABLE_HASHSZ, GFP_KERNEL);
967 if (net->ipv4.fib_table_hash == NULL)
968 return -ENOMEM;
969
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700970 for (i = 0; i < FIB_TABLE_HASHSZ; i++)
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800971 INIT_HLIST_HEAD(&net->ipv4.fib_table_hash[i]);
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -0800972
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800973 return fib4_rules_init(net);
974}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800976static void __net_exit ip_fib_net_exit(struct net *net)
977{
978 unsigned int i;
Thomas Graf63f34442007-03-22 11:55:17 -0700979
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800980#ifdef CONFIG_IP_MULTIPLE_TABLES
981 fib4_rules_exit(net);
982#endif
983
984 for (i = 0; i < FIB_TABLE_HASHSZ; i++) {
985 struct fib_table *tb;
986 struct hlist_head *head;
987 struct hlist_node *node, *tmp;
988
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800989 head = &net->ipv4.fib_table_hash[i];
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800990 hlist_for_each_entry_safe(tb, node, tmp, head, tb_hlist) {
991 hlist_del(node);
992 tb->tb_flush(tb);
993 kfree(tb);
994 }
995 }
Denis V. Luneve4aef8a2008-01-10 03:28:24 -0800996 kfree(net->ipv4.fib_table_hash);
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800997}
998
999static int __net_init fib_net_init(struct net *net)
1000{
1001 int error;
1002
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -08001003 error = ip_fib_net_init(net);
1004 if (error < 0)
1005 goto out;
1006 error = nl_fib_lookup_init(net);
1007 if (error < 0)
1008 goto out_nlfl;
1009 error = fib_proc_init(net);
1010 if (error < 0)
1011 goto out_proc;
1012out:
1013 return error;
1014
1015out_proc:
1016 nl_fib_lookup_exit(net);
1017out_nlfl:
1018 ip_fib_net_exit(net);
1019 goto out;
1020}
1021
1022static void __net_exit fib_net_exit(struct net *net)
1023{
1024 fib_proc_exit(net);
1025 nl_fib_lookup_exit(net);
1026 ip_fib_net_exit(net);
1027}
1028
1029static struct pernet_operations fib_net_ops = {
1030 .init = fib_net_init,
1031 .exit = fib_net_exit,
1032};
1033
1034void __init ip_fib_init(void)
1035{
Thomas Graf63f34442007-03-22 11:55:17 -07001036 rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
1037 rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL);
1038 rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -08001039
1040 register_pernet_subsys(&fib_net_ops);
1041 register_netdevice_notifier(&fib_netdev_notifier);
1042 register_inetaddr_notifier(&fib_inetaddr_notifier);
Stephen Hemminger7f9b8052008-01-14 23:14:20 -08001043
1044 fib_hash_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045}
1046
1047EXPORT_SYMBOL(inet_addr_type);
Laszlo Attila Toth05538112007-12-04 23:28:46 -08001048EXPORT_SYMBOL(inet_dev_addr_type);
Sean Heftya1e87332006-06-17 20:37:28 -07001049EXPORT_SYMBOL(ip_dev_find);