---
# System prepended metadata

title: vwifi 程式碼閱讀
tags: [vwifi]

---

---
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		= &eth_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。
![](https://hackmd.io/_uploads/BJePcxtH2.png)

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