wifi scan
netlink协议
Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口
不同于ioctl的是,ioctl只能由应用将消息单向发往内核,netlink则完全支持双工通信
netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息
基于socket的通信
Netlink的通道是通过Family来组织的,但随着使用越来越多,Family ID已经不够分配了,所以才有了Generic Netlink
Generic Netlink其实是对Netlink报文中的数据进行了再次封装
依赖库
libnl-3.so //netlink
libnl-genl-3.so //Generic Netlink
结构
enum nl_cb_kind {
NL_CB_DEFAULT, //默认回调处理函数
NL_CB_VERBOSE, //默认处理函数,在default的基础上会将错误信息、无效信息等打印到stderr,且nl_cb_set( ) 或者nl_cl_err( ) 中的arg参数提供FILE*替代stderr
NL_CB_DEBUG, //默认处理函数,在VERBOSE的基础上打印所有信息到终端
NL_CB_CUSTOM, //用户自定义处理函数
__NL_CB_KIND_MAX
} ;
enum nl_cb_action {
NL_OK, //表示处理正常。
NL_SKIP, //表示停止当前netlink消息分析,转而去分析接收buffer中下一条netlink消息(消息分 片的情况)。
NL_STOP, //表示停止此次接收buffer中的消息分析。
} ;
type类型:处理底层不同netlink消息的情况
enum nl_cb_type {
NL_CB_VALID, //有效消息
NL_CB_FINISH, //multipart消息结尾
NL_CB_OVERRUN, //数据丢失报告
NL_CB_SKIPPED, //跳过处理消息
NL_CB_ACK, //确认消息
NL_CB_MSG_IN, //所有接收到的消息
NL_CB_MSG_OUT, //所有发出的消息,除nl_sendto( ) 外
NL_CB_INVALID, //无效消息
NL_CB_SEQ_CHECK,
NL_CB_SEND_ACK,
NL_CB_DUMP_INTR,
__NL_CB_TYPE_MAX
} ;
流程说明
对于从user to kernel的通讯,driver必须先向内核注册一个struct genl_family,并且注册一些cmd的处理函数。这些cmd是跟某个family关联起来的。注册family的时候我们可以让内核自动为这个family分配一个ID。每个family都有一个唯一的ID,其中ID号0x10是被内核的nlctrl family所使用。当注册成功以后,如果user program向某个family发送cmd,那么内核就会回调对应cmd的处理函数。对于user program,使用前,除了要创建一个socket并绑定地址以外,还需要先通过family的名字获取family的ID。有了family的ID以后,才能向该family发送cmd
对于从kernel to user的通讯,采用的是广播的形式,只要user program监听了,都能收到。但是同样的,user program在监听前,也必须先查询到family的ID
流程
wifiscan-> ifname = strdup( IFNAME) ;
wifiscan-> ifindex = if_nametoindex( wifiscan-> ifname) ;
nl80211_init_nl_global( )
--> nl_cb_alloc( )
/*
申请回调函数
wifiscan-> nl_cb = nl_cb_alloc( NL_CB_DEFAULT) ; //NL_CB_DEFAULT:默认处理handler类型
calloc空间,最终调用nl_cb_set( ) 将回调函数及其参数按顺序放入nl_recvmsg_msg_cb_t cb_set[ ] 中
*/
--> nl_create_handle( ) //创建nl_cb对应的nl_handle
/* wifiscan-> nl = nl_create_handle( wifiscan-> nl_cb, "nl" ) ; */
--> nl80211_handle_alloc( cb)
/* struct nl_handle *handle = nl80211_handle_alloc( cb) ; */
--> __alloc_socket( cb) ;
/*
struct nl_sock *sk;
申请空间并初始化sk的参数,诸如s_local.nl_family= AF_NETLINK,s_peer,s_local.nl_pid,s_cb等信息
ps:nl_handle即为nl_sockc
*/
--> genl_connect( handle)
--> nl_connect( handle, NETLINK_GENERIC) ;
/*
sk即handle,protocol即NETLINK_GENERIC
sk-> s_fd = socket( AF_NETLINK, SOCK_RAW | flags, protocol) ;
bind( sk-> s_fd, ( struct sockaddr*) & sk-> s_local,sizeof( sk-> s_local)) //绑定本地信息
*/
--> genl_ctrl_resolve( ) //向内核发命令,根据family name "nl80211" 获得family id ( 将nl连接到 内核中的nl80211模块)
/* wifiscan-> nl80211_id = genl_ctrl_resolve( wifiscan-> nl, "nl80211" ) ; */
--> nl_create_handle( ) //创建接收消息的event socket
/* wifiscan-> nl_event = nl_create_handle( wifiscan-> nl_cb, "event" ) ; */
--> nl_socket_set_nonblocking( wifiscan-> nl_event) ; //设置该handle为非阻塞模式( fcntl)
--> nl_get_multicast_id( ) //将nl_event加入到组播组scan
/* ret = nl_get_multicast_id( wifiscan, "nl80211" , "scan" ) ; */
--> struct nl_msg *msg = nlmsg_alloc( ) //申请一个nl_msg结构用于向内核发送控制消息
--> genlmsg_put( ) //Add Generic Netlink headers to Netlink message
/*
void *genlmsg_put( struct nl_msg *msg, uint32_t port, uint32_t seq, int family, int hdrlen, int flags, uint8_t cmd, uint8_t version)
genlmsg_put( msg, 0, 0, genl_ctrl_resolve( wifiscan-> nl, "nlctrl" ) , 0, 0, CTRL_CMD_GETFAMILY, 0) ;
//port:0,表示发非内核; family为"nlctrl" ,表示该消息为控制消息; cmd:CTRL_CMD_GETFAMILY,消息索引;
*/
--> send_and_recv_msgs( ) //发送消息并接收返回
/* ret = send_and_recv_msgs( wifiscan, msg, family_handler, & res) ; */
--> struct nl_cb *cb = nl_cb_clone( wifiscan-> nl_cb) ; //克隆一个nl_cb
--> nl_send_auto_complete( wifiscan-> nl, msg)
/* 发送生成的帧到内核,内核收到后会执行该消息帧中填充的命令索引和参数,如wifi扫描*/
--> nl_cb_err( cb, NL_CB_CUSTOM, error_handler, & err) ; //注册出错回调
--> nl_cb_set( cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, & err) ; //注册消息完成时的回调
--> nl_cb_set( cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, & err) ; //注册接收ack回调
--> nl_cb_set( cb, NL_CB_VALID, NL_CB_CUSTOM,family_handler, ( void *) res) ; //注册传入的回调函数,处理内核返回的有效消息
--> nl_recvmsgs( wifiscan-> nl, cb) ;
/* 接收内核消息,收到消息后会调用nl_cb_set( ) 注册的函数,具体调用哪个,由消息类型( type) 决定,如收到NL_CB_VALID类型会调用family_handler( ) */
--> family_handler( ) //处理接收的消息
/* 遍历attributes, 并比较其group与传入的group( nl_get_multicast_id( ) 的最后一个参数) ,一致则用该attributes的group id填充res.id项,
该id即为nl_get_multicast_id( ) 的返回值
*/
--> nl_socket_add_membership( )
/* nl_socket_add_membership( wifiscan-> nl_event, res.id) */
--> nl_socket_add_memberships( wifiscan-> nl_event, res.id, 0) //将 res.id代表的group( 即"scan" ) 加入多播组进行监听,监听套接字为nl_event-> s_fd
/* setsockopt( wifiscan-> nl_event-> s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, & res.id, sizeof( res.id)) ; */
--> nl_socket_get_fd( ) //wifiscan-> nl_event_fd = wifiscan-> nl_event-> s_fd
/* wifiscan-> nl_event_fd = nl_socket_get_fd( wifiscan-> nl_event) ; */
--> nl_cb_set( wifiscan-> nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL) ;
--> nl_cb_set( wifiscan-> nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_global_event, wifiscan) ; //设置nl_cb的回调函数 process_global_event( )
struct epoll_event events[ MAX_EVENTS] ;
wifiscan-> nl_ev == > struct epoll_event nl_ev;
wifiscan-> epoll_fd = epoll_create( MAX_EVENTS) ;
wifiscan-> nl_ev.events = EPOLLIN;
wifiscan-> nl_ev.data.fd = wifiscan-> nl_event_fd;
epoll_ctl( wifiscan-> epoll_fd, EPOLL_CTL_ADD, wifiscan-> nl_event_fd, & wifiscan-> nl_ev) //监听nl_event handle上的事件
while( ) {
int nfds = epoll_wait( wifiscan-> epoll_fd, events, MAX_EVENTS, 200) ;
for ( i = 0; i < nfds; ++i) {
if ( events[ i] .data.fd == wifiscan-> nl_event_fd) {
nl80211_event_receive( wifiscan) ;
nl80211_get_scan_results( wifiscan) ;
}
}
}
wifiscan-> params == > struct nl80211_scan_params params;
wifi_scan_start( wifiscan) ;
--> nl80211_scan( wifiscan) ; //请求驱动开始扫描
--> nl80211_scan_common( )
/* struct nl_msg *msg = nl80211_scan_common( wifiscan, NL80211_CMD_TRIGGER_SCAN, & wifiscan-> params,NULL) ; */
--> nlmsg_alloc( ) //申请nl_msg结构
/* struct nl_msg *msg = nlmsg_alloc( ) ;
struct nl_msg *ssids = nlmsg_alloc( ) ;
*/
--> nl80211_cmd( ) //添加Generic Netlink头到Netlink message,参数1指定family
/* nl80211_cmd( wifiscan-> nl80211_id, msg, 0, NL80211_CMD_TRIGGER_SCAN) ; */
--> NLA_PUT_U32( msg, NL80211_ATTR_IFINDEX, wifiscan-> ifindex) ; //Add 32 bit integer attribute to netlink message,指定扫描的节点( 如wlan0)
--> NLA_PUT( ssids, 1, 0, "" ) ; //Add unspecific attribute to netlink message.
--> nla_put_nested( msg, NL80211_ATTR_SCAN_SSIDS, ssids) ; //将ssids嵌套进msg,netlink支持消息嵌套,即属性中携带的数据可以是另外一个nl_msg消息
--> nlmsg_free( ssids) ; //从netlink中释放引用
--> 若需指定扫描信道
/* if ( params-> num_freqs) {
struct nlattr *freqs = nla_nest_start( msg, NL80211_ATTR_SCAN_FREQUENCIES) ; //启动新级别的嵌套属性NL80211_ATTR_SCAN_FREQUENCIES
//循环嵌套所有扫描的信道
for( i = 0; i< params-> num_freqs; i++) {
if( nla_put_u32( msg, i + 1, params-> freqs[ i] ) < 0)
goto fail;
}
nla_nest_end( msg, freqs) ;
}
*/
--> send_and_recv_msgs( ) //发送该msg并接收返回,但不进行返回的处理( 返回为ACK,无有效数据)
/* send_and_recv_msgs( wifiscan, msg, NULL, NULL) */
扫描完成后wifiscan->nl_event->s_fd上监听的事件触发,epoll中接收
接收扫描结果
nl80211_event_receive( wifiscan)
--> nl_recvmsgs( ) //接收内核数据并调用对应回调函数进行处理
/* nl_recvmsgs( wifiscan-> nl_event, wifiscan-> nl_cb)
接收到消息会调用wifiscan-> nl_cb上注册的各回调函数,接收到有效消息时调用的是process_global_event( )
*/
--> process_global_event( )
--> nlmsg_data( ) //计算偏移数目,返回message payload的起始指针
/* struct genlmsghdr *gnlh = nlmsg_data( nlmsg_hdr( msg)) ; */
--> nla_parse( ) //解析attributes
/* struct nlattr *tb[ NL80211_ATTR_MAX + 1] ;
nla_parse( tb, NL80211_ATTR_MAX, genlmsg_attrdata( gnlh, 0) , genlmsg_attrlen( gnlh, 0) , NULL) ; //保存到tb中
*/
--> do_process_drv_event( ) //判断接收到的消息类型,NL80211_CMD_NEW_SCAN_RESULTS代表扫描完成
/* do_process_drv_event( gnlh-> cmd, tb,wifiscan) ;
switch ( cmd) {
.. .
case NL80211_CMD_NEW_SCAN_RESULTS:
wifiscan-> scan_results_ready = 1;
break ;
}
*/
if( wifiscan-> scan_results_ready) //扫描完成
--> nl80211_get_scan_results( wifiscan) ;
--> struct scan_results *res = nl80211_try_get_scan_results( wifiscan) ; //获取最新扫面结果
--> nl80211_cmd( wifiscan-> nl80211_id, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN) ; //添加NL80211_CMD_GET_SCAN命令到msg
--> NLA_PUT_U32( msg, NL80211_ATTR_IFINDEX, wifiscan-> ifindex) ;
--> send_and_recv_msgs( ) //发送消息并注册回调函数bss_info_handler( ) ,收到有效数据时触发
/* send_and_recv_msgs( wifiscan, msg, bss_info_handler, res) ; */
--> bss_info_handler( ) //获取bssid,ssid,freq等值
--> check_bss( res) ; //将bss_info_handler( ) 中填充的res进行最终解析,判断协议及加密类型等
/* 解析消息帧,获取ssid,bssid,freq,strength,加密类型,协议版本等信息 */
bss_info_handler( struct nl_msg *msg, struct scan_results *res)
--> nla_parse( ) //解析消息到struct nlattr *tb[ NL80211_ATTR_MAX + 1] 中
--> nla_parse_nested( struct nlattr *bss, .. ., tb[ NL80211_ATTR_BSS] , ) //解析嵌套在attr中的属性,获取bss段相关信息
--> nla_data( ) //获取bss段中指定段的值
/*
const unsigned char *ie = nla_data( bss[ NL80211_BSS_INFORMATION_ELEMENTS] ) ; //获取ie( information element)
int ie_len = nla_len( bss[ NL80211_BSS_INFORMATION_ELEMENTS] ) ; //ie段长度
const unsigned char *beacon_ie = nla_data( bss[ NL80211_BSS_BEACON_IES] ) ; //beacon ie
int beacon_ie_len = nla_len( bss[ NL80211_BSS_BEACON_IES] ) ; //beacon ie段长度
int level = nla_get_u32( bss[ NL80211_BSS_SIGNAL_MBM] ) ; //signal段为u32数值
struct scan_res *r = calloc( 1,sizeof( *r) + ie_len + beacon_ie_len) ; //存储解析结果
r-> level = level;
memcpy( r-> bssid, nla_data( bss[ NL80211_BSS_BSSID] ) , ETH_ALEN) ;
r-> freq = nla_get_u32( bss[ NL80211_BSS_FREQUENCY] ) ;
r-> beacon_int = nla_get_u8( bss[ NL80211_BSS_BEACON_INTERVAL] ) ;
r-> caps = nla_get_u16( bss[ NL80211_BSS_CAPABILITY] ) ;
r-> age = nla_get_u32( bss[ NL80211_BSS_SEEN_MS_AGO] ) ;
*/
--> 解析ie中内容
/*
int ielen = ie_len;
while ( ielen >= 2 && ielen >= ie[ 1] ) {
/* ie[ 0] 中存储当前段的类型,ie[ 1] 为当前段的大小 */
switch ( ie[ 0] ) {
case WLAN_EID_SSID: //当前段为ssid段
strncpy( r-> ssid,( const char*) ie+2,ie[ 1] ) ;
r-> ssid_len = ie[ 1] ;
break ;
case WLAN_EID_RSN: //rsn,网络安全性
r-> psk_type | = 1<< PSK_RSN; //标志,r-> psk_type += 1<< PSK_RSN
wpa_parse_wpa_ie_rsn( ie,ie[ 1] +2,& r-> rsn) ; //解析rsn段( 具体协议解析)
break ;
case WLAN_EID_HT_OPERATION:
r-> ht_flag = ht_secondary_value[ ie[ 3] & 0x3] ;
case 221:
if ( ie[ 1] >= 4 && ( memcmp( & ie[ 2] , ms_oui, 3) == 0) && ( ie[ 5] == 1)) {
r-> psk_type | = 1<< PSK_WPA;
wpa_parse_wpa_ie_wpa( ie, ie[ 1] +2,& r-> wpa) ;
}
default:
break ;
}
ielen -= ie[ 1] + 2;
ie += ie[ 1] + 2;
}
*/
--> 将r内容给到传入的res中
check_bss( struct scan_results *res)
--> psk_info( res) //psk解析
/*
struct wpa_ie_data *data= NULL;
char *wpa = NULL, *auth = NULL, *enc = NULL;
*/
--> 根据之前解析的psk_type判断协议类型
/*
switch( r-> psk_type) {
case BIT( 1) : //wpa协议
wpa = "WPA" ;
data = & r-> wpa;
break ;
case BIT( 2) : //wpa2( wpa升级版,安全性更高)
wpa = "WPA2" ;
data = & r-> rsn;
break ;
case 6: //两种都支持
wpa = "WPA-WPA2" ;
data = & r-> rsn;
break ;
default:
break ;
}
*/
--> 根据pairwise_cipher值判断加密类型
/*
if( data-> key_mgmt & ( 1<< WPA_KEY_MGMT_PSK)) {
auth = "PSK" ;
} else {
auth = "8021X" ;
}
/* 具体解析原则参见下表 table2 cipher suite selectors */
switch ( data-> pairwise_cipher) {
case BIT( 0) :
enc = "Use group cipher suite" ;
break ;
case BIT( 1) :
enc = "WEP-40" ;
break ;
case BIT( 2) :
enc = "TKIP" ;
break ;
case BIT( 4) :
enc = "AES" ;
break ;
case 0x14:
enc = "AES-TKIP" ;
break ;
case BIT( 5) :
enc = "WEP-104" ;
break ;
case BIT( 6) :
enc = "AES-128-CMAC" ;
break ;
case BIT( 8) :
enc = "GCMP" ;
break ;
default:
enc = NULL;
break ;
}
*/
具体解析原理参见下表1 rsn ie格式部分
wpa_parse_wpa_ie_rsn( const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data)
/*
const unsigned char * pos = ( const u8 *) rsn_ie + sizeof( struct rsn_ie_hdr) ; //跳过头部
int left = rsn_ie_len - sizeof( struct rsn_ie_hdr) ; //剩余大小
*/
--> 解析group data cipher suite
/*
if ( left >= RSN_SELECTOR_LEN) {
data-> group_cipher = 1<< pos[ 3] ;
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
*/
--> 解析pairwise cipher suite count和pairwise cipher suite list
/*
data-> pairwise_cipher = 0;
count = WPA_GET_LE16( pos) ; //获取count
pos += 2;
left -= 2;
/* 轮寻pairwise cipher suite list,获取pairwise cipher suite */
for ( i = 0; i < count; i++) {
data-> pairwise_cipher | = 1<< pos[ 3] ;
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
*/
--> 解析AKM suite count和AKM suite list
/*
if ( left >= 2) { //确保存在AKM suite count段
data-> key_mgmt = 0;
count = WPA_GET_LE16( pos) ;
pos += 2;
left -= 2;
for ( i = 0; i < count; i++) {
data-> key_mgmt | = 1<< pos[ 3] ;
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
}
*/
--> 解析rsn capabilities
/*
if ( left >= 2) {
data-> capabilities = WPA_GET_LE16( pos) ;
pos += 2;
left -= 2;
}
*/
--> 解析PMKID count和PMKID list
/*
if ( left >= 2) {
data-> num_pmkid = WPA_GET_LE16( pos) ;
pos += 2;
left -= 2;
data-> pmkid = pos;
pos += data-> num_pmkid * PMKID_LEN;
left -= data-> num_pmkid * PMKID_LEN;
}
*/
基本解析原理同wpa_parse_wpa_ie_rsn,差异之处为具体协议
table
RSNIE格式
table1 RSNIE format
rsn ie element id length version group data cipher suite pairwise cipher suite count pairwise cipher suite list octets 1 1 2 4 2 4 * m
AKM suite count AKM suite list RSN capabilities PMKID count PMKID list group management cipher suite 2 4 * n 2 2 16 * s 4
不同cipher suite释义对照
table2 cipher suite selectors
OUI suite type meaning 00-0F-AC 0 use group cipher suite 00-0F-AC 1 WEP-40 00-0F-AC 2 TKIP 00-0F-AC 3 reserved 00-0F-AC 4 AES 00-0F-AC 5 WEP-104 00-0F-AC 6 AES-128-CMAC 00-0F-AC 7 not allowed 00-0F-AC 8 GCMP
相关结构
struct wpa_ie_data {
int proto;
int pairwise_cipher;
int group_cipher;
int key_mgmt;
int capabilities;
size_t num_pmkid;
const u8 *pmkid;
int mgmt_group_cipher;
} ;
struct scan_res {
unsigned int flags;
unsigned char bssid[ ETH_ALEN] ;
char ssid[ MAX_SSID_LEN] ;
unsigned char ssid_len;
unsigned char ht_flag;
int freq;
unsigned short beacon_int;
unsigned short caps;
int level;
unsigned int age;
size_t ie_len;
size_t beacon_ie_len;
unsigned int psk_type;
struct wpa_ie_data rsn,wpa;
} ;