├── .ci ├── build-n-run.sh └── check-format.sh ├── .clang-format ├── .github └── workflows │ ├── codeql.yml │ └── main.yaml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── assets ├── ibss.drawio ├── ibss.png ├── rssi.png ├── virtio.png ├── vwifi.drawio ├── vwifi.png └── wavemon.png ├── scripts ├── common.sh ├── hostapd.conf ├── plot_rssi.py ├── verify.sh ├── wpa_supplicant.conf └── wpa_supplicant_ibss.conf ├── vwifi-tool.c └── vwifi.c /.ci/build-n-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function build_mod() 4 | { 5 | make all || exit 1 6 | } 7 | 8 | function run_tests() 9 | { 10 | make check || exit 2 11 | } 12 | 13 | build_mod 14 | # vwifi is buildable but failed to load. 15 | # modprobe: FATAL: Module cfg80211 not found in directory /lib/modules/5.11.0-1028-azure 16 | # Installing Module vwifi.ko 17 | # insmod: ERROR: could not insert module vwifi.ko: Unknown symbol in module 18 | # run_tests 19 | -------------------------------------------------------------------------------- /.ci/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SOURCES=$(find $(git rev-parse --show-toplevel) | egrep "\.(cpp|cc|c|h)\$") 4 | 5 | CLANG_FORMAT=$(which clang-format-18) 6 | if [ $? -ne 0 ]; then 7 | CLANG_FORMAT=$(which clang-format) 8 | if [ $? -ne 0 ]; then 9 | echo "[!] clang-format not installed. Unable to check source file format policy." >&2 10 | exit 1 11 | fi 12 | fi 13 | 14 | set -x 15 | 16 | for file in ${SOURCES}; 17 | do 18 | $CLANG_FORMAT ${file} > expected-format 19 | diff -u -p --label="${file}" --label="expected coding style" ${file} expected-format 20 | done 21 | exit $($CLANG_FORMAT --output-replacements-xml ${SOURCES} | egrep -c "") 22 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | Language: Cpp 3 | MaxEmptyLinesToKeep: 3 4 | IndentCaseLabels: false 5 | AllowShortIfStatementsOnASingleLine: false 6 | AllowShortCaseLabelsOnASingleLine: false 7 | AllowShortLoopsOnASingleLine: false 8 | DerivePointerAlignment: false 9 | PointerAlignment: Right 10 | SpaceAfterCStyleCast: true 11 | TabWidth: 4 12 | UseTab: Never 13 | IndentWidth: 4 14 | BreakBeforeBraces: Linux 15 | AccessModifierOffset: -4 16 | ForEachMacros: 17 | - foreach 18 | - Q_FOREACH 19 | - BOOST_FOREACH 20 | - list_for_each 21 | - list_for_each_safe 22 | - list_for_each_entry 23 | - list_for_each_entry_safe 24 | - hlist_for_each_entry 25 | - rb_list_foreach 26 | - rb_list_foreach_safe 27 | - vec_foreach 28 | - hash_for_each_possible_safe 29 | - hash_for_each_safe 30 | - hash_for_each 31 | - hash_for_each_possible -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ "main" ] 9 | schedule: 10 | - cron: '22 21 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'cpp', 'python' ] 25 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 26 | # Use only 'java' to analyze code written in Java, Kotlin or both 27 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 28 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v3 33 | 34 | # Initializes the CodeQL tools for scanning. 35 | - name: Initialize CodeQL 36 | uses: github/codeql-action/init@v3 37 | with: 38 | languages: ${{ matrix.language }} 39 | # If you wish to specify custom queries, you can do so here or in a config file. 40 | # By default, queries listed here will override any specified in a config file. 41 | # Prefix the list here with "+" to use these queries and those in the config file. 42 | 43 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 44 | # queries: security-extended,security-and-quality 45 | 46 | 47 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 48 | # If this step fails, then you should remove it and run the build manually (see below) 49 | - name: Autobuild 50 | uses: github/codeql-action/autobuild@v3 51 | 52 | # ℹ️ Command-line programs to run using the OS shell. 53 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 54 | 55 | # If the Autobuild fails above, remove it and uncomment the following three lines. 56 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 57 | 58 | # - run: | 59 | # echo "Run, Build Application using script" 60 | # ./location_of_script_within_repo/buildscript.sh 61 | 62 | - name: Perform CodeQL Analysis 63 | uses: github/codeql-action/analyze@v3 64 | with: 65 | category: "/language:${{matrix.language}}" 66 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | workflow_dispatch: 10 | 11 | jobs: 12 | validate: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: checkout code 16 | uses: actions/checkout@v4 17 | - name: validate coding style and functionality 18 | run: | 19 | sudo apt-get install -q -y clang-format-18 20 | .ci/check-format.sh 21 | .ci/build-n-run.sh 22 | shell: bash 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | .cache.mk 4 | 5 | # Object files 6 | *.o 7 | *.ko 8 | *.elf 9 | 10 | # Linker output 11 | *.map 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Libraries 18 | *.a 19 | 20 | # Kernel Module Compile Results 21 | *.mod* 22 | *.cmd 23 | .tmp_versions/ 24 | modules.order 25 | Module.symvers 26 | Mkfile.old 27 | dkms.conf 28 | 29 | # Test scripts output files 30 | scan_result.log 31 | scan_bssid.log 32 | connected.log 33 | rssi.txt -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `vwifi` 2 | 3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: 4 | 5 | The following is a set of guidelines for contributing to [vwifi](https://github.com/sysprog21/vwifi) 6 | hosted on GitHub. These are mostly guidelines, not rules. Use your best 7 | judgment, and feel free to propose changes to this document in a pull request. 8 | 9 | ## Issues 10 | 11 | This project uses GitHub Issues to track ongoing development, discuss project plans, and keep track of bugs. Be sure to search for existing issues before you create another one. 12 | 13 | Visit our [Issues page on GitHub](https://github.com/sysprog21/vwifi/issues) to search and submit. 14 | 15 | ## Coding Convention 16 | 17 | We welcome all contributions from corporate, academic and individual developers. However, there are a number of fundamental ground rules that you must adhere to in order to participate. These rules are outlined as follows: 18 | * All code must adhere to the existing C coding style (see below). While we are somewhat flexible in basic style, you will adhere to what is currently in place. Uncommented, complicated algorithmic constructs will be rejected. 19 | * All external pull requests must contain sufficient documentation in the pull request comments in order to be accepted. 20 | 21 | Software requirement: [clang-format](https://clang.llvm.org/docs/ClangFormat.html) version 12 or later. 22 | 23 | Use the command `$ clang-format -i *.[ch]` to enforce a consistent coding style. 24 | 25 | ## Coding Style for Modern C 26 | 27 | This coding style is a variation of the K&R style. Some general principles: honor tradition, but accept progress; be consistent; 28 | embrace the latest C standards; embrace modern compilers, their static analysis 29 | capabilities and sanitizers. 30 | 31 | ### Indentation 32 | 33 | Use 4 spaces rather than tabs. 34 | 35 | ### Line length 36 | 37 | All lines should generally be within 80 characters. Wrap long lines. 38 | There are some good reasons behind this: 39 | * It forces the developer to write more succinct code; 40 | * Humans are better at processing information in smaller quantity portions; 41 | * It helps users of vi/vim (and potentially other editors) who use vertical splits. 42 | 43 | ### Comments 44 | 45 | Multi-line comments shall have the opening and closing characters 46 | in a separate line, with the lines containing the content prefixed by a space 47 | and the `*` characters for alignment, e.g., 48 | ```c 49 | /* 50 | * This is a multi-line comment. 51 | */ 52 | 53 | /* One line comment. */ 54 | ``` 55 | 56 | Use multi-line comments for more elaborative descriptions or before more 57 | significant logical block of code. 58 | 59 | Single-line comments shall be written in C89 style: 60 | ```c 61 | return (uintptr_t) val; /* return a bitfield */ 62 | ``` 63 | 64 | Leave two spaces between the statement and the inline comment. 65 | 66 | ### Spacing and brackets 67 | 68 | Use one space after the conditional or loop keyword, no spaces around 69 | their brackets, and one space before the opening curly bracket. 70 | 71 | Functions (their declarations or calls), `sizeof` operator or similar 72 | macros shall not have a space after their name/keyword or around the 73 | brackets, e.g., 74 | ```c 75 | unsigned total_len = offsetof(obj_t, items[n]); 76 | unsigned obj_len = sizeof(obj_t); 77 | ``` 78 | 79 | Use brackets to avoid ambiguity and with operators such as `sizeof`, 80 | but otherwise avoid redundant or excessive brackets. 81 | 82 | ### Variable names and declarations 83 | 84 | - Use descriptive names for global variables and short names for locals. 85 | Find the right balance between descriptive and succinct. 86 | 87 | - Use [snakecase](https://en.wikipedia.org/wiki/Snake_case). 88 | Do not use "camelcase". 89 | 90 | - Do not use Hungarian notation or other unnecessary prefixing or suffixing. 91 | 92 | - Use the following spacing for pointers: 93 | ```c 94 | const char *name; /* const pointer; '*' with the name and space before it */ 95 | conf_t * const cfg; /* pointer to a const data; spaces around 'const' */ 96 | const uint8_t * const charmap; /* const pointer and const data */ 97 | const void * restrict key; /* const pointer which does not alias */ 98 | ``` 99 | 100 | ### Type definitions 101 | 102 | Declarations shall be on the same line, e.g., 103 | ```c 104 | typedef void (*dir_iter_t)(void *, const char *, struct dirent *); 105 | ``` 106 | 107 | _Typedef_ structures rather than pointers. Note that structures can be kept 108 | opaque if they are not dereferenced outside the translation unit where they 109 | are defined. Pointers can be _typedefed_ only if there is a very compelling 110 | reason. 111 | 112 | New types may be suffixed with `_t`. Structure name, when used within the 113 | translation unit, may be omitted, e.g.: 114 | 115 | ```c 116 | typedef struct { 117 | unsigned if_index; 118 | unsigned addr_len; 119 | addr_t next_hop; 120 | } route_info_t; 121 | ``` 122 | 123 | ### Initialization 124 | 125 | Embrace C99 structure initialization where reasonable, e.g., 126 | ```c 127 | static const crypto_ops_t openssl_ops = { 128 | .create = openssl_crypto_create, 129 | .destroy = openssl_crypto_destroy, 130 | .encrypt = openssl_crypto_encrypt, 131 | .decrypt = openssl_crypto_decrypt, 132 | .hmac = openssl_crypto_hmac, 133 | }; 134 | ``` 135 | 136 | Embrace C99 array initialization, especially for the state machines, e.g., 137 | ```c 138 | static const uint8_t tcp_fsm[TCP_NSTATES][2][TCPFC_COUNT] = { 139 | [TCPS_CLOSED] = { 140 | [FLOW_FORW] = { 141 | /* Handshake (1): initial SYN. */ 142 | [TCPFC_SYN] = TCPS_SYN_SENT, 143 | }, 144 | }, 145 | ... 146 | } 147 | ``` 148 | 149 | ### Control structures 150 | 151 | Try to make the control flow easy to follow. Avoid long convoluted logic 152 | expressions; try to split them where possible (into inline functions, 153 | separate if-statements, etc). 154 | 155 | The control structure keyword and the expression in the brackets should be 156 | separated by a single space. The opening curly bracket shall be in the 157 | same line, also separated by a single space. Example: 158 | 159 | ```c 160 | for (;;) { 161 | obj = get_first(); 162 | while ((obj = get_next(obj))) { 163 | ... 164 | } 165 | if (done) 166 | break; 167 | } 168 | ``` 169 | 170 | Do not add inner spaces around the brackets. There should be one space after 171 | the semicolon when `for` has expressions: 172 | ```c 173 | for (unsigned i = 0; i < __arraycount(items); i++) { 174 | ... 175 | } 176 | ``` 177 | 178 | #### Avoid unnecessary nesting levels 179 | 180 | Avoid: 181 | ```c 182 | int inspect(obj_t *obj) 183 | { 184 | if (cond) { 185 | ... 186 | /* long code block */ 187 | ... 188 | return 0; 189 | } 190 | return -1; 191 | } 192 | ``` 193 | 194 | Consider: 195 | ```c 196 | int inspect(obj_t *obj) 197 | { 198 | if (!cond) 199 | return -1; 200 | 201 | ... 202 | return 0; 203 | } 204 | ``` 205 | 206 | However, do not make logic more convoluted. 207 | 208 | ### `if` statements 209 | 210 | Curly brackets and spacing follow the K&R style: 211 | ```c 212 | if (a == b) { 213 | .. 214 | } else if (a < b) { 215 | ... 216 | } else { 217 | ... 218 | } 219 | ``` 220 | 221 | Simple and succinct one-line if-statements may omit curly brackets: 222 | ```c 223 | if (!valid) 224 | return -1; 225 | ``` 226 | 227 | However, do prefer curly brackets with multi-line or more complex statements. 228 | If one branch uses curly brackets, then all other branches shall use the 229 | curly brackets too. 230 | 231 | Wrap long conditions to the if-statement indentation adding extra 4 spaces: 232 | ```c 233 | if (some_long_expression && 234 | another_expression) { 235 | ... 236 | } 237 | ``` 238 | 239 | #### Avoid redundant `else` 240 | 241 | Avoid: 242 | ```c 243 | if (flag & F_FEATURE_X) { 244 | ... 245 | return 0; 246 | } else { 247 | return -1; 248 | } 249 | ``` 250 | 251 | Consider: 252 | ```c 253 | if (flag & F_FEATURE_X) { 254 | ... 255 | return 0; 256 | } 257 | return -1; 258 | ``` 259 | 260 | ### `switch` statements 261 | 262 | Switch statements should have the `case` blocks at the same indentation 263 | level, e.g.: 264 | ```c 265 | switch (expr) { 266 | case A: 267 | ... 268 | break; 269 | case B: 270 | /* fallthrough */ 271 | case C: 272 | ... 273 | break; 274 | } 275 | ``` 276 | 277 | If the case block does not break, then it is strongly recommended to add a 278 | comment containing "fallthrough" to indicate it. Modern compilers can also 279 | be configured to require such comment (see gcc `-Wimplicit-fallthrough`). 280 | 281 | ### Function definitions 282 | 283 | The opening and closing curly brackets shall also be in the separate lines (K&R style). 284 | 285 | ```c 286 | ssize_t hex_write(FILE *stream, const void *buf, size_t len) 287 | { 288 | ... 289 | } 290 | ``` 291 | 292 | Do not use old style K&R style C definitions. 293 | 294 | ### Object abstraction 295 | 296 | Objects are often "simulated" by the C programmers with a `struct` and 297 | its "public API". To enforce the information hiding principle, it is a 298 | good idea to define the structure in the source file (translation unit) 299 | and provide only the _declaration_ in the header. For example, `obj.c`: 300 | 301 | ```c 302 | #include "obj.h" 303 | 304 | struct obj { 305 | int value; 306 | } 307 | 308 | obj_t *obj_create(void) 309 | { 310 | return calloc(1, sizeof(obj_t)); 311 | } 312 | 313 | void obj_destroy(obj_t *obj) 314 | { 315 | free(obj); 316 | } 317 | ``` 318 | 319 | With an example `obj.h`: 320 | ```c 321 | #ifndef _OBJ_H_ 322 | #define _OBJ_H_ 323 | 324 | typedef struct obj; 325 | 326 | obj_t *obj_create(void); 327 | void obj_destroy(obj_t *); 328 | 329 | #endif 330 | ``` 331 | 332 | Such structuring will prevent direct access of the `obj_t` members outside 333 | the `obj.c` source file. The implementation (of such "class" or "module") 334 | may be large and abstracted within separate source files. In such case, 335 | consider separating structures and "methods" into separate headers (think of 336 | different visibility), for example `obj_impl.h` (private) and `obj.h` (public). 337 | 338 | Consider `crypto_impl.h`: 339 | ```c 340 | #ifndef _CRYPTO_IMPL_H_ 341 | #define _CRYPTO_IMPL_H_ 342 | 343 | #if !defined(__CRYPTO_PRIVATE) 344 | #error "only to be used by the crypto modules" 345 | #endif 346 | 347 | #include "crypto.h" 348 | 349 | typedef struct crypto { 350 | crypto_cipher_t cipher; 351 | void *key; 352 | size_t key_len; 353 | ... 354 | } 355 | ... 356 | 357 | #endif 358 | ``` 359 | 360 | And `crypto.h` (public API): 361 | 362 | ```c 363 | #ifndef _CRYPTO_H_ 364 | #define _CRYPTO_H_ 365 | 366 | typedef struct crypto crypto_t; 367 | 368 | crypto_t *crypto_create(crypto_cipher_t); 369 | void crypto_destroy(crypto_t *); 370 | ... 371 | 372 | #endif 373 | ``` 374 | 375 | ### Use reasonable types 376 | 377 | Use `unsigned` for general iterators; use `size_t` for general sizes; use 378 | `ssize_t` to return a size which may include an error. Of course, consider 379 | possible overflows. 380 | 381 | Avoid using `uint8_t` or `uint16_t` or other sub-word types for general 382 | iterators and similar cases, unless programming for micro-controllers or 383 | other constrained environments. 384 | 385 | C has rather peculiar _type promotion rules_ and unnecessary use of sub-word 386 | types might contribute to a bug once in a while. 387 | 388 | ### Embrace portability 389 | 390 | #### Byte-order 391 | 392 | Do not assume x86 or little-endian architecture. Use endian conversion 393 | functions for operating the on-disk and on-the-wire structures or other 394 | cases where it is appropriate. 395 | 396 | #### Types 397 | 398 | - Do not assume a particular 32-bit vs 64-bit architecture, e.g., do not 399 | assume the size of `long` or `unsigned long`. Use `int64_t` or `uint64_t` 400 | for the 8-byte integers. 401 | 402 | - Do not assume `char` is signed; for example, on Arm it is unsigned. 403 | 404 | - Use C99 macros for constant prefixes or formatting of the fixed-width 405 | types. 406 | 407 | Use: 408 | ```c 409 | #define SOME_CONSTANT (UINT64_C(1) << 48) 410 | printf("val %" PRIu64 "\n", SOME_CONSTANT); 411 | ``` 412 | 413 | Do not use: 414 | ```c 415 | #define SOME_CONSTANT (1ULL << 48) 416 | printf("val %lld\n", SOME_CONSTANT); 417 | ``` 418 | 419 | #### Avoid unaligned access 420 | 421 | Do not assume unaligned access is safe. It is not safe on Arm, POWER, 422 | and various other architectures. Moreover, even on x86 unaligned access 423 | is slower. 424 | 425 | #### Avoid extreme portability 426 | 427 | Unless programming for micro-controllers or exotic CPU architectures, 428 | focus on the common denominator of the modern CPU architectures, avoiding 429 | the very maximum portability which can make the code unnecessarily cumbersome. 430 | 431 | Some examples: 432 | - It is fair to assume `sizeof(int) == 4` since it is the case on all modern 433 | mainstream architectures. PDP-11 era is long gone. 434 | - Using `1U` instead of `UINT32_C(1)` or `(uint32_t) 1` is also fine. 435 | - It is fair to assume that `NULL` is matching `(uintptr_t) 0` and it is fair 436 | to `memset()` structures with zero. Non-zero `NULL` is for retro computing. 437 | 438 | ## References 439 | - [Linux kernel coding style](https://www.kernel.org/doc/html/latest/process/coding-style.html) 440 | - 1999, Brian W. Kernighan and Rob Pike, The Practice of Programming, Addison–Wesley. 441 | - 1993, Bill Shannon, [C Style and Coding Standards for SunOS](https://devnull-cz.github.io/unix-linux-prog-in-c/cstyle.ms.pdf) 442 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2024 National Cheng Kung University, Taiwan. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET_MODULE := vwifi 2 | obj-m := $(TARGET_MODULE).o 3 | 4 | ccflags-y := -std=gnu99 -Wno-declaration-after-statement 5 | KDIR ?= /lib/modules/$(shell uname -r)/build 6 | GIT_HOOKS := .git/hooks/applied 7 | 8 | all: kmod vwifi-tool 9 | 10 | kmod: 11 | $(MAKE) -C $(KDIR) M=$(shell pwd) modules 12 | 13 | vwifi-tool: vwifi-tool.c 14 | $(CC) $(ccflags-y) -o $@ $< 15 | 16 | clean: 17 | $(MAKE) -C $(KDIR) M=$(shell pwd) clean 18 | $(RM) vwifi-tool 19 | 20 | check: all 21 | @scripts/verify.sh -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vwifi: A Simple Virtual Wireless Driver for Linux 2 | 3 | `vwifi` implements a minimal interface to provide essential functionalities, 4 | such as scanning for dummy Wi-Fi networks, establishing connections, and 5 | disconnecting from them. 6 | 7 | It is built upon the [cfg80211 subsystem](https://www.kernel.org/doc/html/latest/driver-api/80211/cfg80211.html), 8 | which collaborates with FullMAC drivers. Currently, `vwifi` supports both 9 | Station Mode and Host AP Mode and is equipped with robust WPA/WPA2 security 10 | features. This enables users to configure a wireless environment using `vwifi`, 11 | `hostapd` (in HostAP mode interface), and `wpa_supplicant` (in station mode interface). 12 | 13 | Moreover, when running on hypervisors like QEMU, `vwifi` functions as a virtio-net 14 | driver, allowing for inter-guest communication. 15 | 16 | ## Prerequisite 17 | 18 | The following packages must be installed before building `vwifi`. 19 | 20 | To compile the kernel driver successfully, the package versions of the currently used kernel, 21 | kernel-devel, and kernel-headers need to be matched. Run the following command to install the required kernel headers: 22 | ```shell 23 | $ sudo apt install linux-headers-$(uname -r) 24 | ``` 25 | 26 | Since `vwifi` relies on the Linux wireless (IEEE-802.11) subsystem, [iw](https://wireless.wiki.kernel.org/en/users/documentation/iw) is necessary for retrieving more information and configuring. 27 | Install it using the following command: 28 | ```shell 29 | $ sudo apt install iw 30 | ``` 31 | 32 | If running the test script (scripts/verify.sh), Python 3, hostapd, and some additional packages are necessary. 33 | ```shell 34 | $ sudo apt install python3 python3-pip hostapd 35 | $ pip3 install numpy matplotlib 36 | ``` 37 | 38 | ## Testing environment (non-virtio) 39 | To test the network environment effectively, we utilize **Linux network namespaces**. These namespaces isolate network environments from the host system, providing distinct instances of network stacks with independent routes, firewall rules, and network devices. 40 | 41 | Without network namespaces, virtual interfaces created within the same namespace use the loopback device for packet transmission between them, as the kernel recognizes them as residing on the same host. 42 | 43 | In our testing setup, all interfaces created by `vwifi` are placed within an isolated network namespace. This approach ensures that each virtual interface operates independently, facilitating comprehensive testing of networking functionalities without interference from the host's network configuration. 44 | 45 | Below, we will conduct two separate tests: Infrastructure BSS and Independent BSS. 46 | ### Infrastructure BSS 47 |

logo image

48 | 49 | The testing environment consists of **one AP and two STAs**. 50 | 51 | The testing environment operates in IEEE 802.11 infrastructure BSS, which imposes a constraint: **STAs cannot directly communicate with each other**. 52 | When an STA wants to communicate with other devices, it must send packets to the AP. 53 | The AP then performs the following actions based on the packet type: 54 | 1. Unicast: If the packet is intended for another STA, the AP forwards it directly to the destination STA without passing it to the protocol stack. 55 | If the packet is intended for the AP itself, it is passed to the protocol stack. 56 | 2. Broadcast: The AP forwards the packet to all other STAs in the network, except for the source STA, and then passes it to the protocol stack. 57 | 3. Multicast: The AP treats multicast packets the same way as broadcast packets. 58 | 59 | ### Independent BSS 60 |

logo image

61 | 62 | The testing environment consists of **two IBSS devices**. 63 | 64 | The testing environment operates in IEEE 802.11 independent BSS. IBSS devices can communicate with any device in the same IBSS network **without the need to establish a connection beforehand**. However, devices in different IBSS networks cannot communicate with each other. 65 | ## Build and Run (non-virtio) 66 | 67 | To build the kernel module, execute the following command: 68 | ```shell 69 | $ make 70 | ``` 71 | 72 | Load the cfg80211 kernel module by running the following command: 73 | ```shell 74 | $ sudo modprobe cfg80211 75 | ``` 76 | 77 | Insert the `vwifi` driver. 78 | This will create three interfaces (the "station" parameter can be modified according to preference): 79 | ```shell 80 | $ sudo insmod vwifi.ko station=5 81 | ``` 82 | 83 | Please note that interfaces can only be created in station mode during the initialization phase. 84 | However, they can be switched to Host AP mode later using hostapd. 85 | 86 | ### Checking Network Interfaces 87 | 88 | To check the network interfaces, run the following command: 89 | ```shell 90 | $ ip link 91 | ``` 92 | 93 | There should be entries starting with `vw0`, `vw1`, `vw2`, `vw3`, and `vw4`, which correspond to the interfaces created by `vwifi`. 94 | 95 | To view the available wireless interfaces, execute the following command: 96 | ```shell 97 | $ sudo iw dev 98 | ``` 99 | 100 | You should see something similar to the following output: 101 | ``` 102 | phy#5 103 | Interface vw4 104 | ifindex 7 105 | wdev 0x500000001 106 | addr 00:76:77:34:00:00 107 | type managed 108 | txpower 0.00 dBm 109 | phy#4 110 | Interface vw3 111 | ifindex 6 112 | wdev 0x400000001 113 | addr 00:76:77:33:00:00 114 | type managed 115 | txpower 0.00 dBm 116 | phy#3 117 | Interface vw2 118 | ifindex 5 119 | wdev 0x300000001 120 | addr 00:76:77:32:00:00 121 | type managed 122 | txpower 0.00 dBm 123 | phy#2 124 | Interface vw1 125 | ifindex 4 126 | wdev 0x200000001 127 | addr 00:76:77:31:00:00 128 | type managed 129 | txpower 0.00 dBm 130 | phy#1 131 | Interface vw0 132 | ifindex 3 133 | wdev 0x100000001 134 | addr 00:76:77:30:00:00 135 | type managed 136 | txpower 0.00 dBm 137 | ``` 138 | 139 | As observed, each interface has its own phy (`struct wiphy`), allowing them to be placed into separate network namespaces. 140 | 141 | ### Dumping Wireless Information 142 | 143 | To obtain wireless information, execute the following command: 144 | ```shell 145 | $ sudo iw list 146 | ``` 147 | 148 | Reference output: 149 | ``` 150 | Wiphy vw_phy4 151 | (... omit) 152 | Wiphy vw_phy3 153 | (... omit) 154 | Wiphy vw_phy2 155 | (... omit) 156 | Wiphy vw_phy1 157 | (... omit) 158 | Wiphy vw_phy0 159 | wiphy index: 0 160 | max # scan SSIDs: 69 161 | max scan IEs length: 0 bytes 162 | max # sched scan SSIDs: 0 163 | max # match sets: 0 164 | Retry short limit: 7 165 | Retry long limit: 4 166 | Coverage class: 0 (up to 0m) 167 | Supported Ciphers: 168 | * WEP40 (00-0f-ac:1) 169 | * WEP104 (00-0f-ac:5) 170 | * TKIP (00-0f-ac:2) 171 | * CCMP-128 (00-0f-ac:4) 172 | Available Antennas: TX 0 RX 0 173 | Supported interface modes: 174 | * managed 175 | * AP 176 | Band 1: 177 | Bitrates (non-HT): 178 | * 1.0 Mbps 179 | * 2.0 Mbps 180 | * 5.5 Mbps 181 | * 11.0 Mbps 182 | * 6.0 Mbps 183 | * 9.0 Mbps 184 | * 12.0 Mbps 185 | * 18.0 Mbps 186 | * 24.0 Mbps 187 | * 36.0 Mbps 188 | * 48.0 Mbps 189 | * 54.0 Mbps 190 | Frequencies: 191 | * 2412 MHz [1] (20.0 dBm) 192 | * 2417 MHz [2] (20.0 dBm) 193 | * 2422 MHz [3] (20.0 dBm) 194 | * 2427 MHz [4] (20.0 dBm) 195 | * 2432 MHz [5] (20.0 dBm) 196 | * 2437 MHz [6] (20.0 dBm) 197 | * 2442 MHz [7] (20.0 dBm) 198 | * 2447 MHz [8] (20.0 dBm) 199 | * 2452 MHz [9] (20.0 dBm) 200 | * 2457 MHz [10] (20.0 dBm) 201 | * 2462 MHz [11] (20.0 dBm) 202 | * 2467 MHz [12] (20.0 dBm) (no IR) 203 | * 2472 MHz [13] (20.0 dBm) (no IR) 204 | * 2484 MHz [14] (20.0 dBm) (no IR) 205 | Supported commands: 206 | * set_interface 207 | * new_key 208 | * start_ap 209 | * set_wiphy_netns 210 | * set_channel 211 | * connect 212 | * disconnect 213 | software interface modes (can always be added): 214 | interface combinations are not supported 215 | Device supports scan flush. 216 | max # scan plans: 1 217 | max scan plan interval: -1 218 | max scan plan iterations: 0 219 | Supported extended features: 220 | ``` 221 | 222 | You can see the supported operating modes, supported ciphers, channels, bitrates, and supported commands in the output. 223 | 224 | The "managed mode" in the Supported interface modes is identical to station mode. 225 | 226 | ### Creating Network Namespaces 227 | Next, create three network namespaces using the following commands: 228 | ```shell 229 | $ sudo ip netns add ns0 230 | $ sudo ip netns add ns1 231 | $ sudo ip netns add ns2 232 | ```` 233 | 234 | Find the `wiphy` name for the three interfaces. 235 | The index number for the `wiphy` name postfix might be different each time. 236 | Please use the following command for the ease of memorizing different index number everytime. 237 | ```shell 238 | $ vw0_phy=$(sudo iw dev vw0 info | grep wiphy | awk '{print $2}') 239 | $ vw0_phy=$(sudo iw list | grep "wiphy index: $vw0_phy" -B 1 | grep Wiphy | awk '{print $2}') 240 | $ vw1_phy=$(sudo iw dev vw1 info | grep wiphy | awk '{print $2}') 241 | $ vw1_phy=$(sudo iw list | grep "wiphy index: $vw1_phy" -B 1 | grep Wiphy | awk '{print $2}') 242 | $ vw2_phy=$(sudo iw dev vw2 info | grep wiphy | awk '{print $2}') 243 | $ vw2_phy=$(sudo iw list | grep "wiphy index: $vw2_phy" -B 1 | grep Wiphy | awk '{print $2}') 244 | ``` 245 | 246 | Check whether the name of each `wiphy` is the same as the name listing under the command `sudo iw list` 247 | ```shell 248 | $ echo $vw0_phy 249 | vw_phy0 250 | $ echo $vw1_phy 251 | vw_phy1 252 | $ echo $vw2_phy 253 | vw_phy2 254 | ``` 255 | 256 | Assign the three interfaces to separate network namespaces. 257 | Please note that the `wiphy` is placed within the network namespace, and the interface associated with that wiphy will be contained within it. 258 | ```shell 259 | $ sudo iw phy vw_phy0 set netns name ns0 260 | $ sudo iw phy vw_phy1 set netns name ns1 261 | $ sudo iw phy vw_phy2 set netns name ns2 262 | ``` 263 | 264 | ### Assigning IP Addresses to Each Interface 265 | 266 | Now, assign an IP address to both interfaces using the following commands: 267 | ```shell 268 | $ sudo ip netns exec ns0 ip addr add 10.0.0.1/24 dev vw0 269 | $ sudo ip netns exec ns1 ip addr add 10.0.0.2/24 dev vw1 270 | $ sudo ip netns exec ns2 ip addr add 10.0.0.3/24 dev vw2 271 | ``` 272 | 273 | ### Running hostapd on the HostAP Mode Interface 274 | Prepare the following script `hostapd.conf` (you can modify the script based on your needs): 275 | ``` 276 | interface=vw0 277 | driver=nl80211 278 | debug=1 279 | ctrl_interface=/var/run/hostapd 280 | ctrl_interface_group=wheel 281 | channel=6 282 | ssid=test 283 | wpa=2 284 | wpa_passphrase=12345678 285 | wpa_key_mgmt=WPA-PSK 286 | wpa_pairwise=CCMP 287 | ``` 288 | 289 | Run `hostapd` on the interface `vw0`: 290 | ```shell 291 | $ sudo ip netns exec ns0 hostapd -i vw0 -B scripts/hostapd.conf 292 | ``` 293 | 294 | ### Running `wpa_supplicant` on the Station Mode Interfaces 295 | Prepare the following script `wpa_supplicant.conf` (you can modify the script based on your needs): 296 | ```shell 297 | network={ 298 | ssid="test" 299 | psk="12345678" 300 | } 301 | ``` 302 | 303 | Then run the `wpa_supplicant` on the interface `ns1` and `ns2`: 304 | ```shell 305 | $ sudo ip netns exec ns1 \ 306 | wpa_supplicant -i vw1 -B -c scripts/wpa_supplicant.conf 307 | $ sudo ip netns exec ns2 \ 308 | wpa_supplicant -i vw2 -B -c scripts/wpa_supplicant.conf 309 | ``` 310 | 311 | ### Validating the Connection 312 | To validate the connection, use the following command: 313 | ```shell 314 | $ sudo ip netns exec ns1 iw dev vw1 link 315 | ``` 316 | 317 | The output might seem like this: 318 | ``` 319 | Connected to 00:6f:77:6c:30:00 (on vw1) 320 | SSID: test 321 | freq: 2437 322 | RX: 282 bytes (2 packets) 323 | TX: 248 bytes (2 packets) 324 | signal: -84 dBm 325 | ``` 326 | 327 | It shows that `vw1` has connected to the BSS with BSSID `00:6f:77:6c:30:00`, which is the MAC address of `vw0`. 328 | 329 | You may also check the connection of `vw2` by slightly changing the command above. 330 | 331 | On the other hand, we can validate all the stations connected to `vw0` by the following commands: 332 | ```shell 333 | sudo ip netns exec ns0 iw dev vw0 station dump 334 | ``` 335 | 336 | The output may seem like this: 337 | ``` 338 | Station 00:6f:77:6c:31:00 (on vw0) 339 | inactive time: 5588 ms 340 | rx bytes: 5366 341 | rx packets: 65 342 | tx bytes: 1772 343 | tx packets: 18 344 | tx failed: 74 345 | signal: -57 dBm 346 | current time: 1689679337171 ms 347 | Station 00:6f:77:6c:32:00 (on vw0) 348 | inactive time: 5588 ms 349 | rx bytes: 5366 350 | rx packets: 65 351 | tx bytes: 1772 352 | tx packets: 18 353 | tx failed: 74 354 | signal: -57 dBm 355 | current time: 1689679337171 ms 356 | ``` 357 | 358 | ### Transmission/Receivement test 359 | Finally, we can do the ping test: 360 | 1. To perform a ping test between two STAs (`vw1` and `vw2`), use the following command: 361 | ```shell 362 | $ sudo ip netns exec ns1 ping -c 4 10.0.0.3 363 | ``` 364 | 365 | You should see output similar to the following: 366 | ``` 367 | PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data. 368 | 64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=0.188 ms 369 | 64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.147 ms 370 | 64 bytes from 10.0.0.3: icmp_seq=3 ttl=64 time=0.082 ms 371 | 64 bytes from 10.0.0.3: icmp_seq=4 ttl=64 time=0.136 ms 372 | 373 | --- 10.0.0.3 ping statistics --- 374 | 4 packets transmitted, 4 received, 0% packet loss, time 3036ms 375 | rtt min/avg/max/mdev = 0.082/0.138/0.188/0.037 ms 376 | ``` 377 | 378 | 2. To perform a ping test between the AP (`vw0`) and a STA (`vw2`), execute the following command: 379 | ```shell 380 | $ sudo ip netns exec ns2 ping -c 4 10.0.0.1 381 | ``` 382 | 383 | You should see output similar to the following: 384 | ``` 385 | PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 386 | 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.342 ms 387 | 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.054 ms 388 | 64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.106 ms 389 | 64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=0.063 ms 390 | 391 | --- 10.0.0.1 ping statistics --- 392 | 4 packets transmitted, 4 received, 0% packet loss, time 3058ms 393 | rtt min/avg/max/mdev = 0.054/0.141/0.342/0.117 ms 394 | ``` 395 | ### IBSS mode 396 | 397 | #### Creating Network Namespaces 398 | Create three network namespaces using the following commands: 399 | ```shell 400 | $ sudo ip netns add ns3 401 | $ sudo ip netns add ns4 402 | ``` 403 | Find the `wiphy` name for the two interfaces. 404 | The index number for the `wiphy` name postfix might be different each time. 405 | Please use the following command for the ease of memorizing different index number everytime. 406 | ```shell 407 | $ vw3_phy=$(sudo iw dev vw3 info | grep wiphy | awk '{print $2}') 408 | $ vw3_phy=$(sudo iw list | grep "wiphy index: $vw3_phy" -B 1 | grep Wiphy | awk '{print $2}') 409 | $ vw4_phy=$(sudo iw dev vw4 info | grep wiphy | awk '{print $2}') 410 | $ vw4_phy=$(sudo iw list | grep "wiphy index: $vw4_phy" -B 1 | grep Wiphy | awk '{print $2}') 411 | ``` 412 | Check whether the name of each `wiphy` is the same as the name listing under the command `sudo iw list` 413 | ```shell 414 | $ echo $vw3_phy 415 | vw_phy3 416 | $ echo $vw4_phy 417 | vw_phy4 418 | ``` 419 | Assign the two interfaces to separate network namespaces. 420 | Please note that the `wiphy` is placed within the network namespace, and the interface associated with that wiphy will be contained within it. 421 | ```shell 422 | $ sudo iw phy vw_phy3 set netns name ns3 423 | $ sudo iw phy vw_phy4 set netns name ns4 424 | ``` 425 | #### Assigning IP Addresses to Each Interface 426 | 427 | Now, assign an IP address to both interfaces using the following commands: 428 | ```shell 429 | $ sudo ip netns exec ns3 ip addr add 10.0.0.4/24 dev vw3 430 | $ sudo ip netns exec ns4 ip addr add 10.0.0.5/24 dev vw4 431 | ``` 432 | There are two methods to configure an IBSS network: manual configuration or using WPA. 433 | #### Option1 : Manual configuration 434 | ##### Switch to IBSS mode 435 | Switch device to IBSS mode using the following command : 436 | 437 | ***iw dev [interface] set type ibss*** 438 | 439 | The following commands switch `vw3` and `vw4` to IBSS mode. 440 | ```shell 441 | $ sudo ip netns exec ns3 iw dev vw3 set type ibss 442 | $ sudo ip netns exec ns4 iw dev vw4 set type ibss 443 | ``` 444 | Check the information of `vw3`. 445 | ```shell 446 | $ sudo ip netns exec ns3 iw dev vw3 info 447 | ``` 448 | You should see output similar to the following: 449 | ``` 450 | Interface vw3 451 | ifindex 6 452 | wdev 0x400000001 453 | addr 00:76:77:33:00:00 454 | type IBSS 455 | wiphy 4 456 | txpower 0.00 dBm 457 | ``` 458 | ##### Join IBSS network 459 | ```shell 460 | $ sudo ip netns exec ns3 ip link set vw3 up 461 | $ sudo ip netns exec ns4 ip link set vw4 up 462 | ``` 463 | Users can join a specific IBSS cell and configure additional settings using the command : 464 | 465 | ***iw dev [interface] ibss join [SSID] [freq in MHz] [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [fixed-freq] [fixed-bssid] [beacon-interval ] [basic-rates ] [mcast-rate ] [key d:0:abcde]*** 466 | 467 | If the IBSS cell does not already exist, it will be created. 468 | 469 | The following command makes `vw3` and `vw4` join the same IBSS cell with the SSID `ibss1` and specifies the frequency as 2412 MHz: 470 | ```shell 471 | $ sudo ip netns exec ns3 iw dev vw3 ibss join ibss1 2412 NOHT fixed-freq 00:76:77:33:00:00 beacon-interval 200 472 | $ sudo ip netns exec ns4 iw dev vw4 ibss join ibss1 2412 NOHT fixed-freq 00:76:77:33:00:00 beacon-interval 200 473 | ``` 474 | Check the information of `vw3`. 475 | ```shell 476 | $ sudo ip netns exec ns3 iw dev vw3 info 477 | ``` 478 | You should see output similar to the following: 479 | ``` 480 | Interface vw3 481 | ifindex 6 482 | wdev 0x400000001 483 | addr 00:76:77:33:00:00 484 | ssid ibss1 485 | type IBSS 486 | wiphy 4 487 | txpower 0.00 dBm 488 | ``` 489 | #### Option2 : Using WPA 490 | ```shell 491 | $ sudo ip netns exec ns3 ip link set vw3 up 492 | $ sudo ip netns exec ns4 ip link set vw4 up 493 | ``` 494 | Prepare the following script `wpa_supplicant_ibss.conf` (you can modify the script based on your needs): 495 | ```shell 496 | network={ 497 | ssid="ibss1" 498 | mode=1 499 | frequency=2412 500 | key_mgmt=WPA-PSK 501 | proto=RSN 502 | pairwise=CCMP 503 | group=CCMP 504 | psk="12345678" 505 | } 506 | ``` 507 | Using the command **wpa_supplicant**, configure `vw3` and `vw4` to join `ibss1`. 508 | ```shell 509 | $ sudo ip netns exec ns3 wpa_supplicant -i vw3 -B -c scripts/wpa_supplicant_ibss.conf 510 | $ sudo ip netns exec ns4 wpa_supplicant -i vw4 -B -c scripts/wpa_supplicant_ibss.conf 511 | ``` 512 | Check the information of `vw3`. 513 | ```shell 514 | $ sudo ip netns exec ns3 iw dev vw3 info 515 | ``` 516 | You should see output similar to the following: 517 | ``` 518 | Interface vw3 519 | ifindex 6 520 | wdev 0x400000001 521 | addr 00:76:77:33:00:00 522 | ssid ibss1 523 | type IBSS 524 | wiphy 4 525 | txpower 0.00 dBm 526 | ``` 527 | #### Transmission/Receivement test 528 | To perform a ping test between two IBSS devices (`vw3` and `vw4`) in the same ibss cell (`ibss1`), use the following command: 529 | ```shell 530 | $ sudo ip netns exec ns3 ping -c 1 10.0.0.5 531 | ``` 532 | You should see output similar to the following: 533 | ``` 534 | PING 10.0.0.5 (10.0.0.5) 56(84) bytes of data. 535 | 64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=0.093 ms 536 | 537 | --- 10.0.0.5 ping statistics --- 538 | 1 packets transmitted, 1 received, 0% packet loss, time 0ms 539 | rtt min/avg/max/mdev = 0.093/0.093/0.093/0.000 ms 540 | ``` 541 | #### Leave IBSS network 542 | To leave the current IBSS cell, use ***iw dev [interface] ibss leave***. 543 | 544 | The following command makes `vw3` and `vw4` leave `ibss1`: 545 | ```shell 546 | $ sudo ip netns exec ns3 iw dev vw3 ibss leave 547 | $ sudo ip netns exec ns4 iw dev vw4 ibss leave 548 | ``` 549 | Check the information of `vw3`. 550 | ```shell 551 | $ sudo ip netns exec ns3 iw dev vw3 info 552 | ``` 553 | You should see output similar to the following: 554 | ``` 555 | Interface vw3 556 | ifindex 6 557 | wdev 0x400000001 558 | addr 00:76:77:33:00:00 559 | type IBSS 560 | wiphy 4 561 | txpower 0.00 dBm 562 | ``` 563 | 564 | ### vwifi-tool 565 | A userspace tool which supports more user-specific utilization for vwifi. 566 | Aiming to provide more flexibility and customization for users of vwifi. 567 | Currently supporting feature: 568 | * display the status of vwifi driver 569 | * Use netlink socket to communicate with vwifi driver allowing user to configure user-specific deny list 570 | 571 | #### Status checking 572 | We can use `vwifi-tool` to check the status of vwifi driver by executing the following command: 573 | ``` 574 | $ ./vwifi-tool 575 | ``` 576 | If vwifi is loaded into kernel, you should see the following output: 577 | ``` 578 | vwifi status : live 579 | ``` 580 | Otherwise, vwifi isn't loaded into kernel yet, the output will be: 581 | ``` 582 | vwifi status : not loaded 583 | ``` 584 | 585 | #### Denylist test 586 | vwifi also supports denylist ability to allow some interfaces to deny packets from certain interfaces. 587 | We can use `vwifi-tool` to set or unset denylist for vwifi, multiple options are explained as below 588 | * `-d` : specify the destination interface for a denylist pair 589 | * `-s` : specify the source interface for a denylist pair 590 | * `-c` : `1` means to unset the denylist in vwifi, default as `0` 591 | 592 | Set the denylist pair using vwifi-tool like the following 593 | ``` 594 | $ ./vwifi-tool -d vw2 -s vw1 595 | ``` 596 | You should see the following output, including your denylist which will be sent to vwifi 597 | ``` 598 | vwifi status : live 599 | denylist: 600 | vw2 denys vw1 601 | Configuring denylist for vwifi... 602 | Message from vwifi: vwifi has received your denylist 603 | ``` 604 | Then you can try to do the ping test again 605 | ``` 606 | $ sudo ip netns exec ns1 ping -c 4 10.0.0.3 607 | ``` 608 | You should see the following output: 609 | ``` 610 | PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data. 611 | 612 | --- 10.0.0.3 ping statistics --- 613 | 4 packets transmitted, 0 received, 100% packet loss, time 3053ms 614 | ``` 615 | You can adjust the content of your denylist and load it into vwifi anytime. 616 | 617 | If you want to unset the denylist in vwifi, simply add the option `-c` with vwifi-tool 618 | ``` 619 | $ ./vwifi-tool -c 620 | ``` 621 | You'll see the following output 622 | ``` 623 | vwifi status : live 624 | Unset denylist for vwifi... 625 | Configuring denylist for vwifi... 626 | Message from vwifi: vwifi has received your denylist 627 | ``` 628 | ## Testing environment (virtio) 629 | Below is our testing environment with virtio feature: 630 | 631 |

vwifi virtio image

632 | 633 | `vwifi` utilizes the virtio-net device, the Linux tap device, and the Linux 634 | bridge device to enable inter-guest communication. 635 | 636 | In our testing environment, on the host side, we create three tap devices and 637 | one bridge device. Then, we attach each tap device to the bridge device. We also 638 | create three VMs using QEMU, configuring each with a virtio-net device and 639 | specifying that each virtio-net device connects to a corresponding tap device on 640 | the host. 641 | 642 | In VM1, the network interface created by `vwifi` operates in HostAP mode, with 643 | the user program hostapd running on top of it. In VM2 and VM3, we have STA mode 644 | interfaces with `wpa_supplicant` running on top of them. 645 | 646 | ## Build and Run (virtio) 647 | Below describes how to build a minimal workable VM environment. 648 | ### Build Linux kernel 649 | Download Linux kernel source from [kernel.org](https://kernel.org/). 650 | 651 | Enter the top directory of the Linux kernel source and use the following command to generate the configuration file: 652 | ```shell 653 | $ make menuconfig 654 | ``` 655 | The default kernel cofiguration will work for our testing environment, so just click `save` and we get `.config` on the top directory. 656 | 657 | Before building the kernel, please ensure that all the needed packges or libraries have been installed in your machine. 658 | 659 | Then Build the kernel: 660 | ```shell 661 | $ make -j 662 | ``` 663 | 664 | Once we have finished building the kernel, the `bzImage` will be in the `arch//boot/` directory. For x86 machine, that is `arch/x86/boot/bzImage`. 665 | 666 | ### Build `vwifi` 667 | Assume directory vwifi is cloned from [vwifi](https://github.com/sysprog21/vwifi), and enter the top directory. 668 | 669 | `vwifi` let the `$(KDIR)` in `Makefile` point to the default kernel source on the host, but the kernel version of the host may differ from the one for the VMs, so we need to replace it to point to the kernel source we previously built for the VMs: 670 | ```shell 671 | KDIR = 672 | ``` 673 | 674 | Save the change and build `vwifi`: 675 | ```shell 676 | $ make 677 | ``` 678 | 679 | On success, we expect that `vwifi.ko` will show in the top directory of `vwifi`. 680 | 681 | The cfg80211 API and net device API in the kernel have changed along with the new kernel version, and forgive us for not testing for all of them. 682 | ### Build Rootfs by Buildroot 683 | We use `buildroot` to build our rootfs, you can either choose your desired tool for building rootfs. 684 | 685 | Get the `builtroot` from [https://buildroot.org](https://buildroot.org). 686 | 687 | Enter the top directory of the `buildroot` source and use the following command to generate the first-phase configuration file: 688 | ```shell 689 | $ make qemu_x86_64_defconfig 690 | ``` 691 | 692 | The `qemu_x86_64_defconfig` configuration will not fit the testing environment, so we need to further configure it: 693 | ```shell 694 | $ make menuconfig 695 | ``` 696 | 697 | We need a filesystem overlay for putting our `vwifi` kernel module and some user program's configuration file into the VM, so choose a place for the filesystem overlay (we recommand creating an empty directory for it). 698 | 699 | Also, we need network applications like `iw`, `hostapd` and `wpa_supplicant`. 700 | ``` 701 | System configuration ---> Run a getty (login prompt) after boot ---> TTY port ---> ttyS0 702 | System configuration ---> Root filesystem overlay directories ---> 703 | Kernel ---> Linux Kernel ---> no 704 | Target packages ---> Networking applications ---> hostapd 705 | Target packages ---> Networking applications ---> iw 706 | Target packages ---> Networking applications ---> wpa_supplicant 707 | Filesystem images ---> ext2/3/4 root filesystem 708 | ``` 709 | 710 | After finishing the configuration, we can put the needed kernel modules and configuration files into the filesystem overlay directory: 711 | ```shell 712 | cp /net/wireless/cfg80211.ko \ 713 | /vwifi.ko \ 714 | /scripts/hostapd.conf \ 715 | /scripts/wpa_supplicant.conf \ 716 | 717 | ``` 718 | 719 | Then start building rootfs: 720 | ```shell 721 | $ make 722 | ``` 723 | This may take a long time, please be patient. 724 | 725 | The output image will be `output/images/rootfs.ext2`. We need three images for three VMs, so we simply copy this file into three: 726 | ```shell 727 | $ cp output/images/rootfs.ext2 output/images/rootfs2.ext2 728 | $ cp output/images/rootfs.ext2 output/images/rootfs3.ext2 729 | ``` 730 | 731 | ### Setup Host Network Device 732 | We need three `tap` devices and each of them must be attached to a `bridge` device. Please note, creating `tap` and `bridge` devices needs privilege permission. 733 | 734 | Creating three `tap` devices: 735 | ```shell 736 | $ sudo ip tuntap add mode tap tap0 737 | $ sudo ip tuntap add mode tap tap1 738 | $ sudo ip tuntap add mode tap tap2 739 | ``` 740 | 741 | Creating `bridge` device: 742 | ```shell 743 | $ sudo ip link add name br0 type bridge 744 | ``` 745 | 746 | Attach three `tap` devices on `bridge` device: 747 | ```shell 748 | $ sudo ip link set tap0 master br0 749 | $ sudo ip link set tap1 master br0 750 | $ sudo ip link set tap2 master br0 751 | ``` 752 | 753 | ### Start VM with Qemu 754 | Once we have our kernel image and rootfs, we can start running `Qemu`: 755 | 756 | ```shell 757 | $ sudo qemu-system-x86_64 -kernel bzImage \ 758 | -drive file= -nographic \ 759 | -append "console=ttyS0" \ 760 | -append root=/dev/sda \ 761 | -netdev tap,id=,ifname= \ 762 | -device virtio-net-pci,netdev=,mac=,mrg_rxbuf=off \ 763 | -serial mon:stdio 764 | ``` 765 | 766 | You need to run the command above three times, please ensure the `buildroot` rootfs image, `tap` device and MAC address in every VM must be different. Also, ensure that the `mrg_rxbuf=off` has been specified. 767 | 768 | ### Needed Steps in Every VM 769 | #### Raondom Number Generator 770 | `hostapd` and `wpa_supplicant` need the random number generator `/dev/random` for generating the random number used in a 4-way handshake. However, for some reason (which may be related to IRQ), accessing `/dev/random` may be not available or even not possible. And we found that `/dev/urandom` is always available, so we use a soft link to let the `/dev/random` link to `/dev/urandom`: 771 | ```shell 772 | mv /dev/random /dev/random.orig 773 | ln -s /dev/urandom /dev/random 774 | ``` 775 | #### Loading Kernel Modules 776 | `vwifi` depends on `cfg80211.ko`, so firstly we load the `cfg80211.ko`: 777 | ```shell 778 | insmod cfg80211.ko 779 | ``` 780 | 781 | Then we can load our `vwifi.ko`. Note that for now, we only allow single network interface in `vwifi` when it's running on virtio: 782 | ```shell 783 | insmod vwifi.ko station=1 784 | ``` 785 | 786 | #### Setting Network Interface 787 | Start the network interface: 788 | ```shell 789 | ip link set vw0 up 790 | ``` 791 | And assign an IP address. Note that, we should assign different IP addresses (but the same subnet) for every network interface in the three VMs: 792 | ```shell 793 | ip addr add dev vw0 794 | ``` 795 | ### Start `hostapd` and `wpa_supplicant` 796 | In our testing environment, the HostAP mode interface is in VM1, so running `hostapd` on VM1: 797 | ```shell 798 | hostapd -i vw0 -B hostapd.conf 799 | ``` 800 | And running `wpa_supplicant` on the other two VMs: 801 | ```shell 802 | wpa_supplicant -i vw0 -B -c wpa_supplicant.conf 803 | ``` 804 | 805 | For now, the two STA mode interfaces in VM1 and VM2 should have been connected to the AP mode interface in VM0. 806 | 807 | For validating the connection for the STA mode network interface, use the following command: 808 | ```shell 809 | iw dev vw0 link 810 | ``` 811 | ### Ping Test 812 | In VM1, we can ping the network interfaces in VM2 and VM3: 813 | ``` 814 | ping 815 | ``` 816 | 817 | ``` 818 | ping 819 | ``` 820 | Likewise, VM2 and VM3 can ping the other VMs as well. 821 | 822 | ### Optional: Monitoring Wireless Device 823 | 824 | If desired, you can use wireless device monitoring applications such as [wavemon](https://github.com/uoaerg/wavemon) to observe signal and noise levels, 825 | packet statistics, device configuration, and network parameters of `vwifi`. 826 | ```shell 827 | $ sudo apt install wavemon 828 | ``` 829 | 830 |

logo image

831 | 832 | ## License 833 | 834 | `vwifi` is released under the MIT license. Use of this source code is governed 835 | by a MIT-style license that can be found in the LICENSE file. 836 | 837 | ## Reference 838 | 839 | * [mac80211_hwsim](https://www.kernel.org/doc/html/latest/networking/mac80211_hwsim/mac80211_hwsim.html): software simulator of 802.11 radio(s) for mac80211 840 | * [Emulating WLAN in Linux - part I: the 802.11 stack](https://linuxembedded.fr/2020/05/emulating-wlan-in-linux-part-i-the-80211-stack) 841 | * [Emulating WLAN in Linux - part II: mac80211_hwsim](https://linuxembedded.fr/2021/01/emulating-wlan-in-linux-part-ii-mac80211hwsim) 842 | * [virt_wifi](https://github.com/torvalds/linux/blob/master/drivers/net/wireless/virtual/virt_wifi.c): a complete virtual wireless driver that can be used as a wrapper around Ethernet. 843 | * [vwifi](https://github.com/Raizo62/vwifi): simulate Wi-Fi (802.11) between Linux Virtual Machines. 844 | * [virtio-overview](https://www.redhat.com/en/blog/virtio-devices-and-drivers-overview-headjack-and-phone): an virtio overview. 845 | * [virtio: How data travels](https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels): explain the virtqueue and virtio ring. 846 | -------------------------------------------------------------------------------- /assets/ibss.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /assets/ibss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysprog21/vwifi/62a3dd860ca93a28a29d932d7c7f4865fd5bc286/assets/ibss.png -------------------------------------------------------------------------------- /assets/rssi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysprog21/vwifi/62a3dd860ca93a28a29d932d7c7f4865fd5bc286/assets/rssi.png -------------------------------------------------------------------------------- /assets/virtio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysprog21/vwifi/62a3dd860ca93a28a29d932d7c7f4865fd5bc286/assets/virtio.png -------------------------------------------------------------------------------- /assets/vwifi.drawio: -------------------------------------------------------------------------------- 1 | 5Zptb5s6FMc/TaTtxY2wjQ15maa727RV6tZtd31JwEnYCI6MuyT79LODCdgmfbgjYV1UqcIHP8D5neO/bTJAk+XmNY9WiyuW0GwAvWQzQJcDCEPkyf/KsC0NGILSMOdpUpoahpv0J9VG3W5+lya0MCoKxjKRrkxjzPKcxsKwRZyztVltxjJz1FU0p47hJo4y1/pfmoiFfi0Y1PY3NJ0vqpEBGZV3llFVWb9JsYgStm6Y0KsBmnDGRHm13ExopnxX+aVs9++Bu/sH4zQXj2nw8dvnb7S42ia3bPzhA/LfvRdf/tG9/IiyO/3C+mHFtvIAZ3d5QlUnYIAu1otU0JtVFKu7a4lc2hZimenbszTLJixjfNcWJZiGiS/theDsO23cCeEUESLv6AegXNDNwTcDe3/JOKNsSQXfyiq6ARxpF+sYg74ur2tiINC2RYNWqG2RDpL5vuvaj/JCu/IJboWOW3MqcjnKWFrzwnOcLF9emJ40PZaznFru1aYoS+e5LMbSWVTaL5QrUxnAY31jmSaJGqYVXQ3XU92zXOgUBGFV1g8JOgCFoA3Kd0GBFlDoWKCQA2p8/WQ4KtCD0dTz3ASYzSiJ4yNj6oKMj00ynptC5JRgsAOGrTM5GMkUiimXV3N19ULx8pZSdl7+5sxlx74NcgZ3IF30ZEpwRzMZAiYG1IIBwBYO5FgcyN8gEKE57aDe9SG4Xx/AmerDfu7fg0I960PogLr5NP4/AjEiAYqImwAUyBQI/nyBAOjhmemkAjFqEwjQIhA7YKdQiDCm7QoxDbGPvY44eCYHv3eFqPLxeUsEtvzav0YAd29miITr5fMQCWzPRL2LBHC3e2eqEphYAt63SgB3gydlAv71MoGRCeIPkAn/YZmgeTJWZ3YqwLOoKNLSTxEXrrnh8fvDmm5S8VXXVNe3qt4wJIEuX250w11h2yhcU57Kl1d5dllzoYlzYvgglYbXcYvTKxunWSTSH2b3bST0CNcslQPX52DQWhv4ZOhBs5eC3fGY6oawcVpo9YUIMjPZDgxJZU6F05EEFW0b1VaqQnH4me1x5DNboVb2WAfe3q2/EYvu6cKJYjGXD/51F4DE2xtuq+hUhTocd6Vts2QHZBXanhHaAb43sh8bxdIbu1h5xOxaxsI9FXGfaeH75lyIbbV5bE741jmzb6/9usqJMLRyAp8gJ9ytLhjK8tvJ1fUuPOLvVDx5TXP6Jaff0TcFbLGu5oyHVpnVHNy9gLo73nMG5NsbYRAM+0YE3a0wVIjGH8+RkJNCuGUNelo+7pb6jPk4GdQ/H3cj/VaRiZKE00KffABvqP6ew/FHR6BG0OCEAuByals/HW1XDd1d9UFOz+FbRkec9qcd1UfUoCWhTgvK3XQfBIXOBxS2P7OS8GigZLH+jU+5WK9/KIVe/QI= -------------------------------------------------------------------------------- /assets/vwifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysprog21/vwifi/62a3dd860ca93a28a29d932d7c7f4865fd5bc286/assets/vwifi.png -------------------------------------------------------------------------------- /assets/wavemon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysprog21/vwifi/62a3dd860ca93a28a29d932d7c7f4865fd5bc286/assets/wavemon.png -------------------------------------------------------------------------------- /scripts/common.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | function check_kmod() { 4 | local mod_name=$1 5 | lsmod | grep $mod_name > /dev/null 6 | if [ $? -eq 0 ]; then 7 | # Module found 8 | return 0 9 | fi 10 | return 1 11 | } 12 | 13 | function insert_kmod() { 14 | local mod_name=$1 15 | local param=$2 16 | local noko_name=$(echo $mod_name |sed s/.ko//) 17 | check_kmod $noko_name 18 | ret=$? 19 | if [ $ret -eq 0 ] ; then 20 | sudo rmmod $noko_name > /dev/null 21 | fi 22 | echo "Installing Module $mod_name" 23 | sudo insmod $mod_name $param 24 | return $(check_kmod $noko_name) 25 | } 26 | 27 | function probe_kmod() { 28 | local mod_name=$1 29 | check_kmod $mod_name 30 | ret=$? 31 | if [ $ret -eq 0 ] ; then 32 | return 0 33 | fi 34 | echo "Installing Module $mod_name" 35 | sudo modprobe $mod_name 36 | return $(check_kmod $mod_name) 37 | } 38 | 39 | function remove_kmod() { 40 | local mod_name=$1 41 | check_kmod $mod_name 42 | ret=$? 43 | if [ $ret -eq 1 ] ; then 44 | return 0 45 | fi 46 | echo "Removing Module $mod_name" 47 | sudo rmmod $mod_name > /dev/null 48 | return 0 49 | } 50 | 51 | function start_hostapd() { 52 | echo "Start Hostapd" 53 | which hostapd 54 | ret=$? 55 | if [ $ret -eq 1 ] ; then 56 | echo "Hostapd is not found" 57 | return 3 58 | fi 59 | sudo hostapd -B $1 > /dev/null 60 | return 0 61 | } 62 | 63 | function stop_hostapd() { 64 | echo "Stop Hostapd" 65 | sudo kill -9 $(pidof hostapd) > /dev/null 66 | return 0 67 | } 68 | 69 | function get_wiphy_name() { 70 | local interface_name=$1 71 | local wiphy_name=$(sudo iw dev $interface_name info | grep wiphy | awk '{print $2}') 72 | wiphy_name=$(sudo iw list | grep "wiphy index: $wiphy_name" -B 1 | grep Wiphy | awk '{print $2}') 73 | echo $wiphy_name 74 | } -------------------------------------------------------------------------------- /scripts/hostapd.conf: -------------------------------------------------------------------------------- 1 | interface=vw0 2 | driver=nl80211 3 | debug=1 4 | ctrl_interface=/var/run/hostapd 5 | ctrl_interface_group=0 6 | channel=6 7 | ssid=test 8 | wpa=2 9 | wpa_passphrase=12345678 10 | wpa_key_mgmt=WPA-PSK 11 | wpa_pairwise=CCMP 12 | 13 | # Beacon interval (kibi-us : 1.024 ms) 14 | beacon_int=100 15 | dtim_period=1 -------------------------------------------------------------------------------- /scripts/plot_rssi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | 6 | if __name__ == '__main__': 7 | input_file = 'rssi.txt' 8 | counts = 1000 9 | 10 | X = [i + 1 for i in range(counts)] 11 | Y = [] 12 | 13 | with open(input_file, 'r') as f: 14 | for i in range(counts): 15 | Y.append(int(f.readline())) 16 | 17 | fig = plt.figure() 18 | ax = fig.add_subplot(1, 1, 1) 19 | ax.set_title('RSSI of sta vw0') 20 | ax.set_xlabel('times get_station called') 21 | ax.set_ylabel('dBm') 22 | 23 | ax.plot(X, Y) 24 | 25 | plt.show() 26 | -------------------------------------------------------------------------------- /scripts/verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 4 | source $ROOT/scripts/common.sh 5 | 6 | final_ret=0 7 | 8 | probe_kmod cfg80211 9 | if [ $? -ne 0 ]; then 10 | final_ret=1 11 | fi 12 | 13 | insert_kmod vwifi.ko station=6 14 | if [ $? -ne 0 ]; then 15 | final_ret=2 16 | fi 17 | 18 | which hostapd > /dev/null 19 | if [ $? -ne 0 ]; then 20 | final_ret=3 21 | fi 22 | 23 | if [ $final_ret -eq 0 ]; then 24 | # to avoid device or resource busy error 25 | sleep 0.5 26 | 27 | # set transmit power (mBm) 28 | sudo iw dev vw0 set txpower auto 29 | sudo iw dev vw1 set txpower fixed 1200 30 | sudo iw dev vw2 set txpower fixed 1300 31 | sudo iw dev vw3 set txpower auto 32 | sudo iw dev vw4 set txpower auto 33 | sudo iw dev vw5 set txpower auto 34 | 35 | # get phy number of each interface 36 | sudo iw dev > device.log 37 | vw0_phy=$(get_wiphy_name vw0) 38 | vw1_phy=$(get_wiphy_name vw1) 39 | vw2_phy=$(get_wiphy_name vw2) 40 | vw3_phy=$(get_wiphy_name vw3) 41 | vw4_phy=$(get_wiphy_name vw4) 42 | vw5_phy=$(get_wiphy_name vw5) 43 | 44 | # create network namespaces for each phy (interface) 45 | sudo ip netns add ns0 46 | sudo ip netns add ns1 47 | sudo ip netns add ns2 48 | sudo ip netns add ns3 49 | sudo ip netns add ns4 50 | sudo ip netns add ns5 51 | 52 | # add each phy (interface) to separate network namesapces 53 | sudo iw phy $vw0_phy set netns name ns0 54 | sudo iw phy $vw1_phy set netns name ns1 55 | sudo iw phy $vw2_phy set netns name ns2 56 | sudo iw phy $vw3_phy set netns name ns3 57 | sudo iw phy $vw4_phy set netns name ns4 58 | sudo iw phy $vw5_phy set netns name ns5 59 | 60 | # running hostapd on vw0, so vw0 becomes AP 61 | sudo ip netns exec ns0 ip link set vw0 up 62 | sudo ip netns exec ns0 ip link set lo up 63 | sudo ip netns exec ns0 hostapd -B scripts/hostapd.conf 64 | 65 | sudo ip netns exec ns1 ip link set vw1 up 66 | sudo ip netns exec ns1 ip link set lo up 67 | 68 | sudo ip netns exec ns2 ip link set vw2 up 69 | sudo ip netns exec ns2 ip link set lo up 70 | 71 | sudo ip netns exec ns3 ip link set vw3 up 72 | sudo ip netns exec ns3 ip link set lo up 73 | 74 | sudo ip netns exec ns4 ip link set vw4 up 75 | sudo ip netns exec ns4 ip link set lo up 76 | 77 | sudo ip netns exec ns5 ip link set vw5 up 78 | sudo ip netns exec ns5 ip link set lo up 79 | 80 | # assing IP address to each interface 81 | sudo ip netns exec ns0 ip addr add 10.0.0.1/24 dev vw0 82 | sudo ip netns exec ns1 ip addr add 10.0.0.2/24 dev vw1 83 | sudo ip netns exec ns2 ip addr add 10.0.0.3/24 dev vw2 84 | sudo ip netns exec ns3 ip addr add 10.0.0.4/24 dev vw3 85 | sudo ip netns exec ns4 ip addr add 10.0.0.5/24 dev vw4 86 | sudo ip netns exec ns5 ip addr add 10.0.0.6/24 dev vw5 87 | 88 | # ping test: STA vw1 <--> STA vw2, should fail, because they 89 | # haven't connected to AP 90 | echo 91 | echo "================================================================================" 92 | echo "Ping Test: STA vw1 (10.0.0.2) (not connected) <--> STA vw2 (10.0.0.3) (not connected)" 93 | echo 94 | echo "(should fail, because they haven't connnected to AP vw0 (10.0.0.1))" 95 | echo "(be patient, it will take some time to route...)" 96 | echo "================================================================================" 97 | sudo ip netns exec ns1 ping -c 1 10.0.0.3 98 | 99 | # STA vw1 performs scan and connect to TestAP 100 | sudo ip netns exec ns1 iw dev vw1 scan > scan_result.log 101 | cat scan_result.log | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'| tail -n 1 > scan_bssid.log 102 | sudo ip netns exec ns1 iw dev vw1 connect test 103 | sudo ip netns exec ns1 iw dev vw1 link | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}' > connected.log 104 | 105 | DIFF=$(diff connected.log scan_bssid.log) 106 | if [ "$DIFF" != "" ]; then 107 | final_ret=4 108 | fi 109 | 110 | echo 111 | echo "==================================" 112 | echo "vw1 connected to AP TestAP (vw0)" 113 | echo "==================================" 114 | 115 | # STA vw2 performs scan and connect to TestAP 116 | sudo ip netns exec ns2 iw dev vw2 scan > scan_result.log 117 | cat scan_result.log | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'| tail -n 1 > scan_bssid.log 118 | sudo ip netns exec ns2 iw dev vw2 connect test 119 | sudo ip netns exec ns2 iw dev vw2 link | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}' > connected.log 120 | 121 | DIFF=$(diff connected.log scan_bssid.log) 122 | if [ "$DIFF" != "" ]; then 123 | final_ret=5 124 | fi 125 | 126 | echo 127 | echo "==================================" 128 | echo "vw2 connected to AP TestAP (vw0)" 129 | echo "==================================" 130 | 131 | # ping test: STA vw1 (10.0.0.2) <--> STA vw2 (10.0.0.3), 132 | # should success, packet will be relayed by AP vw0 (10.0.0.1) 133 | echo 134 | echo "================================================================================" 135 | echo "Ping Test: STA vw1 (10.0.0.2) (connected) <--> STA vw2 (10.0.0.3) (connected)" 136 | echo 137 | echo "(should success, packet will be relay by AP vw0 (10.0.0.1))" 138 | echo "================================================================================" 139 | sudo ip netns exec ns1 ping -c 4 10.0.0.3 140 | 141 | # sudo ip netns exec ns1 ping -c 1 10.0.0.3 142 | ping_rc=$? 143 | if [ $ping_rc -ne 0 ]; then 144 | final_ret=6 145 | fi 146 | 147 | # ping test: STA vw2 (10.0.0.3) <--> AP vw0 (10.0.0.1), 148 | # should success, packet will directly send/receive between STA and AP 149 | echo 150 | echo "================================================================================" 151 | echo "Ping Test: STA vw1 (10.0.0.3) (connected) <--> AP vw0 (10.0.0.1)" 152 | echo 153 | echo "(should success, packet will directly send/receive between STA vw1 and AP vw0)" 154 | echo "================================================================================" 155 | sudo ip netns exec ns2 ping -c 4 10.0.0.1 156 | 157 | # sudo ip netns exec ns2 ping -c 4 10.0.0.1 158 | ping_rc=$? 159 | if [ $ping_rc -ne 0 ]; then 160 | final_ret=7 161 | fi 162 | 163 | # vw3 becomes an IBSS and then joins the "ibss1" network. 164 | echo 165 | echo "==============" 166 | echo "vw3 join ibss1" 167 | echo "==============" 168 | sudo ip netns exec ns3 wpa_supplicant -i vw3 -B -c scripts/wpa_supplicant_ibss.conf 169 | 170 | # vw4 becomes an IBSS and then joins the "ibss1" network. 171 | echo 172 | echo "==============" 173 | echo "vw4 join ibss1" 174 | echo "==============" 175 | sudo ip netns exec ns4 wpa_supplicant -i vw4 -B -c scripts/wpa_supplicant_ibss.conf 176 | 177 | # vw5 becomes an IBSS and then joins the "ibss2" network (BSSID: 00:76:77:35:00:00). 178 | echo 179 | echo "==================================" 180 | echo "vw5 join ibss2 (00:76:77:35:00:00)" 181 | echo "==================================" 182 | sudo ip netns exec ns5 iw dev vw5 set type ibss 183 | sudo ip netns exec ns5 iw dev vw5 ibss join ibss2 2412 NOHT fixed-freq 00:76:77:35:00:00 beacon-interval 300 184 | 185 | # ping test: IBSS vw3 <--> STA vw2, should fail 186 | echo 187 | echo "================================================================================" 188 | echo "Ping Test: IBSS vw3 (10.0.0.4) (in ibss1) <--> STA vw2 (10.0.0.3)" 189 | echo 190 | echo "(should fail)" 191 | echo "(be patient, it will take some time to route...)" 192 | echo "================================================================================" 193 | sudo ip netns exec ns3 ping -c 1 10.0.0.3 194 | 195 | # ping test: IBSS vw3 <--> IBSS vw5, should fail 196 | echo 197 | echo "================================================================================" 198 | echo "Ping Test: IBSS vw3 (10.0.0.4) (in ibss1) <--> IBSS vw5 (10.0.0.6) (in ibss2)" 199 | echo 200 | echo "(should fail)" 201 | echo "(be patient, it will take some time to route...)" 202 | echo "================================================================================" 203 | sudo ip netns exec ns3 ping -c 1 10.0.0.6 204 | 205 | # ping test: IBSS vw3 <--> IBSS vw4, should success 206 | echo 207 | echo "================================================================================" 208 | echo "Ping Test: IBSS vw3 (10.0.0.4) (in ibss1) <--> IBSS vw4 (10.0.0.5) (in ibss1)" 209 | echo 210 | echo "(should success)" 211 | echo "(be patient, it will take some time to route...)" 212 | echo "================================================================================" 213 | sudo ip netns exec ns3 ping -c 1 10.0.0.5 214 | 215 | # sudo ip netns exec ns3 ping -c 1 10.0.0.5 216 | ping_rc=$? 217 | if [ $ping_rc -ne 0 ]; then 218 | final_ret=8 219 | fi 220 | 221 | # verify TSF (in usec) 222 | sudo ip netns exec ns1 iw dev vw1 scan > scan_result.log 223 | tsf=$(cat scan_result.log | grep "TSF" | tail -n 1 | awk '{print $2}') 224 | uptime=$(cat /proc/uptime | awk '{print $1}') 225 | uptime=$(echo "$uptime*1000000" | bc | awk -F "." '{print $1}') 226 | diff=$((tsf - uptime)) 227 | 228 | # difference between tsf and uptime should less than 0.5 sec. 229 | if [ "${diff#-}" -gt 500000 ]; then 230 | final_ret=9 231 | fi 232 | 233 | # plot the distribution of RSSI of vw0 234 | echo -e "\n\n######## collecting RSSI information of vw0, please wait... ##########" 235 | vw0_mac=$(sudo ip netns exec ns0 iw dev | grep -E 'vw0$' -A 3 | grep addr | awk '{print $2}') 236 | counts=1000 # do get_station 1000 times 237 | 238 | for i in $(seq 1 1 $counts); do 239 | vw0_signal=$(sudo ip netns exec ns0 \ 240 | iw dev vw0 station get $vw0_mac | grep "signal" | awk '{print $2}') 241 | echo $vw0_signal >> rssi.txt 242 | done 243 | 244 | python3 $ROOT/scripts/plot_rssi.py 245 | plot_rc=$? 246 | if [ $plot_rc -ne 0 ]; then 247 | final_ret=10 248 | fi 249 | 250 | # TestAP performs station dump 251 | sudo ip netns exec ns0 iw dev vw0 station dump > station_dump_result.log 252 | for num in {1..2}; do 253 | cat station_dump_result.log | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'| sed -n "${num}p" > dump_ssid.log 254 | sudo ip netns exec "ns${num}" iw dev | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}' > station_ssid.log 255 | DIFF=$(diff dump_ssid.log station_ssid.log) 256 | if [ "$DIFF" != "" ]; then 257 | final_ret=11 258 | break 259 | fi 260 | done 261 | fi 262 | 263 | if [ $final_ret -eq 0 ]; then 264 | stop_hostapd 265 | remove_kmod vwifi 266 | sudo ip netns del ns0 267 | sudo ip netns del ns1 268 | sudo ip netns del ns2 269 | sudo ip netns del ns3 270 | sudo ip netns del ns4 271 | sudo ip netns del ns5 272 | rm scan_result.log scan_bssid.log connected.log device.log rssi.txt station_dump_result.log dump_ssid.log station_ssid.log 273 | echo "==== Test PASSED ====" 274 | exit 0 275 | fi 276 | 277 | echo "FAILED (code: $final_ret)" 278 | echo "==== Test FAILED ====" 279 | exit $final_ret 280 | -------------------------------------------------------------------------------- /scripts/wpa_supplicant.conf: -------------------------------------------------------------------------------- 1 | network={ 2 | ssid="test" 3 | psk="12345678" 4 | } -------------------------------------------------------------------------------- /scripts/wpa_supplicant_ibss.conf: -------------------------------------------------------------------------------- 1 | network={ 2 | ssid="ibss1" 3 | mode=1 4 | frequency=2412 5 | key_mgmt=WPA-PSK 6 | proto=RSN 7 | pairwise=CCMP 8 | group=CCMP 9 | psk="12345678" 10 | } -------------------------------------------------------------------------------- /vwifi-tool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAX_PAYLOAD 1024 11 | #define LINE_LENGTH 20 12 | #define MAX_DENYLIST_PAIR 5 13 | #define VWIFI_STATUS_FILE "/sys/module/vwifi/initstate" 14 | 15 | 16 | /* The function aims to check the status of vwifi kernel module */ 17 | bool vwifi_status_check() 18 | { 19 | FILE *fp = fopen(VWIFI_STATUS_FILE, "r"); 20 | if (!fp) { 21 | printf("vwifi status : not loaded\n"); 22 | return false; 23 | } 24 | 25 | char read_buf[LINE_LENGTH]; 26 | fgets(read_buf, LINE_LENGTH, fp); 27 | read_buf[strcspn(read_buf, "\n")] = 28 | 0; /* Remove newline character from string */ 29 | if (!strcmp("live", read_buf)) 30 | printf("vwifi status : live\n"); 31 | else { 32 | printf("vwifi status : %s\n", read_buf); 33 | return false; 34 | } 35 | return true; 36 | } 37 | 38 | /* Check if command line options are specified */ 39 | bool opt_set(int d, int s, int c) 40 | { 41 | return d || s || c; 42 | } 43 | 44 | /* Check whether the number of source interfaces matches with the number of 45 | * destination interfaces */ 46 | bool denylist_pair_check(int src_len, int dest_len) 47 | { 48 | return src_len == dest_len; 49 | } 50 | 51 | /* Copy destination and source interface pair into denylist buffer */ 52 | bool denylist_make(char *denylist, char *dest[], char *src[], int denylist_len) 53 | { 54 | for (int i = 0; i < denylist_len; i++) { 55 | char tmp[LINE_LENGTH] = {'\0'}; 56 | snprintf(tmp, LINE_LENGTH, "%s %s %s\n", dest[i], "denys", src[i]); 57 | if (strlen(tmp) + strlen(denylist) < NLMSG_SPACE(MAX_PAYLOAD)) 58 | strcat(denylist, tmp); 59 | else { 60 | printf("Error: Denylist size exceeds the maximum size of buffer\n"); 61 | return false; 62 | } 63 | } 64 | return true; 65 | } 66 | 67 | /* Send denylist to kernel using netlink socket */ 68 | bool denylist_send(char *denylist) 69 | { 70 | int sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK); 71 | if (sock_fd < 0) { 72 | printf("Error: Can't open socket\n"); 73 | return false; 74 | } 75 | 76 | struct sockaddr_nl src_addr = { 77 | .nl_family = AF_NETLINK, 78 | .nl_pid = getpid(), 79 | }; 80 | 81 | bind(sock_fd, (struct sockaddr *) &src_addr, sizeof(src_addr)); 82 | 83 | struct sockaddr_nl dest_addr = { 84 | .nl_family = AF_NETLINK, 85 | .nl_pid = 0, 86 | .nl_groups = 0, 87 | }; 88 | 89 | struct nlmsghdr *nlh = 90 | (struct nlmsghdr *) calloc(1, NLMSG_SPACE(MAX_PAYLOAD)); 91 | nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); 92 | nlh->nlmsg_pid = getpid(); 93 | nlh->nlmsg_flags = 0; 94 | 95 | strncpy(NLMSG_DATA(nlh), denylist, MAX_PAYLOAD); 96 | 97 | struct iovec iov = { 98 | .iov_base = (void *) nlh, 99 | .iov_len = nlh->nlmsg_len, 100 | }; 101 | 102 | struct msghdr msg = { 103 | .msg_name = (void *) &dest_addr, 104 | .msg_namelen = sizeof(dest_addr), 105 | .msg_iov = &iov, 106 | .msg_iovlen = 1, 107 | }; 108 | 109 | printf("Configuring denylist for vwifi...\n"); 110 | sendmsg(sock_fd, &msg, 0); 111 | 112 | recvmsg(sock_fd, &msg, 0); 113 | printf("Message from vwifi: %s\n", (char *) NLMSG_DATA(nlh)); 114 | 115 | close(sock_fd); 116 | 117 | return true; 118 | } 119 | 120 | int main(int argc, char *argv[]) 121 | { 122 | /* Get opt arguments from command line to configure denylist */ 123 | char *dest[MAX_DENYLIST_PAIR], *src[MAX_DENYLIST_PAIR], 124 | denylist_pair[MAX_DENYLIST_PAIR][LINE_LENGTH]; 125 | int denylist_len = 0, dest_len = 0, src_len = 0, clear = 0; 126 | int c; 127 | 128 | while ((c = getopt(argc, argv, "d:s:ch")) != -1) { 129 | switch (c) { 130 | case 'd': 131 | dest[dest_len++] = optarg; 132 | break; 133 | case 's': 134 | src[src_len++] = optarg; 135 | break; 136 | case 'c': 137 | clear = 1; 138 | break; 139 | case 'h': 140 | printf( 141 | "vwifi-tool: A userspace tool which supports more " 142 | "user-specific utilization for vwifi\n\n"); 143 | printf("Usage:\n\n"); 144 | printf("\tvwifi-tool [arguments]\n\n"); 145 | printf("The arguments are:\n\n"); 146 | printf("\t-d Destination interface name\n"); 147 | printf("\t-s Source interface name\n"); 148 | printf("\t-c Clear denylist\n"); 149 | return 0; 150 | default: 151 | printf("Invalid arguments\n"); 152 | break; 153 | } 154 | } 155 | 156 | if (!vwifi_status_check()) 157 | exit(1); 158 | 159 | /* When no options are specified, simply display the status of vwifi */ 160 | if (!opt_set(dest_len, src_len, clear)) 161 | return 0; 162 | 163 | if (!clear && !denylist_pair_check(src_len, dest_len)) { 164 | printf("Destination number doesn't match with Source number\n"); 165 | exit(1); 166 | } 167 | 168 | denylist_len = 169 | clear ? 0 170 | : (dest_len < MAX_DENYLIST_PAIR ? dest_len : MAX_DENYLIST_PAIR); 171 | 172 | /* Copy denylist pair into message buffer */ 173 | char buffer[NLMSG_SPACE(MAX_PAYLOAD)]; 174 | memset(buffer, '\0', sizeof(buffer)); 175 | 176 | if (!denylist_make(buffer, dest, src, denylist_len)) 177 | exit(1); 178 | 179 | if (!clear) 180 | printf("denylist:\n%s", buffer); 181 | 182 | /* Send denylist buffer to kernel */ 183 | if (!denylist_send(buffer)) 184 | exit(1); 185 | 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /vwifi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | MODULE_LICENSE("Dual MIT/GPL"); 21 | MODULE_AUTHOR("National Cheng Kung University, Taiwan"); 22 | MODULE_DESCRIPTION("virtual cfg80211 driver"); 23 | 24 | #define NAME_PREFIX "vw" 25 | #define NDEV_NAME NAME_PREFIX "%d" 26 | 27 | #define VWIFI_WIPHY_NAME_LEN 12 28 | #define VWIFI_WIPHY_PREFIX "vw_phy" 29 | 30 | #define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ 31 | #define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ 32 | 33 | #define MAX_PROBED_SSIDS 69 34 | #define IE_MAX_LEN 512 35 | 36 | #define SCAN_TIMEOUT_MS 100 /*< millisecond */ 37 | 38 | /* Note: vwifi_cipher_suites is an array of int defining which cipher suites 39 | * are supported. A pointer to this array and the number of entries is passed 40 | * on to upper layers. 41 | */ 42 | static const u32 vwifi_cipher_suites[] = { 43 | WLAN_CIPHER_SUITE_WEP40, 44 | WLAN_CIPHER_SUITE_WEP104, 45 | WLAN_CIPHER_SUITE_TKIP, 46 | WLAN_CIPHER_SUITE_CCMP, 47 | }; 48 | 49 | struct vwifi_packet { 50 | int datalen; 51 | u8 data[ETH_DATA_LEN]; 52 | struct list_head list; 53 | }; 54 | 55 | enum vwifi_state { VWIFI_READY, VWIFI_SHUTDOWN }; 56 | 57 | /* Context for the whole program, so there's only single vwifi_context 58 | * regardless of the number of virtual interfaces. Fields in the structure 59 | * are interface-independent. 60 | */ 61 | struct vwifi_context { 62 | /* We may not need this lock because vif_list would not change during 63 | * the whole lifetime. 64 | */ 65 | struct mutex lock; 66 | enum vwifi_state state; /**< indicate the program state */ 67 | struct list_head vif_list; /**< maintaining all interfaces */ 68 | struct list_head ap_list; /**< maintaining multiple AP */ 69 | struct list_head ibss_list; /**< maintaining all ibss devices */ 70 | char *denylist; /**< maintaining the denylist */ 71 | }; 72 | 73 | static DEFINE_SPINLOCK(vif_list_lock); 74 | 75 | /* SME stands for "station management entity" */ 76 | enum sme_state { SME_DISCONNECTED, SME_CONNECTING, SME_CONNECTED }; 77 | 78 | /* Each virtual interface contains a wiphy, vwifi_wiphy_counter is responsible 79 | * for recording the number of wiphy in vwifi. 80 | */ 81 | static atomic_t vwifi_wiphy_counter = ATOMIC_INIT(0); 82 | 83 | /* Virtual interface pointed to by netdev_priv(). Fields in the structure are 84 | * interface-dependent. Every interface has its own vwifi_vif, regardless of the 85 | * interface mode (STA, AP, IBSS...). 86 | */ 87 | struct vwifi_vif { 88 | struct wireless_dev wdev; 89 | struct net_device *ndev; 90 | struct net_device_stats stats; 91 | 92 | size_t ssid_len; 93 | /* Currently connected BSS id */ 94 | u8 bssid[ETH_ALEN]; 95 | u8 ssid[IEEE80211_MAX_SSID_LEN]; 96 | 97 | struct list_head rx_queue; /**< Head of received packet queue */ 98 | /* Store all vwifi_vif which is in the same BSS (AP will be the head). */ 99 | struct list_head bss_list; 100 | /* List entry for maintaining all vwifi_vif, which can be accessed via 101 | * vwifi->vif_list. 102 | */ 103 | struct list_head list; 104 | 105 | struct mutex lock; 106 | 107 | /* Split logic for the interface mode */ 108 | union { 109 | /* Structure for STA mode */ 110 | struct { 111 | /* For the case the STA is going to roam to another BSS */ 112 | u8 req_ssid[IEEE80211_MAX_SSID_LEN]; 113 | 114 | struct cfg80211_scan_request *scan_request; 115 | enum sme_state sme_state; /* connection information */ 116 | /* last connection time to a AP (in jiffies) */ 117 | unsigned long conn_time; 118 | unsigned long active_time; /**< last tx/rx time (in jiffies) */ 119 | u16 disconnect_reason_code; 120 | 121 | struct timer_list scan_timeout; 122 | struct work_struct ws_connect, ws_disconnect; 123 | struct work_struct ws_scan, ws_scan_timeout; 124 | 125 | /* For quickly finding the AP */ 126 | struct vwifi_vif *ap; 127 | }; 128 | /* Structure for AP mode */ 129 | struct { 130 | bool ap_enabled; 131 | bool privacy; 132 | /* List node for storing AP (vwifi->ap_list is the head), 133 | * this field is for interface in AP mode. 134 | */ 135 | struct list_head ap_list; 136 | /* beacon interval in us */ 137 | u64 beacon_int; 138 | struct hrtimer beacon_timer; 139 | struct ieee80211_channel *channel; 140 | enum nl80211_chan_width bw; 141 | }; 142 | /* Structure for IBSS(ad hoc) mode */ 143 | struct { 144 | /* List node for storing ibss devices (vwifi->ibss_list is the 145 | * head), this field is for interface in IBSS mode. 146 | */ 147 | struct list_head ibss_list; 148 | /* defines the channel to use if no other IBSS to join can be found 149 | */ 150 | struct cfg80211_chan_def ibss_chandef; 151 | u16 ibss_beacon_int; 152 | /* bitmap of basic rates */ 153 | u32 ibss_basic_rates; 154 | /* The channel should be fixed -- do not search for IBSSs to join on 155 | * other channels. */ 156 | bool ibss_channel_fixed; 157 | /* This is a protected network, keys will be configured after 158 | * joining */ 159 | bool ibss_privacy; 160 | /* whether user space controls IEEE 802.1X port, i.e., 161 | * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is 162 | * required to assume that the port is unauthorized until authorized 163 | * by user space. Otherwise, port is marked authorized by default. 164 | */ 165 | bool ibss_control_port; 166 | /* TRUE if userspace expects to exchange control 167 | * port frames over NL80211 instead of the network interface. 168 | */ 169 | bool ibss_control_port_over_nl80211; 170 | /* whether user space controls DFS operation */ 171 | bool ibss_userspace_handles_dfs; 172 | /* per-band multicast rate index + 1 (0: disabled) */ 173 | int ibss_mcast_rate[NUM_NL80211_BANDS]; 174 | /* HT Capabilities over-rides. */ 175 | struct ieee80211_ht_cap ibss_ht_capa; 176 | /* The bits of ht_capa which are to be used. */ 177 | struct ieee80211_ht_cap ibss_ht_capa_mask; 178 | /* static WEP keys */ 179 | struct key_params *ibss_wep_keys; 180 | /* key index (0..3) of the default TX static WEP key */ 181 | int ibss_wep_tx_key; 182 | }; 183 | }; 184 | 185 | struct timer_list scan_complete; 186 | u8 req_bssid[ETH_ALEN]; 187 | u32 beacon_ie_len; 188 | u8 beacon_ie[IE_MAX_LEN]; 189 | 190 | /* Store all STAs in the same BSS, right now only used when virtio enabled 191 | */ 192 | DECLARE_HASHTABLE(bss_sta_table, 4); 193 | /* Don't share the vif->lock because updating bss_sta_table may take a long 194 | * time */ 195 | struct mutex bss_sta_table_lock; 196 | u32 bss_sta_table_entry_num; 197 | 198 | /* Packet virtio header size */ 199 | u8 vnet_hdr_len; 200 | 201 | /* Transmit power */ 202 | s32 tx_power; 203 | }; 204 | 205 | static int station = 2; 206 | module_param(station, int, 0444); 207 | MODULE_PARM_DESC(station, "Number of virtual interfaces running in STA mode."); 208 | 209 | /* Global context */ 210 | static struct vwifi_context *vwifi = NULL; 211 | 212 | /* Denylist content */ 213 | #define MAX_DENYLIST_SIZE 1024 214 | 215 | static struct sock *nl_sk = NULL; 216 | 217 | static int denylist_check(char *dest, char *source) 218 | { 219 | if (!vwifi->denylist || !*(vwifi->denylist)) 220 | return 0; 221 | 222 | char *user_input = 223 | kmalloc(sizeof(char) * (strlen(vwifi->denylist) + 1), GFP_KERNEL); 224 | strncpy(user_input, vwifi->denylist, strlen(vwifi->denylist)); 225 | 226 | char *token = strsep(&user_input, "\n"); 227 | while (token) { 228 | char *denylist_dest = strsep(&token, " "); 229 | strsep(&token, " "); 230 | char *denylist_source = token; 231 | if (!strcmp(dest, denylist_dest) && !strcmp(source, denylist_source)) { 232 | kfree(user_input); 233 | return 1; 234 | } 235 | token = strsep(&user_input, "\n"); 236 | } 237 | kfree(user_input); 238 | 239 | return 0; 240 | } 241 | 242 | static void denylist_load(char *dlist) 243 | { 244 | if (!vwifi->denylist) { 245 | pr_info("vwifi->denylist have to be kmalloc first\n"); 246 | return; 247 | } 248 | memset(vwifi->denylist, '\0', MAX_DENYLIST_SIZE); /* clear the denylist */ 249 | strncpy(vwifi->denylist, dlist, strlen(dlist)); 250 | } 251 | 252 | static void denylist_nl_recv(struct sk_buff *skb) 253 | { 254 | struct nlmsghdr *nlh; /* netlink message header */ 255 | int pid; 256 | struct sk_buff *skb_out; 257 | char *msg = "vwifi has received your denylist"; 258 | int msg_size = strlen(msg); 259 | 260 | nlh = (struct nlmsghdr *) skb->data; 261 | 262 | denylist_load((char *) nlmsg_data(nlh)); 263 | 264 | /* pid of sending process */ 265 | pid = nlh->nlmsg_pid; 266 | 267 | skb_out = nlmsg_new(msg_size, 0); 268 | if (!skb_out) { 269 | pr_info("netlink: Failed to allocate new skb\n"); 270 | return; 271 | } 272 | 273 | nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); 274 | NETLINK_CB(skb_out).dst_group = 0; /* unicast group */ 275 | strncpy(nlmsg_data(nlh), msg, msg_size); 276 | 277 | if (nlmsg_unicast(nl_sk, skb_out, pid) < 0) 278 | pr_info("netlink: Error while sending back to user\n"); 279 | } 280 | 281 | static struct netlink_kernel_cfg nl_config = { 282 | .input = denylist_nl_recv, 283 | }; 284 | 285 | /** 286 | * enum virtio_vqs - queues for virtio frame transmission and receivement 287 | * 288 | * For virtio-net device, We expect 1 RX virtqueue followed by 1 TX virtqueue, 289 | * followed by possible N-1 RX/TX queue pairs used in multiqueue mode, followed 290 | * by possible control vq. For now, we don't support multiqueue mode virtio-net 291 | * device and control vq as well, so there are only 1 RX vq and 1 TX vq. 292 | * 293 | * @VWIFI_VQ_TX: send frames to external entity 294 | * @VWIFI_VQ_RX: receive frames 295 | * @VWIFI_NUM_VQS: enum limit 296 | */ 297 | enum { 298 | VWIFI_VQ_RX, 299 | VWIFI_VQ_TX, 300 | VWIFI_NUM_VQS, 301 | }; 302 | 303 | static struct virtqueue *vwifi_vqs[VWIFI_NUM_VQS]; 304 | static bool vwifi_virtio_enabled; 305 | 306 | static DEFINE_SPINLOCK(vwifi_virtio_lock); 307 | 308 | static void vwifi_virtio_rx_work(struct work_struct *work); 309 | static DECLARE_WORK(vwifi_virtio_rx, vwifi_virtio_rx_work); 310 | 311 | /** 312 | * enum VWIFI_VIRTIO_PACKET_TYPE - non-standard management frame type for VWIFI 313 | * 314 | * Most of these types are inspired by IEEE 802.11 management frame, with 315 | * little modifications since we are sending Ethernet frames. And note that 316 | * we send these management frame with the Ethertype/length field being 317 | * length (i.e. 802.3 frames), so we can distinguish it from a data frame 318 | * (Ethernet II). 319 | * 320 | * See struct vwifi_virtio_header for the VWIFI management frame structures. 321 | * For future modifications, please ensure that the VWIFI management frame 322 | * contains the needed informations consumed by cfg80211/nl80211. 323 | * 324 | * For the reason why we have the VWIFI_STA_ENTRY_REQUEST and 325 | * VWIFI_STA_ENTRY_RESPONSE: There are three to four addresses in a normal 326 | * 802.11 data frame, an STA can recognize whether the frame is for our BSS or 327 | * not by looking at a specific address (ToDS/FromDS in the frame control 328 | * determines the address layout). However, we get Ethernet frames from network 329 | * stack (cfg80211 doesn't implement the 802.11 data TX path), and we don't 330 | * convert it to IEEE 802.11 frame since we pretend ourself (vwifi) to be a 331 | * virtio-net driver and would like to pass Ethernet frame to virtio-net device. 332 | * So the VWIFI_STA_ENTRY_REQUEST and VWIFI_STA_ENTRY_RESPONSE comes to the 333 | * rescue. After an STA has connected to an an AP, the STA will request the AP 334 | * about the informations (currently only MAC address) of other STAs in the same 335 | * BSS, and then store them in an STA entry table. Whenever an STA receives a 336 | * data frame, it checks whether the source address in the Ethernet header is in 337 | * its STA entry table, and only if the condition is true, the STA pass the 338 | * frame into network stack. 339 | * 340 | * @VWIFI_SCAN_REQUEST: active scan, request AP to reveal its informations. 341 | * @VWIFI_SCAN_RESPONSE: AP informs its informations to STA. 342 | * @VWIFI_CONNECT_REQUEST: request a connection to an AP. 343 | * @VWIFI_CONNECT_RESPONSE: inform the STA about the success of the connection, 344 | * and AP will call cfg80211_add_sta() to inform 345 | * hostapd. If the AP runs with WPA/WPA2 (STA knows it since the beacon_ies 346 | * contains WPA/RSN IE), hostapd will then fire the 4-way handshake to change 347 | * keys with the STA. Only until the 4-way handshake is done (we learn it from 348 | * cfg80211->change_station()), the STA can start to exchange packets with other 349 | * STAs in the same BSS. 350 | * @VWIFI_DISCONNECT: inform the disconnection. This type can be sent by STA or 351 | * AP. 352 | * @VWIFI_STA_ENTRY_REQUEST: STA requests the connected AP for the STA entries 353 | * in the same BSS. 354 | * @VWIFI_STA_ENTRY_RESPONSE: There are two case: 355 | * 1. AP reply the STA's request about the STA 356 | * entries and the STA entries include all the STAs in the BSS. 357 | * 2. An unsolicited VWIFI_STA_ENTRY_RESPONSE will be 358 | * broadcasted by AP when an STA is connected or disconnected, so other STAs in 359 | * the same BSS can update their STA entry table. 360 | */ 361 | enum VWIFI_VIRTIO_PACKET_TYPE { 362 | VWIFI_SCAN_REQUEST, 363 | VWIFI_SCAN_RESPONSE, 364 | VWIFI_CONNECT_REQUEST, 365 | VWIFI_CONNECT_RESPONSE, 366 | VWIFI_DISCONNECT, 367 | VWIFI_STA_ENTRY_REQUEST, 368 | VWIFI_STA_ENTRY_RESPONSE, 369 | }; 370 | 371 | struct vwifi_virtio_header { 372 | __le16 type; 373 | #define VWIFI_VIRTIO_HEADER_TYPE_BYTE 2 374 | union { 375 | struct vwifi_virtio_scan_req { 376 | __le32 ssid_len; 377 | u8 ssid[IEEE80211_MAX_SSID_LEN]; 378 | } __packed scan_req; 379 | struct vwifi_virtio_scan_resp { 380 | u8 bssid[ETH_ALEN]; 381 | __le64 timestamp; 382 | __le16 beacon_int; 383 | __le16 capab_info; 384 | __le32 ssid_len; 385 | u8 ssid[IEEE80211_MAX_SSID_LEN]; 386 | __le32 channel; /* center frquency */ 387 | __le32 beacon_ies_len; 388 | u8 beacon_ies[]; 389 | } __packed scan_resp; 390 | struct vwifi_virtio_conn_req { 391 | u8 bssid[ETH_ALEN]; 392 | __le32 ssid_len; 393 | u8 ssid[IEEE80211_MAX_SSID_LEN]; 394 | } __packed connect_req; 395 | struct vwifi_virtio_conn_resp { 396 | __le16 status_code; 397 | __le16 capab_info; 398 | } __packed connect_resp; 399 | struct vwifi_virtio_disconn { 400 | u8 bssid[ETH_ALEN]; 401 | __le16 reason_code; 402 | } __packed disconn; 403 | struct vwifi_virtio_sta_entry_resp { 404 | u8 bssid[ETH_ALEN]; 405 | __le16 cmd; 406 | __le32 count; 407 | u8 macs[ETH_ALEN]; 408 | } __packed sta_entry_resp; 409 | } u; 410 | } __packed; 411 | 412 | enum VWIFI_STA_ENTRY_CMD { 413 | VWIFI_STA_ENTRY_ADD, 414 | VWIFI_STA_ENTRY_ADD_ALL, 415 | VWIFI_STA_ENTRY_DEL, 416 | }; 417 | 418 | struct bss_sta_entry { 419 | struct hlist_node node; 420 | u8 mac[ETH_ALEN]; 421 | }; 422 | 423 | /* helper function to retrieve vif from net_device */ 424 | static inline struct vwifi_vif *ndev_get_vwifi_vif(struct net_device *ndev) 425 | { 426 | return (struct vwifi_vif *) netdev_priv(ndev); 427 | } 428 | 429 | /* helper function to retrieve vif from wireless_dev */ 430 | static inline struct vwifi_vif *wdev_get_vwifi_vif(struct wireless_dev *wdev) 431 | { 432 | return container_of(wdev, struct vwifi_vif, wdev); 433 | } 434 | 435 | /* helper function to retrieve vif from wiphy */ 436 | static struct vwifi_vif *wiphy_get_vwifi_vif(struct wiphy *wiphy) 437 | { 438 | struct wireless_dev *wdev; 439 | struct vwifi_vif *vif = NULL; 440 | 441 | list_for_each_entry (wdev, &wiphy->wdev_list, list) { 442 | vif = container_of(wdev, struct vwifi_vif, wdev); 443 | break; /* Assuming only one virtual interface is present */ 444 | } 445 | 446 | return vif; 447 | } 448 | 449 | static inline u32 vwifi_mac_to_32(const u8 *mac) 450 | { 451 | u32 h = 3323198485U; 452 | for (int i = 0; i < ETH_ALEN; i++) { 453 | h ^= *(mac + i); 454 | h *= 0x5bd1e995; 455 | h ^= h >> 15; 456 | } 457 | return h; 458 | } 459 | 460 | #define SIN_S3_MIN (-(1 << 12)) 461 | #define SIN_S3_MAX (1 << 12) 462 | 463 | /* A sine approximation via a third-order approx. 464 | * Refer to https://www.coranac.com/2009/07/sines for details about the 465 | * algorithm. Some parameters have been adjusted to increase the frequency 466 | * of the sine function. 467 | * Note: __sin_s3() is intended for internal use by rand_int_smooth() and 468 | * should not be called elsewhere. 469 | * 470 | * @x: seed to generate third-order sine value 471 | * @return: signed 32-bit integer ranging from SIN_S3_MIN to SIN_S3_MAX 472 | */ 473 | static inline s32 __sin_s3(s32 x) 474 | { 475 | /* S(x) = (x * (3 * 2^p - (x * x)/2^r)) / 2^s 476 | * @n: the angle scale 477 | * @A: the amplitude 478 | * @p: keep the multiplication from overflowing 479 | */ 480 | const int32_t n = 6, A = 12, p = 10, r = 2 * n - p, s = n + p + 1 - A; 481 | 482 | x = x << (30 - n); 483 | 484 | if ((x ^ (x << 1)) < 0) 485 | x = (1 << 31) - x; 486 | 487 | x = x >> (30 - n); 488 | return (x * ((3 << p) - ((x * x) >> r))) >> s; 489 | } 490 | 491 | /* Generate a signed 32-bit integer by feeding the seed into __sin_s3(). 492 | * The distribution of (seed, rand_int_smooth()) is closer to a sine function 493 | * when plotted. 494 | */ 495 | static inline s32 rand_int_smooth(s32 low, s32 up, s32 seed) 496 | { 497 | s32 result = __sin_s3(seed) - SIN_S3_MIN; 498 | result = (result * (up - low)) / (SIN_S3_MAX - SIN_S3_MIN); 499 | result += low; 500 | return result; 501 | } 502 | 503 | /* Helper function that prepares a structure with self-defined BSS information 504 | * and "informs" the kernel about the "new" BSS. Most of the code is copied from 505 | * the upcoming inform_dummy_bss function. 506 | */ 507 | static void inform_bss(struct vwifi_vif *vif) 508 | { 509 | struct vwifi_vif *ap; 510 | 511 | list_for_each_entry (ap, &vwifi->ap_list, ap_list) { 512 | struct cfg80211_bss *bss = NULL; 513 | struct cfg80211_inform_bss data = { 514 | /* the only channel */ 515 | .chan = &ap->wdev.wiphy->bands[NL80211_BAND_2GHZ]->channels[0], 516 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) 517 | .scan_width = NL80211_BSS_CHAN_WIDTH_20, 518 | #endif 519 | .signal = DBM_TO_MBM(rand_int_smooth(-100, -30, jiffies)), 520 | }; 521 | int capability = WLAN_CAPABILITY_ESS; 522 | 523 | if (ap->privacy) 524 | capability |= WLAN_CAPABILITY_PRIVACY; 525 | 526 | pr_info("vwifi: %s performs scan, found %s (SSID: %s, BSSID: %pM)\n", 527 | vif->ndev->name, ap->ndev->name, ap->ssid, ap->bssid); 528 | pr_info("cap = %d, beacon_ie_len = %d\n", capability, 529 | ap->beacon_ie_len); 530 | 531 | /* Using the CLOCK_BOOTTIME clock, which remains unaffected by changes 532 | * in the system time-of-day clock and includes any time that the 533 | * system is suspended. 534 | * This clock is suitable for synchronizing the machines in the BSS 535 | * using tsf. 536 | */ 537 | u64 tsf = div_u64(ktime_get_boottime_ns(), 1000); 538 | 539 | /* It is possible to use cfg80211_inform_bss() instead. */ 540 | bss = cfg80211_inform_bss_data( 541 | vif->wdev.wiphy, &data, CFG80211_BSS_FTYPE_UNKNOWN, ap->bssid, tsf, 542 | capability, 100, ap->beacon_ie, ap->beacon_ie_len, GFP_KERNEL); 543 | 544 | /* cfg80211_inform_bss_data() returns cfg80211_bss structure reference 545 | * counter of which should be decremented if it is unused. 546 | */ 547 | cfg80211_put_bss(vif->wdev.wiphy, bss); 548 | } 549 | } 550 | 551 | /* Helper function that prepares a structure with self-defined BSS information 552 | * and "informs" the kernel about the "new" Independent BSS. 553 | */ 554 | static void ibss_inform_bss(struct vwifi_vif *vif) 555 | { 556 | struct vwifi_vif *ibss; 557 | 558 | list_for_each_entry (ibss, &vwifi->ibss_list, ibss_list) { 559 | struct cfg80211_bss *bss = NULL; 560 | struct cfg80211_inform_bss data = { 561 | /* the only channel */ 562 | .chan = &ibss->wdev.wiphy->bands[NL80211_BAND_2GHZ]->channels[0], 563 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) 564 | .scan_width = NL80211_BSS_CHAN_WIDTH_20, 565 | #endif 566 | .signal = DBM_TO_MBM(rand_int_smooth(-100, -30, jiffies)), 567 | }; 568 | int capability = WLAN_CAPABILITY_IBSS; 569 | 570 | if (ibss->ibss_privacy) 571 | capability |= WLAN_CAPABILITY_PRIVACY; 572 | 573 | pr_info("vwifi: %s performs scan, found %s (SSID: %s, BSSID: %pM)\n", 574 | vif->ndev->name, ibss->ndev->name, ibss->ssid, ibss->bssid); 575 | pr_info("cap = %d, beacon_ie_len = %d\n", capability, 576 | ibss->beacon_ie_len); 577 | 578 | /* Using the CLOCK_BOOTTIME clock, which remains unaffected by changes 579 | * in the system time-of-day clock and includes any time that the 580 | * system is suspended. 581 | * This clock is suitable for synchronizing the machines in the BSS 582 | * using tsf. 583 | */ 584 | u64 tsf = div_u64(ktime_get_boottime_ns(), 1000); 585 | 586 | /* It is possible to use cfg80211_inform_bss() instead. */ 587 | bss = cfg80211_inform_bss_data( 588 | vif->wdev.wiphy, &data, CFG80211_BSS_FTYPE_UNKNOWN, ibss->bssid, 589 | tsf, capability, ibss->ibss_beacon_int, ibss->beacon_ie, 590 | ibss->beacon_ie_len, GFP_KERNEL); 591 | 592 | /* cfg80211_inform_bss_data() returns cfg80211_bss structure reference 593 | * counter of which should be decremented if it is unused. 594 | */ 595 | cfg80211_put_bss(vif->wdev.wiphy, bss); 596 | } 597 | } 598 | 599 | static void vwifi_beacon_inform_bss(struct vwifi_vif *ap, 600 | struct vwifi_vif *sta, 601 | struct cfg80211_inform_bss *bss_meta, 602 | int capability, 603 | u64 tsf) 604 | { 605 | struct cfg80211_bss *bss = NULL; 606 | bss_meta->signal = DBM_TO_MBM(rand_int_smooth(-100, -30, jiffies)); 607 | 608 | /* It is possible to use cfg80211_inform_bss() instead. */ 609 | bss = cfg80211_inform_bss_data(sta->wdev.wiphy, bss_meta, 610 | CFG80211_BSS_FTYPE_BEACON, ap->bssid, tsf, 611 | capability, ap->beacon_int, ap->beacon_ie, 612 | ap->beacon_ie_len, GFP_KERNEL); 613 | 614 | /* cfg80211_inform_bss_data() returns cfg80211_bss structure reference 615 | * counter of which should be decremented if it is unused. 616 | */ 617 | if (bss) 618 | cfg80211_put_bss(sta->wdev.wiphy, bss); 619 | } 620 | 621 | /* The callback function of the beacon timer prepares a structure with 622 | * custom BSS information and "notifies" the core about the "new" 623 | * BSS information. 624 | */ 625 | static enum hrtimer_restart vwifi_beacon(struct hrtimer *timer) 626 | { 627 | struct vwifi_vif *vif = container_of(timer, struct vwifi_vif, beacon_timer); 628 | 629 | if (vif->wdev.iftype != NL80211_IFTYPE_AP && 630 | vif->wdev.iftype != NL80211_IFTYPE_MESH_POINT && 631 | vif->wdev.iftype != NL80211_IFTYPE_ADHOC && 632 | vif->wdev.iftype != NL80211_IFTYPE_OCB) 633 | return HRTIMER_NORESTART; 634 | 635 | u64 timestamp = div_u64(ktime_get_boottime_ns(), 1000); 636 | 637 | struct cfg80211_inform_bss bss_meta = { 638 | .boottime_ns = ktime_get_boottime_ns(), 639 | .chan = vif->channel, 640 | }; 641 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) 642 | switch (vif->bw) { 643 | case NL80211_CHAN_WIDTH_5: 644 | bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5; 645 | break; 646 | case NL80211_CHAN_WIDTH_10: 647 | bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10; 648 | break; 649 | default: 650 | bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20; 651 | break; 652 | } 653 | #endif 654 | int capability = WLAN_CAPABILITY_ESS; 655 | 656 | if (vif->privacy) 657 | capability |= WLAN_CAPABILITY_PRIVACY; 658 | 659 | spin_lock(&vif_list_lock); 660 | struct vwifi_vif *sta; 661 | list_for_each_entry (sta, &vwifi->vif_list, list) { 662 | if (sta->wdev.iftype != NL80211_IFTYPE_STATION) 663 | continue; 664 | 665 | vwifi_beacon_inform_bss(vif, sta, &bss_meta, capability, timestamp); 666 | } 667 | spin_unlock(&vif_list_lock); 668 | 669 | /* beacon at next TBTT */ 670 | u64 tsf, until_tbtt; 671 | tsf = ktime_to_us(ktime_get_real()); 672 | u32 bcn_int = vif->beacon_int; 673 | until_tbtt = bcn_int - do_div(tsf, bcn_int); 674 | hrtimer_forward_now(&vif->beacon_timer, 675 | ns_to_ktime(until_tbtt * NSEC_PER_USEC)); 676 | 677 | return HRTIMER_RESTART; 678 | } 679 | 680 | static void vwifi_virtio_fill_vq(struct virtqueue *vq, u8 vnet_hdr_len); 681 | 682 | static int vwifi_ndo_open(struct net_device *dev) 683 | { 684 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 685 | 686 | netif_start_queue(dev); 687 | 688 | vwifi_virtio_fill_vq(vwifi_vqs[VWIFI_VQ_RX], vif->vnet_hdr_len); 689 | 690 | return 0; 691 | } 692 | 693 | static int vwifi_ndo_stop(struct net_device *dev) 694 | { 695 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 696 | struct vwifi_packet *pkt, *is = NULL; 697 | list_for_each_entry_safe (pkt, is, &vif->rx_queue, list) { 698 | list_del(&pkt->list); 699 | kfree(pkt); 700 | } 701 | netif_stop_queue(dev); 702 | return 0; 703 | } 704 | 705 | static struct net_device_stats *vwifi_ndo_get_stats(struct net_device *dev) 706 | { 707 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 708 | return &vif->stats; 709 | } 710 | 711 | static netdev_tx_t vwifi_ndo_start_xmit(struct sk_buff *skb, 712 | struct net_device *dev); 713 | 714 | /* Receive a packet: retrieve, encapsulate it in an skb, and perform the 715 | * following operations based on the interface mode: 716 | * - STA mode: Pass the skb to the upper level (protocol stack). 717 | * - AP mode: Perform the following operations based on the packet type: 718 | * 1. Unicast: If the skb is intended for another STA, pass it to that 719 | * STA and do not pass it to the protocol stack. If the skb is intended 720 | * for the AP itself, pass it to the protocol stack. 721 | * 2. Broadcast: Pass the skb to all other STAs except the source STA, and 722 | * then pass it to the protocol stack. 723 | * 3. Multicast: Perform the same operations as for broadcast. 724 | */ 725 | static void vwifi_rx(struct net_device *dev) 726 | { 727 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 728 | /* socket buffer will be sended to protocol stack */ 729 | struct sk_buff *skb; 730 | /* socket buffer will be transmitted to another STA */ 731 | struct sk_buff *skb1 = NULL; 732 | struct vwifi_packet *pkt; 733 | 734 | if (list_empty(&vif->rx_queue)) { 735 | pr_info("vwifi rx: No packet in rx_queue\n"); 736 | return; 737 | } 738 | 739 | if (mutex_lock_interruptible(&vif->lock)) 740 | goto pkt_free; 741 | 742 | pkt = list_first_entry(&vif->rx_queue, struct vwifi_packet, list); 743 | 744 | vif->stats.rx_packets++; 745 | vif->stats.rx_bytes += pkt->datalen; 746 | vif->active_time = jiffies; 747 | 748 | mutex_unlock(&vif->lock); 749 | 750 | /* Put raw packet into socket buffer */ 751 | skb = dev_alloc_skb(pkt->datalen + 2); 752 | if (!skb) { 753 | pr_info("vwifi rx: low on mem - packet dropped\n"); 754 | vif->stats.rx_dropped++; 755 | goto pkt_free; 756 | } 757 | skb_reserve(skb, 2); /* align IP address on 16B boundary */ 758 | memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen); 759 | 760 | list_del(&pkt->list); 761 | kfree(pkt); 762 | 763 | if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 764 | struct ethhdr *eth_hdr = (struct ethhdr *) skb->data; 765 | 766 | /* When receiving a multicast/broadcast packet, it is sent to every 767 | * STA except the source STA, and then passed to the protocol stack. 768 | */ 769 | if (is_multicast_ether_addr(eth_hdr->h_dest)) { 770 | pr_info("vwifi: is_multicast_ether_addr\n"); 771 | skb1 = skb_copy(skb, GFP_KERNEL); 772 | } 773 | /* Receiving a unicast packet */ 774 | else { 775 | /* The packet is not intended for the AP itself. Instead, it is 776 | * sent to the destination STA and not passed to the protocol stack. 777 | */ 778 | if (!ether_addr_equal(eth_hdr->h_dest, vif->ndev->dev_addr)) { 779 | skb1 = skb; 780 | skb = NULL; 781 | } 782 | } 783 | 784 | if (skb1) { 785 | pr_info("vwifi: AP %s relay:\n", vif->ndev->name); 786 | vwifi_ndo_start_xmit(skb1, vif->ndev); 787 | } 788 | 789 | /* Nothing to pass to protocol stack */ 790 | if (!skb) 791 | return; 792 | } 793 | 794 | /* Pass the skb to protocol stack */ 795 | skb->dev = dev; 796 | skb->protocol = eth_type_trans(skb, dev); 797 | skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ 798 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) 799 | netif_rx_ni(skb); 800 | #else 801 | netif_rx(skb); 802 | #endif 803 | 804 | return; 805 | 806 | pkt_free: 807 | list_del(&pkt->list); 808 | kfree(pkt); 809 | } 810 | 811 | static int __vwifi_ndo_start_xmit(struct vwifi_vif *vif, 812 | struct vwifi_vif *dest_vif, 813 | struct sk_buff *skb) 814 | { 815 | struct vwifi_packet *pkt = NULL; 816 | struct ethhdr *eth_hdr = (struct ethhdr *) skb->data; 817 | int datalen; 818 | 819 | if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 820 | pr_info("vwifi: STA %s (%pM) send packet to AP %s (%pM)\n", 821 | vif->ndev->name, eth_hdr->h_source, dest_vif->ndev->name, 822 | eth_hdr->h_dest); 823 | } else if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 824 | pr_info("vwifi: AP %s (%pM) send packet to STA %s (%pM)\n", 825 | vif->ndev->name, eth_hdr->h_source, dest_vif->ndev->name, 826 | eth_hdr->h_dest); 827 | } else if (vif->wdev.iftype == NL80211_IFTYPE_ADHOC) { 828 | pr_info("vwifi: IBSS %s (%pM) send packet to IBSS %s (%pM)\n", 829 | vif->ndev->name, eth_hdr->h_source, dest_vif->ndev->name, 830 | eth_hdr->h_dest); 831 | } 832 | 833 | pkt = kmalloc(sizeof(struct vwifi_packet), GFP_KERNEL); 834 | if (!pkt) { 835 | pr_info("Ran out of memory allocating packet pool\n"); 836 | return NETDEV_TX_OK; 837 | } 838 | datalen = skb->len; 839 | memcpy(pkt->data, skb->data, datalen); 840 | pkt->datalen = datalen; 841 | 842 | /* enqueue packet to destination vif's rx_queue */ 843 | if (mutex_lock_interruptible(&dest_vif->lock)) 844 | goto error_before_rx_queue; 845 | 846 | list_add_tail(&pkt->list, &dest_vif->rx_queue); 847 | 848 | mutex_unlock(&dest_vif->lock); 849 | 850 | if (mutex_lock_interruptible(&vif->lock)) 851 | goto erorr_after_rx_queue; 852 | 853 | /* Update interface statistics */ 854 | vif->stats.tx_packets++; 855 | vif->stats.tx_bytes += datalen; 856 | vif->active_time = jiffies; 857 | 858 | mutex_unlock(&vif->lock); 859 | 860 | if (dest_vif->wdev.iftype == NL80211_IFTYPE_STATION) { 861 | pr_info("vwifi: STA %s (%pM) receive packet from AP %s (%pM)\n", 862 | dest_vif->ndev->name, eth_hdr->h_dest, vif->ndev->name, 863 | eth_hdr->h_source); 864 | } else if (dest_vif->wdev.iftype == NL80211_IFTYPE_AP) { 865 | pr_info("vwifi: AP %s (%pM) receive packet from STA %s (%pM)\n", 866 | dest_vif->ndev->name, eth_hdr->h_dest, vif->ndev->name, 867 | eth_hdr->h_source); 868 | } else if (dest_vif->wdev.iftype == NL80211_IFTYPE_ADHOC) { 869 | pr_info("vwifi: IBSS %s (%pM) receive packet from IBSS %s (%pM)\n", 870 | dest_vif->ndev->name, eth_hdr->h_dest, vif->ndev->name, 871 | eth_hdr->h_source); 872 | } 873 | 874 | /* Directly send to rx_queue, simulate the rx interrupt */ 875 | vwifi_rx(dest_vif->ndev); 876 | 877 | return datalen; 878 | 879 | erorr_after_rx_queue: 880 | list_del(&pkt->list); 881 | error_before_rx_queue: 882 | kfree(pkt); 883 | return 0; 884 | } 885 | 886 | static netdev_tx_t vwifi_virtio_tx(struct vwifi_vif *vif, struct sk_buff *skb); 887 | 888 | /* Network packet transmit. 889 | * Callback called by the kernel when packets need to be sent. 890 | */ 891 | static netdev_tx_t vwifi_ndo_start_xmit(struct sk_buff *skb, 892 | struct net_device *dev) 893 | { 894 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 895 | struct vwifi_vif *dest_vif = NULL; 896 | struct ethhdr *eth_hdr = (struct ethhdr *) skb->data; 897 | unsigned long flags; 898 | int err; 899 | int count = 0; 900 | 901 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 902 | 903 | if (vwifi_virtio_enabled) { 904 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 905 | err = vwifi_virtio_tx(vif, skb); 906 | return err; 907 | } 908 | 909 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 910 | 911 | /* TX by interface of STA mode */ 912 | if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 913 | if (vif->ap && vif->ap->ap_enabled) { 914 | dest_vif = vif->ap; 915 | 916 | if (__vwifi_ndo_start_xmit(vif, dest_vif, skb)) 917 | count++; 918 | } 919 | } 920 | /* TX by interface of AP mode */ 921 | else if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 922 | /* Find the source interface */ 923 | struct vwifi_vif *src_vif; 924 | list_for_each_entry (src_vif, &vif->bss_list, bss_list) { 925 | if (ether_addr_equal(eth_hdr->h_source, src_vif->ndev->dev_addr)) 926 | break; 927 | } 928 | 929 | /* Check if the packet is broadcasting */ 930 | if (is_broadcast_ether_addr(eth_hdr->h_dest)) { 931 | list_for_each_entry (dest_vif, &vif->bss_list, bss_list) { 932 | /* Don't send broadcast packet back to the source interface. 933 | */ 934 | if (ether_addr_equal(eth_hdr->h_source, 935 | dest_vif->ndev->dev_addr)) 936 | continue; 937 | 938 | /* Don't send packet from dest_vif's denylist */ 939 | if (denylist_check(dest_vif->ndev->name, src_vif->ndev->name)) 940 | continue; 941 | 942 | if (__vwifi_ndo_start_xmit(vif, dest_vif, skb)) 943 | count++; 944 | } 945 | } 946 | /* The packet is unicasting */ 947 | else { 948 | list_for_each_entry (dest_vif, &vif->bss_list, bss_list) { 949 | if (ether_addr_equal(eth_hdr->h_dest, 950 | dest_vif->ndev->dev_addr)) { 951 | if (!denylist_check(dest_vif->ndev->name, 952 | src_vif->ndev->name) && 953 | __vwifi_ndo_start_xmit(vif, dest_vif, skb)) 954 | count++; 955 | break; 956 | } 957 | } 958 | } 959 | } 960 | /* TX by interface of IBSS(ad-hoc) mode */ 961 | else if (vif->wdev.iftype == NL80211_IFTYPE_ADHOC) { 962 | /* Check if the packet is broadcasting */ 963 | if (is_broadcast_ether_addr(eth_hdr->h_dest)) { 964 | list_for_each_entry (dest_vif, &vwifi->ibss_list, ibss_list) { 965 | /* Don't send broadcast packet back to the source interface. 966 | */ 967 | if (ether_addr_equal(eth_hdr->h_source, 968 | dest_vif->ndev->dev_addr)) 969 | continue; 970 | /* Don't send packet from dest_vif's denylist */ 971 | if (denylist_check(dest_vif->ndev->name, vif->ndev->name)) 972 | continue; 973 | /* Don't send packet to device with different SSID. */ 974 | if (strcmp(vif->ssid, dest_vif->ssid)) 975 | continue; 976 | /* Don't send packet to device with different BSSID. */ 977 | if (!ether_addr_equal(vif->bssid, dest_vif->bssid)) 978 | continue; 979 | if (__vwifi_ndo_start_xmit(vif, dest_vif, skb)) 980 | count++; 981 | } 982 | } 983 | /* The packet is unicasting */ 984 | else { 985 | list_for_each_entry (dest_vif, &vwifi->ibss_list, ibss_list) { 986 | if (ether_addr_equal(eth_hdr->h_dest, 987 | dest_vif->ndev->dev_addr)) { 988 | /* Don't send packet from dest_vif's denylist */ 989 | if (denylist_check(dest_vif->ndev->name, vif->ndev->name)) 990 | continue; 991 | /* Don't send packet to device with different SSID. */ 992 | if (strcmp(vif->ssid, dest_vif->ssid)) 993 | continue; 994 | /* Don't send packet to device with different BSSID. */ 995 | if (!ether_addr_equal(vif->bssid, dest_vif->bssid)) 996 | continue; 997 | if (__vwifi_ndo_start_xmit(vif, dest_vif, skb)) 998 | count++; 999 | } 1000 | } 1001 | } 1002 | } 1003 | 1004 | if (!count) 1005 | vif->stats.tx_dropped++; 1006 | 1007 | /* Don't forget to cleanup skb, as its ownership moved to xmit callback. */ 1008 | dev_kfree_skb(skb); 1009 | 1010 | return NETDEV_TX_OK; 1011 | } 1012 | 1013 | /* Structure of functions for network devices. 1014 | * It should have at least ndo_start_xmit functions called for packet to be 1015 | * sent. 1016 | */ 1017 | static struct net_device_ops vwifi_ndev_ops = { 1018 | .ndo_open = vwifi_ndo_open, 1019 | .ndo_stop = vwifi_ndo_stop, 1020 | .ndo_start_xmit = vwifi_ndo_start_xmit, 1021 | .ndo_get_stats = vwifi_ndo_get_stats, 1022 | }; 1023 | 1024 | /* Inform the "dummy" BSS to kernel and call cfg80211_scan_done() to finish 1025 | * scan. 1026 | */ 1027 | static void vwifi_scan_timeout_work(struct work_struct *w) 1028 | { 1029 | struct vwifi_vif *vif = container_of(w, struct vwifi_vif, ws_scan_timeout); 1030 | struct cfg80211_scan_info info = { 1031 | /* if scan was aborted by user (calling cfg80211_ops->abort_scan) or by 1032 | * any driver/hardware issue - field should be set to "true" 1033 | */ 1034 | .aborted = false, 1035 | }; 1036 | 1037 | /* inform with dummy BSS */ 1038 | inform_bss(vif); 1039 | ibss_inform_bss(vif); 1040 | 1041 | if (mutex_lock_interruptible(&vif->lock)) 1042 | return; 1043 | 1044 | /* finish scan */ 1045 | cfg80211_scan_done(vif->scan_request, &info); 1046 | 1047 | vif->scan_request = NULL; 1048 | 1049 | mutex_unlock(&vif->lock); 1050 | } 1051 | 1052 | /* Callback called when the scan timer timeouts. This function just schedules 1053 | * the timeout work and offloads the job of informing "dummy" BSS to kernel 1054 | * onto it. 1055 | */ 1056 | static void vwifi_scan_timeout(struct timer_list *t) 1057 | { 1058 | struct vwifi_vif *vif = container_of(t, struct vwifi_vif, scan_timeout); 1059 | 1060 | if (vif->scan_request) 1061 | schedule_work(&vif->ws_scan_timeout); 1062 | } 1063 | 1064 | static void vwifi_virtio_scan_request(struct vwifi_vif *vif); 1065 | 1066 | /* Scan routine. It simulates a fake BSS scan (in fact, it does nothing) and 1067 | * sets a scan timer to start from then. Once the timer timeouts, the timeout 1068 | * routine vwifi_scan_timeout() will be invoked. This routine schedules a 1069 | * timeout work that informs the kernel about the "dummy" BSS and completes the 1070 | * scan. 1071 | */ 1072 | static void vwifi_scan_routine(struct work_struct *w) 1073 | { 1074 | struct vwifi_vif *vif = container_of(w, struct vwifi_vif, ws_scan); 1075 | unsigned long flags; 1076 | 1077 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 1078 | 1079 | if (vwifi_virtio_enabled) { 1080 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1081 | vwifi_virtio_scan_request(vif); 1082 | return; 1083 | } 1084 | 1085 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1086 | 1087 | /* In a real-world driver, BSS scanning would occur here. However, in the 1088 | * case of viwifi, scanning is not performed because dummy BSS entries are 1089 | * already stored in the SSID hash table. Instead, a scan timeout is set 1090 | * after a specific number of jiffies. The timeout worker informs the 1091 | * kernel about the "dummy" BSS and calls cfg80211_scan_done() to complete 1092 | * the scan. 1093 | */ 1094 | mod_timer(&vif->scan_timeout, jiffies + msecs_to_jiffies(SCAN_TIMEOUT_MS)); 1095 | } 1096 | 1097 | static void vwifi_virtio_connect_request(struct vwifi_vif *vif); 1098 | 1099 | static void vwifi_connect_routine(struct work_struct *w) 1100 | { 1101 | struct vwifi_vif *vif = container_of(w, struct vwifi_vif, ws_connect); 1102 | struct vwifi_vif *ap = NULL; 1103 | struct station_info *sinfo; 1104 | unsigned long flags; 1105 | 1106 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 1107 | 1108 | if (vwifi_virtio_enabled) { 1109 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1110 | vwifi_virtio_connect_request(vif); 1111 | return; 1112 | } 1113 | 1114 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1115 | 1116 | if (mutex_lock_interruptible(&vif->lock)) 1117 | return; 1118 | 1119 | /* Finding the AP by request SSID */ 1120 | list_for_each_entry (ap, &vwifi->ap_list, ap_list) { 1121 | if (!memcmp(ap->ssid, vif->req_ssid, ap->ssid_len)) { 1122 | pr_info("vwifi: %s is connected to AP %s (SSID: %s, BSSID: %pM)\n", 1123 | vif->ndev->name, ap->ndev->name, ap->ssid, ap->bssid); 1124 | 1125 | if (mutex_lock_interruptible(&ap->lock)) 1126 | return; 1127 | 1128 | /* AP connection part */ 1129 | sinfo = kmalloc(sizeof(struct station_info), GFP_KERNEL); 1130 | if (!sinfo) 1131 | return; 1132 | 1133 | /* It is safe that we fake the association request IEs 1134 | * by beacon IEs, since they both possibly have the WPA/RSN IE 1135 | * which is what the upper user-space program (e.g. hostapd) 1136 | * cares about. 1137 | */ 1138 | sinfo->assoc_req_ies = ap->beacon_ie; 1139 | sinfo->assoc_req_ies_len = ap->beacon_ie_len; 1140 | 1141 | list_add_tail(&vif->bss_list, &ap->bss_list); 1142 | 1143 | /* nl80211 will inform the user-space program (e.g. hostapd) 1144 | * about the newly-associated station via generic netlink 1145 | * command NL80211_CMD_NEW_STATION for latter processing 1146 | * (e.g. 4-way handshake). 1147 | */ 1148 | cfg80211_new_sta(ap->ndev, vif->ndev->dev_addr, sinfo, GFP_KERNEL); 1149 | 1150 | mutex_unlock(&ap->lock); 1151 | 1152 | /* STA connection part */ 1153 | cfg80211_connect_result(vif->ndev, ap->bssid, NULL, 0, NULL, 0, 1154 | WLAN_STATUS_SUCCESS, GFP_KERNEL); 1155 | memcpy(vif->ssid, ap->ssid, ap->ssid_len); 1156 | memcpy(vif->bssid, ap->bssid, ETH_ALEN); 1157 | vif->sme_state = SME_CONNECTED; 1158 | vif->conn_time = jiffies; 1159 | vif->ap = ap; 1160 | 1161 | mutex_unlock(&vif->lock); 1162 | 1163 | kfree(sinfo); 1164 | 1165 | return; 1166 | } 1167 | } 1168 | 1169 | /* SSID not found */ 1170 | pr_info("vwifi: SSID %s not found\n", vif->req_ssid); 1171 | 1172 | cfg80211_connect_timeout(vif->ndev, NULL, NULL, 0, GFP_KERNEL, 1173 | NL80211_TIMEOUT_SCAN); 1174 | vif->sme_state = SME_DISCONNECTED; 1175 | mutex_unlock(&vif->lock); 1176 | } 1177 | 1178 | static void vwifi_virtio_disconnect(struct vwifi_vif *vif); 1179 | 1180 | /* Invoke cfg80211_disconnected() that informs the kernel that disconnect is 1181 | * complete. Overall disconnect may call cfg80211_connect_timeout() if 1182 | * disconnect interrupting connection routine, but for this module let's keep 1183 | * it simple as possible. This routine is called through workqueue, when the 1184 | * kernel asks to disconnect through cfg80211_ops. 1185 | */ 1186 | static void vwifi_disconnect_routine(struct work_struct *w) 1187 | { 1188 | struct vwifi_vif *vif = container_of(w, struct vwifi_vif, ws_disconnect); 1189 | unsigned long flags; 1190 | 1191 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 1192 | 1193 | if (vwifi_virtio_enabled) { 1194 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1195 | vwifi_virtio_disconnect(vif); 1196 | return; 1197 | } 1198 | 1199 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1200 | 1201 | pr_info("vwifi: %s disconnected from AP %s\n", vif->ndev->name, 1202 | vif->ap->ndev->name); 1203 | 1204 | if (mutex_lock_interruptible(&vif->lock)) 1205 | return; 1206 | 1207 | /* STA cleanup stuff */ 1208 | cfg80211_disconnected(vif->ndev, vif->disconnect_reason_code, NULL, 0, true, 1209 | GFP_KERNEL); 1210 | 1211 | vif->disconnect_reason_code = 0; 1212 | vif->sme_state = SME_DISCONNECTED; 1213 | 1214 | /* AP cleanup stuff */ 1215 | if (vwifi->state != VWIFI_SHUTDOWN) { 1216 | if (mutex_lock_interruptible(&vif->ap->lock)) { 1217 | mutex_unlock(&vif->lock); 1218 | return; 1219 | } 1220 | 1221 | if (vif->ap->ap_enabled && !list_empty(&vif->bss_list)) { 1222 | cfg80211_del_sta(vif->ap->ndev, vif->ndev->dev_addr, GFP_KERNEL); 1223 | list_del(&vif->bss_list); 1224 | } 1225 | 1226 | mutex_unlock(&vif->ap->lock); 1227 | 1228 | vif->ap = NULL; 1229 | } 1230 | 1231 | mutex_unlock(&vif->lock); 1232 | } 1233 | 1234 | /* callback called by the kernel when user decided to scan. 1235 | * This callback should initiate scan routine(through work_struct) and exit with 1236 | * 0 if everything is ok. 1237 | */ 1238 | static int vwifi_scan(struct wiphy *wiphy, 1239 | struct cfg80211_scan_request *request) 1240 | { 1241 | struct vwifi_vif *vif = wdev_get_vwifi_vif(request->wdev); 1242 | 1243 | if (mutex_lock_interruptible(&vif->lock)) 1244 | return -ERESTARTSYS; 1245 | 1246 | if (vif->scan_request) { 1247 | mutex_unlock(&vif->lock); 1248 | return -EBUSY; 1249 | } 1250 | 1251 | if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 1252 | mutex_unlock(&vif->lock); 1253 | return -EPERM; 1254 | } 1255 | vif->scan_request = request; 1256 | 1257 | mutex_unlock(&vif->lock); 1258 | 1259 | if (!schedule_work(&vif->ws_scan)) 1260 | return -EBUSY; 1261 | return 0; 1262 | } 1263 | 1264 | /* callback called by the kernel when there is need to "connect" to some 1265 | * network. It initializes connection routine through work_struct and exits 1266 | * with 0 if everything is ok. connect routine should be finished with 1267 | * cfg80211_connect_bss()/cfg80211_connect_result()/cfg80211_connect_done() or 1268 | * cfg80211_connect_timeout(). 1269 | */ 1270 | static int vwifi_connect(struct wiphy *wiphy, 1271 | struct net_device *dev, 1272 | struct cfg80211_connect_params *sme) 1273 | { 1274 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 1275 | 1276 | if (mutex_lock_interruptible(&vif->lock)) 1277 | return -ERESTARTSYS; 1278 | 1279 | if (vif->sme_state != SME_DISCONNECTED) { 1280 | mutex_unlock(&vif->lock); 1281 | return -EBUSY; 1282 | } 1283 | 1284 | if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 1285 | mutex_unlock(&vif->lock); 1286 | return -EPERM; 1287 | } 1288 | 1289 | vif->sme_state = SME_CONNECTING; 1290 | vif->ssid_len = sme->ssid_len; 1291 | memcpy(vif->req_ssid, sme->ssid, sme->ssid_len); 1292 | if (sme->bssid) 1293 | memcpy(vif->req_bssid, sme->bssid, ETH_ALEN); 1294 | mutex_unlock(&vif->lock); 1295 | 1296 | if (!schedule_work(&vif->ws_connect)) 1297 | return -EBUSY; 1298 | return 0; 1299 | } 1300 | 1301 | /* callback called by the kernel when there is need to "disconnect" from 1302 | * currently connected network. It initializes disconnect routine through 1303 | * work_struct and exits with 0 if everything ok. disconnect routine should 1304 | * call cfg80211_disconnected() to inform the kernel that disconnection is 1305 | * complete. 1306 | */ 1307 | static int vwifi_disconnect(struct wiphy *wiphy, 1308 | struct net_device *dev, 1309 | u16 reason_code) 1310 | { 1311 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 1312 | 1313 | if (mutex_lock_interruptible(&vif->lock)) 1314 | return -ERESTARTSYS; 1315 | 1316 | if (vif->sme_state == SME_DISCONNECTED) { 1317 | mutex_unlock(&vif->lock); 1318 | return -EINVAL; 1319 | } 1320 | 1321 | if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 1322 | mutex_unlock(&vif->lock); 1323 | return -EPERM; 1324 | } 1325 | 1326 | vif->disconnect_reason_code = reason_code; 1327 | 1328 | mutex_unlock(&vif->lock); 1329 | 1330 | if (!schedule_work(&vif->ws_disconnect)) 1331 | return -EBUSY; 1332 | 1333 | return 0; 1334 | } 1335 | 1336 | /* Callback called by the kernel when the user requests information about 1337 | * a specific station. The information includes the number and bytes of 1338 | * transmitted and received packets, signal strength, and timing information 1339 | * such as inactive time and elapsed time since the last connection to an AP. 1340 | * This callback is invoked when the rtnl lock has been acquired. 1341 | */ 1342 | static int vwifi_get_station(struct wiphy *wiphy, 1343 | struct net_device *dev, 1344 | const u8 *mac, 1345 | struct station_info *sinfo) 1346 | { 1347 | struct vwifi_vif *vif = ndev_get_vwifi_vif(dev); 1348 | 1349 | bool found_sta = false; 1350 | switch (dev->ieee80211_ptr->iftype) { 1351 | case NL80211_IFTYPE_AP:; 1352 | struct vwifi_vif *sta_vif = NULL; 1353 | list_for_each_entry (sta_vif, &vif->bss_list, bss_list) { 1354 | if (!memcmp(mac, sta_vif->ndev->dev_addr, ETH_ALEN)) { 1355 | found_sta = true; 1356 | break; 1357 | } 1358 | } 1359 | if (!memcmp(mac, sta_vif->ndev->dev_addr, ETH_ALEN)) 1360 | found_sta = true; 1361 | break; 1362 | case NL80211_IFTYPE_STATION: 1363 | if (!memcmp(mac, vif->bssid, ETH_ALEN)) 1364 | found_sta = true; 1365 | break; 1366 | default: 1367 | pr_info("vwifi: invalid interface type %u\n", 1368 | dev->ieee80211_ptr->iftype); 1369 | return -EINVAL; 1370 | } 1371 | 1372 | if (!found_sta) 1373 | return -ENONET; 1374 | 1375 | sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) | 1376 | BIT_ULL(NL80211_STA_INFO_RX_PACKETS) | 1377 | BIT_ULL(NL80211_STA_INFO_TX_FAILED) | 1378 | BIT_ULL(NL80211_STA_INFO_TX_BYTES) | 1379 | BIT_ULL(NL80211_STA_INFO_RX_BYTES) | 1380 | BIT_ULL(NL80211_STA_INFO_SIGNAL) | 1381 | BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME) | 1382 | BIT_ULL(NL80211_STA_INFO_RX_BITRATE) | 1383 | BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 1384 | 1385 | if (vif->sme_state == SME_CONNECTED) { 1386 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME); 1387 | sinfo->connected_time = 1388 | jiffies_to_msecs(jiffies - vif->conn_time) / 1000; 1389 | 1390 | if (mutex_lock_interruptible(&vif->ap->lock)) 1391 | return -ENONET; 1392 | 1393 | sinfo->bss_param.beacon_interval = 1394 | cpu_to_le16(vif->ap->beacon_int / 1024); 1395 | sinfo->bss_param.dtim_period = 1; 1396 | 1397 | mutex_unlock(&vif->ap->lock); 1398 | sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; 1399 | } 1400 | 1401 | sinfo->tx_packets = vif->stats.tx_packets; 1402 | sinfo->rx_packets = vif->stats.rx_packets; 1403 | sinfo->tx_failed = vif->stats.tx_dropped; 1404 | sinfo->tx_bytes = vif->stats.tx_bytes; 1405 | sinfo->rx_bytes = vif->stats.rx_bytes; 1406 | /* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in dBm */ 1407 | sinfo->signal = rand_int_smooth(-100, -30, jiffies); 1408 | sinfo->inactive_time = jiffies_to_msecs(jiffies - vif->active_time); 1409 | /* 1410 | * Using 802.11n (HT) as the PHY, configure as follows: 1411 | * 1412 | * Modulation: 64-QAM 1413 | * Data Bandwidth: 20MHz 1414 | * Number of Spatial Streams: 4 1415 | * 1416 | * According to the 802.11n (HT) modulation table, we have: 1417 | * 1418 | * Number of Data Subcarriers: 52 1419 | * Number of Coded Bits per Subcarrier per Stream: 6 1420 | * Coding: 5/6 1421 | * OFDM Symbol Duration: 3.2 µs 1422 | * Guard Interval Duration: 0.8 µs 1423 | * Thus, the data rate is 260 Mbps. 1424 | * MCS table, Data Rate Formula : 1425 | * https://semfionetworks.com/blog/mcs-table-updated-with-80211ax-data-rates/ 1426 | * IEEE 802.11n : https://zh.wikipedia.org/zh-tw/IEEE_802.11n 1427 | */ 1428 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS; 1429 | sinfo->rxrate.mcs = 31; 1430 | sinfo->rxrate.bw = RATE_INFO_BW_20; 1431 | sinfo->rxrate.n_bonded_ch = 1; 1432 | 1433 | sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; 1434 | sinfo->txrate.mcs = 31; 1435 | sinfo->txrate.bw = RATE_INFO_BW_20; 1436 | sinfo->txrate.n_bonded_ch = 1; 1437 | return 0; 1438 | } 1439 | 1440 | /* dump station callback -- resume dump at index @idx */ 1441 | static int vwifi_dump_station(struct wiphy *wiphy, 1442 | struct net_device *dev, 1443 | int idx, 1444 | u8 *mac, 1445 | struct station_info *sinfo) 1446 | { 1447 | struct vwifi_vif *ap_vif = ndev_get_vwifi_vif(dev); 1448 | 1449 | pr_info("Dump station at the idx %d\n", idx); 1450 | 1451 | int ret = -ENONET; 1452 | struct vwifi_vif *sta_vif = NULL; 1453 | int i = 0; 1454 | 1455 | list_for_each_entry (sta_vif, &ap_vif->bss_list, bss_list) { 1456 | if (i < idx) { 1457 | ++i; 1458 | continue; 1459 | } 1460 | break; 1461 | } 1462 | 1463 | if (sta_vif == ap_vif) 1464 | return ret; 1465 | 1466 | ret = 0; 1467 | 1468 | memcpy(mac, sta_vif->ndev->dev_addr, ETH_ALEN); 1469 | return vwifi_get_station(wiphy, dev, mac, sinfo); 1470 | } 1471 | 1472 | static void vwifi_virtio_scan_complete(struct timer_list *t); 1473 | 1474 | /* Create a virtual interface that has its own wiphy, not shared with other 1475 | * interfaces. The interface mode is set to STA mode. To change the interface 1476 | * type, use the change_virtual_intf() function. 1477 | */ 1478 | static struct wireless_dev *vwifi_interface_add(struct wiphy *wiphy, int if_idx) 1479 | { 1480 | struct net_device *ndev = NULL; 1481 | struct vwifi_vif *vif = NULL; 1482 | 1483 | /* allocate network device context. */ 1484 | ndev = alloc_netdev(sizeof(struct vwifi_vif), NDEV_NAME, NET_NAME_ENUM, 1485 | ether_setup); 1486 | 1487 | if (!ndev) 1488 | goto error_alloc_ndev; 1489 | 1490 | /* fill private data of network context. */ 1491 | vif = ndev_get_vwifi_vif(ndev); 1492 | vif->ndev = ndev; 1493 | 1494 | /* fill wireless_dev context. 1495 | * wireless_dev with net_device can be represented as inherited class of 1496 | * single net_device. 1497 | */ 1498 | vif->wdev.wiphy = wiphy; 1499 | vif->wdev.netdev = ndev; 1500 | vif->wdev.iftype = NL80211_IFTYPE_STATION; 1501 | vif->ndev->ieee80211_ptr = &vif->wdev; 1502 | 1503 | /* set network device hooks. should implement ndo_start_xmit() at least */ 1504 | vif->ndev->netdev_ops = &vwifi_ndev_ops; 1505 | 1506 | /* Add here proper net_device initialization */ 1507 | vif->ndev->features |= NETIF_F_HW_CSUM; 1508 | 1509 | /* The first byte is '\0' to avoid being a multicast 1510 | * address (the first byte of multicast addrs is odd). 1511 | */ 1512 | char intf_name[ETH_ALEN] = {0}; 1513 | snprintf(intf_name + 1, ETH_ALEN - 1, "%s%d", NAME_PREFIX, if_idx); 1514 | 1515 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) 1516 | eth_hw_addr_set(vif->ndev, intf_name); 1517 | #else 1518 | memcpy(vif->ndev->dev_addr, intf_name, ETH_ALEN); 1519 | #endif 1520 | 1521 | /* register network device. If everything is ok, there should be new 1522 | * network device: $ ip a 1523 | * owl0: mtu 1500 qdisc 1524 | * noop state DOWN group default link/ether 00:00:00:00:00:00 1525 | * brd ff:ff:ff:ff:ff:ff 1526 | */ 1527 | if (register_netdev(vif->ndev)) 1528 | goto error_ndev_register; 1529 | 1530 | /* Initialize connection information */ 1531 | memset(vif->bssid, 0, ETH_ALEN); 1532 | memset(vif->ssid, 0, IEEE80211_MAX_SSID_LEN); 1533 | memset(vif->req_ssid, 0, IEEE80211_MAX_SSID_LEN); 1534 | vif->scan_request = NULL; 1535 | vif->sme_state = SME_DISCONNECTED; 1536 | vif->conn_time = 0; 1537 | vif->active_time = 0; 1538 | vif->disconnect_reason_code = 0; 1539 | vif->ap = NULL; 1540 | vif->bss_sta_table_entry_num = 0; 1541 | 1542 | mutex_init(&vif->lock); 1543 | mutex_init(&vif->bss_sta_table_lock); 1544 | 1545 | /* Initialize timer of scan_timeout */ 1546 | timer_setup(&vif->scan_timeout, vwifi_scan_timeout, 0); 1547 | timer_setup(&vif->scan_complete, vwifi_virtio_scan_complete, 0); 1548 | 1549 | INIT_WORK(&vif->ws_connect, vwifi_connect_routine); 1550 | INIT_WORK(&vif->ws_disconnect, vwifi_disconnect_routine); 1551 | INIT_WORK(&vif->ws_scan, vwifi_scan_routine); 1552 | INIT_WORK(&vif->ws_scan_timeout, vwifi_scan_timeout_work); 1553 | 1554 | /* Initialize rx_queue */ 1555 | INIT_LIST_HEAD(&vif->rx_queue); 1556 | 1557 | hash_init(vif->bss_sta_table); 1558 | 1559 | /* Add vif into global vif_list */ 1560 | spin_lock_bh(&vif_list_lock); 1561 | list_add_tail(&vif->list, &vwifi->vif_list); 1562 | spin_unlock_bh(&vif_list_lock); 1563 | 1564 | return &vif->wdev; 1565 | 1566 | error_ndev_register: 1567 | free_netdev(vif->ndev); 1568 | error_alloc_ndev: 1569 | wiphy_unregister(wiphy); 1570 | wiphy_free(wiphy); 1571 | return NULL; 1572 | } 1573 | 1574 | /* Called by kernel when user decided to change the interface type. */ 1575 | static int vwifi_change_iface(struct wiphy *wiphy, 1576 | struct net_device *ndev, 1577 | enum nl80211_iftype type, 1578 | struct vif_params *params) 1579 | { 1580 | switch (type) { 1581 | case NL80211_IFTYPE_STATION: 1582 | case NL80211_IFTYPE_AP: 1583 | ndev->ieee80211_ptr->iftype = type; 1584 | break; 1585 | case NL80211_IFTYPE_ADHOC: 1586 | pr_info("vwifi: %s start acting in IBSS mode.\n", ndev->name); 1587 | ndev->ieee80211_ptr->iftype = type; 1588 | break; 1589 | default: 1590 | pr_info("vwifi: invalid interface type %u\n", type); 1591 | return -EINVAL; 1592 | } 1593 | 1594 | return 0; 1595 | } 1596 | 1597 | /* Called by the kernel when the user wants to create an Access Point. 1598 | * Currently, it adds an SSID to the SSID table to emulate the AP signal and 1599 | * records the SSID in the vwifi_context. 1600 | */ 1601 | static int vwifi_start_ap(struct wiphy *wiphy, 1602 | struct net_device *ndev, 1603 | struct cfg80211_ap_settings *settings) 1604 | { 1605 | struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); 1606 | struct bss_sta_entry *sta_ent; 1607 | int ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; 1608 | int head_ie_len, tail_ie_len; 1609 | unsigned long flags; 1610 | u32 key; 1611 | 1612 | pr_info("vwifi: %s start acting in AP mode.\n", ndev->name); 1613 | pr_info("ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,", 1614 | settings->chandef.chan->hw_value, settings->chandef.center_freq1, 1615 | settings->chandef.width, settings->beacon_interval, 1616 | settings->dtim_period); 1617 | pr_info("ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d", settings->ssid, 1618 | settings->ssid_len, settings->auth_type, 1619 | settings->inactivity_timeout); 1620 | 1621 | if (settings->ssid == NULL) 1622 | return -EINVAL; 1623 | 1624 | /* Setting up AP SSID and BSSID */ 1625 | vif->ssid_len = settings->ssid_len; 1626 | memcpy(vif->ssid, settings->ssid, settings->ssid_len); 1627 | memcpy(vif->bssid, vif->ndev->dev_addr, ETH_ALEN); 1628 | 1629 | /* AP is the head of vif->bss_list */ 1630 | INIT_LIST_HEAD(&vif->bss_list); 1631 | 1632 | /* Add AP to global ap_list */ 1633 | list_add_tail(&vif->ap_list, &vwifi->ap_list); 1634 | 1635 | vif->ap_enabled = true; 1636 | 1637 | vif->privacy = settings->privacy; 1638 | 1639 | /* cfg80211 and some upper user-space programs treat IEs as two-part: 1640 | * 1. head: 802.11 beacon frame header + beacon IEs before TIM IE 1641 | * 2. tail: beacon IEs after TIM IE 1642 | * We combine them and store them in vif->beacon_ie. 1643 | */ 1644 | head_ie_len = settings->beacon.head_len - ie_offset; 1645 | tail_ie_len = settings->beacon.tail_len; 1646 | 1647 | if (likely(head_ie_len + tail_ie_len <= IE_MAX_LEN)) { 1648 | vif->beacon_ie_len = head_ie_len + tail_ie_len; 1649 | memset(vif->beacon_ie, 0, IE_MAX_LEN); 1650 | memcpy(vif->beacon_ie, &settings->beacon.head[ie_offset], head_ie_len); 1651 | memcpy(vif->beacon_ie + head_ie_len, settings->beacon.tail, 1652 | tail_ie_len); 1653 | 1654 | pr_info( 1655 | "%s: privacy = %x, head_ie_len (before TIM IE) = %d, tail_ie_len = " 1656 | "%d", 1657 | __func__, settings->privacy, head_ie_len, tail_ie_len); 1658 | } else { 1659 | pr_info("%s: IE exceed %d bytes!\n", __func__, IE_MAX_LEN); 1660 | return 1; 1661 | } 1662 | 1663 | if (settings->chandef.chan) { 1664 | pr_info("vwifi: %s center freq: %d\n", ndev->name, 1665 | settings->chandef.chan->center_freq); 1666 | vif->channel = settings->chandef.chan; 1667 | } 1668 | 1669 | if (settings->chandef.width) 1670 | vif->bw = settings->chandef.width; 1671 | 1672 | /* Default beacon interval is 100 time units */ 1673 | u64 beacon_int = 1674 | settings->beacon_interval ? settings->beacon_interval : 100; 1675 | /* beacon interval in us */ 1676 | vif->beacon_int = beacon_int * 1024; 1677 | 1678 | /* Initialize hrtimer of beacon */ 1679 | pr_info("vwifi: init beacon_timer.\n"); 1680 | hrtimer_init(&vif->beacon_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT); 1681 | vif->beacon_timer.function = vwifi_beacon; 1682 | 1683 | if (!hrtimer_is_queued(&vif->beacon_timer)) { 1684 | u64 tsf, until_tbtt; 1685 | tsf = ktime_to_us(ktime_get_real()); 1686 | u32 bcn_int = vif->beacon_int; 1687 | until_tbtt = bcn_int - do_div(tsf, bcn_int); 1688 | 1689 | hrtimer_start(&vif->beacon_timer, 1690 | ns_to_ktime(until_tbtt * NSEC_PER_USEC), 1691 | HRTIMER_MODE_REL_SOFT); 1692 | } 1693 | 1694 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 1695 | if (vwifi_virtio_enabled) { 1696 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1697 | 1698 | sta_ent = kmalloc(sizeof(struct bss_sta_entry), GFP_ATOMIC); 1699 | if (!sta_ent) { 1700 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1701 | return 1; 1702 | } 1703 | 1704 | memcpy(sta_ent->mac, vif->ndev->dev_addr, ETH_ALEN); 1705 | key = vwifi_mac_to_32(sta_ent->mac); 1706 | hash_add(vif->bss_sta_table, &sta_ent->node, key); 1707 | vif->bss_sta_table_entry_num++; 1708 | 1709 | return 0; 1710 | } 1711 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1712 | 1713 | return 0; 1714 | } 1715 | 1716 | static void vwifi_virtio_disconnect_tx(struct vwifi_vif *vif); 1717 | 1718 | /* Called by the kernel when there is a need to "stop" from AP mode. It uses 1719 | * the SSID to remove the AP node from the SSID table. 1720 | */ 1721 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 2) 1722 | static int vwifi_stop_ap(struct wiphy *wiphy, 1723 | struct net_device *ndev, 1724 | unsigned int link_id) 1725 | #else 1726 | static int vwifi_stop_ap(struct wiphy *wiphy, struct net_device *ndev) 1727 | #endif 1728 | { 1729 | struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); 1730 | struct vwifi_vif *pos = NULL, *safe = NULL; 1731 | struct bss_sta_entry *sta_ent; 1732 | struct hlist_node *tmp; 1733 | unsigned long flags; 1734 | int bkt; 1735 | 1736 | pr_info("vwifi: %s stop acting in AP mode.\n", ndev->name); 1737 | 1738 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 1739 | if (vwifi_virtio_enabled) { 1740 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1741 | vwifi_virtio_disconnect_tx(vif); 1742 | 1743 | hash_for_each_safe (vif->bss_sta_table, bkt, tmp, sta_ent, node) 1744 | kfree(sta_ent); 1745 | vif->bss_sta_table_entry_num = 0; 1746 | 1747 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1748 | return 0; 1749 | } 1750 | 1751 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1752 | 1753 | if (vwifi->state == VWIFI_SHUTDOWN) { 1754 | hrtimer_cancel(&vif->beacon_timer); 1755 | 1756 | /* Destroy bss_list first */ 1757 | list_for_each_entry_safe (pos, safe, &vif->bss_list, bss_list) 1758 | list_del(&pos->bss_list); 1759 | 1760 | /* Remove ap from global ap_list */ 1761 | if (mutex_lock_interruptible(&vwifi->lock)) 1762 | return -ERESTARTSYS; 1763 | 1764 | list_del(&vif->ap_list); 1765 | 1766 | mutex_unlock(&vwifi->lock); 1767 | } 1768 | 1769 | vif->ap_enabled = false; 1770 | 1771 | return 0; 1772 | } 1773 | 1774 | static int vwifi_change_beacon(struct wiphy *wiphy, 1775 | struct net_device *ndev, 1776 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0) 1777 | struct cfg80211_ap_update *info 1778 | #else 1779 | struct cfg80211_beacon_data *info 1780 | #endif 1781 | ) 1782 | { 1783 | struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); 1784 | int ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; 1785 | int head_ie_len, tail_ie_len; 1786 | 1787 | /* cfg80211 and some user-space programs treat IEs as two-part: 1788 | * 1. head: 802.11 beacon frame header + beacon IEs before TIM IE 1789 | * 2. tail: beacon IEs after TIM IE 1790 | * We combine them and store them in vif->beacon_ie. 1791 | */ 1792 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0) 1793 | head_ie_len = info->beacon.head_len - ie_offset; 1794 | tail_ie_len = info->beacon.tail_len; 1795 | #else 1796 | head_ie_len = info->head_len - ie_offset; 1797 | tail_ie_len = info->tail_len; 1798 | #endif 1799 | if (likely(head_ie_len + tail_ie_len <= IE_MAX_LEN)) { 1800 | vif->beacon_ie_len = head_ie_len + tail_ie_len; 1801 | memset(vif->beacon_ie, 0, IE_MAX_LEN); 1802 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0) 1803 | memcpy(vif->beacon_ie, &info->beacon.head[ie_offset], head_ie_len); 1804 | memcpy(vif->beacon_ie + head_ie_len, info->beacon.tail, tail_ie_len); 1805 | #else 1806 | memcpy(vif->beacon_ie, &info->head[ie_offset], head_ie_len); 1807 | memcpy(vif->beacon_ie + head_ie_len, info->tail, tail_ie_len); 1808 | #endif 1809 | pr_info( 1810 | "%s: head_ie_len (before TIM IE) = %d, tail_ie_len = " 1811 | "%d", 1812 | __func__, head_ie_len, tail_ie_len); 1813 | } else { 1814 | pr_info("%s: IE exceed %d bytes!\n", __func__, IE_MAX_LEN); 1815 | return 1; 1816 | } 1817 | 1818 | return 0; 1819 | } 1820 | 1821 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) 1822 | static int vwifi_add_key(struct wiphy *wiphy, 1823 | struct net_device *ndev, 1824 | int link_id, 1825 | u8 key_idx, 1826 | bool pairwise, 1827 | const u8 *mac_addr, 1828 | struct key_params *params) 1829 | #else 1830 | static int vwifi_add_key(struct wiphy *wiphy, 1831 | struct net_device *ndev, 1832 | u8 key_idx, 1833 | bool pairwise, 1834 | const u8 *mac_addr, 1835 | struct key_params *params) 1836 | #endif 1837 | { 1838 | return 0; 1839 | } 1840 | 1841 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) 1842 | static int vwifi_del_key(struct wiphy *wiphy, 1843 | struct net_device *ndev, 1844 | int link_id, 1845 | u8 key_idx, 1846 | bool pairwise, 1847 | const u8 *mac_addr) 1848 | #else 1849 | static int vwifi_del_key(struct wiphy *wiphy, 1850 | struct net_device *ndev, 1851 | u8 key_idx, 1852 | bool pairwise, 1853 | const u8 *mac_addr) 1854 | #endif 1855 | { 1856 | return 0; 1857 | } 1858 | 1859 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) 1860 | static int vwifi_set_default_key(struct wiphy *wiphy, 1861 | struct net_device *ndev, 1862 | int link_id, 1863 | u8 key_idx, 1864 | bool unicast, 1865 | bool multicast) 1866 | #else 1867 | static int vwifi_set_default_key(struct wiphy *wiphy, 1868 | struct net_device *ndev, 1869 | u8 key_idx, 1870 | bool unicast, 1871 | bool multicast) 1872 | #endif 1873 | { 1874 | return 0; 1875 | } 1876 | 1877 | static void vwifi_virtio_sta_entry_request(struct vwifi_vif *vif, 1878 | const u8 *bssid); 1879 | static void vwifi_virtio_sta_entry_response(struct vwifi_vif *vif, 1880 | enum VWIFI_STA_ENTRY_CMD cmd, 1881 | const u8 *sta); 1882 | 1883 | static int vwifi_change_station(struct wiphy *wiphy, 1884 | struct net_device *ndev, 1885 | const u8 *mac, 1886 | struct station_parameters *params) 1887 | { 1888 | struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); 1889 | struct bss_sta_entry *sta_entry; 1890 | unsigned long flags; 1891 | u32 key; 1892 | 1893 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 1894 | if (!vwifi_virtio_enabled) { 1895 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1896 | return -EINVAL; 1897 | } 1898 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 1899 | 1900 | /* For now, we only care about the authorized (802.1X) event. */ 1901 | if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))) 1902 | return 0; 1903 | 1904 | if (is_zero_ether_addr(mac)) 1905 | return 0; 1906 | 1907 | /* For AP, we broadcast an unsolicited `VWIFI_STA_ENTRY_RESPONSE` 1908 | * which contains the newly connected STA's MAC, so that other 1909 | * STAs know the existent of the STA. 1910 | */ 1911 | if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 1912 | sta_entry = kmalloc(sizeof(struct bss_sta_entry), GFP_KERNEL); 1913 | if (!sta_entry) 1914 | return -ENOMEM; 1915 | 1916 | memcpy(sta_entry->mac, mac, ETH_ALEN); 1917 | 1918 | mutex_lock(&vif->bss_sta_table_lock); 1919 | 1920 | /* We use a very naive method here to map 6-bytes MAC 1921 | * into a 8-bytes integer so that we could pass it 1922 | * into hash_add() as a key. Please optimize it. 1923 | */ 1924 | key = vwifi_mac_to_32(mac); 1925 | hash_add(vif->bss_sta_table, &sta_entry->node, key); 1926 | vif->bss_sta_table_entry_num++; 1927 | 1928 | mutex_unlock(&vif->bss_sta_table_lock); 1929 | 1930 | vwifi_virtio_sta_entry_response(vif, VWIFI_STA_ENTRY_ADD, mac); 1931 | } 1932 | /* For STA, we send a `VWIFI_STA_ENTRY_REQUEST` for the newly 1933 | * conncted AP to ask about the STAs in the BSS. 1934 | */ 1935 | else if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 1936 | if (mutex_lock_interruptible(&vif->lock)) 1937 | return -ERESTARTSYS; 1938 | 1939 | memcpy(vif->ssid, vif->req_ssid, vif->ssid_len); 1940 | memcpy(vif->bssid, mac, ETH_ALEN); 1941 | vif->sme_state = SME_CONNECTED; 1942 | vif->conn_time = jiffies; 1943 | 1944 | mutex_unlock(&vif->lock); 1945 | 1946 | vwifi_virtio_sta_entry_request(vif, mac); 1947 | } 1948 | 1949 | return 0; 1950 | } 1951 | 1952 | /* Unregister and free a virtual interface identified by @vif->ndev. */ 1953 | static int vwifi_delete_interface(struct vwifi_vif *vif) 1954 | { 1955 | struct vwifi_packet *pkt = NULL, *safe = NULL; 1956 | struct wiphy *wiphy = vif->wdev.wiphy; 1957 | struct bss_sta_entry *sta_ent; 1958 | struct hlist_node *tmp; 1959 | int bkt; 1960 | 1961 | /* Stop TX queue, and delete the pending packets */ 1962 | netif_stop_queue(vif->ndev); 1963 | list_for_each_entry_safe (pkt, safe, &vif->rx_queue, list) { 1964 | list_del(&pkt->list); 1965 | kfree(pkt); 1966 | } 1967 | 1968 | hash_for_each_safe (vif->bss_sta_table, bkt, tmp, sta_ent, node) 1969 | kfree(sta_ent); 1970 | 1971 | if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 1972 | if (mutex_lock_interruptible(&vif->lock)) 1973 | return -ERESTARTSYS; 1974 | 1975 | cancel_work_sync(&vif->ws_scan); 1976 | cancel_work_sync(&vif->ws_scan_timeout); 1977 | del_timer_sync(&vif->scan_complete); 1978 | 1979 | /* If there's a pending scan, call cfg80211_scan_done to finish it. */ 1980 | if (vif->scan_request) { 1981 | struct cfg80211_scan_info info = {.aborted = true}; 1982 | 1983 | cfg80211_scan_done(vif->scan_request, &info); 1984 | vif->scan_request = NULL; 1985 | } 1986 | 1987 | /* Make sure that no work is queued */ 1988 | del_timer_sync(&vif->scan_timeout); 1989 | cancel_work_sync(&vif->ws_connect); 1990 | cancel_work_sync(&vif->ws_disconnect); 1991 | 1992 | mutex_unlock(&vif->lock); 1993 | } 1994 | 1995 | /* Deallocate net_device */ 1996 | unregister_netdev(vif->ndev); 1997 | free_netdev(vif->ndev); 1998 | 1999 | /* Deallocate wiphy device */ 2000 | wiphy_unregister(wiphy); 2001 | wiphy_free(wiphy); 2002 | 2003 | return 0; 2004 | } 2005 | 2006 | /* Set transmit power for the virtual interface */ 2007 | static int vwifi_set_tx_power(struct wiphy *wiphy, 2008 | struct wireless_dev *wdev, 2009 | enum nl80211_tx_power_setting type, 2010 | int mbm) 2011 | { 2012 | struct vwifi_vif *vif = wiphy_get_vwifi_vif(wiphy); 2013 | /* Validate vif pointer */ 2014 | if (!vif) 2015 | return -EINVAL; 2016 | 2017 | if (mutex_lock_interruptible(&vif->lock)) 2018 | return -ERESTARTSYS; 2019 | 2020 | int power = MBM_TO_DBM(mbm); 2021 | switch (type) { 2022 | case NL80211_TX_POWER_AUTOMATIC: 2023 | /* Use default transmit power (11 dBm) */ 2024 | vif->tx_power = 11; 2025 | break; 2026 | 2027 | case NL80211_TX_POWER_LIMITED: 2028 | /* Restrict power limits to a specific value (0 ~ 18 dBm) */ 2029 | if (power < 0) 2030 | vif->tx_power = 0; 2031 | else if (power > 18) 2032 | vif->tx_power = 18; 2033 | else 2034 | vif->tx_power = power; 2035 | break; 2036 | 2037 | case NL80211_TX_POWER_FIXED: 2038 | /* Set power freely */ 2039 | vif->tx_power = power; 2040 | break; 2041 | 2042 | default: 2043 | return -EINVAL; /* Invalid parameter */ 2044 | } 2045 | 2046 | mutex_unlock(&vif->lock); 2047 | 2048 | return 0; 2049 | } 2050 | 2051 | /* Get transmit power from the virtual interface */ 2052 | static int vwifi_get_tx_power(struct wiphy *wiphy, 2053 | struct wireless_dev *wdev, 2054 | int *dbm) 2055 | { 2056 | struct vwifi_vif *vif = wdev_get_vwifi_vif(wdev); 2057 | /* Validate vif pointer */ 2058 | if (!vif) 2059 | return -EINVAL; 2060 | *dbm = vif->tx_power; 2061 | return 0; 2062 | } 2063 | 2064 | /* Make the device join a specific IBSS. */ 2065 | static int vwifi_join_ibss(struct wiphy *wiphy, 2066 | struct net_device *ndev, 2067 | struct cfg80211_ibss_params *params) 2068 | { 2069 | struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); 2070 | /* Validate vif pointer */ 2071 | if (!vif) 2072 | return -EINVAL; 2073 | if (mutex_lock_interruptible(&vif->lock)) 2074 | return -ERESTARTSYS; 2075 | /* Retrieve IBSS configuration parameters */ 2076 | memcpy(vif->ssid, params->ssid, params->ssid_len); 2077 | vif->ibss_chandef = params->chandef; 2078 | vif->ssid_len = params->ssid_len; 2079 | /* When the BSSID is automatically generated by the system, it will not be 2080 | * passed as a parameter to the join function. */ 2081 | if (params->bssid) 2082 | memcpy(vif->bssid, params->bssid, ETH_ALEN); 2083 | else { 2084 | /* Search for IBSS networks with WPA settings in the IBSS list. If a 2085 | * matching network exists, join it. Otherwise, create one. */ 2086 | memcpy(vif->bssid, ndev->dev_addr, ETH_ALEN); 2087 | struct vwifi_vif *ibss_vif = NULL; 2088 | list_for_each_entry (ibss_vif, &vwifi->ibss_list, ibss_list) { 2089 | if (ibss_vif->ssid_len == vif->ssid_len && 2090 | !memcmp(ibss_vif->ssid, vif->ssid, vif->ssid_len) && 2091 | ibss_vif->ibss_chandef.center_freq1 == 2092 | vif->ibss_chandef.center_freq1) { 2093 | memcpy(vif->bssid, ibss_vif->bssid, ETH_ALEN); 2094 | break; 2095 | } 2096 | } 2097 | } 2098 | vif->beacon_ie_len = params->ie_len; 2099 | memcpy(vif->beacon_ie, params->ie, params->ie_len); 2100 | vif->ibss_beacon_int = params->beacon_interval; 2101 | vif->ibss_basic_rates = params->basic_rates; 2102 | vif->ibss_channel_fixed = params->channel_fixed; 2103 | vif->ibss_privacy = params->privacy; 2104 | vif->ibss_control_port = params->control_port; 2105 | vif->ibss_control_port_over_nl80211 = params->control_port_over_nl80211; 2106 | vif->ibss_userspace_handles_dfs = params->userspace_handles_dfs; 2107 | memcpy(vif->ibss_mcast_rate, params->mcast_rate, 2108 | sizeof(params->mcast_rate)); 2109 | vif->ibss_ht_capa = params->ht_capa; 2110 | vif->ibss_ht_capa_mask = params->ht_capa_mask; 2111 | vif->ibss_wep_keys = params->wep_keys; 2112 | vif->ibss_wep_tx_key = params->wep_tx_key; 2113 | 2114 | mutex_unlock(&vif->lock); 2115 | 2116 | /* Insert ibss into global ibss_list */ 2117 | if (mutex_lock_interruptible(&vwifi->lock)) 2118 | return -ERESTARTSYS; 2119 | 2120 | list_add_tail(&vif->ibss_list, &vwifi->ibss_list); 2121 | 2122 | mutex_unlock(&vwifi->lock); 2123 | 2124 | pr_info("vwifi : %s join %s.\n", vif->ndev->name, vif->ssid); 2125 | 2126 | return 0; 2127 | } 2128 | 2129 | /* Make the device leave the current IBSS. */ 2130 | static int vwifi_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) 2131 | { 2132 | struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); 2133 | /* Validate vif pointer */ 2134 | if (!vif) 2135 | return -EINVAL; 2136 | /* Remove ibss from global ibss_list */ 2137 | if (mutex_lock_interruptible(&vwifi->lock)) 2138 | return -ERESTARTSYS; 2139 | 2140 | list_del(&vif->ibss_list); 2141 | 2142 | mutex_unlock(&vwifi->lock); 2143 | 2144 | pr_info("vwifi : %s leave %s.\n", vif->ndev->name, vif->ssid); 2145 | 2146 | return 0; 2147 | } 2148 | 2149 | /* Structure of functions for FullMAC 80211 drivers. Functions implemented 2150 | * along with fields/flags in the wiphy structure represent driver features. 2151 | * This module can only perform "scan" and "connect". Some functions cannot 2152 | * be implemented alone. For example, with "connect" there should be a 2153 | * corresponding "disconnect" function. 2154 | */ 2155 | static struct cfg80211_ops vwifi_cfg_ops = { 2156 | .change_virtual_intf = vwifi_change_iface, 2157 | .scan = vwifi_scan, 2158 | .connect = vwifi_connect, 2159 | .disconnect = vwifi_disconnect, 2160 | .get_station = vwifi_get_station, 2161 | .dump_station = vwifi_dump_station, 2162 | .start_ap = vwifi_start_ap, 2163 | .stop_ap = vwifi_stop_ap, 2164 | .change_beacon = vwifi_change_beacon, 2165 | .add_key = vwifi_add_key, 2166 | .del_key = vwifi_del_key, 2167 | .set_default_key = vwifi_set_default_key, 2168 | .change_station = vwifi_change_station, 2169 | .set_tx_power = vwifi_set_tx_power, 2170 | .get_tx_power = vwifi_get_tx_power, 2171 | .join_ibss = vwifi_join_ibss, 2172 | .leave_ibss = vwifi_leave_ibss, 2173 | }; 2174 | 2175 | /* Macro for defining 2GHZ channel array */ 2176 | #define CHAN_2GHZ(channel, freq) \ 2177 | { \ 2178 | .band = NL80211_BAND_2GHZ, .hw_value = (channel), \ 2179 | .center_freq = (freq), \ 2180 | } 2181 | 2182 | /* Macro for defining 5GHZ channel array */ 2183 | #define CHAN_5GHZ(channel) \ 2184 | { \ 2185 | .band = NL80211_BAND_5GHZ, .hw_value = (channel), \ 2186 | .center_freq = 5000 + (5 * (channel)), \ 2187 | } 2188 | 2189 | /* Macro for defining rate table */ 2190 | #define RATE_ENT(_rate, _hw_value) \ 2191 | { \ 2192 | .bitrate = (_rate), .hw_value = (_hw_value), \ 2193 | } 2194 | 2195 | /* Array of "supported" channels in 2GHz band. It is required for wiphy. */ 2196 | static const struct ieee80211_channel vwifi_supported_channels_2ghz[] = { 2197 | CHAN_2GHZ(1, 2412), CHAN_2GHZ(2, 2417), CHAN_2GHZ(3, 2422), 2198 | CHAN_2GHZ(4, 2427), CHAN_2GHZ(5, 2432), CHAN_2GHZ(6, 2437), 2199 | CHAN_2GHZ(7, 2442), CHAN_2GHZ(8, 2447), CHAN_2GHZ(9, 2452), 2200 | CHAN_2GHZ(10, 2457), CHAN_2GHZ(11, 2462), CHAN_2GHZ(12, 2467), 2201 | CHAN_2GHZ(13, 2472), CHAN_2GHZ(14, 2484), 2202 | }; 2203 | 2204 | /* Array of "supported" channels in 5GHz band. It is required for wiphy. */ 2205 | static const struct ieee80211_channel vwifi_supported_channels_5ghz[] = { 2206 | CHAN_5GHZ(34), CHAN_5GHZ(36), CHAN_5GHZ(38), CHAN_5GHZ(40), 2207 | CHAN_5GHZ(42), CHAN_5GHZ(44), CHAN_5GHZ(46), CHAN_5GHZ(48), 2208 | CHAN_5GHZ(52), CHAN_5GHZ(56), CHAN_5GHZ(60), CHAN_5GHZ(64), 2209 | CHAN_5GHZ(100), CHAN_5GHZ(104), CHAN_5GHZ(108), CHAN_5GHZ(112), 2210 | CHAN_5GHZ(116), CHAN_5GHZ(120), CHAN_5GHZ(124), CHAN_5GHZ(128), 2211 | CHAN_5GHZ(132), CHAN_5GHZ(136), CHAN_5GHZ(140), CHAN_5GHZ(144), 2212 | CHAN_5GHZ(149), CHAN_5GHZ(153), CHAN_5GHZ(157), CHAN_5GHZ(161), 2213 | CHAN_5GHZ(165), 2214 | }; 2215 | 2216 | /* Array of supported rates, required to support those next rates 2217 | * for 2GHz and 5GHz band. 2218 | */ 2219 | static const struct ieee80211_rate vwifi_supported_rates[] = { 2220 | RATE_ENT(10, 0x1), RATE_ENT(20, 0x2), RATE_ENT(55, 0x4), 2221 | RATE_ENT(110, 0x8), RATE_ENT(60, 0x10), RATE_ENT(90, 0x20), 2222 | RATE_ENT(120, 0x40), RATE_ENT(180, 0x80), RATE_ENT(240, 0x100), 2223 | RATE_ENT(360, 0x200), RATE_ENT(480, 0x400), RATE_ENT(540, 0x800), 2224 | }; 2225 | 2226 | /* Describes supported band of 2GHz. */ 2227 | static struct ieee80211_supported_band nf_band_2ghz; 2228 | 2229 | /* Describes supported band of 5GHz. */ 2230 | static struct ieee80211_supported_band nf_band_5ghz; 2231 | 2232 | /* Unregister and free virtual interfaces and wiphy. */ 2233 | static void vwifi_free(void) 2234 | { 2235 | struct vwifi_vif *vif = NULL, *safe = NULL; 2236 | 2237 | spin_lock_bh(&vif_list_lock); 2238 | list_for_each_entry_safe (vif, safe, &vwifi->vif_list, list) { 2239 | spin_unlock_bh(&vif_list_lock); 2240 | vwifi_delete_interface(vif); 2241 | spin_lock_bh(&vif_list_lock); 2242 | } 2243 | spin_unlock_bh(&vif_list_lock); 2244 | 2245 | kfree(vwifi->denylist); 2246 | kfree(vwifi); 2247 | } 2248 | 2249 | /* Allocate and register wiphy. 2250 | * Virtual interfaces should be created by nl80211, which will call 2251 | * cfg80211_ops->add_iface(). This program creates a wiphy for every 2252 | * virtual interface, which means a virtual interface has a physical (virtual) 2253 | * adapter associated with it. 2254 | */ 2255 | static struct wiphy *vwifi_cfg80211_add(void) 2256 | { 2257 | struct wiphy *wiphy = NULL; 2258 | enum nl80211_band band; 2259 | 2260 | /* In order to customize vwifi's wiphy name, we use vwifi_wiphy_counter to 2261 | * keep track of the number of wiphy in vwifi, and use vwifi_wiphy_idx to 2262 | * retreive the value of vwifi_wiphy_counter. 2263 | */ 2264 | int vwifi_wiphy_idx = atomic_inc_return(&vwifi_wiphy_counter); 2265 | 2266 | /* atomic_inc_return makes it start at 1, make it start at 0 */ 2267 | vwifi_wiphy_idx--; 2268 | if (unlikely(vwifi_wiphy_idx < 0)) { 2269 | atomic_dec(&vwifi_wiphy_counter); 2270 | return NULL; 2271 | } 2272 | 2273 | /* allocate wiphy context. It is possible just to use wiphy_new(). 2274 | * wiphy should represent physical FullMAC wireless device. We need 2275 | * to implement add_virtual_intf() from cfg80211_ops for adding 2276 | * interface(s) on top of a wiphy. 2277 | * NULL means use the default phy%d naming. 2278 | * vwifi_wiphy_name is the custom-made vw_phy%d naming we use for 2279 | * wiphy in vwifi. 2280 | */ 2281 | 2282 | /* Reference: 2283 | * https://elixir.bootlin.com/linux/v6.7/source/net/wireless/core.c#L447 2284 | * The default phy%d naming for wiphy in linux kernel depends on the value 2285 | * of a static variable wiphy_counter. The value of wiphy_counter will never 2286 | * decrease even if we unregister the wiphy. This behavior ensures that the 2287 | * naming and indexing for `struct wiphy` will be absolutely unique. 2288 | * However, the kernel might have other modules or projects also utilizing 2289 | * `struct wiphy`, which will cause some confusion of wiphy's index and 2290 | * naming when using the default naming scheme. We implement a custom-made 2291 | * name "vw_phy%d" for wiphy in vwifi device driver, in order to seperate 2292 | * the naming and indexing for `struct wiphy` in vwifi. 2293 | */ 2294 | char vwifi_wiphy_name[VWIFI_WIPHY_NAME_LEN] = {0}; 2295 | snprintf(vwifi_wiphy_name, VWIFI_WIPHY_NAME_LEN, "%s%d", VWIFI_WIPHY_PREFIX, 2296 | vwifi_wiphy_idx); 2297 | 2298 | wiphy = wiphy_new_nm(&vwifi_cfg_ops, 0, vwifi_wiphy_name); 2299 | if (!wiphy) { 2300 | pr_info("couldn't allocate wiphy device\n"); 2301 | return NULL; 2302 | } 2303 | 2304 | /* FIXME: set device object as wiphy "parent" */ 2305 | /* set_wiphy_dev(ret->wiphy, dev); */ 2306 | 2307 | /* wiphy should determinate its type. 2308 | * add other required types like "BIT(NL80211_IFTYPE_STATION) | 2309 | * BIT(NL80211_IFTYPE_AP)" etc. 2310 | */ 2311 | wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 2312 | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_ADHOC); 2313 | 2314 | for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { 2315 | /* FIXME: add other band capabilities if needed, such as 40 width */ 2316 | switch (band) { 2317 | case NL80211_BAND_2GHZ: 2318 | nf_band_2ghz.ht_cap.cap = IEEE80211_HT_CAP_SGI_20; 2319 | nf_band_2ghz.ht_cap.ht_supported = false; 2320 | nf_band_2ghz.channels = 2321 | kmemdup(vwifi_supported_channels_2ghz, 2322 | sizeof(vwifi_supported_channels_2ghz), GFP_KERNEL); 2323 | nf_band_2ghz.n_channels = ARRAY_SIZE(vwifi_supported_channels_2ghz); 2324 | nf_band_2ghz.bitrates = 2325 | kmemdup(vwifi_supported_rates, sizeof(vwifi_supported_rates), 2326 | GFP_KERNEL); 2327 | nf_band_2ghz.n_bitrates = ARRAY_SIZE(vwifi_supported_rates); 2328 | wiphy->bands[band] = &nf_band_2ghz; 2329 | break; 2330 | case NL80211_BAND_5GHZ: 2331 | nf_band_5ghz.channels = 2332 | kmemdup(vwifi_supported_channels_5ghz, 2333 | sizeof(vwifi_supported_channels_5ghz), GFP_KERNEL); 2334 | nf_band_5ghz.n_channels = ARRAY_SIZE(vwifi_supported_channels_5ghz); 2335 | nf_band_5ghz.bitrates = 2336 | kmemdup(vwifi_supported_rates + 4, 2337 | (ARRAY_SIZE(vwifi_supported_rates) - 4) * 2338 | sizeof(struct ieee80211_rate), 2339 | GFP_KERNEL); 2340 | nf_band_5ghz.n_bitrates = ARRAY_SIZE(vwifi_supported_rates) - 4; 2341 | wiphy->bands[band] = &nf_band_5ghz; 2342 | break; 2343 | default: 2344 | continue; 2345 | } 2346 | } 2347 | 2348 | /* scan - if the device supports "scan", we need to define max_scan_ssids 2349 | * at least. 2350 | */ 2351 | wiphy->max_scan_ssids = MAX_PROBED_SSIDS; 2352 | wiphy->max_scan_ie_len = IE_MAX_LEN; 2353 | 2354 | /* Signal type 2355 | * CFG80211_SIGNAL_TYPE_UNSPEC allows us specify signal strength from 0 to 2356 | * 100. The reasonable value for CFG80211_SIGNAL_TYPE_MBM is -3000 to -10000 2357 | * (mBm). 2358 | */ 2359 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 2360 | 2361 | wiphy->flags |= WIPHY_FLAG_NETNS_OK; 2362 | 2363 | wiphy->cipher_suites = vwifi_cipher_suites; 2364 | wiphy->n_cipher_suites = ARRAY_SIZE(vwifi_cipher_suites); 2365 | 2366 | /* register wiphy, if everything ok - there should be another wireless 2367 | * device in system. use command: $ iw list 2368 | */ 2369 | if (wiphy_register(wiphy) < 0) { 2370 | pr_info("couldn't register wiphy device\n"); 2371 | goto error_wiphy_register; 2372 | } 2373 | 2374 | return wiphy; 2375 | 2376 | error_wiphy_register: 2377 | wiphy_free(wiphy); 2378 | return NULL; 2379 | } 2380 | 2381 | static void vwifi_virtio_scan_complete(struct timer_list *t) 2382 | { 2383 | struct vwifi_vif *vif = container_of(t, struct vwifi_vif, scan_complete); 2384 | struct cfg80211_scan_info info = { 2385 | .aborted = false, 2386 | }; 2387 | 2388 | if (mutex_lock_interruptible(&vif->lock)) 2389 | return; 2390 | 2391 | cfg80211_scan_done(vif->scan_request, &info); 2392 | 2393 | vif->scan_request = NULL; 2394 | 2395 | mutex_unlock(&vif->lock); 2396 | } 2397 | 2398 | static void vwifi_virtio_scan_request(struct vwifi_vif *vif) 2399 | { 2400 | struct sk_buff *skb; 2401 | struct ethhdr *eth; 2402 | struct vwifi_virtio_header *vvh; 2403 | struct vwifi_virtio_scan_req *scan_req; 2404 | int len = ETH_HLEN + VWIFI_VIRTIO_HEADER_TYPE_BYTE + 2405 | sizeof(struct vwifi_virtio_scan_req); 2406 | bool wildcard_ssid = false; 2407 | 2408 | if (!vif->scan_request) 2409 | return; 2410 | 2411 | skb = dev_alloc_skb(len); 2412 | if (!skb) 2413 | return; 2414 | 2415 | skb_put(skb, len); 2416 | 2417 | eth = (struct ethhdr *) skb->data; 2418 | eth_broadcast_addr(eth->h_dest); 2419 | memcpy(eth->h_source, vif->ndev->dev_addr, ETH_ALEN); 2420 | 2421 | /* We treat our management frame as 802.3 type, so we put length here */ 2422 | eth->h_proto = htons(len); 2423 | 2424 | vvh = (struct vwifi_virtio_header *) (eth + 1); 2425 | vvh->type = cpu_to_le16(VWIFI_SCAN_REQUEST); 2426 | 2427 | scan_req = (struct vwifi_virtio_scan_req *) ((u8 *) vvh + 2428 | VWIFI_VIRTIO_HEADER_TYPE_BYTE); 2429 | 2430 | if (!vif->scan_request->n_ssids || !vif->scan_request->ssids[0].ssid_len) 2431 | wildcard_ssid = true; 2432 | 2433 | if (wildcard_ssid) 2434 | scan_req->ssid_len = 0; 2435 | else { 2436 | scan_req->ssid_len = cpu_to_le32(vif->scan_request->ssids[0].ssid_len); 2437 | memcpy(scan_req->ssid, vif->scan_request->ssids[0].ssid, 2438 | vif->scan_request->ssids[0].ssid_len); 2439 | } 2440 | 2441 | vwifi_virtio_tx(vif, skb); 2442 | 2443 | mod_timer(&vif->scan_timeout, jiffies + msecs_to_jiffies(2000)); 2444 | } 2445 | 2446 | static void vwifi_virtio_connect_request(struct vwifi_vif *vif) 2447 | { 2448 | struct sk_buff *skb; 2449 | struct ethhdr *eth; 2450 | struct vwifi_virtio_header *vvh; 2451 | struct vwifi_virtio_conn_req *conn_req; 2452 | int len = ETH_HLEN + VWIFI_VIRTIO_HEADER_TYPE_BYTE + 2453 | sizeof(struct vwifi_virtio_conn_req); 2454 | 2455 | skb = dev_alloc_skb(len); 2456 | if (!skb) 2457 | return; 2458 | 2459 | skb_put(skb, len); 2460 | 2461 | eth = (struct ethhdr *) skb->data; 2462 | memcpy(eth->h_dest, vif->req_bssid, ETH_ALEN); 2463 | memcpy(eth->h_source, vif->ndev->dev_addr, ETH_ALEN); 2464 | 2465 | /* We treat our management frame as 802.3 type, so we put length here */ 2466 | eth->h_proto = htons(len); 2467 | 2468 | vvh = (struct vwifi_virtio_header *) (eth + 1); 2469 | vvh->type = cpu_to_le16(VWIFI_CONNECT_REQUEST); 2470 | 2471 | conn_req = (struct vwifi_virtio_conn_req *) ((u8 *) vvh + 2472 | VWIFI_VIRTIO_HEADER_TYPE_BYTE); 2473 | memcpy(conn_req->bssid, vif->req_bssid, ETH_ALEN); 2474 | conn_req->ssid_len = cpu_to_le32(vif->ssid_len); 2475 | memcpy(conn_req->ssid, vif->req_ssid, vif->ssid_len); 2476 | 2477 | vwifi_virtio_tx(vif, skb); 2478 | } 2479 | 2480 | static void vwifi_virtio_disconnect_tx(struct vwifi_vif *vif); 2481 | 2482 | static void vwifi_virtio_disconnect(struct vwifi_vif *vif) 2483 | { 2484 | vwifi_virtio_disconnect_tx(vif); 2485 | 2486 | cfg80211_disconnected(vif->ndev, vif->disconnect_reason_code, NULL, 0, true, 2487 | GFP_KERNEL); 2488 | 2489 | if (mutex_lock_interruptible(&vif->lock)) 2490 | return; 2491 | 2492 | vif->disconnect_reason_code = 0; 2493 | vif->sme_state = SME_DISCONNECTED; 2494 | memset(vif->bssid, 0, ETH_ALEN); 2495 | 2496 | mutex_unlock(&vif->lock); 2497 | } 2498 | 2499 | static void vwifi_virtio_disconnect_tx(struct vwifi_vif *vif) 2500 | { 2501 | struct sk_buff *skb; 2502 | struct ethhdr *eth; 2503 | struct vwifi_virtio_header *vvh; 2504 | struct vwifi_virtio_disconn *disconn; 2505 | int len = ETH_HLEN + VWIFI_VIRTIO_HEADER_TYPE_BYTE + 2506 | sizeof(struct vwifi_virtio_disconn); 2507 | 2508 | skb = dev_alloc_skb(len); 2509 | if (!skb) 2510 | return; 2511 | 2512 | skb_put(skb, len); 2513 | 2514 | eth = (struct ethhdr *) skb->data; 2515 | memcpy(eth->h_source, vif->ndev->dev_addr, ETH_ALEN); 2516 | 2517 | if (vif->wdev.iftype == NL80211_IFTYPE_STATION) 2518 | memcpy(eth->h_dest, vif->bssid, ETH_ALEN); 2519 | /* right now AP only sent disconnect frame when cfg80211->stop_ap() */ 2520 | else if (vif->wdev.iftype == NL80211_IFTYPE_AP) 2521 | eth_broadcast_addr(eth->h_dest); 2522 | else 2523 | return; 2524 | 2525 | /* We treat our management frame as 802.3 type, so we put length here */ 2526 | eth->h_proto = htons(len); 2527 | 2528 | vvh = (struct vwifi_virtio_header *) (eth + 1); 2529 | vvh->type = cpu_to_le16(VWIFI_DISCONNECT); 2530 | 2531 | disconn = (struct vwifi_virtio_disconn *) ((u8 *) vvh + 2532 | VWIFI_VIRTIO_HEADER_TYPE_BYTE); 2533 | memcpy(disconn->bssid, vif->bssid, ETH_ALEN); 2534 | disconn->reason_code = cpu_to_le16(vif->disconnect_reason_code); 2535 | 2536 | vwifi_virtio_tx(vif, skb); 2537 | } 2538 | 2539 | static void vwifi_virtio_sta_entry_request(struct vwifi_vif *vif, 2540 | const u8 *bssid) 2541 | { 2542 | struct sk_buff *skb; 2543 | struct ethhdr *eth; 2544 | struct vwifi_virtio_header *vvh; 2545 | int len = ETH_HLEN + VWIFI_VIRTIO_HEADER_TYPE_BYTE; 2546 | 2547 | if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 2548 | return; 2549 | 2550 | skb = dev_alloc_skb(len); 2551 | if (!skb) 2552 | return; 2553 | 2554 | skb_put(skb, len); 2555 | 2556 | eth = (struct ethhdr *) skb->data; 2557 | memcpy(eth->h_dest, bssid, ETH_ALEN); 2558 | memcpy(eth->h_source, vif->ndev->dev_addr, ETH_ALEN); 2559 | 2560 | /* We treat our management frame as 802.3 type, so we put length here */ 2561 | eth->h_proto = htons(len); 2562 | 2563 | vvh = (struct vwifi_virtio_header *) (eth + 1); 2564 | vvh->type = cpu_to_le16(VWIFI_STA_ENTRY_REQUEST); 2565 | 2566 | vwifi_virtio_tx(vif, skb); 2567 | } 2568 | 2569 | 2570 | static void vwifi_virtio_sta_entry_response(struct vwifi_vif *vif, 2571 | enum VWIFI_STA_ENTRY_CMD cmd, 2572 | const u8 *sta) 2573 | { 2574 | struct sk_buff *skb; 2575 | struct ethhdr *eth; 2576 | struct vwifi_virtio_header *vvh; 2577 | struct vwifi_virtio_sta_entry_resp *sta_ent_resp; 2578 | struct bss_sta_entry *sta_ent; 2579 | int len = ETH_HLEN + VWIFI_VIRTIO_HEADER_TYPE_BYTE + 2580 | sizeof(struct vwifi_virtio_sta_entry_resp); 2581 | u32 bkt; 2582 | u8 *map_p; 2583 | 2584 | if (vif->wdev.iftype != NL80211_IFTYPE_AP) 2585 | return; 2586 | 2587 | if (cmd == VWIFI_STA_ENTRY_ADD || cmd == VWIFI_STA_ENTRY_DEL) 2588 | len += ETH_ALEN; 2589 | else if (cmd == VWIFI_STA_ENTRY_ADD_ALL) 2590 | len += vif->bss_sta_table_entry_num * ETH_ALEN; 2591 | else 2592 | return; 2593 | 2594 | /* Fix me. We should fragment the frame if the size of the 2595 | * VWIFI_STA_ENTRY_RESPONSE frame exceeds the maximum Ethernet 2596 | * frame length. 2597 | */ 2598 | if (len > ETH_FRAME_LEN) { 2599 | pr_info("%s: length %d exceeds ETH_FRAME_LEN, ignore.\n", __func__, 2600 | len); 2601 | return; 2602 | } 2603 | 2604 | skb = dev_alloc_skb(len); 2605 | if (!skb) 2606 | return; 2607 | 2608 | skb_put(skb, len); 2609 | 2610 | eth = (struct ethhdr *) skb->data; 2611 | memcpy(eth->h_source, vif->ndev->dev_addr, ETH_ALEN); 2612 | 2613 | if (cmd == VWIFI_STA_ENTRY_ADD || cmd == VWIFI_STA_ENTRY_DEL) 2614 | eth_broadcast_addr(eth->h_dest); 2615 | else if (cmd == VWIFI_STA_ENTRY_ADD_ALL) 2616 | memcpy(eth->h_dest, sta, ETH_ALEN); 2617 | else 2618 | goto out_free_skb; 2619 | 2620 | /* We treat our management frame as 802.3 type, so we put length here */ 2621 | eth->h_proto = htons(len); 2622 | 2623 | vvh = (struct vwifi_virtio_header *) (eth + 1); 2624 | vvh->type = cpu_to_le16(VWIFI_STA_ENTRY_RESPONSE); 2625 | 2626 | sta_ent_resp = 2627 | (struct vwifi_virtio_sta_entry_resp *) ((u8 *) vvh + 2628 | VWIFI_VIRTIO_HEADER_TYPE_BYTE); 2629 | memcpy(sta_ent_resp->bssid, vif->ndev->dev_addr, ETH_ALEN); 2630 | sta_ent_resp->cmd = cpu_to_le16(cmd); 2631 | 2632 | if (cmd == VWIFI_STA_ENTRY_ADD || cmd == VWIFI_STA_ENTRY_DEL) { 2633 | sta_ent_resp->count = cpu_to_le32(1); 2634 | memcpy(sta_ent_resp->macs, sta, ETH_ALEN); 2635 | } else if (cmd == VWIFI_STA_ENTRY_ADD_ALL) { 2636 | sta_ent_resp->count = cpu_to_le32(vif->bss_sta_table_entry_num); 2637 | 2638 | mutex_lock(&vif->bss_sta_table_lock); 2639 | 2640 | map_p = sta_ent_resp->macs; 2641 | hash_for_each (vif->bss_sta_table, bkt, sta_ent, node) { 2642 | memcpy(map_p, sta_ent->mac, ETH_ALEN); 2643 | map_p += ETH_ALEN; 2644 | } 2645 | 2646 | mutex_unlock(&vif->bss_sta_table_lock); 2647 | } else 2648 | goto out_free_skb; 2649 | 2650 | vwifi_virtio_tx(vif, skb); 2651 | 2652 | return; 2653 | 2654 | out_free_skb: 2655 | dev_kfree_skb(skb); 2656 | } 2657 | 2658 | static void vwifi_virtio_mgmt_rx_sta_entry_response( 2659 | struct vwifi_vif *vif, 2660 | const u8 *src, 2661 | struct vwifi_virtio_sta_entry_resp *sta_ent_resp) 2662 | { 2663 | struct bss_sta_entry *sta_ent; 2664 | int i; 2665 | u32 key; 2666 | u16 cmd = le16_to_cpu(sta_ent_resp->cmd); 2667 | u8 *mac_p; 2668 | 2669 | if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 2670 | return; 2671 | 2672 | if (!ether_addr_equal(sta_ent_resp->bssid, vif->bssid)) 2673 | return; 2674 | 2675 | mutex_lock(&vif->bss_sta_table_lock); 2676 | 2677 | mac_p = sta_ent_resp->macs; 2678 | for (i = 0; i < le32_to_cpu(sta_ent_resp->count); i++) { 2679 | if (ether_addr_equal(mac_p, vif->ndev->dev_addr)) { 2680 | mac_p += ETH_ALEN; 2681 | continue; 2682 | } 2683 | 2684 | if (cmd == VWIFI_STA_ENTRY_ADD || cmd == VWIFI_STA_ENTRY_ADD_ALL) { 2685 | sta_ent = kmalloc(sizeof(struct bss_sta_entry), GFP_KERNEL); 2686 | if (!sta_ent) 2687 | goto out_unlock; 2688 | 2689 | memcpy(sta_ent->mac, mac_p, ETH_ALEN); 2690 | key = vwifi_mac_to_32(sta_ent->mac); 2691 | hash_add(vif->bss_sta_table, &sta_ent->node, key); 2692 | vif->bss_sta_table_entry_num++; 2693 | } else if (cmd == VWIFI_STA_ENTRY_DEL) { 2694 | key = vwifi_mac_to_32(mac_p); 2695 | hash_for_each_possible (vif->bss_sta_table, sta_ent, node, key) { 2696 | if (ether_addr_equal(sta_ent->mac, mac_p)) { 2697 | hlist_del_init(&sta_ent->node); 2698 | kfree(sta_ent); 2699 | vif->bss_sta_table_entry_num--; 2700 | break; 2701 | } 2702 | } 2703 | } else 2704 | goto out_unlock; 2705 | 2706 | mac_p += ETH_ALEN; 2707 | 2708 | if (unlikely((unsigned long) mac_p - 2709 | ((unsigned long) sta_ent_resp - 2710 | VWIFI_VIRTIO_HEADER_TYPE_BYTE - ETH_HLEN) > 2711 | ETH_FRAME_LEN)) 2712 | goto out_unlock; 2713 | } 2714 | out_unlock: 2715 | mutex_unlock(&vif->bss_sta_table_lock); 2716 | } 2717 | 2718 | static void vwifi_virtio_mgmt_rx_sta_entry_request(struct vwifi_vif *vif, 2719 | const u8 *src) 2720 | { 2721 | if (vif->wdev.iftype != NL80211_IFTYPE_AP) 2722 | return; 2723 | 2724 | vwifi_virtio_sta_entry_response(vif, VWIFI_STA_ENTRY_ADD_ALL, src); 2725 | } 2726 | 2727 | static void vwifi_virtio_mgmt_rx_disconnect( 2728 | struct vwifi_vif *vif, 2729 | const u8 *src, 2730 | struct vwifi_virtio_disconn *disconn) 2731 | { 2732 | struct bss_sta_entry *tmp; 2733 | u32 key; 2734 | 2735 | if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 2736 | cfg80211_disconnected(vif->ndev, vif->disconnect_reason_code, NULL, 0, 2737 | true, GFP_KERNEL); 2738 | 2739 | if (mutex_lock_interruptible(&vif->lock)) 2740 | return; 2741 | 2742 | vif->disconnect_reason_code = 0; 2743 | vif->sme_state = SME_DISCONNECTED; 2744 | memset(vif->bssid, 0, ETH_ALEN); 2745 | 2746 | mutex_unlock(&vif->lock); 2747 | } else if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 2748 | cfg80211_del_sta(vif->ndev, src, GFP_KERNEL); 2749 | 2750 | mutex_lock(&vif->bss_sta_table_lock); 2751 | 2752 | key = vwifi_mac_to_32(src); 2753 | hash_for_each_possible (vif->bss_sta_table, tmp, node, key) { 2754 | if (ether_addr_equal(tmp->mac, src)) { 2755 | hlist_del_init(&tmp->node); 2756 | kfree(tmp); 2757 | break; 2758 | } 2759 | } 2760 | 2761 | mutex_unlock(&vif->bss_sta_table_lock); 2762 | 2763 | vwifi_virtio_sta_entry_response(vif, VWIFI_STA_ENTRY_DEL, src); 2764 | } 2765 | } 2766 | 2767 | static void vwifi_virtio_mgmt_rx_connect_response( 2768 | struct vwifi_vif *vif, 2769 | const u8 *src, 2770 | struct vwifi_virtio_conn_resp *conn_resp) 2771 | { 2772 | if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 2773 | return; 2774 | 2775 | cfg80211_connect_result(vif->ndev, vif->req_bssid, NULL, 0, NULL, 0, 2776 | le16_to_cpu(conn_resp->status_code), GFP_KERNEL); 2777 | 2778 | if (!(le16_to_cpu(conn_resp->capab_info) & WLAN_CAPABILITY_PRIVACY)) { 2779 | if (mutex_lock_interruptible(&vif->lock)) 2780 | return; 2781 | 2782 | memcpy(vif->ssid, vif->req_ssid, vif->ssid_len); 2783 | memcpy(vif->bssid, vif->req_bssid, ETH_ALEN); 2784 | vif->sme_state = SME_CONNECTED; 2785 | vif->conn_time = jiffies; 2786 | 2787 | mutex_unlock(&vif->lock); 2788 | 2789 | vwifi_virtio_sta_entry_request(vif, vif->bssid); 2790 | } 2791 | /* Otherwise we defer the AP info's update to cfg80211_ops->change_station() 2792 | */ 2793 | } 2794 | 2795 | static void vwifi_virtio_mgmt_rx_connect_request( 2796 | struct vwifi_vif *vif, 2797 | const u8 *src, 2798 | struct vwifi_virtio_conn_req *conn_req) 2799 | { 2800 | struct sk_buff *skb; 2801 | struct ethhdr *eth; 2802 | struct vwifi_virtio_header *vvh; 2803 | struct vwifi_virtio_conn_resp *conn_resp; 2804 | struct bss_sta_entry *sta_ent; 2805 | struct station_info *sinfo; 2806 | int len = ETH_HLEN + VWIFI_VIRTIO_HEADER_TYPE_BYTE + 2807 | sizeof(struct vwifi_virtio_conn_resp); 2808 | u32 key; 2809 | 2810 | if (vif->wdev.iftype != NL80211_IFTYPE_AP) 2811 | return; 2812 | 2813 | if (!ether_addr_equal(vif->ndev->dev_addr, conn_req->bssid) || 2814 | vif->ssid_len != le32_to_cpu(conn_req->ssid_len) || 2815 | memcmp(vif->ssid, conn_req->ssid, vif->ssid_len)) 2816 | return; 2817 | 2818 | /* Ignore the STA which has been connected */ 2819 | key = vwifi_mac_to_32(src); 2820 | hash_for_each_possible (vif->bss_sta_table, sta_ent, node, key) 2821 | if (ether_addr_equal(sta_ent->mac, src)) 2822 | return; 2823 | 2824 | sinfo = kmalloc(sizeof(struct station_info), GFP_KERNEL); 2825 | if (!sinfo) 2826 | return; 2827 | 2828 | skb = dev_alloc_skb(len); 2829 | if (!skb) 2830 | goto out_free_sinfo; 2831 | 2832 | skb_put(skb, len); 2833 | 2834 | eth = (struct ethhdr *) skb->data; 2835 | memcpy(eth->h_dest, src, ETH_ALEN); 2836 | memcpy(eth->h_source, vif->ndev->dev_addr, ETH_ALEN); 2837 | 2838 | /* We treat our management frame as 802.3 type, so we put length here */ 2839 | eth->h_proto = htons(len); 2840 | 2841 | vvh = (struct vwifi_virtio_header *) (eth + 1); 2842 | vvh->type = cpu_to_le16(VWIFI_CONNECT_RESPONSE); 2843 | 2844 | conn_resp = 2845 | (struct vwifi_virtio_conn_resp *) ((u8 *) vvh + 2846 | VWIFI_VIRTIO_HEADER_TYPE_BYTE); 2847 | conn_resp->status_code = cpu_to_le16(WLAN_STATUS_SUCCESS); 2848 | conn_resp->capab_info = cpu_to_le16(WLAN_CAPABILITY_ESS); 2849 | conn_resp->capab_info |= 2850 | vif->privacy ? cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0; 2851 | 2852 | vwifi_virtio_tx(vif, skb); 2853 | 2854 | if (!vif->privacy) { 2855 | sta_ent = kmalloc(sizeof(struct bss_sta_entry), GFP_KERNEL); 2856 | if (!sta_ent) 2857 | goto out_free_sinfo; 2858 | 2859 | memcpy(sta_ent->mac, src, ETH_ALEN); 2860 | 2861 | mutex_lock(&vif->bss_sta_table_lock); 2862 | 2863 | hash_add(vif->bss_sta_table, &sta_ent->node, key); 2864 | vif->bss_sta_table_entry_num++; 2865 | 2866 | mutex_unlock(&vif->bss_sta_table_lock); 2867 | 2868 | vwifi_virtio_sta_entry_response(vif, VWIFI_STA_ENTRY_ADD, src); 2869 | } 2870 | 2871 | /* It is safe that we fake the association request IEs 2872 | * by beacon IEs, since they both possibly have the WPA/RSN IE 2873 | * which is what the upper user-space program (e.g. hostapd) 2874 | * cares about. 2875 | */ 2876 | sinfo->assoc_req_ies = vif->beacon_ie; 2877 | sinfo->assoc_req_ies_len = vif->beacon_ie_len; 2878 | 2879 | /* nl80211 will inform the user-space program (e.g. hostapd) 2880 | * about the newly-associated station via generic netlink 2881 | * command NL80211_CMD_NEW_STATION for latter processing 2882 | * (e.g. 4-way handshake). 2883 | */ 2884 | cfg80211_new_sta(vif->ndev, src, sinfo, GFP_KERNEL); 2885 | 2886 | out_free_sinfo: 2887 | kfree(sinfo); 2888 | } 2889 | 2890 | static void vwifi_virtio_mgmt_rx_scan_response( 2891 | struct vwifi_vif *vif, 2892 | const u8 *src, 2893 | struct vwifi_virtio_scan_resp *scan_resp) 2894 | { 2895 | struct cfg80211_bss *bss; 2896 | 2897 | if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 2898 | return; 2899 | 2900 | struct ieee80211_channel rx_channel = { 2901 | .band = NL80211_BAND_2GHZ, 2902 | .center_freq = le32_to_cpu(scan_resp->channel), 2903 | }; 2904 | struct cfg80211_inform_bss data = { 2905 | .chan = &rx_channel, 2906 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) 2907 | .scan_width = NL80211_BSS_CHAN_WIDTH_20, 2908 | #endif 2909 | .signal = DBM_TO_MBM(rand_int_smooth(-100, -30, jiffies)), 2910 | }; 2911 | 2912 | bss = cfg80211_inform_bss_data( 2913 | vif->wdev.wiphy, &data, CFG80211_BSS_FTYPE_UNKNOWN, scan_resp->bssid, 2914 | le64_to_cpu(scan_resp->timestamp), le16_to_cpu(scan_resp->capab_info), 2915 | 100, scan_resp->beacon_ies, le32_to_cpu(scan_resp->beacon_ies_len), 2916 | GFP_KERNEL); 2917 | 2918 | cfg80211_put_bss(vif->wdev.wiphy, bss); 2919 | } 2920 | 2921 | static void vwifi_virtio_mgmt_rx_scan_request( 2922 | struct vwifi_vif *vif, 2923 | const u8 src[ETH_ALEN], 2924 | struct vwifi_virtio_scan_req *scan_req) 2925 | { 2926 | struct sk_buff *skb; 2927 | struct ethhdr *eth; 2928 | struct vwifi_virtio_header *vvh; 2929 | struct vwifi_virtio_scan_resp *scan_resp; 2930 | int len = ETH_HLEN + VWIFI_VIRTIO_HEADER_TYPE_BYTE + 2931 | sizeof(struct vwifi_virtio_scan_resp) + vif->beacon_ie_len; 2932 | 2933 | if (vif->wdev.iftype != NL80211_IFTYPE_AP) 2934 | return; 2935 | 2936 | if (scan_req->ssid_len != 0 && 2937 | (le32_to_cpu(scan_req->ssid_len) != vif->ssid_len || 2938 | memcmp(scan_req->ssid, vif->ssid, vif->ssid_len))) 2939 | return; 2940 | 2941 | skb = dev_alloc_skb(len); 2942 | if (!skb) 2943 | return; 2944 | 2945 | skb_put(skb, len); 2946 | 2947 | eth = (struct ethhdr *) skb->data; 2948 | memcpy(eth->h_dest, src, ETH_ALEN); 2949 | memcpy(eth->h_source, vif->ndev->dev_addr, ETH_ALEN); 2950 | 2951 | /* We treat our management frame as 802.3 type, so we put length here */ 2952 | eth->h_proto = htons(len); 2953 | 2954 | vvh = (struct vwifi_virtio_header *) (eth + 1); 2955 | vvh->type = cpu_to_le16(VWIFI_SCAN_RESPONSE); 2956 | 2957 | scan_resp = 2958 | (struct vwifi_virtio_scan_resp *) ((u8 *) vvh + 2959 | VWIFI_VIRTIO_HEADER_TYPE_BYTE); 2960 | memcpy(scan_resp->bssid, vif->bssid, ETH_ALEN); 2961 | scan_resp->timestamp = cpu_to_le64(div_u64(ktime_get_boottime_ns(), 1000)); 2962 | scan_resp->beacon_int = cpu_to_le16(100); 2963 | scan_resp->capab_info = cpu_to_le16(WLAN_CAPABILITY_ESS); 2964 | scan_resp->capab_info |= 2965 | vif->privacy ? cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0; 2966 | scan_resp->ssid_len = cpu_to_le32(vif->ssid_len); 2967 | memcpy(scan_resp->ssid, vif->ssid, vif->ssid_len); 2968 | scan_resp->channel = cpu_to_le32( 2969 | vif->wdev.wiphy->bands[NL80211_BAND_2GHZ]->channels[0].center_freq); 2970 | scan_resp->beacon_ies_len = cpu_to_le32(vif->beacon_ie_len); 2971 | memcpy(scan_resp->beacon_ies, vif->beacon_ie, vif->beacon_ie_len); 2972 | 2973 | vwifi_virtio_tx(vif, skb); 2974 | } 2975 | 2976 | 2977 | static void vwifi_virtio_mgmt_rx(struct vwifi_vif *vif, struct sk_buff *skb) 2978 | { 2979 | struct ethhdr *eth = (struct ethhdr *) skb->data; 2980 | struct vwifi_virtio_header *vh = (struct vwifi_virtio_header *) (eth + 1); 2981 | 2982 | switch (le16_to_cpu(vh->type)) { 2983 | case VWIFI_SCAN_REQUEST: 2984 | vwifi_virtio_mgmt_rx_scan_request( 2985 | vif, eth->h_source, 2986 | (struct vwifi_virtio_scan_req *) ((u8 *) vh + 2987 | VWIFI_VIRTIO_HEADER_TYPE_BYTE)); 2988 | break; 2989 | case VWIFI_SCAN_RESPONSE: 2990 | vwifi_virtio_mgmt_rx_scan_response( 2991 | vif, eth->h_source, 2992 | (struct vwifi_virtio_scan_resp *) ((u8 *) vh + 2993 | VWIFI_VIRTIO_HEADER_TYPE_BYTE)); 2994 | break; 2995 | case VWIFI_CONNECT_REQUEST: 2996 | vwifi_virtio_mgmt_rx_connect_request( 2997 | vif, eth->h_source, 2998 | (struct vwifi_virtio_conn_req *) ((u8 *) vh + 2999 | VWIFI_VIRTIO_HEADER_TYPE_BYTE)); 3000 | break; 3001 | case VWIFI_CONNECT_RESPONSE: 3002 | vwifi_virtio_mgmt_rx_connect_response( 3003 | vif, eth->h_source, 3004 | (struct vwifi_virtio_conn_resp *) ((u8 *) vh + 3005 | VWIFI_VIRTIO_HEADER_TYPE_BYTE)); 3006 | break; 3007 | case VWIFI_DISCONNECT: 3008 | vwifi_virtio_mgmt_rx_disconnect( 3009 | vif, eth->h_source, 3010 | (struct vwifi_virtio_disconn *) ((u8 *) vh + 3011 | VWIFI_VIRTIO_HEADER_TYPE_BYTE)); 3012 | break; 3013 | case VWIFI_STA_ENTRY_REQUEST: 3014 | vwifi_virtio_mgmt_rx_sta_entry_request(vif, eth->h_source); 3015 | break; 3016 | case VWIFI_STA_ENTRY_RESPONSE: 3017 | vwifi_virtio_mgmt_rx_sta_entry_response( 3018 | vif, eth->h_source, 3019 | (struct vwifi_virtio_sta_entry_resp 3020 | *) ((u8 *) vh + VWIFI_VIRTIO_HEADER_TYPE_BYTE)); 3021 | break; 3022 | default: 3023 | break; 3024 | } 3025 | 3026 | dev_kfree_skb(skb); 3027 | } 3028 | 3029 | static void vwifi_virtio_data_rx(struct vwifi_vif *vif, struct sk_buff *skb) 3030 | { 3031 | struct ethhdr *eth = (struct ethhdr *) skb->data; 3032 | struct bss_sta_entry *sta_ent; 3033 | bool same_bss = false; 3034 | 3035 | mutex_lock(&vif->bss_sta_table_lock); 3036 | hash_for_each_possible (vif->bss_sta_table, sta_ent, node, 3037 | vwifi_mac_to_32(eth->h_source)) { 3038 | if (ether_addr_equal(sta_ent->mac, eth->h_source)) { 3039 | same_bss = true; 3040 | break; 3041 | } 3042 | } 3043 | mutex_unlock(&vif->bss_sta_table_lock); 3044 | 3045 | /* We allow EAPOL frames to enter even when the sender is 3046 | * not in the STA entry table. 3047 | */ 3048 | if (!same_bss && eth->h_proto != htons(ETH_P_PAE)) 3049 | return; 3050 | 3051 | skb->dev = vif->ndev; 3052 | skb->protocol = eth_type_trans(skb, vif->ndev); 3053 | skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ 3054 | 3055 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) 3056 | netif_rx_ni(skb); 3057 | #else 3058 | netif_rx(skb); 3059 | #endif 3060 | } 3061 | 3062 | static void vwifi_virtio_rx_switch(struct vwifi_vif *vif, struct sk_buff *skb) 3063 | { 3064 | struct ethhdr *eth = (struct ethhdr *) skb->data; 3065 | 3066 | if (likely(eth_proto_is_802_3(eth->h_proto))) 3067 | vwifi_virtio_data_rx(vif, skb); 3068 | else 3069 | vwifi_virtio_mgmt_rx(vif, skb); 3070 | } 3071 | 3072 | static void vwifi_virtio_rx_work(struct work_struct *work) 3073 | { 3074 | struct vwifi_vif *vif = 3075 | list_first_entry(&vwifi->vif_list, struct vwifi_vif, list); 3076 | struct sk_buff *skb; 3077 | unsigned int len; 3078 | unsigned long flags; 3079 | 3080 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 3081 | if (!vwifi_virtio_enabled) 3082 | goto out_unlock; 3083 | 3084 | skb = virtqueue_get_buf(vwifi_vqs[VWIFI_VQ_RX], &len); 3085 | if (!skb) 3086 | goto out_unlock; 3087 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3088 | 3089 | skb_put(skb, len - vif->vnet_hdr_len); 3090 | 3091 | vwifi_virtio_rx_switch(vif, skb); 3092 | 3093 | vwifi_virtio_fill_vq(vwifi_vqs[VWIFI_VQ_RX], vif->vnet_hdr_len); 3094 | 3095 | schedule_work(&vwifi_virtio_rx); 3096 | return; 3097 | 3098 | out_unlock: 3099 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3100 | } 3101 | 3102 | static void vwifi_virtio_tx_done(struct virtqueue *vq) 3103 | { 3104 | struct sk_buff *skb; 3105 | unsigned long flags; 3106 | unsigned int len; 3107 | 3108 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 3109 | while ((skb = virtqueue_get_buf(vq, &len))) 3110 | dev_kfree_skb_irq(skb); 3111 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3112 | } 3113 | 3114 | static void vwifi_virtio_rx_done(struct virtqueue *vq) 3115 | { 3116 | schedule_work(&vwifi_virtio_rx); 3117 | } 3118 | 3119 | static netdev_tx_t vwifi_virtio_tx(struct vwifi_vif *vif, struct sk_buff *skb) 3120 | { 3121 | struct virtio_net_hdr_mrg_rxbuf *hdr = 3122 | (struct virtio_net_hdr_mrg_rxbuf *) skb->cb; 3123 | struct scatterlist sg[2]; 3124 | int err; 3125 | unsigned long flags; 3126 | 3127 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 3128 | if (!vwifi_virtio_enabled) { 3129 | err = -ENODEV; 3130 | goto out_free; 3131 | } 3132 | 3133 | memset(hdr, 0, vif->vnet_hdr_len); 3134 | hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE; 3135 | hdr->hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID; 3136 | 3137 | sg_init_table(sg, 2); 3138 | sg_set_buf(sg, hdr, vif->vnet_hdr_len); 3139 | sg_set_buf(sg + 1, skb->data, skb->len); 3140 | 3141 | err = virtqueue_add_outbuf(vwifi_vqs[VWIFI_VQ_TX], sg, 2, skb, GFP_ATOMIC); 3142 | 3143 | if (err) 3144 | goto out_free; 3145 | if (!virtqueue_kick(vwifi_vqs[VWIFI_VQ_TX])) { 3146 | pr_info("%s: virtqueue_kick fail\n", __func__); 3147 | goto out_free; 3148 | } 3149 | 3150 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3151 | return NETDEV_TX_OK; 3152 | 3153 | out_free: 3154 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3155 | dev_kfree_skb(skb); 3156 | return err; 3157 | } 3158 | 3159 | static int vwifi_virtio_init_vqs(struct virtio_device *vdev) 3160 | { 3161 | vq_callback_t *callbacks[VWIFI_NUM_VQS] = { 3162 | [VWIFI_VQ_RX] = vwifi_virtio_rx_done, 3163 | [VWIFI_VQ_TX] = vwifi_virtio_tx_done, 3164 | }; 3165 | const char *names[VWIFI_NUM_VQS] = { 3166 | [VWIFI_VQ_RX] = "rx", 3167 | [VWIFI_VQ_TX] = "tx", 3168 | }; 3169 | 3170 | return virtio_find_vqs(vdev, VWIFI_NUM_VQS, vwifi_vqs, callbacks, names, 3171 | NULL); 3172 | } 3173 | 3174 | static void vwifi_virtio_fill_vq(struct virtqueue *vq, u8 vnet_hdr_len) 3175 | { 3176 | struct sk_buff *skb; 3177 | struct scatterlist sg[2]; 3178 | unsigned long flags; 3179 | int err; 3180 | 3181 | skb = dev_alloc_skb(ETH_FRAME_LEN + NET_IP_ALIGN); 3182 | if (!skb) 3183 | return; 3184 | 3185 | /* align IP address on 16B boundary */ 3186 | skb_reserve(skb, NET_IP_ALIGN); 3187 | 3188 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 3189 | if (!vwifi_virtio_enabled) 3190 | goto out_free; 3191 | 3192 | sg_init_table(sg, 2); 3193 | sg_set_buf(sg, skb->cb, vnet_hdr_len); 3194 | sg_set_buf(sg + 1, skb->data, ETH_FRAME_LEN); 3195 | 3196 | err = virtqueue_add_inbuf(vq, sg, 2, skb, GFP_ATOMIC); 3197 | if (err) 3198 | goto out_free; 3199 | 3200 | if (!virtqueue_kick(vq)) 3201 | goto out_free; 3202 | 3203 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3204 | return; 3205 | 3206 | out_free: 3207 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3208 | dev_kfree_skb(skb); 3209 | } 3210 | 3211 | static void vwifi_virtio_remove_vqs(struct virtio_device *vdev) 3212 | { 3213 | int i; 3214 | 3215 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) 3216 | virtio_reset_device(vdev); 3217 | #else 3218 | vdev->config->reset(vdev); 3219 | #endif 3220 | 3221 | for (i = 0; i < ARRAY_SIZE(vwifi_vqs); i++) { 3222 | struct virtqueue *vq = vwifi_vqs[i]; 3223 | struct sk_buff *skb; 3224 | 3225 | while ((skb = virtqueue_detach_unused_buf(vq))) 3226 | dev_kfree_skb(skb); 3227 | } 3228 | 3229 | vdev->config->del_vqs(vdev); 3230 | } 3231 | 3232 | /* For now, we only support virtio when station=1 */ 3233 | static int vwifi_virtio_probe(struct virtio_device *vdev) 3234 | { 3235 | struct vwifi_vif *vif; 3236 | unsigned long flags; 3237 | int err; 3238 | 3239 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 3240 | if (vwifi_virtio_enabled) { 3241 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3242 | return -EEXIST; 3243 | } 3244 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3245 | 3246 | if (station != 1) { 3247 | pr_info("virtio not enabled, please reload module with station=1\n"); 3248 | return -EINVAL; 3249 | } 3250 | 3251 | vif = list_first_entry(&vwifi->vif_list, struct vwifi_vif, list); 3252 | if (!vif) 3253 | return -ENOENT; 3254 | 3255 | /* We assum VIRTIO_NET_F_MRG_RXBUF feature is off on the device */ 3256 | if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) 3257 | vif->vnet_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf); 3258 | else 3259 | vif->vnet_hdr_len = sizeof(struct virtio_net_hdr); 3260 | 3261 | err = vwifi_virtio_init_vqs(vdev); 3262 | if (err) 3263 | return err; 3264 | 3265 | /* Configuration may specify what MAC to use. Otherwise random. */ 3266 | if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) { 3267 | u8 addr[ETH_ALEN]; 3268 | 3269 | virtio_cread_bytes(vdev, offsetof(struct virtio_net_config, mac), addr, 3270 | ETH_ALEN); 3271 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) 3272 | eth_hw_addr_set(vif->ndev, addr); 3273 | #else 3274 | memcpy(vif->ndev->dev_addr, addr, ETH_ALEN); 3275 | #endif 3276 | } else 3277 | eth_hw_addr_random(vif->ndev); 3278 | 3279 | virtio_device_ready(vdev); 3280 | 3281 | spin_lock_irqsave(&vwifi_virtio_lock, flags); 3282 | vwifi_virtio_enabled = true; 3283 | spin_unlock_irqrestore(&vwifi_virtio_lock, flags); 3284 | 3285 | return 0; 3286 | } 3287 | 3288 | static void vwifi_virtio_remove(struct virtio_device *vdev) 3289 | { 3290 | vwifi_virtio_enabled = false; 3291 | 3292 | cancel_work_sync(&vwifi_virtio_rx); 3293 | 3294 | vwifi_virtio_remove_vqs(vdev); 3295 | } 3296 | 3297 | 3298 | /* vwifi virtio device id table */ 3299 | static const struct virtio_device_id id_table[] = { 3300 | {VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID}, 3301 | {0}, 3302 | }; 3303 | MODULE_DEVICE_TABLE(virtio, id_table); 3304 | 3305 | static unsigned int features[] = { 3306 | VIRTIO_NET_F_MAC, 3307 | }; 3308 | 3309 | static struct virtio_driver virtio_vwifi = { 3310 | .feature_table = features, 3311 | .feature_table_size = ARRAY_SIZE(features), 3312 | .driver.name = KBUILD_MODNAME, 3313 | .driver.owner = THIS_MODULE, 3314 | .id_table = id_table, 3315 | .probe = vwifi_virtio_probe, 3316 | .remove = vwifi_virtio_remove, 3317 | }; 3318 | 3319 | static int __init vwifi_init(void) 3320 | { 3321 | int err; 3322 | 3323 | vwifi = kmalloc(sizeof(struct vwifi_context), GFP_KERNEL); 3324 | if (!vwifi) { 3325 | pr_info("couldn't allocate space for vwifi_context\n"); 3326 | return -ENOMEM; 3327 | } 3328 | 3329 | mutex_init(&vwifi->lock); 3330 | INIT_LIST_HEAD(&vwifi->vif_list); 3331 | INIT_LIST_HEAD(&vwifi->ap_list); 3332 | INIT_LIST_HEAD(&vwifi->ibss_list); 3333 | vwifi->denylist = kmalloc(sizeof(char) * MAX_DENYLIST_SIZE, GFP_KERNEL); 3334 | 3335 | for (int i = 0; i < station; i++) { 3336 | struct wiphy *wiphy = vwifi_cfg80211_add(); 3337 | if (!wiphy) 3338 | goto cfg80211_add; 3339 | if (!vwifi_interface_add(wiphy, i)) 3340 | goto interface_add; 3341 | } 3342 | 3343 | nl_sk = netlink_kernel_create(&init_net, NETLINK_USERSOCK, &nl_config); 3344 | if (!nl_sk) { 3345 | pr_info("Error creating netlink socket\n"); 3346 | goto cfg80211_add; 3347 | } 3348 | 3349 | err = register_virtio_driver(&virtio_vwifi); 3350 | if (err) 3351 | goto err_register_virtio_driver; 3352 | 3353 | vwifi->state = VWIFI_READY; 3354 | 3355 | return 0; 3356 | 3357 | err_register_virtio_driver: 3358 | interface_add: 3359 | /* FIXME: check for resource deallocation */ 3360 | cfg80211_add: 3361 | vwifi_free(); 3362 | 3363 | return -1; 3364 | } 3365 | 3366 | static void __exit vwifi_exit(void) 3367 | { 3368 | vwifi->state = VWIFI_SHUTDOWN; 3369 | 3370 | unregister_virtio_driver(&virtio_vwifi); 3371 | vwifi_free(); 3372 | netlink_kernel_release(nl_sk); 3373 | } 3374 | 3375 | module_init(vwifi_init); 3376 | module_exit(vwifi_exit); 3377 | --------------------------------------------------------------------------------