---
tags: vwifi
---
# vwifi 程式碼閱讀
struct owl_context: 表示最基本的結構體,整個 driver 只會有一個
```
struct owl_context {
/* We may not need this lock, cause vif_list would not change during
* the whole lifetime.
*/
struct mutex lock;
/* Indicate the program state */
enum vwifi_state state;
/* List for maintaining all interfaces */
struct list_head vif_list;
/* List for maintaining multiple AP */
struct list_head ap_list;
};
```
struct owl_vif: net_device 的 private data 用來表示 Virtual interface,不論是 STA 或 AP 或 Ad-hoc 都是用同一個(主要是因為裡面用 union 來區分)
另外可以從下方知道 STA mode 主要有四個 MLME 邏輯的實作:
```
struct work_struct ws_connect, ws_disconnect;
struct work_struct ws_scan, ws_scan_timeout;
```
全部實作完後這些函式的位址會被填入 cfg80211_ops 結構體中
```
static struct cfg80211_ops owl_cfg_ops = {
.change_virtual_intf = owl_change_iface,
.scan = owl_scan,
.connect = owl_connect,
.disconnect = owl_disconnect,
.get_station = owl_get_station,
.start_ap = owl_start_ap,
.stop_ap = owl_stop_ap,
};
```
### MLME 函式實作
#### change_virtual_intf
用來改變 virtual interface 的 type
參考 [brcmf_cfg80211_change_iface](https://elixir.bootlin.com/linux/latest/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c#L1199)
這邊就只是單純的改變 virtual interface 的 iftype
```
switch (type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
ndev->ieee80211_ptr->iftype = type;
break;
default:
pr_info("owl: invalid interface type %u\n", type);
return -EINVAL;
}
```
#### scan
當 user 做 scan 時,kernel 會執行 owl_scan
首先透過 wdev_get_owl_vif 取得 virtual interface `owl_vif *vif`,然後取得 vif->lock 鎖後,做 request 和 IFTYPE 的檢查後就把 request 填入 interface。
```
vif->scan_request = request
```
最後把鎖解開後就執行 schedule_work 把 scan 這項任務加入 CFS。
真正在做 scan 的函式是 owl_scan_routine
在 vwifi 裡並不會真正去掃描,只會做 timeout 的檢查。
```
mod_timer(&vif->scan_timeout, jiffies + msecs_to_jiffies(SCAN_TIMEOUT_MS));
```
#### timeout
scan 時如果 timeout 就會呼叫 owl_scan_timeout,然後就會把 timeout 加入排程,owl_scan_timeout_work。
:::info
timeout 裡面執行 cfg80211_scan_done,所以一定會觸發 timeout?
:::
inform_bss 用來通知 kernel 掃描的結果
主要是用 cfg80211_inform_bss_data 做這件事
其中的參數有 cfg80211_inform_bss data,表示頻道、頻寬、訊號
```
struct cfg80211_inform_bss data = {
/* the only channel */
.chan = &ap->wdev.wiphy->bands[NL80211_BAND_2GHZ]->channels[0],
.scan_width = NL80211_BSS_CHAN_WIDTH_20,
.signal = DBM_TO_MBM(rand_int_smooth(-100, -30, jiffies)),
};
```
CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is from a beacon or probe response。
bssid 表示 MAC address,共 48 bits。
```
char bssid[6] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
```
infornation element(ie): 用來放入 frame 的欄位內,表示 scan 掃描到的 AP。
第一個 byte 表示 ie 類型,第二個 byte 表示後面接著的資料長度。
```
u8 *ie = kmalloc(ap->ssid_len + 2, GFP_KERNEL);
ie[0] = WLAN_EID_SSID;
ie[1] = ap->ssid_len;
memcpy(ie + 2, ap->ssid, ap->ssid_len);
```
cfg80211_put_bss 用來減少 cfg80211_inform_bss_data 回傳的 cfg80211_bss 結構體的 refcounter,用來決定要不要 free。
回到 owl_scan_timeout_work 最後會用 cfg80211_scan_done 通知 kernel 掃描已經結束。
```
if (mutex_lock_interruptible(&vif->lock))
return;
/* finish scan */
cfg80211_scan_done(vif->scan_request, &info);
vif->scan_request = NULL;
mutex_unlock(&vif->lock);
```
#### connect
由 kernel 執行 owl_connect 開始,一樣先檢查 STA 是否已連線或目前的 IFTYPE 是否為 STA。
接著把 req_ssid 改為想要連線的 SSID
```
vif->sme_state = SME_CONNECTING;
vif->ssid_len = sme->ssid_len;
memcpy(vif->req_ssid, sme->ssid, sme->ssid_len);
```
:::info
這邊感覺 req_ssid 連完要改回來,因為已經是已連線的狀態了,req_ssid 比較像是正在請求連線的狀態。
:::
然後把 owl_connect_routine 加入排程
owl_connect_routine
vif 代表自己(要求連線方)的 vitrual interface。
ap 代表被連線方的 vitrual interface。
```
struct owl_vif *vif = container_of(w, struct owl_vif, ws_connect);
struct owl_vif *ap = NULL;
```
接下來用 list_for_each_entry 走訪整個 ap_list,如果 ap->ssid 跟 vif->req_ssid 一樣就開始連線。
vwifi 實作連線的部分只用 cfg80211_connect_result 來回傳連線成功。
然後修改 vif 的各個設定:
```
memcpy(vif->ssid, ap->ssid, ap->ssid_len);
memcpy(vif->bssid, ap->bssid, ETH_ALEN);
vif->sme_state = SME_CONNECTED;
vif->conn_time = jiffies;
vif->ap = ap;
```
接著是將 vif 加入 bss_list 的最後(AP 是 head)。
```
list_add_tail(&vif->bss_list, &ap->bss_list);
```
#### disconnect
kernel 呼叫 owl_disconnect 來執行 disconnect,前面得判斷邏輯與 connect 相同,不過 disconnect 還需要加入 reason code
```
vif->disconnect_reason_code = reason_code;
```
接下來看到 owl_disconnect_routine
在呼叫 cfg80211_disconnected 之後,driver 會進入 idle 狀態並且不會嘗試去連線其他 AP。
>After it calls this function, the driver should enter an idle state and not try to connect to any AP any more.
```
cfg80211_disconnected(vif->ndev, vif->disconnect_reason_code, NULL, 0, true,
GFP_KERNEL);
```
接著更新一些參數
```
vif->disconnect_reason_code = 0;
vif->sme_state = SME_DISCONNECTED;
```
把這個 vif(節點) 從 bss_list 移除,表示這個節點已經沒有與這個 AP(bss_list head) 連線了。
```
list_del(&vif->bss_list);
```
最後把這個 vif 的 ap 清空即可
```
vif->ap = NULL;
```
以上是 STA 的 MLME 的實作
接著看 AP 的 MLME 的實作
#### get_station
BIT_ULL 定義在 `/include/linux/bitops.h`,代表將 nr 向左位移 1 位然後轉型為 unsigned long long。
```
#define BIT_ULL(nr) (1ULL << (nr))
```
get_station 主要在做的事就是將資訊填入 station_info
```
struct station_info *sinfo
```
如果有填就把 sinfo->filled 的對應的 bit 改成 1,vwifi 這邊有填入的值有
```
sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
BIT_ULL(NL80211_STA_INFO_TX_FAILED) |
BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
BIT_ULL(NL80211_STA_INFO_SIGNAL) |
BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
```
最後再依據目前是否已連線來設定 CONNECTED_TIME
```
if (vif->sme_state == SME_CONNECTED) {
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);
sinfo->connected_time =
jiffies_to_msecs(jiffies - vif->conn_time) / 1000;
}
```
#### start_ap
需要開始 AP mode 時由 kernel 執行 owl_start_ap。
首先印出 `struct cfg80211_ap_settings *settings` 各個參數。
```
pr_info("owl: %s start acting in AP mode.\n", ndev->name);
pr_info("ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,",
settings->chandef.chan->hw_value, settings->chandef.center_freq1,
settings->chandef.width, settings->beacon_interval,
settings->dtim_period);
pr_info("ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d", settings->ssid,
settings->ssid_len, settings->auth_type,
settings->inactivity_timeout);
```
然後 vwifi 目前 start_up 的就只是簡單的設定 AP 的 SSID 和 BSSID。
```
vif->ssid_len = settings->ssid_len;
memcpy(vif->ssid, settings->ssid, settings->ssid_len);
memcpy(vif->bssid, vif->ndev->dev_addr, ETH_ALEN);
```
然後依照定義,將這個 AP 設為 bss_list 的 head
```
/* AP is the head of vif->bss_list */
INIT_LIST_HEAD(&vif->bss_list);
```
最後再把這個 AP 加入 ap_list
```
/* Add AP to global ap_list */
list_add_tail(&vif->ap_list, &owl->ap_list);
```
#### stop_ap
stop_ap 的操作其實與 start_ap 對應。
因為是停掉一個 AP,所以對其連線的 STA 都要從 bss_list 刪掉。
:::info
不需要其他斷開連線的操作(比如通知連線的 STA, 變更 STA 的 sme_state, etc),是因為不需要嗎
:::
```
list_for_each_entry_safe (pos, safe, &vif->bss_list, bss_list)
list_del(&pos->bss_list);
```
最後將這個 AP 從 ap_list 移除
```
list_del(&vif->ap_list);
```
#### change_virtual_intf
:::info
不知道 owl_vif 是怎麼改的,因為初始化的時候都是固定 STA mode
:::
change the interface type
```
static int owl_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type,
struct vif_params *params)
{
switch (type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
ndev->ieee80211_ptr->iftype = type;
break;
default:
pr_info("owl: invalid interface type %u\n", type);
return -EINVAL;
}
return 0;
}
```
### 初始化
#### vwifi_init
先配置整個 driver 唯一的 context,然後初始化裡面的參數。
```
owl = kmalloc(sizeof(struct owl_context), GFP_KERNEL);
...
mutex_init(&owl->lock);
INIT_LIST_HEAD(&owl->vif_list);
INIT_LIST_HEAD(&owl->ap_list);
```
接下來依照 station 數量來建立虛擬網路設備
首先要先建立 nl80211 上用來描述一個的設備結構體 wiphy,通過 owl_cfg80211_add 來建立。
#### owl_cfg80211_add
:::info
註解的這部分 `which will call cfg80211_ops->add_iface()`,我去查 [cfg80211_ops](https://www.kernel.org/doc/html/v4.12/driver-api/80211/cfg80211.html) 好像沒有這個成員。
:::
建立 wiphy 的函式可以用 wiphy_new_nm 或 wiphy_new,差在要不要自己取名。
```
/* NULL means use the default phy%d naming. */
wiphy = wiphy_new_nm(&owl_cfg_ops, 0, NULL);
```
設定這個設備支援的 interface type (AP、STA)。
```
wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
```
接著填入這個設備的支援 band 以及可以 scan 的最大數量。
```
wiphy->bands[NL80211_BAND_2GHZ] = &nf_band_2ghz;
wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
```
設定 signal strength value
```
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
```
定義在 `/include/net/cfg80211.h`
```
/**
* enum cfg80211_signal_type - signal type
*
* @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available
* @CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm)
* @CFG80211_SIGNAL_TYPE_UNSPEC: signal strength, increasing from 0 through 100
*/
enum cfg80211_signal_type {
CFG80211_SIGNAL_TYPE_NONE,
CFG80211_SIGNAL_TYPE_MBM,
CFG80211_SIGNAL_TYPE_UNSPEC,
};
```
最後在 cfg80211 註冊這個 wiphy
```
if (wiphy_register(wiphy) < 0) {
pr_info("couldn't register wiphy device\n");
goto l_error_wiphy_register;
}
```
#### owl_interface_add
接下來要替這個設備建立一個虛擬介面(virtual interface)。
首先先 allocate 一個網路設備
```
ndev = alloc_netdev(sizeof(struct owl_vif), NDEV_NAME, NET_NAME_ENUM,
ether_setup);
```
第一個參數 sizeof(struct owl_vif) 代表 private data 的空間大小,這邊 private data 是指向該設備的 virtual interface。
然後是基本的網路設備的初始化函式 ether_setup。
```
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_header_ops;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->min_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->min_mtu = ETH_MIN_MTU;
dev->max_mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
dev->priv_flags |= IFF_TX_SKB_SHARING;
eth_broadcast_addr(dev->broadcast);
}
```
接下來就是填入 vitrual interface 的資料,
其中有個結構體 wireless_dev 用來表示設備的無線介面,要把 net_device 的 ieee80211_ptr 指向 wireless_dev 結構體。
>the network interface’s ieee80211_ptr pointer to a struct wireless_dev which further describes the wireless part of the interface, normally this struct is embedded in the network interface’s private data area.
```
/* fill private data of network context. */
vif = ndev_get_owl_vif(ndev);
vif->ndev = ndev;
/* fill wireless_dev context.
* wireless_dev with net_device can be represented as inherited class of
* single net_device.
*/
vif->wdev.wiphy = wiphy;
vif->wdev.netdev = ndev;
vif->wdev.iftype = NL80211_IFTYPE_STATION;
vif->ndev->ieee80211_ptr = &vif->wdev;
/* set network device hooks. should implement ndo_start_xmit() at least */
vif->ndev->netdev_ops = &owl_ndev_ops;
/* Add here proper net_device initialization */
vif->ndev->features |= NETIF_F_HW_CSUM;
```
值得一提的地方是設定 MAC address 地方,用的方法是直接將設備名稱複製過去,不過要注意的是要避免使用到 multicast 的 MAC address,也就是說整個 MAC address 最高位的 byte 的最低位 bit 不能是 1,所以這邊就直接設定成 0。
:::info
這邊的 snprintf 的 size 部分,我覺得應該可以是 ETH_ALEN - 1,因為 intf_name[ETH_ALEN] 已經填 0 了。
:::
```
/* The first byte is '\0' to avoid being a multicast
* address (the first byte of multicast addrs is odd).
*/
char intf_name[ETH_ALEN] = {0};
snprintf(intf_name + 1, ETH_ALEN, "%s%d", NAME_PREFIX, if_idx);
memcpy(vif->ndev->dev_addr, intf_name, ETH_ALEN);
```
最後就註冊這個 net_device 就可以了
```
if (register_netdev(vif->ndev))
goto l_error_ndev_register;
```
以上可以參考 [brcmf_cfg80211_request_sta_if](https://elixir.bootlin.com/linux/latest/C/ident/brcmf_cfg80211_request_sta_if)
接下來就是繼續 vif 的初始化
```
/* Initialize connection information */
memset(vif->bssid, 0, ETH_ALEN);
memset(vif->ssid, 0, IEEE80211_MAX_SSID_LEN);
memset(vif->req_ssid, 0, IEEE80211_MAX_SSID_LEN);
vif->scan_request = NULL;
vif->sme_state = SME_DISCONNECTED;
vif->conn_time = 0;
vif->active_time = 0;
vif->disconnect_reason_code = 0;
vif->ap = NULL;
mutex_init(&vif->lock);
```
初始化 scan_timeout 計時器
```
/* Initialize timer of scan_timeout */
timer_setup(&vif->scan_timeout, owl_scan_timeout, 0);
```
Initialize all of a work item
```
INIT_WORK(&vif->ws_connect, owl_connect_routine);
INIT_WORK(&vif->ws_disconnect, owl_disconnect_routine);
INIT_WORK(&vif->ws_scan, owl_scan_routine);
INIT_WORK(&vif->ws_scan_timeout, owl_scan_timeout_work);
```
最後把該 virtual interface 加入 owl 的 vif->list
```
/* Add vif into global vif_list */
if (mutex_lock_interruptible(&owl->lock))
goto l_error_add_list;
list_add_tail(&vif->list, &owl->vif_list);
mutex_unlock(&owl->lock);
```
### net_device_ops
> This structure defines the management hooks for network devices.
> The following hooks can be defined; unless noted otherwise, they are optional and can be filled with a null pointer.
net_device_ops 相當於 network device 的管理函式的集合,除非有定義是必要,不然都是可有可無。
參考 [Network Drivers](https://static.lwn.net/images/pdf/LDD3/ch17.pdf) 有講最基本的概念
實作的部分參考 [Linux Kernel(16.1)- Network Device Driver, simple snull.](http://nano-chicken.blogspot.com/2016/02/linux-kernel161-network-device-driver.html)
#### ndo_open
> This function is called when a network device transitions to the up state.
owl_ndo_open 的定義如下
```
static int owl_ndo_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
```
netif_start_queue 用來表示這個設備允許傳輸
```
/**
* netif_start_queue - allow transmit
* @dev: network device
*
* Allow upper layers to call the device hard_start_xmit routine.
*/
static inline void netif_start_queue(struct net_device *dev)
{
netif_tx_start_queue(netdev_get_tx_queue(dev, 0));
}
```
#### owl_ndo_stop
> This function is called when a network device transitions to the down state.
這裡做的事比較單純,因為設備停止傳輸了,所以也要把收到的封包刪除。
```
list_for_each_entry_safe (pkt, is, &vif->rx_queue, list) {
list_del(&pkt->list);
kfree(pkt);
}
```
最後再停止接收封包
```
netif_stop_queue - stop transmitted packets
```
#### ndo_get_stats
> Whenever an application needs to get statistics for the interface, this method is called. This happens, for example, when ifconfig or netstat -i is run.
可以取得 device 的統計資料,通常透過 ifconfig 或 netstat -i 呼叫。
```
static struct net_device_stats *owl_ndo_get_stats(struct net_device *dev)
{
struct owl_vif *vif = ndev_get_owl_vif(dev);
return &vif->stats;
}
```
#### ndo_start_xmit
> Method that initiates the transmission of a packet. The full packet (protocol headers and all) is contained in a socket buffer (sk_buff) structure.
開始傳送封包,整個封包由 sk_buff 結構體表示。
輸入的參數為 sk_buff *skb 和 net_device *dev 分別代表要傳送的封包和負責傳送的設備。
首先先取得傳送跟接收的 virtual interface 和 第二層的標頭
```
struct owl_vif *vif = ndev_get_owl_vif(dev);
struct owl_vif *dest_vif = NULL;
struct ethhdr *eth_hdr = (struct ethhdr *) skb->data;
```
接下來會判斷自己是 STA 或 AP,各自會有不同的實作。
這邊的邏輯很簡單,STA 傳輸的對象只可能是目前連線中的 AP(multicast/broadcast 都會先傳給 AP 轉傳),所以就直接傳。
```
if (vif->wdev.iftype == NL80211_IFTYPE_STATION) {
if (vif->ap && vif->ap->ap_enabled) {
dest_vif = vif->ap;
if (__owl_ndo_start_xmit(vif, dest_vif, skb))
count++;
}
}
```
如果是 AP,目前只有支援 broadcast 跟 unicast
:::info
multicast 不知道有沒有支援?
:::
broadcast:
```
if (is_broadcast_ether_addr(eth_hdr->h_dest)) {
list_for_each_entry (dest_vif, &vif->bss_list, bss_list) {
/* Don't send broadcast packet back
* to the source interface.
*/
if (ether_addr_equal(eth_hdr->h_source,
dest_vif->ndev->dev_addr))
continue;
if (__owl_ndo_start_xmit(vif, dest_vif, skb))
count++;
}
}
```
unicast:
```
/* The packet is unicasting */
else {
list_for_each_entry (dest_vif, &vif->bss_list, bss_list) {
if (ether_addr_equal(eth_hdr->h_dest,
dest_vif->ndev->dev_addr)) {
if (__owl_ndo_start_xmit(vif, dest_vif, skb))
count++;
break;
}
}
}
```
最後就是判斷封包是否 drop,以及最後要把封包 free 掉。
```
if (!count)
vif->stats.tx_dropped++;
/* Don't forget to cleanup skb, as its ownership moved to xmit callback. */
dev_kfree_skb(skb);
```
#### __owl_ndo_start_xmit
這個函式負責把封包的資料取出
一開始先配置 owl_packet 所需空間
```
pkt = kmalloc(sizeof(struct owl_packet), GFP_KERNEL);
```
然後從 sk_buff 取資料填入 owl_packet
```
datalen = skb->len;
memcpy(pkt->data, skb->data, datalen);
pkt->datalen = datalen;
```
接下來把封包放入目的端的 virtual interface 的 rx_queue
```
/* enqueue packet to destination vif's rx_queue */
if (mutex_lock_interruptible(&dest_vif->lock))
goto l_error_before_rx_queue;
list_add_tail(&pkt->list, &dest_vif->rx_queue);
mutex_unlock(&dest_vif->lock);
```
然後是在來源端的 virtual interface 填入相關資料
```
if (mutex_lock_interruptible(&vif->lock))
goto l_erorr_after_rx_queue;
/* Update interface statistics */
vif->stats.tx_packets++;
vif->stats.tx_bytes += datalen;
vif->active_time = jiffies;
mutex_unlock(&vif->lock);
```
最後還要處理接收封包
```
/* Directly send to rx_queue, simulate the rx interrupt */
owl_rx(dest_vif->ndev);
```
#### owl_rx
處理 rx_queue 收到的封包,將它轉成 skb_buff 的格式。
先處理第一個收到封包
```
if (mutex_lock_interruptible(&vif->lock))
goto pkt_free;
pkt = list_first_entry(&vif->rx_queue, struct owl_packet, list);
vif->stats.rx_packets++;
vif->stats.rx_bytes += pkt->datalen;
vif->active_time = jiffies;
mutex_unlock(&vif->lock);
```
接下來可以先看到這篇關於 [On the alignment of IP packets](https://lwn.net/Articles/89597/) 的說明,可以了解因為 Ethernet Header(14B),導致後面的 IP Header(16B) 無法對齊 four-byte boundary,所以才要多了前面的 2B。

skb_reserve 用來移動 data、tail 指標。
skb_put 往下移動 tail 指標用來增加 data 的空間,並回傳移動前的 tail 指標位置。
最後將該封包從 rx_queue 移除並釋放空間。
```
/* Put raw packet into socket buffer */
skb = dev_alloc_skb(pkt->datalen + 2);
if (!skb) {
pr_info("owl rx: low on mem - packet dropped\n");
vif->stats.rx_dropped++;
goto pkt_free;
}
skb_reserve(skb, 2); /* align IP on 16B boundary */
memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
list_del(&pkt->list);
kfree(pkt);
```
AP 處理 multicast/broadcast packet 的部分,會轉傳給除了來源 STA 外的所有 STA。
因為 broadcast 是 multicast 的一個特例,所以可以用 is_multicast_ether_addr 來判斷,若是則將 skb 複製到 skb1。
```
/* Receiving a multicast/broadcast packet, send it to every
* STA except the source STA, and pass it to protocol stack.
*/
if (is_multicast_ether_addr(eth_hdr->h_dest)) {
pr_info("owl: is_multicast_ether_addr\n");
skb1 = skb_copy(skb, GFP_KERNEL);
}
```
如果 AP 收到的封包為 unicast,會有兩種可能(給 AP 的、給 BSS 內的某 STA),這邊先處理第二種可能,一樣先把 skb 複製到 skb1,然後把 skb assign 為 NULL。
```
/* Receiving a unicast packet */
else {
/* The packet is not for AP itself, send it to destination
* STA, and do not pass it to procotol stack.
*/
if (!ether_addr_equal(eth_hdr->h_dest, vif->ndev->dev_addr)) {
skb1 = skb;
skb = NULL;
}
}
```
接著先處理轉傳的部分(送給其他 STA)
```
if (skb1) {
pr_info("owl: AP %s relay:\n", vif->ndev->name);
owl_ndo_start_xmit(skb1, vif->ndev);
}
/* Nothing to pass to protocol stack */
if (!skb)
return;
```
處理第一種可能,這個封包就是給自己(STA 或 AP)的就送給 protocol stack 處理
```
/* Pass the skb to protocol stack */
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0)
netif_rx_ni(skb);
#else
netif_rx(skb);
#endif
return;
```
### signal
TODO