天天看点

hostapd源代码分析(三):管理帧的收发和处理

这篇文章我来讲解一下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, 0, NULL, NULL, 0); //通过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), 0); //读取从原始套接字接收的帧
...
while (1) {
    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 -= 4;
        break;
    case IEEE80211_RADIOTAP_RX_FLAGS: //接收(Rx)帧(一般是“请求帧”)
        rxflags = 1;
        break;
    case IEEE80211_RADIOTAP_TX_FLAGS: //发送(Tx)帧(一般是“回应帧”)
        injected = 1;
        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 * 5;
        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 0;

	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[0];
		else
			return 0;
	}

	os_memset(&fi, 0, sizeof(fi));
	fi.datarate = rx_mgmt->datarate;
	fi.ssi_signal = rx_mgmt->ssi_signal;

	if (hapd == HAPD_BROADCAST) { //广播帧
		size_t i;
		ret = 0;
                //将广播帧发送给每一个BSS
		for (i = 0; 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) > 0)
				ret = 1;
		}
	} 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),根据具体的帧来执行相应的操作。

继续阅读