2005-04-17 02:20:36 +04:00
/*
* INET An implementation of the TCP / IP protocol suite for the LINUX
* operating system . INET is implemented using the BSD Socket
* interface as the means of communication with the user level .
*
* IPv4 Forwarding Information Base : FIB frontend .
*
* Version : $ Id : fib_frontend . c , v 1.26 2001 / 10 / 31 21 : 55 : 54 davem Exp $
*
* Authors : Alexey Kuznetsov , < kuznet @ ms2 . inr . ac . ru >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/module.h>
# include <asm/uaccess.h>
# include <asm/system.h>
# include <linux/bitops.h>
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/string.h>
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/errno.h>
# include <linux/in.h>
# include <linux/inet.h>
2005-12-27 07:43:12 +03:00
# include <linux/inetdevice.h>
2005-04-17 02:20:36 +04:00
# include <linux/netdevice.h>
2006-08-05 10:04:54 +04:00
# include <linux/if_addr.h>
2005-04-17 02:20:36 +04:00
# include <linux/if_arp.h>
# include <linux/skbuff.h>
# include <linux/init.h>
2006-08-11 10:10:46 +04:00
# include <linux/list.h>
2005-04-17 02:20:36 +04:00
# include <net/ip.h>
# include <net/protocol.h>
# include <net/route.h>
# include <net/tcp.h>
# include <net/sock.h>
# include <net/icmp.h>
# include <net/arp.h>
# include <net/ip_fib.h>
2007-03-22 21:55:17 +03:00
# include <net/rtnetlink.h>
2005-04-17 02:20:36 +04:00
# ifndef CONFIG_IP_MULTIPLE_TABLES
2008-01-10 14:22:17 +03:00
static int __net_init fib4_rules_init ( struct net * net )
2007-11-07 10:34:04 +03:00
{
2008-01-10 14:23:38 +03:00
struct fib_table * local_table , * main_table ;
2008-01-15 10:14:20 +03:00
local_table = fib_hash_table ( RT_TABLE_LOCAL ) ;
2008-01-10 14:23:38 +03:00
if ( local_table = = NULL )
2008-01-10 14:21:49 +03:00
return - ENOMEM ;
2008-01-15 10:14:20 +03:00
main_table = fib_hash_table ( RT_TABLE_MAIN ) ;
2008-01-10 14:23:38 +03:00
if ( main_table = = NULL )
2008-01-10 14:21:49 +03:00
goto fail ;
2008-01-10 14:23:38 +03:00
hlist_add_head_rcu ( & local_table - > tb_hlist ,
2008-01-10 14:28:24 +03:00
& net - > ipv4 . fib_table_hash [ TABLE_LOCAL_INDEX ] ) ;
2008-01-10 14:23:38 +03:00
hlist_add_head_rcu ( & main_table - > tb_hlist ,
2008-01-10 14:28:24 +03:00
& net - > ipv4 . fib_table_hash [ TABLE_MAIN_INDEX ] ) ;
2008-01-10 14:21:49 +03:00
return 0 ;
fail :
2008-01-10 14:23:38 +03:00
kfree ( local_table ) ;
2008-01-10 14:21:49 +03:00
return - ENOMEM ;
2007-11-07 10:34:04 +03:00
}
2006-08-11 10:10:46 +04:00
# else
2005-04-17 02:20:36 +04:00
2008-01-10 14:24:11 +03:00
struct fib_table * fib_new_table ( struct net * net , u32 id )
2005-04-17 02:20:36 +04:00
{
struct fib_table * tb ;
2006-08-11 10:10:46 +04:00
unsigned int h ;
2005-04-17 02:20:36 +04:00
2006-08-11 10:10:46 +04:00
if ( id = = 0 )
id = RT_TABLE_MAIN ;
2008-01-10 14:24:11 +03:00
tb = fib_get_table ( net , id ) ;
2006-08-11 10:10:46 +04:00
if ( tb )
return tb ;
2008-01-15 10:14:20 +03:00
tb = fib_hash_table ( id ) ;
2005-04-17 02:20:36 +04:00
if ( ! tb )
return NULL ;
2006-08-11 10:10:46 +04:00
h = id & ( FIB_TABLE_HASHSZ - 1 ) ;
2008-01-10 14:28:24 +03:00
hlist_add_head_rcu ( & tb - > tb_hlist , & net - > ipv4 . fib_table_hash [ h ] ) ;
2005-04-17 02:20:36 +04:00
return tb ;
}
2008-01-10 14:24:11 +03:00
struct fib_table * fib_get_table ( struct net * net , u32 id )
2006-08-11 10:10:46 +04:00
{
struct fib_table * tb ;
struct hlist_node * node ;
2008-01-10 14:28:24 +03:00
struct hlist_head * head ;
2006-08-11 10:10:46 +04:00
unsigned int h ;
2005-04-17 02:20:36 +04:00
2006-08-11 10:10:46 +04:00
if ( id = = 0 )
id = RT_TABLE_MAIN ;
h = id & ( FIB_TABLE_HASHSZ - 1 ) ;
2008-01-10 14:28:24 +03:00
2006-08-11 10:10:46 +04:00
rcu_read_lock ( ) ;
2008-01-10 14:28:24 +03:00
head = & net - > ipv4 . fib_table_hash [ h ] ;
hlist_for_each_entry_rcu ( tb , node , head , tb_hlist ) {
2006-08-11 10:10:46 +04:00
if ( tb - > tb_id = = id ) {
rcu_read_unlock ( ) ;
return tb ;
}
}
rcu_read_unlock ( ) ;
return NULL ;
}
2005-04-17 02:20:36 +04:00
# endif /* CONFIG_IP_MULTIPLE_TABLES */
2008-01-10 14:28:24 +03:00
static void fib_flush ( struct net * net )
2005-04-17 02:20:36 +04:00
{
int flushed = 0 ;
struct fib_table * tb ;
2006-08-11 10:10:46 +04:00
struct hlist_node * node ;
2008-01-10 14:28:24 +03:00
struct hlist_head * head ;
2006-08-11 10:10:46 +04:00
unsigned int h ;
2005-04-17 02:20:36 +04:00
2006-08-11 10:10:46 +04:00
for ( h = 0 ; h < FIB_TABLE_HASHSZ ; h + + ) {
2008-01-10 14:28:24 +03:00
head = & net - > ipv4 . fib_table_hash [ h ] ;
hlist_for_each_entry ( tb , node , head , tb_hlist )
2006-08-11 10:10:46 +04:00
flushed + = tb - > tb_flush ( tb ) ;
2005-04-17 02:20:36 +04:00
}
if ( flushed )
rt_cache_flush ( - 1 ) ;
}
/*
* Find the first device with a given source address .
*/
2006-09-27 09:17:09 +04:00
struct net_device * ip_dev_find ( __be32 addr )
2005-04-17 02:20:36 +04:00
{
struct flowi fl = { . nl_u = { . ip4_u = { . daddr = addr } } } ;
struct fib_result res ;
struct net_device * dev = NULL ;
2007-10-24 08:17:27 +04:00
struct fib_table * local_table ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_IP_MULTIPLE_TABLES
res . r = NULL ;
# endif
2008-01-10 14:24:11 +03:00
local_table = fib_get_table ( & init_net , RT_TABLE_LOCAL ) ;
2007-10-24 08:17:27 +04:00
if ( ! local_table | | local_table - > tb_lookup ( local_table , & fl , & res ) )
2005-04-17 02:20:36 +04:00
return NULL ;
if ( res . type ! = RTN_LOCAL )
goto out ;
dev = FIB_RES_DEV ( res ) ;
if ( dev )
dev_hold ( dev ) ;
out :
fib_res_put ( & res ) ;
return dev ;
}
2007-12-05 10:28:46 +03:00
/*
* Find address type as if only " dev " was present in the system . If
* on_dev is NULL then all interfaces are taken into consideration .
*/
2008-01-10 14:25:28 +03:00
static inline unsigned __inet_dev_addr_type ( struct net * net ,
const struct net_device * dev ,
2007-12-05 10:28:46 +03:00
__be32 addr )
2005-04-17 02:20:36 +04:00
{
struct flowi fl = { . nl_u = { . ip4_u = { . daddr = addr } } } ;
struct fib_result res ;
unsigned ret = RTN_BROADCAST ;
2007-10-24 08:17:27 +04:00
struct fib_table * local_table ;
2005-04-17 02:20:36 +04:00
2008-01-21 14:18:08 +03:00
if ( ipv4_is_zeronet ( addr ) | | ipv4_is_lbcast ( addr ) )
2005-04-17 02:20:36 +04:00
return RTN_BROADCAST ;
2007-12-17 00:45:43 +03:00
if ( ipv4_is_multicast ( addr ) )
2005-04-17 02:20:36 +04:00
return RTN_MULTICAST ;
# ifdef CONFIG_IP_MULTIPLE_TABLES
res . r = NULL ;
# endif
2007-02-09 17:24:47 +03:00
2008-01-10 14:25:28 +03:00
local_table = fib_get_table ( net , RT_TABLE_LOCAL ) ;
2007-10-24 08:17:27 +04:00
if ( local_table ) {
2005-04-17 02:20:36 +04:00
ret = RTN_UNICAST ;
2007-10-24 08:17:27 +04:00
if ( ! local_table - > tb_lookup ( local_table , & fl , & res ) ) {
2007-12-05 10:28:46 +03:00
if ( ! dev | | dev = = res . fi - > fib_dev )
ret = res . type ;
2005-04-17 02:20:36 +04:00
fib_res_put ( & res ) ;
}
}
return ret ;
}
2008-01-10 14:25:28 +03:00
unsigned int inet_addr_type ( struct net * net , __be32 addr )
2007-12-05 10:28:46 +03:00
{
2008-01-10 14:25:28 +03:00
return __inet_dev_addr_type ( net , NULL , addr ) ;
2007-12-05 10:28:46 +03:00
}
2008-01-10 14:25:28 +03:00
unsigned int inet_dev_addr_type ( struct net * net , const struct net_device * dev ,
__be32 addr )
2007-12-05 10:28:46 +03:00
{
2008-01-10 14:25:28 +03:00
return __inet_dev_addr_type ( net , dev , addr ) ;
2007-12-05 10:28:46 +03:00
}
2005-04-17 02:20:36 +04:00
/* Given (packet source, input interface) and optional (dst, oif, tos):
- ( main ) check , that source is valid i . e . not broadcast or our local
address .
- figure out what " logical " interface this packet arrived
and calculate " specific destination " address .
- check , that packet arrived from expected physical interface .
*/
2006-09-27 08:28:14 +04:00
int fib_validate_source ( __be32 src , __be32 dst , u8 tos , int oif ,
struct net_device * dev , __be32 * spec_dst , u32 * itag )
2005-04-17 02:20:36 +04:00
{
struct in_device * in_dev ;
struct flowi fl = { . nl_u = { . ip4_u =
{ . daddr = src ,
. saddr = dst ,
. tos = tos } } ,
. iif = oif } ;
struct fib_result res ;
int no_addr , rpf ;
int ret ;
2008-01-22 04:33:15 +03:00
struct net * net ;
2005-04-17 02:20:36 +04:00
no_addr = rpf = 0 ;
rcu_read_lock ( ) ;
2005-10-04 01:35:55 +04:00
in_dev = __in_dev_get_rcu ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( in_dev ) {
no_addr = in_dev - > ifa_list = = NULL ;
rpf = IN_DEV_RPFILTER ( in_dev ) ;
}
rcu_read_unlock ( ) ;
if ( in_dev = = NULL )
goto e_inval ;
2008-01-22 04:33:15 +03:00
net = dev - > nd_net ;
if ( fib_lookup ( net , & fl , & res ) )
2005-04-17 02:20:36 +04:00
goto last_resort ;
if ( res . type ! = RTN_UNICAST )
goto e_inval_res ;
* spec_dst = FIB_RES_PREFSRC ( res ) ;
fib_combine_itag ( itag , & res ) ;
# ifdef CONFIG_IP_ROUTE_MULTIPATH
if ( FIB_RES_DEV ( res ) = = dev | | res . fi - > fib_nhs > 1 )
# else
if ( FIB_RES_DEV ( res ) = = dev )
# endif
{
ret = FIB_RES_NH ( res ) . nh_scope > = RT_SCOPE_HOST ;
fib_res_put ( & res ) ;
return ret ;
}
fib_res_put ( & res ) ;
if ( no_addr )
goto last_resort ;
if ( rpf )
goto e_inval ;
fl . oif = dev - > ifindex ;
ret = 0 ;
2008-01-22 04:33:15 +03:00
if ( fib_lookup ( net , & fl , & res ) = = 0 ) {
2005-04-17 02:20:36 +04:00
if ( res . type = = RTN_UNICAST ) {
* spec_dst = FIB_RES_PREFSRC ( res ) ;
ret = FIB_RES_NH ( res ) . nh_scope > = RT_SCOPE_HOST ;
}
fib_res_put ( & res ) ;
}
return ret ;
last_resort :
if ( rpf )
goto e_inval ;
* spec_dst = inet_select_addr ( dev , 0 , RT_SCOPE_UNIVERSE ) ;
* itag = 0 ;
return 0 ;
e_inval_res :
fib_res_put ( & res ) ;
e_inval :
return - EINVAL ;
}
2006-09-28 05:40:00 +04:00
static inline __be32 sk_extract_addr ( struct sockaddr * addr )
2006-08-18 05:14:52 +04:00
{
return ( ( struct sockaddr_in * ) addr ) - > sin_addr . s_addr ;
}
static int put_rtax ( struct nlattr * mx , int len , int type , u32 value )
{
struct nlattr * nla ;
nla = ( struct nlattr * ) ( ( char * ) mx + len ) ;
nla - > nla_type = type ;
nla - > nla_len = nla_attr_size ( 4 ) ;
* ( u32 * ) nla_data ( nla ) = value ;
return len + nla_total_size ( 4 ) ;
}
2008-01-10 14:29:23 +03:00
static int rtentry_to_fib_config ( struct net * net , int cmd , struct rtentry * rt ,
2006-08-18 05:14:52 +04:00
struct fib_config * cfg )
{
2006-09-27 09:15:46 +04:00
__be32 addr ;
2006-08-18 05:14:52 +04:00
int plen ;
memset ( cfg , 0 , sizeof ( * cfg ) ) ;
2008-01-10 14:29:23 +03:00
cfg - > fc_nlinfo . nl_net = net ;
2006-08-18 05:14:52 +04:00
if ( rt - > rt_dst . sa_family ! = AF_INET )
return - EAFNOSUPPORT ;
/*
* Check mask for validity :
* a ) it must be contiguous .
* b ) destination must have all host bits clear .
* c ) if application forgot to set correct family ( AF_INET ) ,
* reject request unless it is absolutely clear i . e .
* both family and mask are zero .
*/
plen = 32 ;
addr = sk_extract_addr ( & rt - > rt_dst ) ;
if ( ! ( rt - > rt_flags & RTF_HOST ) ) {
2006-09-28 05:40:00 +04:00
__be32 mask = sk_extract_addr ( & rt - > rt_genmask ) ;
2006-08-18 05:14:52 +04:00
if ( rt - > rt_genmask . sa_family ! = AF_INET ) {
if ( mask | | rt - > rt_genmask . sa_family )
return - EAFNOSUPPORT ;
}
if ( bad_mask ( mask , addr ) )
return - EINVAL ;
plen = inet_mask_len ( mask ) ;
}
cfg - > fc_dst_len = plen ;
cfg - > fc_dst = addr ;
if ( cmd ! = SIOCDELRT ) {
cfg - > fc_nlflags = NLM_F_CREATE ;
cfg - > fc_protocol = RTPROT_BOOT ;
}
if ( rt - > rt_metric )
cfg - > fc_priority = rt - > rt_metric - 1 ;
if ( rt - > rt_flags & RTF_REJECT ) {
cfg - > fc_scope = RT_SCOPE_HOST ;
cfg - > fc_type = RTN_UNREACHABLE ;
return 0 ;
}
cfg - > fc_scope = RT_SCOPE_NOWHERE ;
cfg - > fc_type = RTN_UNICAST ;
if ( rt - > rt_dev ) {
char * colon ;
struct net_device * dev ;
char devname [ IFNAMSIZ ] ;
if ( copy_from_user ( devname , rt - > rt_dev , IFNAMSIZ - 1 ) )
return - EFAULT ;
devname [ IFNAMSIZ - 1 ] = 0 ;
colon = strchr ( devname , ' : ' ) ;
if ( colon )
* colon = 0 ;
2008-01-10 14:29:23 +03:00
dev = __dev_get_by_name ( net , devname ) ;
2006-08-18 05:14:52 +04:00
if ( ! dev )
return - ENODEV ;
cfg - > fc_oif = dev - > ifindex ;
if ( colon ) {
struct in_ifaddr * ifa ;
struct in_device * in_dev = __in_dev_get_rtnl ( dev ) ;
if ( ! in_dev )
return - ENODEV ;
* colon = ' : ' ;
for ( ifa = in_dev - > ifa_list ; ifa ; ifa = ifa - > ifa_next )
if ( strcmp ( ifa - > ifa_label , devname ) = = 0 )
break ;
if ( ifa = = NULL )
return - ENODEV ;
cfg - > fc_prefsrc = ifa - > ifa_local ;
}
}
addr = sk_extract_addr ( & rt - > rt_gateway ) ;
if ( rt - > rt_gateway . sa_family = = AF_INET & & addr ) {
cfg - > fc_gw = addr ;
if ( rt - > rt_flags & RTF_GATEWAY & &
2008-01-10 14:29:23 +03:00
inet_addr_type ( net , addr ) = = RTN_UNICAST )
2006-08-18 05:14:52 +04:00
cfg - > fc_scope = RT_SCOPE_UNIVERSE ;
}
if ( cmd = = SIOCDELRT )
return 0 ;
if ( rt - > rt_flags & RTF_GATEWAY & & ! cfg - > fc_gw )
return - EINVAL ;
if ( cfg - > fc_scope = = RT_SCOPE_NOWHERE )
cfg - > fc_scope = RT_SCOPE_LINK ;
if ( rt - > rt_flags & ( RTF_MTU | RTF_WINDOW | RTF_IRTT ) ) {
struct nlattr * mx ;
int len = 0 ;
mx = kzalloc ( 3 * nla_total_size ( 4 ) , GFP_KERNEL ) ;
2007-02-09 17:24:47 +03:00
if ( mx = = NULL )
2006-08-18 05:14:52 +04:00
return - ENOMEM ;
if ( rt - > rt_flags & RTF_MTU )
len = put_rtax ( mx , len , RTAX_ADVMSS , rt - > rt_mtu - 40 ) ;
if ( rt - > rt_flags & RTF_WINDOW )
len = put_rtax ( mx , len , RTAX_WINDOW , rt - > rt_window ) ;
if ( rt - > rt_flags & RTF_IRTT )
len = put_rtax ( mx , len , RTAX_RTT , rt - > rt_irtt < < 3 ) ;
cfg - > fc_mx = mx ;
cfg - > fc_mx_len = len ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Handle IP routing ioctl calls . These are used to manipulate the routing tables
*/
2007-02-09 17:24:47 +03:00
2008-01-10 14:29:53 +03:00
int ip_rt_ioctl ( struct net * net , unsigned int cmd , void __user * arg )
2005-04-17 02:20:36 +04:00
{
2006-08-18 05:14:52 +04:00
struct fib_config cfg ;
struct rtentry rt ;
2005-04-17 02:20:36 +04:00
int err ;
switch ( cmd ) {
case SIOCADDRT : /* Add a route */
case SIOCDELRT : /* Delete a route */
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
2006-08-18 05:14:52 +04:00
if ( copy_from_user ( & rt , arg , sizeof ( rt ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-08-18 05:14:52 +04:00
2005-04-17 02:20:36 +04:00
rtnl_lock ( ) ;
2008-01-10 14:29:53 +03:00
err = rtentry_to_fib_config ( net , cmd , & rt , & cfg ) ;
2005-04-17 02:20:36 +04:00
if ( err = = 0 ) {
2006-08-18 05:14:52 +04:00
struct fib_table * tb ;
2005-04-17 02:20:36 +04:00
if ( cmd = = SIOCDELRT ) {
2008-01-10 14:29:53 +03:00
tb = fib_get_table ( net , cfg . fc_table ) ;
2005-04-17 02:20:36 +04:00
if ( tb )
2006-08-18 05:14:52 +04:00
err = tb - > tb_delete ( tb , & cfg ) ;
else
err = - ESRCH ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-10 14:29:53 +03:00
tb = fib_new_table ( net , cfg . fc_table ) ;
2005-04-17 02:20:36 +04:00
if ( tb )
2006-08-18 05:14:52 +04:00
err = tb - > tb_insert ( tb , & cfg ) ;
else
err = - ENOBUFS ;
2005-04-17 02:20:36 +04:00
}
2006-08-18 05:14:52 +04:00
/* allocated by rtentry_to_fib_config() */
kfree ( cfg . fc_mx ) ;
2005-04-17 02:20:36 +04:00
}
rtnl_unlock ( ) ;
return err ;
}
return - EINVAL ;
}
2007-06-05 23:38:30 +04:00
const struct nla_policy rtm_ipv4_policy [ RTA_MAX + 1 ] = {
2006-08-18 05:14:52 +04:00
[ RTA_DST ] = { . type = NLA_U32 } ,
[ RTA_SRC ] = { . type = NLA_U32 } ,
[ RTA_IIF ] = { . type = NLA_U32 } ,
[ RTA_OIF ] = { . type = NLA_U32 } ,
[ RTA_GATEWAY ] = { . type = NLA_U32 } ,
[ RTA_PRIORITY ] = { . type = NLA_U32 } ,
[ RTA_PREFSRC ] = { . type = NLA_U32 } ,
[ RTA_METRICS ] = { . type = NLA_NESTED } ,
2006-08-27 07:13:18 +04:00
[ RTA_MULTIPATH ] = { . len = sizeof ( struct rtnexthop ) } ,
2006-08-18 05:14:52 +04:00
[ RTA_PROTOINFO ] = { . type = NLA_U32 } ,
[ RTA_FLOW ] = { . type = NLA_U32 } ,
} ;
2008-01-10 14:29:23 +03:00
static int rtm_to_fib_config ( struct net * net , struct sk_buff * skb ,
struct nlmsghdr * nlh , struct fib_config * cfg )
2005-04-17 02:20:36 +04:00
{
2006-08-18 05:14:52 +04:00
struct nlattr * attr ;
int err , remaining ;
struct rtmsg * rtm ;
err = nlmsg_validate ( nlh , sizeof ( * rtm ) , RTA_MAX , rtm_ipv4_policy ) ;
if ( err < 0 )
goto errout ;
memset ( cfg , 0 , sizeof ( * cfg ) ) ;
rtm = nlmsg_data ( nlh ) ;
cfg - > fc_dst_len = rtm - > rtm_dst_len ;
cfg - > fc_tos = rtm - > rtm_tos ;
cfg - > fc_table = rtm - > rtm_table ;
cfg - > fc_protocol = rtm - > rtm_protocol ;
cfg - > fc_scope = rtm - > rtm_scope ;
cfg - > fc_type = rtm - > rtm_type ;
cfg - > fc_flags = rtm - > rtm_flags ;
cfg - > fc_nlflags = nlh - > nlmsg_flags ;
cfg - > fc_nlinfo . pid = NETLINK_CB ( skb ) . pid ;
cfg - > fc_nlinfo . nlh = nlh ;
2008-01-10 14:29:23 +03:00
cfg - > fc_nlinfo . nl_net = net ;
2006-08-18 05:14:52 +04:00
2007-03-25 07:32:54 +04:00
if ( cfg - > fc_type > RTN_MAX ) {
err = - EINVAL ;
goto errout ;
}
2006-08-18 05:14:52 +04:00
nlmsg_for_each_attr ( attr , nlh , sizeof ( struct rtmsg ) , remaining ) {
2007-09-12 16:44:36 +04:00
switch ( nla_type ( attr ) ) {
2006-08-18 05:14:52 +04:00
case RTA_DST :
2006-09-27 09:15:25 +04:00
cfg - > fc_dst = nla_get_be32 ( attr ) ;
2006-08-18 05:14:52 +04:00
break ;
case RTA_OIF :
cfg - > fc_oif = nla_get_u32 ( attr ) ;
break ;
case RTA_GATEWAY :
2006-09-27 09:15:25 +04:00
cfg - > fc_gw = nla_get_be32 ( attr ) ;
2006-08-18 05:14:52 +04:00
break ;
case RTA_PRIORITY :
cfg - > fc_priority = nla_get_u32 ( attr ) ;
break ;
case RTA_PREFSRC :
2006-09-27 09:15:25 +04:00
cfg - > fc_prefsrc = nla_get_be32 ( attr ) ;
2006-08-18 05:14:52 +04:00
break ;
case RTA_METRICS :
cfg - > fc_mx = nla_data ( attr ) ;
cfg - > fc_mx_len = nla_len ( attr ) ;
break ;
case RTA_MULTIPATH :
cfg - > fc_mp = nla_data ( attr ) ;
cfg - > fc_mp_len = nla_len ( attr ) ;
break ;
case RTA_FLOW :
cfg - > fc_flow = nla_get_u32 ( attr ) ;
break ;
case RTA_TABLE :
cfg - > fc_table = nla_get_u32 ( attr ) ;
break ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-18 05:14:52 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
2006-08-18 05:14:52 +04:00
errout :
return err ;
2005-04-17 02:20:36 +04:00
}
2007-03-22 21:55:17 +03:00
static int inet_rtm_delroute ( struct sk_buff * skb , struct nlmsghdr * nlh , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-11-30 16:21:31 +03:00
struct net * net = skb - > sk - > sk_net ;
2006-08-18 05:14:52 +04:00
struct fib_config cfg ;
struct fib_table * tb ;
int err ;
2005-04-17 02:20:36 +04:00
2008-01-10 14:29:23 +03:00
err = rtm_to_fib_config ( net , skb , nlh , & cfg ) ;
2006-08-18 05:14:52 +04:00
if ( err < 0 )
goto errout ;
2005-04-17 02:20:36 +04:00
2008-01-10 14:24:11 +03:00
tb = fib_get_table ( net , cfg . fc_table ) ;
2006-08-18 05:14:52 +04:00
if ( tb = = NULL ) {
err = - ESRCH ;
goto errout ;
}
err = tb - > tb_delete ( tb , & cfg ) ;
errout :
return err ;
2005-04-17 02:20:36 +04:00
}
2007-03-22 21:55:17 +03:00
static int inet_rtm_newroute ( struct sk_buff * skb , struct nlmsghdr * nlh , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-11-30 16:21:31 +03:00
struct net * net = skb - > sk - > sk_net ;
2006-08-18 05:14:52 +04:00
struct fib_config cfg ;
struct fib_table * tb ;
int err ;
2005-04-17 02:20:36 +04:00
2008-01-10 14:29:23 +03:00
err = rtm_to_fib_config ( net , skb , nlh , & cfg ) ;
2006-08-18 05:14:52 +04:00
if ( err < 0 )
goto errout ;
2005-04-17 02:20:36 +04:00
2008-01-10 14:30:24 +03:00
tb = fib_new_table ( net , cfg . fc_table ) ;
2006-08-18 05:14:52 +04:00
if ( tb = = NULL ) {
err = - ENOBUFS ;
goto errout ;
}
err = tb - > tb_insert ( tb , & cfg ) ;
errout :
return err ;
2005-04-17 02:20:36 +04:00
}
2007-03-22 21:55:17 +03:00
static int inet_dump_fib ( struct sk_buff * skb , struct netlink_callback * cb )
2005-04-17 02:20:36 +04:00
{
2007-11-30 16:21:31 +03:00
struct net * net = skb - > sk - > sk_net ;
2006-08-11 10:10:46 +04:00
unsigned int h , s_h ;
unsigned int e = 0 , s_e ;
2005-04-17 02:20:36 +04:00
struct fib_table * tb ;
2006-08-11 10:10:46 +04:00
struct hlist_node * node ;
2008-01-10 14:28:24 +03:00
struct hlist_head * head ;
2006-08-11 10:10:46 +04:00
int dumped = 0 ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:15:17 +04:00
if ( nlmsg_len ( cb - > nlh ) > = sizeof ( struct rtmsg ) & &
( ( struct rtmsg * ) nlmsg_data ( cb - > nlh ) ) - > rtm_flags & RTM_F_CLONED )
2005-04-17 02:20:36 +04:00
return ip_rt_dump ( skb , cb ) ;
2006-08-11 10:10:46 +04:00
s_h = cb - > args [ 0 ] ;
s_e = cb - > args [ 1 ] ;
for ( h = s_h ; h < FIB_TABLE_HASHSZ ; h + + , s_e = 0 ) {
e = 0 ;
2008-01-10 14:28:24 +03:00
head = & net - > ipv4 . fib_table_hash [ h ] ;
hlist_for_each_entry ( tb , node , head , tb_hlist ) {
2006-08-11 10:10:46 +04:00
if ( e < s_e )
goto next ;
if ( dumped )
memset ( & cb - > args [ 2 ] , 0 , sizeof ( cb - > args ) -
2007-02-09 17:24:47 +03:00
2 * sizeof ( cb - > args [ 0 ] ) ) ;
2006-08-11 10:10:46 +04:00
if ( tb - > tb_dump ( tb , skb , cb ) < 0 )
goto out ;
dumped = 1 ;
next :
e + + ;
}
2005-04-17 02:20:36 +04:00
}
2006-08-11 10:10:46 +04:00
out :
cb - > args [ 1 ] = e ;
cb - > args [ 0 ] = h ;
2005-04-17 02:20:36 +04:00
return skb - > len ;
}
/* Prepare and feed intra-kernel routing request.
Really , it should be netlink message , but : - ( netlink
can be not configured , so that we feed it directly
to fib engine . It is legal , because all events occur
only when netlink is already locked .
*/
2006-09-28 05:40:00 +04:00
static void fib_magic ( int cmd , int type , __be32 dst , int dst_len , struct in_ifaddr * ifa )
2005-04-17 02:20:36 +04:00
{
2008-01-10 14:29:23 +03:00
struct net * net = ifa - > ifa_dev - > dev - > nd_net ;
2006-08-18 05:14:52 +04:00
struct fib_table * tb ;
struct fib_config cfg = {
. fc_protocol = RTPROT_KERNEL ,
. fc_type = type ,
. fc_dst = dst ,
. fc_dst_len = dst_len ,
. fc_prefsrc = ifa - > ifa_local ,
. fc_oif = ifa - > ifa_dev - > dev - > ifindex ,
. fc_nlflags = NLM_F_CREATE | NLM_F_APPEND ,
2008-01-10 14:26:13 +03:00
. fc_nlinfo = {
2008-01-10 14:29:23 +03:00
. nl_net = net ,
2008-01-10 14:26:13 +03:00
} ,
2006-08-18 05:14:52 +04:00
} ;
2005-04-17 02:20:36 +04:00
if ( type = = RTN_UNICAST )
2008-01-10 14:29:23 +03:00
tb = fib_new_table ( net , RT_TABLE_MAIN ) ;
2005-04-17 02:20:36 +04:00
else
2008-01-10 14:29:23 +03:00
tb = fib_new_table ( net , RT_TABLE_LOCAL ) ;
2005-04-17 02:20:36 +04:00
if ( tb = = NULL )
return ;
2006-08-18 05:14:52 +04:00
cfg . fc_table = tb - > tb_id ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:14:52 +04:00
if ( type ! = RTN_LOCAL )
cfg . fc_scope = RT_SCOPE_LINK ;
else
cfg . fc_scope = RT_SCOPE_HOST ;
2005-04-17 02:20:36 +04:00
if ( cmd = = RTM_NEWROUTE )
2006-08-18 05:14:52 +04:00
tb - > tb_insert ( tb , & cfg ) ;
2005-04-17 02:20:36 +04:00
else
2006-08-18 05:14:52 +04:00
tb - > tb_delete ( tb , & cfg ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-23 01:47:37 +03:00
void fib_add_ifaddr ( struct in_ifaddr * ifa )
2005-04-17 02:20:36 +04:00
{
struct in_device * in_dev = ifa - > ifa_dev ;
struct net_device * dev = in_dev - > dev ;
struct in_ifaddr * prim = ifa ;
2006-09-29 05:00:55 +04:00
__be32 mask = ifa - > ifa_mask ;
__be32 addr = ifa - > ifa_local ;
__be32 prefix = ifa - > ifa_address & mask ;
2005-04-17 02:20:36 +04:00
if ( ifa - > ifa_flags & IFA_F_SECONDARY ) {
prim = inet_ifa_byprefix ( in_dev , prefix , mask ) ;
if ( prim = = NULL ) {
2008-01-13 07:58:35 +03:00
printk ( KERN_WARNING " fib_add_ifaddr: bug: prim == NULL \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
}
fib_magic ( RTM_NEWROUTE , RTN_LOCAL , addr , 32 , prim ) ;
if ( ! ( dev - > flags & IFF_UP ) )
return ;
/* Add broadcast address, if it is explicitly assigned. */
2006-09-29 05:00:55 +04:00
if ( ifa - > ifa_broadcast & & ifa - > ifa_broadcast ! = htonl ( 0xFFFFFFFF ) )
2005-04-17 02:20:36 +04:00
fib_magic ( RTM_NEWROUTE , RTN_BROADCAST , ifa - > ifa_broadcast , 32 , prim ) ;
2007-12-17 00:45:43 +03:00
if ( ! ipv4_is_zeronet ( prefix ) & & ! ( ifa - > ifa_flags & IFA_F_SECONDARY ) & &
2005-04-17 02:20:36 +04:00
( prefix ! = addr | | ifa - > ifa_prefixlen < 32 ) ) {
fib_magic ( RTM_NEWROUTE , dev - > flags & IFF_LOOPBACK ? RTN_LOCAL :
RTN_UNICAST , prefix , ifa - > ifa_prefixlen , prim ) ;
/* Add network specific broadcasts, when it takes a sense */
if ( ifa - > ifa_prefixlen < 31 ) {
fib_magic ( RTM_NEWROUTE , RTN_BROADCAST , prefix , 32 , prim ) ;
fib_magic ( RTM_NEWROUTE , RTN_BROADCAST , prefix | ~ mask , 32 , prim ) ;
}
}
}
static void fib_del_ifaddr ( struct in_ifaddr * ifa )
{
struct in_device * in_dev = ifa - > ifa_dev ;
struct net_device * dev = in_dev - > dev ;
struct in_ifaddr * ifa1 ;
struct in_ifaddr * prim = ifa ;
2006-09-29 05:00:55 +04:00
__be32 brd = ifa - > ifa_address | ~ ifa - > ifa_mask ;
__be32 any = ifa - > ifa_address & ifa - > ifa_mask ;
2005-04-17 02:20:36 +04:00
# define LOCAL_OK 1
# define BRD_OK 2
# define BRD0_OK 4
# define BRD1_OK 8
unsigned ok = 0 ;
if ( ! ( ifa - > ifa_flags & IFA_F_SECONDARY ) )
fib_magic ( RTM_DELROUTE , dev - > flags & IFF_LOOPBACK ? RTN_LOCAL :
RTN_UNICAST , any , ifa - > ifa_prefixlen , prim ) ;
else {
prim = inet_ifa_byprefix ( in_dev , any , ifa - > ifa_mask ) ;
if ( prim = = NULL ) {
2008-01-13 07:58:35 +03:00
printk ( KERN_WARNING " fib_del_ifaddr: bug: prim == NULL \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
}
/* Deletion is more complicated than add.
We should take care of not to delete too much : - )
Scan address list to be sure that addresses are really gone .
*/
for ( ifa1 = in_dev - > ifa_list ; ifa1 ; ifa1 = ifa1 - > ifa_next ) {
if ( ifa - > ifa_local = = ifa1 - > ifa_local )
ok | = LOCAL_OK ;
if ( ifa - > ifa_broadcast = = ifa1 - > ifa_broadcast )
ok | = BRD_OK ;
if ( brd = = ifa1 - > ifa_broadcast )
ok | = BRD1_OK ;
if ( any = = ifa1 - > ifa_broadcast )
ok | = BRD0_OK ;
}
if ( ! ( ok & BRD_OK ) )
fib_magic ( RTM_DELROUTE , RTN_BROADCAST , ifa - > ifa_broadcast , 32 , prim ) ;
if ( ! ( ok & BRD1_OK ) )
fib_magic ( RTM_DELROUTE , RTN_BROADCAST , brd , 32 , prim ) ;
if ( ! ( ok & BRD0_OK ) )
fib_magic ( RTM_DELROUTE , RTN_BROADCAST , any , 32 , prim ) ;
if ( ! ( ok & LOCAL_OK ) ) {
fib_magic ( RTM_DELROUTE , RTN_LOCAL , ifa - > ifa_local , 32 , prim ) ;
/* Check, that this local address finally disappeared. */
2008-01-10 14:30:24 +03:00
if ( inet_addr_type ( dev - > nd_net , ifa - > ifa_local ) ! = RTN_LOCAL ) {
2005-04-17 02:20:36 +04:00
/* And the last, but not the least thing.
We must flush stray FIB entries .
First of all , we scan fib_info list searching
for stray nexthop entries , then ignite fib_flush .
*/
if ( fib_sync_down ( ifa - > ifa_local , NULL , 0 ) )
2008-01-10 14:30:24 +03:00
fib_flush ( dev - > nd_net ) ;
2005-04-17 02:20:36 +04:00
}
}
# undef LOCAL_OK
# undef BRD_OK
# undef BRD0_OK
# undef BRD1_OK
}
2005-06-21 00:36:39 +04:00
static void nl_fib_lookup ( struct fib_result_nl * frn , struct fib_table * tb )
{
2007-02-09 17:24:47 +03:00
2005-06-21 00:36:39 +04:00
struct fib_result res ;
2006-11-10 02:21:41 +03:00
struct flowi fl = { . mark = frn - > fl_mark ,
2006-11-10 02:20:38 +03:00
. nl_u = { . ip4_u = { . daddr = frn - > fl_addr ,
2005-06-21 00:36:39 +04:00
. tos = frn - > fl_tos ,
. scope = frn - > fl_scope } } } ;
2007-04-26 00:07:28 +04:00
2007-04-27 13:17:19 +04:00
# ifdef CONFIG_IP_MULTIPLE_TABLES
res . r = NULL ;
# endif
2007-04-26 00:07:28 +04:00
frn - > err = - ENOENT ;
2005-06-21 00:36:39 +04:00
if ( tb ) {
local_bh_disable ( ) ;
frn - > tb_id = tb - > tb_id ;
frn - > err = tb - > tb_lookup ( tb , & fl , & res ) ;
if ( ! frn - > err ) {
frn - > prefixlen = res . prefixlen ;
frn - > nh_sel = res . nh_sel ;
frn - > type = res . type ;
frn - > scope = res . scope ;
2007-04-26 00:07:28 +04:00
fib_res_put ( & res ) ;
2005-06-21 00:36:39 +04:00
}
local_bh_enable ( ) ;
}
}
2007-10-11 08:32:39 +04:00
static void nl_fib_input ( struct sk_buff * skb )
2005-06-21 00:36:39 +04:00
{
2008-01-10 14:28:55 +03:00
struct net * net ;
2005-06-21 00:36:39 +04:00
struct fib_result_nl * frn ;
2007-10-11 08:32:39 +04:00
struct nlmsghdr * nlh ;
2005-06-21 00:36:39 +04:00
struct fib_table * tb ;
2007-10-11 08:32:39 +04:00
u32 pid ;
2007-04-26 00:07:28 +04:00
2008-01-10 14:28:55 +03:00
net = skb - > sk - > sk_net ;
2007-04-26 06:08:35 +04:00
nlh = nlmsg_hdr ( skb ) ;
2005-12-02 01:30:00 +03:00
if ( skb - > len < NLMSG_SPACE ( 0 ) | | skb - > len < nlh - > nlmsg_len | |
2007-12-21 13:01:53 +03:00
nlh - > nlmsg_len < NLMSG_LENGTH ( sizeof ( * frn ) ) )
2005-12-02 01:30:00 +03:00
return ;
2007-12-21 13:01:53 +03:00
skb = skb_clone ( skb , GFP_KERNEL ) ;
if ( skb = = NULL )
return ;
nlh = nlmsg_hdr ( skb ) ;
2007-02-09 17:24:47 +03:00
2005-06-21 00:36:39 +04:00
frn = ( struct fib_result_nl * ) NLMSG_DATA ( nlh ) ;
2008-01-10 14:28:55 +03:00
tb = fib_get_table ( net , frn - > tb_id_in ) ;
2005-06-21 00:36:39 +04:00
nl_fib_lookup ( frn , tb ) ;
2007-02-09 17:24:47 +03:00
2007-04-26 00:07:28 +04:00
pid = NETLINK_CB ( skb ) . pid ; /* pid of sending process */
2005-06-21 00:36:39 +04:00
NETLINK_CB ( skb ) . pid = 0 ; /* from kernel */
2005-08-15 06:29:52 +04:00
NETLINK_CB ( skb ) . dst_group = 0 ; /* unicast */
2008-01-10 14:28:55 +03:00
netlink_unicast ( net - > ipv4 . fibnl , skb , pid , MSG_DONTWAIT ) ;
2007-02-09 17:24:47 +03:00
}
2005-06-21 00:36:39 +04:00
2008-01-10 14:22:17 +03:00
static int nl_fib_lookup_init ( struct net * net )
2005-06-21 00:36:39 +04:00
{
2008-01-10 14:28:55 +03:00
struct sock * sk ;
sk = netlink_kernel_create ( net , NETLINK_FIB_LOOKUP , 0 ,
nl_fib_input , NULL , THIS_MODULE ) ;
if ( sk = = NULL )
2008-01-10 14:22:17 +03:00
return - EAFNOSUPPORT ;
2008-01-10 14:28:55 +03:00
net - > ipv4 . fibnl = sk ;
2008-01-10 14:22:17 +03:00
return 0 ;
}
static void nl_fib_lookup_exit ( struct net * net )
{
2008-01-29 01:41:19 +03:00
netlink_kernel_release ( net - > ipv4 . fibnl ) ;
2008-01-19 10:55:19 +03:00
net - > ipv4 . fibnl = NULL ;
2005-06-21 00:36:39 +04:00
}
2005-04-17 02:20:36 +04:00
static void fib_disable_ip ( struct net_device * dev , int force )
{
if ( fib_sync_down ( 0 , dev , force ) )
2008-01-10 14:30:24 +03:00
fib_flush ( dev - > nd_net ) ;
2005-04-17 02:20:36 +04:00
rt_cache_flush ( 0 ) ;
arp_ifdown ( dev ) ;
}
static int fib_inetaddr_event ( struct notifier_block * this , unsigned long event , void * ptr )
{
struct in_ifaddr * ifa = ( struct in_ifaddr * ) ptr ;
switch ( event ) {
case NETDEV_UP :
fib_add_ifaddr ( ifa ) ;
# ifdef CONFIG_IP_ROUTE_MULTIPATH
fib_sync_up ( ifa - > ifa_dev - > dev ) ;
# endif
rt_cache_flush ( - 1 ) ;
break ;
case NETDEV_DOWN :
fib_del_ifaddr ( ifa ) ;
2005-10-28 02:10:01 +04:00
if ( ifa - > ifa_dev - > ifa_list = = NULL ) {
2005-04-17 02:20:36 +04:00
/* Last address was deleted from this interface.
Disable IP .
*/
fib_disable_ip ( ifa - > ifa_dev - > dev , 1 ) ;
} else {
rt_cache_flush ( - 1 ) ;
}
break ;
}
return NOTIFY_DONE ;
}
static int fib_netdev_event ( struct notifier_block * this , unsigned long event , void * ptr )
{
struct net_device * dev = ptr ;
2005-10-04 01:35:55 +04:00
struct in_device * in_dev = __in_dev_get_rtnl ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( event = = NETDEV_UNREGISTER ) {
fib_disable_ip ( dev , 2 ) ;
return NOTIFY_DONE ;
}
if ( ! in_dev )
return NOTIFY_DONE ;
switch ( event ) {
case NETDEV_UP :
for_ifa ( in_dev ) {
fib_add_ifaddr ( ifa ) ;
} endfor_ifa ( in_dev ) ;
# ifdef CONFIG_IP_ROUTE_MULTIPATH
fib_sync_up ( dev ) ;
# endif
rt_cache_flush ( - 1 ) ;
break ;
case NETDEV_DOWN :
fib_disable_ip ( dev , 0 ) ;
break ;
case NETDEV_CHANGEMTU :
case NETDEV_CHANGE :
rt_cache_flush ( 0 ) ;
break ;
}
return NOTIFY_DONE ;
}
static struct notifier_block fib_inetaddr_notifier = {
. notifier_call = fib_inetaddr_event ,
} ;
static struct notifier_block fib_netdev_notifier = {
. notifier_call = fib_netdev_event ,
} ;
2008-01-10 14:22:17 +03:00
static int __net_init ip_fib_net_init ( struct net * net )
2005-04-17 02:20:36 +04:00
{
2006-08-11 10:10:46 +04:00
unsigned int i ;
2008-01-10 14:28:24 +03:00
net - > ipv4 . fib_table_hash = kzalloc (
sizeof ( struct hlist_head ) * FIB_TABLE_HASHSZ , GFP_KERNEL ) ;
if ( net - > ipv4 . fib_table_hash = = NULL )
return - ENOMEM ;
2006-08-11 10:10:46 +04:00
for ( i = 0 ; i < FIB_TABLE_HASHSZ ; i + + )
2008-01-10 14:28:24 +03:00
INIT_HLIST_HEAD ( & net - > ipv4 . fib_table_hash [ i ] ) ;
2007-11-07 10:34:04 +03:00
2008-01-10 14:22:17 +03:00
return fib4_rules_init ( net ) ;
}
2005-04-17 02:20:36 +04:00
2008-01-10 14:22:17 +03:00
static void __net_exit ip_fib_net_exit ( struct net * net )
{
unsigned int i ;
# ifdef CONFIG_IP_MULTIPLE_TABLES
fib4_rules_exit ( net ) ;
# endif
for ( i = 0 ; i < FIB_TABLE_HASHSZ ; i + + ) {
struct fib_table * tb ;
struct hlist_head * head ;
struct hlist_node * node , * tmp ;
2007-03-22 21:55:17 +03:00
2008-01-10 14:28:24 +03:00
head = & net - > ipv4 . fib_table_hash [ i ] ;
2008-01-10 14:22:17 +03:00
hlist_for_each_entry_safe ( tb , node , tmp , head , tb_hlist ) {
hlist_del ( node ) ;
tb - > tb_flush ( tb ) ;
kfree ( tb ) ;
}
}
2008-01-10 14:28:24 +03:00
kfree ( net - > ipv4 . fib_table_hash ) ;
2008-01-10 14:22:17 +03:00
}
static int __net_init fib_net_init ( struct net * net )
{
int error ;
error = ip_fib_net_init ( net ) ;
if ( error < 0 )
goto out ;
error = nl_fib_lookup_init ( net ) ;
if ( error < 0 )
goto out_nlfl ;
error = fib_proc_init ( net ) ;
if ( error < 0 )
goto out_proc ;
out :
return error ;
out_proc :
nl_fib_lookup_exit ( net ) ;
out_nlfl :
ip_fib_net_exit ( net ) ;
goto out ;
}
static void __net_exit fib_net_exit ( struct net * net )
{
fib_proc_exit ( net ) ;
nl_fib_lookup_exit ( net ) ;
ip_fib_net_exit ( net ) ;
}
static struct pernet_operations fib_net_ops = {
. init = fib_net_init ,
. exit = fib_net_exit ,
} ;
void __init ip_fib_init ( void )
{
2007-03-22 21:55:17 +03:00
rtnl_register ( PF_INET , RTM_NEWROUTE , inet_rtm_newroute , NULL ) ;
rtnl_register ( PF_INET , RTM_DELROUTE , inet_rtm_delroute , NULL ) ;
rtnl_register ( PF_INET , RTM_GETROUTE , NULL , inet_dump_fib ) ;
2008-01-10 14:22:17 +03:00
register_pernet_subsys ( & fib_net_ops ) ;
register_netdevice_notifier ( & fib_netdev_notifier ) ;
register_inetaddr_notifier ( & fib_inetaddr_notifier ) ;
2008-01-15 10:14:20 +03:00
fib_hash_init ( ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( inet_addr_type ) ;
2007-12-05 10:28:46 +03:00
EXPORT_SYMBOL ( inet_dev_addr_type ) ;
2006-06-18 07:37:28 +04:00
EXPORT_SYMBOL ( ip_dev_find ) ;