]> git.ipfire.org Git - people/ms/network.git/blame - src/functions/functions.wireless
wireless-ap: Enable ACS only for ath* devices
[people/ms/network.git] / src / functions / functions.wireless
CommitLineData
d76f5107 1#!/bin/bash
1578dae9
MT
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
0e035311 5# Copyright (C) 2012 IPFire Network Development Team #
1578dae9
MT
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
d76f5107 21
31670741
MT
22# Sets the global wireless country code. Default is 00 = world.
23WIRELESS_REGULATORY_DOMAIN="00"
e9df08ad 24NETWORK_SETTINGS_FILE_PARAMS="${NETWORK_SETTINGS_FILE_PARAMS} WIRELESS_REGULATORY_DOMAIN"
31670741 25
ab9e0fd0
MT
26WIRELESS_REGULATORY_DOMAIN_DATABASE="/usr/lib/crda/regulatory.bin"
27
1c9e2fa8
MT
28WIRELESS_DEFAULT_ENCRYPTION_MODE="NONE"
29WIRELESS_VALID_ENCRYPTION_MODES="WPA2-PSK-SHA256 WPA2-PSK \
eefac93d 30 WPA-PSK-SHA256 WPA-PSK NONE"
1c9e2fa8 31
54094fc7
MT
32declare -A WIRELESS_CHANNEL_BANDWIDTHS=(
33 ["802.11ac"]="20 40 80 160 80+80"
34 ["802.11a/n"]="20 40"
35 ["802.11a"]="20 40"
36 ["802.11g/n"]="20 40"
37 ["802.11g"]="20 40"
38)
39
49958b8c
MT
40cli_wireless() {
41 local action=${1}
42 shift 1
47feaba1 43
49958b8c
MT
44 case "${action}" in
45 network)
46 cli_wireless_network "$@"
47 ;;
48 *)
49 error "Unrecognized argument: ${action}"
50 exit ${EXIT_ERROR}
51 ;;
52 esac
53}
47feaba1 54
1c6a4e30 55wireless_create() {
d76f5107 56 local device=${1}
d76f5107 57 assert isset device
22a61046 58 shift
d76f5107 59
22a61046 60 local address
9170fc35 61 local channel
22a61046
MT
62 local phy
63 local type="managed"
64
65 while [ $# -gt 0 ]; do
66 case "${1}" in
67 --address=*)
2212045f 68 address=$(cli_get_val "${1}")
22a61046 69 ;;
9170fc35
MT
70 --channel=*)
71 channel=$(cli_get_val "${1}")
72 ;;
22a61046 73 --phy=*)
2212045f 74 phy=$(cli_get_val "${1}")
22a61046
MT
75 phy=$(phy_get ${phy})
76 ;;
77 --type=*)
2212045f 78 type=$(cli_get_val "${1}")
22a61046
MT
79
80 # ap --> __ap
81 [ "${type}" = "ap" ] && type="__ap"
82 ;;
83 *)
84 error "Unrecognized argument: ${1}"
85 return ${EXIT_ERROR}
86 ;;
87 esac
88 shift
89 done
d76f5107 90
be5ddfb5
MT
91 case "${type}" in
92 ibss|managed|monitor|__ap)
93 ;;
94 mesh-point)
95 type="mp"
96 ;;
97 *)
98 log ERROR "Unknown type: ${type}"
99 return ${EXIT_ERROR}
100 ;;
101
102 esac
103
22a61046
MT
104 assert phy_exists ${phy}
105 isset address || address=$(mac_generate)
106
107 cmd_quiet iw phy ${phy} interface add ${device} type ${type}
108 local ret=$?
d76f5107 109
22a61046
MT
110 if [ ${ret} -eq ${EXIT_OK} ]; then
111 log DEBUG "created wireless device '${device}' (${type})"
d76f5107 112
22a61046
MT
113 if isset address; then
114 device_set_address ${device} ${address}
115 fi
116 else
117 log ERROR "could not create wireless device '${device}' (${type}): ${ret}"
d76f5107
MT
118 fi
119
9170fc35
MT
120 # Set the channel
121 if isset channel; then
109884be 122 wireless_set_channel "${device}" "${channel}" "auto" || return $?
9170fc35
MT
123 fi
124
22a61046 125 return ${ret}
d76f5107
MT
126}
127
1c6a4e30 128wireless_remove() {
d76f5107 129 local device=${1}
22a61046 130 assert isset device
d76f5107 131
22a61046
MT
132 if ! device_exists ${device}; then
133 return ${EXIT_OK}
134 fi
d76f5107 135
22a61046 136 # Tear down the device (if necessary).
d76f5107
MT
137 device_set_down ${device}
138
22a61046
MT
139 # Remove it.
140 cmd_quiet iw dev ${device} del
141 local ret=$?
142
143 if [ ${ret} -eq ${EXIT_OK} ]; then
144 log DEBUG "removed wireless device '${device}'"
145 else
146 log ERROR "could not remove wireless device '${device}': ${ret}"
147 fi
148
149 return ${ret}
d76f5107
MT
150}
151
1c6a4e30 152wireless_get_reg_domain() {
31670741
MT
153 # Returns the country code for the wireless device.
154 # Defaults to 00 = world if unset.
155 print "${WIRELESS_REGULATORY_DOMAIN:-00}"
156}
157
1c6a4e30 158wireless_init_reg_domain() {
31670741
MT
159 local country_code="$(wireless_get_reg_domain)"
160
06a6f01e 161 wireless_set_reg_domain "${country_code}" --no-reset
31670741
MT
162}
163
1c6a4e30 164wireless_set_reg_domain() {
06a6f01e
MT
165 local country_code
166 local reset="true"
167
168 while [ $# -gt 0 ]; do
169 case "${1}" in
170 --no-reset)
171 reset="false"
172 ;;
173 -*)
174 log ERROR "Ignoring invalid option: ${1}"
175 ;;
176 *)
177 country_code="${1}"
178 ;;
179 esac
180 shift
181 done
182
ab9e0fd0
MT
183 # Check if configuration value is valid
184 if ! wireless_valid_reg_domain "${country_code}"; then
185 log ERROR "Invalid wireless regulatory domain: ${country_code}"
186 return ${EXIT_ERROR}
187 fi
31670741
MT
188
189 # Before the wireless reg domain is set, it helps to reset to 00 first.
06a6f01e
MT
190 if enabled reset; then
191 iw reg set 00 &>/dev/null
192 fi
31670741
MT
193
194 log INFO "Setting wireless regulatory domain country to '${country_code}'"
195 iw reg set "${country_code}"
196}
197
ab9e0fd0
MT
198wireless_valid_reg_domain() {
199 local country_code="${1}"
200
201 # Empty country codes are invalid
202 isset country_code || return ${EXIT_FALSE}
203
204 local valid_country_codes="$(wireless_list_reg_domains)"
205
206 if list_match "${country_code}" ${valid_country_codes}; then
207 return ${EXIT_TRUE}
208 fi
209
210 return ${EXIT_FALSE}
211}
212
213wireless_list_reg_domains() {
214 if [ ! -r "${WIRELESS_REGULATORY_DOMAIN_DATABASE}" ]; then
215 log ERROR "Could not read ${WIRELESS_REGULATORY_DOMAIN_DATABASE}"
216 return ${EXIT_ERROR}
217 fi
218
219 local line
220 while read line; do
221 # Check if line starts with "country"
222 [ "${line:0:7}" = "country" ] || continue
223
224 # Print country code
225 print "${line:8:2}"
226 done <<< "$(regdbdump ${WIRELESS_REGULATORY_DOMAIN_DATABASE})"
227}
228
f8b7f135 229# http://en.wikipedia.org/wiki/List_of_WLAN_channels
1c6a4e30 230wireless_channel_to_frequency() {
91987cc5 231 local channel=${1}
91987cc5 232
f8b7f135
MT
233 # Works only for valid channel numbers
234 if ! wireless_channel_is_valid "${channel}"; then
235 log ERROR "Invalid wireless channel: ${channel}"
236 return ${EXIT_ERROR}
237 fi
91987cc5
MT
238
239 # 2.4 GHz band
240 case "${channel}" in
241 [123456789]|1[0123])
242 print "$(( 2407 + (${channel} * 5)))"
243 return ${EXIT_OK}
244 ;;
245 14)
246 print "2484"
247 return ${EXIT_OK}
248 ;;
249 esac
250
251 # 5 GHz band
252 case "${channel}" in
253 3[68]|4[02468]|5[26]|6[04]|10[048]|11[26]|12[048]|13[26]|14[09]|15[37]|16[15])
254 print "$(( 5000 + (${channel} * 5)))"
255 return ${EXIT_OK}
256 ;;
257 esac
258
259 return ${EXIT_ERROR}
260}
261
97877f23
MT
262wireless_frequency_to_channel() {
263 local frequency=${1}
264
265 assert isinteger frequency
266
267 # Everything that is too high
268 if [ ${frequency} -gt 5825 ]; then
269 return ${EXIT_ERROR}
270
271 # 5 GHz Band
272 elif [ ${frequency} -gt 5000 ]; then
273 (( frequency = frequency - 5000 ))
274
275 # Must be divisible by 5
276 [ "$(( frequency % 5 ))" -ne 0 ] && return ${EXIT_ERROR}
277
278 print "$(( frequency / 5 ))"
279
280 # 2.4 GHz Band - Channel 14
281 elif [ ${frequency} -eq 2484 ]; then
282 print "14"
283
284 # 2.4 GHz Band
285 elif [ ${frequency} -gt 2407 ]; then
286 (( frequency = frequency - 2407 ))
287
288 # Must be divisible by 5
289 [ "$(( frequency % 5 ))" -ne 0 ] && return ${EXIT_ERROR}
290
291 print "$(( frequency / 5 ))"
292
293 # Everything else
294 else
295 return ${EXIT_ERROR}
296 fi
297
298 return ${EXIT_OK}
299}
300
f8b7f135
MT
301wireless_channel_is_valid() {
302 local channel=${1}
303
304 case "${channel}" in
305 # 2.4 GHz Band
306 [123456789]|1[0123]|14)
307 return ${EXIT_TRUE}
308 ;;
309
310 # 5 GHz Band
311 3[68]|4[02468]|5[26]|6[04]|10[048]|11[26]|12[048]|13[26]|14[09]|15[37]|16[15])
312 return ${EXIT_TRUE}
313 ;;
314 esac
315
316 # Invalid channel number given
317 return ${EXIT_FALSE}
318}
319
54094fc7
MT
320wireless_channel_bandwidth_is_valid() {
321 local mode="${1}"
322 assert isset mode
323
324 local bandwidth="${2}"
325 assert isset bandwidth
326
327 local bandwidths="${WIRELESS_CHANNEL_BANDWIDTHS["${mode}"]}"
328
329 list_match "${bandwidth}" ${bandwidths}
330}
331
109884be
MT
332wireless_channel_is_ht40_plus() {
333 local channel="${1}"
334 assert isinteger channel
335
336 # 2.4 GHz
337 if [ ${channel} -le 6 ]; then
338 return ${EXIT_TRUE}
339 fi
340
341 return ${EXIT_FALSE}
342}
343
344wireless_channel_is_ht40_minus() {
345 local channel="${1}"
346 assert isinteger channel
347
348 # 2.4 GHz
349 if [ ${channel} -ge 6 ]; then
350 return ${EXIT_TRUE}
351 fi
352
353 return ${EXIT_FALSE}
354}
355
1c6a4e30 356wireless_set_channel() {
109884be
MT
357 local device="${1}"
358 local channel="${2}"
359 local bandwidth="${3}"
d76f5107 360
f8b7f135
MT
361 # Check if the device exists
362 if ! device_exists "${device}"; then
363 log ERROR "No such device: ${device}"
364 return ${EXIT_ERROR}
365 fi
366
367 # Check if the channel number is valid
368 if ! wireless_channel_is_valid "${channel}"; then
369 log ERROR "Invalid wireless channel: ${channel}"
370 return ${EXIT_ERROR}
371 fi
22a61046 372
109884be
MT
373 local ht_flag
374 if [ "${bandwidth}" = "auto" ]; then
375 local phy="$(device_get_phy "${device}")"
376
377 # Offset of a 40 MHz channel
378 local ht_offset=5
379
380 if wireless_channel_is_ht40_plus "${channel}" \
381 && phy_supports_ht_capability "${phy}" "HT40+" \
382 && phy_supports_channel "${phy}" $(( channel + ht_offset )); then
383 ht_flag="HT40+"
384
385 elif wireless_channel_is_ht40_minus "${channel}" \
386 && phy_supports_ht_capability "${phy}" "HT40-" \
387 && phy_supports_channel "${phy}" $(( channel - ht_offset )); then
388 ht_flags="HT40-"
389 fi
390 fi
391
5a38ea84 392 log DEBUG "Setting wireless channel on device '${device}' to channel '${channel}'"
109884be 393 cmd iw dev "${device}" set channel "${channel}" "${ht_flag}"
d76f5107 394}
91987cc5 395
549c5b97
MT
396wireless_pre_shared_key_is_valid() {
397 local encryption_mode="${1}"
398 local psk="${2}"
399
400 # Length of the PSK
401 local l="${#psk}"
402
403 case "${encryption_mode}" in
404 # For WPA*, the key must be between 8 and 63 chars
405 WPA2-PSK|WPA2-PSK-SHA256|WPA-PSK|WPA-PSK-SHA256)
406 if [ ${l} -ge 8 ] && [ ${l} -le 63 ]; then
407 return ${EXIT_TRUE}
408 fi
409
410 return ${EXIT_FALSE}
411 ;;
549c5b97
MT
412 esac
413
414 return ${EXIT_ERROR}
415}
416
607481ac
MT
417wireless_client_is_connected() {
418 local device="${1}"
419
420 device_has_carrier "${device}"
421}
422
1c6a4e30 423wireless_ibss_join() {
91987cc5
MT
424 local device=${1}
425 assert isset device
426 shift
427
428 local bssid
429 local essid
430 local frequency
431
432 while [ $# -gt 0 ]; do
433 case "${1}" in
434 --bssid=*)
2212045f 435 bssid="$(cli_get_val "${1}")"
91987cc5
MT
436 ;;
437 --essid=*)
2212045f 438 essid="$(cli_get_val "${1}")"
91987cc5
MT
439 ;;
440 --channel=*)
2212045f 441 local channel="$(cli_get_val "${1}")"
91987cc5
MT
442
443 # Save the frequency of the channel instead
444 # of the channel itself.
445 if isset channel; then
446 frequency="$(wireless_channel_to_frequency ${channel})"
447 fi
448 ;;
449 esac
450 shift
451 done
452
453 # Check input.
454 assert ismac bssid
455 assert isset essid
456 assert isinteger frequency
457
458 # Set device up.
459 device_set_up "${device}"
460
461 log INFO "${device} joining ibss network: ${essid} (${bssid})"
462 cmd_quiet iw dev "${device}" ibss join "${essid}" \
463 "${frequency}" fixed-freq "${bssid}"
464}
465
1c6a4e30 466wireless_ibss_leave() {
91987cc5
MT
467 local device=${1}
468 assert isset device
469
470 log INFO "${device} leaving ibss network"
471 cmd_quiet iw dev "${device}" ibss leave
472}
646ae5b2 473
1c6a4e30 474wireless_is_radar_frequency() {
646ae5b2
MT
475 local frequency="${1}"
476 assert isset frequency
477
478 [[ ${frequency} -ge 5260 ]] && [[ ${frequency} -le 5700 ]]
479}
5a38ea84 480
1c6a4e30 481wireless_monitor() {
5a38ea84
MT
482 local device="${1}"
483 assert isset device
484 shift
485
a23fdc0e 486 local monitor_device="$(port_find_free "${PORT_PATTERN_WIRELESS_MONITOR}")"
5a38ea84
MT
487
488 # Create an 802.11 monitoring device
489 wireless_create "${monitor_device}" --phy="${device}" --type="monitor"
490 local ret=$?
491
492 case "${ret}" in
493 0)
494 # Bring up the device
495 device_set_up "${monitor_device}"
496
497 # Starting tcpdump
498 tcpdump -i "${monitor_device}" "$@"
499
500 # Remove the monitoring interface.
501 wireless_remove "${monitor_device}"
502 ;;
503
504 *)
505 log ERROR "Could not create a monitoring interface on ${device}"
506 return ${EXIT_ERROR}
507 ;;
508 esac
509
510 return ${EXIT_OK}
511}
0e1c630c
MT
512
513wireless_get_ht_caps() {
514 local device="${1}"
515 assert isset device
516
517 local phy="$(device_get_phy "${device}")"
518 if ! isset phy; then
519 log ERROR "Could not determine PHY for ${device}"
520 return ${EXIT_ERROR}
521 fi
522
523 network-phy-list-ht-caps "${phy}"
524}
1526e219
MT
525
526wireless_get_vht_caps() {
527 local device="${1}"
528 assert isset device
529
530 local phy="$(device_get_phy "${device}")"
531 if ! isset phy; then
532 log ERROR "Could not determine PHY for ${device}"
533 return ${EXIT_ERROR}
534 fi
535
536 network-phy-list-vht-caps "${phy}"
537}
dc6d97fb 538
1b4aa2ca
MT
539wireless_supports_acs() {
540 local device="${1}"
541 assert isset device
542
543 local phy="$(device_get_phy "${device}")"
544 if ! isset phy; then
545 log ERROR "Could not determine PHY for ${device}"
546 return ${EXIT_ERROR}
547 fi
548
549 phy_supports_acs "${phy}"
550}
551
dc6d97fb
MT
552wireless_supports_dfs() {
553 local device="${1}"
554 assert isset device
555
556 local phy="$(device_get_phy "${device}")"
557 if ! isset phy; then
558 log ERROR "Could not determine PHY for ${device}"
559 return ${EXIT_ERROR}
560 fi
561
562 phy_supports_dfs "${phy}"
563}