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