summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2019-11-23 20:44:45 +0100
committerChristian Poessinger <christian@poessinger.com>2019-11-23 20:44:45 +0100
commit9ab56f29f07a3c94a92bb7e1ffa54fcb4762a8fb (patch)
tree5c329ed1e1521b5b63fab3b72d375bde430ea7d3
parent0491fcd47adf39b0649b1f9f402f15256ad40571 (diff)
parentb7038311f72b2666e847d08d4b5fc70aede458d3 (diff)
downloadvyos-1x-9ab56f29f07a3c94a92bb7e1ffa54fcb4762a8fb.tar.gz
vyos-1x-9ab56f29f07a3c94a92bb7e1ffa54fcb4762a8fb.zip
Merge branch 't1627-wireless' of github.com:c-po/vyos-1x into current
* 't1627-wireless' of github.com:c-po/vyos-1x: wireless: T1627: support station mode wireless: T1627: support DHCP(v6) addresses wireless: T1627: add support for RADIUS source-address wireless: T1627: RADIUS servers must have a key specified wireless: T1627: change RADIUS CLI syntax l2tp: harmonize RADIUS wording wireless: T1627: re-order WPA key in hostapd config wireless: T1627: change priority from 318 to 400 wireless: T1627: fix generated ht_capab and vht_capab wireless: T1627: fix regex for 'ht channel-set-width' wireless: T1627: config migrator does not support camel casing wireless: T1627: initial rewrite of show-wireless.pl in Python wireless: T1627: add op-mode commands wireless: T1627: initial rewrite in XML/Python style
-rw-r--r--Makefile1
-rw-r--r--debian/control11
-rw-r--r--interface-definitions/interfaces-wireless.xml963
-rw-r--r--interface-definitions/l2tp-server.xml4
-rw-r--r--op-mode-definitions/wireless.xml119
-rwxr-xr-xsrc/completion/list_wireless_phys.sh5
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py1471
-rwxr-xr-xsrc/conf_mode/system-wifi-regdom.py111
-rwxr-xr-xsrc/migration-scripts/interfaces/3-to-497
-rwxr-xr-xsrc/op_mode/show_wireless.py158
10 files changed, 2936 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 1321126ae..7986ccdc9 100644
--- a/Makefile
+++ b/Makefile
@@ -34,6 +34,7 @@ op_mode_definitions:
# XXX: delete top level op mode node.def's that now live in other packages
rm -f $(OP_TMPL_DIR)/clear/node.def
+ rm -f $(OP_TMPL_DIR)/clear/interfaces/node.def
rm -f $(OP_TMPL_DIR)/set/node.def
rm -f $(OP_TMPL_DIR)/show/node.def
rm -f $(OP_TMPL_DIR)/show/interfaces/node.def
diff --git a/debian/control b/debian/control
index b77a79ed9..fa9f3b489 100644
--- a/debian/control
+++ b/debian/control
@@ -64,8 +64,15 @@ Depends: python3,
mtr-tiny,
telnet,
traceroute,
- ssl-cert, nginx-light,
- vyos-qat-kernel-modules, vyos-qat-utilities
+ vyos-qat-kernel-modules,
+ vyos-qat-utilities,
+ ssl-cert,
+ nginx-light,
+ hostapd (>= 0.6.8),
+ wpasupplicant (>= 0.6.7),
+ iw,
+ crda,
+ wireless-regdb,
${shlibs:Depends},
${misc:Depends}
Description: VyOS configuration scripts and data
diff --git a/interface-definitions/interfaces-wireless.xml b/interface-definitions/interfaces-wireless.xml
new file mode 100644
index 000000000..6bc49dddb
--- /dev/null
+++ b/interface-definitions/interfaces-wireless.xml
@@ -0,0 +1,963 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="interfaces">
+ <children>
+ <tagNode name="wireless" owner="${vyos_conf_scripts_dir}/interfaces-wireless.py">
+ <properties>
+ <help>Wireless network interface (WiFi/WLAN)</help>
+ <priority>400</priority>
+ <constraint>
+ <regex>wlan[0-9]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Wireless interface must be named wlanN</constraintErrorMessage>
+ <valueHelp>
+ <format>wlanN</format>
+ <description>Wireless (WiFi/WLAN) interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <completionHelp>
+ <list>dhcp dhcpv6</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcp</format>
+ <description>Dynamic Host Configuration Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcpv6</format>
+ <description>Dynamic Host Configuration Protocol for IPv6</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-cidr"/>
+ <regex>(dhcp|dhcpv6)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <node name="capabilities">
+ <properties>
+ <help>HT and VHT capabilities for your card</help>
+ </properties>
+ <children>
+ <node name="ht">
+ <properties>
+ <help>HT (High Throughput) settings</help>
+ </properties>
+ <children>
+ <leafNode name="40mhz-incapable">
+ <properties>
+ <help>40MHz intolerance, use 20MHz only!</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="auto-powersave">
+ <properties>
+ <help>Enable WMM-PS unscheduled automatic power aave delivery [U-APSD]</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="channel-set-width">
+ <properties>
+ <help>Supported channel set width</help>
+ <completionHelp>
+ <list>ht20 ht40+ ht40-</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ht20</format>
+ <description>Supported channel set width both 20 MHz only</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ht40+</format>
+ <description>Supported channel set width both 20 MHz and 40 MHz with secondary channel above primary channel</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ht40-</format>
+ <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>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="delayed-block-ack">
+ <properties>
+ <help>Enable HT-delayed block ack</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="dsss-cck-40">
+ <properties>
+ <help>Enable DSSS_CCK-40</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="greenfield">
+ <properties>
+ <help>Enable HT-greenfield</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ldpc">
+ <properties>
+ <help>Enable LDPC coding capability</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="lsig-protection">
+ <properties>
+ <help>Enable L-SIG TXOP protection capability</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="max-amsdu">
+ <properties>
+ <help>Set maximum A-MSDU length</help>
+ <completionHelp>
+ <list>3839 7935</list>
+ </completionHelp>
+ <valueHelp>
+ <format>3839</format>
+ <description>Set maximum A-MSDU length to 3839 octets</description>
+ </valueHelp>
+ <valueHelp>
+ <format>7935</format>
+ <description>Set maximum A-MSDU length to 7935 octets</description>
+ </valueHelp>
+ <constraint>
+ <regex>(3839|7935)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="short-gi">
+ <properties>
+ <help>Short GI capabilities</help>
+ <completionHelp>
+ <list>20 40</list>
+ </completionHelp>
+ <valueHelp>
+ <format>20</format>
+ <description>Short GI for 20 MHz</description>
+ </valueHelp>
+ <valueHelp>
+ <format>40</format>
+ <description>Short GI for 40 MHz</description>
+ </valueHelp>
+ <constraint>
+ <regex>(20|40)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="smps">
+ <properties>
+ <help>Spatial Multiplexing Power Save (SMPS) settings</help>
+ <completionHelp>
+ <list>static dynamic</list>
+ </completionHelp>
+ <valueHelp>
+ <format>static</format>
+ <description>STATIC Spatial Multiplexing (SM) Power Save</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dynamic</format>
+ <description>DYNAMIC Spatial Multiplexing (SM) Power Save</description>
+ </valueHelp>
+ <constraint>
+ <regex>(static|dynamic)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <node name="stbc">
+ <properties>
+ <help>Support for sending and receiving PPDU using STBC (Space Time Block Coding)</help>
+ </properties>
+ <children>
+ <leafNode name="rx">
+ <properties>
+ <help>Enable receiving PPDU using STBC (Space Time Block Coding)</help>
+ <valueHelp>
+ <format>[1-3]+</format>
+ <description>Number of spacial streams that can use RX STBC</description>
+ </valueHelp>
+ <constraint>
+ <regex>[1-3]+</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid capability item</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="tx">
+ <properties>
+ <help>Enable sending PPDU using STBC (Space Time Block Coding)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="require-ht">
+ <properties>
+ <help>Require stations to support HT PHY (reject association if they do not)</help>
+ <completionHelp>
+ <script>echo If you reject non-HT, you also disable 802.11g</script>
+ </completionHelp>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="require-vht">
+ <properties>
+ <help>Require stations to support VHT PHY (reject association if they do not)</help>
+ <completionHelp>
+ <script>echo If you reject non-VHT, you also disable 802.11n</script>
+ </completionHelp>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="vht">
+ <properties>
+ <help>VHT (Very High Throughput) settings</help>
+ </properties>
+ <children>
+ <leafNode name="antenna-count">
+ <properties>
+ <help>Number of antennas on this card</help>
+ <valueHelp>
+ <format>1-9</format>
+ <description>Number of antennas for this card</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-9"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="antenna-pattern-fixed">
+ <properties>
+ <help>Set if antenna pattern does not change during the lifetime of an association</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="beamform">
+ <properties>
+ <help>Beamforming capabilities</help>
+ <completionHelp>
+ <list>single-user-beamformer single-user-beamformee multi-user-beamformer multi-user-beamformee</list>
+ </completionHelp>
+ <valueHelp>
+ <format>single-user-beamformer</format>
+ <description>Support for operation as single user beamformer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>single-user-beamformee</format>
+ <description>Support for operation as single user beamformee</description>
+ </valueHelp>
+ <valueHelp>
+ <format>multi-user-beamformer</format>
+ <description>Support for operation as multi user beamformer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>multi-user-beamformee</format>
+ <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>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <node name="center-channel-freq">
+ <properties>
+ <help>VHT operating channel center frequency</help>
+ </properties>
+ <children>
+ <leafNode name="freq-1">
+ <properties>
+ <help>VHT operating channel center frequency - center freq 1 (for use with 80, 80+80 and 160 modes)</help>
+ <valueHelp>
+ <format>&lt;34-173&gt;</format>
+ <description>5Ghz (802.11 a/h/j/n/ac) center channel index (use 42 for primary 80MHz channel 36)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 34-173"/>
+ </constraint>
+ <constraintErrorMessage>Channel center value must be between 34 and 173</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="freq-2">
+ <properties>
+ <help>VHT operating channel center frequency - center freq 2 (for use with the 80+80 mode)</help>
+ <valueHelp>
+ <format>34-173</format>
+ <description>5Ghz (802.11 a/h/j/n/ac) center channel index (use 58 for primary 80MHz channel 52)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 34-173"/>
+ </constraint>
+ <constraintErrorMessage>Channel center value must be between 34 and 173</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="channel-set-width">
+ <properties>
+ <help>VHT operating Channel width</help>
+ <completionHelp>
+ <list>0 1 2 3</list>
+ </completionHelp>
+ <valueHelp>
+ <format>0</format>
+ <description>20 or 40 MHz channel width (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>1</format>
+ <description>80 MHz channel width</description>
+ </valueHelp>
+ <valueHelp>
+ <format>2</format>
+ <description>160 MHz channel width</description>
+ </valueHelp>
+ <valueHelp>
+ <format>3</format>
+ <description>80+80 MHz channel width</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-3"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="ldpc">
+ <properties>
+ <help>Enable LDPC (Low Density Parity Check) coding capability</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="link-adaptation">
+ <properties>
+ <help>VHT link adaptation capabilities</help>
+ <completionHelp>
+ <list>single-user-beamformer single-user-beamformee multi-user-beamformer multi-user-beamformee</list>
+ </completionHelp>
+ <valueHelp>
+ <format>unsolicited</format>
+ <description>Station provides only unsolicited VHT MFB</description>
+ </valueHelp>
+ <valueHelp>
+ <format>both</format>
+ <description>Station can provide VHT MFB in response to VHT MRQ and unsolicited VHT MFB</description>
+ </valueHelp>
+ <constraint>
+ <regex>(unsolicited|both)</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid capability item</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="max-mpdu-exp">
+ <properties>
+ <help>Set the maximum length of A-MPDU pre-EOF padding that the station can receive</help>
+ <valueHelp>
+ <format>&lt;0-7&gt;</format>
+ <description>Maximum length of A-MPDU pre-EOF padding = 2 pow(13 + x) -1 octets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-7"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="max-mpdu">
+ <properties>
+ <help>Increase Maximum MPDU length to 7991 or 11454 octets (otherwise: 3895 octets)</help>
+ <completionHelp>
+ <list>7991 11454</list>
+ </completionHelp>
+ <valueHelp>
+ <format>7991</format>
+ <description>ncrease Maximum MPDU length to 7991 octets</description>
+ </valueHelp>
+ <valueHelp>
+ <format>11454</format>
+ <description>ncrease Maximum MPDU length to 11454 octets</description>
+ </valueHelp>
+ <constraint>
+ <regex>(7991|11454)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="short-gi">
+ <properties>
+ <help>Short GI capabilities</help>
+ <completionHelp>
+ <list>80 160</list>
+ </completionHelp>
+ <valueHelp>
+ <format>80</format>
+ <description>Short GI for 80 MHz</description>
+ </valueHelp>
+ <valueHelp>
+ <format>160</format>
+ <description>Short GI for 160 MHz</description>
+ </valueHelp>
+ <constraint>
+ <regex>(80|160)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <node name="stbc">
+ <properties>
+ <help>Support for sending and receiving PPDU using STBC (Space Time Block Coding)</help>
+ </properties>
+ <children>
+ <leafNode name="rx">
+ <properties>
+ <help>Enable receiving PPDU using STBC (Space Time Block Coding)</help>
+ <valueHelp>
+ <format>[1-4]+</format>
+ <description>Number of spacial streams that can use RX STBC</description>
+ </valueHelp>
+ <constraint>
+ <regex>[1-4]+</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid capability item</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="tx">
+ <properties>
+ <help>Enable sending PPDU using STBC (Space Time Block Coding)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="tx-powersave">
+ <properties>
+ <help>Enable VHT TXOP Power Save Mode</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="vht-cf">
+ <properties>
+ <help>Station supports receiving VHT variant HT Control field</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="channel">
+ <properties>
+ <help>Wireless radio channel (use 0 for ACS auto channel selection)</help>
+ <valueHelp>
+ <format>&lt;1-14&gt;</format>
+ <description>2.4Ghz (802.11 b/g/n) Channel</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;0,34-173&gt;</format>
+ <description>5Ghz (802.11 a/h/j/n/ac) Channel</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-0 --range 1-14 --range 34-173"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Interface description</help>
+ <constraint>
+ <regex>.{1,256}$</regex>
+ </constraint>
+ <constraintErrorMessage>Interface description too long (limit 256 characters)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="dhcp-options">
+ <properties>
+ <help>DHCP options</help>
+ </properties>
+ <children>
+ <leafNode name="client-id">
+ <properties>
+ <help>DHCP client identifier</help>
+ </properties>
+ </leafNode>
+ <leafNode name="host-name">
+ <properties>
+ <help>DHCP client host name (overrides system host name)</help>
+ </properties>
+ </leafNode>
+ <leafNode name="vendor-class-id">
+ <properties>
+ <help>DHCP client vendor type</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="dhcpv6-options">
+ <properties>
+ <help>DHCPv6 options</help>
+ <priority>319</priority>
+ </properties>
+ <children>
+ <leafNode name="parameters-only">
+ <properties>
+ <help>Acquire only config parameters, no address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="temporary">
+ <properties>
+ <help>IPv6 "temporary" address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-broadcast-ssid">
+ <properties>
+ <help>Disable broadcast of SSID from access-point</help>
+ </properties>
+ </leafNode>
+ <leafNode name="disable-link-detect">
+ <properties>
+ <help>Ignore link state changes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable this bridge interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="expunge-failing-stations">
+ <properties>
+ <help>Disassociate stations based on excessive transmission failures</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="hw-id">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="isolate-stations">
+ <properties>
+ <help>Isolate stations on the AP so they cannot see each other</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="max-stations">
+ <properties>
+ <help>Maximum number of wireless radio stations. Excess stations will be rejected upon authentication request.</help>
+ <valueHelp>
+ <format>&lt;1-2007&gt;</format>
+ <description>Number of allowed stations</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-2007"/>
+ </constraint>
+ <constraintErrorMessage>Number of stations must be between 1 and 2007</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="mgmt-frame-protection">
+ <properties>
+ <help>Management Frame Protection (MFP) according to IEEE 802.11w</help>
+ <completionHelp>
+ <list>disabled optional required</list>
+ </completionHelp>
+ <valueHelp>
+ <format>disabled</format>
+ <description>no MFP (hostapd default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>optional</format>
+ <description>MFP optional</description>
+ </valueHelp>
+ <valueHelp>
+ <format>required</format>
+ <description>MFP enforced</description>
+ </valueHelp>
+ <constraint>
+ <regex>(disabled|optional|required)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mode">
+ <properties>
+ <help>Wireless radio mode</help>
+ <completionHelp>
+ <list>a b g n ac</list>
+ </completionHelp>
+ <valueHelp>
+ <format>a</format>
+ <description>802.11a - 54 Mbits/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>b</format>
+ <description>802.11b - 11 Mbits/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>g</format>
+ <description>802.11g - 54 Mbits/sec (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>n</format>
+ <description>802.11n - 600 Mbits/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ac</format>
+ <description>802.11ac - 1300 Mbits/sec</description>
+ </valueHelp>
+ <constraint>
+ <regex>(a|b|g|n|ac)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="physical-device">
+ <properties>
+ <help>Wireless physical device</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_wireless_phys.sh</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="reduce-transmit-power">
+ <properties>
+ <help>Transmission power reduction in dBm</help>
+ <valueHelp>
+ <format>&lt;0-255&gt;</format>
+ <description>TX power reduction in dBm</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ <constraintErrorMessage>dBm value must be between 0 and 255</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="security">
+ <properties>
+ <help>Wireless security settings</help>
+ </properties>
+ <children>
+ <node name="wep">
+ <properties>
+ <help>Wired Equivalent Privacy (WEP) parameters</help>
+ </properties>
+ <children>
+ <leafNode name="key">
+ <properties>
+ <help>WEP encryption key</help>
+ <valueHelp>
+ <format>&lt;hexdigits&gt;</format>
+ <description>Wired Equivalent Privacy key</description>
+ </valueHelp>
+ <constraint>
+ <regex>([a-fA-F0-9]{10}|[a-fA-F0-9]{26}|[a-fA-F0-9]{32})</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid WEP key</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="wpa">
+ <properties>
+ <help>Wifi Protected Access (WPA) parameters</help>
+ </properties>
+ <children>
+ <leafNode name="cipher">
+ <properties>
+ <help>Cipher suite for WPA</help>
+ <completionHelp>
+ <list>TKIP CCMP</list>
+ </completionHelp>
+ <valueHelp>
+ <format>CCMP</format>
+ <description>AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]</description>
+ </valueHelp>
+ <valueHelp>
+ <format>TKIP</format>
+ <description>Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]</description>
+ </valueHelp>
+ <constraint>
+ <regex>(CCMP|TKIP)</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid WEP key</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="mode">
+ <properties>
+ <help>WPA mode</help>
+ <completionHelp>
+ <list>wpa wpa2 both</list>
+ </completionHelp>
+ <valueHelp>
+ <format>wpa</format>
+ <description>WPA (IEEE 802.11i/D3.0)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>wpa2</format>
+ <description>WPA2 (full IEEE 802.11i/RSN)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>both</format>
+ <description>Allow both WPA and WPA2</description>
+ </valueHelp>
+ <constraint>
+ <regex>(wpa|wpa2|both)</regex>
+ </constraint>
+ <constraintErrorMessage>Unknown WPA mode</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="passphrase">
+ <properties>
+ <help>WPA personal shared pass phrase. If you are
+ using special characters in the WPA passphrase then single
+ quotes are required.</help>
+ <valueHelp>
+ <format>&lt;text&gt;</format>
+ <description>Passphrase of at least 8 but not more than 63 printable characters</description>
+ </valueHelp>
+ <constraint>
+ <regex>.{8,63}$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid WPA pass phrase, must be 8 to 63 printable characters!</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="radius">
+ <properties>
+ <help>RADIUS specific configuration</help>
+ </properties>
+ <children>
+ <leafNode name="source-address">
+ <properties>
+ <help>RADIUS client forced local IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address of RADIUS server</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <tagNode name="server">
+ <properties>
+ <help>IP address of RADIUS server</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address of RADIUS server</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="accounting">
+ <properties>
+ <help>Enable RADIUS server to receive accounting info</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
+ <properties>
+ <help>RADIUS server port (default: 1812)</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>RADIUS server port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="key">
+ <properties>
+ <help>RADIUS shared secret key</help>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="ssid">
+ <properties>
+ <help>Wireless access-point service set identifier (SSID)</help>
+ <constraint>
+ <regex>.{1,32}$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid SSID</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>Wireless device type for this interface</help>
+ <completionHelp>
+ <list>access-point station monitor</list>
+ </completionHelp>
+ <valueHelp>
+ <format>access-point</format>
+ <description>Access-point forwards packets between other nodes</description>
+ </valueHelp>
+ <valueHelp>
+ <format>station</format>
+ <description>Connects to another access point</description>
+ </valueHelp>
+ <valueHelp>
+ <format>monitor</format>
+ <description>Passively monitor all packets on the frequency/channel</description>
+ </valueHelp>
+ <constraint>
+ <regex>(access-point|station|monitor)</regex>
+ </constraint>
+ <constraintErrorMessage>Type must be access-point, station or monitor</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <tagNode name="vif">
+ <properties>
+ <help>Virtual Local Area Network (VLAN) ID</help>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4094"/>
+ </constraint>
+ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <completionHelp>
+ <list>dhcp dhcpv6</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcp</format>
+ <description>Dynamic Host Configuration Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcpv6</format>
+ <description>Dynamic Host Configuration Protocol for IPv6</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-cidr"/>
+ <regex>(dhcp|dhcpv6)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Interface description</help>
+ <constraint>
+ <regex>^.{1,256}$</regex>
+ </constraint>
+ <constraintErrorMessage>Interface description too long (limit 256 characters)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="dhcpv6-options">
+ <properties>
+ <help>DHCPv6 options</help>
+ </properties>
+ <children>
+ <leafNode name="parameters-only">
+ <properties>
+ <help>Acquire only config parameters, no address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="temporary">
+ <properties>
+ <help>IPv6 "temporary" address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-link-detect">
+ <properties>
+ <help>Ignore link state changes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable this bridge interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </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/interface-definitions/l2tp-server.xml b/interface-definitions/l2tp-server.xml
index f795c96db..e76b92b23 100644
--- a/interface-definitions/l2tp-server.xml
+++ b/interface-definitions/l2tp-server.xml
@@ -452,10 +452,10 @@
<children>
<tagNode name="server">
<properties>
- <help>IP address of radius server</help>
+ <help>IP address of RADIUS server</help>
<valueHelp>
<format>ipv4</format>
- <description>IP address of RADIUS server</description>
+ <description>IPv4 address of RADIUS server</description>
</valueHelp>
</properties>
<children>
diff --git a/op-mode-definitions/wireless.xml b/op-mode-definitions/wireless.xml
new file mode 100644
index 000000000..c3c6dee59
--- /dev/null
+++ b/op-mode-definitions/wireless.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="clear">
+ <children>
+ <node name="interfaces">
+ <children>
+ <node name="wireless">
+ <properties>
+ <help>Clear wireless interface information</help>
+ </properties>
+ <children>
+ <leafNode name="counters">
+ <properties>
+ <help>Clear all wireless interface counters</help>
+ </properties>
+ <command>sudo ${vyatta_bindir}/sudo-users/vyatta-show-interfaces.pl --action=clear --intf-type="$3"</command>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="wireless">
+ <properties>
+ <help>Clear interface information for a given wireless interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --type wireless</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="counters">
+ <properties>
+ <help>Clear all wireless interface counters</help>
+ </properties>
+ <command>sudo ${vyatta_bindir}/sudo-users/vyatta-show-interfaces.pl --action=clear --intf="$4"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="show">
+ <children>
+ <node name="interfaces">
+ <children>
+ <node name="wireless">
+ <properties>
+ <help>Show wireless interface information</help>
+ </properties>
+ <command>${vyatta_bindir}/vyatta-show-interfaces.pl --intf-type=wireless --action=show-brief</command>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed wireless interface information</help>
+ </properties>
+ <command>${vyatta_bindir}/vyatta-show-interfaces.pl --intf-type=wireless --action=show</command>
+ </leafNode>
+ <leafNode name="info">
+ <properties>
+ <help>Show wireless interface configuration</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_wireless.py --brief</command>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="wireless">
+ <properties>
+ <help>Show specified wireless interface information</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --type wireless</script>
+ </completionHelp>
+ </properties>
+ <command>${vyatta_bindir}/vyatta-show-interfaces.pl --intf="$4"</command>
+ <children>
+ <leafNode name="brief">
+ <properties>
+ <help>Show summary of the specified wireless interface information</help>
+ </properties>
+ <command>${vyatta_bindir}/vyatta-show-interfaces.pl --intf="$4" --action=show-brief</command>
+ </leafNode>
+ <node name="scan">
+ <properties>
+ <help>Show summary of the specified wireless interface information</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/show_wireless.py --scan "$4"</command>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed scan results</help>
+ </properties>
+ <command>sudo /sbin/iw dev "$4" scan ap-force</command>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="stations">
+ <properties>
+ <help>Show specified wireless interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_wireless.py --stations "$4"</command>
+ </leafNode>
+ <tagNode name="vif">
+ <properties>
+ <help>Show specified virtual network interface (vif) information</help>
+ </properties>
+ <command>${vyatta_bindir}/vyatta-show-interfaces.pl --intf="$4.$6"</command>
+ <children>
+ <leafNode name="brief">
+ <properties>
+ <help>Show summary of specified virtual network interface (vif) information</help>
+ </properties>
+ <command>${vyatta_bindir}/vyatta-show-interfaces.pl --intf="$4.$6" --action=show-brief</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/src/completion/list_wireless_phys.sh b/src/completion/list_wireless_phys.sh
new file mode 100755
index 000000000..70b8d1ff9
--- /dev/null
+++ b/src/completion/list_wireless_phys.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -d /sys/class/ieee80211 ]; then
+ ls -x /sys/class/ieee80211
+fi
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
new file mode 100755
index 000000000..17b0876a0
--- /dev/null
+++ b/src/conf_mode/interfaces-wireless.py
@@ -0,0 +1,1471 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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 jinja2 import Template
+from copy import deepcopy
+from sys import exit
+from stat import S_IRWXU,S_IRGRP,S_IXGRP,S_IROTH,S_IXOTH
+from pwd import getpwnam
+from grp import getgrnam
+
+from subprocess import Popen, PIPE
+from psutil import pid_exists
+
+from vyos.ifconfig import EthernetIf, VLANIf
+from vyos.configdict import list_diff, vlan_to_dict
+from vyos.config import Config
+from vyos import ConfigError
+
+user = 'root'
+group = 'vyattacfg'
+
+# Please be careful if you edit the template.
+config_hostapd_tmpl = """
+### Autogenerated by interfaces-wireless.py ###
+{% if description %}
+# Description: {{ description }}
+# User-friendly description of device; up to 32 octets encoded in UTF-8
+device_name={{ description | truncate(32, True) }}
+{% endif %}
+
+# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
+# 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 }}
+
+# Driver interface type (hostap/wired/none/nl80211/bsd);
+# default: hostap). nl80211 is used with all Linux mac80211 drivers.
+# Use driver=none if building hostapd as a standalone RADIUS server that does
+# not control any wireless/wired driver.
+driver=nl80211
+
+#
+# What about bridge?
+# bridge=br0
+# wds_sta=1
+#
+
+# Levels (minimum value for logged events):
+# 0 = verbose debugging
+# 1 = debugging
+# 2 = informational messages
+# 3 = notification
+# 4 = warning
+logger_syslog=-1
+logger_syslog_level=0
+logger_stdout=-1
+logger_stdout_level=0
+
+{%- 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.
+country_code={{ country_code }}
+
+# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
+# channels and transmit power levels based on the regulatory limits. The
+# country_code setting must be configured with the correct country for
+# IEEE 802.11d functions.
+ieee80211d=1
+{% endif %}
+
+{%- if ssid %}
+
+# SSID to be used in IEEE 802.11 management frames
+ssid={{ ssid }}
+{% endif %}
+
+{%- 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
+# channel will need to be configured separately with iwconfig.
+#
+# If CONFIG_ACS build option is enabled, the channel can be selected
+# automatically at run time by setting channel=acs_survey or channel=0, both of
+# which will enable the ACS survey based algorithm.
+channel={{ channel }}
+{% endif %}
+
+{%- 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
+# 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 -%}
+hw_mode=g
+ieee80211n=1
+{% elif 'ac' in mode -%}
+hw_mode=a
+ieee80211h=1
+ieee80211ac=1
+{% else -%}
+hw_mode={{ mode }}
+{% endif %}
+{% endif %}
+
+# ieee80211w: Whether management frame protection (MFP) is enabled
+# 0 = disabled (default)
+# 1 = optional
+# 2 = required
+{% if 'disabled' in mgmt_frame_protection -%}
+ieee80211w=0
+{% elif 'optional' in mgmt_frame_protection -%}
+ieee80211w=1
+{% elif 'required' in mgmt_frame_protection -%}
+ieee80211w=2
+{% endif %}
+
+# 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
+# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz
+# with secondary channel above the primary channel
+# (20 MHz only if neither is set)
+# Note: There are limits on which channels can be used with HT40- and
+# HT40+. Following table shows the channels that may be available for
+# HT40- and HT40+ use per IEEE 802.11n Annex J:
+# freq HT40- HT40+
+# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan)
+# 5 GHz 40,48,56,64 36,44,52,60
+# (depending on the location, not all of these channels may be available
+# for use)
+# Please note that 40 MHz channels may switch their primary and secondary
+# channels if needed or creation of 40 MHz channel maybe rejected based
+# on overlapping BSSes. These changes are done automatically when hostapd
+# is setting up the 40 MHz channel.
+# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC]
+# (SMPS disabled if neither is set)
+# HT-greenfield: [GF] (disabled if not set)
+# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
+# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
+# Tx STBC: [TX-STBC] (disabled if not set)
+# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial
+# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC
+# disabled if none of these set
+# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set)
+# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not
+# set)
+# 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 %}
+
+# Required for full HT and VHT functionality
+wme_enabled=1
+
+{% if cap_ht_powersave -%}
+# 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 %}
+
+{% if cap_req_ht -%}
+# Require stations to support HT PHY (reject association if they do not)
+require_ht=1
+{% endif %}
+
+# vht_capab: VHT capabilities (list of flags)
+#
+# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
+# Indicates maximum MPDU length
+# 0 = 3895 octets (default)
+# 1 = 7991 octets
+# 2 = 11454 octets
+# 3 = reserved
+#
+# supported_chan_width: [VHT160] [VHT160-80PLUS80]
+# Indicates supported Channel widths
+# 0 = 160 MHz & 80+80 channel widths are not supported (default)
+# 1 = 160 MHz channel width is supported
+# 2 = 160 MHz & 80+80 channel widths are supported
+# 3 = reserved
+#
+# Rx LDPC coding capability: [RXLDPC]
+# Indicates support for receiving LDPC coded pkts
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 80 MHz: [SHORT-GI-80]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 80Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 160 MHz: [SHORT-GI-160]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 160Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Tx STBC: [TX-STBC-2BY1]
+# Indicates support for the transmission of at least 2x1 STBC
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
+# Indicates support for the reception of PPDUs using STBC
+# 0 = Not supported (default)
+# 1 = support of one spatial stream
+# 2 = support of one and two spatial streams
+# 3 = support of one, two and three spatial streams
+# 4 = support of one, two, three and four spatial streams
+# 5,6,7 = reserved
+#
+# SU Beamformer Capable: [SU-BEAMFORMER]
+# Indicates support for operation as a single user beamformer
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# SU Beamformee Capable: [SU-BEAMFORMEE]
+# Indicates support for operation as a single user beamformee
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Compressed Steering Number of Beamformer Antennas Supported:
+# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
+# Beamformee's capability indicating the maximum number of beamformer
+# antennas the beamformee can support when sending compressed beamforming
+# feedback
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# Number of Sounding Dimensions:
+# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
+# Beamformer's capability indicating the maximum value of the NUM_STS parameter
+# in the TXVECTOR of a VHT NDP
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# MU Beamformer Capable: [MU-BEAMFORMER]
+# Indicates support for operation as an MU beamformer
+# 0 = Not supported or sent by Non-AP STA (default)
+# 1 = Supported
+#
+# VHT TXOP PS: [VHT-TXOP-PS]
+# Indicates whether or not the AP supports VHT TXOP Power Save Mode
+# or whether or not the STA is in VHT TXOP Power Save mode
+# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
+# mode
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
+# mode
+#
+# +HTC-VHT Capable: [HTC-VHT]
+# Indicates whether or not the STA supports receiving a VHT variant HT Control
+# field.
+# 0 = Not supported (default)
+# 1 = supported
+#
+# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
+# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
+# This field is an integer in the range of 0 to 7.
+# The length defined by this field is equal to
+# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
+#
+# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
+# Indicates whether or not the STA supports link adaptation using VHT variant
+# HT Control field
+# If +HTC-VHTcapable is 1
+# 0 = (no feedback) if the STA does not provide VHT MFB (default)
+# 1 = reserved
+# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
+# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
+# STA provides unsolicited VHT MFB
+# Reserved if +HTC-VHTcapable is 0
+#
+# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
+# Indicates the possibility of Rx antenna pattern change
+# 0 = Rx antenna pattern might change during the lifetime of an association
+# 1 = Rx antenna pattern does not change during the lifetime of an association
+#
+# 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 -%}
+[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 -%}
+{%- for beamform in cap_vht_beamform -%}
+{%- if 'single-user-beamformer' in beamform -%}
+[BF-ANTENNA-{{ cap_vht_antenna_cnt|int -1 }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt|int -1}}]
+{%- else -%}
+[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}]
+{%- endif -%}
+{%- endfor -%}
+{%- 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 -%}
+ieee80211n=0
+# Require stations to support VHT PHY (reject association if they do not)
+require_vht=1
+{% 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 -%}
+# 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)
+# 1 = send empty (length=0) SSID in beacon and ignore probe request for
+# broadcast SSID
+# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
+# with some clients that do not support empty SSID) and ignore probe
+# requests for broadcast SSID
+ignore_broadcast_ssid=1
+{% endif %}
+
+# Station MAC address -based authentication
+# Please note that this kind of access control requires a driver that uses
+# hostapd to take care of management frame processing and as such, this can be
+# used with driver=hostap or driver=nl80211, but not with driver=atheros.
+# 0 = accept unless in deny list
+# 1 = deny unless in accept list
+# 2 = use external RADIUS server (accept/deny lists are searched first)
+macaddr_acl=0
+
+{% if max_stations -%}
+# 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.
+# (default: 2007)
+max_num_sta={{ max_stations }}
+{% endif %}
+
+{% if isolate_stations -%}
+# 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 -%}
+# 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
+# Control. This can be used only with ieee80211d=1.
+# Valid values are 0..255.
+local_pwr_constraint={{ reduce_transmit_power }}
+{% endif %}
+
+{% if expunge_failing_stations -%}
+# 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 -%}
+# 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.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=2
+
+# WEP rekeying (disabled if key lengths are not set or are set to 0)
+# Key lengths for default/broadcast and individual/unicast keys:
+# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
+# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
+wep_key_len_broadcast=5
+wep_key_len_unicast=5
+
+# Static WEP key configuration
+#
+# The key number to use when transmitting.
+# It must be between 0 and 3, and the corresponding key must be set.
+# default: not set
+wep_default_key=0
+
+# The WEP keys to use.
+# A key may be a quoted string or unquoted hexadecimal digits.
+# The key length should be 5, 13, or 16 characters, or 10, 26, or 32
+# 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 %}
+
+{% elif sec_wpa -%}
+##### WPA/IEEE 802.11i configuration ##########################################
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+{% if 'both' in sec_wpa_mode -%}
+wpa=3
+{%- elif 'wpa2' in sec_wpa_mode -%}
+wpa=2
+{%- elif 'wpa' in sec_wpa_mode -%}
+wpa=1
+{%- endif %}
+
+{% if sec_wpa_cipher -%}
+# 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)
+# TKIP = Temporal Key Integrity Protocol
+# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key
+# GCMP = Galois/counter mode protocol (GCMP-128)
+# GCMP-256 = Galois/counter mode protocol with 256-bit key
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# 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 -%}
+# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
+rsn_pairwise={{ sec_wpa_cipher | join(" ") }}
+{% else -%}
+# Pairwise cipher for WPA (v1) (default: TKIP)
+wpa_pairwise={{ sec_wpa_cipher | join(" ") }}
+{%- endif -%}
+{% endif %}
+
+{% if sec_wpa_passphrase -%}
+# 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.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# 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 }}
+
+# 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_key_mgmt=WPA-PSK
+
+{% elif sec_wpa_radius -%}
+##### IEEE 802.1X-2004 related configuration ##################################
+# Require IEEE 802.1X authorization
+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-EAP = WPA-Enterprise / WPA2-Enterprise
+# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
+wpa_key_mgmt=WPA-EAP
+
+{% if sec_wpa_radius_source -%}
+# 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 }}
+{% endif %}
+
+{% for radius in sec_wpa_radius -%}
+# RADIUS authentication server
+auth_server_addr={{ radius.server }}
+auth_server_port={{ radius.port }}
+auth_server_shared_secret={{ radius.key }}
+{% 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 %}
+{% endfor %}
+
+{% endif %}
+
+{% else %}
+# Open system
+auth_algs=1
+{% endif %}
+
+# TX queue parameters (EDCF / bursting)
+# tx_queue_<queue name>_<param>
+# queues: data0, data1, data2, data3
+# (data0 is the highest priority queue)
+# parameters:
+# aifs: AIFS (default 2)
+# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
+# 16383, 32767)
+# cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
+# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
+# bursting
+#
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# These parameters are used by the access point when transmitting frames
+# to the clients.
+#
+# Low priority / AC_BK = background
+tx_queue_data3_aifs=7
+tx_queue_data3_cwmin=15
+tx_queue_data3_cwmax=1023
+tx_queue_data3_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
+#
+# Normal priority / AC_BE = best effort
+tx_queue_data2_aifs=3
+tx_queue_data2_cwmin=15
+tx_queue_data2_cwmax=63
+tx_queue_data2_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
+#
+# High priority / AC_VI = video
+tx_queue_data1_aifs=1
+tx_queue_data1_cwmin=7
+tx_queue_data1_cwmax=15
+tx_queue_data1_burst=3.0
+# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
+#
+# Highest priority / AC_VO = voice
+tx_queue_data0_aifs=1
+tx_queue_data0_cwmin=3
+tx_queue_data0_cwmax=7
+tx_queue_data0_burst=1.5
+
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# for 802.11a or 802.11g networks
+# These parameters are sent to WMM clients when they associate.
+# The parameters will be used by WMM clients for frames transmitted to the
+# access point.
+#
+# note - txop_limit is in units of 32microseconds
+# note - acm is admission control mandatory flag. 0 = admission control not
+# required, 1 = mandatory
+# note - Here cwMin and cmMax are in exponent form. The actual cw value used
+# will be (2^n)-1 where n is the value given here. The allowed range for these
+# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
+#
+wmm_enabled=1
+
+# Low priority / AC_BK = background
+wmm_ac_bk_cwmin=4
+wmm_ac_bk_cwmax=10
+wmm_ac_bk_aifs=7
+wmm_ac_bk_txop_limit=0
+wmm_ac_bk_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
+#
+# Normal priority / AC_BE = best effort
+wmm_ac_be_aifs=3
+wmm_ac_be_cwmin=4
+wmm_ac_be_cwmax=10
+wmm_ac_be_txop_limit=0
+wmm_ac_be_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
+#
+# High priority / AC_VI = video
+wmm_ac_vi_aifs=2
+wmm_ac_vi_cwmin=3
+wmm_ac_vi_cwmax=4
+wmm_ac_vi_txop_limit=94
+wmm_ac_vi_acm=0
+# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
+#
+# Highest priority / AC_VO = voice
+wmm_ac_vo_aifs=2
+wmm_ac_vo_cwmin=2
+wmm_ac_vo_cwmax=3
+wmm_ac_vo_txop_limit=47
+wmm_ac_vo_acm=0
+
+"""
+
+# Please be careful if you edit the template.
+config_wpa_suppl_tmpl = """
+# WPA supplicant config
+network={
+ ssid="{{ ssid }}"
+{%- if sec_wpa_passphrase %}
+ psk="{{ sec_wpa_passphrase }}"
+{% endif %}
+}
+
+"""
+
+default_config_data = {
+ 'address': [],
+ 'address_remove': [],
+ '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' : False,
+ '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': '',
+ 'description': '',
+ 'deleted': False,
+ 'dhcp_client_id': '',
+ 'dhcp_hostname': '',
+ 'dhcp_vendor_class_id': '',
+ 'dhcpv6_prm_only': False,
+ 'dhcpv6_temporary': False,
+ 'disable': False,
+ 'disable_broadcast_ssid' : False,
+ 'disable_link_detect' : 1,
+ 'expunge_failing_stations' : False,
+ 'hw_id' : '',
+ 'intf': '',
+ 'isolate_stations' : False,
+ 'mac' : '',
+ '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' : '',
+ 'type' : 'monitor',
+ 'vif': [],
+ 'vif_remove': []
+}
+
+def get_conf_file(conf_type, intf):
+ cfg_dir = '/var/run/' + conf_type
+
+ # create directory on demand
+ if not os.path.exists(cfg_dir):
+ os.mkdir(cfg_dir)
+ # fix permissions - corresponds to mode 755
+ os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+ uid = getpwnam(user).pw_uid
+ gid = getgrnam(group).gr_gid
+ os.chown(cfg_dir, uid, gid)
+
+ cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
+ return cfg_file
+
+def get_pid(conf_type, intf):
+ cfg_dir = '/var/run/' + conf_type
+
+ # create directory on demand
+ if not os.path.exists(cfg_dir):
+ os.mkdir(cfg_dir)
+ # fix permissions - corresponds to mode 755
+ os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+ uid = getpwnam(user).pw_uid
+ gid = getgrnam(group).gr_gid
+ os.chown(cfg_dir, uid, gid)
+
+ cfg_file = cfg_dir + r'/{}.pid'.format(intf)
+ return cfg_file
+
+
+def get_wpa_suppl_config_name(intf):
+ cfg_dir = '/var/run/wpa_supplicant'
+
+ # create directory on demand
+ if not os.path.exists(cfg_dir):
+ os.mkdir(cfg_dir)
+ # fix permissions - corresponds to mode 755
+ os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+ uid = getpwnam(user).pw_uid
+ gid = getgrnam(group).gr_gid
+ os.chown(cfg_dir, uid, gid)
+
+ cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
+ return cfg_file
+
+def subprocess_cmd(command):
+ p = Popen(command, stdout=PIPE, shell=True)
+ p.communicate()
+
+
+def apply_vlan_config(vlan, config):
+ """
+ Generic function to apply a VLAN configuration from a dictionary
+ to a VLAN interface
+ """
+
+ if type(vlan) != type(VLANIf("lo")):
+ raise TypeError()
+
+ # update interface description used e.g. within SNMP
+ vlan.set_alias(config['description'])
+ # ignore link state changes
+ vlan.set_link_detect(config['disable_link_detect'])
+ # Maximum Transmission Unit (MTU)
+ vlan.set_mtu(config['mtu'])
+ # Change VLAN interface MAC address
+ if config['mac']:
+ vlan.set_mac(config['mac'])
+
+ # enable/disable VLAN interface
+ if config['disable']:
+ vlan.set_state('down')
+ else:
+ vlan.set_state('up')
+
+ # Configure interface address(es)
+ # - not longer required addresses get removed first
+ # - newly addresses will be added second
+ for addr in config['address_remove']:
+ vlan.del_addr(addr)
+ for addr in config['address']:
+ vlan.add_addr(addr)
+
+
+def get_config():
+ wifi = deepcopy(default_config_data)
+ conf = Config()
+
+ # determine tagNode instance
+ try:
+ wifi['intf'] = os.environ['VYOS_TAGNODE_VALUE']
+ except KeyError as E:
+ print("Interface not specified")
+
+ # check if wireless interface has been removed
+ cfg_base = 'interfaces wireless ' + wifi['intf']
+ if not conf.exists(cfg_base):
+ wifi['deleted'] = True
+ # 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)
+
+ # retrieve configured interface addresses
+ if conf.exists('address'):
+ wifi['address'] = conf.return_values('address')
+
+ # get interface addresses (currently effective) - to determine which
+ # address is no longer valid and needs to be removed
+ eff_addr = conf.return_effective_values('address')
+ wifi['address_remove'] = list_diff(eff_addr, wifi['address'])
+
+ # 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'] = True
+
+ # 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')
+
+ # retrieve interface description
+ if conf.exists('description'):
+ wifi['description'] = conf.return_value('description')
+
+ # get DHCP client identifier
+ if conf.exists('dhcp-options client-id'):
+ wifi['dhcp_client_id'] = conf.return_value('dhcp-options client-id')
+
+ # DHCP client host name (overrides the system host name)
+ if conf.exists('dhcp-options host-name'):
+ wifi['dhcp_hostname'] = conf.return_value('dhcp-options host-name')
+
+ # DHCP client vendor identifier
+ if conf.exists('dhcp-options vendor-class-id'):
+ wifi['dhcp_vendor_class_id'] = conf.return_value('dhcp-options vendor-class-id')
+
+ # DHCPv6 only acquire config parameters, no address
+ if conf.exists('dhcpv6-options parameters-only'):
+ wifi['dhcpv6_prm_only'] = conf.return_value('dhcpv6-options parameters-only')
+
+ # DHCPv6 temporary IPv6 address
+ if conf.exists('dhcpv6-options temporary'):
+ wifi['dhcpv6_temporary'] = conf.return_value('dhcpv6-options temporary')
+
+ # Disable broadcast of SSID from access-point
+ if conf.exists('disable-broadcast-ssid'):
+ wifi['disable_broadcast_ssid'] = True
+
+ # ignore link state changes on this interface
+ if conf.exists('disable-link-detect'):
+ wifi['disable_link_detect'] = 2
+
+ # 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
+
+ # Media Access Control (MAC) address
+ if conf.exists('mac'):
+ wifi['mac'] = conf.return_value('mac')
+
+ # 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')
+
+ # Wireless physical device
+ if conf.exists('phy'):
+ wifi['phy'] = conf.return_value('phy')
+
+ # 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 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' : '',
+ '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
+
+ # 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'):
+ wifi['type'] = conf.return_value('type')
+
+ # re-set configuration level to parse new nodes
+ conf.set_level(cfg_base)
+ # Determine vif interfaces (currently effective) - to determine which
+ # vif interface is no longer present and needs to be removed
+ eff_intf = conf.list_effective_nodes('vif')
+ act_intf = conf.list_nodes('vif')
+ wifi['vif_remove'] = list_diff(eff_intf, act_intf)
+
+ if conf.exists('vif'):
+ for vif in conf.list_nodes('vif'):
+ # set config level to vif interface
+ conf.set_level(cfg_base + ' vif ' + vif)
+ wifi['vif'].append(vlan_to_dict(conf))
+
+
+ # retrieve configured regulatory domain
+ conf.set_level('system')
+ if conf.exists('wifi-regulatory-domain'):
+ wifi['country_code'] = conf.return_value('wifi-regulatory-domain')
+
+ return wifi
+
+
+def verify(wifi):
+ if wifi['deleted']:
+ return None
+
+ if wifi['type'] != 'monitor' and not wifi['ssid']:
+ raise ConfigError('SSID must be set for {}'.format(wifi['intf']))
+
+ if wifi['type'] == 'access-point' and not wifi['channel']:
+ raise ConfigError('Channel must be set for {}'.format(wifi['intf']))
+
+ 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['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']))
+
+ conf = Config()
+ if not conf.exists('system wifi-regulatory-domain'):
+ raise ConfigError('Wireless regulatory domain is mandatory.\n' \
+ 'Use "set system wifi-regulatory-domain" to set.')
+
+ return None
+
+def generate(wifi):
+ pid = 0
+ # always stop hostapd service first before reconfiguring it
+ pidfile = get_pid('hostapd', wifi['intf'])
+ if os.path.isfile(pidfile):
+ pid = 0
+ with open(pidfile, 'r') as f:
+ pid = int(f.read())
+
+ if pid_exists(pid):
+ cmd = 'start-stop-daemon --stop --quiet'
+ cmd += ' --pidfile ' + pidfile
+ subprocess_cmd(cmd)
+
+ # always stop wpa_supplicant service first before reconfiguring it
+ pidfile = get_pid('wpa_supplicant', wifi['intf'])
+ if os.path.isfile(pidfile):
+ pid = 0
+ with open(pidfile, 'r') as f:
+ pid = int(f.read())
+
+ if pid_exists(pid):
+ cmd = 'start-stop-daemon --stop --quiet'
+ cmd += ' --pidfile ' + pidfile
+ subprocess_cmd(cmd)
+
+ # Delete config files if interface is removed
+ if wifi['deleted']:
+ if os.path.isfile(get_conf_file('hostapd', wifi['intf'])):
+ os.unlink(get_conf_file('hostapd', wifi['intf']))
+
+ if os.path.isfile(get_conf_file('wpa_supplicant', wifi['intf'])):
+ os.unlink(get_conf_file('wpa_supplicant', wifi['intf']))
+
+ return None
+
+ # render appropriate new config files depending on access-point or station mode
+ if wifi['type'] == 'access-point':
+ tmpl = Template(config_hostapd_tmpl)
+ config_text = tmpl.render(wifi)
+ with open(get_conf_file('hostapd', wifi['intf']), 'w') as f:
+ f.write(config_text)
+
+ elif wifi['type'] == 'station':
+ tmpl = Template(config_wpa_suppl_tmpl)
+ config_text = tmpl.render(wifi)
+ with open(get_conf_file('wpa_supplicant', wifi['intf']), 'w') as f:
+ f.write(config_text)
+
+ return None
+
+def apply(wifi):
+ w = EthernetIf(wifi['intf'])
+ if wifi['deleted']:
+ # delete interface
+ w.remove()
+ else:
+ # Some parts e.g. MAC address can't be changed when interface is up
+ w.set_state('down')
+
+ # update interface description used e.g. within SNMP
+ w.set_alias(wifi['description'])
+
+ # get DHCP config dictionary and update values
+ opt = w.get_dhcp_options()
+
+ if wifi['dhcp_client_id']:
+ opt['client_id'] = wifi['dhcp_client_id']
+
+ if wifi['dhcp_hostname']:
+ opt['hostname'] = wifi['dhcp_hostname']
+
+ if wifi['dhcp_vendor_class_id']:
+ opt['vendor_class_id'] = wifi['dhcp_vendor_class_id']
+
+ # store DHCP config dictionary - used later on when addresses are aquired
+ w.set_dhcp_options(opt)
+
+ # get DHCPv6 config dictionary and update values
+ opt = w.get_dhcpv6_options()
+
+ if wifi['dhcpv6_prm_only']:
+ opt['dhcpv6_prm_only'] = True
+
+ if wifi['dhcpv6_temporary']:
+ opt['dhcpv6_temporary'] = True
+
+ # store DHCPv6 config dictionary - used later on when addresses are aquired
+ w.set_dhcpv6_options(opt)
+
+ # ignore link state changes
+ w.set_link_detect(wifi['disable_link_detect'])
+
+ # 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'])
+ else:
+ w.set_mac(wifi['hw_id'])
+
+ # enable interface
+ if not wifi['disable']:
+ w.set_state('up')
+
+ # 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)
+
+ # remove no longer required VLAN interfaces (vif)
+ for vif in wifi['vif_remove']:
+ e.del_vlan(vif)
+
+ # create VLAN interfaces (vif)
+ for vif in wifi['vif']:
+ # QoS priority mapping can only be set during interface creation
+ # so we delete the interface first if required.
+ if vif['egress_qos_changed'] or vif['ingress_qos_changed']:
+ try:
+ # on system bootup the above condition is true but the interface
+ # does not exists, which throws an exception, but that's legal
+ e.del_vlan(vif['id'])
+ except:
+ pass
+
+ vlan = e.add_vlan(vif['id'])
+ apply_vlan_config(vlan, vif)
+
+ # Physical interface is now configured. Proceed by starting hostapd or
+ # wpa_supplicant daemon
+ cmd = 'start-stop-daemon --start --quiet'
+ if wifi['type'] == 'access-point':
+ cmd += ' --exec /usr/sbin/hostapd'
+ # now pass arguments to hostapd binary
+ cmd += ' -- -B'
+ cmd += ' -P {}'.format(get_pid('hostapd', wifi['intf']))
+ cmd += ' {}'.format(get_conf_file('hostapd', wifi['intf']))
+ elif wifi['type'] == 'station':
+ cmd += ' --exec /sbin/wpa_supplicant'
+ # now pass arguments to hostapd binary
+ cmd += ' -- -s -B -D nl80211'
+ cmd += ' -P {}'.format(get_pid('wpa_supplicant', wifi['intf']))
+ cmd += ' -i {}'.format(wifi['intf'])
+ cmd += ' -c {}'.format(get_conf_file('wpa_supplicant', wifi['intf']))
+
+ # execute assembled command
+ subprocess_cmd(cmd)
+
+ 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/conf_mode/system-wifi-regdom.py b/src/conf_mode/system-wifi-regdom.py
new file mode 100755
index 000000000..01dc92a20
--- /dev/null
+++ b/src/conf_mode/system-wifi-regdom.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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
+import jinja2
+
+from copy import deepcopy
+from sys import exit
+
+from vyos.config import Config
+from vyos import ConfigError
+
+config_80211_file='/etc/modprobe.d/cfg80211.conf'
+config_crda_file='/etc/default/crda'
+
+# Please be careful if you edit the template.
+config_80211_tmpl = """
+{%- if regdom -%}
+options cfg80211 ieee80211_regdom={{ regdom }}
+{% endif %}
+"""
+
+# Please be careful if you edit the template.
+config_crda_tmpl = """
+{%- if regdom -%}
+REGDOMAIN={{ regdom }}
+{% endif %}
+"""
+
+default_config_data = {
+ 'regdom' : '',
+ 'deleted' : False
+}
+
+
+def get_config():
+ regdom = deepcopy(default_config_data)
+ conf = Config()
+
+ # set new configuration level
+ conf.set_level('system')
+
+ # Check if interface has been removed
+ if not conf.exists('wifi-regulatory-domain'):
+ regdom['deleted'] = True
+ return regdom
+
+ # retrieve configured regulatory domain
+ if conf.exists('wifi-regulatory-domain'):
+ regdom['regdom'] = conf.return_value('wifi-regulatory-domain')
+
+ 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
+
+ tmpl = jinja2.Template(config_80211_tmpl)
+ config_text = tmpl.render(regdom)
+ with open(config_80211_file, 'w') as f:
+ f.write(config_text)
+
+ tmpl = jinja2.Template(config_crda_tmpl)
+ config_text = tmpl.render(regdom)
+ with open(config_crda_file, 'w') as f:
+ f.write(config_text)
+
+ 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/3-to-4 b/src/migration-scripts/interfaces/3-to-4
new file mode 100755
index 000000000..e3bd25a68
--- /dev/null
+++ b/src/migration-scripts/interfaces/3-to-4
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+
+# Change syntax of wireless interfaces
+# Migrate boolean nodes to valueless
+
+import sys
+from vyos.configtree import ConfigTree
+
+if (len(sys.argv) < 1):
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.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
+ sys.exit(0)
+else:
+ for wifi in config.list_nodes(base):
+ # as converting a node to bool is always the same, we can script it
+ to_bool_nodes = ['capabilities ht 40MHz-incapable',
+ 'capabilities ht auto-powersave',
+ 'capabilities ht delayed-block-ack',
+ 'capabilities ht dsss-cck-40',
+ 'capabilities ht greenfield',
+ 'capabilities ht ldpc',
+ 'capabilities ht lsig-protection',
+ 'capabilities ht stbc tx',
+ 'capabilities require-ht',
+ 'capabilities require-vht',
+ 'capabilities vht antenna-pattern-fixed',
+ 'capabilities vht ldpc',
+ 'capabilities vht stbc tx',
+ 'capabilities vht tx-powersave',
+ 'capabilities vht vht-cf',
+ 'expunge-failing-stations',
+ 'isolate-stations']
+
+ for node in to_bool_nodes:
+ if config.exists(base + [wifi, node]):
+ tmp = config.return_value(base + [wifi, node])
+ # delete old node
+ config.delete(base + [wifi, node])
+ # set new node if it was enabled
+ if tmp == 'true':
+ # OLD CLI used camel casing in 40MHz-incapable which is
+ # not supported in the new backend. Convert all to lower-case
+ config.set(base + [wifi, node.lower()])
+
+ # Remove debug node
+ if config.exists(base + [wifi, 'debug']):
+ config.delete(base + [wifi, 'debug'])
+
+ # RADIUS servers
+ if config.exists(base + [wifi, 'security', 'wpa', 'radius-server']):
+ for server in config.list_nodes(base + [wifi, 'security', 'wpa', 'radius-server']):
+ base_server = base + [wifi, 'security', 'wpa', 'radius-server', server]
+
+ # Migrate RADIUS shared secret
+ if config.exists(base_server + ['secret']):
+ key = config.return_value(base_server + ['secret'])
+ # write new configuration node
+ config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'key'], value=key)
+ # format as tag node
+ config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server'])
+
+ # Migrate RADIUS port
+ if config.exists(base_server + ['port']):
+ port = config.return_value(base_server + ['port'])
+ # write new configuration node
+ config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'port'], value=port)
+ # format as tag node
+ config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server'])
+
+ # Migrate RADIUS accounting
+ if config.exists(base_server + ['accounting']):
+ port = config.return_value(base_server + ['accounting'])
+ # write new configuration node
+ config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'accounting'])
+ # format as tag node
+ config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server'])
+
+ # delete old radius-server nodes
+ config.delete(base + [wifi, 'security', 'wpa', 'radius-server'])
+
+ 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))
+ sys.exit(1)
diff --git a/src/op_mode/show_wireless.py b/src/op_mode/show_wireless.py
new file mode 100755
index 000000000..aff882559
--- /dev/null
+++ b/src/op_mode/show_wireless.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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 argparse
+import re
+
+from sys import exit
+from copy import deepcopy
+from subprocess import Popen, PIPE, STDOUT
+
+from vyos.config import Config
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-s", "--scan", help="Scan for Wireless APs on given interface, e.g. 'wlan0'")
+parser.add_argument("-b", "--brief", action="store_true", help="Show wireless configuration")
+parser.add_argument("-c", "--stations", help="Show wireless clients connected on interface, e.g. 'wlan0'")
+
+def _cmd(command):
+ p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True)
+ tmp = p.communicate()[0].strip()
+ return tmp.decode()
+
+def show_brief():
+ config = Config()
+ if len(config.list_effective_nodes('interfaces wireless')) == 0:
+ print("No Wireless interfaces configured")
+ exit(0)
+
+ interfaces = []
+ for intf in config.list_effective_nodes('interfaces wireless'):
+ config.set_level('interfaces wireless {}'.format(intf))
+ data = {
+ 'name': intf,
+ 'type': '',
+ 'ssid': '',
+ 'channel': ''
+ }
+ data['type'] = config.return_effective_value('type')
+ data['ssid'] = config.return_effective_value('ssid')
+ data['channel'] = config.return_effective_value('channel')
+
+ interfaces.append(data)
+
+ return interfaces
+
+def ssid_scan(intf):
+ tmp = _cmd('/sbin/iw dev {} scan ap-force'.format(intf))
+ networks = []
+ data = {
+ 'ssid': '',
+ 'mac': '',
+ 'channel': '',
+ 'signal': ''
+ }
+ re_mac = re.compile(r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})')
+ for line in tmp.splitlines():
+ if line.startswith('BSS '):
+ ssid = deepcopy(data)
+ ssid['mac'] = re.search(re_mac, line).group()
+
+ elif line.lstrip().startswith('SSID: '):
+ # SSID can be " SSID: WLAN-57 6405", thus strip all leading whitespaces
+ ssid['ssid'] = line.lstrip().split(':')[-1].lstrip()
+
+ elif line.lstrip().startswith('signal: '):
+ # Siganl can be " signal: -67.00 dBm", thus strip all leading whitespaces
+ ssid['signal'] = line.lstrip().split(':')[-1].split()[0]
+
+ elif line.lstrip().startswith('DS Parameter set: channel'):
+ # Channel can be " DS Parameter set: channel 6" , thus
+ # strip all leading whitespaces
+ ssid['channel'] = line.lstrip().split(':')[-1].split()[-1]
+ networks.append(ssid)
+ continue
+
+ return networks
+
+def show_clients(intf):
+ tmp = _cmd('/sbin/iw dev {} station dump'.format(intf))
+ clients = []
+ data = {
+ 'mac': '',
+ 'signal': '',
+ 'rx_bytes': '',
+ 'rx_packets': '',
+ 'tx_bytes': '',
+ 'tx_packets': ''
+ }
+ re_mac = re.compile(r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})')
+ for line in tmp.splitlines():
+ if line.startswith('Station'):
+ client = deepcopy(data)
+ client['mac'] = re.search(re_mac, line).group()
+
+ elif line.lstrip().startswith('signal avg:'):
+ client['signal'] = line.lstrip().split(':')[-1].lstrip().split()[0]
+
+ elif line.lstrip().startswith('rx bytes:'):
+ client['rx_bytes'] = line.lstrip().split(':')[-1].lstrip()
+
+ elif line.lstrip().startswith('rx packets:'):
+ client['rx_packets'] = line.lstrip().split(':')[-1].lstrip()
+
+ elif line.lstrip().startswith('tx bytes:'):
+ client['tx_bytes'] = line.lstrip().split(':')[-1].lstrip()
+
+ elif line.lstrip().startswith('tx packets:'):
+ client['tx_packets'] = line.lstrip().split(':')[-1].lstrip()
+ clients.append(client)
+ continue
+
+ return clients
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+
+ if args.scan:
+ print("Address SSID Channel Signal (dbm)")
+ for network in ssid_scan(args.scan):
+ print("{:<17} {:<32} {:>3} {}".format(network['mac'],
+ network['ssid'],
+ network['channel'],
+ network['signal']))
+ exit(0)
+
+ elif args.brief:
+ print("Interface Type SSID Channel")
+ for intf in show_brief():
+ print("{:<9} {:<12} {:<32} {:>3}".format(intf['name'],
+ intf['type'],
+ intf['ssid'],
+ intf['channel']))
+ exit(0)
+
+ elif args.stations:
+ print("Station Signal RX: bytes packets TX: bytes packets")
+ for client in show_clients(args.stations):
+ print("{:<17} {:>3} {:>15} {:>9} {:>15} {:>10} ".format(client['mac'],
+ client['signal'], client['rx_bytes'], client['rx_packets'], client['tx_bytes'], client['tx_packets']))
+
+ exit(0)
+
+ else:
+ parser.print_help()
+ exit(1)