├── .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 |

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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------