summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2020-07-24 22:00:36 +0200
committerChristian Poessinger <christian@poessinger.com>2020-07-25 17:30:12 +0200
commit79af6c7b35164d3313c39dff2bc1bffbb4b326cd (patch)
tree4bc224952303fcdfa9b64033eb222233dff87a6e
parentee65528d720964cf77bc9b28e6f8fb19b9783066 (diff)
downloadvyos-1x-79af6c7b35164d3313c39dff2bc1bffbb4b326cd.tar.gz
vyos-1x-79af6c7b35164d3313c39dff2bc1bffbb4b326cd.zip
wireless: ifconfig: T2653: move to get_config_dict()
The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge.
-rw-r--r--data/templates/wifi/cfg80211.conf.tmpl4
-rw-r--r--data/templates/wifi/crda.tmpl4
-rw-r--r--data/templates/wifi/hostapd.conf.tmpl424
-rw-r--r--data/templates/wifi/wpa_supplicant.conf.tmpl4
-rw-r--r--interface-definitions/interfaces-wireless.xml.in15
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py686
6 files changed, 337 insertions, 800 deletions
diff --git a/data/templates/wifi/cfg80211.conf.tmpl b/data/templates/wifi/cfg80211.conf.tmpl
index b21bacc1e..91df57aab 100644
--- a/data/templates/wifi/cfg80211.conf.tmpl
+++ b/data/templates/wifi/cfg80211.conf.tmpl
@@ -1,3 +1 @@
-{%- if regdom -%}
-options cfg80211 ieee80211_regdom={{ regdom }}
-{% endif %}
+{{ 'options cfg80211 ieee80211_regdom=' + regdom if regdom is defined }}
diff --git a/data/templates/wifi/crda.tmpl b/data/templates/wifi/crda.tmpl
index 750ad86ee..6cd125e37 100644
--- a/data/templates/wifi/crda.tmpl
+++ b/data/templates/wifi/crda.tmpl
@@ -1,3 +1 @@
-{%- if regdom -%}
-REGDOMAIN={{ regdom }}
-{% endif %}
+{{ 'REGDOMAIN=' + regdom if regdom is defined }}
diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl
index d6068e4db..765668c57 100644
--- a/data/templates/wifi/hostapd.conf.tmpl
+++ b/data/templates/wifi/hostapd.conf.tmpl
@@ -9,7 +9,7 @@ device_name={{ description | truncate(32, True) }}
# management frames with the Host AP driver); wlan0 with many nl80211 drivers
# Note: This attribute can be overridden by the values supplied with the '-i'
# command line parameter.
-interface={{ intf }}
+interface={{ ifname }}
# Driver interface type (hostap/wired/none/nl80211/bsd);
# default: hostap). nl80211 is used with all Linux mac80211 drivers.
@@ -28,8 +28,7 @@ logger_syslog_level=0
logger_stdout=-1
logger_stdout_level=0
-{%- if country_code %}
-
+{% if country_code %}
# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
# Set as needed to indicate country in which device is operating.
# This can limit available channels and transmit power.
@@ -42,14 +41,12 @@ country_code={{ country_code }}
ieee80211d=1
{% endif %}
-{%- if ssid %}
-
+{% if ssid %}
# SSID to be used in IEEE 802.11 management frames
ssid={{ ssid }}
{% endif %}
-{%- if channel %}
-
+{% if channel %}
# Channel number (IEEE 802.11)
# (default: 0, i.e., not set)
# Please note that some drivers do not use this value from hostapd and the
@@ -61,8 +58,7 @@ ssid={{ ssid }}
channel={{ channel }}
{% endif %}
-{%- if mode %}
-
+{% if mode %}
# 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
@@ -71,29 +67,30 @@ channel={{ channel }}
# 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 'n' in mode %}
hw_mode=g
-{% elif 'ac' in mode -%}
+{% elif 'ac' in mode %}
hw_mode=a
ieee80211h=1
ieee80211ac=1
-{% else -%}
+{% else %}
hw_mode={{ mode }}
-{% endif %}
+{% endif %}
{% endif %}
# ieee80211w: Whether management frame protection (MFP) is enabled
# 0 = disabled (default)
# 1 = optional
# 2 = required
-{% if 'disabled' in mgmt_frame_protection -%}
+{% if 'disabled' in mgmt_frame_protection %}
ieee80211w=0
-{% elif 'optional' in mgmt_frame_protection -%}
+{% elif 'optional' in mgmt_frame_protection %}
ieee80211w=1
-{% elif 'required' in mgmt_frame_protection -%}
+{% elif 'required' in mgmt_frame_protection %}
ieee80211w=2
{% endif %}
+{% if capabilities is defined and capabilities.ht is defined %}
# ht_capab: HT capabilities (list of flags)
# LDPC coding capability: [LDPC] = supported
# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
@@ -127,79 +124,50 @@ ieee80211w=2
# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set)
# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
-{% if cap_ht %}
-ht_capab=
-{%- endif -%}
-
-{%- if cap_ht_40mhz_incapable -%}
-[40-INTOLERANT]
-{%- endif -%}
-
-{%- if cap_ht_delayed_block_ack -%}
-[DELAYED-BA]
-{%- endif -%}
-
-{%- if cap_ht_dsss_cck_40 -%}
-[DSSS_CCK-40]
-{%- endif -%}
-
-{%- if cap_ht_greenfield -%}
-[GF]
-{%- endif -%}
-
-{%- if cap_ht_ldpc -%}
-[LDPC]
-{%- endif -%}
-
-{%- if cap_ht_lsig_protection -%}
-[LSIG-TXOP-PROT]
-{%- endif -%}
-
-{%- if cap_ht_max_amsdu -%}
-[MAX-AMSDU-{{ cap_ht_max_amsdu }}]
-{%- endif -%}
-
-{%- if cap_ht_smps -%}
-[SMPS-{{ cap_ht_smps | upper }}]
-{%- endif -%}
-
-{%- if cap_ht_chan_set_width -%}
-{%- for csw in cap_ht_chan_set_width -%}
-[{{ csw | upper }}]
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_ht_short_gi -%}
-{%- for gi in cap_ht_short_gi -%}
-[SHORT-GI-{{ gi }}]
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_ht_stbc_tx -%}
-[TX-STBC]
-{%- endif -%}
-{%- if cap_ht_stbc_rx -%}
-[RX-STBC{{ cap_ht_stbc_rx }}]
-{%- endif %}
+{% set output = '' %}
+{% set output = output + '[40-INTOLERANT]' if capabilities.ht.fourtymhz_incapable is defined else '' %}
+{% set output = output + '[DELAYED-BA]' if capabilities.ht.delayed_block_ack is defined else '' %}
+{% set output = output + '[DSSS_CCK-40]' if capabilities.ht.dsss_cck_40 is defined else '' %}
+{% set output = output + '[GF]' if capabilities.ht.greenfield is defined else '' %}
+{% set output = output + '[LDPC]' if capabilities.ht.ldpc is defined else '' %}
+{% set output = output + '[LSIG-TXOP-PROT]' if capabilities.ht.lsig_protection is defined else '' %}
+{% set output = output + '[TX-STBC]' if capabilities.ht.stbc.tx is defined else '' %}
+{% set output = output + '[RX-STBC-' + capabilities.ht.stbc.rx | upper + ']' if capabilities.ht.stbc.tx is defined else '' %}
+{% set output = output + '[MAX-AMSDU-' + capabilities.ht.max_amsdu + ']' if capabilities.ht.max_amsdu is defined else '' %}
+{% set output = output + '[SMPS-' + capabilities.ht.smps | upper + ']' if capabilities.ht.smps is defined else '' %}
+
+{% if capabilities.ht.channel_set_width is defined %}
+{% for csw in capabilities.ht.channel_set_width %}
+{% set output = output + '[' + csw | upper + ']' %}
+{% endfor %}
+{% endif %}
-# Required for full HT and VHT functionality
-wme_enabled=1
+{% if capabilities.ht.short_gi is defined %}
+{% for short_gi in capabilities.ht.short_gi %}
+{% set output = output + '[SHORT-GI-' + short_gi | upper + ']' %}
+{% endfor %}
+{% endif %}
-{% if cap_ht_powersave -%}
+ht_capab={{ output }}
+
+{% if capabilities.ht.auto_powersave is defined %}
# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver)
uapsd_advertisement_enabled=1
-{%- endif %}
+{% endif %}
+
+{% endif %}
+
+# Required for full HT and VHT functionality
+wme_enabled=1
-{% if cap_req_ht -%}
+
+{% if capabilities is defined and capabilities.require_ht is defined %}
# Require stations to support HT PHY (reject association if they do not)
require_ht=1
{% endif %}
-{%- if cap_vht_chan_set_width -%}
-vht_oper_chwidth={{ cap_vht_chan_set_width }}
-{%- endif %}
-
+{% if capabilities is defined and capabilities.vht is defined %}
# vht_capab: VHT capabilities (list of flags)
#
# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
@@ -316,133 +284,95 @@ vht_oper_chwidth={{ cap_vht_chan_set_width }}
# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
# Indicates the possibility of Tx antenna pattern change
# 0 = Tx antenna pattern might change during the lifetime of an association
-# 1 = Tx antenna pattern does not change during the lifetime of an association
-{% if cap_vht %}
-vht_capab=
-{%- endif -%}
-
-{%- if cap_vht_max_mpdu -%}
-[MAX-MPDU-{{ cap_vht_max_mpdu }}]
-{%- endif -%}
-
-{%- if cap_vht_max_mpdu_exp -%}
-[MAX-A-MPDU-LEN-EXP{{ cap_vht_max_mpdu_exp }}]
-{%- endif -%}
-
-{%- if cap_vht_chan_set_width -%}
-{%- if '2' in cap_vht_chan_set_width -%}
-[VHT160]
-{%- elif '3' in cap_vht_chan_set_width -%}
-[VHT160-80PLUS80]
-{%- endif -%}
-{%- endif -%}
-
-{%- if cap_vht_stbc_tx -%}
-[TX-STBC-2BY1]
-{%- endif -%}
-
-{%- if cap_vht_stbc_rx -%}
-[RX-STBC-{{ cap_vht_stbc_rx }}]
-{%- endif -%}
-
-{%- if cap_vht_link_adaptation -%}
-{%- if 'unsolicited' in cap_vht_link_adaptation -%}
-[VHT-LINK-ADAPT2]
-{%- elif 'both' in cap_vht_link_adaptation -%}
-[VHT-LINK-ADAPT3]
-{%- endif -%}
-{%- endif -%}
-
-{%- if cap_vht_short_gi -%}
-{%- for gi in cap_vht_short_gi -%}
-[SHORT-GI-{{ gi }}]
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_vht_ldpc -%}
-[RXLDPC]
-{%- endif -%}
-
-{%- if cap_vht_tx_powersave -%}
-[VHT-TXOP-PS]
-{%- endif -%}
-
-{%- if cap_vht_vht_cf -%}
-[HTC-VHT]
-{%- endif -%}
-
-{%- if cap_vht_beamform -%}
-{%- for beamform in cap_vht_beamform -%}
-{%- if 'single-user-beamformer' in beamform -%}
-[SU-BEAMFORMER]
-{%- elif 'single-user-beamformee' in beamform -%}
-[SU-BEAMFORMEE]
-{%- elif 'multi-user-beamformer' in beamform -%}
-[MU-BEAMFORMER]
-{%- elif 'multi-user-beamformee' in beamform -%}
-[MU-BEAMFORMEE]
-{%- endif -%}
-{%- endfor -%}
-{%- endif -%}
-
-{%- if cap_vht_antenna_fixed -%}
-[RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]
-{%- endif -%}
-
-{%- if cap_vht_antenna_cnt -%}
-{%- if cap_vht_antenna_cnt|int > 1 -%}
-{%- if cap_vht_beamform -%}
-{%- for beamform in cap_vht_beamform -%}
-{%- if 'single-user-beamformer' in beamform -%}
-{%- if cap_vht_antenna_cnt|int < 6 -%}
-[BF-ANTENNA-{{ cap_vht_antenna_cnt|int -1 }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt|int -1}}]
-{%- endif -%}
-{%- else -%}
-{%- if cap_vht_antenna_cnt|int < 5 -%}
-[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}]
-{%- endif -%}
-{%- endif -%}
-{%- endfor -%}
-{%- else -%}
-{%- if cap_vht_antenna_cnt|int < 5 -%}
-[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}]
-{%- endif -%}
-{%- endif -%}
-{%- endif -%}
-{%- endif %}
+# 1 = Tx antenna pattern does not change during the lifetime of an
+
+{% if capabilities.vht.center_channel_freq.freq_1 is defined %}
+# center freq = 5 GHz + (5 * index)
+# So index 42 gives center freq 5.210 GHz
+# which is channel 42 in 5G band
+vht_oper_centr_freq_seg0_idx={{ capabilities.vht.center_channel_freq.freq_1 }}
+{% endif %}
+
+{% if capabilities.vht.center_channel_freq.freq_2 is defined %}
+# center freq = 5 GHz + (5 * index)
+# So index 159 gives center freq 5.795 GHz
+# which is channel 159 in 5G band
+vht_oper_centr_freq_seg1_idx={{ capabilities.vht.center_channel_freq.freq_2 }}
+{% endif %}
+
+{% if capabilities.vht.channel_set_width is defined %}
+vht_oper_chwidth={{ capabilities.vht.channel_set_width }}
+{% endif %}
+
+{% set output = '' %}
+{% set output = output + '[TX-STBC-2BY1]' if capabilities.vht.stbc.tx is defined else '' %}
+{% set output = output + '[RXLDPC]' if capabilities.vht.ldpc is defined else '' %}
+{% set output = output + '[VHT-TXOP-PS]' if capabilities.vht.tx_powersave is defined else '' %}
+{% set output = output + '[HTC-VHT]' if capabilities.vht.vht_cf is defined else '' %}
+{% set output = output + '[RX-ANTENNA-PATTERN]' if capabilities.vht.antenna_pattern_fixed is defined else '' %}
+{% set output = output + '[TX-ANTENNA-PATTERN]' if capabilities.vht.antenna_pattern_fixed is defined else '' %}
+
+{% set output = output + '[RX-STBC-' + capabilities.vht.stbc.rx + ']' if capabilities.vht.stbc.rx is defined else '' %}
+{% set output = output + '[MAX-MPDU-' + capabilities.vht.max_mpdu + ']' if capabilities.vht.max_mpdu is defined else '' %}
+{% set output = output + '[MAX-A-MPDU-LEN-EXP-' + capabilities.vht.max_mpdu_exp + ']' if capabilities.vht.max_mpdu_exp is defined else '' %}
+{% set output = output + '[MAX-A-MPDU-LEN-EXP-' + capabilities.vht.max_mpdu_exp + ']' if capabilities.vht.max_mpdu_exp is defined else '' %}
+
+{% set output = output + '[VHT160]' if capabilities.vht.max_mpdu_exp is defined and capabilities.vht.max_mpdu_exp == '2' else '' %}
+{% set output = output + '[VHT160-80PLUS80]' if capabilities.vht.max_mpdu_exp is defined and capabilities.vht.max_mpdu_exp == '3' else '' %}
+{% set output = output + '[VHT-LINK-ADAPT2]' if capabilities.vht.link_adaptation is defined and capabilities.vht.link_adaptation == 'unsolicited' else '' %}
+{% set output = output + '[VHT-LINK-ADAPT3]' if capabilities.vht.link_adaptation is defined and capabilities.vht.link_adaptation == 'both' else '' %}
+
+{% if capabilities.vht.short_gi is defined %}
+{% for short_gi in capabilities.vht.short_gi %}
+{% set output = output + '[SHORT-GI-' + short_gi | upper + ']' %}
+{% endfor %}
+{% endif %}
+
+{% if capabilities.vht.beamform %}
+{% for beamform in capabilities.vht.beamform %}
+{% set output = output + '[SU-BEAMFORMER]' if beamform == 'single-user-beamformer' else '' %}
+{% set output = output + '[SU-BEAMFORMEE]' if beamform == 'single-user-beamformee' else '' %}
+{% set output = output + '[MU-BEAMFORMER]' if beamform == 'multi-user-beamformer' else '' %}
+{% set output = output + '[MU-BEAMFORMEE]' if beamform == 'multi-user-beamformee' else '' %}
+{% endfor %}
+{% endif %}
+
+{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 %}
+{% if capabilities.vht.beamform %}
+{% if beamform == 'single-user-beamformer' %}
+{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 and capabilities.vht.antenna_count|int < 6 %}
+{% set output = output + '[BF-ANTENNA-' + capabilities.vht.antenna_count|int -1 + ']' %}
+{% set output = output + '[SOUNDING-DIMENSION-' + capabilities.vht.antenna_count|int -1 + ']' %}
+{% endif %}
+{% endif %}
+{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 and capabilities.vht.antenna_count|int < 5 %}
+{% set output = output + '[BF-ANTENNA-' + capabilities.vht.antenna_count + ']' %}
+{% set output = output + '[SOUNDING-DIMENSION-' + capabilities.vht.antenna_count+ ']' %}
+{% endif %}
+{% endif %}
+{% endif %}
+
+vht_capab={{ output }}
+{% endif %}
# ieee80211n: Whether IEEE 802.11n (HT) is enabled
# 0 = disabled (default)
# 1 = enabled
# Note: You will also need to enable WMM for full HT functionality.
# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
-{% if cap_req_vht -%}
+{% if capabilities is defined and capabilities.require_vht is defined %}
ieee80211n=0
# Require stations to support VHT PHY (reject association if they do not)
require_vht=1
-{% else -%}
-{% if 'n' in mode or 'ac' in mode -%}
+{% else %}
+{% if 'n' in mode or 'ac' in mode %}
ieee80211n=1
-{% else -%}
+{% else %}
ieee80211n=0
-{%- endif %}
+{% endif %}
{% endif %}
-{% if cap_vht_center_freq_1 -%}
-# center freq = 5 GHz + (5 * index)
-# So index 42 gives center freq 5.210 GHz
-# which is channel 42 in 5G band
-vht_oper_centr_freq_seg0_idx={{ cap_vht_center_freq_1 }}
-{% endif %}
-
-{% if cap_vht_center_freq_2 -%}
-# center freq = 5 GHz + (5 * index)
-# So index 159 gives center freq 5.795 GHz
-# which is channel 159 in 5G band
-vht_oper_centr_freq_seg1_idx={{ cap_vht_center_freq_2 }}
-{% endif %}
-
-{% if disable_broadcast_ssid -%}
+{% if disable_broadcast_ssid is defined %}
# Send empty SSID in beacons and ignore probe request frames that do not
# specify full SSID, i.e., require stations to know SSID.
# default: disabled (0)
@@ -463,7 +393,7 @@ ignore_broadcast_ssid=1
# 2 = use external RADIUS server (accept/deny lists are searched first)
macaddr_acl=0
-{% if max_stations -%}
+{% if max_stations is defined %}
# Maximum number of stations allowed in station table. New stations will be
# rejected after the station table is full. IEEE 802.11 has a limit of 2007
# different association IDs, so this number should not be larger than that.
@@ -471,13 +401,13 @@ macaddr_acl=0
max_num_sta={{ max_stations }}
{% endif %}
-{% if isolate_stations -%}
+{% if isolate_stations is defined %}
# Client isolation can be used to prevent low-level bridging of frames between
# associated stations in the BSS. By default, this bridging is allowed.
ap_isolate=1
{% endif %}
-{% if reduce_transmit_power -%}
+{% if reduce_transmit_power is defined %}
# Add Power Constraint element to Beacon and Probe Response frames
# This config option adds Power Constraint element when applicable and Country
# element is added. Power Constraint element is required by Transmit Power
@@ -486,14 +416,15 @@ ap_isolate=1
local_pwr_constraint={{ reduce_transmit_power }}
{% endif %}
-{% if expunge_failing_stations -%}
+{% if expunge_failing_stations is defined %}
# Disassociate stations based on excessive transmission failures or other
# indications of connection loss. This depends on the driver capabilities and
# may not be available with all drivers.
disassoc_low_ack=1
{% endif %}
-{% if sec_wep -%}
+
+{% if security is defined and security.wep is defined %}
# IEEE 802.11 specifies two authentication algorithms. hostapd can be
# configured to allow both of these or only one. Open system authentication
# should be used with IEEE 802.1X.
@@ -522,13 +453,14 @@ wep_default_key=0
# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or
# 128-bit (152-bit) WEP is used.
# Only the default key must be supplied; the others are optional.
-{% if sec_wep_key -%}
-{% for key in sec_wep_key -%}
-wep_key{{ loop.index -1 }}={{ key}}
-{% endfor %}
-{%- endif %}
+{% if security.wep.key is defined %}
+{% for key in sec_wep_key %}
+wep_key{{ loop.index -1 }}={{ security.wep.key }}
+{% endfor %}
+{% endif %}
-{% elif sec_wpa -%}
+
+{% elif security is defined and security.wpa is defined %}
##### WPA/IEEE 802.11i configuration ##########################################
# Enable WPA. Setting this variable configures the AP to require WPA (either
@@ -542,15 +474,17 @@ wep_key{{ loop.index -1 }}={{ key}}
# and/or WPA2 (full IEEE 802.11i/RSN):
# bit0 = WPA
# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
-{% if 'both' in sec_wpa_mode -%}
+{% if security.wpa.mode is defined %}
+{% if security.wpa.mode == 'both' %}
wpa=3
-{%- elif 'wpa2' in sec_wpa_mode -%}
+{% elif security.wpa.mode == 'wpa2' %}
wpa=2
-{%- elif 'wpa' in sec_wpa_mode -%}
+{% elif security.wpa.mode == 'wpa' %}
wpa=1
-{%- endif %}
+{% endif %}
+{% endif %}
-{% if sec_wpa_cipher -%}
+{% if security.wpa.cipher is defined %}
# Set of accepted cipher suites (encryption algorithms) for pairwise keys
# (unicast packets). This is a space separated list of algorithms:
# CCMP = AES in Counter mode with CBC-MAC (CCMP-128)
@@ -563,26 +497,39 @@ wpa=1
# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
# TKIP will be used as the group cipher. The optional group_cipher parameter can
# be used to override this automatic selection.
-{% if 'wpa2' in sec_wpa_mode -%}
+
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa2' %}
# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
-rsn_pairwise={{ sec_wpa_cipher | join(" ") }}
-{% else -%}
+{% if security.wpa.cipher is string %}
+rsn_pairwise={{ security.wpa.cipher }}
+{% else %}
+rsn_pairwise={{ security.wpa.cipher | join(" ") }}
+{% endif %}
+{% else %}
# Pairwise cipher for WPA (v1) (default: TKIP)
-wpa_pairwise={{ sec_wpa_cipher | join(" ") }}
-{%- endif -%}
-{% endif %}
-
-{% if sec_wpa_group_cipher -%}
+{% if security.wpa.cipher is string %}
+wpa_pairwise={{ security.wpa.cipher }}
+{% else %}
+wpa_pairwise={{ security.wpa.cipher | join(" ") }}
+{% endif %}
+{% endif %}
+{% endif %}
+
+{% if security.wpa.group_cipher is defined %}
# Optional override for automatic group cipher selection
# This can be used to select a specific group cipher regardless of which
# pairwise ciphers were enabled for WPA and RSN. It should be noted that
# overriding the group cipher with an unexpected value can result in
# interoperability issues and in general, this parameter is mainly used for
# testing purposes.
-group_cipher={{ sec_wpa_group_cipher | join(" ") }}
-{% endif %}
-
-{% if sec_wpa_passphrase -%}
+{% if security.wpa.group_cipher is string %}
+group_cipher={{ security.wpa.group_cipher }}
+{% else %}
+group_cipher={{ security.wpa.group_cipher | join(" ") }}
+{% endif %}
+{% endif %}
+
+{% if security.wpa.passphrase is defined %}
# IEEE 802.11 specifies two authentication algorithms. hostapd can be
# configured to allow both of these or only one. Open system authentication
# should be used with IEEE 802.1X.
@@ -595,7 +542,7 @@ auth_algs=1
# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
# (8..63 characters) that will be converted to PSK. This conversion uses SSID
# so the PSK changes when ASCII passphrase is used and the SSID is changed.
-wpa_passphrase={{ sec_wpa_passphrase }}
+wpa_passphrase={{ security.wpa.passphrase }}
# 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
@@ -604,7 +551,7 @@ wpa_passphrase={{ sec_wpa_passphrase }}
# WPA-PSK-SHA256 = WPA2-Personal using SHA256
wpa_key_mgmt=WPA-PSK
-{% elif sec_wpa_radius -%}
+{% elif security.wpa.radius is defined %}
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
ieee8021x=1
@@ -616,40 +563,37 @@ ieee8021x=1
# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
wpa_key_mgmt=WPA-EAP
-{% if sec_wpa_radius_source -%}
+{% if security.wpa.radius.server is defined %}
# RADIUS client forced local IP address for the access point
# Normally the local IP address is determined automatically based on configured
# IP addresses, but this field can be used to force a specific address to be
# used, e.g., when the device has multiple IP addresses.
-radius_client_addr={{ sec_wpa_radius_source }}
-
-# The own IP address of the access point (used as NAS-IP-Address)
-own_ip_addr={{ sec_wpa_radius_source }}
-{% else %}
# The own IP address of the access point (used as NAS-IP-Address)
+{% if security.wpa.radius.source_address is defined %}
+radius_client_addr={{ security.wpa.radius.source_address }}
+own_ip_addr={{ security.wpa.radius.source_address }}
+{% else %}
own_ip_addr=127.0.0.1
-{% endif %}
+{% endif %}
-{% for radius in sec_wpa_radius -%}
-{%- if not radius.disabled -%}
+{% for radius in security.wpa.radius.server if not radius.disabled %}
# RADIUS authentication server
auth_server_addr={{ radius.server }}
auth_server_port={{ radius.port }}
auth_server_shared_secret={{ radius.key }}
-{% if radius.acc_port -%}
+
+{% if radius.acc_port %}
# RADIUS accounting server
acct_server_addr={{ radius.server }}
acct_server_port={{ radius.acc_port }}
acct_server_shared_secret={{ radius.key }}
-{% endif %}
-{% endif %}
-{% endfor %}
-
-{% endif %}
-
-{% else %}
+{% endif %}
+{% endfor %}
+{% else %}
# Open system
auth_algs=1
+{% endif %}
+{% endif %}
{% endif %}
# TX queue parameters (EDCF / bursting)
diff --git a/data/templates/wifi/wpa_supplicant.conf.tmpl b/data/templates/wifi/wpa_supplicant.conf.tmpl
index 2784883f1..9ddad35fd 100644
--- a/data/templates/wifi/wpa_supplicant.conf.tmpl
+++ b/data/templates/wifi/wpa_supplicant.conf.tmpl
@@ -1,8 +1,8 @@
# WPA supplicant config
network={
ssid="{{ ssid }}"
-{%- if sec_wpa_passphrase %}
- psk="{{ sec_wpa_passphrase }}"
+{% if security is defined and security.wpa is defined and security.wpa.passphrase is defined %}
+ psk="{{ security.wpa.passphrase }}"
{% else %}
key_mgmt=NONE
{% endif %}
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index 06c7734f5..6f0ec9e71 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -320,7 +320,7 @@
<properties>
<help>VHT link adaptation capabilities</help>
<completionHelp>
- <list>unsolicited both</list>
+ <list>unsolicited both</list>
</completionHelp>
<valueHelp>
<format>unsolicited</format>
@@ -451,6 +451,7 @@
<leafNode name="disable-broadcast-ssid">
<properties>
<help>Disable broadcast of SSID from access-point</help>
+ <valueless/>
</properties>
</leafNode>
#include <include/interface-disable-link-detect.xml.i>
@@ -551,9 +552,10 @@
<description>802.11ac - 1300 Mbits/sec</description>
</valueHelp>
<constraint>
- <regex>(a|b|g|n|ac)</regex>
+ <regex>^(a|b|g|n|ac)$</regex>
</constraint>
</properties>
+ <defaultValue>g</defaultValue>
</leafNode>
<leafNode name="physical-device">
<properties>
@@ -637,7 +639,7 @@
<description>Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]</description>
</valueHelp>
<constraint>
- <regex>(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)</regex>
+ <regex>^(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)$</regex>
</constraint>
<constraintErrorMessage>Invalid cipher selection</constraintErrorMessage>
<multi/>
@@ -670,7 +672,7 @@
<description>Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]</description>
</valueHelp>
<constraint>
- <regex>(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)</regex>
+ <regex>^(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)$</regex>
</constraint>
<constraintErrorMessage>Invalid group cipher selection</constraintErrorMessage>
<multi/>
@@ -695,7 +697,7 @@
<description>Allow both WPA and WPA2</description>
</valueHelp>
<constraint>
- <regex>(wpa|wpa2|both)</regex>
+ <regex>^(wpa|wpa2|both)$</regex>
</constraint>
<constraintErrorMessage>Unknown WPA mode</constraintErrorMessage>
</properties>
@@ -762,10 +764,11 @@
<description>Passively monitor all packets on the frequency/channel</description>
</valueHelp>
<constraint>
- <regex>(access-point|station|monitor)</regex>
+ <regex>^(access-point|station|monitor)$</regex>
</constraint>
<constraintErrorMessage>Type must be access-point, station or monitor</constraintErrorMessage>
</properties>
+ <defaultValue>monitor</defaultValue>
</leafNode>
#include <include/vif.xml.i>
#include <include/vif-s.xml.i>
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 0162b642c..42b55ee6a 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -15,497 +15,169 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+
from sys import exit
from re import findall
-
from copy import deepcopy
-
-from netifaces import interfaces
from netaddr import EUI, mac_unix_expanded
from vyos.config import Config
-from vyos.configdict import list_diff, intf_to_dict, add_to_dict, interface_default_data
-from vyos.ifconfig import WiFiIf, Section
-from vyos.ifconfig_vlan import apply_all_vlans, verify_vlan_config
+from vyos.configdict import get_interface_dict
+from vyos.configdict import dict_merge
+from vyos.configverify import verify_address
+from vyos.configverify import verify_bridge_delete
+from vyos.configverify import verify_dhcpv6
+from vyos.configverify import verify_source_interface
+from vyos.configverify import verify_vlan_config
+from vyos.configverify import verify_vrf
+from vyos.ifconfig import WiFiIf
from vyos.template import render
-from vyos.util import chown, call
-from vyos.validate import is_member
+from vyos.util import call
from vyos import ConfigError
-
from vyos import airbag
airbag.enable()
-default_config_data = {
- **interface_default_data,
- 'cap_ht' : False,
- 'cap_ht_40mhz_incapable' : False,
- 'cap_ht_powersave' : False,
- 'cap_ht_chan_set_width' : '',
- 'cap_ht_delayed_block_ack' : False,
- 'cap_ht_dsss_cck_40' : False,
- 'cap_ht_greenfield' : False,
- 'cap_ht_ldpc' : False,
- 'cap_ht_lsig_protection' : False,
- 'cap_ht_max_amsdu' : '',
- 'cap_ht_short_gi' : [],
- 'cap_ht_smps' : '',
- 'cap_ht_stbc_rx' : '',
- 'cap_ht_stbc_tx' : False,
- 'cap_req_ht' : False,
- 'cap_req_vht' : False,
- 'cap_vht' : False,
- 'cap_vht_antenna_cnt' : '',
- 'cap_vht_antenna_fixed' : False,
- 'cap_vht_beamform' : '',
- 'cap_vht_center_freq_1' : '',
- 'cap_vht_center_freq_2' : '',
- 'cap_vht_chan_set_width' : '',
- 'cap_vht_ldpc' : False,
- 'cap_vht_link_adaptation' : '',
- 'cap_vht_max_mpdu_exp' : '',
- 'cap_vht_max_mpdu' : '',
- 'cap_vht_short_gi' : [],
- 'cap_vht_stbc_rx' : '',
- 'cap_vht_stbc_tx' : False,
- 'cap_vht_tx_powersave' : False,
- 'cap_vht_vht_cf' : False,
- 'channel': '',
- 'country_code': '',
- 'deleted': False,
- 'disable_broadcast_ssid' : False,
- 'disable_link_detect' : 1,
- 'expunge_failing_stations' : False,
- 'hw_id' : '',
- 'intf': '',
- 'isolate_stations' : False,
- 'max_stations' : '',
- 'mgmt_frame_protection' : 'disabled',
- 'mode' : 'g',
- 'phy' : '',
- 'reduce_transmit_power' : '',
- 'sec_wep' : False,
- 'sec_wep_key' : [],
- 'sec_wpa' : False,
- 'sec_wpa_cipher' : [],
- 'sec_wpa_mode' : 'both',
- 'sec_wpa_passphrase' : '',
- 'sec_wpa_radius' : [],
- 'ssid' : '',
- 'op_mode' : 'monitor',
- 'vif': {},
- 'vif_remove': [],
- 'vif_s': {},
- 'vif_s_remove': []
-}
-
# XXX: wpa_supplicant works on the source interface
-wpa_suppl_conf = '/run/wpa_supplicant/{intf}.conf'
-hostapd_conf = '/run/hostapd/{intf}.conf'
+wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf'
+hostapd_conf = '/run/hostapd/{ifname}.conf'
+
+def find_other_stations(conf, base, ifname):
+ """
+ Only one wireless interface per phy can be in station mode -
+ find all interfaces attached to a phy which run in station mode
+ """
+ old_level = conf.get_level()
+ conf.set_level(base)
+ dict = {}
+ for phy in os.listdir('/sys/class/ieee80211'):
+ list = []
+ for interface in conf.list_nodes([]):
+ if interface == ifname:
+ continue
+ # the following node is mandatory
+ if conf.exists([interface, 'physical-device', phy]):
+ tmp = conf.return_value([interface, 'type'])
+ if tmp == 'station':
+ list.append(interface)
+ if list:
+ dict.update({phy: list})
+ conf.set_level(old_level)
+ return dict
def get_config():
+ """
+ Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
+ interface name will be added or a deleted flag
+ """
+ conf = Config()
+ base = ['interfaces', 'wireless']
+
# determine tagNode instance
if 'VYOS_TAGNODE_VALUE' not in os.environ:
raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
ifname = os.environ['VYOS_TAGNODE_VALUE']
- conf = Config()
-
- # check if wireless interface has been removed
- cfg_base = ['interfaces', 'wireless ', ifname]
- if not conf.exists(cfg_base):
- wifi = deepcopy(default_config_data)
- wifi['intf'] = ifname
- wifi['deleted'] = True
- # we need to know if we're a bridge member so we can refuse deletion
- wifi['is_bridge_member'] = is_member(conf, wifi['intf'], 'bridge')
- # we can not bail out early as wireless interface can not be removed
- # Kernel will complain with: RTNETLINK answers: Operation not supported.
- # Thus we need to remove individual settings
- return wifi
-
- # set new configuration level
- conf.set_level(cfg_base)
-
- # get common interface settings
- wifi, disabled = intf_to_dict(conf, default_config_data)
-
- # 40MHz intolerance, use 20MHz only
- if conf.exists('capabilities ht 40mhz-incapable'):
- wifi['cap_ht'] = True
- wifi['cap_ht_40mhz_incapable'] = True
-
- # WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
- if conf.exists('capabilities ht auto-powersave'):
- wifi['cap_ht'] = True
- wifi['cap_ht_powersave'] = True
-
- # Supported channel set width
- if conf.exists('capabilities ht channel-set-width'):
- wifi['cap_ht'] = True
- wifi['cap_ht_chan_set_width'] = conf.return_values('capabilities ht channel-set-width')
-
- # HT-delayed Block Ack
- if conf.exists('capabilities ht delayed-block-ack'):
- wifi['cap_ht'] = True
- wifi['cap_ht_delayed_block_ack'] = True
-
- # DSSS/CCK Mode in 40 MHz
- if conf.exists('capabilities ht dsss-cck-40'):
- wifi['cap_ht'] = True
- wifi['cap_ht_dsss_cck_40'] = True
-
- # HT-greenfield capability
- if conf.exists('capabilities ht greenfield'):
- wifi['cap_ht'] = True
- wifi['cap_ht_greenfield'] = True
-
- # LDPC coding capability
- if conf.exists('capabilities ht ldpc'):
- wifi['cap_ht'] = True
- wifi['cap_ht_ldpc'] = True
-
- # L-SIG TXOP protection capability
- if conf.exists('capabilities ht lsig-protection'):
- wifi['cap_ht'] = True
- wifi['cap_ht_lsig_protection'] = True
-
- # Set Maximum A-MSDU length
- if conf.exists('capabilities ht max-amsdu'):
- wifi['cap_ht'] = True
- wifi['cap_ht_max_amsdu'] = conf.return_value('capabilities ht max-amsdu')
-
- # Short GI capabilities
- if conf.exists('capabilities ht short-gi'):
- wifi['cap_ht'] = True
- wifi['cap_ht_short_gi'] = conf.return_values('capabilities ht short-gi')
-
- # Spatial Multiplexing Power Save (SMPS) settings
- if conf.exists('capabilities ht smps'):
- wifi['cap_ht'] = True
- wifi['cap_ht_smps'] = conf.return_value('capabilities ht smps')
-
- # Support for receiving PPDU using STBC (Space Time Block Coding)
- if conf.exists('capabilities ht stbc rx'):
- wifi['cap_ht'] = True
- wifi['cap_ht_stbc_rx'] = conf.return_value('capabilities ht stbc rx')
-
- # Support for sending PPDU using STBC (Space Time Block Coding)
- if conf.exists('capabilities ht stbc tx'):
- wifi['cap_ht'] = True
- wifi['cap_ht_stbc_tx'] = True
-
- # Require stations to support HT PHY (reject association if they do not)
- if conf.exists('capabilities require-ht'):
- wifi['cap_req_ht'] = True
-
- # Require stations to support VHT PHY (reject association if they do not)
- if conf.exists('capabilities require-vht'):
- wifi['cap_req_vht'] = True
-
- # Number of antennas on this card
- if conf.exists('capabilities vht antenna-count'):
- wifi['cap_vht'] = True
- wifi['cap_vht_antenna_cnt'] = conf.return_value('capabilities vht antenna-count')
-
- # set if antenna pattern does not change during the lifetime of an association
- if conf.exists('capabilities vht antenna-pattern-fixed'):
- wifi['cap_vht'] = True
- wifi['cap_vht_antenna_fixed'] = True
-
- # Beamforming capabilities
- if conf.exists('capabilities vht beamform'):
- wifi['cap_vht'] = True
- wifi['cap_vht_beamform'] = conf.return_values('capabilities vht beamform')
-
- # VHT operating channel center frequency - center freq 1 (for use with 80, 80+80 and 160 modes)
- if conf.exists('capabilities vht center-channel-freq freq-1'):
- wifi['cap_vht'] = True
- wifi['cap_vht_center_freq_1'] = conf.return_value('capabilities vht center-channel-freq freq-1')
-
- # VHT operating channel center frequency - center freq 2 (for use with the 80+80 mode)
- if conf.exists('capabilities vht center-channel-freq freq-2'):
- wifi['cap_vht'] = True
- wifi['cap_vht_center_freq_2'] = conf.return_value('capabilities vht center-channel-freq freq-2')
-
- # VHT operating Channel width
- if conf.exists('capabilities vht channel-set-width'):
- wifi['cap_vht'] = True
- wifi['cap_vht_chan_set_width'] = conf.return_value('capabilities vht channel-set-width')
-
- # LDPC coding capability
- if conf.exists('capabilities vht ldpc'):
- wifi['cap_vht'] = True
- wifi['cap_vht_ldpc'] = True
-
- # VHT link adaptation capabilities
- if conf.exists('capabilities vht link-adaptation'):
- wifi['cap_vht'] = True
- wifi['cap_vht_link_adaptation'] = conf.return_value('capabilities vht link-adaptation')
-
- # Set the maximum length of A-MPDU pre-EOF padding that the station can receive
- if conf.exists('capabilities vht max-mpdu-exp'):
- wifi['cap_vht'] = True
- wifi['cap_vht_max_mpdu_exp'] = conf.return_value('capabilities vht max-mpdu-exp')
-
- # Increase Maximum MPDU length
- if conf.exists('capabilities vht max-mpdu'):
- wifi['cap_vht'] = True
- wifi['cap_vht_max_mpdu'] = conf.return_value('capabilities vht max-mpdu')
-
- # Increase Maximum MPDU length
- if conf.exists('capabilities vht short-gi'):
- wifi['cap_vht'] = True
- wifi['cap_vht_short_gi'] = conf.return_values('capabilities vht short-gi')
-
- # Support for receiving PPDU using STBC (Space Time Block Coding)
- if conf.exists('capabilities vht stbc rx'):
- wifi['cap_vht'] = True
- wifi['cap_vht_stbc_rx'] = conf.return_value('capabilities vht stbc rx')
-
- # Support for the transmission of at least 2x1 STBC (Space Time Block Coding)
- if conf.exists('capabilities vht stbc tx'):
- wifi['cap_vht'] = True
- wifi['cap_vht_stbc_tx'] = True
-
- # Support for VHT TXOP Power Save Mode
- if conf.exists('capabilities vht tx-powersave'):
- wifi['cap_vht'] = True
- wifi['cap_vht_tx_powersave'] = True
-
- # STA supports receiving a VHT variant HT Control field
- if conf.exists('capabilities vht vht-cf'):
- wifi['cap_vht'] = True
- wifi['cap_vht_vht_cf'] = True
-
- # Wireless radio channel
- if conf.exists('channel'):
- wifi['channel'] = conf.return_value('channel')
-
- # Disable broadcast of SSID from access-point
- if conf.exists('disable-broadcast-ssid'):
- wifi['disable_broadcast_ssid'] = True
-
- # Disassociate stations based on excessive transmission failures
- if conf.exists('expunge-failing-stations'):
- wifi['expunge_failing_stations'] = True
-
- # retrieve real hardware address
- if conf.exists('hw-id'):
- wifi['hw_id'] = conf.return_value('hw-id')
-
- # Isolate stations on the AP so they cannot see each other
- if conf.exists('isolate-stations'):
- wifi['isolate_stations'] = True
-
- # Wireless physical device
- if conf.exists('physical-device'):
- wifi['phy'] = conf.return_value('physical-device')
-
- # Maximum number of wireless radio stations
- if conf.exists('max-stations'):
- wifi['max_stations'] = conf.return_value('max-stations')
-
- # Management Frame Protection (MFP) according to IEEE 802.11w
- if conf.exists('mgmt-frame-protection'):
- wifi['mgmt_frame_protection'] = conf.return_value('mgmt-frame-protection')
-
- # Wireless radio mode
- if conf.exists('mode'):
- wifi['mode'] = conf.return_value('mode')
-
- # Transmission power reduction in dBm
- if conf.exists('reduce-transmit-power'):
- wifi['reduce_transmit_power'] = conf.return_value('reduce-transmit-power')
-
- # WEP enabled?
- if conf.exists('security wep'):
- wifi['sec_wep'] = True
-
- # WEP encryption key(s)
- if conf.exists('security wep key'):
- wifi['sec_wep_key'] = conf.return_values('security wep key')
-
- # WPA enabled?
- if conf.exists('security wpa'):
- wifi['sec_wpa'] = True
-
- # WPA Cipher suite
- if conf.exists('security wpa cipher'):
- wifi['sec_wpa_cipher'] = conf.return_values('security wpa cipher')
-
- # WPA mode
- if conf.exists('security wpa mode'):
- wifi['sec_wpa_mode'] = conf.return_value('security wpa mode')
-
- # WPA default ciphers depend on WPA mode
- if not wifi['sec_wpa_cipher']:
- if wifi['sec_wpa_mode'] == 'wpa':
- wifi['sec_wpa_cipher'].append('TKIP')
- wifi['sec_wpa_cipher'].append('CCMP')
-
- elif wifi['sec_wpa_mode'] == 'wpa2':
- wifi['sec_wpa_cipher'].append('CCMP')
-
- elif wifi['sec_wpa_mode'] == 'both':
- wifi['sec_wpa_cipher'].append('CCMP')
- wifi['sec_wpa_cipher'].append('TKIP')
-
- # WPA Group Cipher suite
- if conf.exists('security wpa group-cipher'):
- wifi['sec_wpa_group_cipher'] = conf.return_values('security wpa group-cipher')
-
- # WPA personal shared pass phrase
- if conf.exists('security wpa passphrase'):
- wifi['sec_wpa_passphrase'] = conf.return_value('security wpa passphrase')
-
- # WPA RADIUS source address
- if conf.exists('security wpa radius source-address'):
- wifi['sec_wpa_radius_source'] = conf.return_value('security wpa radius source-address')
-
- # WPA RADIUS server
- for server in conf.list_nodes('security wpa radius server'):
- # set new configuration level
- conf.set_level(cfg_base + ' security wpa radius server ' + server)
- radius = {
- 'server' : server,
- 'acc_port' : '',
- 'disabled': False,
- 'port' : 1812,
- 'key' : ''
- }
-
- # RADIUS server port
- if conf.exists('port'):
- radius['port'] = int(conf.return_value('port'))
-
- # receive RADIUS accounting info
- if conf.exists('accounting'):
- radius['acc_port'] = radius['port'] + 1
-
- # Check if RADIUS server was temporary disabled
- if conf.exists(['disable']):
- radius['disabled'] = True
-
- # RADIUS server shared-secret
- if conf.exists('key'):
- radius['key'] = conf.return_value('key')
-
- # append RADIUS server to list of servers
- wifi['sec_wpa_radius'].append(radius)
-
- # re-set configuration level to parse new nodes
- conf.set_level(cfg_base)
-
- # Wireless access-point service set identifier (SSID)
- if conf.exists('ssid'):
- wifi['ssid'] = conf.return_value('ssid')
-
- # Wireless device type for this interface
- if conf.exists('type'):
- tmp = conf.return_value('type')
- if tmp == 'access-point':
- tmp = 'ap'
-
- wifi['op_mode'] = tmp
+ wifi = get_interface_dict(conf, base, ifname)
+
+ if 'security' in wifi and 'wpa' in wifi['security']:
+ wpa_cipher = wifi['security']['wpa'].get('cipher')
+ wpa_mode = wifi['security']['wpa'].get('mode')
+ if not wpa_cipher:
+ tmp = None
+ if wpa_mode == 'wpa':
+ tmp = {'security': {'wpa': {'cipher' : ['TKIP', 'CCMP']}}}
+ elif wpa_mode == 'wpa2':
+ tmp = {'security': {'wpa': {'cipher' : ['CCMP']}}}
+ elif wpa_mode == 'both':
+ tmp = {'security': {'wpa': {'cipher' : ['CCMP', 'TKIP']}}}
+
+ 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')
+ conf.set_level(['system'])
+ if conf.exists(['wifi-regulatory-domain']):
+ wifi['country_code'] = conf.return_value(['wifi-regulatory-domain'])
- return wifi
+ # 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
+ return wifi
def verify(wifi):
- if wifi['deleted']:
- if wifi['is_bridge_member']:
- raise ConfigError((
- f'Cannot delete interface "{wifi["intf"]}" as it is a '
- f'member of bridge "{wifi["is_bridge_member"]}"!'))
-
+ if 'deleted' in wifi:
+ verify_bridge_delete(wifi)
return None
- if wifi['op_mode'] != 'monitor' and not wifi['ssid']:
- raise ConfigError('SSID must be set for {}'.format(wifi['intf']))
-
- if not wifi['phy']:
- raise ConfigError('You must specify physical-device')
+ if 'physical_device' not in wifi:
+ raise ConfigError('You must specify a physical-device "phy"')
- if not wifi['mode']:
+ if 'type' not in wifi:
raise ConfigError('You must specify a WiFi mode')
- if wifi['op_mode'] == 'ap':
- c = Config()
- if not c.exists('system wifi-regulatory-domain'):
- raise ConfigError('Wireless regulatory domain is mandatory,\n' \
- 'use "set system wifi-regulatory-domain".')
-
- if not wifi['channel']:
- raise ConfigError('Channel must be set for {}'.format(wifi['intf']))
+ if 'ssid' not in wifi and wifi['type'] != 'monitor':
+ raise ConfigError('SSID must be configured')
- if len(wifi['sec_wep_key']) > 4:
- raise ConfigError('No more then 4 WEP keys configurable')
-
- if wifi['cap_vht'] and not wifi['cap_ht']:
- raise ConfigError('Specify HT flags if you want to use VHT!')
-
- if wifi['cap_vht_beamform'] and wifi['cap_vht_antenna_cnt'] == 1:
- raise ConfigError('Cannot use beam forming with just one antenna!')
-
- if wifi['cap_vht_beamform'] == 'single-user-beamformer' and wifi['cap_vht_antenna_cnt'] < 3:
- # Nasty Gotcha: see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf lines 692-705
- raise ConfigError('Single-user beam former requires at least 3 antennas!')
-
- if wifi['sec_wep'] and (len(wifi['sec_wep_key']) == 0):
- raise ConfigError('Missing WEP keys')
-
- if wifi['sec_wpa'] and not (wifi['sec_wpa_passphrase'] or wifi['sec_wpa_radius']):
- raise ConfigError('Misssing WPA key or RADIUS server')
-
- for radius in wifi['sec_wpa_radius']:
- if not radius['key']:
- raise ConfigError('Misssing RADIUS shared secret key for server: {}'.format(radius['server']))
-
- if ( wifi['is_bridge_member']
- and ( wifi['address']
- or wifi['ipv6_eui64_prefix']
- or wifi['ipv6_autoconf'] ) ):
- raise ConfigError((
- f'Cannot assign address to interface "{wifi["intf"]}" '
- f'as it is a member of bridge "{wifi["is_bridge_member"]}"!'))
-
- if wifi['vrf']:
- if wifi['vrf'] not in interfaces():
- raise ConfigError(f'VRF "{wifi["vrf"]}" does not exist')
-
- if wifi['is_bridge_member']:
- raise ConfigError((
- f'Interface "{wifi["intf"]}" cannot be member of VRF '
- f'"{wifi["vrf"]}" and bridge {wifi["is_bridge_member"]} '
- f'at the same time!'))
+ 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.')
+
+ if 'channel' not in wifi:
+ raise ConfigError('Wireless channel must be configured!')
+
+ if 'security' in wifi:
+ if {'wep', 'wpa'} <= set(wifi.get('security', {})):
+ raise ConfigError('Must either use WEP or WPA security!')
+
+ if 'wep' in wifi['security']:
+ if 'key' in wifi['security']['wep'] and len(wifi['security']['wep']) > 4:
+ raise ConfigError('No more then 4 WEP keys configurable')
+ elif 'key' not in wifi['security']['wep']:
+ raise ConfigError('Security WEP configured - missing WEP keys!')
+
+ elif 'wpa' in wifi['security']:
+ wpa = wifi['security']['wpa']
+ if not any(i in ['passphrase', 'radius'] for i in wpa):
+ raise ConfigError('Misssing WPA key or RADIUS server')
+
+ if 'radius' in wpa:
+ if 'server' in wpa['radius']:
+ for server in wpa['radius']['server']:
+ if 'key' not in wpa['radius']['server'][server]:
+ raise ConfigError(f'Misssing RADIUS shared secret key for server: {server}')
+
+ if 'capabilities' in wifi:
+ capabilities = wifi['capabilities']
+ if 'vht' in capabilities:
+ if 'ht' not in capabilities:
+ raise ConfigError('Specify HT flags if you want to use VHT!')
+
+ if {'beamform', 'antenna_count'} <= set(capabilities.get('vht', {})):
+ if capabilities['vht']['antenna_count'] == '1':
+ raise ConfigError('Cannot use beam forming with just one antenna!')
+
+ if capabilities['vht']['beamform'] == 'single-user-beamformer':
+ if int(capabilities['vht']['antenna_count']) < 3:
+ # Nasty Gotcha: see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf lines 692-705
+ raise ConfigError('Single-user beam former requires at least 3 antennas!')
+
+ if 'station_interfaces' in wifi and wifi['type'] == 'station':
+ phy = wifi['physical_device']
+ if phy in wifi['station_interfaces']:
+ if len(wifi['station_interfaces'][phy]) > 0:
+ raise ConfigError('Only one station per wireless physical interface possible!')
+
+ verify_address(wifi)
+ verify_vrf(wifi)
# use common function to verify VLAN configuration
verify_vlan_config(wifi)
- conf = Config()
- # Only one wireless interface per phy can be in station mode
- base = ['interfaces', 'wireless']
- for phy in os.listdir('/sys/class/ieee80211'):
- stations = []
- for wlan in conf.list_nodes(base):
- # the following node is mandatory
- if conf.exists(base + [wlan, 'physical-device', phy]):
- tmp = conf.return_value(base + [wlan, 'type'])
- if tmp == 'station':
- stations.append(wlan)
-
- if len(stations) > 1:
- raise ConfigError('Only one station per wireless physical interface possible!')
-
return None
def generate(wifi):
- interface = wifi['intf']
+ interface = wifi['ifname']
# always stop hostapd service first before reconfiguring it
call(f'systemctl stop hostapd@{interface}.service')
@@ -513,7 +185,7 @@ def generate(wifi):
call(f'systemctl stop wpa_supplicant@{interface}.service')
# Delete config files if interface is removed
- if wifi['deleted']:
+ if 'deleted' in wifi:
if os.path.isfile(hostapd_conf.format(**wifi)):
os.unlink(hostapd_conf.format(**wifi))
@@ -522,10 +194,10 @@ def generate(wifi):
return None
- if not wifi['mac']:
+ if 'mac' not in wifi:
# http://wiki.stocksy.co.uk/wiki/Multiple_SSIDs_with_hostapd
# generate locally administered MAC address from used phy interface
- with open('/sys/class/ieee80211/{}/addresses'.format(wifi['phy']), 'r') as f:
+ with open('/sys/class/ieee80211/{physical_device}/addresses'.format(**wifi), 'r') as f:
# some PHYs tend to have multiple interfaces and thus supply multiple MAC
# addresses - we only need the first one for our calculation
tmp = f.readline().rstrip()
@@ -545,20 +217,18 @@ def generate(wifi):
wifi['mac'] = str(mac)
# render appropriate new config files depending on access-point or station mode
- if wifi['op_mode'] == 'ap':
- render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi)
+ if wifi['type'] == 'access-point':
+ render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi, trim_blocks=True)
- elif wifi['op_mode'] == 'station':
- render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl', wifi)
+ elif wifi['type'] == 'station':
+ render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl', wifi, trim_blocks=True)
return None
def apply(wifi):
- interface = wifi['intf']
- if wifi['deleted']:
- w = WiFiIf(interface)
- # delete interface
- w.remove()
+ interface = wifi['ifname']
+ if 'deleted' in wifi:
+ WiFiIf(interface).remove()
else:
# WiFi interface needs to be created on-block (e.g. mode or physical
# interface) instead of passing a ton of arguments, I just use a dict
@@ -566,97 +236,21 @@ def apply(wifi):
conf = deepcopy(WiFiIf.get_config())
# Assign WiFi instance configuration parameters to config dict
- conf['phy'] = wifi['phy']
+ conf['phy'] = wifi['physical_device']
# Finally create the new interface
w = WiFiIf(interface, **conf)
-
- # assign/remove VRF (ONLY when not a member of a bridge,
- # otherwise 'nomaster' removes it from it)
- if not wifi['is_bridge_member']:
- w.set_vrf(wifi['vrf'])
-
- # update interface description used e.g. within SNMP
- w.set_alias(wifi['description'])
-
- if wifi['dhcp_client_id']:
- w.dhcp.v4.options['client_id'] = wifi['dhcp_client_id']
-
- if wifi['dhcp_hostname']:
- w.dhcp.v4.options['hostname'] = wifi['dhcp_hostname']
-
- if wifi['dhcp_vendor_class_id']:
- w.dhcp.v4.options['vendor_class_id'] = wifi['dhcp_vendor_class_id']
-
- if wifi['dhcpv6_prm_only']:
- w.dhcp.v6.options['dhcpv6_prm_only'] = True
-
- if wifi['dhcpv6_temporary']:
- w.dhcp.v6.options['dhcpv6_temporary'] = True
-
- if wifi['dhcpv6_pd_length']:
- w.dhcp.v6.options['dhcpv6_pd_length'] = wifi['dhcpv6_pd_length']
-
- if wifi['dhcpv6_pd_interfaces']:
- w.dhcp.v6.options['dhcpv6_pd_interfaces'] = wifi['dhcpv6_pd_interfaces']
-
- # ignore link state changes
- w.set_link_detect(wifi['disable_link_detect'])
-
- # Delete old IPv6 EUI64 addresses before changing MAC
- for addr in wifi['ipv6_eui64_prefix_remove']:
- w.del_ipv6_eui64_address(addr)
-
- # Change interface MAC address - re-set to real hardware address (hw-id)
- # if custom mac is removed
- if wifi['mac']:
- w.set_mac(wifi['mac'])
- elif wifi['hw_id']:
- w.set_mac(wifi['hw_id'])
-
- # Add IPv6 EUI-based addresses
- for addr in wifi['ipv6_eui64_prefix']:
- w.add_ipv6_eui64_address(addr)
-
- # configure ARP filter configuration
- w.set_arp_filter(wifi['ip_disable_arp_filter'])
- # configure ARP accept
- w.set_arp_accept(wifi['ip_enable_arp_accept'])
- # configure ARP announce
- w.set_arp_announce(wifi['ip_enable_arp_announce'])
- # configure ARP ignore
- w.set_arp_ignore(wifi['ip_enable_arp_ignore'])
- # IPv6 accept RA
- w.set_ipv6_accept_ra(wifi['ipv6_accept_ra'])
- # IPv6 address autoconfiguration
- w.set_ipv6_autoconf(wifi['ipv6_autoconf'])
- # IPv6 forwarding
- w.set_ipv6_forwarding(wifi['ipv6_forwarding'])
- # IPv6 Duplicate Address Detection (DAD) tries
- w.set_ipv6_dad_messages(wifi['ipv6_dup_addr_detect'])
-
- # Configure interface address(es)
- # - not longer required addresses get removed first
- # - newly addresses will be added second
- for addr in wifi['address_remove']:
- w.del_addr(addr)
- for addr in wifi['address']:
- w.add_addr(addr)
-
- # apply all vlans to interface
- apply_all_vlans(w, wifi)
+ w.update(wifi)
# Enable/Disable interface - interface is always placed in
# administrative down state in WiFiIf class
- if not wifi['disable']:
- w.set_admin_state('up')
-
+ if 'disable' not in wifi:
# Physical interface is now configured. Proceed by starting hostapd or
# wpa_supplicant daemon. When type is monitor we can just skip this.
- if wifi['op_mode'] == 'ap':
+ if wifi['type'] == 'access-point':
call(f'systemctl start hostapd@{interface}.service')
- elif wifi['op_mode'] == 'station':
+ elif wifi['type'] == 'station':
call(f'systemctl start wpa_supplicant@{interface}.service')
return None