summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/configd-include.json3
-rw-r--r--data/templates/wifi/hostapd.conf.tmpl42
-rw-r--r--data/templates/wifi/wpa_supplicant.conf.tmpl70
-rw-r--r--interface-definitions/interfaces-wireless.xml.in69
-rw-r--r--python/vyos/ifconfig/bridge.py4
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py80
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py8
-rwxr-xr-xsrc/conf_mode/system-wifi-regdom.py90
-rwxr-xr-xsrc/migration-scripts/interfaces/13-to-1461
9 files changed, 276 insertions, 151 deletions
diff --git a/data/configd-include.json b/data/configd-include.json
index 95aef65ad..da6fb915f 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -48,7 +48,6 @@
"system-options.py",
"system-syslog.py",
"system-timezone.py",
-"system-wifi-regdom.py",
"system_console.py",
"system_lcd.py",
"task_scheduler.py",
@@ -59,4 +58,4 @@
"vrf.py",
"vrrp.py",
"vyos_cert.py"
-] \ No newline at end of file
+]
diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl
index c5e4240d1..16d9f7c98 100644
--- a/data/templates/wifi/hostapd.conf.tmpl
+++ b/data/templates/wifi/hostapd.conf.tmpl
@@ -23,7 +23,10 @@ interface={{ ifname }}
# added to the bridge automatically (brctl may refuse to do this before hostapd
# has been started to change the interface mode). If needed, the bridge
# interface is also created.
-bridge={{ is_bridge_member }}
+{# as there can only be one bridge interface it is save to loop #}
+{% for bridge in is_bridge_member %}
+bridge={{ bridge }}
+{% endfor %}
{% endif %}
# Driver interface type (hostap/wired/none/nl80211/bsd);
@@ -69,18 +72,18 @@ ssid={{ ssid }}
channel={{ channel }}
{% endif %}
-{% if mode %}
+{% if mode is defined and mode is not none %}
# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
-# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
-# to be set to hw_mode=a. When using ACS (see channel parameter), a
+# needs to be set to hw_mode a. For IEEE 802.11ax (HE) on 6 GHz this needs
+# to be set to hw_mode a. When using ACS (see channel parameter), a
# special value "any" can be used to indicate that any support band can be used.
# This special case is currently supported only with drivers with which
# offloaded ACS is used.
-{% if 'n' in mode %}
+{% if mode == 'n' %}
hw_mode=g
-{% elif 'ac' in mode %}
+{% elif mode == 'ac' %}
hw_mode=a
ieee80211h=1
ieee80211ac=1
@@ -529,10 +532,13 @@ wep_key{{ loop.index -1 }}={{ security.wep.key }}
# and/or WPA2 (full IEEE 802.11i/RSN):
# bit0 = WPA
# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2.
+# In other words, for WPA3, wpa 2 is used the configuration (and
+# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
{% if security.wpa.mode is defined %}
-{% if security.wpa.mode == 'both' %}
+{% if security.wpa.mode == 'wpa+wpa2' %}
wpa=3
-{% elif security.wpa.mode == 'wpa2' %}
+{% elif security.wpa.mode == 'wpa2' or security.wpa.mode == 'wpa3' %}
wpa=2
{% elif security.wpa.mode == 'wpa' %}
wpa=1
@@ -592,7 +598,15 @@ wpa_passphrase={{ security.wpa.passphrase }}
# added to enable SHA256-based stronger algorithms.
# WPA-PSK = WPA-Personal / WPA2-Personal
# WPA-PSK-SHA256 = WPA2-Personal using SHA256
-wpa_key_mgmt=WPA-PSK
+# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
+# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
+# SAE = SAE (WPA3-Personal)
+# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa3' %}
+wpa_key_mgmt=SAE
+{% else %}
+wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256
+{% endif %}
{% elif security.wpa.radius is defined %}
##### IEEE 802.1X-2004 related configuration ##################################
@@ -602,9 +616,17 @@ ieee8021x=1
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
# added to enable SHA256-based stronger algorithms.
+# WPA-PSK = WPA-Personal / WPA2-Personal
+# WPA-PSK-SHA256 = WPA2-Personal using SHA256
# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
-wpa_key_mgmt=WPA-EAP
+# SAE = SAE (WPA3-Personal)
+# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa3' %}
+wpa_key_mgmt=WPA-EAP-SUITE-B-192
+{% else %}
+wpa_key_mgmt=WPA-EAP WPA-EAP-SHA256
+{% endif %}
{% if security.wpa.radius.server is defined %}
# RADIUS client forced local IP address for the access point
diff --git a/data/templates/wifi/wpa_supplicant.conf.tmpl b/data/templates/wifi/wpa_supplicant.conf.tmpl
index f84892dc0..20b4f7976 100644
--- a/data/templates/wifi/wpa_supplicant.conf.tmpl
+++ b/data/templates/wifi/wpa_supplicant.conf.tmpl
@@ -4,10 +4,78 @@
# https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf
network={
+ # ssid: SSID (mandatory); network name in one of the optional formats:
+ # - an ASCII string with double quotation
+ # - a hex string (two characters per octet of SSID)
+ # - a printf-escaped ASCII string P"<escaped string>"
+ #
ssid="{{ ssid }}"
+
+ # scan_ssid:
+ # 0 = do not scan this SSID with specific Probe Request frames (default)
+ # 1 = scan with SSID-specific Probe Request frames (this can be used to
+ # find APs that do not accept broadcast SSID or use multiple SSIDs;
+ # this will add latency to scanning, so enable this only when needed)
scan_ssid=1
+
{% if security is defined and security.wpa is defined and security.wpa.passphrase is defined %}
- key_mgmt=WPA-PSK
+ # ieee80211w: whether management frame protection is enabled
+ # 0 = disabled (default unless changed with the global pmf parameter)
+ # 1 = optional
+ # 2 = required
+ # The most common configuration options for this based on the PMF (protected
+ # management frames) certification program are:
+ # PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
+ # PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
+ # (and similarly for WPA-PSK and WPA-PSK-SHA256 if WPA2-Personal is used)
+ # WPA3-Personal-only mode: ieee80211w=2 and key_mgmt=SAE
+ ieee80211w=1
+
+ # key_mgmt: list of accepted authenticated key management protocols
+ # WPA-PSK = WPA pre-shared key (this requires 'psk' field)
+ # WPA-EAP = WPA using EAP authentication
+ # IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
+ # generated WEP keys
+ # NONE = WPA is not used; plaintext or static WEP could be used
+ # WPA-NONE = WPA-None for IBSS (deprecated; use proto=RSN key_mgmt=WPA-PSK
+ # instead)
+ # FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key
+ # FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication
+ # FT-EAP-SHA384 = Fast BSS Transition (IEEE 802.11r) with EAP authentication
+ # and using SHA384
+ # WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
+ # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
+ # SAE = Simultaneous authentication of equals; pre-shared key/password -based
+ # authentication with stronger security than WPA-PSK especially when using
+ # not that strong password; a.k.a. WPA3-Personal
+ # FT-SAE = SAE with FT
+ # WPA-EAP-SUITE-B = Suite B 128-bit level
+ # WPA-EAP-SUITE-B-192 = Suite B 192-bit level
+ # OSEN = Hotspot 2.0 Rel 2 online signup connection
+ # FILS-SHA256 = Fast Initial Link Setup with SHA256
+ # FILS-SHA384 = Fast Initial Link Setup with SHA384
+ # FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
+ # FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
+ # OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
+ # DPP = Device Provisioning Protocol
+ # If not set, this defaults to: WPA-PSK WPA-EAP
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa3' %}
+ key_mgmt=SAE
+{% else %}
+ key_mgmt=WPA-PSK WPA-PSK-SHA256
+{% endif %}
+
+ # psk: WPA preshared key; 256-bit pre-shared key
+ # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
+ # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
+ # generated using the passphrase and SSID). ASCII passphrase must be between
+ # 8 and 63 characters (inclusive). ext:<name of external PSK field> format can
+ # be used to indicate that the PSK/passphrase is stored in external storage.
+ # This field is not needed, if WPA-EAP is used.
+ # Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
+ # from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
+ # startup and reconfiguration time can be optimized by generating the PSK only
+ # only when the passphrase or SSID has actually changed.
psk="{{ security.wpa.passphrase }}"
{% else %}
key_mgmt=NONE
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index 423ec7ba2..78c40d876 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -5,7 +5,7 @@
<tagNode name="wireless" owner="${vyos_conf_scripts_dir}/interfaces-wireless.py">
<properties>
<help>Wireless (WiFi/WLAN) Network Interface</help>
- <priority>400</priority>
+ <priority>318</priority>
<constraint>
<regex>^wlan[0-9]+$</regex>
</constraint>
@@ -58,7 +58,7 @@
<description>Supported channel set width both 20 MHz and 40 MHz with secondary channel below primary channel</description>
</valueHelp>
<constraint>
- <regex>(ht20|ht40\+|ht40-)</regex>
+ <regex>^(ht20|ht40\+|ht40-)$</regex>
</constraint>
<multi/>
</properties>
@@ -108,7 +108,7 @@
<description>Set maximum A-MSDU length to 7935 octets</description>
</valueHelp>
<constraint>
- <regex>(3839|7935)</regex>
+ <regex>^(3839|7935)$</regex>
</constraint>
</properties>
</leafNode>
@@ -127,7 +127,7 @@
<description>Short GI for 40 MHz</description>
</valueHelp>
<constraint>
- <regex>(20|40)</regex>
+ <regex>^(20|40)$</regex>
</constraint>
<multi/>
</properties>
@@ -147,7 +147,7 @@
<description>DYNAMIC Spatial Multiplexing (SM) Power Save</description>
</valueHelp>
<constraint>
- <regex>(static|dynamic)</regex>
+ <regex>^(static|dynamic)$</regex>
</constraint>
</properties>
</leafNode>
@@ -164,7 +164,7 @@
<description>Number of spacial streams that can use RX STBC</description>
</valueHelp>
<constraint>
- <regex>[1-3]+</regex>
+ <regex>^[1-3]+$</regex>
</constraint>
<constraintErrorMessage>Invalid capability item</constraintErrorMessage>
</properties>
@@ -243,7 +243,7 @@
<description>Support for operation as multi user beamformee</description>
</valueHelp>
<constraint>
- <regex>(single-user-beamformer|single-user-beamformee|multi-user-beamformer|multi-user-beamformee)</regex>
+ <regex>^(single-user-beamformer|single-user-beamformee|multi-user-beamformer|multi-user-beamformee)$</regex>
</constraint>
<multi/>
</properties>
@@ -329,7 +329,7 @@
<description>Station can provide VHT MFB in response to VHT MRQ and unsolicited VHT MFB</description>
</valueHelp>
<constraint>
- <regex>(unsolicited|both)</regex>
+ <regex>^(unsolicited|both)$</regex>
</constraint>
<constraintErrorMessage>Invalid capability item</constraintErrorMessage>
</properties>
@@ -361,7 +361,7 @@
<description>ncrease Maximum MPDU length to 11454 octets</description>
</valueHelp>
<constraint>
- <regex>(7991|11454)</regex>
+ <regex>^(7991|11454)$</regex>
</constraint>
</properties>
</leafNode>
@@ -380,7 +380,7 @@
<description>Short GI for 160 MHz</description>
</valueHelp>
<constraint>
- <regex>(80|160)</regex>
+ <regex>^(80|160)$</regex>
</constraint>
<multi/>
</properties>
@@ -398,7 +398,7 @@
<description>Number of spacial streams that can use RX STBC</description>
</valueHelp>
<constraint>
- <regex>[1-4]+</regex>
+ <regex>^[1-4]+$</regex>
</constraint>
<constraintErrorMessage>Invalid capability item</constraintErrorMessage>
</properties>
@@ -443,6 +443,22 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="country-code">
+ <properties>
+ <help>Indicate country in which device is operating</help>
+ <completionHelp>
+ <list>US EU JP DE UK CN ES FR RU</list>
+ </completionHelp>
+ <valueHelp>
+ <format>&lt;code%gt;</format>
+ <description>ISO/IEC 3166-1 Country Code</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[A-Z][A-Z]$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid ISO/IEC 3166-1 Country Code</constraintErrorMessage>
+ </properties>
+ </leafNode>
#include <include/interface-description.xml.i>
#include <include/dhcp-options.xml.i>
#include <include/dhcpv6-options.xml.i>
@@ -520,7 +536,7 @@
<description>MFP enforced</description>
</valueHelp>
<constraint>
- <regex>(disabled|optional|required)</regex>
+ <regex>^(disabled|optional|required)$</regex>
</constraint>
</properties>
</leafNode>
@@ -681,7 +697,7 @@
<properties>
<help>WPA mode</help>
<completionHelp>
- <list>wpa wpa2 both</list>
+ <list>wpa wpa2 wpa+wpa2 wpa3</list>
</completionHelp>
<valueHelp>
<format>wpa</format>
@@ -692,15 +708,15 @@
<description>WPA2 (full IEEE 802.11i/RSN)</description>
</valueHelp>
<valueHelp>
- <format>both</format>
+ <format>wpa+wpa2</format>
<description>Allow both WPA and WPA2</description>
</valueHelp>
<constraint>
- <regex>^(wpa|wpa2|both)$</regex>
+ <regex>^(wpa|wpa2|wpa\+wpa2|wpa3)$</regex>
</constraint>
<constraintErrorMessage>Unknown WPA mode</constraintErrorMessage>
</properties>
- <defaultValue>both</defaultValue>
+ <defaultValue>wpa+wpa2</defaultValue>
</leafNode>
<leafNode name="passphrase">
<properties>
@@ -782,25 +798,4 @@
</tagNode>
</children>
</node>
- <node name="system">
- <children>
- <leafNode name="wifi-regulatory-domain" owner="${vyos_conf_scripts_dir}/system-wifi-regdom.py">
- <properties>
- <help>Wireless regulatory domain (mandatory)</help>
- <priority>305</priority>
- <completionHelp>
- <list>US EU JP DE UK CN</list>
- </completionHelp>
- <valueHelp>
- <format>&lt;code%gt;</format>
- <description>Country code (ISO/IEC 3166-1)</description>
- </valueHelp>
- <constraint>
- <regex>[A-Z][A-Z]$</regex>
- </constraint>
- <constraintErrorMessage>invalid country code</constraintErrorMessage>
- </properties>
- </leafNode>
- </children>
- </node>
</interfaceDefinition>
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index f7388b298..bd43d4874 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -177,6 +177,10 @@ class BridgeIf(Interface):
>>> BridgeIf('br0').add_port('eth0')
>>> BridgeIf('br0').add_port('eth1')
"""
+ # Bridge port handling of wireless interfaces is done by hostapd.
+ if 'wlan' in interface:
+ return
+
return self.set_interface('add_port', interface)
def del_port(self, interface):
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index 0e93b6432..65cf127ce 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -18,14 +18,16 @@ import os
import re
import unittest
+from vyos.configsession import ConfigSessionError
from base_interfaces_test import BasicInterfaceTest
+
from vyos.util import process_named_running
from vyos.util import check_kmod
from vyos.util import read_file
def get_config_value(interface, key):
tmp = read_file(f'/run/hostapd/{interface}.conf')
- tmp = re.findall(r'\n?{}=+(.*)'.format(key), tmp)
+ tmp = re.findall(f'{key}=+(.*)', tmp)
return tmp[0]
class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
@@ -36,15 +38,14 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
self._options = {
'wlan0': ['physical-device phy0', 'ssid VyOS-WIFI-0',
'type station', 'address 192.0.2.1/30'],
- 'wlan1': ['physical-device phy0', 'ssid VyOS-WIFI-1',
+ 'wlan1': ['physical-device phy0', 'ssid VyOS-WIFI-1', 'country-code SE',
'type access-point', 'address 192.0.2.5/30', 'channel 0'],
'wlan10': ['physical-device phy1', 'ssid VyOS-WIFI-2',
'type station', 'address 192.0.2.9/30'],
- 'wlan11': ['physical-device phy1', 'ssid VyOS-WIFI-3',
+ 'wlan11': ['physical-device phy1', 'ssid VyOS-WIFI-3', 'country-code SE',
'type access-point', 'address 192.0.2.13/30', 'channel 0'],
}
self._interfaces = list(self._options)
- self.session.set(['system', 'wifi-regulatory-domain', 'SE'])
def test_add_address_single(self):
""" derived method to check if member interfaces are enslaved properly """
@@ -73,6 +74,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
self.session.set(self._base_path + [interface, 'ssid', ssid])
self.session.set(self._base_path + [interface, 'type', 'access-point'])
self.session.set(self._base_path + [interface, 'channel', channel])
+ self.session.set(self._base_path + [interface, 'country-code', 'SE'])
# auto-powersave is special
self.session.set(self._base_path + [interface, 'capabilities', 'ht', 'auto-powersave'])
@@ -114,6 +116,8 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
#
# Validate Config
#
+ tmp = get_config_value(interface, 'interface')
+ self.assertEqual(interface, tmp)
# ssid
tmp = get_config_value(interface, 'ssid')
@@ -138,6 +142,74 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
# Check for running process
self.assertTrue(process_named_running('hostapd'))
+ def test_hostapd_wpa_config(self):
+ """ Check if hostapd config is properly generated """
+
+ # Only set the hostapd (access-point) options
+ interface = 'wlan0'
+ phy = 'phy0'
+ ssid = 'ssid'
+ channel = '0'
+ wpa_key = 'VyOSVyOSVyOS'
+ mode = 'n'
+ country = 'DE'
+
+ self.session.set(self._base_path + [interface, 'physical-device', phy])
+ self.session.set(self._base_path + [interface, 'type', 'access-point'])
+ self.session.set(self._base_path + [interface, 'mode', mode])
+
+ # SSID must be set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'ssid', ssid])
+
+ # Channel must be set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'channel', channel])
+
+ # Country-Code must be set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'country-code', country])
+
+ self.session.set(self._base_path + [interface, 'security', 'wpa', 'mode', 'wpa2'])
+ self.session.set(self._base_path + [interface, 'security', 'wpa', 'passphrase', wpa_key])
+
+ self.session.commit()
+
+ #
+ # Validate Config
+ #
+ tmp = get_config_value(interface, 'interface')
+ self.assertEqual(interface, tmp)
+
+ tmp = get_config_value(interface, 'hw_mode')
+ # rewrite special mode
+ if mode == 'n': mode = 'g'
+ self.assertEqual(mode, tmp)
+
+ # WPA key
+ tmp = get_config_value(interface, 'wpa')
+ self.assertEqual('2', tmp)
+ tmp = get_config_value(interface, 'wpa_passphrase')
+ self.assertEqual(wpa_key, tmp)
+
+ # SSID
+ tmp = get_config_value(interface, 'ssid')
+ self.assertEqual(ssid, tmp)
+
+ # channel
+ tmp = get_config_value(interface, 'channel')
+ self.assertEqual(channel, tmp)
+
+ # Country code
+ tmp = get_config_value(interface, 'country_code')
+ self.assertEqual(country, tmp)
+
+ # Check for running process
+ self.assertTrue(process_named_running('hostapd'))
+
if __name__ == '__main__':
check_kmod('mac80211_hwsim')
unittest.main()
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index a18a21b83..5d723bbfd 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -109,11 +109,6 @@ def get_config(config=None):
if tmp: wifi = dict_merge(tmp, wifi)
- # retrieve configured regulatory domain
- conf.set_level(['system'])
- if conf.exists(['wifi-regulatory-domain']):
- wifi['country_code'] = conf.return_value(['wifi-regulatory-domain'])
-
# Only one wireless interface per phy can be in station mode
tmp = find_other_stations(conf, base, wifi['ifname'])
if tmp: wifi['station_interfaces'] = tmp
@@ -144,8 +139,7 @@ def verify(wifi):
if wifi['type'] == 'access-point':
if 'country_code' not in wifi:
- raise ConfigError('Wireless regulatory domain is mandatory,\n' \
- 'use "set system wifi-regulatory-domain" for configuration.')
+ raise ConfigError('Wireless country-code is mandatory')
if 'channel' not in wifi:
raise ConfigError('Wireless channel must be configured!')
diff --git a/src/conf_mode/system-wifi-regdom.py b/src/conf_mode/system-wifi-regdom.py
deleted file mode 100755
index 874f93923..000000000
--- a/src/conf_mode/system-wifi-regdom.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>.
-
-import os
-
-from copy import deepcopy
-from sys import exit
-
-from vyos.config import Config
-from vyos import ConfigError
-from vyos.template import render
-
-from vyos import airbag
-airbag.enable()
-
-config_80211_file='/etc/modprobe.d/cfg80211.conf'
-config_crda_file='/etc/default/crda'
-
-default_config_data = {
- 'regdom' : '',
- 'deleted' : False
-}
-
-def get_config(config=None):
- regdom = deepcopy(default_config_data)
- if config:
- conf = config
- else:
- conf = Config()
- base = ['system', 'wifi-regulatory-domain']
-
- # Check if interface has been removed
- if not conf.exists(base):
- regdom['deleted'] = True
- return regdom
- else:
- regdom['regdom'] = conf.return_value(base)
-
- return regdom
-
-def verify(regdom):
- if regdom['deleted']:
- return None
-
- if not regdom['regdom']:
- raise ConfigError("Wireless regulatory domain is mandatory.")
-
- return None
-
-def generate(regdom):
- print("Changing the wireless regulatory domain requires a system reboot.")
-
- if regdom['deleted']:
- if os.path.isfile(config_80211_file):
- os.unlink(config_80211_file)
-
- if os.path.isfile(config_crda_file):
- os.unlink(config_crda_file)
-
- return None
-
- render(config_80211_file, 'wifi/cfg80211.conf.tmpl', regdom)
- render(config_crda_file, 'wifi/crda.tmpl', regdom)
- return None
-
-def apply(regdom):
- return None
-
-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/13-to-14 b/src/migration-scripts/interfaces/13-to-14
new file mode 100755
index 000000000..fc6d7f443
--- /dev/null
+++ b/src/migration-scripts/interfaces/13-to-14
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
+
+# T3043: rename Wireless interface security mode 'both' to 'wpa+wpa2'
+# T3043: move "system wifi-regulatory-domain" to indicidual wireless interface
+
+import os
+
+from sys import exit, argv
+from vyos.configtree import ConfigTree
+
+if __name__ == '__main__':
+ if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+ file_name = argv[1]
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ config = ConfigTree(config_file)
+ base = ['interfaces', 'wireless']
+ if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+ country_code = ''
+ cc_cli = ['system', 'wifi-regulatory-domain']
+ if config.exists(cc_cli):
+ country_code = config.return_value(cc_cli)
+ config.delete(cc_cli)
+
+ for wifi in config.list_nodes(base):
+ sec_mode = base + [wifi, 'security', 'wpa', 'mode']
+ if config.exists(sec_mode):
+ mode = config.return_value(sec_mode)
+ if mode == 'both':
+ config.set(sec_mode, value='wpa+wpa2', replace=True)
+
+ if country_code:
+ config.set(base + [wifi, 'country-code'], value=country_code)
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)