blob: ac6238a3b0fd247414884f68f4a088f60f147f5c [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. Miller28f7b032007-10-10 21:32:39 -070052static struct sock *fibnl;
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#ifndef CONFIG_IP_MULTIPLE_TABLES
55
Linus Torvalds1da177e2005-04-16 15:20:36 -070056struct fib_table *ip_fib_local_table;
57struct fib_table *ip_fib_main_table;
58
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070059#define FIB_TABLE_HASHSZ 1
60static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
61
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -080062static void __init fib4_rules_init(void)
63{
64 ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL);
65 hlist_add_head_rcu(&ip_fib_local_table->tb_hlist, &fib_table_hash[0]);
66 ip_fib_main_table = fib_hash_init(RT_TABLE_MAIN);
67 hlist_add_head_rcu(&ip_fib_main_table->tb_hlist, &fib_table_hash[0]);
68}
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#else
70
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070071#define FIB_TABLE_HASHSZ 256
72static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070074struct fib_table *fib_new_table(u32 id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
76 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070077 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070079 if (id == 0)
80 id = RT_TABLE_MAIN;
81 tb = fib_get_table(id);
82 if (tb)
83 return tb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 tb = fib_hash_init(id);
85 if (!tb)
86 return NULL;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070087 h = id & (FIB_TABLE_HASHSZ - 1);
88 hlist_add_head_rcu(&tb->tb_hlist, &fib_table_hash[h]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 return tb;
90}
91
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070092struct fib_table *fib_get_table(u32 id)
93{
94 struct fib_table *tb;
95 struct hlist_node *node;
96 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
Patrick McHardy1af5a8c2006-08-10 23:10:46 -070098 if (id == 0)
99 id = RT_TABLE_MAIN;
100 h = id & (FIB_TABLE_HASHSZ - 1);
101 rcu_read_lock();
102 hlist_for_each_entry_rcu(tb, node, &fib_table_hash[h], tb_hlist) {
103 if (tb->tb_id == id) {
104 rcu_read_unlock();
105 return tb;
106 }
107 }
108 rcu_read_unlock();
109 return NULL;
110}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111#endif /* CONFIG_IP_MULTIPLE_TABLES */
112
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113static void fib_flush(void)
114{
115 int flushed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700117 struct hlist_node *node;
118 unsigned int h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700120 for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
121 hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist)
122 flushed += tb->tb_flush(tb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
125 if (flushed)
126 rt_cache_flush(-1);
127}
128
129/*
130 * Find the first device with a given source address.
131 */
132
Al Viro60cad5d2006-09-26 22:17:09 -0700133struct net_device * ip_dev_find(__be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
135 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
136 struct fib_result res;
137 struct net_device *dev = NULL;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700138 struct fib_table *local_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
140#ifdef CONFIG_IP_MULTIPLE_TABLES
141 res.r = NULL;
142#endif
143
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700144 local_table = fib_get_table(RT_TABLE_LOCAL);
145 if (!local_table || local_table->tb_lookup(local_table, &fl, &res))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 return NULL;
147 if (res.type != RTN_LOCAL)
148 goto out;
149 dev = FIB_RES_DEV(res);
150
151 if (dev)
152 dev_hold(dev);
153out:
154 fib_res_put(&res);
155 return dev;
156}
157
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800158/*
159 * Find address type as if only "dev" was present in the system. If
160 * on_dev is NULL then all interfaces are taken into consideration.
161 */
162static inline unsigned __inet_dev_addr_type(const struct net_device *dev,
163 __be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164{
165 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
166 struct fib_result res;
167 unsigned ret = RTN_BROADCAST;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700168 struct fib_table *local_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
Joe Perchesf97c1e02007-12-16 13:45:43 -0800170 if (ipv4_is_zeronet(addr) || ipv4_is_badclass(addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 return RTN_BROADCAST;
Joe Perchesf97c1e02007-12-16 13:45:43 -0800172 if (ipv4_is_multicast(addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 return RTN_MULTICAST;
174
175#ifdef CONFIG_IP_MULTIPLE_TABLES
176 res.r = NULL;
177#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900178
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700179 local_table = fib_get_table(RT_TABLE_LOCAL);
180 if (local_table) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 ret = RTN_UNICAST;
Pavel Emelyanov03cf7862007-10-23 21:17:27 -0700182 if (!local_table->tb_lookup(local_table, &fl, &res)) {
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800183 if (!dev || dev == res.fi->fib_dev)
184 ret = res.type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 fib_res_put(&res);
186 }
187 }
188 return ret;
189}
190
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800191unsigned int inet_addr_type(__be32 addr)
192{
193 return __inet_dev_addr_type(NULL, addr);
194}
195
196unsigned int inet_dev_addr_type(const struct net_device *dev, __be32 addr)
197{
198 return __inet_dev_addr_type(dev, addr);
199}
200
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201/* Given (packet source, input interface) and optional (dst, oif, tos):
202 - (main) check, that source is valid i.e. not broadcast or our local
203 address.
204 - figure out what "logical" interface this packet arrived
205 and calculate "specific destination" address.
206 - check, that packet arrived from expected physical interface.
207 */
208
Al Virod9c9df82006-09-26 21:28:14 -0700209int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
210 struct net_device *dev, __be32 *spec_dst, u32 *itag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
212 struct in_device *in_dev;
213 struct flowi fl = { .nl_u = { .ip4_u =
214 { .daddr = src,
215 .saddr = dst,
216 .tos = tos } },
217 .iif = oif };
218 struct fib_result res;
219 int no_addr, rpf;
220 int ret;
221
222 no_addr = rpf = 0;
223 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700224 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 if (in_dev) {
226 no_addr = in_dev->ifa_list == NULL;
227 rpf = IN_DEV_RPFILTER(in_dev);
228 }
229 rcu_read_unlock();
230
231 if (in_dev == NULL)
232 goto e_inval;
233
234 if (fib_lookup(&fl, &res))
235 goto last_resort;
236 if (res.type != RTN_UNICAST)
237 goto e_inval_res;
238 *spec_dst = FIB_RES_PREFSRC(res);
239 fib_combine_itag(itag, &res);
240#ifdef CONFIG_IP_ROUTE_MULTIPATH
241 if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
242#else
243 if (FIB_RES_DEV(res) == dev)
244#endif
245 {
246 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
247 fib_res_put(&res);
248 return ret;
249 }
250 fib_res_put(&res);
251 if (no_addr)
252 goto last_resort;
253 if (rpf)
254 goto e_inval;
255 fl.oif = dev->ifindex;
256
257 ret = 0;
258 if (fib_lookup(&fl, &res) == 0) {
259 if (res.type == RTN_UNICAST) {
260 *spec_dst = FIB_RES_PREFSRC(res);
261 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
262 }
263 fib_res_put(&res);
264 }
265 return ret;
266
267last_resort:
268 if (rpf)
269 goto e_inval;
270 *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
271 *itag = 0;
272 return 0;
273
274e_inval_res:
275 fib_res_put(&res);
276e_inval:
277 return -EINVAL;
278}
279
Al Viro81f7bf62006-09-27 18:40:00 -0700280static inline __be32 sk_extract_addr(struct sockaddr *addr)
Thomas Graf4e902c52006-08-17 18:14:52 -0700281{
282 return ((struct sockaddr_in *) addr)->sin_addr.s_addr;
283}
284
285static int put_rtax(struct nlattr *mx, int len, int type, u32 value)
286{
287 struct nlattr *nla;
288
289 nla = (struct nlattr *) ((char *) mx + len);
290 nla->nla_type = type;
291 nla->nla_len = nla_attr_size(4);
292 *(u32 *) nla_data(nla) = value;
293
294 return len + nla_total_size(4);
295}
296
297static int rtentry_to_fib_config(int cmd, struct rtentry *rt,
298 struct fib_config *cfg)
299{
Al Viro6d85c102006-09-26 22:15:46 -0700300 __be32 addr;
Thomas Graf4e902c52006-08-17 18:14:52 -0700301 int plen;
302
303 memset(cfg, 0, sizeof(*cfg));
304
305 if (rt->rt_dst.sa_family != AF_INET)
306 return -EAFNOSUPPORT;
307
308 /*
309 * Check mask for validity:
310 * a) it must be contiguous.
311 * b) destination must have all host bits clear.
312 * c) if application forgot to set correct family (AF_INET),
313 * reject request unless it is absolutely clear i.e.
314 * both family and mask are zero.
315 */
316 plen = 32;
317 addr = sk_extract_addr(&rt->rt_dst);
318 if (!(rt->rt_flags & RTF_HOST)) {
Al Viro81f7bf62006-09-27 18:40:00 -0700319 __be32 mask = sk_extract_addr(&rt->rt_genmask);
Thomas Graf4e902c52006-08-17 18:14:52 -0700320
321 if (rt->rt_genmask.sa_family != AF_INET) {
322 if (mask || rt->rt_genmask.sa_family)
323 return -EAFNOSUPPORT;
324 }
325
326 if (bad_mask(mask, addr))
327 return -EINVAL;
328
329 plen = inet_mask_len(mask);
330 }
331
332 cfg->fc_dst_len = plen;
333 cfg->fc_dst = addr;
334
335 if (cmd != SIOCDELRT) {
336 cfg->fc_nlflags = NLM_F_CREATE;
337 cfg->fc_protocol = RTPROT_BOOT;
338 }
339
340 if (rt->rt_metric)
341 cfg->fc_priority = rt->rt_metric - 1;
342
343 if (rt->rt_flags & RTF_REJECT) {
344 cfg->fc_scope = RT_SCOPE_HOST;
345 cfg->fc_type = RTN_UNREACHABLE;
346 return 0;
347 }
348
349 cfg->fc_scope = RT_SCOPE_NOWHERE;
350 cfg->fc_type = RTN_UNICAST;
351
352 if (rt->rt_dev) {
353 char *colon;
354 struct net_device *dev;
355 char devname[IFNAMSIZ];
356
357 if (copy_from_user(devname, rt->rt_dev, IFNAMSIZ-1))
358 return -EFAULT;
359
360 devname[IFNAMSIZ-1] = 0;
361 colon = strchr(devname, ':');
362 if (colon)
363 *colon = 0;
Eric W. Biederman881d9662007-09-17 11:56:21 -0700364 dev = __dev_get_by_name(&init_net, devname);
Thomas Graf4e902c52006-08-17 18:14:52 -0700365 if (!dev)
366 return -ENODEV;
367 cfg->fc_oif = dev->ifindex;
368 if (colon) {
369 struct in_ifaddr *ifa;
370 struct in_device *in_dev = __in_dev_get_rtnl(dev);
371 if (!in_dev)
372 return -ENODEV;
373 *colon = ':';
374 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
375 if (strcmp(ifa->ifa_label, devname) == 0)
376 break;
377 if (ifa == NULL)
378 return -ENODEV;
379 cfg->fc_prefsrc = ifa->ifa_local;
380 }
381 }
382
383 addr = sk_extract_addr(&rt->rt_gateway);
384 if (rt->rt_gateway.sa_family == AF_INET && addr) {
385 cfg->fc_gw = addr;
386 if (rt->rt_flags & RTF_GATEWAY &&
387 inet_addr_type(addr) == RTN_UNICAST)
388 cfg->fc_scope = RT_SCOPE_UNIVERSE;
389 }
390
391 if (cmd == SIOCDELRT)
392 return 0;
393
394 if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw)
395 return -EINVAL;
396
397 if (cfg->fc_scope == RT_SCOPE_NOWHERE)
398 cfg->fc_scope = RT_SCOPE_LINK;
399
400 if (rt->rt_flags & (RTF_MTU | RTF_WINDOW | RTF_IRTT)) {
401 struct nlattr *mx;
402 int len = 0;
403
404 mx = kzalloc(3 * nla_total_size(4), GFP_KERNEL);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900405 if (mx == NULL)
Thomas Graf4e902c52006-08-17 18:14:52 -0700406 return -ENOMEM;
407
408 if (rt->rt_flags & RTF_MTU)
409 len = put_rtax(mx, len, RTAX_ADVMSS, rt->rt_mtu - 40);
410
411 if (rt->rt_flags & RTF_WINDOW)
412 len = put_rtax(mx, len, RTAX_WINDOW, rt->rt_window);
413
414 if (rt->rt_flags & RTF_IRTT)
415 len = put_rtax(mx, len, RTAX_RTT, rt->rt_irtt << 3);
416
417 cfg->fc_mx = mx;
418 cfg->fc_mx_len = len;
419 }
420
421 return 0;
422}
423
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424/*
425 * Handle IP routing ioctl calls. These are used to manipulate the routing tables
426 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900427
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428int ip_rt_ioctl(unsigned int cmd, void __user *arg)
429{
Thomas Graf4e902c52006-08-17 18:14:52 -0700430 struct fib_config cfg;
431 struct rtentry rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434 switch (cmd) {
435 case SIOCADDRT: /* Add a route */
436 case SIOCDELRT: /* Delete a route */
437 if (!capable(CAP_NET_ADMIN))
438 return -EPERM;
Thomas Graf4e902c52006-08-17 18:14:52 -0700439
440 if (copy_from_user(&rt, arg, sizeof(rt)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 return -EFAULT;
Thomas Graf4e902c52006-08-17 18:14:52 -0700442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 rtnl_lock();
Thomas Graf4e902c52006-08-17 18:14:52 -0700444 err = rtentry_to_fib_config(cmd, &rt, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 if (err == 0) {
Thomas Graf4e902c52006-08-17 18:14:52 -0700446 struct fib_table *tb;
447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 if (cmd == SIOCDELRT) {
Thomas Graf4e902c52006-08-17 18:14:52 -0700449 tb = fib_get_table(cfg.fc_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 if (tb)
Thomas Graf4e902c52006-08-17 18:14:52 -0700451 err = tb->tb_delete(tb, &cfg);
452 else
453 err = -ESRCH;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 } else {
Thomas Graf4e902c52006-08-17 18:14:52 -0700455 tb = fib_new_table(cfg.fc_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 if (tb)
Thomas Graf4e902c52006-08-17 18:14:52 -0700457 err = tb->tb_insert(tb, &cfg);
458 else
459 err = -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 }
Thomas Graf4e902c52006-08-17 18:14:52 -0700461
462 /* allocated by rtentry_to_fib_config() */
463 kfree(cfg.fc_mx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 }
465 rtnl_unlock();
466 return err;
467 }
468 return -EINVAL;
469}
470
Patrick McHardyef7c79e2007-06-05 12:38:30 -0700471const struct nla_policy rtm_ipv4_policy[RTA_MAX+1] = {
Thomas Graf4e902c52006-08-17 18:14:52 -0700472 [RTA_DST] = { .type = NLA_U32 },
473 [RTA_SRC] = { .type = NLA_U32 },
474 [RTA_IIF] = { .type = NLA_U32 },
475 [RTA_OIF] = { .type = NLA_U32 },
476 [RTA_GATEWAY] = { .type = NLA_U32 },
477 [RTA_PRIORITY] = { .type = NLA_U32 },
478 [RTA_PREFSRC] = { .type = NLA_U32 },
479 [RTA_METRICS] = { .type = NLA_NESTED },
Thomas Graf5176f912006-08-26 20:13:18 -0700480 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Thomas Graf4e902c52006-08-17 18:14:52 -0700481 [RTA_PROTOINFO] = { .type = NLA_U32 },
482 [RTA_FLOW] = { .type = NLA_U32 },
Thomas Graf4e902c52006-08-17 18:14:52 -0700483};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Thomas Graf4e902c52006-08-17 18:14:52 -0700485static int rtm_to_fib_config(struct sk_buff *skb, struct nlmsghdr *nlh,
486 struct fib_config *cfg)
487{
488 struct nlattr *attr;
489 int err, remaining;
490 struct rtmsg *rtm;
491
492 err = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipv4_policy);
493 if (err < 0)
494 goto errout;
495
496 memset(cfg, 0, sizeof(*cfg));
497
498 rtm = nlmsg_data(nlh);
Thomas Graf4e902c52006-08-17 18:14:52 -0700499 cfg->fc_dst_len = rtm->rtm_dst_len;
Thomas Graf4e902c52006-08-17 18:14:52 -0700500 cfg->fc_tos = rtm->rtm_tos;
501 cfg->fc_table = rtm->rtm_table;
502 cfg->fc_protocol = rtm->rtm_protocol;
503 cfg->fc_scope = rtm->rtm_scope;
504 cfg->fc_type = rtm->rtm_type;
505 cfg->fc_flags = rtm->rtm_flags;
506 cfg->fc_nlflags = nlh->nlmsg_flags;
507
508 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
509 cfg->fc_nlinfo.nlh = nlh;
510
Thomas Grafa0ee18b2007-03-24 20:32:54 -0700511 if (cfg->fc_type > RTN_MAX) {
512 err = -EINVAL;
513 goto errout;
514 }
515
Thomas Graf4e902c52006-08-17 18:14:52 -0700516 nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +0200517 switch (nla_type(attr)) {
Thomas Graf4e902c52006-08-17 18:14:52 -0700518 case RTA_DST:
Al Viro17fb2c62006-09-26 22:15:25 -0700519 cfg->fc_dst = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700520 break;
Thomas Graf4e902c52006-08-17 18:14:52 -0700521 case RTA_OIF:
522 cfg->fc_oif = nla_get_u32(attr);
523 break;
524 case RTA_GATEWAY:
Al Viro17fb2c62006-09-26 22:15:25 -0700525 cfg->fc_gw = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700526 break;
527 case RTA_PRIORITY:
528 cfg->fc_priority = nla_get_u32(attr);
529 break;
530 case RTA_PREFSRC:
Al Viro17fb2c62006-09-26 22:15:25 -0700531 cfg->fc_prefsrc = nla_get_be32(attr);
Thomas Graf4e902c52006-08-17 18:14:52 -0700532 break;
533 case RTA_METRICS:
534 cfg->fc_mx = nla_data(attr);
535 cfg->fc_mx_len = nla_len(attr);
536 break;
537 case RTA_MULTIPATH:
538 cfg->fc_mp = nla_data(attr);
539 cfg->fc_mp_len = nla_len(attr);
540 break;
541 case RTA_FLOW:
542 cfg->fc_flow = nla_get_u32(attr);
543 break;
Thomas Graf4e902c52006-08-17 18:14:52 -0700544 case RTA_TABLE:
545 cfg->fc_table = nla_get_u32(attr);
546 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 }
548 }
Thomas Graf4e902c52006-08-17 18:14:52 -0700549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 return 0;
Thomas Graf4e902c52006-08-17 18:14:52 -0700551errout:
552 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553}
554
Thomas Graf63f34442007-03-22 11:55:17 -0700555static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100557 struct net *net = skb->sk->sk_net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700558 struct fib_config cfg;
559 struct fib_table *tb;
560 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Denis V. Lunevb8542722007-12-01 00:21:31 +1100562 if (net != &init_net)
563 return -EINVAL;
564
Thomas Graf4e902c52006-08-17 18:14:52 -0700565 err = rtm_to_fib_config(skb, nlh, &cfg);
566 if (err < 0)
567 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Thomas Graf4e902c52006-08-17 18:14:52 -0700569 tb = fib_get_table(cfg.fc_table);
570 if (tb == NULL) {
571 err = -ESRCH;
572 goto errout;
573 }
574
575 err = tb->tb_delete(tb, &cfg);
576errout:
577 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
Thomas Graf63f34442007-03-22 11:55:17 -0700580static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100582 struct net *net = skb->sk->sk_net;
Thomas Graf4e902c52006-08-17 18:14:52 -0700583 struct fib_config cfg;
584 struct fib_table *tb;
585 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Denis V. Lunevb8542722007-12-01 00:21:31 +1100587 if (net != &init_net)
588 return -EINVAL;
589
Thomas Graf4e902c52006-08-17 18:14:52 -0700590 err = rtm_to_fib_config(skb, nlh, &cfg);
591 if (err < 0)
592 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Thomas Graf4e902c52006-08-17 18:14:52 -0700594 tb = fib_new_table(cfg.fc_table);
595 if (tb == NULL) {
596 err = -ENOBUFS;
597 goto errout;
598 }
599
600 err = tb->tb_insert(tb, &cfg);
601errout:
602 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603}
604
Thomas Graf63f34442007-03-22 11:55:17 -0700605static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606{
Denis V. Lunevb8542722007-12-01 00:21:31 +1100607 struct net *net = skb->sk->sk_net;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700608 unsigned int h, s_h;
609 unsigned int e = 0, s_e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 struct fib_table *tb;
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700611 struct hlist_node *node;
612 int dumped = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Denis V. Lunevb8542722007-12-01 00:21:31 +1100614 if (net != &init_net)
615 return 0;
616
Thomas Grafbe403ea2006-08-17 18:15:17 -0700617 if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
618 ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 return ip_rt_dump(skb, cb);
620
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700621 s_h = cb->args[0];
622 s_e = cb->args[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700624 for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
625 e = 0;
626 hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist) {
627 if (e < s_e)
628 goto next;
629 if (dumped)
630 memset(&cb->args[2], 0, sizeof(cb->args) -
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900631 2 * sizeof(cb->args[0]));
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700632 if (tb->tb_dump(tb, skb, cb) < 0)
633 goto out;
634 dumped = 1;
635next:
636 e++;
637 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 }
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700639out:
640 cb->args[1] = e;
641 cb->args[0] = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643 return skb->len;
644}
645
646/* Prepare and feed intra-kernel routing request.
647 Really, it should be netlink message, but :-( netlink
648 can be not configured, so that we feed it directly
649 to fib engine. It is legal, because all events occur
650 only when netlink is already locked.
651 */
652
Al Viro81f7bf62006-09-27 18:40:00 -0700653static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654{
Thomas Graf4e902c52006-08-17 18:14:52 -0700655 struct fib_table *tb;
656 struct fib_config cfg = {
657 .fc_protocol = RTPROT_KERNEL,
658 .fc_type = type,
659 .fc_dst = dst,
660 .fc_dst_len = dst_len,
661 .fc_prefsrc = ifa->ifa_local,
662 .fc_oif = ifa->ifa_dev->dev->ifindex,
663 .fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
664 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 if (type == RTN_UNICAST)
667 tb = fib_new_table(RT_TABLE_MAIN);
668 else
669 tb = fib_new_table(RT_TABLE_LOCAL);
670
671 if (tb == NULL)
672 return;
673
Thomas Graf4e902c52006-08-17 18:14:52 -0700674 cfg.fc_table = tb->tb_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Thomas Graf4e902c52006-08-17 18:14:52 -0700676 if (type != RTN_LOCAL)
677 cfg.fc_scope = RT_SCOPE_LINK;
678 else
679 cfg.fc_scope = RT_SCOPE_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
681 if (cmd == RTM_NEWROUTE)
Thomas Graf4e902c52006-08-17 18:14:52 -0700682 tb->tb_insert(tb, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 else
Thomas Graf4e902c52006-08-17 18:14:52 -0700684 tb->tb_delete(tb, &cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685}
686
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800687void fib_add_ifaddr(struct in_ifaddr *ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
689 struct in_device *in_dev = ifa->ifa_dev;
690 struct net_device *dev = in_dev->dev;
691 struct in_ifaddr *prim = ifa;
Al Viroa144ea42006-09-28 18:00:55 -0700692 __be32 mask = ifa->ifa_mask;
693 __be32 addr = ifa->ifa_local;
694 __be32 prefix = ifa->ifa_address&mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 if (ifa->ifa_flags&IFA_F_SECONDARY) {
697 prim = inet_ifa_byprefix(in_dev, prefix, mask);
698 if (prim == NULL) {
699 printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL\n");
700 return;
701 }
702 }
703
704 fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
705
706 if (!(dev->flags&IFF_UP))
707 return;
708
709 /* Add broadcast address, if it is explicitly assigned. */
Al Viroa144ea42006-09-28 18:00:55 -0700710 if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
712
Joe Perchesf97c1e02007-12-16 13:45:43 -0800713 if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 (prefix != addr || ifa->ifa_prefixlen < 32)) {
715 fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
716 RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
717
718 /* Add network specific broadcasts, when it takes a sense */
719 if (ifa->ifa_prefixlen < 31) {
720 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
721 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
722 }
723 }
724}
725
726static void fib_del_ifaddr(struct in_ifaddr *ifa)
727{
728 struct in_device *in_dev = ifa->ifa_dev;
729 struct net_device *dev = in_dev->dev;
730 struct in_ifaddr *ifa1;
731 struct in_ifaddr *prim = ifa;
Al Viroa144ea42006-09-28 18:00:55 -0700732 __be32 brd = ifa->ifa_address|~ifa->ifa_mask;
733 __be32 any = ifa->ifa_address&ifa->ifa_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734#define LOCAL_OK 1
735#define BRD_OK 2
736#define BRD0_OK 4
737#define BRD1_OK 8
738 unsigned ok = 0;
739
740 if (!(ifa->ifa_flags&IFA_F_SECONDARY))
741 fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
742 RTN_UNICAST, any, ifa->ifa_prefixlen, prim);
743 else {
744 prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
745 if (prim == NULL) {
746 printk(KERN_DEBUG "fib_del_ifaddr: bug: prim == NULL\n");
747 return;
748 }
749 }
750
751 /* Deletion is more complicated than add.
752 We should take care of not to delete too much :-)
753
754 Scan address list to be sure that addresses are really gone.
755 */
756
757 for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) {
758 if (ifa->ifa_local == ifa1->ifa_local)
759 ok |= LOCAL_OK;
760 if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
761 ok |= BRD_OK;
762 if (brd == ifa1->ifa_broadcast)
763 ok |= BRD1_OK;
764 if (any == ifa1->ifa_broadcast)
765 ok |= BRD0_OK;
766 }
767
768 if (!(ok&BRD_OK))
769 fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
770 if (!(ok&BRD1_OK))
771 fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
772 if (!(ok&BRD0_OK))
773 fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
774 if (!(ok&LOCAL_OK)) {
775 fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
776
777 /* Check, that this local address finally disappeared. */
778 if (inet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
779 /* And the last, but not the least thing.
780 We must flush stray FIB entries.
781
782 First of all, we scan fib_info list searching
783 for stray nexthop entries, then ignite fib_flush.
784 */
785 if (fib_sync_down(ifa->ifa_local, NULL, 0))
786 fib_flush();
787 }
788 }
789#undef LOCAL_OK
790#undef BRD_OK
791#undef BRD0_OK
792#undef BRD1_OK
793}
794
Robert Olsson246955f2005-06-20 13:36:39 -0700795static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb )
796{
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900797
Robert Olsson246955f2005-06-20 13:36:39 -0700798 struct fib_result res;
Thomas Graf5f300892006-11-09 15:21:41 -0800799 struct flowi fl = { .mark = frn->fl_mark,
Thomas Graf47dcf0c2006-11-09 15:20:38 -0800800 .nl_u = { .ip4_u = { .daddr = frn->fl_addr,
Robert Olsson246955f2005-06-20 13:36:39 -0700801 .tos = frn->fl_tos,
802 .scope = frn->fl_scope } } };
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700803
Sergey Vlasov912a41a2007-04-27 02:17:19 -0700804#ifdef CONFIG_IP_MULTIPLE_TABLES
805 res.r = NULL;
806#endif
807
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700808 frn->err = -ENOENT;
Robert Olsson246955f2005-06-20 13:36:39 -0700809 if (tb) {
810 local_bh_disable();
811
812 frn->tb_id = tb->tb_id;
813 frn->err = tb->tb_lookup(tb, &fl, &res);
814
815 if (!frn->err) {
816 frn->prefixlen = res.prefixlen;
817 frn->nh_sel = res.nh_sel;
818 frn->type = res.type;
819 frn->scope = res.scope;
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700820 fib_res_put(&res);
Robert Olsson246955f2005-06-20 13:36:39 -0700821 }
822 local_bh_enable();
823 }
824}
825
David S. Miller28f7b032007-10-10 21:32:39 -0700826static void nl_fib_input(struct sk_buff *skb)
Robert Olsson246955f2005-06-20 13:36:39 -0700827{
Robert Olsson246955f2005-06-20 13:36:39 -0700828 struct fib_result_nl *frn;
David S. Miller28f7b032007-10-10 21:32:39 -0700829 struct nlmsghdr *nlh;
Robert Olsson246955f2005-06-20 13:36:39 -0700830 struct fib_table *tb;
David S. Miller28f7b032007-10-10 21:32:39 -0700831 u32 pid;
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700832
Arnaldo Carvalho de Melob529ccf2007-04-25 19:08:35 -0700833 nlh = nlmsg_hdr(skb);
Thomas Grafea865752005-12-01 14:30:00 -0800834 if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len ||
Denis V. Lunevd883a032007-12-21 02:01:53 -0800835 nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*frn)))
Thomas Grafea865752005-12-01 14:30:00 -0800836 return;
Denis V. Lunevd883a032007-12-21 02:01:53 -0800837
838 skb = skb_clone(skb, GFP_KERNEL);
839 if (skb == NULL)
840 return;
841 nlh = nlmsg_hdr(skb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900842
Robert Olsson246955f2005-06-20 13:36:39 -0700843 frn = (struct fib_result_nl *) NLMSG_DATA(nlh);
844 tb = fib_get_table(frn->tb_id_in);
845
846 nl_fib_lookup(frn, tb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900847
Alexey Kuznetsov1194ed02007-04-25 13:07:28 -0700848 pid = NETLINK_CB(skb).pid; /* pid of sending process */
Robert Olsson246955f2005-06-20 13:36:39 -0700849 NETLINK_CB(skb).pid = 0; /* from kernel */
Patrick McHardyac6d4392005-08-14 19:29:52 -0700850 NETLINK_CB(skb).dst_group = 0; /* unicast */
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -0700851 netlink_unicast(fibnl, skb, pid, MSG_DONTWAIT);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900852}
Robert Olsson246955f2005-06-20 13:36:39 -0700853
854static void nl_fib_lookup_init(void)
855{
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -0700856 fibnl = netlink_kernel_create(&init_net, NETLINK_FIB_LOOKUP, 0,
857 nl_fib_input, NULL, THIS_MODULE);
Robert Olsson246955f2005-06-20 13:36:39 -0700858}
859
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860static void fib_disable_ip(struct net_device *dev, int force)
861{
862 if (fib_sync_down(0, dev, force))
863 fib_flush();
864 rt_cache_flush(0);
865 arp_ifdown(dev);
866}
867
868static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
869{
870 struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
871
872 switch (event) {
873 case NETDEV_UP:
874 fib_add_ifaddr(ifa);
875#ifdef CONFIG_IP_ROUTE_MULTIPATH
876 fib_sync_up(ifa->ifa_dev->dev);
877#endif
878 rt_cache_flush(-1);
879 break;
880 case NETDEV_DOWN:
881 fib_del_ifaddr(ifa);
Jayachandran C9fcc2e82005-10-27 15:10:01 -0700882 if (ifa->ifa_dev->ifa_list == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 /* Last address was deleted from this interface.
884 Disable IP.
885 */
886 fib_disable_ip(ifa->ifa_dev->dev, 1);
887 } else {
888 rt_cache_flush(-1);
889 }
890 break;
891 }
892 return NOTIFY_DONE;
893}
894
895static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
896{
897 struct net_device *dev = ptr;
Herbert Xue5ed6392005-10-03 14:35:55 -0700898 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Eric W. Biedermane9dc8652007-09-12 13:02:17 +0200900 if (dev->nd_net != &init_net)
901 return NOTIFY_DONE;
902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 if (event == NETDEV_UNREGISTER) {
904 fib_disable_ip(dev, 2);
905 return NOTIFY_DONE;
906 }
907
908 if (!in_dev)
909 return NOTIFY_DONE;
910
911 switch (event) {
912 case NETDEV_UP:
913 for_ifa(in_dev) {
914 fib_add_ifaddr(ifa);
915 } endfor_ifa(in_dev);
916#ifdef CONFIG_IP_ROUTE_MULTIPATH
917 fib_sync_up(dev);
918#endif
919 rt_cache_flush(-1);
920 break;
921 case NETDEV_DOWN:
922 fib_disable_ip(dev, 0);
923 break;
924 case NETDEV_CHANGEMTU:
925 case NETDEV_CHANGE:
926 rt_cache_flush(0);
927 break;
928 }
929 return NOTIFY_DONE;
930}
931
932static struct notifier_block fib_inetaddr_notifier = {
933 .notifier_call =fib_inetaddr_event,
934};
935
936static struct notifier_block fib_netdev_notifier = {
937 .notifier_call =fib_netdev_event,
938};
939
940void __init ip_fib_init(void)
941{
Patrick McHardy1af5a8c2006-08-10 23:10:46 -0700942 unsigned int i;
943
944 for (i = 0; i < FIB_TABLE_HASHSZ; i++)
945 INIT_HLIST_HEAD(&fib_table_hash[i]);
Pavel Emelyanovc3e9a352007-11-06 23:34:04 -0800946
Thomas Grafe1ef4bf2006-08-04 03:39:22 -0700947 fib4_rules_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 register_netdevice_notifier(&fib_netdev_notifier);
950 register_inetaddr_notifier(&fib_inetaddr_notifier);
Robert Olsson246955f2005-06-20 13:36:39 -0700951 nl_fib_lookup_init();
Thomas Graf63f34442007-03-22 11:55:17 -0700952
953 rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
954 rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL);
955 rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956}
957
958EXPORT_SYMBOL(inet_addr_type);
Laszlo Attila Toth05538112007-12-04 23:28:46 -0800959EXPORT_SYMBOL(inet_dev_addr_type);
Sean Heftya1e87332006-06-17 20:37:28 -0700960EXPORT_SYMBOL(ip_dev_find);