2007-09-20 13:09:35 -04:00
/*
* This is the new netlink - based wireless configuration interface .
*
* Copyright 2006 , 2007 Johannes Berg < johannes @ sipsolutions . net >
*/
# include <linux/if.h>
# include <linux/module.h>
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/list.h>
# include <linux/if_ether.h>
# include <linux/ieee80211.h>
# include <linux/nl80211.h>
# include <linux/rtnetlink.h>
# include <linux/netlink.h>
# include <net/genetlink.h>
# include <net/cfg80211.h>
# include "core.h"
# include "nl80211.h"
/* the netlink family */
static struct genl_family nl80211_fam = {
. id = GENL_ID_GENERATE , /* don't bother with a hardcoded ID */
. name = " nl80211 " , /* have users key off the name instead */
. hdrsize = 0 , /* no private header */
. version = 1 , /* no particular meaning now */
. maxattr = NL80211_ATTR_MAX ,
} ;
/* internal helper: get drv and dev */
static int get_drv_dev_by_info_ifindex ( struct genl_info * info ,
struct cfg80211_registered_device * * drv ,
struct net_device * * dev )
{
int ifindex ;
if ( ! info - > attrs [ NL80211_ATTR_IFINDEX ] )
return - EINVAL ;
ifindex = nla_get_u32 ( info - > attrs [ NL80211_ATTR_IFINDEX ] ) ;
* dev = dev_get_by_index ( & init_net , ifindex ) ;
if ( ! * dev )
return - ENODEV ;
* drv = cfg80211_get_dev_from_ifindex ( ifindex ) ;
if ( IS_ERR ( * drv ) ) {
dev_put ( * dev ) ;
return PTR_ERR ( * drv ) ;
}
return 0 ;
}
/* policy for the attributes */
static struct nla_policy nl80211_policy [ NL80211_ATTR_MAX + 1 ] __read_mostly = {
[ NL80211_ATTR_WIPHY ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_WIPHY_NAME ] = { . type = NLA_NUL_STRING ,
. len = BUS_ID_SIZE - 1 } ,
[ NL80211_ATTR_IFTYPE ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_IFINDEX ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_IFNAME ] = { . type = NLA_NUL_STRING , . len = IFNAMSIZ - 1 } ,
2007-12-19 02:03:29 +01:00
[ NL80211_ATTR_MAC ] = { . type = NLA_BINARY , . len = ETH_ALEN } ,
[ NL80211_ATTR_KEY_DATA ] = { . type = NLA_BINARY ,
. len = WLAN_MAX_KEY_LEN } ,
[ NL80211_ATTR_KEY_IDX ] = { . type = NLA_U8 } ,
[ NL80211_ATTR_KEY_CIPHER ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_KEY_DEFAULT ] = { . type = NLA_FLAG } ,
2007-12-19 02:03:32 +01:00
[ NL80211_ATTR_BEACON_INTERVAL ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_DTIM_PERIOD ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_BEACON_HEAD ] = { . type = NLA_BINARY ,
. len = IEEE80211_MAX_DATA_LEN } ,
[ NL80211_ATTR_BEACON_TAIL ] = { . type = NLA_BINARY ,
. len = IEEE80211_MAX_DATA_LEN } ,
2007-12-19 02:03:34 +01:00
[ NL80211_ATTR_STA_AID ] = { . type = NLA_U16 } ,
[ NL80211_ATTR_STA_FLAGS ] = { . type = NLA_NESTED } ,
[ NL80211_ATTR_STA_LISTEN_INTERVAL ] = { . type = NLA_U16 } ,
[ NL80211_ATTR_STA_SUPPORTED_RATES ] = { . type = NLA_BINARY ,
. len = NL80211_MAX_SUPP_RATES } ,
[ NL80211_ATTR_STA_VLAN ] = { . type = NLA_U32 } ,
2007-09-20 13:09:35 -04:00
} ;
/* message building helper */
static inline void * nl80211hdr_put ( struct sk_buff * skb , u32 pid , u32 seq ,
int flags , u8 cmd )
{
/* since there is no private header just add the generic one */
return genlmsg_put ( skb , pid , seq , & nl80211_fam , flags , cmd ) ;
}
/* netlink command implementations */
static int nl80211_send_wiphy ( struct sk_buff * msg , u32 pid , u32 seq , int flags ,
struct cfg80211_registered_device * dev )
{
void * hdr ;
hdr = nl80211hdr_put ( msg , pid , seq , flags , NL80211_CMD_NEW_WIPHY ) ;
if ( ! hdr )
return - 1 ;
NLA_PUT_U32 ( msg , NL80211_ATTR_WIPHY , dev - > idx ) ;
NLA_PUT_STRING ( msg , NL80211_ATTR_WIPHY_NAME , wiphy_name ( & dev - > wiphy ) ) ;
return genlmsg_end ( msg , hdr ) ;
nla_put_failure :
return genlmsg_cancel ( msg , hdr ) ;
}
static int nl80211_dump_wiphy ( struct sk_buff * skb , struct netlink_callback * cb )
{
int idx = 0 ;
int start = cb - > args [ 0 ] ;
struct cfg80211_registered_device * dev ;
mutex_lock ( & cfg80211_drv_mutex ) ;
list_for_each_entry ( dev , & cfg80211_drv_list , list ) {
if ( + + idx < start )
continue ;
if ( nl80211_send_wiphy ( skb , NETLINK_CB ( cb - > skb ) . pid ,
cb - > nlh - > nlmsg_seq , NLM_F_MULTI ,
dev ) < 0 )
break ;
}
mutex_unlock ( & cfg80211_drv_mutex ) ;
cb - > args [ 0 ] = idx ;
return skb - > len ;
}
static int nl80211_get_wiphy ( struct sk_buff * skb , struct genl_info * info )
{
struct sk_buff * msg ;
struct cfg80211_registered_device * dev ;
dev = cfg80211_get_dev_from_info ( info ) ;
if ( IS_ERR ( dev ) )
return PTR_ERR ( dev ) ;
msg = nlmsg_new ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! msg )
goto out_err ;
if ( nl80211_send_wiphy ( msg , info - > snd_pid , info - > snd_seq , 0 , dev ) < 0 )
goto out_free ;
cfg80211_put_dev ( dev ) ;
return genlmsg_unicast ( msg , info - > snd_pid ) ;
out_free :
nlmsg_free ( msg ) ;
out_err :
cfg80211_put_dev ( dev ) ;
return - ENOBUFS ;
}
static int nl80211_set_wiphy ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * rdev ;
int result ;
if ( ! info - > attrs [ NL80211_ATTR_WIPHY_NAME ] )
return - EINVAL ;
rdev = cfg80211_get_dev_from_info ( info ) ;
if ( IS_ERR ( rdev ) )
return PTR_ERR ( rdev ) ;
result = cfg80211_dev_rename ( rdev , nla_data ( info - > attrs [ NL80211_ATTR_WIPHY_NAME ] ) ) ;
cfg80211_put_dev ( rdev ) ;
return result ;
}
static int nl80211_send_iface ( struct sk_buff * msg , u32 pid , u32 seq , int flags ,
struct net_device * dev )
{
void * hdr ;
hdr = nl80211hdr_put ( msg , pid , seq , flags , NL80211_CMD_NEW_INTERFACE ) ;
if ( ! hdr )
return - 1 ;
NLA_PUT_U32 ( msg , NL80211_ATTR_IFINDEX , dev - > ifindex ) ;
NLA_PUT_STRING ( msg , NL80211_ATTR_IFNAME , dev - > name ) ;
/* TODO: interface type */
return genlmsg_end ( msg , hdr ) ;
nla_put_failure :
return genlmsg_cancel ( msg , hdr ) ;
}
static int nl80211_dump_interface ( struct sk_buff * skb , struct netlink_callback * cb )
{
int wp_idx = 0 ;
int if_idx = 0 ;
int wp_start = cb - > args [ 0 ] ;
int if_start = cb - > args [ 1 ] ;
struct cfg80211_registered_device * dev ;
struct wireless_dev * wdev ;
mutex_lock ( & cfg80211_drv_mutex ) ;
list_for_each_entry ( dev , & cfg80211_drv_list , list ) {
if ( + + wp_idx < wp_start )
continue ;
if_idx = 0 ;
mutex_lock ( & dev - > devlist_mtx ) ;
list_for_each_entry ( wdev , & dev - > netdev_list , list ) {
if ( + + if_idx < if_start )
continue ;
if ( nl80211_send_iface ( skb , NETLINK_CB ( cb - > skb ) . pid ,
cb - > nlh - > nlmsg_seq , NLM_F_MULTI ,
wdev - > netdev ) < 0 )
break ;
}
mutex_unlock ( & dev - > devlist_mtx ) ;
}
mutex_unlock ( & cfg80211_drv_mutex ) ;
cb - > args [ 0 ] = wp_idx ;
cb - > args [ 1 ] = if_idx ;
return skb - > len ;
}
static int nl80211_get_interface ( struct sk_buff * skb , struct genl_info * info )
{
struct sk_buff * msg ;
struct cfg80211_registered_device * dev ;
struct net_device * netdev ;
int err ;
err = get_drv_dev_by_info_ifindex ( info , & dev , & netdev ) ;
if ( err )
return err ;
msg = nlmsg_new ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! msg )
goto out_err ;
if ( nl80211_send_iface ( msg , info - > snd_pid , info - > snd_seq , 0 , netdev ) < 0 )
goto out_free ;
dev_put ( netdev ) ;
cfg80211_put_dev ( dev ) ;
return genlmsg_unicast ( msg , info - > snd_pid ) ;
out_free :
nlmsg_free ( msg ) ;
out_err :
dev_put ( netdev ) ;
cfg80211_put_dev ( dev ) ;
return - ENOBUFS ;
}
static int nl80211_set_interface ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err , ifindex ;
enum nl80211_iftype type ;
struct net_device * dev ;
if ( info - > attrs [ NL80211_ATTR_IFTYPE ] ) {
type = nla_get_u32 ( info - > attrs [ NL80211_ATTR_IFTYPE ] ) ;
if ( type > NL80211_IFTYPE_MAX )
return - EINVAL ;
} else
return - EINVAL ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
ifindex = dev - > ifindex ;
dev_put ( dev ) ;
if ( ! drv - > ops - > change_virtual_intf ) {
err = - EOPNOTSUPP ;
goto unlock ;
}
rtnl_lock ( ) ;
err = drv - > ops - > change_virtual_intf ( & drv - > wiphy , ifindex , type ) ;
rtnl_unlock ( ) ;
unlock :
cfg80211_put_dev ( drv ) ;
return err ;
}
static int nl80211_new_interface ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED ;
if ( ! info - > attrs [ NL80211_ATTR_IFNAME ] )
return - EINVAL ;
if ( info - > attrs [ NL80211_ATTR_IFTYPE ] ) {
type = nla_get_u32 ( info - > attrs [ NL80211_ATTR_IFTYPE ] ) ;
if ( type > NL80211_IFTYPE_MAX )
return - EINVAL ;
}
drv = cfg80211_get_dev_from_info ( info ) ;
if ( IS_ERR ( drv ) )
return PTR_ERR ( drv ) ;
if ( ! drv - > ops - > add_virtual_intf ) {
err = - EOPNOTSUPP ;
goto unlock ;
}
rtnl_lock ( ) ;
err = drv - > ops - > add_virtual_intf ( & drv - > wiphy ,
nla_data ( info - > attrs [ NL80211_ATTR_IFNAME ] ) , type ) ;
rtnl_unlock ( ) ;
unlock :
cfg80211_put_dev ( drv ) ;
return err ;
}
static int nl80211_del_interface ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int ifindex , err ;
struct net_device * dev ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
ifindex = dev - > ifindex ;
dev_put ( dev ) ;
if ( ! drv - > ops - > del_virtual_intf ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > del_virtual_intf ( & drv - > wiphy , ifindex ) ;
rtnl_unlock ( ) ;
out :
cfg80211_put_dev ( drv ) ;
return err ;
}
2007-12-19 02:03:29 +01:00
struct get_key_cookie {
struct sk_buff * msg ;
int error ;
} ;
static void get_key_callback ( void * c , struct key_params * params )
{
struct get_key_cookie * cookie = c ;
if ( params - > key )
NLA_PUT ( cookie - > msg , NL80211_ATTR_KEY_DATA ,
params - > key_len , params - > key ) ;
if ( params - > seq )
NLA_PUT ( cookie - > msg , NL80211_ATTR_KEY_SEQ ,
params - > seq_len , params - > seq ) ;
if ( params - > cipher )
NLA_PUT_U32 ( cookie - > msg , NL80211_ATTR_KEY_CIPHER ,
params - > cipher ) ;
return ;
nla_put_failure :
cookie - > error = 1 ;
}
static int nl80211_get_key ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
u8 key_idx = 0 ;
u8 * mac_addr = NULL ;
struct get_key_cookie cookie = {
. error = 0 ,
} ;
void * hdr ;
struct sk_buff * msg ;
if ( info - > attrs [ NL80211_ATTR_KEY_IDX ] )
key_idx = nla_get_u8 ( info - > attrs [ NL80211_ATTR_KEY_IDX ] ) ;
if ( key_idx > 3 )
return - EINVAL ;
if ( info - > attrs [ NL80211_ATTR_MAC ] )
mac_addr = nla_data ( info - > attrs [ NL80211_ATTR_MAC ] ) ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
if ( ! drv - > ops - > get_key ) {
err = - EOPNOTSUPP ;
goto out ;
}
msg = nlmsg_new ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! msg ) {
err = - ENOMEM ;
goto out ;
}
hdr = nl80211hdr_put ( msg , info - > snd_pid , info - > snd_seq , 0 ,
NL80211_CMD_NEW_KEY ) ;
if ( IS_ERR ( hdr ) ) {
err = PTR_ERR ( hdr ) ;
goto out ;
}
cookie . msg = msg ;
NLA_PUT_U32 ( msg , NL80211_ATTR_IFINDEX , dev - > ifindex ) ;
NLA_PUT_U8 ( msg , NL80211_ATTR_KEY_IDX , key_idx ) ;
if ( mac_addr )
NLA_PUT ( msg , NL80211_ATTR_MAC , ETH_ALEN , mac_addr ) ;
rtnl_lock ( ) ;
err = drv - > ops - > get_key ( & drv - > wiphy , dev , key_idx , mac_addr ,
& cookie , get_key_callback ) ;
rtnl_unlock ( ) ;
if ( err )
goto out ;
if ( cookie . error )
goto nla_put_failure ;
genlmsg_end ( msg , hdr ) ;
err = genlmsg_unicast ( msg , info - > snd_pid ) ;
goto out ;
nla_put_failure :
err = - ENOBUFS ;
nlmsg_free ( msg ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
static int nl80211_set_key ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
u8 key_idx ;
if ( ! info - > attrs [ NL80211_ATTR_KEY_IDX ] )
return - EINVAL ;
key_idx = nla_get_u8 ( info - > attrs [ NL80211_ATTR_KEY_IDX ] ) ;
if ( key_idx > 3 )
return - EINVAL ;
/* currently only support setting default key */
if ( ! info - > attrs [ NL80211_ATTR_KEY_DEFAULT ] )
return - EINVAL ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
if ( ! drv - > ops - > set_default_key ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > set_default_key ( & drv - > wiphy , dev , key_idx ) ;
rtnl_unlock ( ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
static int nl80211_new_key ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
struct key_params params ;
u8 key_idx = 0 ;
u8 * mac_addr = NULL ;
memset ( & params , 0 , sizeof ( params ) ) ;
if ( ! info - > attrs [ NL80211_ATTR_KEY_CIPHER ] )
return - EINVAL ;
if ( info - > attrs [ NL80211_ATTR_KEY_DATA ] ) {
params . key = nla_data ( info - > attrs [ NL80211_ATTR_KEY_DATA ] ) ;
params . key_len = nla_len ( info - > attrs [ NL80211_ATTR_KEY_DATA ] ) ;
}
if ( info - > attrs [ NL80211_ATTR_KEY_IDX ] )
key_idx = nla_get_u8 ( info - > attrs [ NL80211_ATTR_KEY_IDX ] ) ;
params . cipher = nla_get_u32 ( info - > attrs [ NL80211_ATTR_KEY_CIPHER ] ) ;
if ( info - > attrs [ NL80211_ATTR_MAC ] )
mac_addr = nla_data ( info - > attrs [ NL80211_ATTR_MAC ] ) ;
if ( key_idx > 3 )
return - EINVAL ;
/*
* Disallow pairwise keys with non - zero index unless it ' s WEP
* ( because current deployments use pairwise WEP keys with
* non - zero indizes but 802.11 i clearly specifies to use zero )
*/
if ( mac_addr & & key_idx & &
params . cipher ! = WLAN_CIPHER_SUITE_WEP40 & &
params . cipher ! = WLAN_CIPHER_SUITE_WEP104 )
return - EINVAL ;
/* TODO: add definitions for the lengths to linux/ieee80211.h */
switch ( params . cipher ) {
case WLAN_CIPHER_SUITE_WEP40 :
if ( params . key_len ! = 5 )
return - EINVAL ;
break ;
case WLAN_CIPHER_SUITE_TKIP :
if ( params . key_len ! = 32 )
return - EINVAL ;
break ;
case WLAN_CIPHER_SUITE_CCMP :
if ( params . key_len ! = 16 )
return - EINVAL ;
break ;
case WLAN_CIPHER_SUITE_WEP104 :
if ( params . key_len ! = 13 )
return - EINVAL ;
break ;
default :
return - EINVAL ;
}
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
if ( ! drv - > ops - > add_key ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > add_key ( & drv - > wiphy , dev , key_idx , mac_addr , & params ) ;
rtnl_unlock ( ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
static int nl80211_del_key ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
u8 key_idx = 0 ;
u8 * mac_addr = NULL ;
if ( info - > attrs [ NL80211_ATTR_KEY_IDX ] )
key_idx = nla_get_u8 ( info - > attrs [ NL80211_ATTR_KEY_IDX ] ) ;
if ( key_idx > 3 )
return - EINVAL ;
if ( info - > attrs [ NL80211_ATTR_MAC ] )
mac_addr = nla_data ( info - > attrs [ NL80211_ATTR_MAC ] ) ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
if ( ! drv - > ops - > del_key ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > del_key ( & drv - > wiphy , dev , key_idx , mac_addr ) ;
rtnl_unlock ( ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
2007-12-19 02:03:32 +01:00
static int nl80211_addset_beacon ( struct sk_buff * skb , struct genl_info * info )
{
int ( * call ) ( struct wiphy * wiphy , struct net_device * dev ,
struct beacon_parameters * info ) ;
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
struct beacon_parameters params ;
int haveinfo = 0 ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
switch ( info - > genlhdr - > cmd ) {
case NL80211_CMD_NEW_BEACON :
/* these are required for NEW_BEACON */
if ( ! info - > attrs [ NL80211_ATTR_BEACON_INTERVAL ] | |
! info - > attrs [ NL80211_ATTR_DTIM_PERIOD ] | |
! info - > attrs [ NL80211_ATTR_BEACON_HEAD ] ) {
err = - EINVAL ;
goto out ;
}
call = drv - > ops - > add_beacon ;
break ;
case NL80211_CMD_SET_BEACON :
call = drv - > ops - > set_beacon ;
break ;
default :
WARN_ON ( 1 ) ;
err = - EOPNOTSUPP ;
goto out ;
}
if ( ! call ) {
err = - EOPNOTSUPP ;
goto out ;
}
memset ( & params , 0 , sizeof ( params ) ) ;
if ( info - > attrs [ NL80211_ATTR_BEACON_INTERVAL ] ) {
params . interval =
nla_get_u32 ( info - > attrs [ NL80211_ATTR_BEACON_INTERVAL ] ) ;
haveinfo = 1 ;
}
if ( info - > attrs [ NL80211_ATTR_DTIM_PERIOD ] ) {
params . dtim_period =
nla_get_u32 ( info - > attrs [ NL80211_ATTR_DTIM_PERIOD ] ) ;
haveinfo = 1 ;
}
if ( info - > attrs [ NL80211_ATTR_BEACON_HEAD ] ) {
params . head = nla_data ( info - > attrs [ NL80211_ATTR_BEACON_HEAD ] ) ;
params . head_len =
nla_len ( info - > attrs [ NL80211_ATTR_BEACON_HEAD ] ) ;
haveinfo = 1 ;
}
if ( info - > attrs [ NL80211_ATTR_BEACON_TAIL ] ) {
params . tail = nla_data ( info - > attrs [ NL80211_ATTR_BEACON_TAIL ] ) ;
params . tail_len =
nla_len ( info - > attrs [ NL80211_ATTR_BEACON_TAIL ] ) ;
haveinfo = 1 ;
}
if ( ! haveinfo ) {
err = - EINVAL ;
goto out ;
}
rtnl_lock ( ) ;
err = call ( & drv - > wiphy , dev , & params ) ;
rtnl_unlock ( ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
static int nl80211_del_beacon ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
if ( ! drv - > ops - > del_beacon ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > del_beacon ( & drv - > wiphy , dev ) ;
rtnl_unlock ( ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
2007-12-19 02:03:34 +01:00
static const struct nla_policy sta_flags_policy [ NL80211_STA_FLAG_MAX + 1 ] = {
[ NL80211_STA_FLAG_AUTHORIZED ] = { . type = NLA_FLAG } ,
[ NL80211_STA_FLAG_SHORT_PREAMBLE ] = { . type = NLA_FLAG } ,
[ NL80211_STA_FLAG_WME ] = { . type = NLA_FLAG } ,
} ;
static int parse_station_flags ( struct nlattr * nla , u32 * staflags )
{
struct nlattr * flags [ NL80211_STA_FLAG_MAX + 1 ] ;
int flag ;
* staflags = 0 ;
if ( ! nla )
return 0 ;
if ( nla_parse_nested ( flags , NL80211_STA_FLAG_MAX ,
nla , sta_flags_policy ) )
return - EINVAL ;
* staflags = STATION_FLAG_CHANGED ;
for ( flag = 1 ; flag < = NL80211_STA_FLAG_MAX ; flag + + )
if ( flags [ flag ] )
* staflags | = ( 1 < < flag ) ;
return 0 ;
}
2007-12-19 02:03:36 +01:00
static int nl80211_send_station ( struct sk_buff * msg , u32 pid , u32 seq ,
int flags , struct net_device * dev ,
u8 * mac_addr , struct station_stats * stats )
{
void * hdr ;
struct nlattr * statsattr ;
hdr = nl80211hdr_put ( msg , pid , seq , flags , NL80211_CMD_NEW_STATION ) ;
if ( ! hdr )
return - 1 ;
NLA_PUT_U32 ( msg , NL80211_ATTR_IFINDEX , dev - > ifindex ) ;
NLA_PUT ( msg , NL80211_ATTR_MAC , ETH_ALEN , mac_addr ) ;
statsattr = nla_nest_start ( msg , NL80211_ATTR_STA_STATS ) ;
if ( ! statsattr )
goto nla_put_failure ;
if ( stats - > filled & STATION_STAT_INACTIVE_TIME )
NLA_PUT_U32 ( msg , NL80211_STA_STAT_INACTIVE_TIME ,
stats - > inactive_time ) ;
if ( stats - > filled & STATION_STAT_RX_BYTES )
NLA_PUT_U32 ( msg , NL80211_STA_STAT_RX_BYTES ,
stats - > rx_bytes ) ;
if ( stats - > filled & STATION_STAT_TX_BYTES )
NLA_PUT_U32 ( msg , NL80211_STA_STAT_TX_BYTES ,
stats - > tx_bytes ) ;
nla_nest_end ( msg , statsattr ) ;
return genlmsg_end ( msg , hdr ) ;
nla_put_failure :
return genlmsg_cancel ( msg , hdr ) ;
}
2007-12-19 02:03:34 +01:00
static int nl80211_get_station ( struct sk_buff * skb , struct genl_info * info )
{
2007-12-19 02:03:36 +01:00
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
struct station_stats stats ;
struct sk_buff * msg ;
u8 * mac_addr = NULL ;
memset ( & stats , 0 , sizeof ( stats ) ) ;
if ( ! info - > attrs [ NL80211_ATTR_MAC ] )
return - EINVAL ;
mac_addr = nla_data ( info - > attrs [ NL80211_ATTR_MAC ] ) ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
if ( ! drv - > ops - > get_station ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > get_station ( & drv - > wiphy , dev , mac_addr , & stats ) ;
rtnl_unlock ( ) ;
msg = nlmsg_new ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! msg )
goto out ;
if ( nl80211_send_station ( msg , info - > snd_pid , info - > snd_seq , 0 ,
dev , mac_addr , & stats ) < 0 )
goto out_free ;
err = genlmsg_unicast ( msg , info - > snd_pid ) ;
goto out ;
out_free :
nlmsg_free ( msg ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
2007-12-19 02:03:34 +01:00
}
/*
* Get vlan interface making sure it is on the right wiphy .
*/
static int get_vlan ( struct nlattr * vlanattr ,
struct cfg80211_registered_device * rdev ,
struct net_device * * vlan )
{
* vlan = NULL ;
if ( vlanattr ) {
* vlan = dev_get_by_index ( & init_net , nla_get_u32 ( vlanattr ) ) ;
if ( ! * vlan )
return - ENODEV ;
if ( ! ( * vlan ) - > ieee80211_ptr )
return - EINVAL ;
if ( ( * vlan ) - > ieee80211_ptr - > wiphy ! = & rdev - > wiphy )
return - EINVAL ;
}
return 0 ;
}
static int nl80211_set_station ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
struct station_parameters params ;
u8 * mac_addr = NULL ;
memset ( & params , 0 , sizeof ( params ) ) ;
params . listen_interval = - 1 ;
if ( info - > attrs [ NL80211_ATTR_STA_AID ] )
return - EINVAL ;
if ( ! info - > attrs [ NL80211_ATTR_MAC ] )
return - EINVAL ;
mac_addr = nla_data ( info - > attrs [ NL80211_ATTR_MAC ] ) ;
if ( info - > attrs [ NL80211_ATTR_STA_SUPPORTED_RATES ] ) {
params . supported_rates =
nla_data ( info - > attrs [ NL80211_ATTR_STA_SUPPORTED_RATES ] ) ;
params . supported_rates_len =
nla_len ( info - > attrs [ NL80211_ATTR_STA_SUPPORTED_RATES ] ) ;
}
if ( info - > attrs [ NL80211_ATTR_STA_LISTEN_INTERVAL ] )
params . listen_interval =
nla_get_u16 ( info - > attrs [ NL80211_ATTR_STA_LISTEN_INTERVAL ] ) ;
if ( parse_station_flags ( info - > attrs [ NL80211_ATTR_STA_FLAGS ] ,
& params . station_flags ) )
return - EINVAL ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
err = get_vlan ( info - > attrs [ NL80211_ATTR_STA_VLAN ] , drv , & params . vlan ) ;
if ( err )
goto out ;
if ( ! drv - > ops - > change_station ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > change_station ( & drv - > wiphy , dev , mac_addr , & params ) ;
rtnl_unlock ( ) ;
out :
if ( params . vlan )
dev_put ( params . vlan ) ;
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
static int nl80211_new_station ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
struct station_parameters params ;
u8 * mac_addr = NULL ;
memset ( & params , 0 , sizeof ( params ) ) ;
if ( ! info - > attrs [ NL80211_ATTR_MAC ] )
return - EINVAL ;
if ( ! info - > attrs [ NL80211_ATTR_STA_AID ] )
return - EINVAL ;
if ( ! info - > attrs [ NL80211_ATTR_STA_LISTEN_INTERVAL ] )
return - EINVAL ;
if ( ! info - > attrs [ NL80211_ATTR_STA_SUPPORTED_RATES ] )
return - EINVAL ;
mac_addr = nla_data ( info - > attrs [ NL80211_ATTR_MAC ] ) ;
params . supported_rates =
nla_data ( info - > attrs [ NL80211_ATTR_STA_SUPPORTED_RATES ] ) ;
params . supported_rates_len =
nla_len ( info - > attrs [ NL80211_ATTR_STA_SUPPORTED_RATES ] ) ;
params . listen_interval =
nla_get_u16 ( info - > attrs [ NL80211_ATTR_STA_LISTEN_INTERVAL ] ) ;
params . listen_interval = nla_get_u16 ( info - > attrs [ NL80211_ATTR_STA_AID ] ) ;
if ( parse_station_flags ( info - > attrs [ NL80211_ATTR_STA_FLAGS ] ,
& params . station_flags ) )
return - EINVAL ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
err = get_vlan ( info - > attrs [ NL80211_ATTR_STA_VLAN ] , drv , & params . vlan ) ;
if ( err )
goto out ;
if ( ! drv - > ops - > add_station ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > add_station ( & drv - > wiphy , dev , mac_addr , & params ) ;
rtnl_unlock ( ) ;
out :
if ( params . vlan )
dev_put ( params . vlan ) ;
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
static int nl80211_del_station ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
int err ;
struct net_device * dev ;
u8 * mac_addr = NULL ;
if ( info - > attrs [ NL80211_ATTR_MAC ] )
mac_addr = nla_data ( info - > attrs [ NL80211_ATTR_MAC ] ) ;
err = get_drv_dev_by_info_ifindex ( info , & drv , & dev ) ;
if ( err )
return err ;
if ( ! drv - > ops - > del_station ) {
err = - EOPNOTSUPP ;
goto out ;
}
rtnl_lock ( ) ;
err = drv - > ops - > del_station ( & drv - > wiphy , dev , mac_addr ) ;
rtnl_unlock ( ) ;
out :
cfg80211_put_dev ( drv ) ;
dev_put ( dev ) ;
return err ;
}
2007-09-20 13:09:35 -04:00
static struct genl_ops nl80211_ops [ ] = {
{
. cmd = NL80211_CMD_GET_WIPHY ,
. doit = nl80211_get_wiphy ,
. dumpit = nl80211_dump_wiphy ,
. policy = nl80211_policy ,
/* can be retrieved by unprivileged users */
} ,
{
. cmd = NL80211_CMD_SET_WIPHY ,
. doit = nl80211_set_wiphy ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_GET_INTERFACE ,
. doit = nl80211_get_interface ,
. dumpit = nl80211_dump_interface ,
. policy = nl80211_policy ,
/* can be retrieved by unprivileged users */
} ,
{
. cmd = NL80211_CMD_SET_INTERFACE ,
. doit = nl80211_set_interface ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_NEW_INTERFACE ,
. doit = nl80211_new_interface ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_DEL_INTERFACE ,
. doit = nl80211_del_interface ,
. policy = nl80211_policy ,
2007-12-19 02:03:29 +01:00
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_GET_KEY ,
. doit = nl80211_get_key ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_SET_KEY ,
. doit = nl80211_set_key ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_NEW_KEY ,
. doit = nl80211_new_key ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_DEL_KEY ,
. doit = nl80211_del_key ,
. policy = nl80211_policy ,
2007-09-20 13:09:35 -04:00
. flags = GENL_ADMIN_PERM ,
} ,
2007-12-19 02:03:32 +01:00
{
. cmd = NL80211_CMD_SET_BEACON ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
. doit = nl80211_addset_beacon ,
} ,
{
. cmd = NL80211_CMD_NEW_BEACON ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
. doit = nl80211_addset_beacon ,
} ,
{
. cmd = NL80211_CMD_DEL_BEACON ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
. doit = nl80211_del_beacon ,
} ,
2007-12-19 02:03:34 +01:00
{
. cmd = NL80211_CMD_GET_STATION ,
. doit = nl80211_get_station ,
/* TODO: implement dumpit */
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_SET_STATION ,
. doit = nl80211_set_station ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_NEW_STATION ,
. doit = nl80211_new_station ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NL80211_CMD_DEL_STATION ,
. doit = nl80211_del_station ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
} ,
2007-09-20 13:09:35 -04:00
} ;
/* multicast groups */
static struct genl_multicast_group nl80211_config_mcgrp = {
. name = " config " ,
} ;
/* notification functions */
void nl80211_notify_dev_rename ( struct cfg80211_registered_device * rdev )
{
struct sk_buff * msg ;
msg = nlmsg_new ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( ! msg )
return ;
if ( nl80211_send_wiphy ( msg , 0 , 0 , 0 , rdev ) < 0 ) {
nlmsg_free ( msg ) ;
return ;
}
genlmsg_multicast ( msg , 0 , nl80211_config_mcgrp . id , GFP_KERNEL ) ;
}
/* initialisation/exit functions */
int nl80211_init ( void )
{
int err , i ;
err = genl_register_family ( & nl80211_fam ) ;
if ( err )
return err ;
for ( i = 0 ; i < ARRAY_SIZE ( nl80211_ops ) ; i + + ) {
err = genl_register_ops ( & nl80211_fam , & nl80211_ops [ i ] ) ;
if ( err )
goto err_out ;
}
err = genl_register_mc_group ( & nl80211_fam , & nl80211_config_mcgrp ) ;
if ( err )
goto err_out ;
return 0 ;
err_out :
genl_unregister_family ( & nl80211_fam ) ;
return err ;
}
void nl80211_exit ( void )
{
genl_unregister_family ( & nl80211_fam ) ;
}