hostapd源代码分析(三):管理帧的收发和处理
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来建立监视接口。
1 drv->monitor_ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, 0, NULL, NULL, 0); //通过Netlink通知内核新建一个监视接口
监视接口建立以后,通过socket来接收原始帧,并把socket注册到event loop中(关于event loop,请参考《hostapd源代码分析(二):》)
1 //建立原始套接字 2 drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 3 ... 4 //把原始套接字的描述符和回调函数handle_monitor_read注册到event loop 5 if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, drv, NULL)) { 6 wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); 7 goto error; 8 }
二、接收和处理管理帧
当原始套接字接收到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函数的部分代码如下。
1 len = recv(sock, buf, sizeof(buf), 0); //读取从原始套接字接收的帧 2 ... 3 while (1) { 4 ret = ieee80211_radiotap_iterator_next(&iter); //抽取radiotap报头 5 if (ret == -ENOENT) 6 break; 7 if (ret) { 8 wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)", ret); 9 return; 10 } 11 switch (iter.this_arg_index) { 12 case IEEE80211_RADIOTAP_FLAGS: 13 if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) 14 len -= 4; 15 break; 16 case IEEE80211_RADIOTAP_RX_FLAGS: //接收(Rx)帧(一般是“请求帧”) 17 rxflags = 1; 18 break; 19 case IEEE80211_RADIOTAP_TX_FLAGS: //发送(Tx)帧(一般是“回应帧”) 20 injected = 1; 21 failed = le_to_host16((*(uint16_t *) iter.this_arg)) & IEEE80211_RADIOTAP_F_TX_FAIL; 22 break; 23 case IEEE80211_RADIOTAP_DATA_RETRIES: 24 break; 25 case IEEE80211_RADIOTAP_CHANNEL: 26 /* TODO: convert from freq/flags to channel number */ 27 break; 28 case IEEE80211_RADIOTAP_RATE: 29 datarate = *iter.this_arg * 5; 30 break; 31 case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: 32 ssi_signal = (s8) *iter.this_arg; 33 break; 34 } 35 } 36 37 if (rxflags && injected) 38 return; 39 40 if (!injected) 41 handle_frame(drv, buf + iter._max_length, len - iter._max_length, datarate, ssi_signal); //处理“请求帧” 42 else 43 handle_tx_callback(drv->ctx, buf + iter._max_length, len - iter._max_length, !failed); //处理“发送帧” 44 }
然后,进入handle_frame后,再调用wpa_supplicant_event(位于src/ap/drv_callbacks.c)来进一步处理。对于“请求帧”,会调用hostapd_mgmt_rx(位于src/ap/drv_callbacks.c)。hostapd_mgmt_rx的代码如下:
1 static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) 2 { 3 struct hostapd_iface *iface = hapd->iface; 4 const struct ieee80211_hdr *hdr; 5 const u8 *bssid; 6 struct hostapd_frame_info fi; 7 int ret; 8 9 hdr = (const struct ieee80211_hdr *) rx_mgmt->frame; 10 bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); //获取该帧所属的BSSID 11 if (bssid == NULL) 12 return 0; 13 14 hapd = get_hapd_bssid(iface, bssid); //根据BSSID获取相应的BSS 15 if (hapd == NULL) { //相应的BSS不存在,则抛弃不处理。 16 u16 fc; 17 fc = le_to_host16(hdr->frame_control); 18 19 /* 20 * Drop frames to unknown BSSIDs except for Beacon frames which 21 * could be used to update neighbor information. 22 */ 23 if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && 24 WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) 25 hapd = iface->bss[0]; 26 else 27 return 0; 28 } 29 30 os_memset(&fi, 0, sizeof(fi)); 31 fi.datarate = rx_mgmt->datarate; 32 fi.ssi_signal = rx_mgmt->ssi_signal; 33 34 if (hapd == HAPD_BROADCAST) { //广播帧 35 size_t i; 36 ret = 0; 37 //将广播帧发送给每一个BSS 38 for (i = 0; i < iface->num_bss; i++) { 39 /* if bss is set, driver will call this function for 40 * each bss individually. */ 41 if (rx_mgmt->drv_priv && 42 (iface->bss[i]->drv_priv != rx_mgmt->drv_priv)) 43 continue; 44 45 if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, 46 rx_mgmt->frame_len, &fi) > 0) 47 ret = 1; 48 } 49 } else //单播帧 50 ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, 51 &fi); 52 53 random_add_randomness(&fi, sizeof(fi)); 54 55 return ret; 56 }
接下来,继续调用ieee802_11_mgmt(位于src/ap/ieee80211.c),根据具体的帧来执行相应的操作。