# [VWIFI](https://github.com/sysprog21/vwifi) 2024 ## [vwifi.c](https://github.com/sysprog21/vwifi/blob/main/vwifi.c) :::info [Introduce a userspace tool](https://github.com/sysprog21/vwifi/commit/7814d59e38cc4839c167a179742342ab4ef030bf) ([#53](https://github.com/sysprog21/vwifi/pull/53)) ::: ### denylist_check 確認當前這組(dest, source)是否在 denylist 中 ### denylist_load 將資料存入 denylist ### denylist_nl_recv 送 denylist 給 vwifi ,並回報結果給 process * [sk_buff](https://elixir.bootlin.com/linux/latest/source/include/linux/skbuff.h#L855) * [nlmsghdr](https://elixir.bootlin.com/linux/v5.15.75/source/include/uapi/linux/netlink.h#L44) * [nlmsg_data](https://elixir.bootlin.com/linux/v6.9/source/include/net/netlink.h#L591) * head of message payload * [nlmsg_new](https://elixir.bootlin.com/linux/v6.9/source/include/net/netlink.h#L1008) * Allocate a new netlink message (socket buffer) * [nlmsg_put](https://elixir.bootlin.com/linux/v6.9/source/include/net/netlink.h#L950) * Add a new netlink message to an skb * [nlmsg_unicast](https://elixir.bootlin.com/linux/v6.9/source/include/net/netlink.h#L1140) * unicast a netlink message * [NLMSG_DONE](https://elixir.bootlin.com/linux/v6.9/source/include/uapi/linux/netlink.h#L114) * End of a dump * [NETLINK_CB](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netlink.h#L35) * 取得 [netlink_skb_parms](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netlink.h#L25) ```c static struct netlink_kernel_cfg nl_config = { .input = denylist_nl_recv, }; ``` ### [netlink_kernel_cfg](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netlink.h#L46) optional Netlink kernel configuration parameters * .input : 指定 denylist_nl_recv 為 callback function,用於接收處理來自 userspace 之訊息 ___ ### ndev_get_vwifi_vif 取得 net_device 之 virtual interface * [net_device](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netdevice.h#L2031) * The DEVICE structure. * [netdev_priv](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netdevice.h#L2588) * access network device private data ### wdev_get_vwifi_vif 取得 wireless_dev 之 virtual interface * [wireless_dev](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L6149) * wireless device state * [container_of](https://hackmd.io/@sysprog/linux-macro-containerof) * Calculate address of object that contains address ptr ___ :::info [Support virtio-net](https://github.com/sysprog21/vwifi/commit/038b13dcb486ff06c2ee0e9a61dc208c13727895) ([#45](https://github.com/sysprog21/vwifi/pull/45)) * virtio * vwifi mangement frame ::: ### [vwifi_mac_to_32](https://github.com/sysprog21/vwifi/pull/45#discussion_r1311203160) maps 48-bit MAC addresses into 32-bit integers > rickywu0421 on Aug 31, 2023 We need a hash function that maps 48-bit MAC addresses into 32-bit integers, so that the integer can be treated as a key in hash_add() and hash_for_each_possible(). This hash function is from murmur hash and is once used in #PR11 as our hash function. I think there may be a better hash function but currently, this works fine. ___ ### __sin_s3 A sine approximation via a third-order approx. ### rand_int_smooth 產生特定範圍的 sin 平滑函數 ___ ### inform_bss 告知 linux 核心子系統 cfg80211 有關新的 bss 的資訊 * [list_for_each_entry](https://elixir.bootlin.com/linux/v6.9/source/include/linux/list.h#L777) * pos: the type * to use as a loop cursor. * head: the head for your list. * member: the name of the list_head within the struct. * [cfg80211_bss](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L2949) * BSS description * [cfg80211_inform_bss](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L2877) * the BSS metadata * [NL80211_BAND_2GHZ](https://elixir.bootlin.com/linux/v6.9/source/include/uapi/linux/nl80211.h#L5434) * 2.4 GHz ISM band * [NL80211_BSS_CHAN_WIDTH_20](https://elixir.bootlin.com/linux/v6.9/source/include/uapi/linux/nl80211.h#L5085) * control channel is 20 MHz wide or compatible * [DBM_TO_MBM](https://elixir.bootlin.com/linux/v6.9/source/include/linux/ieee80211.h#L4612) * 1 DBM = 100 MBM * [WLAN_CAPABILITY_ESS](https://elixir.bootlin.com/linux/v6.9/source/include/linux/ieee80211.h#L3261) * [WLAN_CAPABILITY_PRIVACY](https://elixir.bootlin.com/linux/v6.9/source/include/linux/ieee80211.h#L3274) * [ktime_get_boottime_ns](https://elixir.bootlin.com/linux/v6.9/source/include/linux/timekeeping.h#L187) * Get the monotonic time since boot in nanoseconds * [cfg80211_inform_bss_data](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L7270) * This informs cfg80211 that BSS information was found and the BSS should be updated/added. * Return: A referenced struct, **must be released with cfg80211_put_bss()!** Or %NULL on error. * [cfg80211_inform_single_bss_data](https://elixir.bootlin.com/linux/v6.9/source/net/wireless/scan.c#L2155) * [cfg80211_parse_mbssid_data](https://elixir.bootlin.com/linux/v6.9/source/net/wireless/scan.c#L2368) * [cfg80211_parse_ml_sta_data](https://elixir.bootlin.com/linux/v6.9/source/net/wireless/scan.c#L3023) * [CFG80211_BSS_FTYPE_UNKNOWN](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L7215) * frame type that the BSS data came from * driver doesn't know whether the data is from a beacon or probe response * [GFP_KERNEL](https://elixir.bootlin.com/linux/v6.9/source/include/linux/gfp_types.h#L363) * context flag * is typical for kernel-internal allocations. * [cfg80211_put_bss](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L7363) * unref BSS struct * [jiffies](https://elixir.bootlin.com/linux/v6.9/source/include/linux/raid/pq.h#L164) ### vwifi_beacon_inform_bss 傳送 beacon frame 到 cfg80211 子系統,再轉送給 STA * [CFG80211_BSS_FTYPE_BEACON](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L7216) * frame type that the BSS data came from * data comes from a beacon ### vwifi_beacon 設置 meta data 並傳送 beacon 給 STA * [(virtual) interface types](https://elixir.bootlin.com/linux/v6.9/source/include/uapi/linux/nl80211.h#L3503) * NL80211_IFTYPE_AP * access point * NL80211_IFTYPE_MESH_POINT * mesh point * NL80211_IFTYPE_ADHOC * independent BSS member * NL80211_IFTYPE_OCB * Outside Context of a BSS * This mode corresponds to the MIB variable dot11OCBActivated=true * NL80211_IFTYPE_STATION * managed BSS member * [ktime_get_real](https://elixir.bootlin.com/linux/v6.9/source/include/linux/timekeeping.h#L80) * get the real (wall-) time in ktime_t format * [ktime_to_us](https://elixir.bootlin.com/linux/v6.9/source/include/linux/ktime.h#L157) * [ns_to_ktime](https://elixir.bootlin.com/linux/v6.9/source/include/linux/ktime.h#L220) * return ns * [hrtimer_forward_now](https://elixir.bootlin.com/linux/v6.9/source/include/linux/hrtimer.h#L352) * forward the timer expiry so it expires after now * [Return values for the callback function](https://elixir.bootlin.com/linux/v6.9/source/include/linux/hrtimer_types.h#L13) * HRTIMER_NORESTART * Timer is not restarted * HRTIMER_RESTART * Timer must be restarted ___ ### vwifi_ndo_open 啟動 network device * [netif_start_queue](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netdevice.h#L3312) * allow device to transmit * Allow upper layers to call the device hard_start_xmit routine. ### vwifi_ndo_stop 關閉 network device,並清空 rx_queue * [netif_stop_queue](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netdevice.h#L3364) * stop transmitted packets * Stop upper layers calling the device hard_start_xmit routine. Used for flow control when transmit resources are unavailable. * [list_for_each_entry_safe](https://elixir.bootlin.com/linux/v6.9/source/include/linux/list.h#L864) * [list_del](https://elixir.bootlin.com/linux/v6.9/source/include/linux/list.h#L227) * [kfree](https://elixir.bootlin.com/linux/v6.9/source/tools/virtio/linux/kernel.h#L80) ### vwifi_ndo_get_stats 取得 network device 之 `stats` * [net_device_stats](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netdevice.h#L188) ### vwifi_rx 從 rx_queue 取得封包 * STA mode * 單純利用 `skb` 傳送封包到上層 protocol * AP mode * multicast/broadcast * 利用 `skb1` 傳給除了 source STA 的所有 STA,亦用 `skb` 傳給上層 protocol * unicast * 傳給 AP : 單純利用 `skb` 傳送封包到上層 protocol * 利用 `skb1` 傳給其他 STA ### __vwifi_ndo_start_xmit 新增 `pkt` 到 `dest_vir` 的 `rx_queue` 中,並呼叫 `vwifi_rx(dest_vif->ndev)` 取得封包 ### vwifi_ndo_start_xmit 利用 `__vwifi_ndo_start_xmit` 傳送封包 * STA mode * 直接傳 * AP mode * multicast/broadcast * 不可傳給 source STA 和 deny STA * unicast * 不可傳給 deny STA ```c static struct net_device_ops vwifi_ndev_ops = { .ndo_open = vwifi_ndo_open, .ndo_stop = vwifi_ndo_stop, .ndo_start_xmit = vwifi_ndo_start_xmit, .ndo_get_stats = vwifi_ndo_get_stats, }; ``` ### [net_device_ops](https://elixir.bootlin.com/linux/v6.9/source/include/linux/netdevice.h#L1355) ___ ### vwifi_scan_timeout_work 呼叫 `cfg80211_scan_done` 完成 scan * [work_struct](https://elixir.bootlin.com/linux/v6.9/source/include/linux/workqueue_types.h#L16) * [cfg80211_scan_done](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L7093) * notify that scan finished ### vwifi_scan_timeout ### vwifi_scan_routine 僅設置 scan timer,等到 timeout 後就會呼叫 `vwifi_scan_timeout` ### vwifi_connect_routine ### vwifi_disconnect_routine * STA : 呼叫 `cfg80211_disconnected` * AP : 呼叫 `cfg80211_disconnected` 並清空 bss_list ### vwifi_scan ### vwifi_connect ### vwifi_disconnect ### vwifi_get_station 取得特定 STA 資訊 * [station_info](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L2111) * filled: * bitflag of flags using the bits of &enum nl80211_sta_info to indicate the relevant values in this struct for them * rxrate * [rate_info](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L1928) * [rate_info_flags](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L1860) * current unicast bitrate to this station * txrate * current unicast bitrate from this station * [MCS Index Table, Modulation and Coding Scheme Index 11n, 11ac, and 11ax](https://mcsindex.com/) * [WI-FI 7 MCS TABLE](https://semfionetworks.com/blog/wi-fi-7-mcs-table/) * sme_state * station 連線狀態 ### vwifi_dump_station ### vwifi_interface_add 初始化 interface 為 STA mode ### vwifi_change_iface 將 STA mode 改為 AP mode ### vwifi_start_ap 將 net_device `ndev` 設為 AP,並設置 SSID ### vwifi_stop_ap 關閉 AP,刪除 `bss_list` ### vwifi_change_beacon ### vwifi_add_key ### vwifi_del_key ### vwifi_set_default_key ### vwifi_change_station ### vwifi_delete_interface 取消 virtual interface 註冊 ```c static struct cfg80211_ops vwifi_cfg_ops = { .change_virtual_intf = vwifi_change_iface, .scan = vwifi_scan, .connect = vwifi_connect, .disconnect = vwifi_disconnect, .get_station = vwifi_get_station, .dump_station = vwifi_dump_station, .start_ap = vwifi_start_ap, .stop_ap = vwifi_stop_ap, .change_beacon = vwifi_change_beacon, .add_key = vwifi_add_key, .del_key = vwifi_del_key, .set_default_key = vwifi_set_default_key, .change_station = vwifi_change_station, }; ``` ### [cfg80211_ops](https://elixir.bootlin.com/linux/v6.9/source/include/net/cfg80211.h#L4574) ___ ### vwifi_free Unregister 所有 virtual interface ### vwifi_cfg80211_add 對所有 virtual interface 註冊一個 wiphy ___ ### vwifi_virtio_scan_complete ### vwifi_virtio_scan_request ### vwifi_virtio_connect_request ### vwifi_virtio_disconnect ### vwifi_virtio_disconnect_tx ### vwifi_virtio_sta_entry_request ### vwifi_virtio_sta_entry_response ### vwifi_virtio_mgmt_rx_sta_entry_response ### vwifi_virtio_mgmt_rx_sta_entry_request ### vwifi_virtio_mgmt_rx_disconnect ### vwifi_virtio_mgmt_rx_connect_response ### vwifi_virtio_mgmt_rx_connect_request ### vwifi_virtio_mgmt_rx_scan_response ### vwifi_virtio_mgmt_rx_scan_request ### vwifi_virtio_mgmt_rx ### vwifi_virtio_data_rx ### vwifi_virtio_rx_switch ### vwifi_virtio_rx_work ### vwifi_virtio_tx_done * [virtqueue](https://elixir.bootlin.com/linux/v6.9/source/include/linux/virtio.h#L30) * a queue to register buffers for sending or receiving. ### vwifi_virtio_rx_done ### vwifi_virtio_tx ### vwifi_virtio_init_vqs ### vwifi_virtio_fill_vq ### vwifi_virtio_remove_vqs ### vwifi_virtio_probe ### vwifi_virtio_remove ___ ### vwifi_init ### vwifi_exit ___ ## vwifi-tool.c ### vwifi_status_check 開啟 `/sys/module/vwifi/initstate` 查看 vwifi status ### opt_set 確認使用者是否有使用 -d -s -c 之選項 ### denylist_pair_check 確認 source 和 destination interfaces 數量是否相同 ### denylist_make 生成 denylist ### denylist_send 透過 netlink 傳送 denylist 給 vwifi 核心模組 ### main 從 commandline 獲取訊息以生成 denylist ___ 在編譯時會產生以下警告訊息: ```shell vwifi-tool.c: In function ‘denylist_send’: vwifi-tool.c:95:5: warning: ‘strncpy’ writing 1040 bytes into a region of size 1024 overflows the destination [-Wstringop-overflow=] 95 | strncpy(NLMSG_DATA(nlh), denylist, NLMSG_SPACE(MAX_PAYLOAD)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ vwifi-tool.c:90:29: note: at offset 16 into destination object of size 1040 allocated by ‘calloc’ 90 | (struct nlmsghdr *) calloc(1, NLMSG_SPACE(MAX_PAYLOAD)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` calloc 分配 NLMSG_SPACE(MAX_PAYLOAD)的空間給 nlh 使用,這包含了標頭的空間,實際可用來裝訊息的空間大小只有 MAX_PAYLOAD。 ```diff -strncpy(NLMSG_DATA(nlh), denylist, NLMSG_SPACE(MAX_PAYLOAD)); +strncpy(NLMSG_DATA(nlh), denylist, MAX_PAYLOAD); ``` :::success ***[commit 3626397](https://github.com/sysprog21/vwifi/commit/362639786d5611b855c47365e62bcaed615dc20a)*** Fix buffer overflow ([#63](https://github.com/sysprog21/vwifi/pull/65)) ::: ___ ```c mod_timer(&vif->scan_timeout, jiffies + msecs_to_jiffies(SCAN_TIMEOUT_MS)); ``` * mod_timer * Timer 是一種 **Callback** 機制,提供使用者設定片段時間後觸發的方式 * Timer 有區分一次性或者週期性,Linux Kernel 提供的 mod_timer() 是屬於**一次性** * Timer時間使用 `jiffies` 計算, * jiffies 是全域變數,該變數用來紀錄時間的中斷次數(從開機後),可以搭配 `msecs_to_jiffies()` 計算出需要的時間間隔 ```c mutex_init(&vif->lock); mutex_lock_interruptible(&vif->lock); mutex_unlock(&vif->lock); ``` * mutex_lock_interruptible * 互斥鎖,可睡眠**可中斷** ```c spin_lock_irqsave(&vwifi_virtio_lock, flags); spin_unlock_irqrestore(&vwifi_virtio_lock, flags); ``` * spin_lock_irqsave * 保存 Local Processor 當前的 irq 狀態,然後在 Local Processor 上**禁止硬體中斷**產生,並獲取指定 Lock ```c spin_lock_bh(&vif_list_lock); spin_unlock_bh(&vif_list_lock); ``` * spin_lock_bh * **禁止軟體中斷**產生,並獲取指定 Lock ___