From 549a16f5b1437d437ccc873ded500b8cecd03c28 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Sat, 15 Jun 2024 21:40:04 +0200 Subject: wireless: T6318: move country-code to a system wide configuration Wireless devices are subject to regulations issued by authorities. For any given AP or router, there will most likely be no case where one wireless NIC is located in one country and another wireless NIC in the same device is located in another country, resulting in different regulatory domains to apply to the same box. Currently, wireless regulatory domains in VyOS need to be configured per-NIC: set interfaces wireless wlan0 country-code us This leads to several side-effects: * When operating multiple WiFi NICs, they all can have different regulatory domains configured which might offend legislation. * Some NICs need additional entries to /etc/modprobe.d/cfg80211.conf to apply regulatory domain settings, such as: "options cfg80211 ieee80211_regdom=US" This is true for the Compex WLE600VX. This setting cannot be done per-interface. Migrate the first found wireless module country-code from the wireless interface CLI to: "system wireless country-code" (cherry picked from commit 9e22ab6b2aee48029d3455f65880e45c558cf1da) --- data/config-mode-dependencies/vyos-1x.json | 3 + data/configd-include.json | 1 + .../include/version/interfaces-version.xml.i | 2 +- interface-definitions/interfaces_wireless.xml.in | 20 ------- interface-definitions/system_wireless.xml.in | 36 ++++++++++++ smoketest/config-tests/wireless-basic | 25 ++++++++ smoketest/configs/wireless-basic | 66 ++++++++++++++++++++++ src/conf_mode/interfaces_wireless.py | 11 +++- src/conf_mode/system_wireless.py | 64 +++++++++++++++++++++ src/migration-scripts/interfaces/32-to-33 | 57 +++++++++++++++++++ 10 files changed, 262 insertions(+), 23 deletions(-) create mode 100644 interface-definitions/system_wireless.xml.in create mode 100644 smoketest/config-tests/wireless-basic create mode 100644 smoketest/configs/wireless-basic create mode 100644 src/conf_mode/system_wireless.py create mode 100755 src/migration-scripts/interfaces/32-to-33 diff --git a/data/config-mode-dependencies/vyos-1x.json b/data/config-mode-dependencies/vyos-1x.json index ca4ceb58f..a38635cbe 100644 --- a/data/config-mode-dependencies/vyos-1x.json +++ b/data/config-mode-dependencies/vyos-1x.json @@ -63,5 +63,8 @@ "system_option": { "ip": ["system_ip"], "ipv6": ["system_ipv6"] + }, + "system_wireless": { + "wireless": ["interfaces_wireless"] } } diff --git a/data/configd-include.json b/data/configd-include.json index 420960fe7..b92d58c72 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -104,6 +104,7 @@ "system_task-scheduler.py", "system_timezone.py", "system_update-check.py", +"system_wireless.py", "vpn_ipsec.py", "vpn_l2tp.py", "vpn_openconnect.py", diff --git a/interface-definitions/include/version/interfaces-version.xml.i b/interface-definitions/include/version/interfaces-version.xml.i index 854e60f4e..2915b318e 100644 --- a/interface-definitions/include/version/interfaces-version.xml.i +++ b/interface-definitions/include/version/interfaces-version.xml.i @@ -1,3 +1,3 @@ - + diff --git a/interface-definitions/interfaces_wireless.xml.in b/interface-definitions/interfaces_wireless.xml.in index d69daa485..0a62b3255 100644 --- a/interface-definitions/interfaces_wireless.xml.in +++ b/interface-definitions/interfaces_wireless.xml.in @@ -570,26 +570,6 @@ 0 - - - Indicate country in which device is operating - - 00 ad ae af ai al am an ar as at au aw az ba bb bd be bf bg bh bl bm bn bo br bs bt by bz ca cf ch ci cl cn co cr cu cx cy cz de dk dm do dz ec ee eg es et fi fm fr gb gd ge gf gh gl gp gr gt gu gy hk hn hr ht hu id ie il in ir is it jm jo jp ke kh kn kp kr kw ky kz lb lc li lk ls lt lu lv ma mc md me mf mh mk mn mo mp mq mr mt mu mv mw mx my ng ni nl no np nz om pa pe pf pg ph pk pl pm pr pt pw py qa re ro rs ru rw sa se sg si sk sn sr sv sy tc td tg th tn tr tt tw tz ua ug us uy uz vc ve vi vn vu wf ws ye yt za zw - - - 00 - World regulatory domain - - - txt - ISO/IEC 3166-1 Country Code - - - (00|ad|ae|af|ai|al|am|an|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bl|bm|bn|bo|br|bs|bt|by|bz|ca|cf|ch|ci|cl|cn|co|cr|cu|cx|cy|cz|de|dk|dm|do|dz|ec|ee|eg|es|et|fi|fm|fr|gb|gd|ge|gf|gh|gl|gp|gr|gt|gu|gy|hk|hn|hr|ht|hu|id|ie|il|in|ir|is|it|jm|jo|jp|ke|kh|kn|kp|kr|kw|ky|kz|lb|lc|li|lk|ls|lt|lu|lv|ma|mc|md|me|mf|mh|mk|mn|mo|mp|mq|mr|mt|mu|mv|mw|mx|my|ng|ni|nl|no|np|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pr|pt|pw|py|qa|re|ro|rs|ru|rw|sa|se|sg|si|sk|sn|sr|sv|sy|tc|td|tg|th|tn|tr|tt|tw|tz|ua|ug|us|uy|uz|vc|ve|vi|vn|vu|wf|ws|ye|yt|za|zw) - - Invalid ISO/IEC 3166-1 Country Code - - #include #include #include diff --git a/interface-definitions/system_wireless.xml.in b/interface-definitions/system_wireless.xml.in new file mode 100644 index 000000000..834f8b624 --- /dev/null +++ b/interface-definitions/system_wireless.xml.in @@ -0,0 +1,36 @@ + + + + + + + Wireless (IEEE-802.11) subsystem settings + + 317 + + + + + Indicate country in which device is operating + + 00 ad ae af ai al am an ar as at au aw az ba bb bd be bf bg bh bl bm bn bo br bs bt by bz ca cf ch ci cl cn co cr cu cx cy cz de dk dm do dz ec ee eg es et fi fm fr gb gd ge gf gh gl gp gr gt gu gy hk hn hr ht hu id ie il in ir is it jm jo jp ke kh kn kp kr kw ky kz lb lc li lk ls lt lu lv ma mc md me mf mh mk mn mo mp mq mr mt mu mv mw mx my ng ni nl no np nz om pa pe pf pg ph pk pl pm pr pt pw py qa re ro rs ru rw sa se sg si sk sn sr sv sy tc td tg th tn tr tt tw tz ua ug us uy uz vc ve vi vn vu wf ws ye yt za zw + + + 00 + World regulatory domain + + + txt + ISO/IEC 3166-1 Country Code + + + (00|ad|ae|af|ai|al|am|an|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bl|bm|bn|bo|br|bs|bt|by|bz|ca|cf|ch|ci|cl|cn|co|cr|cu|cx|cy|cz|de|dk|dm|do|dz|ec|ee|eg|es|et|fi|fm|fr|gb|gd|ge|gf|gh|gl|gp|gr|gt|gu|gy|hk|hn|hr|ht|hu|id|ie|il|in|ir|is|it|jm|jo|jp|ke|kh|kn|kp|kr|kw|ky|kz|lb|lc|li|lk|ls|lt|lu|lv|ma|mc|md|me|mf|mh|mk|mn|mo|mp|mq|mr|mt|mu|mv|mw|mx|my|ng|ni|nl|no|np|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pr|pt|pw|py|qa|re|ro|rs|ru|rw|sa|se|sg|si|sk|sn|sr|sv|sy|tc|td|tg|th|tn|tr|tt|tw|tz|ua|ug|us|uy|uz|vc|ve|vi|vn|vu|wf|ws|ye|yt|za|zw) + + Invalid ISO/IEC 3166-1 Country Code + + + + + + + diff --git a/smoketest/config-tests/wireless-basic b/smoketest/config-tests/wireless-basic new file mode 100644 index 000000000..5d7bc8aaa --- /dev/null +++ b/smoketest/config-tests/wireless-basic @@ -0,0 +1,25 @@ +set interfaces ethernet eth0 duplex 'auto' +set interfaces ethernet eth0 speed 'auto' +set interfaces ethernet eth1 duplex 'auto' +set interfaces ethernet eth1 speed 'auto' +set interfaces wireless wlan0 address '192.168.0.1/24' +set interfaces wireless wlan0 channel '1' +set interfaces wireless wlan0 mode 'n' +set interfaces wireless wlan0 security wpa cipher 'CCMP' +set interfaces wireless wlan0 security wpa mode 'wpa2' +set interfaces wireless wlan0 security wpa passphrase '12345678' +set interfaces wireless wlan0 ssid 'VyOS' +set interfaces wireless wlan0 type 'access-point' +set interfaces wireless wlan1 address '192.168.1.1/24' +set interfaces wireless wlan1 channel '2' +set interfaces wireless wlan1 mode 'n' +set interfaces wireless wlan1 ssid 'VyOS-PUBLIC' +set interfaces wireless wlan1 type 'access-point' +set system config-management commit-revisions '200' +set system console device ttyS0 speed 115200 +set system domain-name 'dev.vyos.net' +set system host-name 'WR1' +set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0' +set system wireless country-code 'es' +set system syslog global facility all level 'info' +set system syslog global facility local7 level 'debug' diff --git a/smoketest/configs/wireless-basic b/smoketest/configs/wireless-basic new file mode 100644 index 000000000..9cc34f5bc --- /dev/null +++ b/smoketest/configs/wireless-basic @@ -0,0 +1,66 @@ +interfaces { + ethernet eth0 { + duplex "auto" + speed "auto" + } + ethernet eth1 { + duplex "auto" + speed "auto" + } + wireless wlan0 { + address 192.168.0.1/24 + channel 1 + country-code es + mode n + security { + wpa { + cipher CCMP + mode wpa2 + passphrase 12345678 + } + } + ssid VyOS + type access-point + } + wireless wlan1 { + address 192.168.1.1/24 + channel 2 + country-code de + mode n + ssid VyOS-PUBLIC + type access-point + } +} +system { + config-management { + commit-revisions "200" + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name "dev.vyos.net" + host-name "WR1" + login { + user vyos { + authentication { + encrypted-password "$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0" + } + } + } + syslog { + global { + facility all { + level "info" + } + facility local7 { + level "debug" + } + } + } +} + +// Warning: Do not remove the following line. +// vyos-config-version: "bgp@5:broadcast-relay@1:cluster@2:config-management@1:conntrack@5:conntrack-sync@2:container@2:dhcp-relay@2:dhcp-server@8:dhcpv6-server@1:dns-dynamic@4:dns-forwarding@4:firewall@15:flow-accounting@1:https@6:ids@1:interfaces@32:ipoe-server@3:ipsec@13:isis@3:l2tp@9:lldp@2:mdns@1:monitoring@1:nat@8:nat66@3:ntp@3:openconnect@3:ospf@2:pim@1:policy@8:pppoe-server@10:pptp@5:qos@2:quagga@11:reverse-proxy@1:rip@1:rpki@2:salt@1:snmp@3:ssh@2:sstp@6:system@27:vrf@3:vrrp@4:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2" +// Release version: 1.4.0 diff --git a/src/conf_mode/interfaces_wireless.py b/src/conf_mode/interfaces_wireless.py index 9488f6797..aa65adc10 100755 --- a/src/conf_mode/interfaces_wireless.py +++ b/src/conf_mode/interfaces_wireless.py @@ -48,6 +48,8 @@ hostapd_conf = '/run/hostapd/{ifname}.conf' hostapd_accept_station_conf = '/run/hostapd/{ifname}_station_accept.conf' hostapd_deny_station_conf = '/run/hostapd/{ifname}_station_deny.conf' +country_code_path = ['system', 'wireless', 'country-code'] + def find_other_stations(conf, base, ifname): """ Only one wireless interface per phy can be in station mode - @@ -82,7 +84,11 @@ def get_config(config=None): conf = Config() base = ['interfaces', 'wireless'] - ifname, wifi = get_interface_dict(conf, base) + _, wifi = get_interface_dict(conf, base) + + # retrieve global Wireless regulatory domain setting + if conf.exists(country_code_path): + wifi['country_code'] = conf.return_value(country_code_path) if 'deleted' not in wifi: # then get_interface_dict provides default keys @@ -149,7 +155,8 @@ def verify(wifi): if wifi['type'] == 'access-point': if 'country_code' not in wifi: - raise ConfigError('Wireless country-code is mandatory') + raise ConfigError(f'Wireless country-code is mandatory, use: '\ + f'"set {" ".join(country_code_path)}"!') if 'channel' not in wifi: raise ConfigError('Wireless channel must be configured!') diff --git a/src/conf_mode/system_wireless.py b/src/conf_mode/system_wireless.py new file mode 100644 index 000000000..e0ca0ab8e --- /dev/null +++ b/src/conf_mode/system_wireless.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from sys import exit + +from vyos.config import Config +from vyos.configdep import set_dependents +from vyos.configdep import call_dependents +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['system', 'wireless'] + interface_base = ['interfaces', 'wireless'] + + wireless = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) + + + if conf.exists(interface_base): + wireless['interfaces'] = conf.list_nodes(interface_base) + for interface in wireless['interfaces']: + set_dependents('wireless', conf, interface) + + return wireless + +def verify(wireless): + pass + +def generate(wireless): + pass + +def apply(wireless): + if 'interfaces' in wireless: + call_dependents() + pass + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/migration-scripts/interfaces/32-to-33 b/src/migration-scripts/interfaces/32-to-33 new file mode 100755 index 000000000..caf588474 --- /dev/null +++ b/src/migration-scripts/interfaces/32-to-33 @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# T6318: WiFi country-code should be set system-wide instead of per-device + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree + +if len(argv) < 2: + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['interfaces', 'wireless'] + +config = ConfigTree(config_file) +if not config.exists(base): + # Nothing to do + exit(0) + +installed = False +for interface in config.list_nodes(base): + cc_path = base + [interface, 'country-code'] + if config.exists(cc_path): + tmp = config.return_value(cc_path) + config.delete(cc_path) + + # There can be only ONE wireless country-code per device, everything + # else makes no sense as a WIFI router can not operate in two + # different countries + if not installed: + config.set(['system', 'wireless', 'country-code'], value=tmp) + installed = True + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) -- cgit v1.2.3