@ -411,6 +411,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[ NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR ] = { . len = ETH_ALEN } ,
[ NL80211_ATTR_NAN_MASTER_PREF ] = { . type = NLA_U8 } ,
[ NL80211_ATTR_NAN_DUAL ] = { . type = NLA_U8 } ,
[ NL80211_ATTR_NAN_FUNC ] = { . type = NLA_NESTED } ,
} ;
/* policy for the key attributes */
@ -504,6 +505,39 @@ nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
} ,
} ;
/* policy for NAN function attributes */
static const struct nla_policy
nl80211_nan_func_policy [ NL80211_NAN_FUNC_ATTR_MAX + 1 ] = {
[ NL80211_NAN_FUNC_TYPE ] = { . type = NLA_U8 } ,
[ NL80211_NAN_FUNC_SERVICE_ID ] = { . type = NLA_BINARY ,
. len = NL80211_NAN_FUNC_SERVICE_ID_LEN } ,
[ NL80211_NAN_FUNC_PUBLISH_TYPE ] = { . type = NLA_U8 } ,
[ NL80211_NAN_FUNC_PUBLISH_BCAST ] = { . type = NLA_FLAG } ,
[ NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE ] = { . type = NLA_FLAG } ,
[ NL80211_NAN_FUNC_FOLLOW_UP_ID ] = { . type = NLA_U8 } ,
[ NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID ] = { . type = NLA_U8 } ,
[ NL80211_NAN_FUNC_FOLLOW_UP_DEST ] = { . len = ETH_ALEN } ,
[ NL80211_NAN_FUNC_CLOSE_RANGE ] = { . type = NLA_FLAG } ,
[ NL80211_NAN_FUNC_TTL ] = { . type = NLA_U32 } ,
[ NL80211_NAN_FUNC_SERVICE_INFO ] = { . type = NLA_BINARY ,
. len = NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN } ,
[ NL80211_NAN_FUNC_SRF ] = { . type = NLA_NESTED } ,
[ NL80211_NAN_FUNC_RX_MATCH_FILTER ] = { . type = NLA_NESTED } ,
[ NL80211_NAN_FUNC_TX_MATCH_FILTER ] = { . type = NLA_NESTED } ,
[ NL80211_NAN_FUNC_INSTANCE_ID ] = { . type = NLA_U8 } ,
[ NL80211_NAN_FUNC_TERM_REASON ] = { . type = NLA_U8 } ,
} ;
/* policy for Service Response Filter attributes */
static const struct nla_policy
nl80211_nan_srf_policy [ NL80211_NAN_SRF_ATTR_MAX + 1 ] = {
[ NL80211_NAN_SRF_INCLUDE ] = { . type = NLA_FLAG } ,
[ NL80211_NAN_SRF_BF ] = { . type = NLA_BINARY ,
. len = NL80211_NAN_FUNC_SRF_MAX_LEN } ,
[ NL80211_NAN_SRF_BF_IDX ] = { . type = NLA_U8 } ,
[ NL80211_NAN_SRF_MAC_ADDRS ] = { . type = NLA_NESTED } ,
} ;
static int nl80211_prepare_wdev_dump ( struct sk_buff * skb ,
struct netlink_callback * cb ,
struct cfg80211_registered_device * * rdev ,
@ -10566,6 +10600,325 @@ static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info)
return 0 ;
}
static int validate_nan_filter ( struct nlattr * filter_attr )
{
struct nlattr * attr ;
int len = 0 , n_entries = 0 , rem ;
nla_for_each_nested ( attr , filter_attr , rem ) {
len + = nla_len ( attr ) ;
n_entries + + ;
}
if ( len > = U8_MAX )
return - EINVAL ;
return n_entries ;
}
static int handle_nan_filter ( struct nlattr * attr_filter ,
struct cfg80211_nan_func * func ,
bool tx )
{
struct nlattr * attr ;
int n_entries , rem , i ;
struct cfg80211_nan_func_filter * filter ;
n_entries = validate_nan_filter ( attr_filter ) ;
if ( n_entries < 0 )
return n_entries ;
BUILD_BUG_ON ( sizeof ( * func - > rx_filters ) ! = sizeof ( * func - > tx_filters ) ) ;
filter = kcalloc ( n_entries , sizeof ( * func - > rx_filters ) , GFP_KERNEL ) ;
if ( ! filter )
return - ENOMEM ;
i = 0 ;
nla_for_each_nested ( attr , attr_filter , rem ) {
filter [ i ] . filter = kmemdup ( nla_data ( attr ) , nla_len ( attr ) ,
GFP_KERNEL ) ;
filter [ i ] . len = nla_len ( attr ) ;
i + + ;
}
if ( tx ) {
func - > num_tx_filters = n_entries ;
func - > tx_filters = filter ;
} else {
func - > num_rx_filters = n_entries ;
func - > rx_filters = filter ;
}
return 0 ;
}
static int nl80211_nan_add_func ( struct sk_buff * skb ,
struct genl_info * info )
{
struct cfg80211_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct wireless_dev * wdev = info - > user_ptr [ 1 ] ;
struct nlattr * tb [ NUM_NL80211_NAN_FUNC_ATTR ] , * func_attr ;
struct cfg80211_nan_func * func ;
struct sk_buff * msg = NULL ;
void * hdr = NULL ;
int err = 0 ;
if ( wdev - > iftype ! = NL80211_IFTYPE_NAN )
return - EOPNOTSUPP ;
if ( ! wdev - > nan_started )
return - ENOTCONN ;
if ( ! info - > attrs [ NL80211_ATTR_NAN_FUNC ] )
return - EINVAL ;
if ( wdev - > owner_nlportid & &
wdev - > owner_nlportid ! = info - > snd_portid )
return - ENOTCONN ;
err = nla_parse ( tb , NL80211_NAN_FUNC_ATTR_MAX ,
nla_data ( info - > attrs [ NL80211_ATTR_NAN_FUNC ] ) ,
nla_len ( info - > attrs [ NL80211_ATTR_NAN_FUNC ] ) ,
nl80211_nan_func_policy ) ;
if ( err )
return err ;
func = kzalloc ( sizeof ( * func ) , GFP_KERNEL ) ;
if ( ! func )
return - ENOMEM ;
func - > cookie = wdev - > wiphy - > cookie_counter + + ;
if ( ! tb [ NL80211_NAN_FUNC_TYPE ] | |
nla_get_u8 ( tb [ NL80211_NAN_FUNC_TYPE ] ) > NL80211_NAN_FUNC_MAX_TYPE ) {
err = - EINVAL ;
goto out ;
}
func - > type = nla_get_u8 ( tb [ NL80211_NAN_FUNC_TYPE ] ) ;
if ( ! tb [ NL80211_NAN_FUNC_SERVICE_ID ] ) {
err = - EINVAL ;
goto out ;
}
memcpy ( func - > service_id , nla_data ( tb [ NL80211_NAN_FUNC_SERVICE_ID ] ) ,
sizeof ( func - > service_id ) ) ;
func - > close_range =
nla_get_flag ( tb [ NL80211_NAN_FUNC_CLOSE_RANGE ] ) ;
if ( tb [ NL80211_NAN_FUNC_SERVICE_INFO ] ) {
func - > serv_spec_info_len =
nla_len ( tb [ NL80211_NAN_FUNC_SERVICE_INFO ] ) ;
func - > serv_spec_info =
kmemdup ( nla_data ( tb [ NL80211_NAN_FUNC_SERVICE_INFO ] ) ,
func - > serv_spec_info_len ,
GFP_KERNEL ) ;
if ( ! func - > serv_spec_info ) {
err = - ENOMEM ;
goto out ;
}
}
if ( tb [ NL80211_NAN_FUNC_TTL ] )
func - > ttl = nla_get_u32 ( tb [ NL80211_NAN_FUNC_TTL ] ) ;
switch ( func - > type ) {
case NL80211_NAN_FUNC_PUBLISH :
if ( ! tb [ NL80211_NAN_FUNC_PUBLISH_TYPE ] ) {
err = - EINVAL ;
goto out ;
}
func - > publish_type =
nla_get_u8 ( tb [ NL80211_NAN_FUNC_PUBLISH_TYPE ] ) ;
func - > publish_bcast =
nla_get_flag ( tb [ NL80211_NAN_FUNC_PUBLISH_BCAST ] ) ;
if ( ( ! ( func - > publish_type & NL80211_NAN_SOLICITED_PUBLISH ) ) & &
func - > publish_bcast ) {
err = - EINVAL ;
goto out ;
}
break ;
case NL80211_NAN_FUNC_SUBSCRIBE :
func - > subscribe_active =
nla_get_flag ( tb [ NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE ] ) ;
break ;
case NL80211_NAN_FUNC_FOLLOW_UP :
if ( ! tb [ NL80211_NAN_FUNC_FOLLOW_UP_ID ] | |
! tb [ NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID ] ) {
err = - EINVAL ;
goto out ;
}
func - > followup_id =
nla_get_u8 ( tb [ NL80211_NAN_FUNC_FOLLOW_UP_ID ] ) ;
func - > followup_reqid =
nla_get_u8 ( tb [ NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID ] ) ;
memcpy ( func - > followup_dest . addr ,
nla_data ( tb [ NL80211_NAN_FUNC_FOLLOW_UP_DEST ] ) ,
sizeof ( func - > followup_dest . addr ) ) ;
if ( func - > ttl ) {
err = - EINVAL ;
goto out ;
}
break ;
default :
err = - EINVAL ;
goto out ;
}
if ( tb [ NL80211_NAN_FUNC_SRF ] ) {
struct nlattr * srf_tb [ NUM_NL80211_NAN_SRF_ATTR ] ;
err = nla_parse ( srf_tb , NL80211_NAN_SRF_ATTR_MAX ,
nla_data ( tb [ NL80211_NAN_FUNC_SRF ] ) ,
nla_len ( tb [ NL80211_NAN_FUNC_SRF ] ) , NULL ) ;
if ( err )
goto out ;
func - > srf_include =
nla_get_flag ( srf_tb [ NL80211_NAN_SRF_INCLUDE ] ) ;
if ( srf_tb [ NL80211_NAN_SRF_BF ] ) {
if ( srf_tb [ NL80211_NAN_SRF_MAC_ADDRS ] | |
! srf_tb [ NL80211_NAN_SRF_BF_IDX ] ) {
err = - EINVAL ;
goto out ;
}
func - > srf_bf_len =
nla_len ( srf_tb [ NL80211_NAN_SRF_BF ] ) ;
func - > srf_bf =
kmemdup ( nla_data ( srf_tb [ NL80211_NAN_SRF_BF ] ) ,
func - > srf_bf_len , GFP_KERNEL ) ;
if ( ! func - > srf_bf ) {
err = - ENOMEM ;
goto out ;
}
func - > srf_bf_idx =
nla_get_u8 ( srf_tb [ NL80211_NAN_SRF_BF_IDX ] ) ;
} else {
struct nlattr * attr , * mac_attr =
srf_tb [ NL80211_NAN_SRF_MAC_ADDRS ] ;
int n_entries , rem , i = 0 ;
if ( ! mac_attr ) {
err = - EINVAL ;
goto out ;
}
n_entries = validate_acl_mac_addrs ( mac_attr ) ;
if ( n_entries < = 0 ) {
err = - EINVAL ;
goto out ;
}
func - > srf_num_macs = n_entries ;
func - > srf_macs =
kzalloc ( sizeof ( * func - > srf_macs ) * n_entries ,
GFP_KERNEL ) ;
if ( ! func - > srf_macs ) {
err = - ENOMEM ;
goto out ;
}
nla_for_each_nested ( attr , mac_attr , rem )
memcpy ( func - > srf_macs [ i + + ] . addr , nla_data ( attr ) ,
sizeof ( * func - > srf_macs ) ) ;
}
}
if ( tb [ NL80211_NAN_FUNC_TX_MATCH_FILTER ] ) {
err = handle_nan_filter ( tb [ NL80211_NAN_FUNC_TX_MATCH_FILTER ] ,
func , true ) ;
if ( err )
goto out ;
}
if ( tb [ NL80211_NAN_FUNC_RX_MATCH_FILTER ] ) {
err = handle_nan_filter ( tb [ NL80211_NAN_FUNC_RX_MATCH_FILTER ] ,
func , false ) ;
if ( err )
goto out ;
}
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg ) {
err = - ENOMEM ;
goto out ;
}
hdr = nl80211hdr_put ( msg , info - > snd_portid , info - > snd_seq , 0 ,
NL80211_CMD_ADD_NAN_FUNCTION ) ;
/* This can't really happen - we just allocated 4KB */
if ( WARN_ON ( ! hdr ) ) {
err = - ENOMEM ;
goto out ;
}
err = rdev_add_nan_func ( rdev , wdev , func ) ;
out :
if ( err < 0 ) {
cfg80211_free_nan_func ( func ) ;
nlmsg_free ( msg ) ;
return err ;
}
/* propagate the instance id and cookie to userspace */
if ( nla_put_u64_64bit ( msg , NL80211_ATTR_COOKIE , func - > cookie ,
NL80211_ATTR_PAD ) )
goto nla_put_failure ;
func_attr = nla_nest_start ( msg , NL80211_ATTR_NAN_FUNC ) ;
if ( ! func_attr )
goto nla_put_failure ;
if ( nla_put_u8 ( msg , NL80211_NAN_FUNC_INSTANCE_ID ,
func - > instance_id ) )
goto nla_put_failure ;
nla_nest_end ( msg , func_attr ) ;
genlmsg_end ( msg , hdr ) ;
return genlmsg_reply ( msg , info ) ;
nla_put_failure :
nlmsg_free ( msg ) ;
return - ENOBUFS ;
}
static int nl80211_nan_del_func ( struct sk_buff * skb ,
struct genl_info * info )
{
struct cfg80211_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct wireless_dev * wdev = info - > user_ptr [ 1 ] ;
u64 cookie ;
if ( wdev - > iftype ! = NL80211_IFTYPE_NAN )
return - EOPNOTSUPP ;
if ( ! wdev - > nan_started )
return - ENOTCONN ;
if ( ! info - > attrs [ NL80211_ATTR_COOKIE ] )
return - EINVAL ;
if ( wdev - > owner_nlportid & &
wdev - > owner_nlportid ! = info - > snd_portid )
return - ENOTCONN ;
cookie = nla_get_u64 ( info - > attrs [ NL80211_ATTR_COOKIE ] ) ;
rdev_del_nan_func ( rdev , wdev , cookie ) ;
return 0 ;
}
static int nl80211_get_protocol_features ( struct sk_buff * skb ,
struct genl_info * info )
{
@ -11923,6 +12276,22 @@ static const struct genl_ops nl80211_ops[] = {
. internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL ,
} ,
{
. cmd = NL80211_CMD_ADD_NAN_FUNCTION ,
. doit = nl80211_nan_add_func ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL ,
} ,
{
. cmd = NL80211_CMD_DEL_NAN_FUNCTION ,
. doit = nl80211_nan_del_func ,
. policy = nl80211_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL ,
} ,
{
. cmd = NL80211_CMD_SET_MCAST_RATE ,
. doit = nl80211_set_mcast_rate ,