blob: d1a45cb6f6b0cb997ed772219e79adb1df6ade9e [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
50#define FFprint(a...) printk(KERN_DEBUG a)
51
David S. Miller28f7b0362007-10-10 21:32:39 -070052static struct sock *fibnl;
Denis V. Lunev93456b62008-01-10 03:23:38 -080053struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
David S. Miller28f7b0362007-10-10 21:32:39 -070054
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#ifndef CONFIG_IP_MULTIPLE_TABLES
56
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -080057static int __net_init fib4_rules_init(struct net *net)
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -080058{
Denis V. Lunev93456b62008-01-10 03:23:38 -080059 struct fib_table *local_table, *main_table;
60
61 local_table = fib_hash_init(RT_TABLE_LOCAL);
62 if (local_table == NULL)
Denis V. Lunevdbb50162008-01-10 03:21:49 -080063 return -ENOMEM;
64
Denis V. Lunev93456b62008-01-10 03:23:38 -080065 main_table = fib_hash_init(RT_TABLE_MAIN);
66 if (main_table == NULL)
Denis V. Lunevdbb50162008-01-10 03:21:49 -080067 goto fail;
68
Denis V. Lunev93456b62008-01-10 03:23:38 -080069 hlist_add_head_rcu(&local_table->tb_hlist,
70 &fib_table_hash[TABLE_LOCAL_INDEX]);
71 hlist_add_head_rcu(&main_table->tb_hlist,
72 &fib_table_hash[TABLE_MAIN_INDEX]);
Denis V. Lunevdbb50162008-01-10 03:21:49 -080073 return 0;
74
75fail:
Denis V. Lunev93456b62008-01-10 03:23:38 -080076 kfree(local_table);
Denis V. Lunevdbb50162008-01-10 03:21:49 -080077 return -ENOMEM;
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -080078}
Linus Torvalds1da177e2005-04-16 15:20:36 -070079#else
80
Denis V. Lunev8ad49422008-01-10 03:24:11 -080081struct fib_table *fib_new_table(struct net *net, u32 id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
83 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070084 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070086 if (id == 0)
87 id = RT_TABLE_MAIN;
Denis V. Lunev8ad49422008-01-10 03:24:11 -080088 tb = fib_get_table(net, id);
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070089 if (tb)
90 return tb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 tb = fib_hash_init(id);
92 if (!tb)
93 return NULL;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070094 h = id & (FIB_TABLE_HASHSZ - 1);
95 hlist_add_head_rcu(&tb->tb_hlist, &fib_table_hash[h]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 return tb;
97}
98
Denis V. Lunev8ad49422008-01-10 03:24:11 -080099struct fib_table *fib_get_table(struct net *net, u32 id)
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700100{
101 struct fib_table *tb;
102 struct hlist_node *node;
103 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700105 if (id == 0)
106 id = RT_TABLE_MAIN;
107 h = id & (FIB_TABLE_HASHSZ - 1);
108 rcu_read_lock();
109 hlist_for_each_entry_rcu(tb, node, &fib_table_hash[h], tb_hlist) {
110 if (tb->tb_id == id) {
111 rcu_read_unlock();
112 return tb;
113 }
114 }
115 rcu_read_unlock();
116 return NULL;
117}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118#endif /* CONFIG_IP_MULTIPLE_TABLES */
119
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120static void fib_flush(void)
121{
122 int flushed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700124 struct hlist_node *node;
125 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++) {
128 hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist)
129 flushed += tb->tb_flush(tb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132 if (flushed)
133 rt_cache_flush(-1);
134}
135
136/*
137 * Find the first device with a given source address.
138 */
139
Al Viro60cad5d2006-09-26 22:17:09 -0700140struct net_device * ip_dev_find(__be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141{
142 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
143 struct fib_result res;
144 struct net_device *dev = NULL;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700145 struct fib_table *local_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
147#ifdef CONFIG_IP_MULTIPLE_TABLES
148 res.r = NULL;
149#endif
150
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800151 local_table = fib_get_table(&init_net, RT_TABLE_LOCAL);
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700152 if (!local_table || local_table->tb_lookup(local_table, &fl, &res))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 return NULL;
154 if (res.type != RTN_LOCAL)
155 goto out;
156 dev = FIB_RES_DEV(res);
157
158 if (dev)
159 dev_hold(dev);
160out:
161 fib_res_put(&res);
162 return dev;
163}
164
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800165/*
166 * Find address type as if only "dev" was present in the system. If
167 * on_dev is NULL then all interfaces are taken into consideration.
168 */
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800169static inline unsigned __inet_dev_addr_type(struct net *net,
170 const struct net_device *dev,
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800171 __be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
173 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
174 struct fib_result res;
175 unsigned ret = RTN_BROADCAST;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700176 struct fib_table *local_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Joe Perchesf97c1e02007-12-16 13:45:43 -0800178 if (ipv4_is_zeronet(addr) || ipv4_is_badclass(addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 return RTN_BROADCAST;
Joe Perchesf97c1e02007-12-16 13:45:43 -0800180 if (ipv4_is_multicast(addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return RTN_MULTICAST;
182
183#ifdef CONFIG_IP_MULTIPLE_TABLES
184 res.r = NULL;
185#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900186
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800187 local_table = fib_get_table(net, RT_TABLE_LOCAL);
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700188 if (local_table) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 ret = RTN_UNICAST;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700190 if (!local_table->tb_lookup(local_table, &fl, &res)) {
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800191 if (!dev || dev == res.fi->fib_dev)
192 ret = res.type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 fib_res_put(&res);
194 }
195 }
196 return ret;
197}
198
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800199unsigned int inet_addr_type(struct net *net, __be32 addr)
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800200{
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800201 return __inet_dev_addr_type(net, NULL, addr);
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800202}
203
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800204unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
205 __be32 addr)
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800206{
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800207 return __inet_dev_addr_type(net, dev, addr);
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800208}
209
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210/* Given (packet source, input interface) and optional (dst, oif, tos):
211 - (main) check, that source is valid i.e. not broadcast or our local
212 address.
213 - figure out what "logical" interface this packet arrived
214 and calculate "specific destination" address.
215 - check, that packet arrived from expected physical interface.
216 */
217
Al Virod9c9df82006-09-26 21:28:14 -0700218int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
219 struct net_device *dev, __be32 *spec_dst, u32 *itag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220{
221 struct in_device *in_dev;
222 struct flowi fl = { .nl_u = { .ip4_u =
223 { .daddr = src,
224 .saddr = dst,
225 .tos = tos } },
226 .iif = oif };
227 struct fib_result res;
228 int no_addr, rpf;
229 int ret;
230
231 no_addr = rpf = 0;
232 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700233 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 if (in_dev) {
235 no_addr = in_dev->ifa_list == NULL;
236 rpf = IN_DEV_RPFILTER(in_dev);
237 }
238 rcu_read_unlock();
239
240 if (in_dev == NULL)
241 goto e_inval;
242
243 if (fib_lookup(&fl, &res))
244 goto last_resort;
245 if (res.type != RTN_UNICAST)
246 goto e_inval_res;
247 *spec_dst = FIB_RES_PREFSRC(res);
248 fib_combine_itag(itag, &res);
249#ifdef CONFIG_IP_ROUTE_MULTIPATH
250 if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
251#else
252 if (FIB_RES_DEV(res) == dev)
253#endif
254 {
255 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
256 fib_res_put(&res);
257 return ret;
258 }
259 fib_res_put(&res);
260 if (no_addr)
261 goto last_resort;
262 if (rpf)
263 goto e_inval;
264 fl.oif = dev->ifindex;
265
266 ret = 0;
267 if (fib_lookup(&fl, &res) == 0) {
268 if (res.type == RTN_UNICAST) {
269 *spec_dst = FIB_RES_PREFSRC(res);
270 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
271 }
272 fib_res_put(&res);
273 }
274 return ret;
275
276last_resort:
277 if (rpf)
278 goto e_inval;
279 *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
280 *itag = 0;
281 return 0;
282
283e_inval_res:
284 fib_res_put(&res);
285e_inval:
286 return -EINVAL;
287}
288
Al Viro81f7bf62006-09-27 18:40:00 -0700289static inline __be32 sk_extract_addr(struct sockaddr *addr)
Thomas Graf4e902c52006-08-17 18:14:52 -0700290{
291 return ((struct sockaddr_in *) addr)->sin_addr.s_addr;
292}
293
294static int put_rtax(struct nlattr *mx, int len, int type, u32 value)
295{
296 struct nlattr *nla;
297
298 nla = (struct nlattr *) ((char *) mx + len);
299 nla->nla_type = type;
300 nla->nla_len = nla_attr_size(4);
301 *(u32 *) nla_data(nla) = value;
302
303 return len + nla_total_size(4);
304}
305
306static int rtentry_to_fib_config(int cmd, struct rtentry *rt,
307 struct fib_config *cfg)
308{
Al Viro6d85c102006-09-26 22:15:46 -0700309 __be32 addr;
Thomas Graf4e902c52006-08-17 18:14:52 -0700310 int plen;
311
312 memset(cfg, 0, sizeof(*cfg));
313
314 if (rt->rt_dst.sa_family != AF_INET)
315 return -EAFNOSUPPORT;
316
317 /*
318 * Check mask for validity:
319 * a) it must be contiguous.
320 * b) destination must have all host bits clear.
321 * c) if application forgot to set correct family (AF_INET),
322 * reject request unless it is absolutely clear i.e.
323 * both family and mask are zero.
324 */
325 plen = 32;
326 addr = sk_extract_addr(&rt->rt_dst);
327 if (!(rt->rt_flags & RTF_HOST)) {
Al Viro81f7bf62006-09-27 18:40:00 -0700328 __be32 mask = sk_extract_addr(&rt->rt_genmask);
Thomas Graf4e902c52006-08-17 18:14:52 -0700329
330 if (rt->rt_genmask.sa_family != AF_INET) {
331 if (mask || rt->rt_genmask.sa_family)
332 return -EAFNOSUPPORT;
333 }
334
335 if (bad_mask(mask, addr))
336 return -EINVAL;
337
338 plen = inet_mask_len(mask);
339 }
340
341 cfg->fc_dst_len = plen;
342 cfg->fc_dst = addr;
343
344 if (cmd != SIOCDELRT) {
345 cfg->fc_nlflags = NLM_F_CREATE;
346 cfg->fc_protocol = RTPROT_BOOT;
347 }
348
349 if (rt->rt_metric)
350 cfg->fc_priority = rt->rt_metric - 1;
351
352 if (rt->rt_flags & RTF_REJECT) {
353 cfg->fc_scope = RT_SCOPE_HOST;
354 cfg->fc_type = RTN_UNREACHABLE;
355 return 0;
356 }
357
358 cfg->fc_scope = RT_SCOPE_NOWHERE;
359 cfg->fc_type = RTN_UNICAST;
360
361 if (rt->rt_dev) {
362 char *colon;
363 struct net_device *dev;
364 char devname[IFNAMSIZ];
365
366 if (copy_from_user(devname, rt->rt_dev, IFNAMSIZ-1))
367 return -EFAULT;
368
369 devname[IFNAMSIZ-1] = 0;
370 colon = strchr(devname, ':');
371 if (colon)
372 *colon = 0;
Eric W. Biederman881d9662007-09-17 11:56:21 -0700373 dev = __dev_get_by_name(&init_net, devname);
Thomas Graf4e902c52006-08-17 18:14:52 -0700374 if (!dev)
375 return -ENODEV;
376 cfg->fc_oif = dev->ifindex;
377 if (colon) {
378 struct in_ifaddr *ifa;
379 struct in_device *in_dev = __in_dev_get_rtnl(dev);
380 if (!in_dev)
381 return -ENODEV;
382 *colon = ':';
383 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
384 if (strcmp(ifa->ifa_label, devname) == 0)
385 break;
386 if (ifa == NULL)
387 return -ENODEV;
388 cfg->fc_prefsrc = ifa->ifa_local;
389 }
390 }
391
392 addr = sk_extract_addr(&rt->rt_gateway);
393 if (rt->rt_gateway.sa_family == AF_INET && addr) {
394 cfg->fc_gw = addr;
395 if (rt->rt_flags & RTF_GATEWAY &&
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800396 inet_addr_type(&init_net, addr) == RTN_UNICAST)
Thomas Graf4e902c52006-08-17 18:14:52 -0700397 cfg->fc_scope = RT_SCOPE_UNIVERSE;
398 }
399
400 if (cmd == SIOCDELRT)
401 return 0;
402
403 if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw)
404 return -EINVAL;
405
406 if (cfg->fc_scope == RT_SCOPE_NOWHERE)
407 cfg->fc_scope = RT_SCOPE_LINK;
408
409 if (rt->rt_flags & (RTF_MTU | RTF_WINDOW | RTF_IRTT)) {
410 struct nlattr *mx;
411 int len = 0;
412
413 mx = kzalloc(3 * nla_total_size(4), GFP_KERNEL);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900414 if (mx == NULL)
Thomas Graf4e902c52006-08-17 18:14:52 -0700415 return -ENOMEM;
416
417 if (rt->rt_flags & RTF_MTU)
418 len = put_rtax(mx, len, RTAX_ADVMSS, rt->rt_mtu - 40);
419
420 if (rt->rt_flags & RTF_WINDOW)
421 len = put_rtax(mx, len, RTAX_WINDOW, rt->rt_window);
422
423 if (rt->rt_flags & RTF_IRTT)
424 len = put_rtax(mx, len, RTAX_RTT, rt->rt_irtt << 3);
425
426 cfg->fc_mx = mx;
427 cfg->fc_mx_len = len;
428 }
429
430 return 0;
431}
432
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433/*
434 * Handle IP routing ioctl calls. These are used to manipulate the routing tables
435 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900436
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437int ip_rt_ioctl(unsigned int cmd, void __user *arg)
438{
Thomas Graf4e902c52006-08-17 18:14:52 -0700439 struct fib_config cfg;
440 struct rtentry rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
443 switch (cmd) {
444 case SIOCADDRT: /* Add a route */
445 case SIOCDELRT: /* Delete a route */
446 if (!capable(CAP_NET_ADMIN))
447 return -EPERM;
Thomas Graf4e902c52006-08-17 18:14:52 -0700448
449 if (copy_from_user(&rt, arg, sizeof(rt)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 return -EFAULT;
Thomas Graf4e902c52006-08-17 18:14:52 -0700451
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 rtnl_lock();
Thomas Graf4e902c52006-08-17 18:14:52 -0700453 err = rtentry_to_fib_config(cmd, &rt, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 if (err == 0) {
Thomas Graf4e902c52006-08-17 18:14:52 -0700455 struct fib_table *tb;
456
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 if (cmd == SIOCDELRT) {
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800458 tb = fib_get_table(&init_net, cfg.fc_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 if (tb)
Thomas Graf4e902c52006-08-17 18:14:52 -0700460 err = tb->tb_delete(tb, &cfg);
461 else
462 err = -ESRCH;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 } else {
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800464 tb = fib_new_table(&init_net, cfg.fc_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 if (tb)
Thomas Graf4e902c52006-08-17 18:14:52 -0700466 err = tb->tb_insert(tb, &cfg);
467 else
468 err = -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
Thomas Graf4e902c52006-08-17 18:14:52 -0700470
471 /* allocated by rtentry_to_fib_config() */
472 kfree(cfg.fc_mx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 }
474 rtnl_unlock();
475 return err;
476 }
477 return -EINVAL;
478}
479
Patrick McHardyef7c79e2007-06-05 12:38:30 -0700480const struct nla_policy rtm_ipv4_policy[RTA_MAX+1] = {
Thomas Graf4e902c52006-08-17 18:14:52 -0700481 [RTA_DST] = { .type = NLA_U32 },
482 [RTA_SRC] = { .type = NLA_U32 },
483 [RTA_IIF] = { .type = NLA_U32 },
484 [RTA_OIF] = { .type = NLA_U32 },
485 [RTA_GATEWAY] = { .type = NLA_U32 },
486 [RTA_PRIORITY] = { .type = NLA_U32 },
487 [RTA_PREFSRC] = { .type = NLA_U32 },
488 [RTA_METRICS] = { .type = NLA_NESTED },
Thomas Graf5176f912006-08-26 20:13:18 -0700489 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Thomas Graf4e902c52006-08-17 18:14:52 -0700490 [RTA_PROTOINFO] = { .type = NLA_U32 },
491 [RTA_FLOW] = { .type = NLA_U32 },
Thomas Graf4e902c52006-08-17 18:14:52 -0700492};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Thomas Graf4e902c52006-08-17 18:14:52 -0700494static int rtm_to_fib_config(struct sk_buff *skb, struct nlmsghdr *nlh,
495 struct fib_config *cfg)
496{
497 struct nlattr *attr;
498 int err, remaining;
499 struct rtmsg *rtm;
500
501 err = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipv4_policy);
502 if (err < 0)
503 goto errout;
504
505 memset(cfg, 0, sizeof(*cfg));
506
507 rtm = nlmsg_data(nlh);
Thomas Graf4e902c52006-08-17 18:14:52 -0700508 cfg->fc_dst_len = rtm->rtm_dst_len;
Thomas Graf4e902c52006-08-17 18:14:52 -0700509 cfg->fc_tos = rtm->rtm_tos;
510 cfg->fc_table = rtm->rtm_table;
511 cfg->fc_protocol = rtm->rtm_protocol;
512 cfg->fc_scope = rtm->rtm_scope;
513 cfg->fc_type = rtm->rtm_type;
514 cfg->fc_flags = rtm->rtm_flags;
515 cfg->fc_nlflags = nlh->nlmsg_flags;
516
517 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
518 cfg->fc_nlinfo.nlh = nlh;
519
Thomas Grafa0ee18b2007-03-24 20:32:54 -0700520 if (cfg->fc_type > RTN_MAX) {
521 err = -EINVAL;
522 goto errout;
523 }
524
Thomas Graf4e902c52006-08-17 18:14:52 -0700525 nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +0200526 switch (nla_type(attr)) {
Thomas Graf4e902c52006-08-17 18:14:52 -0700527 case RTA_DST:
Al Viro17fb2c62006-09-26 22:15:25 -0700528 cfg->fc_dst = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700529 break;
Thomas Graf4e902c52006-08-17 18:14:52 -0700530 case RTA_OIF:
531 cfg->fc_oif = nla_get_u32(attr);
532 break;
533 case RTA_GATEWAY:
Al Viro17fb2c62006-09-26 22:15:25 -0700534 cfg->fc_gw = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700535 break;
536 case RTA_PRIORITY:
537 cfg->fc_priority = nla_get_u32(attr);
538 break;
539 case RTA_PREFSRC:
Al Viro17fb2c62006-09-26 22:15:25 -0700540 cfg->fc_prefsrc = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700541 break;
542 case RTA_METRICS:
543 cfg->fc_mx = nla_data(attr);
544 cfg->fc_mx_len = nla_len(attr);
545 break;
546 case RTA_MULTIPATH:
547 cfg->fc_mp = nla_data(attr);
548 cfg->fc_mp_len = nla_len(attr);
549 break;
550 case RTA_FLOW:
551 cfg->fc_flow = nla_get_u32(attr);
552 break;
Thomas Graf4e902c52006-08-17 18:14:52 -0700553 case RTA_TABLE:
554 cfg->fc_table = nla_get_u32(attr);
555 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 }
557 }
Thomas Graf4e902c52006-08-17 18:14:52 -0700558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 return 0;
Thomas Graf4e902c52006-08-17 18:14:52 -0700560errout:
561 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562}
563
Thomas Graf63f34442007-03-22 11:55:17 -0700564static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100566 struct net *net = skb->sk->sk_net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700567 struct fib_config cfg;
568 struct fib_table *tb;
569 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Denis V. Lunevb8542722007-12-01 00:21:31 +1100571 if (net != &init_net)
572 return -EINVAL;
573
Thomas Graf4e902c52006-08-17 18:14:52 -0700574 err = rtm_to_fib_config(skb, nlh, &cfg);
575 if (err < 0)
576 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800578 tb = fib_get_table(net, cfg.fc_table);
Thomas Graf4e902c52006-08-17 18:14:52 -0700579 if (tb == NULL) {
580 err = -ESRCH;
581 goto errout;
582 }
583
584 err = tb->tb_delete(tb, &cfg);
585errout:
586 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587}
588
Thomas Graf63f34442007-03-22 11:55:17 -0700589static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100591 struct net *net = skb->sk->sk_net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700592 struct fib_config cfg;
593 struct fib_table *tb;
594 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Denis V. Lunevb8542722007-12-01 00:21:31 +1100596 if (net != &init_net)
597 return -EINVAL;
598
Thomas Graf4e902c52006-08-17 18:14:52 -0700599 err = rtm_to_fib_config(skb, nlh, &cfg);
600 if (err < 0)
601 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800603 tb = fib_new_table(&init_net, cfg.fc_table);
Thomas Graf4e902c52006-08-17 18:14:52 -0700604 if (tb == NULL) {
605 err = -ENOBUFS;
606 goto errout;
607 }
608
609 err = tb->tb_insert(tb, &cfg);
610errout:
611 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612}
613
Thomas Graf63f34442007-03-22 11:55:17 -0700614static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100616 struct net *net = skb->sk->sk_net;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700617 unsigned int h, s_h;
618 unsigned int e = 0, s_e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700620 struct hlist_node *node;
621 int dumped = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
Denis V. Lunevb8542722007-12-01 00:21:31 +1100623 if (net != &init_net)
624 return 0;
625
Thomas Grafbe403ea2006-08-17 18:15:17 -0700626 if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
627 ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 return ip_rt_dump(skb, cb);
629
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700630 s_h = cb->args[0];
631 s_e = cb->args[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700633 for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
634 e = 0;
635 hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist) {
636 if (e < s_e)
637 goto next;
638 if (dumped)
639 memset(&cb->args[2], 0, sizeof(cb->args) -
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900640 2 * sizeof(cb->args[0]));
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700641 if (tb->tb_dump(tb, skb, cb) < 0)
642 goto out;
643 dumped = 1;
644next:
645 e++;
646 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 }
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700648out:
649 cb->args[1] = e;
650 cb->args[0] = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
652 return skb->len;
653}
654
655/* Prepare and feed intra-kernel routing request.
656 Really, it should be netlink message, but :-( netlink
657 can be not configured, so that we feed it directly
658 to fib engine. It is legal, because all events occur
659 only when netlink is already locked.
660 */
661
Al Viro81f7bf62006-09-27 18:40:00 -0700662static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663{
Thomas Graf4e902c52006-08-17 18:14:52 -0700664 struct fib_table *tb;
665 struct fib_config cfg = {
666 .fc_protocol = RTPROT_KERNEL,
667 .fc_type = type,
668 .fc_dst = dst,
669 .fc_dst_len = dst_len,
670 .fc_prefsrc = ifa->ifa_local,
671 .fc_oif = ifa->ifa_dev->dev->ifindex,
672 .fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
673 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675 if (type == RTN_UNICAST)
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800676 tb = fib_new_table(&init_net, RT_TABLE_MAIN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 else
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800678 tb = fib_new_table(&init_net, RT_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
680 if (tb == NULL)
681 return;
682
Thomas Graf4e902c52006-08-17 18:14:52 -0700683 cfg.fc_table = tb->tb_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
Thomas Graf4e902c52006-08-17 18:14:52 -0700685 if (type != RTN_LOCAL)
686 cfg.fc_scope = RT_SCOPE_LINK;
687 else
688 cfg.fc_scope = RT_SCOPE_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
690 if (cmd == RTM_NEWROUTE)
Thomas Graf4e902c52006-08-17 18:14:52 -0700691 tb->tb_insert(tb, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 else
Thomas Graf4e902c52006-08-17 18:14:52 -0700693 tb->tb_delete(tb, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694}
695
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800696void fib_add_ifaddr(struct in_ifaddr *ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697{
698 struct in_device *in_dev = ifa->ifa_dev;
699 struct net_device *dev = in_dev->dev;
700 struct in_ifaddr *prim = ifa;
Al Viroa144ea42006-09-28 18:00:55 -0700701 __be32 mask = ifa->ifa_mask;
702 __be32 addr = ifa->ifa_local;
703 __be32 prefix = ifa->ifa_address&mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705 if (ifa->ifa_flags&IFA_F_SECONDARY) {
706 prim = inet_ifa_byprefix(in_dev, prefix, mask);
707 if (prim == NULL) {
708 printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL\n");
709 return;
710 }
711 }
712
713 fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
714
715 if (!(dev->flags&IFF_UP))
716 return;
717
718 /* Add broadcast address, if it is explicitly assigned. */
Al Viroa144ea42006-09-28 18:00:55 -0700719 if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
721
Joe Perchesf97c1e02007-12-16 13:45:43 -0800722 if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 (prefix != addr || ifa->ifa_prefixlen < 32)) {
724 fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
725 RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
726
727 /* Add network specific broadcasts, when it takes a sense */
728 if (ifa->ifa_prefixlen < 31) {
729 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
730 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
731 }
732 }
733}
734
735static void fib_del_ifaddr(struct in_ifaddr *ifa)
736{
737 struct in_device *in_dev = ifa->ifa_dev;
738 struct net_device *dev = in_dev->dev;
739 struct in_ifaddr *ifa1;
740 struct in_ifaddr *prim = ifa;
Al Viroa144ea42006-09-28 18:00:55 -0700741 __be32 brd = ifa->ifa_address|~ifa->ifa_mask;
742 __be32 any = ifa->ifa_address&ifa->ifa_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743#define LOCAL_OK 1
744#define BRD_OK 2
745#define BRD0_OK 4
746#define BRD1_OK 8
747 unsigned ok = 0;
748
749 if (!(ifa->ifa_flags&IFA_F_SECONDARY))
750 fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
751 RTN_UNICAST, any, ifa->ifa_prefixlen, prim);
752 else {
753 prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
754 if (prim == NULL) {
755 printk(KERN_DEBUG "fib_del_ifaddr: bug: prim == NULL\n");
756 return;
757 }
758 }
759
760 /* Deletion is more complicated than add.
761 We should take care of not to delete too much :-)
762
763 Scan address list to be sure that addresses are really gone.
764 */
765
766 for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) {
767 if (ifa->ifa_local == ifa1->ifa_local)
768 ok |= LOCAL_OK;
769 if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
770 ok |= BRD_OK;
771 if (brd == ifa1->ifa_broadcast)
772 ok |= BRD1_OK;
773 if (any == ifa1->ifa_broadcast)
774 ok |= BRD0_OK;
775 }
776
777 if (!(ok&BRD_OK))
778 fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
779 if (!(ok&BRD1_OK))
780 fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
781 if (!(ok&BRD0_OK))
782 fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
783 if (!(ok&LOCAL_OK)) {
784 fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
785
786 /* Check, that this local address finally disappeared. */
Eric W. Biederman6b175b22008-01-10 03:25:28 -0800787 if (inet_addr_type(&init_net, ifa->ifa_local) != RTN_LOCAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 /* And the last, but not the least thing.
789 We must flush stray FIB entries.
790
791 First of all, we scan fib_info list searching
792 for stray nexthop entries, then ignite fib_flush.
793 */
794 if (fib_sync_down(ifa->ifa_local, NULL, 0))
795 fib_flush();
796 }
797 }
798#undef LOCAL_OK
799#undef BRD_OK
800#undef BRD0_OK
801#undef BRD1_OK
802}
803
Robert Olsson246955f2005-06-20 13:36:39 -0700804static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb )
805{
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900806
Robert Olsson246955f2005-06-20 13:36:39 -0700807 struct fib_result res;
Thomas Graf5f3008932006-11-09 15:21:41 -0800808 struct flowi fl = { .mark = frn->fl_mark,
Thomas Graf47dcf0c2006-11-09 15:20:38 -0800809 .nl_u = { .ip4_u = { .daddr = frn->fl_addr,
Robert Olsson246955f2005-06-20 13:36:39 -0700810 .tos = frn->fl_tos,
811 .scope = frn->fl_scope } } };
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700812
Sergey Vlasov912a41a2007-04-27 02:17:19 -0700813#ifdef CONFIG_IP_MULTIPLE_TABLES
814 res.r = NULL;
815#endif
816
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700817 frn->err = -ENOENT;
Robert Olsson246955f2005-06-20 13:36:39 -0700818 if (tb) {
819 local_bh_disable();
820
821 frn->tb_id = tb->tb_id;
822 frn->err = tb->tb_lookup(tb, &fl, &res);
823
824 if (!frn->err) {
825 frn->prefixlen = res.prefixlen;
826 frn->nh_sel = res.nh_sel;
827 frn->type = res.type;
828 frn->scope = res.scope;
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700829 fib_res_put(&res);
Robert Olsson246955f2005-06-20 13:36:39 -0700830 }
831 local_bh_enable();
832 }
833}
834
David S. Miller28f7b0362007-10-10 21:32:39 -0700835static void nl_fib_input(struct sk_buff *skb)
Robert Olsson246955f2005-06-20 13:36:39 -0700836{
Robert Olsson246955f2005-06-20 13:36:39 -0700837 struct fib_result_nl *frn;
David S. Miller28f7b0362007-10-10 21:32:39 -0700838 struct nlmsghdr *nlh;
Robert Olsson246955f2005-06-20 13:36:39 -0700839 struct fib_table *tb;
David S. Miller28f7b0362007-10-10 21:32:39 -0700840 u32 pid;
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700841
Arnaldo Carvalho de Melob529ccf2007-04-25 19:08:35 -0700842 nlh = nlmsg_hdr(skb);
Thomas Grafea865752005-12-01 14:30:00 -0800843 if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len ||
Denis V. Lunevd883a032007-12-21 02:01:53 -0800844 nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*frn)))
Thomas Grafea865752005-12-01 14:30:00 -0800845 return;
Denis V. Lunevd883a032007-12-21 02:01:53 -0800846
847 skb = skb_clone(skb, GFP_KERNEL);
848 if (skb == NULL)
849 return;
850 nlh = nlmsg_hdr(skb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900851
Robert Olsson246955f2005-06-20 13:36:39 -0700852 frn = (struct fib_result_nl *) NLMSG_DATA(nlh);
Denis V. Lunev8ad49422008-01-10 03:24:11 -0800853 tb = fib_get_table(&init_net, frn->tb_id_in);
Robert Olsson246955f2005-06-20 13:36:39 -0700854
855 nl_fib_lookup(frn, tb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900856
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700857 pid = NETLINK_CB(skb).pid; /* pid of sending process */
Robert Olsson246955f2005-06-20 13:36:39 -0700858 NETLINK_CB(skb).pid = 0; /* from kernel */
Patrick McHardyac6d4392005-08-14 19:29:52 -0700859 NETLINK_CB(skb).dst_group = 0; /* unicast */
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -0700860 netlink_unicast(fibnl, skb, pid, MSG_DONTWAIT);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900861}
Robert Olsson246955f2005-06-20 13:36:39 -0700862
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800863static int nl_fib_lookup_init(struct net *net)
Robert Olsson246955f2005-06-20 13:36:39 -0700864{
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800865 fibnl = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, 0,
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -0700866 nl_fib_input, NULL, THIS_MODULE);
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800867 if (fibnl == NULL)
868 return -EAFNOSUPPORT;
869 return 0;
870}
871
872static void nl_fib_lookup_exit(struct net *net)
873{
874 sock_put(fibnl);
Robert Olsson246955f2005-06-20 13:36:39 -0700875}
876
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877static void fib_disable_ip(struct net_device *dev, int force)
878{
879 if (fib_sync_down(0, dev, force))
880 fib_flush();
881 rt_cache_flush(0);
882 arp_ifdown(dev);
883}
884
885static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
886{
887 struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
888
889 switch (event) {
890 case NETDEV_UP:
891 fib_add_ifaddr(ifa);
892#ifdef CONFIG_IP_ROUTE_MULTIPATH
893 fib_sync_up(ifa->ifa_dev->dev);
894#endif
895 rt_cache_flush(-1);
896 break;
897 case NETDEV_DOWN:
898 fib_del_ifaddr(ifa);
Jayachandran C9fcc2e82005-10-27 15:10:01 -0700899 if (ifa->ifa_dev->ifa_list == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 /* Last address was deleted from this interface.
901 Disable IP.
902 */
903 fib_disable_ip(ifa->ifa_dev->dev, 1);
904 } else {
905 rt_cache_flush(-1);
906 }
907 break;
908 }
909 return NOTIFY_DONE;
910}
911
912static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
913{
914 struct net_device *dev = ptr;
Herbert Xue5ed6392005-10-03 14:35:55 -0700915 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Eric W. Biedermane9dc8652007-09-12 13:02:17 +0200917 if (dev->nd_net != &init_net)
918 return NOTIFY_DONE;
919
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 if (event == NETDEV_UNREGISTER) {
921 fib_disable_ip(dev, 2);
922 return NOTIFY_DONE;
923 }
924
925 if (!in_dev)
926 return NOTIFY_DONE;
927
928 switch (event) {
929 case NETDEV_UP:
930 for_ifa(in_dev) {
931 fib_add_ifaddr(ifa);
932 } endfor_ifa(in_dev);
933#ifdef CONFIG_IP_ROUTE_MULTIPATH
934 fib_sync_up(dev);
935#endif
936 rt_cache_flush(-1);
937 break;
938 case NETDEV_DOWN:
939 fib_disable_ip(dev, 0);
940 break;
941 case NETDEV_CHANGEMTU:
942 case NETDEV_CHANGE:
943 rt_cache_flush(0);
944 break;
945 }
946 return NOTIFY_DONE;
947}
948
949static struct notifier_block fib_inetaddr_notifier = {
950 .notifier_call =fib_inetaddr_event,
951};
952
953static struct notifier_block fib_netdev_notifier = {
954 .notifier_call =fib_netdev_event,
955};
956
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800957static int __net_init ip_fib_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958{
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700959 unsigned int i;
960
961 for (i = 0; i < FIB_TABLE_HASHSZ; i++)
962 INIT_HLIST_HEAD(&fib_table_hash[i]);
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -0800963
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800964 return fib4_rules_init(net);
965}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800967static void __net_exit ip_fib_net_exit(struct net *net)
968{
969 unsigned int i;
Thomas Graf63f34442007-03-22 11:55:17 -0700970
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -0800971#ifdef CONFIG_IP_MULTIPLE_TABLES
972 fib4_rules_exit(net);
973#endif
974
975 for (i = 0; i < FIB_TABLE_HASHSZ; i++) {
976 struct fib_table *tb;
977 struct hlist_head *head;
978 struct hlist_node *node, *tmp;
979
980 head = &fib_table_hash[i];
981 hlist_for_each_entry_safe(tb, node, tmp, head, tb_hlist) {
982 hlist_del(node);
983 tb->tb_flush(tb);
984 kfree(tb);
985 }
986 }
987}
988
989static int __net_init fib_net_init(struct net *net)
990{
991 int error;
992
993 error = 0;
994 if (net != &init_net)
995 goto out;
996
997 error = ip_fib_net_init(net);
998 if (error < 0)
999 goto out;
1000 error = nl_fib_lookup_init(net);
1001 if (error < 0)
1002 goto out_nlfl;
1003 error = fib_proc_init(net);
1004 if (error < 0)
1005 goto out_proc;
1006out:
1007 return error;
1008
1009out_proc:
1010 nl_fib_lookup_exit(net);
1011out_nlfl:
1012 ip_fib_net_exit(net);
1013 goto out;
1014}
1015
1016static void __net_exit fib_net_exit(struct net *net)
1017{
1018 fib_proc_exit(net);
1019 nl_fib_lookup_exit(net);
1020 ip_fib_net_exit(net);
1021}
1022
1023static struct pernet_operations fib_net_ops = {
1024 .init = fib_net_init,
1025 .exit = fib_net_exit,
1026};
1027
1028void __init ip_fib_init(void)
1029{
Thomas Graf63f34442007-03-22 11:55:17 -07001030 rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
1031 rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL);
1032 rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
Denis V. Lunev7b1a74f2008-01-10 03:22:17 -08001033
1034 register_pernet_subsys(&fib_net_ops);
1035 register_netdevice_notifier(&fib_netdev_notifier);
1036 register_inetaddr_notifier(&fib_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037}
1038
1039EXPORT_SYMBOL(inet_addr_type);
Laszlo Attila Toth05538112007-12-04 23:28:46 -08001040EXPORT_SYMBOL(inet_dev_addr_type);
Sean Heftya1e87332006-06-17 20:37:28 -07001041EXPORT_SYMBOL(ip_dev_find);