2006-05-26 03:00:12 +04:00
/*
* Bridge netlink control interface
*
* Authors :
* Stephen Hemminger < shemminger @ osdl . org >
*
* 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/kernel.h>
2007-03-22 21:59:03 +03:00
# include <net/rtnetlink.h>
2007-09-17 22:56:21 +04:00
# include <net/net_namespace.h>
2006-05-26 03:00:12 +04:00
# include "br_private.h"
2006-11-11 01:10:15 +03:00
static inline size_t br_nlmsg_size ( void )
{
return NLMSG_ALIGN ( sizeof ( struct ifinfomsg ) )
+ nla_total_size ( IFNAMSIZ ) /* IFLA_IFNAME */
+ nla_total_size ( MAX_ADDR_LEN ) /* IFLA_ADDRESS */
+ nla_total_size ( 4 ) /* IFLA_MASTER */
+ nla_total_size ( 4 ) /* IFLA_MTU */
+ nla_total_size ( 4 ) /* IFLA_LINK */
+ nla_total_size ( 1 ) /* IFLA_OPERSTATE */
+ nla_total_size ( 1 ) ; /* IFLA_PROTINFO */
}
2006-05-26 03:00:12 +04:00
/*
* Create one netlink message for one interface
* Contains port and master info as well as carrier and bridge state .
*/
static int br_fill_ifinfo ( struct sk_buff * skb , const struct net_bridge_port * port ,
u32 pid , u32 seq , int event , unsigned int flags )
{
const struct net_bridge * br = port - > br ;
const struct net_device * dev = port - > dev ;
2006-11-21 03:20:22 +03:00
struct ifinfomsg * hdr ;
2006-05-26 03:00:12 +04:00
struct nlmsghdr * nlh ;
u8 operstate = netif_running ( dev ) ? dev - > operstate : IF_OPER_DOWN ;
pr_debug ( " br_fill_info event %d port %s master %s \n " ,
event , dev - > name , br - > dev - > name ) ;
2006-11-21 03:20:22 +03:00
nlh = nlmsg_put ( skb , pid , seq , event , sizeof ( * hdr ) , flags ) ;
if ( nlh = = NULL )
2007-02-01 10:16:40 +03:00
return - EMSGSIZE ;
2006-05-26 03:00:12 +04:00
2006-11-21 03:20:22 +03:00
hdr = nlmsg_data ( nlh ) ;
hdr - > ifi_family = AF_BRIDGE ;
hdr - > __ifi_pad = 0 ;
hdr - > ifi_type = dev - > type ;
hdr - > ifi_index = dev - > ifindex ;
hdr - > ifi_flags = dev_get_flags ( dev ) ;
hdr - > ifi_change = 0 ;
2006-05-26 03:00:12 +04:00
2006-11-21 03:20:22 +03:00
NLA_PUT_STRING ( skb , IFLA_IFNAME , dev - > name ) ;
NLA_PUT_U32 ( skb , IFLA_MASTER , br - > dev - > ifindex ) ;
NLA_PUT_U32 ( skb , IFLA_MTU , dev - > mtu ) ;
NLA_PUT_U8 ( skb , IFLA_OPERSTATE , operstate ) ;
2006-05-26 03:00:12 +04:00
if ( dev - > addr_len )
2006-11-21 03:20:22 +03:00
NLA_PUT ( skb , IFLA_ADDRESS , dev - > addr_len , dev - > dev_addr ) ;
2006-05-26 03:00:12 +04:00
if ( dev - > ifindex ! = dev - > iflink )
2006-11-21 03:20:22 +03:00
NLA_PUT_U32 ( skb , IFLA_LINK , dev - > iflink ) ;
2006-05-26 03:00:12 +04:00
if ( event = = RTM_NEWLINK )
2006-11-21 03:20:22 +03:00
NLA_PUT_U8 ( skb , IFLA_PROTINFO , port - > state ) ;
2006-05-26 03:00:12 +04:00
2006-11-21 03:20:22 +03:00
return nlmsg_end ( skb , nlh ) ;
2006-05-26 03:00:12 +04:00
2006-11-21 03:20:22 +03:00
nla_put_failure :
2007-02-01 10:16:40 +03:00
nlmsg_cancel ( skb , nlh ) ;
return - EMSGSIZE ;
2006-05-26 03:00:12 +04:00
}
/*
* Notify listeners of a change in port information
*/
void br_ifinfo_notify ( int event , struct net_bridge_port * port )
{
struct sk_buff * skb ;
2006-08-15 11:36:28 +04:00
int err = - ENOBUFS ;
2006-05-26 03:00:12 +04:00
pr_debug ( " bridge notify event=%d \n " , event ) ;
2006-11-11 01:10:15 +03:00
skb = nlmsg_new ( br_nlmsg_size ( ) , GFP_ATOMIC ) ;
2006-08-15 11:36:28 +04:00
if ( skb = = NULL )
goto errout ;
err = br_fill_ifinfo ( skb , port , 0 , 0 , event , 0 ) ;
2007-02-01 10:16:40 +03:00
if ( err < 0 ) {
/* -EMSGSIZE implies BUG in br_nlmsg_size() */
WARN_ON ( err = = - EMSGSIZE ) ;
kfree_skb ( skb ) ;
goto errout ;
}
2006-08-15 11:36:28 +04:00
err = rtnl_notify ( skb , 0 , RTNLGRP_LINK , NULL , GFP_ATOMIC ) ;
errout :
2006-08-04 03:24:02 +04:00
if ( err < 0 )
2006-08-15 11:36:28 +04:00
rtnl_set_sk_err ( RTNLGRP_LINK , err ) ;
2006-05-26 03:00:12 +04:00
}
/*
* Dump information about all ports , in response to GETLINK
*/
static int br_dump_ifinfo ( struct sk_buff * skb , struct netlink_callback * cb )
{
struct net_device * dev ;
int idx ;
2007-05-04 02:13:45 +04:00
idx = 0 ;
2007-09-17 22:56:21 +04:00
for_each_netdev ( & init_net , dev ) {
2006-05-26 03:00:12 +04:00
/* not a bridge port */
2006-11-21 03:20:22 +03:00
if ( dev - > br_port = = NULL | | idx < cb - > args [ 0 ] )
goto skip ;
2006-05-26 03:00:12 +04:00
2006-11-21 03:20:22 +03:00
if ( br_fill_ifinfo ( skb , dev - > br_port , NETLINK_CB ( cb - > skb ) . pid ,
cb - > nlh - > nlmsg_seq , RTM_NEWLINK ,
NLM_F_MULTI ) < 0 )
2006-05-26 03:00:12 +04:00
break ;
2006-11-21 03:20:22 +03:00
skip :
2006-05-26 03:00:12 +04:00
+ + idx ;
}
cb - > args [ 0 ] = idx ;
return skb - > len ;
}
/*
* Change state of port ( ie from forwarding to blocking etc )
* Used by spanning tree in user space .
*/
static int br_rtm_setlink ( struct sk_buff * skb , struct nlmsghdr * nlh , void * arg )
{
2006-11-21 03:20:22 +03:00
struct ifinfomsg * ifm ;
struct nlattr * protinfo ;
2006-05-26 03:00:12 +04:00
struct net_device * dev ;
struct net_bridge_port * p ;
u8 new_state ;
2006-11-21 03:20:22 +03:00
if ( nlmsg_len ( nlh ) < sizeof ( * ifm ) )
return - EINVAL ;
ifm = nlmsg_data ( nlh ) ;
2006-05-26 03:00:12 +04:00
if ( ifm - > ifi_family ! = AF_BRIDGE )
return - EPFNOSUPPORT ;
2006-11-21 03:20:22 +03:00
protinfo = nlmsg_find_attr ( nlh , sizeof ( * ifm ) , IFLA_PROTINFO ) ;
if ( ! protinfo | | nla_len ( protinfo ) < sizeof ( u8 ) )
2006-05-26 03:00:12 +04:00
return - EINVAL ;
2006-11-21 03:20:22 +03:00
new_state = nla_get_u8 ( protinfo ) ;
2006-05-26 03:00:12 +04:00
if ( new_state > BR_STATE_BLOCKING )
return - EINVAL ;
2007-09-17 22:56:21 +04:00
dev = __dev_get_by_index ( & init_net , ifm - > ifi_index ) ;
2006-05-26 03:00:12 +04:00
if ( ! dev )
return - ENODEV ;
p = dev - > br_port ;
if ( ! p )
return - EINVAL ;
/* if kernel STP is running, don't allow changes */
2007-03-22 00:22:44 +03:00
if ( p - > br - > stp_enabled = = BR_KERNEL_STP )
2006-05-26 03:00:12 +04:00
return - EBUSY ;
2006-11-21 03:20:22 +03:00
if ( ! netif_running ( dev ) | |
( ! netif_carrier_ok ( dev ) & & new_state ! = BR_STATE_DISABLED ) )
2006-05-26 03:00:12 +04:00
return - ENETDOWN ;
p - > state = new_state ;
br_log_state ( p ) ;
return 0 ;
}
2007-03-22 21:59:03 +03:00
int __init br_netlink_init ( void )
2006-05-26 03:00:12 +04:00
{
2007-03-22 21:59:03 +03:00
if ( __rtnl_register ( PF_BRIDGE , RTM_GETLINK , NULL , br_dump_ifinfo ) )
return - ENOBUFS ;
/* Only the first call to __rtnl_register can fail */
__rtnl_register ( PF_BRIDGE , RTM_SETLINK , br_rtm_setlink , NULL ) ;
return 0 ;
2006-05-26 03:00:12 +04:00
}
void __exit br_netlink_fini ( void )
{
2007-03-22 21:59:03 +03:00
rtnl_unregister_all ( PF_BRIDGE ) ;
2006-05-26 03:00:12 +04:00
}