├── Makefile └── virtualwifi.c /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += virtualwifi.o 2 | 3 | all: 4 | make -C /lib/modules/`uname -r`/build M=`pwd` modules 5 | 6 | clean: 7 | make -C /lib/modules/`uname -r`/build M=`pwd` clean 8 | -------------------------------------------------------------------------------- /virtualwifi.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include /* wiphy and probably everything that would required for FullMAC driver */ 4 | #include 5 | 6 | #include /* work_struct */ 7 | #include 8 | 9 | #define WIPHY_NAME "navifly" 10 | #define NDEV_NAME "navifly%d" 11 | #define SSID_DUMMY "MyAwesomeWiFi" 12 | #define SSID_DUMMY_SIZE (sizeof("MyAwesomeWiFi") - 1) 13 | 14 | struct navifly_context { 15 | struct wiphy *wiphy; 16 | struct net_device *ndev; 17 | 18 | /* DEMO */ 19 | struct semaphore sem; 20 | struct work_struct ws_connect; 21 | char connecting_ssid[sizeof(SSID_DUMMY)]; 22 | struct work_struct ws_disconnect; 23 | u16 disconnect_reason_code; 24 | struct work_struct ws_scan; 25 | struct cfg80211_scan_request *scan_request; 26 | }; 27 | 28 | struct navifly_wiphy_priv_context { 29 | struct navifly_context *navi; 30 | }; 31 | 32 | struct navifly_ndev_priv_context { 33 | struct navifly_context *navi; 34 | struct wireless_dev wdev; 35 | }; 36 | 37 | /* helper function that will retrieve main context from "priv" data of the wiphy */ 38 | static struct navifly_wiphy_priv_context * 39 | wiphy_get_navi_context(struct wiphy *wiphy) { return (struct navifly_wiphy_priv_context *) wiphy_priv(wiphy); } 40 | 41 | /* helper function that will retrieve main context from "priv" data of the network device */ 42 | static struct navifly_ndev_priv_context * 43 | ndev_get_navi_context(struct net_device *ndev) { return (struct navifly_ndev_priv_context *) netdev_priv(ndev); } 44 | 45 | /* Helper function that will prepare structure with "dummy" BSS information and "inform" the kernel about "new" BSS */ 46 | static void inform_dummy_bss(struct navifly_context *navi) { 47 | struct cfg80211_bss *bss = NULL; 48 | struct cfg80211_inform_bss data = { 49 | .chan = &navi->wiphy->bands[NL80211_BAND_2GHZ]->channels[0], /* the only channel for this demo */ 50 | .scan_width = NL80211_BSS_CHAN_WIDTH_20, 51 | /* signal "type" not specified in this DEMO so its basically unused, it can be some kind of percentage from 0 to 100 or mBm value*/ 52 | /* signal "type" may be specified before wiphy registration by setting wiphy->signal_type */ 53 | .signal = 1337, 54 | }; 55 | char bssid[6] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; 56 | 57 | /* ie - array of tags that usually retrieved from beacon frame or probe responce. */ 58 | char ie[SSID_DUMMY_SIZE + 2] = {WLAN_EID_SSID, SSID_DUMMY_SIZE}; 59 | memcpy(ie + 2, SSID_DUMMY, SSID_DUMMY_SIZE); 60 | 61 | /* also it posible to use cfg80211_inform_bss() instead of cfg80211_inform_bss_data() */ 62 | bss = cfg80211_inform_bss_data(navi->wiphy, &data, CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, WLAN_CAPABILITY_ESS, 100, 63 | ie, sizeof(ie), GFP_KERNEL); 64 | 65 | /* free, cfg80211_inform_bss_data() returning cfg80211_bss structure refcounter of which should be decremented if its not used. */ 66 | cfg80211_put_bss(navi->wiphy, bss); 67 | } 68 | 69 | /* "Scan" routine for DEMO. It just inform the kernel about "dummy" BSS and "finishs" scan. 70 | * When scan is done it should call cfg80211_scan_done() to inform the kernel that scan is finished. 71 | * This routine called through workqueue, when the kernel asks about scan through cfg80211_ops. */ 72 | static void navifly_scan_routine(struct work_struct *w) { 73 | struct navifly_context *navi = container_of(w, struct navifly_context, ws_scan); 74 | struct cfg80211_scan_info info = { 75 | /* if scan was aborted by user(calling cfg80211_ops->abort_scan) or by any driver/hardware issue - field should be set to "true"*/ 76 | .aborted = false, 77 | }; 78 | 79 | /* pretend some work, also u can't call cfg80211_scan_done right away after cfg80211_ops->scan(), 80 | * idk why, but netlink client would not get message with "scan done", 81 | * is it because of "scan_routine" and cfg80211_ops->scan() may run in concurrent and cfg80211_scan_done() called before cfg80211_ops->scan() returns? */ 82 | msleep(100); 83 | 84 | /* inform with dummy BSS */ 85 | inform_dummy_bss(navi); 86 | 87 | if(down_interruptible(&navi->sem)) { 88 | return; 89 | } 90 | 91 | /* finish scan */ 92 | cfg80211_scan_done(navi->scan_request, &info); 93 | 94 | navi->scan_request = NULL; 95 | 96 | up(&navi->sem); 97 | } 98 | 99 | /* It just checks SSID of the ESS to connect and informs the kernel that connect is finished. 100 | * It should call cfg80211_connect_bss() when connect is finished or cfg80211_connect_timeout() when connect is failed. 101 | * This "demo" can connect only to ESS with SSID equal to SSID_DUMMY value. 102 | * This routine called through workqueue, when the kernel asks about connect through cfg80211_ops. */ 103 | static void navifly_connect_routine(struct work_struct *w) { 104 | struct navifly_context *navi = container_of(w, struct navifly_context, ws_connect); 105 | 106 | if(down_interruptible(&navi->sem)) { 107 | return; 108 | } 109 | 110 | if (memcmp(navi->connecting_ssid, SSID_DUMMY, sizeof(SSID_DUMMY)) != 0) { 111 | cfg80211_connect_timeout(navi->ndev, NULL, NULL, 0, GFP_KERNEL, NL80211_TIMEOUT_SCAN); 112 | } else { 113 | /* we can connect to ESS that already know. If else, technically kernel will only warn.*/ 114 | /* so, lets send dummy bss to the kernel before complete. */ 115 | inform_dummy_bss(navi); 116 | 117 | /* also its possible to use cfg80211_connect_result() or cfg80211_connect_done() */ 118 | cfg80211_connect_bss(navi->ndev, NULL, NULL, NULL, 0, NULL, 0, WLAN_STATUS_SUCCESS, GFP_KERNEL, 119 | NL80211_TIMEOUT_UNSPECIFIED); 120 | } 121 | navi->connecting_ssid[0] = 0; 122 | 123 | up(&navi->sem); 124 | } 125 | 126 | /* Just calls cfg80211_disconnected() that informs the kernel that disconnect is complete. 127 | * Overall disconnect may call cfg80211_connect_timeout() if disconnect interrupting connection routine, but for this demo I keep it simple. 128 | * This routine called through workqueue, when the kernel asks about disconnect through cfg80211_ops. */ 129 | static void navifly_disconnect_routine(struct work_struct *w) { 130 | 131 | struct navifly_context *navi = container_of(w, struct navifly_context, ws_disconnect); 132 | 133 | if(down_interruptible(&navi->sem)) { 134 | return; 135 | } 136 | 137 | cfg80211_disconnected(navi->ndev, navi->disconnect_reason_code, NULL, 0, true, GFP_KERNEL); 138 | 139 | navi->disconnect_reason_code = 0; 140 | 141 | up(&navi->sem); 142 | } 143 | 144 | /* callback that called by the kernel when user decided to scan. 145 | * This callback should initiate scan routine(through work_struct) and exit with 0 if everything ok. 146 | * Scan routine should be finished with cfg80211_scan_done() call. */ 147 | static int nvf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { 148 | struct navifly_context *navi = wiphy_get_navi_context(wiphy)->navi; 149 | 150 | if(down_interruptible(&navi->sem)) { 151 | return -ERESTARTSYS; 152 | } 153 | 154 | if (navi->scan_request != NULL) { 155 | up(&navi->sem); 156 | return -EBUSY; 157 | } 158 | navi->scan_request = request; 159 | 160 | up(&navi->sem); 161 | 162 | if (!schedule_work(&navi->ws_scan)) { 163 | return -EBUSY; 164 | } 165 | 166 | return 0; /* OK */ 167 | } 168 | 169 | /* callback that called by the kernel when there is need to "connect" to some network. 170 | * It inits connection routine through work_struct and exits with 0 if everything ok. 171 | * connect routine should be finished with cfg80211_connect_bss()/cfg80211_connect_result()/cfg80211_connect_done() or cfg80211_connect_timeout(). */ 172 | static int nvf_connect(struct wiphy *wiphy, struct net_device *dev, 173 | struct cfg80211_connect_params *sme) { 174 | struct navifly_context *navi = wiphy_get_navi_context(wiphy)->navi; 175 | size_t ssid_len = sme->ssid_len > 15 ? 15 : sme->ssid_len; 176 | 177 | if(down_interruptible(&navi->sem)) { 178 | return -ERESTARTSYS; 179 | } 180 | 181 | memcpy(navi->connecting_ssid, sme->ssid, ssid_len); 182 | navi->connecting_ssid[ssid_len] = 0; 183 | 184 | up(&navi->sem); 185 | 186 | if (!schedule_work(&navi->ws_connect)) { 187 | return -EBUSY; 188 | } 189 | return 0; 190 | } 191 | /* callback that called by the kernel when there is need to "diconnect" from currently connected network. 192 | * It inits disconnect routine through work_struct and exits with 0 if everything ok. 193 | * disconnect routine should call cfg80211_disconnected() to inform the kernel that disconnection is complete. */ 194 | static int nvf_disconnect(struct wiphy *wiphy, struct net_device *dev, 195 | u16 reason_code) { 196 | struct navifly_context *navi = wiphy_get_navi_context(wiphy)->navi; 197 | 198 | if(down_interruptible(&navi->sem)) { 199 | return -ERESTARTSYS; 200 | } 201 | 202 | navi->disconnect_reason_code = reason_code; 203 | 204 | up(&navi->sem); 205 | 206 | if (!schedule_work(&navi->ws_disconnect)) { 207 | return -EBUSY; 208 | } 209 | return 0; 210 | } 211 | 212 | /* Structure of functions for FullMAC 80211 drivers. 213 | * Functions that implemented along with fields/flags in wiphy structure would represent drivers features. 214 | * This DEMO can only perform "scan" and "connect". 215 | * Some functions cant be implemented alone, for example: with "connect" there is should be function "disconnect". */ 216 | static struct cfg80211_ops nvf_cfg_ops = { 217 | .scan = nvf_scan, 218 | .connect = nvf_connect, 219 | .disconnect = nvf_disconnect, 220 | }; 221 | 222 | /* Network packet transmit. 223 | * Callback that called by the kernel when packet of data should be sent. 224 | * In this example it does nothing. */ 225 | static netdev_tx_t nvf_ndo_start_xmit(struct sk_buff *skb, 226 | struct net_device *dev) { 227 | /* Dont forget to cleanup skb, as its ownership moved to xmit callback. */ 228 | kfree_skb(skb); 229 | return NETDEV_TX_OK; 230 | } 231 | 232 | /* Structure of functions for network devices. 233 | * It should have at least ndo_start_xmit functions that called for packet to be sent. */ 234 | static struct net_device_ops nvf_ndev_ops = { 235 | .ndo_start_xmit = nvf_ndo_start_xmit, 236 | }; 237 | 238 | /* Array of "supported" channels in 2ghz band. It's required for wiphy. 239 | * For demo - the only channel 6. */ 240 | static struct ieee80211_channel nvf_supported_channels_2ghz[] = { 241 | { 242 | .band = NL80211_BAND_2GHZ, 243 | .hw_value = 6, 244 | .center_freq = 2437, 245 | } 246 | }; 247 | 248 | /* Array of supported rates. Its required to support at least those next rates for 2ghz band. */ 249 | static struct ieee80211_rate nvf_supported_rates_2ghz[] = { 250 | { 251 | .bitrate = 10, 252 | .hw_value = 0x1, 253 | }, 254 | { 255 | .bitrate = 20, 256 | .hw_value = 0x2, 257 | }, 258 | { 259 | .bitrate = 55, 260 | .hw_value = 0x4, 261 | }, 262 | { 263 | .bitrate = 110, 264 | .hw_value = 0x8, 265 | } 266 | }; 267 | 268 | /* Structure that describes supported band of 2ghz. */ 269 | static struct ieee80211_supported_band nf_band_2ghz = { 270 | .ht_cap.cap = IEEE80211_HT_CAP_SGI_20, /* add other band capabilities if needed, like 40 width etc. */ 271 | .ht_cap.ht_supported = false, 272 | 273 | .channels = nvf_supported_channels_2ghz, 274 | .n_channels = ARRAY_SIZE(nvf_supported_channels_2ghz), 275 | 276 | .bitrates = nvf_supported_rates_2ghz, 277 | .n_bitrates = ARRAY_SIZE(nvf_supported_rates_2ghz), 278 | }; 279 | 280 | /* Function that creates wiphy context and net_device with wireless_dev. 281 | * wiphy/net_device/wireless_dev is basic interfaces for the kernel to interact with driver as wireless one. 282 | * It returns driver's main "navifly" context. */ 283 | static struct navifly_context *navifly_create_context(void) { 284 | struct navifly_context *ret = NULL; 285 | struct navifly_wiphy_priv_context *wiphy_data = NULL; 286 | struct navifly_ndev_priv_context *ndev_data = NULL; 287 | 288 | /* allocate for navifly context*/ 289 | ret = kmalloc(sizeof(*ret), GFP_KERNEL); 290 | if (!ret) { 291 | goto l_error; 292 | } 293 | 294 | /* allocate wiphy context, also it possible just to use wiphy_new() function. 295 | * wiphy should represent physical FullMAC wireless device. 296 | * One wiphy can have serveral network interfaces - for that u need to implement add_virtual_intf() and co. from cfg80211_ops. */ 297 | ret->wiphy = wiphy_new_nm(&nvf_cfg_ops, sizeof(struct navifly_wiphy_priv_context), WIPHY_NAME); 298 | if (ret->wiphy == NULL) { 299 | goto l_error_wiphy; 300 | } 301 | 302 | /* save navifly context in wiphy private data. */ 303 | wiphy_data = wiphy_get_navi_context(ret->wiphy); 304 | wiphy_data->navi = ret; 305 | 306 | /* set device object as wiphy "parent", I dont have any device yet. */ 307 | /* set_wiphy_dev(ret->wiphy, dev); */ 308 | 309 | /* wiphy should determinate it type */ 310 | /* add other required types like "BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP)" etc. */ 311 | ret->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); 312 | 313 | /* wiphy should have at least 1 band. */ 314 | /* fill also NL80211_BAND_5GHZ if required, in this small example I use only 1 band with 1 "channel" */ 315 | ret->wiphy->bands[NL80211_BAND_2GHZ] = &nf_band_2ghz; 316 | 317 | /* scan - if ur device supports "scan" u need to define max_scan_ssids at least. */ 318 | ret->wiphy->max_scan_ssids = 69; 319 | 320 | /* register wiphy, if everything ok - there should be another wireless device in system. 321 | * use command: 322 | * $ iw list 323 | * Wiphy navifly 324 | * ... 325 | * */ 326 | if (wiphy_register(ret->wiphy) < 0) { 327 | goto l_error_wiphy_register; 328 | } 329 | 330 | /* allocate network device context. */ 331 | ret->ndev = alloc_netdev(sizeof(*ndev_data), NDEV_NAME, NET_NAME_ENUM, ether_setup); 332 | if (ret->ndev == NULL) { 333 | goto l_error_alloc_ndev; 334 | } 335 | /* fill private data of network context.*/ 336 | ndev_data = ndev_get_navi_context(ret->ndev); 337 | ndev_data->navi = ret; 338 | 339 | /* fill wireless_dev context. 340 | * wireless_dev with net_device can be represented as inherited class of single net_device. */ 341 | ndev_data->wdev.wiphy = ret->wiphy; 342 | ndev_data->wdev.netdev = ret->ndev; 343 | ndev_data->wdev.iftype = NL80211_IFTYPE_STATION; 344 | ret->ndev->ieee80211_ptr = &ndev_data->wdev; 345 | 346 | /* set device object for net_device */ 347 | /* SET_NETDEV_DEV(ret->ndev, wiphy_dev(ret->wiphy)); */ 348 | 349 | /* set network device hooks. It should implement ndo_start_xmit() at least. */ 350 | ret->ndev->netdev_ops = &nvf_ndev_ops; 351 | 352 | /* Add here proper net_device initialization. */ 353 | 354 | /* register network device. If everything ok, there should be new network device: 355 | * $ ip a 356 | * ... 357 | * 4: navifly0: mtu 1500 qdisc noop state DOWN group default qlen 1000 358 | * link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff 359 | * ... 360 | * */ 361 | if (register_netdev(ret->ndev)) { 362 | goto l_error_ndev_register; 363 | } 364 | 365 | return ret; 366 | l_error_ndev_register: 367 | free_netdev(ret->ndev); 368 | l_error_alloc_ndev: 369 | wiphy_unregister(ret->wiphy); 370 | l_error_wiphy_register: 371 | wiphy_free(ret->wiphy); 372 | l_error_wiphy: 373 | kfree(ret); 374 | l_error: 375 | return NULL; 376 | } 377 | 378 | static void navifly_free(struct navifly_context *ctx) { 379 | if (ctx == NULL) { 380 | return; 381 | } 382 | 383 | unregister_netdev(ctx->ndev); 384 | free_netdev(ctx->ndev); 385 | wiphy_unregister(ctx->wiphy); 386 | wiphy_free(ctx->wiphy); 387 | kfree(ctx); 388 | } 389 | 390 | static struct navifly_context *g_ctx = NULL; 391 | 392 | static int __init virtual_wifi_init(void) { 393 | g_ctx = navifly_create_context(); 394 | 395 | if (g_ctx != NULL) { 396 | /*DEMO*/ 397 | sema_init(&g_ctx->sem, 1); 398 | INIT_WORK(&g_ctx->ws_connect, navifly_connect_routine); 399 | g_ctx->connecting_ssid[0] = 0; 400 | INIT_WORK(&g_ctx->ws_disconnect, navifly_disconnect_routine); 401 | g_ctx->disconnect_reason_code = 0; 402 | INIT_WORK(&g_ctx->ws_scan, navifly_scan_routine); 403 | g_ctx->scan_request = NULL; 404 | } 405 | return g_ctx == NULL; 406 | } 407 | 408 | static void __exit virtual_wifi_exit(void) { 409 | /* make sure that no work is queued */ 410 | cancel_work_sync(&g_ctx->ws_connect); 411 | cancel_work_sync(&g_ctx->ws_disconnect); 412 | cancel_work_sync(&g_ctx->ws_scan); 413 | 414 | navifly_free(g_ctx); 415 | } 416 | 417 | module_init(virtual_wifi_init); 418 | module_exit(virtual_wifi_exit); 419 | 420 | MODULE_LICENSE("GPL v2"); 421 | 422 | MODULE_DESCRIPTION("Dumb example for cfg80211(aka FullMAC) driver." 423 | "Module creates wireless device with network." 424 | "The device can work as station(STA mode) only." 425 | "The device can perform scan that \"scans\" only dummy network." 426 | "Also it performs \"connect\" to the dummy network."); --------------------------------------------------------------------------------