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 .
*
* Routing netlink socket interface : protocol independent part .
*
* 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 .
*
* Fixes :
* Vitaly E . Lavrov RTA_OK arithmetics was wrong .
*/
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/kernel.h>
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <linux/fcntl.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/capability.h>
# include <linux/skbuff.h>
# include <linux/init.h>
# include <linux/security.h>
2006-03-21 09:23:58 +03:00
# include <linux/mutex.h>
2006-08-05 10:04:54 +04:00
# include <linux/if_addr.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/system.h>
# include <asm/string.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <net/ip.h>
# include <net/protocol.h>
# include <net/arp.h>
# include <net/route.h>
# include <net/udp.h>
# include <net/sock.h>
# include <net/pkt_sched.h>
2006-08-04 14:38:38 +04:00
# include <net/fib_rules.h>
2007-03-22 21:48:11 +03:00
# include <net/rtnetlink.h>
2005-04-17 02:20:36 +04:00
2007-03-22 21:48:11 +03:00
struct rtnl_link
{
rtnl_doit_func doit ;
rtnl_dumpit_func dumpit ;
} ;
2006-03-21 09:23:58 +03:00
static DEFINE_MUTEX ( rtnl_mutex ) ;
2006-08-15 11:37:29 +04:00
static struct sock * rtnl ;
2005-04-17 02:20:36 +04:00
void rtnl_lock ( void )
{
2006-03-21 09:23:58 +03:00
mutex_lock ( & rtnl_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:23:58 +03:00
void __rtnl_unlock ( void )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:23:58 +03:00
mutex_unlock ( & rtnl_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:23:58 +03:00
2005-04-17 02:20:36 +04:00
void rtnl_unlock ( void )
{
2006-03-21 09:23:58 +03:00
mutex_unlock ( & rtnl_mutex ) ;
if ( rtnl & & rtnl - > sk_receive_queue . qlen )
rtnl - > sk_data_ready ( rtnl , 0 ) ;
2005-04-17 02:20:36 +04:00
netdev_run_todo ( ) ;
}
2006-03-21 09:23:58 +03:00
int rtnl_trylock ( void )
{
return mutex_trylock ( & rtnl_mutex ) ;
}
2005-04-17 02:20:36 +04:00
int rtattr_parse ( struct rtattr * tb [ ] , int maxattr , struct rtattr * rta , int len )
{
memset ( tb , 0 , sizeof ( struct rtattr * ) * maxattr ) ;
while ( RTA_OK ( rta , len ) ) {
unsigned flavor = rta - > rta_type ;
if ( flavor & & flavor < = maxattr )
tb [ flavor - 1 ] = rta ;
rta = RTA_NEXT ( rta , len ) ;
}
return 0 ;
}
2007-06-26 14:23:44 +04:00
int __rtattr_parse_nested_compat ( struct rtattr * tb [ ] , int maxattr ,
2007-07-19 05:43:23 +04:00
struct rtattr * rta , int len )
2007-06-26 01:30:16 +04:00
{
if ( RTA_PAYLOAD ( rta ) < len )
return - 1 ;
if ( RTA_PAYLOAD ( rta ) > = RTA_ALIGN ( len ) + sizeof ( struct rtattr ) ) {
rta = RTA_DATA ( rta ) + RTA_ALIGN ( len ) ;
return rtattr_parse_nested ( tb , maxattr , rta ) ;
}
memset ( tb , 0 , sizeof ( struct rtattr * ) * maxattr ) ;
return 0 ;
}
2007-04-26 11:57:41 +04:00
static struct rtnl_link * rtnl_msg_handlers [ NPROTO ] ;
2007-03-22 21:48:11 +03:00
static inline int rtm_msgindex ( int msgtype )
{
int msgindex = msgtype - RTM_BASE ;
/*
* msgindex < 0 implies someone tried to register a netlink
* control code . msgindex > = RTM_NR_MSGTYPES may indicate that
* the message type has not been added to linux / rtnetlink . h
*/
BUG_ON ( msgindex < 0 | | msgindex > = RTM_NR_MSGTYPES ) ;
return msgindex ;
}
static rtnl_doit_func rtnl_get_doit ( int protocol , int msgindex )
{
struct rtnl_link * tab ;
tab = rtnl_msg_handlers [ protocol ] ;
2007-03-23 07:41:06 +03:00
if ( tab = = NULL | | tab [ msgindex ] . doit = = NULL )
2007-03-22 21:48:11 +03:00
tab = rtnl_msg_handlers [ PF_UNSPEC ] ;
2007-03-23 07:41:06 +03:00
return tab ? tab [ msgindex ] . doit : NULL ;
2007-03-22 21:48:11 +03:00
}
static rtnl_dumpit_func rtnl_get_dumpit ( int protocol , int msgindex )
{
struct rtnl_link * tab ;
tab = rtnl_msg_handlers [ protocol ] ;
2007-03-23 07:41:06 +03:00
if ( tab = = NULL | | tab [ msgindex ] . dumpit = = NULL )
2007-03-22 21:48:11 +03:00
tab = rtnl_msg_handlers [ PF_UNSPEC ] ;
2007-03-23 07:41:06 +03:00
return tab ? tab [ msgindex ] . dumpit : NULL ;
2007-03-22 21:48:11 +03:00
}
/**
* __rtnl_register - Register a rtnetlink message type
* @ protocol : Protocol family or PF_UNSPEC
* @ msgtype : rtnetlink message type
* @ doit : Function pointer called for each request message
* @ dumpit : Function pointer called for each dump request ( NLM_F_DUMP ) message
*
* Registers the specified function pointers ( at least one of them has
* to be non - NULL ) to be called whenever a request message for the
* specified protocol family and message type is received .
*
* The special protocol family PF_UNSPEC may be used to define fallback
* function pointers for the case when no entry for the specific protocol
* family exists .
*
* Returns 0 on success or a negative error code .
*/
int __rtnl_register ( int protocol , int msgtype ,
rtnl_doit_func doit , rtnl_dumpit_func dumpit )
{
struct rtnl_link * tab ;
int msgindex ;
BUG_ON ( protocol < 0 | | protocol > = NPROTO ) ;
msgindex = rtm_msgindex ( msgtype ) ;
tab = rtnl_msg_handlers [ protocol ] ;
if ( tab = = NULL ) {
tab = kcalloc ( RTM_NR_MSGTYPES , sizeof ( * tab ) , GFP_KERNEL ) ;
if ( tab = = NULL )
return - ENOBUFS ;
rtnl_msg_handlers [ protocol ] = tab ;
}
if ( doit )
tab [ msgindex ] . doit = doit ;
if ( dumpit )
tab [ msgindex ] . dumpit = dumpit ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( __rtnl_register ) ;
/**
* rtnl_register - Register a rtnetlink message type
*
* Identical to __rtnl_register ( ) but panics on failure . This is useful
* as failure of this function is very unlikely , it can only happen due
* to lack of memory when allocating the chain to store all message
* handlers for a protocol . Meant for use in init functions where lack
* of memory implies no sense in continueing .
*/
void rtnl_register ( int protocol , int msgtype ,
rtnl_doit_func doit , rtnl_dumpit_func dumpit )
{
if ( __rtnl_register ( protocol , msgtype , doit , dumpit ) < 0 )
panic ( " Unable to register rtnetlink message handler, "
" protocol = %d, message type = %d \n " ,
protocol , msgtype ) ;
}
EXPORT_SYMBOL_GPL ( rtnl_register ) ;
/**
* rtnl_unregister - Unregister a rtnetlink message type
* @ protocol : Protocol family or PF_UNSPEC
* @ msgtype : rtnetlink message type
*
* Returns 0 on success or a negative error code .
*/
int rtnl_unregister ( int protocol , int msgtype )
{
int msgindex ;
BUG_ON ( protocol < 0 | | protocol > = NPROTO ) ;
msgindex = rtm_msgindex ( msgtype ) ;
if ( rtnl_msg_handlers [ protocol ] = = NULL )
return - ENOENT ;
rtnl_msg_handlers [ protocol ] [ msgindex ] . doit = NULL ;
rtnl_msg_handlers [ protocol ] [ msgindex ] . dumpit = NULL ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( rtnl_unregister ) ;
/**
* rtnl_unregister_all - Unregister all rtnetlink message type of a protocol
* @ protocol : Protocol family or PF_UNSPEC
*
* Identical to calling rtnl_unregster ( ) for all registered message types
* of a certain protocol family .
*/
void rtnl_unregister_all ( int protocol )
{
BUG_ON ( protocol < 0 | | protocol > = NPROTO ) ;
kfree ( rtnl_msg_handlers [ protocol ] ) ;
rtnl_msg_handlers [ protocol ] = NULL ;
}
EXPORT_SYMBOL_GPL ( rtnl_unregister_all ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:03:51 +04:00
static LIST_HEAD ( link_ops ) ;
/**
* __rtnl_link_register - Register rtnl_link_ops with rtnetlink .
* @ ops : struct rtnl_link_ops * to register
*
* The caller must hold the rtnl_mutex . This function should be used
* by drivers that create devices during module initialization . It
* must be called before registering the devices .
*
* Returns 0 on success or a negative error code .
*/
int __rtnl_link_register ( struct rtnl_link_ops * ops )
{
2007-07-12 06:42:13 +04:00
if ( ! ops - > dellink )
ops - > dellink = unregister_netdevice ;
2007-06-13 23:03:51 +04:00
list_add_tail ( & ops - > list , & link_ops ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( __rtnl_link_register ) ;
/**
* rtnl_link_register - Register rtnl_link_ops with rtnetlink .
* @ ops : struct rtnl_link_ops * to register
*
* Returns 0 on success or a negative error code .
*/
int rtnl_link_register ( struct rtnl_link_ops * ops )
{
int err ;
rtnl_lock ( ) ;
err = __rtnl_link_register ( ops ) ;
rtnl_unlock ( ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( rtnl_link_register ) ;
/**
* __rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink .
* @ ops : struct rtnl_link_ops * to unregister
*
2007-07-12 06:42:13 +04:00
* The caller must hold the rtnl_mutex .
2007-06-13 23:03:51 +04:00
*/
void __rtnl_link_unregister ( struct rtnl_link_ops * ops )
{
2007-07-12 06:42:13 +04:00
struct net_device * dev , * n ;
for_each_netdev_safe ( dev , n ) {
if ( dev - > rtnl_link_ops = = ops )
ops - > dellink ( dev ) ;
}
2007-06-13 23:03:51 +04:00
list_del ( & ops - > list ) ;
}
EXPORT_SYMBOL_GPL ( __rtnl_link_unregister ) ;
/**
* rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink .
* @ ops : struct rtnl_link_ops * to unregister
*/
void rtnl_link_unregister ( struct rtnl_link_ops * ops )
{
rtnl_lock ( ) ;
__rtnl_link_unregister ( ops ) ;
rtnl_unlock ( ) ;
}
EXPORT_SYMBOL_GPL ( rtnl_link_unregister ) ;
static const struct rtnl_link_ops * rtnl_link_ops_get ( const char * kind )
{
const struct rtnl_link_ops * ops ;
list_for_each_entry ( ops , & link_ops , list ) {
if ( ! strcmp ( ops - > kind , kind ) )
return ops ;
}
return NULL ;
}
static size_t rtnl_link_get_size ( const struct net_device * dev )
{
const struct rtnl_link_ops * ops = dev - > rtnl_link_ops ;
size_t size ;
if ( ! ops )
return 0 ;
size = nlmsg_total_size ( sizeof ( struct nlattr ) ) + /* IFLA_LINKINFO */
nlmsg_total_size ( strlen ( ops - > kind ) + 1 ) ; /* IFLA_INFO_KIND */
if ( ops - > get_size )
/* IFLA_INFO_DATA + nested data */
size + = nlmsg_total_size ( sizeof ( struct nlattr ) ) +
ops - > get_size ( dev ) ;
if ( ops - > get_xstats_size )
size + = ops - > get_xstats_size ( dev ) ; /* IFLA_INFO_XSTATS */
return size ;
}
static int rtnl_link_fill ( struct sk_buff * skb , const struct net_device * dev )
{
const struct rtnl_link_ops * ops = dev - > rtnl_link_ops ;
struct nlattr * linkinfo , * data ;
int err = - EMSGSIZE ;
linkinfo = nla_nest_start ( skb , IFLA_LINKINFO ) ;
if ( linkinfo = = NULL )
goto out ;
if ( nla_put_string ( skb , IFLA_INFO_KIND , ops - > kind ) < 0 )
goto err_cancel_link ;
if ( ops - > fill_xstats ) {
err = ops - > fill_xstats ( skb , dev ) ;
if ( err < 0 )
goto err_cancel_link ;
}
if ( ops - > fill_info ) {
data = nla_nest_start ( skb , IFLA_INFO_DATA ) ;
if ( data = = NULL )
goto err_cancel_link ;
err = ops - > fill_info ( skb , dev ) ;
if ( err < 0 )
goto err_cancel_data ;
nla_nest_end ( skb , data ) ;
}
nla_nest_end ( skb , linkinfo ) ;
return 0 ;
err_cancel_data :
nla_nest_cancel ( skb , data ) ;
err_cancel_link :
nla_nest_cancel ( skb , linkinfo ) ;
out :
return err ;
}
2005-05-04 01:29:39 +04:00
static const int rtm_min [ RTM_NR_FAMILIES ] =
2005-04-17 02:20:36 +04:00
{
2005-05-04 01:29:00 +04:00
[ RTM_FAM ( RTM_NEWLINK ) ] = NLMSG_LENGTH ( sizeof ( struct ifinfomsg ) ) ,
[ RTM_FAM ( RTM_NEWADDR ) ] = NLMSG_LENGTH ( sizeof ( struct ifaddrmsg ) ) ,
[ RTM_FAM ( RTM_NEWROUTE ) ] = NLMSG_LENGTH ( sizeof ( struct rtmsg ) ) ,
2006-08-04 14:38:38 +04:00
[ RTM_FAM ( RTM_NEWRULE ) ] = NLMSG_LENGTH ( sizeof ( struct fib_rule_hdr ) ) ,
2005-05-04 01:29:00 +04:00
[ RTM_FAM ( RTM_NEWQDISC ) ] = NLMSG_LENGTH ( sizeof ( struct tcmsg ) ) ,
[ RTM_FAM ( RTM_NEWTCLASS ) ] = NLMSG_LENGTH ( sizeof ( struct tcmsg ) ) ,
[ RTM_FAM ( RTM_NEWTFILTER ) ] = NLMSG_LENGTH ( sizeof ( struct tcmsg ) ) ,
[ RTM_FAM ( RTM_NEWACTION ) ] = NLMSG_LENGTH ( sizeof ( struct tcamsg ) ) ,
[ RTM_FAM ( RTM_GETMULTICAST ) ] = NLMSG_LENGTH ( sizeof ( struct rtgenmsg ) ) ,
[ RTM_FAM ( RTM_GETANYCAST ) ] = NLMSG_LENGTH ( sizeof ( struct rtgenmsg ) ) ,
2005-04-17 02:20:36 +04:00
} ;
2005-05-04 01:29:39 +04:00
static const int rta_max [ RTM_NR_FAMILIES ] =
2005-04-17 02:20:36 +04:00
{
2005-05-04 01:29:00 +04:00
[ RTM_FAM ( RTM_NEWLINK ) ] = IFLA_MAX ,
[ RTM_FAM ( RTM_NEWADDR ) ] = IFA_MAX ,
[ RTM_FAM ( RTM_NEWROUTE ) ] = RTA_MAX ,
2006-08-04 14:38:38 +04:00
[ RTM_FAM ( RTM_NEWRULE ) ] = FRA_MAX ,
2005-05-04 01:29:00 +04:00
[ RTM_FAM ( RTM_NEWQDISC ) ] = TCA_MAX ,
[ RTM_FAM ( RTM_NEWTCLASS ) ] = TCA_MAX ,
[ RTM_FAM ( RTM_NEWTFILTER ) ] = TCA_MAX ,
[ RTM_FAM ( RTM_NEWACTION ) ] = TCAA_MAX ,
2005-04-17 02:20:36 +04:00
} ;
void __rta_fill ( struct sk_buff * skb , int attrtype , int attrlen , const void * data )
{
struct rtattr * rta ;
int size = RTA_LENGTH ( attrlen ) ;
rta = ( struct rtattr * ) skb_put ( skb , RTA_ALIGN ( size ) ) ;
rta - > rta_type = attrtype ;
rta - > rta_len = size ;
memcpy ( RTA_DATA ( rta ) , data , attrlen ) ;
2005-06-28 23:54:43 +04:00
memset ( RTA_DATA ( rta ) + attrlen , 0 , RTA_ALIGN ( size ) - size ) ;
2005-04-17 02:20:36 +04:00
}
size_t rtattr_strlcpy ( char * dest , const struct rtattr * rta , size_t size )
{
size_t ret = RTA_PAYLOAD ( rta ) ;
char * src = RTA_DATA ( rta ) ;
if ( ret > 0 & & src [ ret - 1 ] = = ' \0 ' )
ret - - ;
if ( size > 0 ) {
size_t len = ( ret > = size ) ? size - 1 : ret ;
memset ( dest , 0 , size ) ;
memcpy ( dest , src , len ) ;
}
return ret ;
}
int rtnetlink_send ( struct sk_buff * skb , u32 pid , unsigned group , int echo )
{
int err = 0 ;
2005-08-15 06:29:52 +04:00
NETLINK_CB ( skb ) . dst_group = group ;
2005-04-17 02:20:36 +04:00
if ( echo )
atomic_inc ( & skb - > users ) ;
netlink_broadcast ( rtnl , skb , pid , group , GFP_KERNEL ) ;
if ( echo )
err = netlink_unicast ( rtnl , skb , pid , MSG_DONTWAIT ) ;
return err ;
}
2006-08-15 11:30:25 +04:00
int rtnl_unicast ( struct sk_buff * skb , u32 pid )
{
return nlmsg_unicast ( rtnl , skb , pid ) ;
}
2006-08-15 11:31:41 +04:00
int rtnl_notify ( struct sk_buff * skb , u32 pid , u32 group ,
struct nlmsghdr * nlh , gfp_t flags )
{
int report = 0 ;
if ( nlh )
report = nlmsg_report ( nlh ) ;
return nlmsg_notify ( rtnl , skb , pid , group , report , flags ) ;
}
void rtnl_set_sk_err ( u32 group , int error )
{
netlink_set_err ( rtnl , 0 , group , error ) ;
}
2005-04-17 02:20:36 +04:00
int rtnetlink_put_metrics ( struct sk_buff * skb , u32 * metrics )
{
2006-08-22 11:01:27 +04:00
struct nlattr * mx ;
int i , valid = 0 ;
mx = nla_nest_start ( skb , RTA_METRICS ) ;
if ( mx = = NULL )
return - ENOBUFS ;
for ( i = 0 ; i < RTAX_MAX ; i + + ) {
if ( metrics [ i ] ) {
valid + + ;
NLA_PUT_U32 ( skb , i + 1 , metrics [ i ] ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-08-23 09:20:14 +04:00
if ( ! valid ) {
nla_nest_cancel ( skb , mx ) ;
return 0 ;
}
2006-08-22 11:01:27 +04:00
return nla_nest_end ( skb , mx ) ;
nla_put_failure :
return nla_nest_cancel ( skb , mx ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-27 20:27:07 +03:00
int rtnl_put_cacheinfo ( struct sk_buff * skb , struct dst_entry * dst , u32 id ,
u32 ts , u32 tsage , long expires , u32 error )
{
struct rta_cacheinfo ci = {
. rta_lastuse = jiffies_to_clock_t ( jiffies - dst - > lastuse ) ,
. rta_used = dst - > __use ,
. rta_clntref = atomic_read ( & ( dst - > __refcnt ) ) ,
. rta_error = error ,
. rta_id = id ,
. rta_ts = ts ,
. rta_tsage = tsage ,
} ;
if ( expires )
ci . rta_expires = jiffies_to_clock_t ( expires ) ;
return nla_put ( skb , RTA_CACHEINFO , sizeof ( ci ) , & ci ) ;
}
EXPORT_SYMBOL_GPL ( rtnl_put_cacheinfo ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 04:09:11 +03:00
static void set_operstate ( struct net_device * dev , unsigned char transition )
{
unsigned char operstate = dev - > operstate ;
switch ( transition ) {
case IF_OPER_UP :
if ( ( operstate = = IF_OPER_DORMANT | |
operstate = = IF_OPER_UNKNOWN ) & &
! netif_dormant ( dev ) )
operstate = IF_OPER_UP ;
break ;
case IF_OPER_DORMANT :
if ( operstate = = IF_OPER_UP | |
operstate = = IF_OPER_UNKNOWN )
operstate = IF_OPER_DORMANT ;
break ;
2007-04-21 04:09:22 +04:00
}
2006-03-21 04:09:11 +03:00
if ( dev - > operstate ! = operstate ) {
write_lock_bh ( & dev_base_lock ) ;
dev - > operstate = operstate ;
write_unlock_bh ( & dev_base_lock ) ;
netdev_state_change ( dev ) ;
}
}
2006-08-05 10:05:34 +04:00
static void copy_rtnl_link_stats ( struct rtnl_link_stats * a ,
struct net_device_stats * b )
2005-04-17 02:20:36 +04:00
{
2006-08-05 10:05:34 +04:00
a - > rx_packets = b - > rx_packets ;
a - > tx_packets = b - > tx_packets ;
a - > rx_bytes = b - > rx_bytes ;
a - > tx_bytes = b - > tx_bytes ;
a - > rx_errors = b - > rx_errors ;
a - > tx_errors = b - > tx_errors ;
a - > rx_dropped = b - > rx_dropped ;
a - > tx_dropped = b - > tx_dropped ;
a - > multicast = b - > multicast ;
a - > collisions = b - > collisions ;
a - > rx_length_errors = b - > rx_length_errors ;
a - > rx_over_errors = b - > rx_over_errors ;
a - > rx_crc_errors = b - > rx_crc_errors ;
a - > rx_frame_errors = b - > rx_frame_errors ;
a - > rx_fifo_errors = b - > rx_fifo_errors ;
a - > rx_missed_errors = b - > rx_missed_errors ;
a - > tx_aborted_errors = b - > tx_aborted_errors ;
a - > tx_carrier_errors = b - > tx_carrier_errors ;
a - > tx_fifo_errors = b - > tx_fifo_errors ;
a - > tx_heartbeat_errors = b - > tx_heartbeat_errors ;
a - > tx_window_errors = b - > tx_window_errors ;
a - > rx_compressed = b - > rx_compressed ;
a - > tx_compressed = b - > tx_compressed ;
} ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:03:51 +04:00
static inline size_t if_nlmsg_size ( const struct net_device * dev )
2006-11-11 01:10:15 +03:00
{
return NLMSG_ALIGN ( sizeof ( struct ifinfomsg ) )
+ nla_total_size ( IFNAMSIZ ) /* IFLA_IFNAME */
+ nla_total_size ( IFNAMSIZ ) /* IFLA_QDISC */
+ nla_total_size ( sizeof ( struct rtnl_link_ifmap ) )
+ nla_total_size ( sizeof ( struct rtnl_link_stats ) )
+ nla_total_size ( MAX_ADDR_LEN ) /* IFLA_ADDRESS */
+ nla_total_size ( MAX_ADDR_LEN ) /* IFLA_BROADCAST */
+ nla_total_size ( 4 ) /* IFLA_TXQLEN */
+ nla_total_size ( 4 ) /* IFLA_WEIGHT */
+ nla_total_size ( 4 ) /* IFLA_MTU */
+ nla_total_size ( 4 ) /* IFLA_LINK */
+ nla_total_size ( 4 ) /* IFLA_MASTER */
+ nla_total_size ( 1 ) /* IFLA_OPERSTATE */
2007-06-13 23:03:51 +04:00
+ nla_total_size ( 1 ) /* IFLA_LINKMODE */
+ rtnl_link_get_size ( dev ) ; /* IFLA_LINKINFO */
2006-11-11 01:10:15 +03:00
}
2006-08-05 10:05:34 +04:00
static int rtnl_fill_ifinfo ( struct sk_buff * skb , struct net_device * dev ,
2007-05-23 04:00:49 +04:00
int type , u32 pid , u32 seq , u32 change ,
unsigned int flags )
2006-08-05 10:05:34 +04:00
{
struct ifinfomsg * ifm ;
struct nlmsghdr * nlh ;
2005-04-17 02:20:36 +04:00
2006-08-05 10:05:34 +04:00
nlh = nlmsg_put ( skb , pid , seq , type , sizeof ( * ifm ) , flags ) ;
if ( nlh = = NULL )
2007-02-01 10:16:40 +03:00
return - EMSGSIZE ;
2005-04-17 02:20:36 +04:00
2006-08-05 10:05:34 +04:00
ifm = nlmsg_data ( nlh ) ;
ifm - > ifi_family = AF_UNSPEC ;
ifm - > __ifi_pad = 0 ;
ifm - > ifi_type = dev - > type ;
ifm - > ifi_index = dev - > ifindex ;
ifm - > ifi_flags = dev_get_flags ( dev ) ;
ifm - > ifi_change = change ;
NLA_PUT_STRING ( skb , IFLA_IFNAME , dev - > name ) ;
NLA_PUT_U32 ( skb , IFLA_TXQLEN , dev - > tx_queue_len ) ;
NLA_PUT_U8 ( skb , IFLA_OPERSTATE ,
netif_running ( dev ) ? dev - > operstate : IF_OPER_DOWN ) ;
NLA_PUT_U8 ( skb , IFLA_LINKMODE , dev - > link_mode ) ;
NLA_PUT_U32 ( skb , IFLA_MTU , dev - > mtu ) ;
if ( dev - > ifindex ! = dev - > iflink )
NLA_PUT_U32 ( skb , IFLA_LINK , dev - > iflink ) ;
if ( dev - > master )
NLA_PUT_U32 ( skb , IFLA_MASTER , dev - > master - > ifindex ) ;
2005-04-17 02:20:36 +04:00
2006-08-05 10:05:34 +04:00
if ( dev - > qdisc_sleeping )
NLA_PUT_STRING ( skb , IFLA_QDISC , dev - > qdisc_sleeping - > ops - > id ) ;
2006-03-21 04:09:11 +03:00
2005-04-17 02:20:36 +04:00
if ( 1 ) {
struct rtnl_link_ifmap map = {
. mem_start = dev - > mem_start ,
. mem_end = dev - > mem_end ,
. base_addr = dev - > base_addr ,
. irq = dev - > irq ,
. dma = dev - > dma ,
. port = dev - > if_port ,
} ;
2006-08-05 10:05:34 +04:00
NLA_PUT ( skb , IFLA_MAP , sizeof ( map ) , & map ) ;
2005-04-17 02:20:36 +04:00
}
if ( dev - > addr_len ) {
2006-08-05 10:05:34 +04:00
NLA_PUT ( skb , IFLA_ADDRESS , dev - > addr_len , dev - > dev_addr ) ;
NLA_PUT ( skb , IFLA_BROADCAST , dev - > addr_len , dev - > broadcast ) ;
2005-04-17 02:20:36 +04:00
}
if ( dev - > get_stats ) {
2006-08-05 10:05:34 +04:00
struct net_device_stats * stats = dev - > get_stats ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( stats ) {
2006-08-05 10:05:34 +04:00
struct nlattr * attr ;
attr = nla_reserve ( skb , IFLA_STATS ,
sizeof ( struct rtnl_link_stats ) ) ;
if ( attr = = NULL )
goto nla_put_failure ;
copy_rtnl_link_stats ( nla_data ( attr ) , stats ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-06-13 23:03:51 +04:00
if ( dev - > rtnl_link_ops ) {
if ( rtnl_link_fill ( skb , dev ) < 0 )
goto nla_put_failure ;
}
2006-08-05 10:05:34 +04:00
return nlmsg_end ( skb , nlh ) ;
nla_put_failure :
2007-02-01 10:16:40 +03:00
nlmsg_cancel ( skb , nlh ) ;
return - EMSGSIZE ;
2005-04-17 02:20:36 +04:00
}
2006-08-05 10:05:34 +04:00
static int rtnl_dump_ifinfo ( struct sk_buff * skb , struct netlink_callback * cb )
2005-04-17 02:20:36 +04:00
{
int idx ;
int s_idx = cb - > args [ 0 ] ;
struct net_device * dev ;
2007-05-04 02:13:45 +04:00
idx = 0 ;
for_each_netdev ( dev ) {
2005-04-17 02:20:36 +04:00
if ( idx < s_idx )
2007-05-04 02:13:45 +04:00
goto cont ;
2007-05-23 04:00:49 +04:00
if ( rtnl_fill_ifinfo ( skb , dev , RTM_NEWLINK ,
2006-08-05 10:05:34 +04:00
NETLINK_CB ( cb - > skb ) . pid ,
cb - > nlh - > nlmsg_seq , 0 , NLM_F_MULTI ) < = 0 )
2005-04-17 02:20:36 +04:00
break ;
2007-05-04 02:13:45 +04:00
cont :
idx + + ;
2005-04-17 02:20:36 +04:00
}
cb - > args [ 0 ] = idx ;
return skb - > len ;
}
2007-06-05 23:38:30 +04:00
static const struct nla_policy ifla_policy [ IFLA_MAX + 1 ] = {
2006-08-27 07:13:18 +04:00
[ IFLA_IFNAME ] = { . type = NLA_STRING , . len = IFNAMSIZ - 1 } ,
2007-06-13 23:03:51 +04:00
[ IFLA_ADDRESS ] = { . type = NLA_BINARY , . len = MAX_ADDR_LEN } ,
[ IFLA_BROADCAST ] = { . type = NLA_BINARY , . len = MAX_ADDR_LEN } ,
2006-08-27 07:13:18 +04:00
[ IFLA_MAP ] = { . len = sizeof ( struct rtnl_link_ifmap ) } ,
2006-08-11 08:17:37 +04:00
[ IFLA_MTU ] = { . type = NLA_U32 } ,
[ IFLA_TXQLEN ] = { . type = NLA_U32 } ,
[ IFLA_WEIGHT ] = { . type = NLA_U32 } ,
[ IFLA_OPERSTATE ] = { . type = NLA_U8 } ,
[ IFLA_LINKMODE ] = { . type = NLA_U8 } ,
} ;
2007-06-13 23:03:51 +04:00
static const struct nla_policy ifla_info_policy [ IFLA_INFO_MAX + 1 ] = {
[ IFLA_INFO_KIND ] = { . type = NLA_STRING } ,
[ IFLA_INFO_DATA ] = { . type = NLA_NESTED } ,
} ;
2007-06-13 23:03:36 +04:00
static int do_setlink ( struct net_device * dev , struct ifinfomsg * ifm ,
2007-06-13 23:03:51 +04:00
struct nlattr * * tb , char * ifname , int modified )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:03:51 +04:00
int send_addr_notify = 0 ;
2007-06-13 23:03:36 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2006-08-11 08:17:37 +04:00
if ( tb [ IFLA_MAP ] ) {
2005-04-17 02:20:36 +04:00
struct rtnl_link_ifmap * u_map ;
struct ifmap k_map ;
if ( ! dev - > set_config ) {
err = - EOPNOTSUPP ;
2007-06-13 23:03:36 +04:00
goto errout ;
2005-04-17 02:20:36 +04:00
}
if ( ! netif_device_present ( dev ) ) {
err = - ENODEV ;
2007-06-13 23:03:36 +04:00
goto errout ;
2005-04-17 02:20:36 +04:00
}
2006-08-11 08:17:37 +04:00
u_map = nla_data ( tb [ IFLA_MAP ] ) ;
2005-04-17 02:20:36 +04:00
k_map . mem_start = ( unsigned long ) u_map - > mem_start ;
k_map . mem_end = ( unsigned long ) u_map - > mem_end ;
k_map . base_addr = ( unsigned short ) u_map - > base_addr ;
k_map . irq = ( unsigned char ) u_map - > irq ;
k_map . dma = ( unsigned char ) u_map - > dma ;
k_map . port = ( unsigned char ) u_map - > port ;
err = dev - > set_config ( dev , & k_map ) ;
2006-08-11 08:17:37 +04:00
if ( err < 0 )
2007-06-13 23:03:36 +04:00
goto errout ;
2005-04-17 02:20:36 +04:00
2006-08-11 08:17:37 +04:00
modified = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-08-11 08:17:37 +04:00
if ( tb [ IFLA_ADDRESS ] ) {
2006-08-09 03:47:37 +04:00
struct sockaddr * sa ;
int len ;
2005-04-17 02:20:36 +04:00
if ( ! dev - > set_mac_address ) {
err = - EOPNOTSUPP ;
2007-06-13 23:03:36 +04:00
goto errout ;
2005-04-17 02:20:36 +04:00
}
2006-08-11 08:17:37 +04:00
2005-04-17 02:20:36 +04:00
if ( ! netif_device_present ( dev ) ) {
err = - ENODEV ;
2007-06-13 23:03:36 +04:00
goto errout ;
2005-04-17 02:20:36 +04:00
}
2006-08-09 03:47:37 +04:00
len = sizeof ( sa_family_t ) + dev - > addr_len ;
sa = kmalloc ( len , GFP_KERNEL ) ;
if ( ! sa ) {
err = - ENOMEM ;
2007-06-13 23:03:36 +04:00
goto errout ;
2006-08-09 03:47:37 +04:00
}
sa - > sa_family = dev - > type ;
2006-08-11 08:17:37 +04:00
memcpy ( sa - > sa_data , nla_data ( tb [ IFLA_ADDRESS ] ) ,
2006-08-09 03:47:37 +04:00
dev - > addr_len ) ;
err = dev - > set_mac_address ( dev , sa ) ;
kfree ( sa ) ;
2005-04-17 02:20:36 +04:00
if ( err )
2007-06-13 23:03:36 +04:00
goto errout ;
2005-04-17 02:20:36 +04:00
send_addr_notify = 1 ;
2006-08-11 08:17:37 +04:00
modified = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-08-11 08:17:37 +04:00
if ( tb [ IFLA_MTU ] ) {
err = dev_set_mtu ( dev , nla_get_u32 ( tb [ IFLA_MTU ] ) ) ;
if ( err < 0 )
2007-06-13 23:03:36 +04:00
goto errout ;
2006-08-11 08:17:37 +04:00
modified = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-08-11 08:17:37 +04:00
/*
* Interface selected by interface index but interface
* name provided implies that a name change has been
* requested .
*/
2007-06-05 23:40:01 +04:00
if ( ifm - > ifi_index > 0 & & ifname [ 0 ] ) {
2006-08-11 08:17:37 +04:00
err = dev_change_name ( dev , ifname ) ;
if ( err < 0 )
2007-06-13 23:03:36 +04:00
goto errout ;
2006-08-11 08:17:37 +04:00
modified = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-08-11 08:17:37 +04:00
if ( tb [ IFLA_BROADCAST ] ) {
nla_memcpy ( dev - > broadcast , tb [ IFLA_BROADCAST ] , dev - > addr_len ) ;
send_addr_notify = 1 ;
2005-04-17 02:20:36 +04:00
}
2007-05-23 04:00:01 +04:00
if ( ifm - > ifi_flags | | ifm - > ifi_change ) {
unsigned int flags = ifm - > ifi_flags ;
/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
if ( ifm - > ifi_change )
flags = ( flags & ifm - > ifi_change ) |
( dev - > flags & ~ ifm - > ifi_change ) ;
dev_change_flags ( dev , flags ) ;
}
2005-04-17 02:20:36 +04:00
2006-08-11 08:17:37 +04:00
if ( tb [ IFLA_TXQLEN ] )
dev - > tx_queue_len = nla_get_u32 ( tb [ IFLA_TXQLEN ] ) ;
2006-03-21 04:09:11 +03:00
2006-08-11 08:17:37 +04:00
if ( tb [ IFLA_OPERSTATE ] )
set_operstate ( dev , nla_get_u8 ( tb [ IFLA_OPERSTATE ] ) ) ;
2006-03-21 04:09:11 +03:00
2006-08-11 08:17:37 +04:00
if ( tb [ IFLA_LINKMODE ] ) {
2006-03-21 04:09:11 +03:00
write_lock_bh ( & dev_base_lock ) ;
2006-08-11 08:17:37 +04:00
dev - > link_mode = nla_get_u8 ( tb [ IFLA_LINKMODE ] ) ;
2006-03-21 04:09:11 +03:00
write_unlock_bh ( & dev_base_lock ) ;
}
2005-04-17 02:20:36 +04:00
err = 0 ;
2007-06-13 23:03:36 +04:00
errout :
2006-08-11 08:17:37 +04:00
if ( err < 0 & & modified & & net_ratelimit ( ) )
printk ( KERN_WARNING " A link change request failed with "
" some changes comitted already. Interface %s may "
" have been left with an inconsistent configuration, "
" please check. \n " , dev - > name ) ;
2005-04-17 02:20:36 +04:00
if ( send_addr_notify )
call_netdevice_notifiers ( NETDEV_CHANGEADDR , dev ) ;
2007-06-13 23:03:36 +04:00
return err ;
}
2005-04-17 02:20:36 +04:00
2007-06-13 23:03:36 +04:00
static int rtnl_setlink ( struct sk_buff * skb , struct nlmsghdr * nlh , void * arg )
{
struct ifinfomsg * ifm ;
struct net_device * dev ;
int err ;
struct nlattr * tb [ IFLA_MAX + 1 ] ;
char ifname [ IFNAMSIZ ] ;
err = nlmsg_parse ( nlh , sizeof ( * ifm ) , tb , IFLA_MAX , ifla_policy ) ;
if ( err < 0 )
goto errout ;
if ( tb [ IFLA_IFNAME ] )
nla_strlcpy ( ifname , tb [ IFLA_IFNAME ] , IFNAMSIZ ) ;
else
ifname [ 0 ] = ' \0 ' ;
err = - EINVAL ;
ifm = nlmsg_data ( nlh ) ;
if ( ifm - > ifi_index > 0 )
dev = dev_get_by_index ( ifm - > ifi_index ) ;
else if ( tb [ IFLA_IFNAME ] )
dev = dev_get_by_name ( ifname ) ;
else
goto errout ;
if ( dev = = NULL ) {
err = - ENODEV ;
goto errout ;
}
if ( tb [ IFLA_ADDRESS ] & &
nla_len ( tb [ IFLA_ADDRESS ] ) < dev - > addr_len )
goto errout_dev ;
if ( tb [ IFLA_BROADCAST ] & &
nla_len ( tb [ IFLA_BROADCAST ] ) < dev - > addr_len )
goto errout_dev ;
2007-06-13 23:03:51 +04:00
err = do_setlink ( dev , ifm , tb , ifname , 0 ) ;
2007-06-13 23:03:36 +04:00
errout_dev :
2005-04-17 02:20:36 +04:00
dev_put ( dev ) ;
2006-08-11 08:17:37 +04:00
errout :
2005-04-17 02:20:36 +04:00
return err ;
}
2007-06-13 23:03:51 +04:00
static int rtnl_dellink ( struct sk_buff * skb , struct nlmsghdr * nlh , void * arg )
{
const struct rtnl_link_ops * ops ;
struct net_device * dev ;
struct ifinfomsg * ifm ;
char ifname [ IFNAMSIZ ] ;
struct nlattr * tb [ IFLA_MAX + 1 ] ;
int err ;
err = nlmsg_parse ( nlh , sizeof ( * ifm ) , tb , IFLA_MAX , ifla_policy ) ;
if ( err < 0 )
return err ;
if ( tb [ IFLA_IFNAME ] )
nla_strlcpy ( ifname , tb [ IFLA_IFNAME ] , IFNAMSIZ ) ;
ifm = nlmsg_data ( nlh ) ;
if ( ifm - > ifi_index > 0 )
dev = __dev_get_by_index ( ifm - > ifi_index ) ;
else if ( tb [ IFLA_IFNAME ] )
dev = __dev_get_by_name ( ifname ) ;
else
return - EINVAL ;
if ( ! dev )
return - ENODEV ;
ops = dev - > rtnl_link_ops ;
if ( ! ops )
return - EOPNOTSUPP ;
ops - > dellink ( dev ) ;
return 0 ;
}
static int rtnl_newlink ( struct sk_buff * skb , struct nlmsghdr * nlh , void * arg )
{
const struct rtnl_link_ops * ops ;
struct net_device * dev ;
struct ifinfomsg * ifm ;
char kind [ MODULE_NAME_LEN ] ;
char ifname [ IFNAMSIZ ] ;
struct nlattr * tb [ IFLA_MAX + 1 ] ;
struct nlattr * linkinfo [ IFLA_INFO_MAX + 1 ] ;
int err ;
2007-08-01 01:13:50 +04:00
# ifdef CONFIG_KMOD
2007-06-13 23:03:51 +04:00
replay :
2007-08-01 01:13:50 +04:00
# endif
2007-06-13 23:03:51 +04:00
err = nlmsg_parse ( nlh , sizeof ( * ifm ) , tb , IFLA_MAX , ifla_policy ) ;
if ( err < 0 )
return err ;
if ( tb [ IFLA_IFNAME ] )
nla_strlcpy ( ifname , tb [ IFLA_IFNAME ] , IFNAMSIZ ) ;
else
ifname [ 0 ] = ' \0 ' ;
ifm = nlmsg_data ( nlh ) ;
if ( ifm - > ifi_index > 0 )
dev = __dev_get_by_index ( ifm - > ifi_index ) ;
else if ( ifname [ 0 ] )
dev = __dev_get_by_name ( ifname ) ;
else
dev = NULL ;
if ( tb [ IFLA_LINKINFO ] ) {
err = nla_parse_nested ( linkinfo , IFLA_INFO_MAX ,
tb [ IFLA_LINKINFO ] , ifla_info_policy ) ;
if ( err < 0 )
return err ;
} else
memset ( linkinfo , 0 , sizeof ( linkinfo ) ) ;
if ( linkinfo [ IFLA_INFO_KIND ] ) {
nla_strlcpy ( kind , linkinfo [ IFLA_INFO_KIND ] , sizeof ( kind ) ) ;
ops = rtnl_link_ops_get ( kind ) ;
} else {
kind [ 0 ] = ' \0 ' ;
ops = NULL ;
}
if ( 1 ) {
struct nlattr * attr [ ops ? ops - > maxtype + 1 : 0 ] , * * data = NULL ;
if ( ops ) {
if ( ops - > maxtype & & linkinfo [ IFLA_INFO_DATA ] ) {
err = nla_parse_nested ( attr , ops - > maxtype ,
linkinfo [ IFLA_INFO_DATA ] ,
ops - > policy ) ;
if ( err < 0 )
return err ;
data = attr ;
}
if ( ops - > validate ) {
err = ops - > validate ( tb , data ) ;
if ( err < 0 )
return err ;
}
}
if ( dev ) {
int modified = 0 ;
if ( nlh - > nlmsg_flags & NLM_F_EXCL )
return - EEXIST ;
if ( nlh - > nlmsg_flags & NLM_F_REPLACE )
return - EOPNOTSUPP ;
if ( linkinfo [ IFLA_INFO_DATA ] ) {
if ( ! ops | | ops ! = dev - > rtnl_link_ops | |
! ops - > changelink )
return - EOPNOTSUPP ;
err = ops - > changelink ( dev , tb , data ) ;
if ( err < 0 )
return err ;
modified = 1 ;
}
return do_setlink ( dev , ifm , tb , ifname , modified ) ;
}
if ( ! ( nlh - > nlmsg_flags & NLM_F_CREATE ) )
return - ENODEV ;
if ( ifm - > ifi_index | | ifm - > ifi_flags | | ifm - > ifi_change )
return - EOPNOTSUPP ;
2007-07-12 06:42:31 +04:00
if ( tb [ IFLA_MAP ] | | tb [ IFLA_MASTER ] | | tb [ IFLA_PROTINFO ] )
2007-06-13 23:03:51 +04:00
return - EOPNOTSUPP ;
if ( ! ops ) {
# ifdef CONFIG_KMOD
if ( kind [ 0 ] ) {
__rtnl_unlock ( ) ;
request_module ( " rtnl-link-%s " , kind ) ;
rtnl_lock ( ) ;
ops = rtnl_link_ops_get ( kind ) ;
if ( ops )
goto replay ;
}
# endif
return - EOPNOTSUPP ;
}
if ( ! ifname [ 0 ] )
snprintf ( ifname , IFNAMSIZ , " %s%%d " , ops - > kind ) ;
dev = alloc_netdev ( ops - > priv_size , ifname , ops - > setup ) ;
if ( ! dev )
return - ENOMEM ;
if ( strchr ( dev - > name , ' % ' ) ) {
err = dev_alloc_name ( dev , dev - > name ) ;
if ( err < 0 )
goto err_free ;
}
dev - > rtnl_link_ops = ops ;
if ( tb [ IFLA_MTU ] )
dev - > mtu = nla_get_u32 ( tb [ IFLA_MTU ] ) ;
2007-07-12 06:42:31 +04:00
if ( tb [ IFLA_ADDRESS ] )
memcpy ( dev - > dev_addr , nla_data ( tb [ IFLA_ADDRESS ] ) ,
nla_len ( tb [ IFLA_ADDRESS ] ) ) ;
if ( tb [ IFLA_BROADCAST ] )
memcpy ( dev - > broadcast , nla_data ( tb [ IFLA_BROADCAST ] ) ,
nla_len ( tb [ IFLA_BROADCAST ] ) ) ;
2007-06-13 23:03:51 +04:00
if ( tb [ IFLA_TXQLEN ] )
dev - > tx_queue_len = nla_get_u32 ( tb [ IFLA_TXQLEN ] ) ;
if ( tb [ IFLA_OPERSTATE ] )
set_operstate ( dev , nla_get_u8 ( tb [ IFLA_OPERSTATE ] ) ) ;
if ( tb [ IFLA_LINKMODE ] )
dev - > link_mode = nla_get_u8 ( tb [ IFLA_LINKMODE ] ) ;
2007-07-12 06:42:13 +04:00
if ( ops - > newlink )
err = ops - > newlink ( dev , tb , data ) ;
else
err = register_netdevice ( dev ) ;
2007-06-13 23:03:51 +04:00
err_free :
if ( err < 0 )
free_netdev ( dev ) ;
return err ;
}
}
2006-08-05 10:05:34 +04:00
static int rtnl_getlink ( struct sk_buff * skb , struct nlmsghdr * nlh , void * arg )
2006-02-23 02:10:56 +03:00
{
2006-08-05 10:05:34 +04:00
struct ifinfomsg * ifm ;
struct nlattr * tb [ IFLA_MAX + 1 ] ;
struct net_device * dev = NULL ;
struct sk_buff * nskb ;
2006-11-11 01:10:15 +03:00
int err ;
2006-02-23 02:10:56 +03:00
2006-08-05 10:05:34 +04:00
err = nlmsg_parse ( nlh , sizeof ( * ifm ) , tb , IFLA_MAX , ifla_policy ) ;
if ( err < 0 )
2006-09-27 10:26:38 +04:00
return err ;
2006-08-05 10:05:34 +04:00
ifm = nlmsg_data ( nlh ) ;
2007-06-05 23:40:01 +04:00
if ( ifm - > ifi_index > 0 ) {
2006-02-23 02:10:56 +03:00
dev = dev_get_by_index ( ifm - > ifi_index ) ;
2006-08-05 10:05:34 +04:00
if ( dev = = NULL )
return - ENODEV ;
} else
2006-02-23 02:10:56 +03:00
return - EINVAL ;
2007-06-13 23:03:51 +04:00
nskb = nlmsg_new ( if_nlmsg_size ( dev ) , GFP_KERNEL ) ;
2006-08-05 10:05:34 +04:00
if ( nskb = = NULL ) {
err = - ENOBUFS ;
goto errout ;
}
2007-05-23 04:00:49 +04:00
err = rtnl_fill_ifinfo ( nskb , dev , RTM_NEWLINK , NETLINK_CB ( skb ) . pid ,
nlh - > nlmsg_seq , 0 , 0 ) ;
2007-02-01 10:16:40 +03:00
if ( err < 0 ) {
/* -EMSGSIZE implies BUG in if_nlmsg_size */
WARN_ON ( err = = - EMSGSIZE ) ;
kfree_skb ( nskb ) ;
goto errout ;
}
2006-10-12 12:50:30 +04:00
err = rtnl_unicast ( nskb , NETLINK_CB ( skb ) . pid ) ;
2006-08-05 10:05:34 +04:00
errout :
2006-02-23 02:10:56 +03:00
dev_put ( dev ) ;
2006-08-05 10:05:34 +04:00
return err ;
2006-02-23 02:10:56 +03:00
}
2007-04-26 11:57:41 +04:00
static int rtnl_dump_all ( struct sk_buff * skb , struct netlink_callback * cb )
2005-04-17 02:20:36 +04:00
{
int idx ;
int s_idx = cb - > family ;
if ( s_idx = = 0 )
s_idx = 1 ;
for ( idx = 1 ; idx < NPROTO ; idx + + ) {
int type = cb - > nlh - > nlmsg_type - RTM_BASE ;
if ( idx < s_idx | | idx = = PF_PACKET )
continue ;
2007-03-22 21:48:11 +03:00
if ( rtnl_msg_handlers [ idx ] = = NULL | |
rtnl_msg_handlers [ idx ] [ type ] . dumpit = = NULL )
2005-04-17 02:20:36 +04:00
continue ;
if ( idx > s_idx )
memset ( & cb - > args [ 0 ] , 0 , sizeof ( cb - > args ) ) ;
2007-03-22 21:48:11 +03:00
if ( rtnl_msg_handlers [ idx ] [ type ] . dumpit ( skb , cb ) )
2005-04-17 02:20:36 +04:00
break ;
}
cb - > family = idx ;
return skb - > len ;
}
void rtmsg_ifinfo ( int type , struct net_device * dev , unsigned change )
{
struct sk_buff * skb ;
2006-08-15 11:37:09 +04:00
int err = - ENOBUFS ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:03:51 +04:00
skb = nlmsg_new ( if_nlmsg_size ( dev ) , GFP_KERNEL ) ;
2006-08-15 11:37:09 +04:00
if ( skb = = NULL )
goto errout ;
2005-04-17 02:20:36 +04:00
2007-05-23 04:00:49 +04:00
err = rtnl_fill_ifinfo ( skb , dev , type , 0 , 0 , change , 0 ) ;
2007-02-01 10:16:40 +03:00
if ( err < 0 ) {
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
WARN_ON ( err = = - EMSGSIZE ) ;
kfree_skb ( skb ) ;
goto errout ;
}
2006-08-15 11:37:09 +04:00
err = rtnl_notify ( skb , 0 , RTNLGRP_LINK , NULL , GFP_KERNEL ) ;
errout :
if ( err < 0 )
rtnl_set_sk_err ( RTNLGRP_LINK , err ) ;
2005-04-17 02:20:36 +04:00
}
/* Protected by RTNL sempahore. */
static struct rtattr * * rta_buf ;
static int rtattr_max ;
/* Process one rtnetlink message. */
2007-03-23 09:30:12 +03:00
static int rtnetlink_rcv_msg ( struct sk_buff * skb , struct nlmsghdr * nlh )
2005-04-17 02:20:36 +04:00
{
2007-03-22 21:48:11 +03:00
rtnl_doit_func doit ;
2005-04-17 02:20:36 +04:00
int sz_idx , kind ;
int min_len ;
int family ;
int type ;
2007-04-17 03:59:10 +04:00
int err ;
2005-04-17 02:20:36 +04:00
type = nlh - > nlmsg_type ;
if ( type > RTM_MAX )
2007-04-06 01:35:52 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
type - = RTM_BASE ;
/* All the messages must have at least 1 byte length */
if ( nlh - > nlmsg_len < NLMSG_LENGTH ( sizeof ( struct rtgenmsg ) ) )
return 0 ;
family = ( ( struct rtgenmsg * ) NLMSG_DATA ( nlh ) ) - > rtgen_family ;
2007-03-23 09:30:12 +03:00
if ( family > = NPROTO )
return - EAFNOSUPPORT ;
2005-04-17 02:20:36 +04:00
sz_idx = type > > 2 ;
kind = type & 3 ;
2007-03-23 09:30:12 +03:00
if ( kind ! = 2 & & security_netlink_recv ( skb , CAP_NET_ADMIN ) )
return - EPERM ;
2005-04-17 02:20:36 +04:00
if ( kind = = 2 & & nlh - > nlmsg_flags & NLM_F_DUMP ) {
2007-03-22 21:48:11 +03:00
rtnl_dumpit_func dumpit ;
2005-04-17 02:20:36 +04:00
2007-03-22 21:48:11 +03:00
dumpit = rtnl_get_dumpit ( family , type ) ;
if ( dumpit = = NULL )
2007-04-06 01:35:52 +04:00
return - EOPNOTSUPP ;
2005-11-10 04:25:55 +03:00
2007-04-17 03:59:10 +04:00
__rtnl_unlock ( ) ;
err = netlink_dump_start ( rtnl , skb , nlh , dumpit , NULL ) ;
rtnl_lock ( ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
memset ( rta_buf , 0 , ( rtattr_max * sizeof ( struct rtattr * ) ) ) ;
min_len = rtm_min [ sz_idx ] ;
if ( nlh - > nlmsg_len < min_len )
2007-03-23 09:30:12 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( nlh - > nlmsg_len > min_len ) {
int attrlen = nlh - > nlmsg_len - NLMSG_ALIGN ( min_len ) ;
struct rtattr * attr = ( void * ) nlh + NLMSG_ALIGN ( min_len ) ;
while ( RTA_OK ( attr , attrlen ) ) {
unsigned flavor = attr - > rta_type ;
if ( flavor ) {
if ( flavor > rta_max [ sz_idx ] )
2007-03-23 09:30:12 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
rta_buf [ flavor - 1 ] = attr ;
}
attr = RTA_NEXT ( attr , attrlen ) ;
}
}
2007-03-22 21:48:11 +03:00
doit = rtnl_get_doit ( family , type ) ;
if ( doit = = NULL )
2007-04-06 01:35:52 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
2007-03-23 09:30:12 +03:00
return doit ( skb , nlh , ( void * ) & rta_buf [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
}
static void rtnetlink_rcv ( struct sock * sk , int len )
{
2005-11-10 04:25:55 +03:00
unsigned int qlen = 0 ;
2005-05-04 01:55:09 +04:00
2005-04-17 02:20:36 +04:00
do {
2006-03-21 09:23:58 +03:00
mutex_lock ( & rtnl_mutex ) ;
2005-11-10 04:25:55 +03:00
netlink_run_queue ( sk , & qlen , & rtnetlink_rcv_msg ) ;
2006-03-21 09:23:58 +03:00
mutex_unlock ( & rtnl_mutex ) ;
2005-04-17 02:20:36 +04:00
netdev_run_todo ( ) ;
2005-05-04 01:55:09 +04:00
} while ( qlen ) ;
2005-04-17 02:20:36 +04:00
}
static int rtnetlink_event ( struct notifier_block * this , unsigned long event , void * ptr )
{
struct net_device * dev = ptr ;
switch ( event ) {
case NETDEV_UNREGISTER :
rtmsg_ifinfo ( RTM_DELLINK , dev , ~ 0U ) ;
break ;
case NETDEV_REGISTER :
rtmsg_ifinfo ( RTM_NEWLINK , dev , ~ 0U ) ;
break ;
case NETDEV_UP :
case NETDEV_DOWN :
rtmsg_ifinfo ( RTM_NEWLINK , dev , IFF_UP | IFF_RUNNING ) ;
break ;
case NETDEV_CHANGE :
case NETDEV_GOING_DOWN :
break ;
default :
rtmsg_ifinfo ( RTM_NEWLINK , dev , 0 ) ;
break ;
}
return NOTIFY_DONE ;
}
static struct notifier_block rtnetlink_dev_notifier = {
. notifier_call = rtnetlink_event ,
} ;
void __init rtnetlink_init ( void )
{
int i ;
rtattr_max = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( rta_max ) ; i + + )
if ( rta_max [ i ] > rtattr_max )
rtattr_max = rta_max [ i ] ;
rta_buf = kmalloc ( rtattr_max * sizeof ( struct rtattr * ) , GFP_KERNEL ) ;
if ( ! rta_buf )
panic ( " rtnetlink_init: cannot allocate rta_buf \n " ) ;
2005-08-15 23:33:26 +04:00
rtnl = netlink_kernel_create ( NETLINK_ROUTE , RTNLGRP_MAX , rtnetlink_rcv ,
2007-04-17 03:59:10 +04:00
& rtnl_mutex , THIS_MODULE ) ;
2005-04-17 02:20:36 +04:00
if ( rtnl = = NULL )
panic ( " rtnetlink_init: cannot initialize rtnetlink \n " ) ;
netlink_set_nonroot ( NETLINK_ROUTE , NL_NONROOT_RECV ) ;
register_netdevice_notifier ( & rtnetlink_dev_notifier ) ;
2007-03-22 21:49:22 +03:00
rtnl_register ( PF_UNSPEC , RTM_GETLINK , rtnl_getlink , rtnl_dump_ifinfo ) ;
rtnl_register ( PF_UNSPEC , RTM_SETLINK , rtnl_setlink , NULL ) ;
2007-06-13 23:03:51 +04:00
rtnl_register ( PF_UNSPEC , RTM_NEWLINK , rtnl_newlink , NULL ) ;
rtnl_register ( PF_UNSPEC , RTM_DELLINK , rtnl_dellink , NULL ) ;
2007-03-22 21:59:42 +03:00
rtnl_register ( PF_UNSPEC , RTM_GETADDR , NULL , rtnl_dump_all ) ;
rtnl_register ( PF_UNSPEC , RTM_GETROUTE , NULL , rtnl_dump_all ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( __rta_fill ) ;
EXPORT_SYMBOL ( rtattr_strlcpy ) ;
EXPORT_SYMBOL ( rtattr_parse ) ;
2007-06-26 14:23:44 +04:00
EXPORT_SYMBOL ( __rtattr_parse_nested_compat ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( rtnetlink_put_metrics ) ;
EXPORT_SYMBOL ( rtnl_lock ) ;
2006-03-21 09:23:58 +03:00
EXPORT_SYMBOL ( rtnl_trylock ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( rtnl_unlock ) ;
2006-08-15 11:30:25 +04:00
EXPORT_SYMBOL ( rtnl_unicast ) ;
2006-08-15 11:31:41 +04:00
EXPORT_SYMBOL ( rtnl_notify ) ;
EXPORT_SYMBOL ( rtnl_set_sk_err ) ;