hostapd源代码分析(三):管理帧的收发和处理
原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379
这篇文章我来讲解一下hostapd是如何处理IEEE 802.11管理帧的。我们知道,hostapd主要负责管理工作站(station)认证和接入。因此,它只处理管理帧(Management Frame),并不处理数据帧。802.11的管理帧主要有信标帧(beacon)、探测请求帧(probe request)、探测回应帧(probe response)、请求认证帧(authentication request)、认证回应帧(authentication response)、请求关联帧(association request)和关联回应帧(association response)等。hostapd在初始化的阶段,会将无线网卡转换为AP模式,并且建立监视接口(Monitor Interface,一般是mon.wlan0),这个监视接口主要负责接收802.11管理帧。内核收到管理帧后,就会把它送回用户空间的hostapd来处理。
一、建立监视接口
基于nl80211驱动的hostapd在初始化的时候,会调用位于src/driver/driver_nl80211.c的nl80211_create_monitor_interface来建立监视接口。
drv->monitor_ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, , NULL, NULL, ); //通过Netlink通知内核新建一个监视接口
监视接口建立以后,通过socket来接收原始帧,并把socket注册到event loop中(关于event loop,请参考《hostapd源代码分析(二):》)
//建立原始套接字
drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
...
//把原始套接字的描述符和回调函数handle_monitor_read注册到event loop
if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, drv, NULL)) {
wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
goto error;
}
二、接收和处理管理帧
当原始套接字接收到802.11管理帧后,会调用handle_monitor_read来进一步处理。handle_monitor_read中,会根据Rx或者Tx标志来调用handle_frame或者handle_tx_callback函数来处理。根据我的理解和分析,一般handle_frame处理“请求”帧,比如probe request帧,authentication request帧,等等;handle_tx_callback一般用来处理“回应”帧,比如,authentication response帧,association response帧等。handle_monitor_read函数的部分代码如下。
len = recv(sock, buf, sizeof(buf), ); //读取从原始套接字接收的帧
...
while () {
ret = ieee80211_radiotap_iterator_next(&iter); //抽取radiotap报头
if (ret == -ENOENT)
break;
if (ret) {
wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)", ret);
return;
}
switch (iter.this_arg_index) {
case IEEE80211_RADIOTAP_FLAGS:
if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
len -= ;
break;
case IEEE80211_RADIOTAP_RX_FLAGS: //接收(Rx)帧(一般是“请求帧”)
rxflags = ;
break;
case IEEE80211_RADIOTAP_TX_FLAGS: //发送(Tx)帧(一般是“回应帧”)
injected = ;
failed = le_to_host16((*(uint16_t *) iter.this_arg)) & IEEE80211_RADIOTAP_F_TX_FAIL;
break;
case IEEE80211_RADIOTAP_DATA_RETRIES:
break;
case IEEE80211_RADIOTAP_CHANNEL:
/* TODO: convert from freq/flags to channel number */
break;
case IEEE80211_RADIOTAP_RATE:
datarate = *iter.this_arg * ;
break;
case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
ssi_signal = (s8) *iter.this_arg;
break;
}
} if (rxflags && injected)
return; if (!injected)
handle_frame(drv, buf + iter._max_length, len - iter._max_length, datarate, ssi_signal); //处理“请求帧”
else
handle_tx_callback(drv->ctx, buf + iter._max_length, len - iter._max_length, !failed); //处理“发送帧”
}
然后,进入handle_frame后,再调用wpa_supplicant_event(位于src/ap/drv_callbacks.c)来进一步处理。对于“请求帧”,会调用hostapd_mgmt_rx(位于src/ap/drv_callbacks.c)。hostapd_mgmt_rx的代码如下:
static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
{
struct hostapd_iface *iface = hapd->iface;
const struct ieee80211_hdr *hdr;
const u8 *bssid;
struct hostapd_frame_info fi;
int ret; hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); //获取该帧所属的BSSID
if (bssid == NULL)
return ; hapd = get_hapd_bssid(iface, bssid); //根据BSSID获取相应的BSS
if (hapd == NULL) { //相应的BSS不存在,则抛弃不处理。
u16 fc;
fc = le_to_host16(hdr->frame_control); /*
* Drop frames to unknown BSSIDs except for Beacon frames which
* could be used to update neighbor information.
*/
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
hapd = iface->bss[];
else
return ;
} os_memset(&fi, , sizeof(fi));
fi.datarate = rx_mgmt->datarate;
fi.ssi_signal = rx_mgmt->ssi_signal; if (hapd == HAPD_BROADCAST) { //广播帧
size_t i;
ret = ;
//将广播帧发送给每一个BSS
for (i = ; i < iface->num_bss; i++) {
/* if bss is set, driver will call this function for
* each bss individually. */
if (rx_mgmt->drv_priv &&
(iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
continue; if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
rx_mgmt->frame_len, &fi) > )
ret = ;
}
} else //单播帧
ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
&fi); random_add_randomness(&fi, sizeof(fi)); return ret;
}
接下来,继续调用ieee802_11_mgmt(位于src/ap/ieee80211.c),根据具体的帧来执行相应的操作。