]> git.ipfire.org Git - people/stevee/network.git/blob - src/functions/functions.wireless
Rename NETWORK_WIRELESS_NETWORK_DIR to NETWORK_WIRELESS_NETWORKS_DIR
[people/stevee/network.git] / src / functions / functions.wireless
1 #!/bin/bash
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2012 IPFire Network Development Team #
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 ###############################################################################
21
22 # Sets the global wireless country code. Default is 00 = world.
23 WIRELESS_REGULATORY_DOMAIN="00"
24 NETWORK_SETTINGS_FILE_PARAMS="${NETWORK_SETTINGS_FILE_PARAMS} WIRELESS_REGULATORY_DOMAIN"
25
26 WIRELESS_REGULATORY_DOMAIN_DATABASE="/usr/lib/crda/regulatory.bin"
27
28 WIRELESS_NETWORK_CONFIG_SETTINGS="\
29 SSID \
30 ENCRYPTION_MODE \
31 KEY \
32 PRIORITY"
33
34 WIRELESS_NETWORKS_VALID_ENCRYPTION_MODES="WPA2-PSK"
35
36 wireless_create() {
37 local device=${1}
38 assert isset device
39 shift
40
41 local address
42 local channel
43 local phy
44 local type="managed"
45
46 while [ $# -gt 0 ]; do
47 case "${1}" in
48 --address=*)
49 address=$(cli_get_val "${1}")
50 ;;
51 --channel=*)
52 channel=$(cli_get_val "${1}")
53 ;;
54 --phy=*)
55 phy=$(cli_get_val "${1}")
56 phy=$(phy_get ${phy})
57 ;;
58 --type=*)
59 type=$(cli_get_val "${1}")
60
61 # ap --> __ap
62 [ "${type}" = "ap" ] && type="__ap"
63 ;;
64 *)
65 error "Unrecognized argument: ${1}"
66 return ${EXIT_ERROR}
67 ;;
68 esac
69 shift
70 done
71
72 case "${type}" in
73 ibss|managed|monitor|__ap)
74 ;;
75 mesh-point)
76 type="mp"
77 ;;
78 *)
79 log ERROR "Unknown type: ${type}"
80 return ${EXIT_ERROR}
81 ;;
82
83 esac
84
85 assert phy_exists ${phy}
86 isset address || address=$(mac_generate)
87
88 cmd_quiet iw phy ${phy} interface add ${device} type ${type}
89 local ret=$?
90
91 if [ ${ret} -eq ${EXIT_OK} ]; then
92 log DEBUG "created wireless device '${device}' (${type})"
93
94 if isset address; then
95 device_set_address ${device} ${address}
96 fi
97 else
98 log ERROR "could not create wireless device '${device}' (${type}): ${ret}"
99 fi
100
101 # Set the channel
102 if isset channel; then
103 wireless_set_channel "${device}" "${channel}" || return $?
104 fi
105
106 return ${ret}
107 }
108
109 wireless_remove() {
110 local device=${1}
111 assert isset device
112
113 if ! device_exists ${device}; then
114 return ${EXIT_OK}
115 fi
116
117 # Tear down the device (if necessary).
118 device_set_down ${device}
119
120 # Remove it.
121 cmd_quiet iw dev ${device} del
122 local ret=$?
123
124 if [ ${ret} -eq ${EXIT_OK} ]; then
125 log DEBUG "removed wireless device '${device}'"
126 else
127 log ERROR "could not remove wireless device '${device}': ${ret}"
128 fi
129
130 return ${ret}
131 }
132
133 wireless_get_reg_domain() {
134 # Returns the country code for the wireless device.
135 # Defaults to 00 = world if unset.
136 print "${WIRELESS_REGULATORY_DOMAIN:-00}"
137 }
138
139 wireless_init_reg_domain() {
140 local country_code="$(wireless_get_reg_domain)"
141
142 wireless_set_reg_domain "${country_code}" --no-reset
143 }
144
145 wireless_set_reg_domain() {
146 local country_code
147 local reset="true"
148
149 while [ $# -gt 0 ]; do
150 case "${1}" in
151 --no-reset)
152 reset="false"
153 ;;
154 -*)
155 log ERROR "Ignoring invalid option: ${1}"
156 ;;
157 *)
158 country_code="${1}"
159 ;;
160 esac
161 shift
162 done
163
164 # Check if configuration value is valid
165 if ! wireless_valid_reg_domain "${country_code}"; then
166 log ERROR "Invalid wireless regulatory domain: ${country_code}"
167 return ${EXIT_ERROR}
168 fi
169
170 # Before the wireless reg domain is set, it helps to reset to 00 first.
171 if enabled reset; then
172 iw reg set 00 &>/dev/null
173 fi
174
175 log INFO "Setting wireless regulatory domain country to '${country_code}'"
176 iw reg set "${country_code}"
177 }
178
179 wireless_valid_reg_domain() {
180 local country_code="${1}"
181
182 # Empty country codes are invalid
183 isset country_code || return ${EXIT_FALSE}
184
185 local valid_country_codes="$(wireless_list_reg_domains)"
186
187 if list_match "${country_code}" ${valid_country_codes}; then
188 return ${EXIT_TRUE}
189 fi
190
191 return ${EXIT_FALSE}
192 }
193
194 wireless_list_reg_domains() {
195 if [ ! -r "${WIRELESS_REGULATORY_DOMAIN_DATABASE}" ]; then
196 log ERROR "Could not read ${WIRELESS_REGULATORY_DOMAIN_DATABASE}"
197 return ${EXIT_ERROR}
198 fi
199
200 local line
201 while read line; do
202 # Check if line starts with "country"
203 [ "${line:0:7}" = "country" ] || continue
204
205 # Print country code
206 print "${line:8:2}"
207 done <<< "$(regdbdump ${WIRELESS_REGULATORY_DOMAIN_DATABASE})"
208 }
209
210 # http://en.wikipedia.org/wiki/List_of_WLAN_channels
211 wireless_channel_to_frequency() {
212 local channel=${1}
213
214 # Works only for valid channel numbers
215 if ! wireless_channel_is_valid "${channel}"; then
216 log ERROR "Invalid wireless channel: ${channel}"
217 return ${EXIT_ERROR}
218 fi
219
220 # 2.4 GHz band
221 case "${channel}" in
222 [123456789]|1[0123])
223 print "$(( 2407 + (${channel} * 5)))"
224 return ${EXIT_OK}
225 ;;
226 14)
227 print "2484"
228 return ${EXIT_OK}
229 ;;
230 esac
231
232 # 5 GHz band
233 case "${channel}" in
234 3[68]|4[02468]|5[26]|6[04]|10[048]|11[26]|12[048]|13[26]|14[09]|15[37]|16[15])
235 print "$(( 5000 + (${channel} * 5)))"
236 return ${EXIT_OK}
237 ;;
238 esac
239
240 return ${EXIT_ERROR}
241 }
242
243 wireless_frequency_to_channel() {
244 local frequency=${1}
245
246 assert isinteger frequency
247
248 # Everything that is too high
249 if [ ${frequency} -gt 5825 ]; then
250 return ${EXIT_ERROR}
251
252 # 5 GHz Band
253 elif [ ${frequency} -gt 5000 ]; then
254 (( frequency = frequency - 5000 ))
255
256 # Must be divisible by 5
257 [ "$(( frequency % 5 ))" -ne 0 ] && return ${EXIT_ERROR}
258
259 print "$(( frequency / 5 ))"
260
261 # 2.4 GHz Band - Channel 14
262 elif [ ${frequency} -eq 2484 ]; then
263 print "14"
264
265 # 2.4 GHz Band
266 elif [ ${frequency} -gt 2407 ]; then
267 (( frequency = frequency - 2407 ))
268
269 # Must be divisible by 5
270 [ "$(( frequency % 5 ))" -ne 0 ] && return ${EXIT_ERROR}
271
272 print "$(( frequency / 5 ))"
273
274 # Everything else
275 else
276 return ${EXIT_ERROR}
277 fi
278
279 return ${EXIT_OK}
280 }
281
282 wireless_channel_is_valid() {
283 local channel=${1}
284
285 case "${channel}" in
286 # 2.4 GHz Band
287 [123456789]|1[0123]|14)
288 return ${EXIT_TRUE}
289 ;;
290
291 # 5 GHz Band
292 3[68]|4[02468]|5[26]|6[04]|10[048]|11[26]|12[048]|13[26]|14[09]|15[37]|16[15])
293 return ${EXIT_TRUE}
294 ;;
295 esac
296
297 # Invalid channel number given
298 return ${EXIT_FALSE}
299 }
300
301 wireless_set_channel() {
302 local device=${1}
303 local channel=${2}
304
305 # Check if the device exists
306 if ! device_exists "${device}"; then
307 log ERROR "No such device: ${device}"
308 return ${EXIT_ERROR}
309 fi
310
311 # Check if the channel number is valid
312 if ! wireless_channel_is_valid "${channel}"; then
313 log ERROR "Invalid wireless channel: ${channel}"
314 return ${EXIT_ERROR}
315 fi
316
317 log DEBUG "Setting wireless channel on device '${device}' to channel '${channel}'"
318 cmd iw dev "${device}" set channel "${channel}"
319 }
320
321 wireless_ibss_join() {
322 local device=${1}
323 assert isset device
324 shift
325
326 local bssid
327 local essid
328 local frequency
329
330 while [ $# -gt 0 ]; do
331 case "${1}" in
332 --bssid=*)
333 bssid="$(cli_get_val "${1}")"
334 ;;
335 --essid=*)
336 essid="$(cli_get_val "${1}")"
337 ;;
338 --channel=*)
339 local channel="$(cli_get_val "${1}")"
340
341 # Save the frequency of the channel instead
342 # of the channel itself.
343 if isset channel; then
344 frequency="$(wireless_channel_to_frequency ${channel})"
345 fi
346 ;;
347 esac
348 shift
349 done
350
351 # Check input.
352 assert ismac bssid
353 assert isset essid
354 assert isinteger frequency
355
356 # Set device up.
357 device_set_up "${device}"
358
359 log INFO "${device} joining ibss network: ${essid} (${bssid})"
360 cmd_quiet iw dev "${device}" ibss join "${essid}" \
361 "${frequency}" fixed-freq "${bssid}"
362 }
363
364 wireless_ibss_leave() {
365 local device=${1}
366 assert isset device
367
368 log INFO "${device} leaving ibss network"
369 cmd_quiet iw dev "${device}" ibss leave
370 }
371
372 wireless_is_radar_frequency() {
373 local frequency="${1}"
374 assert isset frequency
375
376 [[ ${frequency} -ge 5260 ]] && [[ ${frequency} -le 5700 ]]
377 }
378
379 wireless_monitor() {
380 local device="${1}"
381 assert isset device
382 shift
383
384 local monitor_device="$(port_find_free "${PORT_PATTERN_WIRELESS_MONITOR}")"
385
386 # Create an 802.11 monitoring device
387 wireless_create "${monitor_device}" --phy="${device}" --type="monitor"
388 local ret=$?
389
390 case "${ret}" in
391 0)
392 # Bring up the device
393 device_set_up "${monitor_device}"
394
395 # Starting tcpdump
396 tcpdump -i "${monitor_device}" "$@"
397
398 # Remove the monitoring interface.
399 wireless_remove "${monitor_device}"
400 ;;
401
402 *)
403 log ERROR "Could not create a monitoring interface on ${device}"
404 return ${EXIT_ERROR}
405 ;;
406 esac
407
408 return ${EXIT_OK}
409 }
410
411 cli_wireless() {
412 local action=${1}
413 shift 1
414
415 case "${action}" in
416 network)
417 cli_wireless_network "$@"
418 ;;
419 *)
420 error "Unrecognized argument: ${action}"
421 exit ${EXIT_ERROR}
422 ;;
423 esac
424 }
425
426 cli_wireless_network() {
427 if wireless_network_exists "${1}"; then
428 local ssid="${1}"
429 local key="${2}"
430 key=${key//-/_}
431 shift 2
432
433 case "${key}" in
434 encryption_mode|key|priority)
435 wireless_network_${key} "${ssid}" "$@"
436 ;;
437 show)
438 wireless_network_show "${ssid}"
439 exit $?
440 ;;
441 *)
442 error "Unrecognized argument: ${key}"
443 exit ${EXIT_ERROR}
444 ;;
445 esac
446 else
447 local action=${1}
448 shift
449
450 case "${action}" in
451 new)
452 wireless_network_new "$@"
453 ;;
454 destroy)
455 wireless_network_destroy "$@"
456 ;;
457 ""|*)
458 if [ -n "${action}" ]; then
459 error "Unrecognized argument: '${action}'"
460 fi
461 exit ${EXIT_ERROR}
462 ;;
463 esac
464 fi
465 }
466
467 # This function writes all values to a via ${ssid} specificated wireless network configuration file
468 wireless_network_write_config() {
469 assert [ $# -ge 1 ]
470
471 local ssid="${1}"
472
473 local ssid_hash="$(wireless_network_hash "${ssid}")"
474 assert isset ssid_hash
475
476 if ! wireless_network_exists "${ssid}"; then
477 log ERROR "No such wireless network: '${ssid}'"
478 return ${EXIT_ERROR}
479 fi
480
481 local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings"
482
483 if ! settings_write "${path}" ${WIRELESS_NETWORK_CONFIG_SETTINGS}; then
484 log ERROR "Could not write configuration settings for wireless network ${ssid}"
485 return ${EXIT_ERROR}
486 fi
487
488 # When we get here the writing of the config file was successful
489 return ${EXIT_OK}
490 }
491
492 # This funtion writes the value for one key to a via ${ssid} specificated
493 # wireless network configuration file
494 wireless_network_write_config_key() {
495 assert [ $# -ge 3 ]
496
497 local ssid="${1}"
498 local key="${2}"
499 shift 2
500
501 local value="$@"
502
503 if ! wireless_network_exists "${ssid}"; then
504 log ERROR "No such wireless network: ${ssid}"
505 return ${EXIT_ERROR}
506 fi
507
508 log DEBUG "Set '${key}' to new value '${value}' in wireless network '${ssid}'"
509
510 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
511
512 # Read the config settings
513 if ! wireless_network_read_config "${ssid}"; then
514 return ${EXIT_ERROR}
515 fi
516
517 # Set the key to a new value
518 assign "${key}" "${value}"
519
520 if ! wireless_network_write_config "${ssid}"; then
521 return ${EXIT_ERROR}
522 fi
523
524 return ${EXIT_OK}
525 }
526
527 # Reads one or more keys out of a settings file or all if no key is provided.
528 wireless_network_read_config() {
529 assert [ $# -ge 1 ]
530
531 local ssid="${1}"
532 shift 1
533
534 local ssid_hash="$(wireless_network_hash "${ssid}")"
535 assert isset ssid_hash
536
537 if ! wireless_network_exists "${ssid}"; then
538 log ERROR "No such wireless network : ${ssid}"
539 return ${EXIT_ERROR}
540 fi
541
542 local args
543 if [ $# -eq 0 ] && [ -n "${WIRELESS_NETWORK_CONFIG_SETTINGS}" ]; then
544 list_append args ${WIRELESS_NETWORK_CONFIG_SETTINGS}
545 else
546 list_append args "$@"
547 fi
548
549 local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings"
550
551 if ! settings_read "${path}" ${args}; then
552 log ERROR "Could not read settings for wireless network ${ssid}"
553 return ${EXIT_ERROR}
554 fi
555 }
556
557 # This function checks if a wireless network exists
558 # Returns True when yes and false when not
559 wireless_network_exists() {
560 assert [ $# -eq 1 ]
561
562 local ssid="${1}"
563 local ssid_hash="$(wireless_network_hash "${ssid}")"
564 assert isset ssid_hash
565
566 local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}"
567
568 # We cannot use wireless_network_read_config here beacuse we would end in a loop
569 local SSID
570
571 local path_settings="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings"
572
573 if ! settings_read "${path_settings}" SSID; then
574 return ${EXIT_FALSE}
575 fi
576
577 assert isset SSID
578
579 if [ -d "${path}" ] && [[ "${ssid}" = "${SSID}" ]]; then
580 return ${EXIT_TRUE}
581 else
582 return ${EXIT_FALSE}
583 fi
584 }
585
586 wireless_network_hash() {
587 assert [ $# -eq 1 ]
588
589 local string="${1}"
590
591 local hash=$(echo -n "${string}" | md5sum )
592 hash=${hash%% -}
593
594 local path="${NETWORK_WIRELESS_NETWORKS_DIR}/*${hash}"
595
596 if [ -d "${path}" ]; then
597 basename "${path}"
598 else
599 local normalized=$(normalize "${string}")
600 normalized=${normalized%-}
601 echo "${normalized}-${hash}"
602 fi
603 }
604
605 wireless_network_new() {
606 if [ $# -gt 1 ]; then
607 error "Too many arguments"
608 return ${EXIT_ERROR}
609 fi
610
611 local ssid="${1}"
612 if ! isset ssid; then
613 error "Please provide a SSID"
614 return ${EXIT_ERROR}
615 fi
616
617 local ssid_hash="$(wireless_network_hash "${ssid}")"
618 assert isset ssid_hash
619
620 # Check for duplicates
621 if wireless_network_exists "${ssid}"; then
622 error "The wireless network ${ssid} already exists"
623 return ${EXIT_ERROR}
624 fi
625
626 log DEBUG "Creating wireless network '${ssid}'"
627
628 if ! mkdir -p "${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}"; then
629 log ERROR "Could not create config directory for wireless network ${ssid}"
630 return ${EXIT_ERROR}
631 fi
632
633 # When the ssid is not set in the settings file we cannot write it because wireless_network_exists fails
634 echo "SSID=\"${ssid}\"" >>"${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings"
635
636 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
637 SSID="${ssid}"
638 PRIORITY=500
639
640 if ! wireless_network_write_config "${ssid}"; then
641 log ERROR "Could not write new config file"
642 return ${EXIT_ERROR}
643 fi
644 }
645
646 # Function that deletes based on the passed parameters
647 # one ore more wireless networks
648 wireless_network_destroy() {
649 local ssid
650 for ssid in "$@"; do
651 local ssid_hash="$(wireless_network_hash "${ssid}")"
652 assert isset ssid_hash
653
654 if ! wireless_network_exists "${ssid}"; then
655 log ERROR "The wireless network ${ssid} does not exist."
656 continue
657 fi
658
659 log DEBUG "Deleting wireless network ${ssid}"
660
661 if ! rm -rf "${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}"; then
662 log ERROR "Deleting the wireless network ${ssid} was not sucessful"
663 return ${EXIT_ERROR}
664 fi
665 done
666 }
667
668 wireless_network_encryption_mode() {
669 if [ ! $# -eq 2 ]; then
670 log ERROR "Not enough arguments"
671 return ${EXIT_ERROR}
672 fi
673 local ssid="${1}"
674 local mode="${2}"
675
676 if ! isoneof mode ${WIRELESS_NETWORKS_VALID_ENCRYPTION_MODES}; then
677 log ERROR "Encryption mode '${mode}' is invalid"
678 return ${EXIT_ERROR}
679 fi
680
681 if ! wireless_network_write_config_key "${ssid}" "ENCRYPTION_MODE" ${mode^^}; then
682 log ERROR "Could not write configuration settings"
683 return ${EXIT_ERROR}
684 fi
685 }
686
687 wireless_network_key() {
688 if [ ! $# -eq 2 ]; then
689 log ERROR "Not enough arguments"
690 return ${EXIT_ERROR}
691 fi
692 local ssid="${1}"
693 local key="${2}"
694
695 if ! wireless_network_write_config_key "${ssid}" "KEY" "${key}"; then
696 log ERROR "Could not write configuration settings"
697 return ${EXIT_ERROR}
698 fi
699 }
700
701 wireless_network_priority() {
702 if [ ! $# -eq 2 ]; then
703 log ERROR "Not enough arguments"
704 return ${EXIT_ERROR}
705 fi
706 local ssid="${1}"
707 local priority=${2}
708
709 if ! isinteger priority && [ ! ${priority} -ge 0 ]; then
710 log ERROR "The priority must be an integer greater or eqal zero"
711 return ${EXIT_ERROR}
712 fi
713
714 if ! wireless_network_write_config_key "${ssid}" "PRIORITY" "${priority}"; then
715 log ERROR "Could not write configuration settings"
716 return ${EXIT_ERROR}
717 fi
718 }