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.
6 * IPv4 Forwarding Information Base: FIB frontend.
8 * Version: $Id: fib_frontend.c,v 1.16 1999/06/09 10:10:42 davem Exp $
10 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
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.
18 #include <linux/config.h>
19 #include <asm/uaccess.h>
20 #include <asm/system.h>
21 #include <asm/bitops.h>
22 #include <linux/types.h>
23 #include <linux/kernel.h>
24 #include <linux/sched.h>
26 #include <linux/string.h>
27 #include <linux/socket.h>
28 #include <linux/sockios.h>
29 #include <linux/errno.h>
31 #include <linux/inet.h>
32 #include <linux/netdevice.h>
33 #include <linux/if_arp.h>
34 #include <linux/proc_fs.h>
35 #include <linux/skbuff.h>
36 #include <linux/netlink.h>
37 #include <linux/init.h>
40 #include <net/protocol.h>
41 #include <net/route.h>
46 #include <net/ip_fib.h>
48 #define FFprint(a...) printk(KERN_DEBUG a)
50 #ifndef CONFIG_IP_MULTIPLE_TABLES
52 #define RT_TABLE_MIN RT_TABLE_MAIN
54 struct fib_table
*local_table
;
55 struct fib_table
*main_table
;
59 #define RT_TABLE_MIN 1
61 struct fib_table
*fib_tables
[RT_TABLE_MAX
+1];
63 struct fib_table
*__fib_new_table(int id
)
67 tb
= fib_hash_init(id
);
75 #endif /* CONFIG_IP_MULTIPLE_TABLES */
81 #ifdef CONFIG_IP_MULTIPLE_TABLES
85 for (id
= RT_TABLE_MAX
; id
>0; id
--) {
86 if ((tb
= fib_get_table(id
))==NULL
)
88 flushed
+= tb
->tb_flush(tb
);
90 #else /* CONFIG_IP_MULTIPLE_TABLES */
91 flushed
+= main_table
->tb_flush(main_table
);
92 flushed
+= local_table
->tb_flush(local_table
);
93 #endif /* CONFIG_IP_MULTIPLE_TABLES */
100 #ifdef CONFIG_PROC_FS
103 * Called from the PROCfs module. This outputs /proc/net/route.
105 * It always works in backward compatibility mode.
106 * The format of the file is not supposed to be changed.
110 fib_get_procinfo(char *buffer
, char **start
, off_t offset
, int length
, int dummy
)
112 int first
= offset
/128;
114 int count
= (length
+127)/128;
117 *start
= buffer
+ offset
%128;
120 sprintf(buffer
, "%-127s\n", "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
126 if (main_table
&& count
> 0) {
127 int n
= main_table
->tb_get_info(main_table
, ptr
, first
, count
);
139 #endif /* CONFIG_PROC_FS */
142 * Find the first device with a given source address.
145 struct device
* ip_dev_find(u32 addr
)
148 struct fib_result res
;
150 memset(&key
, 0, sizeof(key
));
153 if (!local_table
|| local_table
->tb_lookup(local_table
, &key
, &res
)
154 || res
.type
!= RTN_LOCAL
)
157 return FIB_RES_DEV(res
);
160 unsigned inet_addr_type(u32 addr
)
163 struct fib_result res
;
165 if (ZERONET(addr
) || BADCLASS(addr
))
166 return RTN_BROADCAST
;
168 return RTN_MULTICAST
;
170 memset(&key
, 0, sizeof(key
));
174 if (local_table
->tb_lookup(local_table
, &key
, &res
) == 0)
178 return RTN_BROADCAST
;
181 /* Given (packet source, input interface) and optional (dst, oif, tos):
182 - (main) check, that source is valid i.e. not broadcast or our local
184 - figure out what "logical" interface this packet arrived
185 and calculate "specific destination" address.
186 - check, that packet arrived from expected physical interface.
189 int fib_validate_source(u32 src
, u32 dst
, u8 tos
, int oif
,
190 struct device
*dev
, u32
*spec_dst
, u32
*itag
)
192 struct in_device
*in_dev
= dev
->ip_ptr
;
194 struct fib_result res
;
201 key
.scope
= RT_SCOPE_UNIVERSE
;
205 if (fib_lookup(&key
, &res
))
207 if (res
.type
!= RTN_UNICAST
)
209 *spec_dst
= FIB_RES_PREFSRC(res
);
211 fib_combine_itag(itag
, &res
);
212 #ifdef CONFIG_IP_ROUTE_MULTIPATH
213 if (FIB_RES_DEV(res
) == dev
|| res
.fi
->fib_nhs
> 1)
215 if (FIB_RES_DEV(res
) == dev
)
217 return FIB_RES_NH(res
).nh_scope
>= RT_SCOPE_HOST
;
219 if (in_dev
->ifa_list
== NULL
)
221 if (IN_DEV_RPFILTER(in_dev
))
223 key
.oif
= dev
->ifindex
;
224 if (fib_lookup(&key
, &res
) == 0 && res
.type
== RTN_UNICAST
) {
225 *spec_dst
= FIB_RES_PREFSRC(res
);
226 return FIB_RES_NH(res
).nh_scope
>= RT_SCOPE_HOST
;
231 if (IN_DEV_RPFILTER(in_dev
))
233 *spec_dst
= inet_select_addr(dev
, 0, RT_SCOPE_UNIVERSE
);
238 #ifndef CONFIG_IP_NOSIOCRT
241 * Handle IP routing ioctl calls. These are used to manipulate the routing tables
244 int ip_rt_ioctl(unsigned int cmd
, void *arg
)
255 case SIOCADDRT
: /* Add a route */
256 case SIOCDELRT
: /* Delete a route */
257 if (!capable(CAP_NET_ADMIN
))
259 if (copy_from_user(&r
, arg
, sizeof(struct rtentry
)))
262 err
= fib_convert_rtentry(cmd
, &req
.nlh
, &req
.rtm
, &rta
, &r
);
264 if (cmd
== SIOCDELRT
) {
265 struct fib_table
*tb
= fib_get_table(req
.rtm
.rtm_table
);
268 err
= tb
->tb_delete(tb
, &req
.rtm
, &rta
, &req
.nlh
, NULL
);
270 struct fib_table
*tb
= fib_new_table(req
.rtm
.rtm_table
);
273 err
= tb
->tb_insert(tb
, &req
.rtm
, &rta
, &req
.nlh
, NULL
);
286 int ip_rt_ioctl(unsigned int cmd
, void *arg
)
293 #ifdef CONFIG_RTNETLINK
295 static int inet_check_attr(struct rtmsg
*r
, struct rtattr
**rta
)
299 for (i
=1; i
<=RTA_MAX
; i
++) {
300 struct rtattr
*attr
= rta
[i
-1];
302 if (RTA_PAYLOAD(attr
) < 4)
304 if (i
!= RTA_MULTIPATH
&& i
!= RTA_METRICS
)
305 rta
[i
-1] = (struct rtattr
*)RTA_DATA(attr
);
311 int inet_rtm_delroute(struct sk_buff
*skb
, struct nlmsghdr
* nlh
, void *arg
)
313 struct fib_table
* tb
;
314 struct rtattr
**rta
= arg
;
315 struct rtmsg
*r
= NLMSG_DATA(nlh
);
317 if (inet_check_attr(r
, rta
))
320 tb
= fib_get_table(r
->rtm_table
);
322 return tb
->tb_delete(tb
, r
, (struct kern_rta
*)rta
, nlh
, &NETLINK_CB(skb
));
326 int inet_rtm_newroute(struct sk_buff
*skb
, struct nlmsghdr
* nlh
, void *arg
)
328 struct fib_table
* tb
;
329 struct rtattr
**rta
= arg
;
330 struct rtmsg
*r
= NLMSG_DATA(nlh
);
332 if (inet_check_attr(r
, rta
))
335 tb
= fib_new_table(r
->rtm_table
);
337 return tb
->tb_insert(tb
, r
, (struct kern_rta
*)rta
, nlh
, &NETLINK_CB(skb
));
341 int inet_dump_fib(struct sk_buff
*skb
, struct netlink_callback
*cb
)
345 struct fib_table
*tb
;
347 if (NLMSG_PAYLOAD(cb
->nlh
, 0) >= sizeof(struct rtmsg
) &&
348 ((struct rtmsg
*)NLMSG_DATA(cb
->nlh
))->rtm_flags
&RTM_F_CLONED
)
349 return ip_rt_dump(skb
, cb
);
353 s_t
= cb
->args
[0] = RT_TABLE_MIN
;
355 for (t
=s_t
; t
<=RT_TABLE_MAX
; t
++) {
356 if (t
< s_t
) continue;
358 memset(&cb
->args
[1], 0, sizeof(cb
->args
)-sizeof(cb
->args
[0]));
359 if ((tb
= fib_get_table(t
))==NULL
)
361 if (tb
->tb_dump(tb
, skb
, cb
) < 0)
372 /* Prepare and feed intra-kernel routing request.
373 Really, it should be netlink message, but :-( netlink
374 can be not configured, so that we feed it directly
375 to fib engine. It is legal, because all events occur
376 only when netlink is already locked.
379 static void fib_magic(int cmd
, int type
, u32 dst
, int dst_len
, struct in_ifaddr
*ifa
)
381 struct fib_table
* tb
;
388 memset(&req
.rtm
, 0, sizeof(req
.rtm
));
389 memset(&rta
, 0, sizeof(rta
));
391 if (type
== RTN_UNICAST
)
392 tb
= fib_new_table(RT_TABLE_MAIN
);
394 tb
= fib_new_table(RT_TABLE_LOCAL
);
399 req
.nlh
.nlmsg_len
= sizeof(req
);
400 req
.nlh
.nlmsg_type
= cmd
;
401 req
.nlh
.nlmsg_flags
= NLM_F_REQUEST
|NLM_F_CREATE
|NLM_F_APPEND
;
402 req
.nlh
.nlmsg_pid
= 0;
403 req
.nlh
.nlmsg_seq
= 0;
405 req
.rtm
.rtm_dst_len
= dst_len
;
406 req
.rtm
.rtm_table
= tb
->tb_id
;
407 req
.rtm
.rtm_protocol
= RTPROT_KERNEL
;
408 req
.rtm
.rtm_scope
= (type
!= RTN_LOCAL
? RT_SCOPE_LINK
: RT_SCOPE_HOST
);
409 req
.rtm
.rtm_type
= type
;
412 rta
.rta_prefsrc
= &ifa
->ifa_local
;
413 rta
.rta_oif
= &ifa
->ifa_dev
->dev
->ifindex
;
415 if (cmd
== RTM_NEWROUTE
)
416 tb
->tb_insert(tb
, &req
.rtm
, &rta
, &req
.nlh
, NULL
);
418 tb
->tb_delete(tb
, &req
.rtm
, &rta
, &req
.nlh
, NULL
);
421 static void fib_add_ifaddr(struct in_ifaddr
*ifa
)
423 struct in_device
*in_dev
= ifa
->ifa_dev
;
424 struct device
*dev
= in_dev
->dev
;
425 struct in_ifaddr
*prim
= ifa
;
426 u32 mask
= ifa
->ifa_mask
;
427 u32 addr
= ifa
->ifa_local
;
428 u32 prefix
= ifa
->ifa_address
&mask
;
430 if (ifa
->ifa_flags
&IFA_F_SECONDARY
) {
431 prim
= inet_ifa_byprefix(in_dev
, prefix
, mask
);
433 printk(KERN_DEBUG
"fib_add_ifaddr: bug: prim == NULL\n");
438 fib_magic(RTM_NEWROUTE
, RTN_LOCAL
, addr
, 32, prim
);
440 if (!(dev
->flags
&IFF_UP
))
443 /* Add broadcast address, if it is explicitly assigned. */
444 if (ifa
->ifa_broadcast
&& ifa
->ifa_broadcast
!= 0xFFFFFFFF)
445 fib_magic(RTM_NEWROUTE
, RTN_BROADCAST
, ifa
->ifa_broadcast
, 32, prim
);
447 if (!ZERONET(prefix
) && !(ifa
->ifa_flags
&IFA_F_SECONDARY
) &&
448 (prefix
!= addr
|| ifa
->ifa_prefixlen
< 32)) {
449 fib_magic(RTM_NEWROUTE
, dev
->flags
&IFF_LOOPBACK
? RTN_LOCAL
:
450 RTN_UNICAST
, prefix
, ifa
->ifa_prefixlen
, prim
);
452 /* Add network specific broadcasts, when it takes a sense */
453 if (ifa
->ifa_prefixlen
< 31) {
454 fib_magic(RTM_NEWROUTE
, RTN_BROADCAST
, prefix
, 32, prim
);
455 fib_magic(RTM_NEWROUTE
, RTN_BROADCAST
, prefix
|~mask
, 32, prim
);
460 static void fib_del_ifaddr(struct in_ifaddr
*ifa
)
462 struct in_device
*in_dev
= ifa
->ifa_dev
;
463 struct device
*dev
= in_dev
->dev
;
464 struct in_ifaddr
*ifa1
;
465 struct in_ifaddr
*prim
= ifa
;
466 u32 brd
= ifa
->ifa_address
|~ifa
->ifa_mask
;
467 u32 any
= ifa
->ifa_address
&ifa
->ifa_mask
;
474 if (!(ifa
->ifa_flags
&IFA_F_SECONDARY
))
475 fib_magic(RTM_DELROUTE
, dev
->flags
&IFF_LOOPBACK
? RTN_LOCAL
:
476 RTN_UNICAST
, any
, ifa
->ifa_prefixlen
, prim
);
478 prim
= inet_ifa_byprefix(in_dev
, any
, ifa
->ifa_mask
);
480 printk(KERN_DEBUG
"fib_del_ifaddr: bug: prim == NULL\n");
485 /* Deletion is more complicated than add.
486 We should take care of not to delete too much :-)
488 Scan address list to be sure that addresses are really gone.
491 for (ifa1
= in_dev
->ifa_list
; ifa1
; ifa1
= ifa1
->ifa_next
) {
492 if (ifa
->ifa_local
== ifa1
->ifa_local
)
494 if (ifa
->ifa_broadcast
== ifa1
->ifa_broadcast
)
496 if (brd
== ifa1
->ifa_broadcast
)
498 if (any
== ifa1
->ifa_broadcast
)
503 fib_magic(RTM_DELROUTE
, RTN_BROADCAST
, ifa
->ifa_broadcast
, 32, prim
);
505 fib_magic(RTM_DELROUTE
, RTN_BROADCAST
, brd
, 32, prim
);
507 fib_magic(RTM_DELROUTE
, RTN_BROADCAST
, any
, 32, prim
);
508 if (!(ok
&LOCAL_OK
)) {
509 fib_magic(RTM_DELROUTE
, RTN_LOCAL
, ifa
->ifa_local
, 32, prim
);
511 /* Check, that this local address finally disappeared. */
512 if (inet_addr_type(ifa
->ifa_local
) != RTN_LOCAL
) {
513 /* And the last, but not the least thing.
514 We must flush stray FIB entries.
516 First of all, we scan fib_info list searching
517 for stray nexthop entries, then ignite fib_flush.
519 if (fib_sync_down(ifa
->ifa_local
, NULL
, 0))
529 static void fib_disable_ip(struct device
*dev
, int force
)
531 if (fib_sync_down(0, dev
, force
))
537 static int fib_inetaddr_event(struct notifier_block
*this, unsigned long event
, void *ptr
)
539 struct in_ifaddr
*ifa
= (struct in_ifaddr
*)ptr
;
547 if (ifa
->ifa_dev
&& ifa
->ifa_dev
->ifa_list
== NULL
) {
548 /* Last address was deleted from this interface.
551 fib_disable_ip(ifa
->ifa_dev
->dev
, 1);
561 static int fib_netdev_event(struct notifier_block
*this, unsigned long event
, void *ptr
)
563 struct device
*dev
= ptr
;
564 struct in_device
*in_dev
= dev
->ip_ptr
;
573 } endfor_ifa(in_dev
);
574 #ifdef CONFIG_IP_ROUTE_MULTIPATH
580 fib_disable_ip(dev
, 0);
582 case NETDEV_UNREGISTER
:
583 fib_disable_ip(dev
, 1);
585 case NETDEV_CHANGEMTU
:
593 struct notifier_block fib_inetaddr_notifier
= {
599 struct notifier_block fib_netdev_notifier
= {
605 __initfunc(void ip_fib_init(void))
607 #ifdef CONFIG_PROC_FS
608 proc_net_register(&(struct proc_dir_entry
) {
609 PROC_NET_ROUTE
, 5, "route",
610 S_IFREG
| S_IRUGO
, 1, 0, 0,
611 0, &proc_net_inode_operations
,
614 #endif /* CONFIG_PROC_FS */
616 #ifndef CONFIG_IP_MULTIPLE_TABLES
617 local_table
= fib_hash_init(RT_TABLE_LOCAL
);
618 main_table
= fib_hash_init(RT_TABLE_MAIN
);
623 register_netdevice_notifier(&fib_netdev_notifier
);
624 register_inetaddr_notifier(&fib_inetaddr_notifier
);