summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/lint-with-ruff.yml (renamed from .github/workflows/lint-with-darker-ruff.yml)6
-rw-r--r--.github/workflows/trigger-rebuild-repo-package.yml32
-rw-r--r--Makefile1
-rw-r--r--data/templates/wifi/hostapd.conf.j212
-rw-r--r--debian/control1
-rw-r--r--debian/vyos-1x.postinst3
-rw-r--r--[-rwxr-xr-x]interface-definitions/include/firewall/common-rule-bridge.xml.i0
-rw-r--r--[-rwxr-xr-x]interface-definitions/include/firewall/global-options.xml.i0
-rw-r--r--[-rwxr-xr-x]interface-definitions/include/firewall/match-ether-type.xml.i0
-rw-r--r--interface-definitions/include/firewall/match-vlan.xml.i1
-rw-r--r--interface-definitions/interfaces_wireless.xml.in68
-rw-r--r--op-mode-definitions/execute-port-scan.xml.in34
-rw-r--r--op-mode-definitions/wake-on-lan.xml.in32
-rwxr-xr-xpython/vyos/firewall.py13
-rwxr-xr-xsmoketest/scripts/cli/test_container.py18
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py3
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py89
-rw-r--r--src/op_mode/execute_port-scan.py155
-rw-r--r--src/systemd/podman.service16
-rw-r--r--src/systemd/podman.socket10
20 files changed, 458 insertions, 36 deletions
diff --git a/.github/workflows/lint-with-darker-ruff.yml b/.github/workflows/lint-with-ruff.yml
index 01f7cd448..00cc9ca1b 100644
--- a/.github/workflows/lint-with-darker-ruff.yml
+++ b/.github/workflows/lint-with-ruff.yml
@@ -1,4 +1,4 @@
-name: Lint py code with darker and ruff
+name: Lint py code with ruff
on:
pull_request_target:
branches:
@@ -9,6 +9,6 @@ permissions:
contents: read
jobs:
- darker-ruff-lint:
- uses: vyos/.github/.github/workflows/lint-with-darker-ruff.yml@current
+ ruff-lint:
+ uses: vyos/.github/.github/workflows/lint-with-ruff.yml@current
secrets: inherit
diff --git a/.github/workflows/trigger-rebuild-repo-package.yml b/.github/workflows/trigger-rebuild-repo-package.yml
new file mode 100644
index 000000000..9c1176b01
--- /dev/null
+++ b/.github/workflows/trigger-rebuild-repo-package.yml
@@ -0,0 +1,32 @@
+name: Trigger to build a deb package from repo
+
+on:
+ pull_request:
+ types:
+ - closed
+ branches:
+ - current
+ workflow_dispatch:
+
+jobs:
+ trigger-build:
+ if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
+ runs-on: ubuntu-latest
+
+ env:
+ REF: main # Used for curl to trigger build package
+
+ steps:
+ - name: Set variables
+ run: |
+ echo "PACKAGE_NAME=$(basename ${{ github.repository }})" >> $GITHUB_ENV
+
+ - name: Trigger rebuild for ${{ env.PACKAGE_NAME }}
+ run: |
+ curl -L \
+ -X POST \
+ -H "Accept: application/vnd.github+json" \
+ -H "Authorization: Bearer ${{ secrets.PAT }}" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ https://api.github.com/repos/${{ secrets.REMOTE_OWNER }}/${{ secrets.REMOTE_REUSE_REPO }}/actions/workflows/build-package.yml/dispatches \
+ -d '{"ref": "${{ env.REF }}", "inputs":{"package_name":"'"$PACKAGE_NAME"'", "gpg_key_id": "${{ secrets.GPG_KEY_ID }}", "package_branch": "${{ github.ref_name }}"}}'
diff --git a/Makefile b/Makefile
index c83380be5..411399c3a 100644
--- a/Makefile
+++ b/Makefile
@@ -64,6 +64,7 @@ op_mode_definitions: $(op_xml_obj)
ln -s ../node.tag $(OP_TMPL_DIR)/mtr/node.tag/node.tag/
ln -s ../node.tag $(OP_TMPL_DIR)/monitor/traceroute/node.tag/node.tag/
ln -s ../node.tag $(OP_TMPL_DIR)/monitor/traffic/interface/node.tag/node.tag/
+ ln -s ../node.tag $(OP_TMPL_DIR)/execute/port-scan/host/node.tag/node.tag/
# XXX: test if there are empty node.def files - this is not allowed as these
# could mask help strings or mandatory priority statements
diff --git a/data/templates/wifi/hostapd.conf.j2 b/data/templates/wifi/hostapd.conf.j2
index 0459fbc69..5f3757216 100644
--- a/data/templates/wifi/hostapd.conf.j2
+++ b/data/templates/wifi/hostapd.conf.j2
@@ -46,7 +46,14 @@ hw_mode=a
ieee80211h=1
ieee80211ac=1
{% elif mode is vyos_defined('ax') %}
+{#{% if capabilities.ht is vyos_defined and capabilities.vht not vyos_defined %}#}
+{% if capabilities.he.channel_set_width is vyos_defined('81') or capabilities.he.channel_set_width is vyos_defined('83') or capabilities.he.channel_set_width is vyos_defined('84') %}
+{# This is almost certainly a 2.4GHz network #}
+hw_mode=g
+{% else %}
+{# This is likely a 5GHz or 6GHz network #}
hw_mode=a
+{% endif %}
ieee80211h=1
ieee80211ax=1
{% else %}
@@ -202,7 +209,7 @@ require_he=1
{% else %}
ieee80211n={{ '1' if 'n' in mode or 'ac' in mode or 'ax' in mode else '0' }}
{% endif %}
-{# HE (802.11ax 6GHz) #}
+{# HE (802.11ax) #}
{% if capabilities.he is vyos_defined and mode in 'ax' %}
{# For now, hard-code power levels for indoor-only AP #}
he_6ghz_reg_pwr_type=0
@@ -220,6 +227,9 @@ op_class={{ capabilities.he.channel_set_width }}
{% if capabilities.he.bss_color is vyos_defined %}
he_bss_color={{ capabilities.he.bss_color }}
{% endif %}
+{% if capabilities.he.coding_scheme is vyos_defined %}
+he_basic_mcs_nss_set={{ capabilities.he.coding_scheme }}
+{% endif %}
he_6ghz_rx_ant_pat={{ '1' if capabilities.he.antenna_pattern_fixed is vyos_defined else '0' }}
he_su_beamformer={{ '1' if capabilities.he.beamform.single_user_beamformer is vyos_defined else '0' }}
he_su_beamformee={{ '1' if capabilities.he.beamform.single_user_beamformee is vyos_defined else '0' }}
diff --git a/debian/control b/debian/control
index d3f5fb464..890100fd8 100644
--- a/debian/control
+++ b/debian/control
@@ -149,6 +149,7 @@ Depends:
openvpn-auth-ldap,
openvpn-auth-radius,
openvpn-otp,
+ openvpn-dco,
libpam-google-authenticator,
# End "interfaces openvpn"
# For "interfaces wireguard"
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 141a9e8f9..dc8ada267 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -244,6 +244,9 @@ fi
# Enable Cloud-init pre-configuration service
systemctl enable vyos-config-cloud-init.service
+# Enable Podman API
+systemctl enable podman.service
+
# Generate API GraphQL schema
/usr/libexec/vyos/services/api/graphql/generate/generate_schema.py
diff --git a/interface-definitions/include/firewall/common-rule-bridge.xml.i b/interface-definitions/include/firewall/common-rule-bridge.xml.i
index 80088bbec..80088bbec 100755..100644
--- a/interface-definitions/include/firewall/common-rule-bridge.xml.i
+++ b/interface-definitions/include/firewall/common-rule-bridge.xml.i
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
index 05fdd75cb..05fdd75cb 100755..100644
--- a/interface-definitions/include/firewall/global-options.xml.i
+++ b/interface-definitions/include/firewall/global-options.xml.i
diff --git a/interface-definitions/include/firewall/match-ether-type.xml.i b/interface-definitions/include/firewall/match-ether-type.xml.i
index abfa9034d..abfa9034d 100755..100644
--- a/interface-definitions/include/firewall/match-ether-type.xml.i
+++ b/interface-definitions/include/firewall/match-ether-type.xml.i
diff --git a/interface-definitions/include/firewall/match-vlan.xml.i b/interface-definitions/include/firewall/match-vlan.xml.i
index 44ad02c99..d58e84353 100644
--- a/interface-definitions/include/firewall/match-vlan.xml.i
+++ b/interface-definitions/include/firewall/match-vlan.xml.i
@@ -36,6 +36,7 @@
</constraint>
</properties>
</leafNode>
+ #include <include/firewall/match-ether-type.xml.i>
</children>
</node>
<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/interfaces_wireless.xml.in b/interface-definitions/interfaces_wireless.xml.in
index fdcb79b19..474953500 100644
--- a/interface-definitions/interfaces_wireless.xml.in
+++ b/interface-definitions/interfaces_wireless.xml.in
@@ -248,26 +248,26 @@
<properties>
<help>VHT operating channel center frequency - center freq 1 (for use with 80, 80+80 and 160 modes)</help>
<valueHelp>
- <format>u32:34-173</format>
+ <format>u32:34-177</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"/>
+ <validator name="numeric" argument="--range 34-177"/>
</constraint>
- <constraintErrorMessage>Channel center value must be between 34 and 173</constraintErrorMessage>
+ <constraintErrorMessage>Channel center value must be between 34 and 177</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>u32:34-173</format>
+ <format>u32:34-177</format>
<description>5Ghz (802.11 ac) center channel index (use 58 for secondary 80MHz channel 52)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 34-173"/>
+ <validator name="numeric" argument="--range 34-177"/>
</constraint>
- <constraintErrorMessage>Channel center value must be between 34 and 173</constraintErrorMessage>
+ <constraintErrorMessage>Channel center value must be between 34 and 177</constraintErrorMessage>
</properties>
</leafNode>
</children>
@@ -436,30 +436,42 @@
https://w1.fi/cgit/hostap/tree/src/common/ieee802_11_common.c?id=195cc3d919503fb0d699d9a56a58a72602b25f51#n1525
802.11ax (WiFi-6e - HE) can use up to 160MHz bandwidth channels
-->
- <list>131 132 133 134 135</list>
+ <list>81 83 84 131 132 133 134 135</list>
</completionHelp>
<valueHelp>
+ <format>81</format>
+ <description>2.4GHz, 20 MHz channel width</description>
+ </valueHelp>
+ <valueHelp>
+ <format>83</format>
+ <description>2.4GHz, 40 MHz channel width, secondary 20MHz channel above primary channel</description>
+ </valueHelp>
+ <valueHelp>
+ <format>84</format>
+ <description>2.4GHz, 40 MHz channel width, secondary 20MHz channel below primary channel</description>
+ </valueHelp>
+ <valueHelp>
<format>131</format>
- <description>20 MHz channel width</description>
+ <description>6GHz, 20 MHz channel width</description>
</valueHelp>
<valueHelp>
<format>132</format>
- <description>40 MHz channel width</description>
+ <description>6GHz, 40 MHz channel width</description>
</valueHelp>
<valueHelp>
<format>133</format>
- <description>80 MHz channel width</description>
+ <description>6GHz, 80 MHz channel width</description>
</valueHelp>
<valueHelp>
<format>134</format>
- <description>160 MHz channel width</description>
+ <description>6GHz, 160 MHz channel width</description>
</valueHelp>
<valueHelp>
<format>135</format>
- <description>80+80 MHz channel width</description>
+ <description>6GHz, 80+80 MHz channel width</description>
</valueHelp>
<constraint>
- <regex>(131|132|133|134|135)</regex>
+ <regex>(81|83|84|131|132|133|134|135)</regex>
</constraint>
</properties>
</leafNode>
@@ -535,6 +547,30 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="coding-scheme">
+ <properties>
+ <help>Spacial Stream and Modulation Coding Scheme settings</help>
+ <valueHelp>
+ <format>u32:0</format>
+ <description>HE-MCS 0-7</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:1</format>
+ <description>HE-MCS 0-9</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:2</format>
+ <description>HE-MCS 0-11</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:3</format>
+ <description>HE-MCS is not supported</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-3"/>
+ </constraint>
+ </properties>
+ </leafNode>
</children>
</node>
<leafNode name="require-he">
@@ -554,10 +590,10 @@
</valueHelp>
<valueHelp>
<format>u32:1-14</format>
- <description>2.4Ghz (802.11 b/g/n) Channel</description>
+ <description>2.4Ghz (802.11 b/g/n/ax) Channel</description>
</valueHelp>
<valueHelp>
- <format>u32:34-173</format>
+ <format>u32:34-177</format>
<description>5Ghz (802.11 a/h/j/n/ac) Channel</description>
</valueHelp>
<valueHelp>
@@ -565,7 +601,7 @@
<description>6Ghz (802.11 ax) Channel</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-0 --range 1-14 --range 34-173 --range 1-233"/>
+ <validator name="numeric" argument="--range 0-0 --range 1-14 --range 34-177 --range 1-233"/>
</constraint>
</properties>
<defaultValue>0</defaultValue>
diff --git a/op-mode-definitions/execute-port-scan.xml.in b/op-mode-definitions/execute-port-scan.xml.in
new file mode 100644
index 000000000..52cdab5f0
--- /dev/null
+++ b/op-mode-definitions/execute-port-scan.xml.in
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="execute">
+ <children>
+ <node name="port-scan">
+ <properties>
+ <help>Scan network for open ports</help>
+ </properties>
+ <children>
+ <tagNode name="host">
+ <properties>
+ <help>IP address or domain name of the host to scan (scan all ports 1-65535)</help>
+ <completionHelp>
+ <list>&lt;hostname&gt; &lt;x.x.x.x&gt; &lt;h:h:h:h:h:h:h:h&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>nmap -p- -T4 --max-retries=1 --host-timeout=30s "$4"</command>
+ <children>
+ <leafNode name="node.tag">
+ <properties>
+ <help>Port scan options</help>
+ <completionHelp>
+ <script>${vyos_op_scripts_dir}/execute_port-scan.py --get-options-nested "${COMP_WORDS[@]}"</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/execute_port-scan.py "${@:4}"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/wake-on-lan.xml.in b/op-mode-definitions/wake-on-lan.xml.in
index 7119eeb65..625cf4056 100644
--- a/op-mode-definitions/wake-on-lan.xml.in
+++ b/op-mode-definitions/wake-on-lan.xml.in
@@ -1,26 +1,30 @@
<?xml version="1.0"?>
<interfaceDefinition>
- <node name="wake-on-lan">
- <properties>
- <help>Send Wake-On-LAN (WOL) Magic Packet</help>
- </properties>
+ <node name="execute">
<children>
- <tagNode name="interface">
+ <node name="wake-on-lan">
<properties>
- <help>Interface where the station is connected</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces</script>
- </completionHelp>
+ <help>Send Wake-On-LAN (WOL) Magic Packet</help>
</properties>
<children>
- <tagNode name="host">
+ <tagNode name="interface">
<properties>
- <help>Station (MAC) address to wake up</help>
+ <help>Interface where the station is connected</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
</properties>
- <command>sudo /usr/sbin/etherwake -i "$3" "$5"</command>
- </tagNode>
+ <children>
+ <tagNode name="host">
+ <properties>
+ <help>Station (MAC) address to wake up</help>
+ </properties>
+ <command>sudo /usr/sbin/etherwake -i "$3" "$5"</command>
+ </tagNode>
+ </children>
+ </tagNode>
</children>
- </tagNode>
+ </node>
</children>
</node>
</interfaceDefinition>
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index b1978c1fa..64fed8177 100755
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -496,6 +496,19 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
output.append(f'vlan id {rule_conf["vlan"]["id"]}')
if 'priority' in rule_conf['vlan']:
output.append(f'vlan pcp {rule_conf["vlan"]["priority"]}')
+ if 'ethernet_type' in rule_conf['vlan']:
+ ether_type_mapping = {
+ '802.1q': '8021q',
+ '802.1ad': '8021ad',
+ 'ipv6': 'ip6',
+ 'ipv4': 'ip',
+ 'arp': 'arp'
+ }
+ ether_type = rule_conf['vlan']['ethernet_type']
+ operator = '!=' if ether_type.startswith('!') else ''
+ ether_type = ether_type.lstrip('!')
+ ether_type = ether_type_mapping.get(ether_type, ether_type)
+ output.append(f'vlan type {operator} {ether_type}')
if 'log' in rule_conf:
action = rule_conf['action'] if 'action' in rule_conf else 'accept'
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index 3dd97a175..5e33eba40 100755
--- a/smoketest/scripts/cli/test_container.py
+++ b/smoketest/scripts/cli/test_container.py
@@ -230,5 +230,23 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
tmp = cmd(f'sudo podman exec -it {cont_name} id -g')
self.assertEqual(tmp, gid)
+ def test_api_socket(self):
+ base_name = 'api-test'
+ container_list = range(1, 5)
+
+ for ii in container_list:
+ name = f'{base_name}-{ii}'
+ self.cli_set(base_path + ['name', name, 'image', cont_image])
+ self.cli_set(base_path + ['name', name, 'allow-host-networks'])
+
+ self.cli_commit()
+
+ # Query API about running containers
+ tmp = cmd("sudo curl --unix-socket /run/podman/podman.sock -H 'content-type: application/json' -sf http://localhost/containers/json")
+ tmp = json.loads(tmp)
+
+ # We expect the same amount of containers from the API that we started above
+ self.assertEqual(len(container_list), len(tmp))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index e4f9b14be..3e9ec2935 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -721,6 +721,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'default-log'])
self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'action', 'accept'])
self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'vlan', 'id', vlan_id])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'vlan', 'ethernet-type', 'ipv4'])
self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'action', 'jump'])
self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'jump-target', name])
self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'vlan', 'priority', vlan_prior])
@@ -745,7 +746,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['chain VYOS_FORWARD_filter'],
['type filter hook forward priority filter; policy accept;'],
['jump VYOS_STATE_POLICY'],
- [f'vlan id {vlan_id}', 'accept'],
+ [f'vlan id {vlan_id}', 'vlan type ip', 'accept'],
[f'vlan pcp {vlan_prior}', f'jump NAME_{name}'],
['log prefix "[bri-FWD-filter-default-D]"', 'drop', 'FWD-filter default-action drop'],
[f'chain NAME_{name}'],
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index 7bfe0d221..b8b18f30f 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -300,7 +300,89 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
for key, value in vht_opt.items():
self.assertIn(value, tmp)
- def test_wireless_hostapd_he_config(self):
+ def test_wireless_hostapd_he_2ghz_config(self):
+ # Only set the hostapd (access-point) options - HE mode for 802.11ax at 2.4GHz
+ interface = self._interfaces[1] # wlan1
+ ssid = 'ssid'
+ channel = '1'
+ sae_pw = 'VyOSVyOSVyOS'
+ bss_color = '13'
+ channel_set_width = '81'
+
+ self.cli_set(self._base_path + [interface, 'ssid', ssid])
+ self.cli_set(self._base_path + [interface, 'type', 'access-point'])
+ self.cli_set(self._base_path + [interface, 'channel', channel])
+ self.cli_set(self._base_path + [interface, 'mode', 'ax'])
+ self.cli_set(self._base_path + [interface, 'security', 'wpa', 'mode', 'wpa2'])
+ self.cli_set(self._base_path + [interface, 'security', 'wpa', 'passphrase', sae_pw])
+ self.cli_set(self._base_path + [interface, 'security', 'wpa', 'cipher', 'CCMP'])
+ self.cli_set(self._base_path + [interface, 'security', 'wpa', 'cipher', 'GCMP'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'ht', '40mhz-incapable'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'ht', 'channel-set-width', 'ht20'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'ht', 'channel-set-width', 'ht40+'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'ht', 'channel-set-width', 'ht40-'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'ht', 'short-gi', '20'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'ht', 'short-gi', '40'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'bss-color', bss_color])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'channel-set-width', channel_set_width])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'beamform', 'multi-user-beamformer'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'beamform', 'single-user-beamformer'])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'beamform', 'single-user-beamformee'])
+
+ self.cli_commit()
+
+ #
+ # Validate Config
+ #
+ tmp = get_config_value(interface, 'interface')
+ self.assertEqual(interface, tmp)
+
+ # ssid
+ tmp = get_config_value(interface, 'ssid')
+ self.assertEqual(ssid, tmp)
+
+ # mode of operation resulting from [interface, 'mode', 'ax']
+ tmp = get_config_value(interface, 'hw_mode')
+ self.assertEqual('g', tmp)
+ tmp = get_config_value(interface, 'ieee80211h')
+ self.assertEqual('1', tmp)
+ tmp = get_config_value(interface, 'ieee80211ax')
+ self.assertEqual('1', tmp)
+
+ # channel and channel width
+ tmp = get_config_value(interface, 'channel')
+ self.assertEqual(channel, tmp)
+ tmp = get_config_value(interface, 'op_class')
+ self.assertEqual(channel_set_width, tmp)
+
+ # BSS coloring
+ tmp = get_config_value(interface, 'he_bss_color')
+ self.assertEqual(bss_color, tmp)
+
+ # sae_password
+ tmp = get_config_value(interface, 'wpa_passphrase')
+ self.assertEqual(sae_pw, tmp)
+
+ # WPA3 and dependencies
+ tmp = get_config_value(interface, 'wpa')
+ self.assertEqual('2', tmp)
+ tmp = get_config_value(interface, 'rsn_pairwise')
+ self.assertEqual('CCMP GCMP', tmp)
+ tmp = get_config_value(interface, 'wpa_key_mgmt')
+ self.assertEqual('WPA-PSK WPA-PSK-SHA256', tmp)
+
+ # beamforming
+ tmp = get_config_value(interface, 'he_mu_beamformer')
+ self.assertEqual('1', tmp)
+ tmp = get_config_value(interface, 'he_su_beamformee')
+ self.assertEqual('1', tmp)
+ tmp = get_config_value(interface, 'he_mu_beamformer')
+ self.assertEqual('1', tmp)
+
+ # Check for running process
+ self.assertTrue(process_named_running('hostapd'))
+
+ def test_wireless_hostapd_he_6ghz_config(self):
# Only set the hostapd (access-point) options - HE mode for 802.11ax at 6GHz
interface = self._interfaces[1] # wlan1
ssid = 'ssid'
@@ -323,6 +405,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'bss-color', bss_color])
self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'channel-set-width', channel_set_width])
self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'center-channel-freq', 'freq-1', center_channel_freq_1])
+ self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'antenna-pattern-fixed'])
self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'beamform', 'multi-user-beamformer'])
self.cli_set(self._base_path + [interface, 'capabilities', 'he', 'beamform', 'single-user-beamformer'])
@@ -370,6 +453,10 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
tmp = get_config_value(interface, 'wpa_key_mgmt')
self.assertEqual('SAE', tmp)
+ # antenna pattern
+ tmp = get_config_value(interface, 'he_6ghz_rx_ant_pat')
+ self.assertEqual('1', tmp)
+
# beamforming
tmp = get_config_value(interface, 'he_mu_beamformer')
self.assertEqual('1', tmp)
diff --git a/src/op_mode/execute_port-scan.py b/src/op_mode/execute_port-scan.py
new file mode 100644
index 000000000..bf17d0379
--- /dev/null
+++ b/src/op_mode/execute_port-scan.py
@@ -0,0 +1,155 @@
+#! /usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+
+from vyos.utils.process import call
+
+
+options = {
+ 'port': {
+ 'cmd': '{command} -p {value}',
+ 'type': '<1-65535> <list>',
+ 'help': 'Scan specified ports.'
+ },
+ 'tcp': {
+ 'cmd': '{command} -sT',
+ 'type': 'noarg',
+ 'help': 'Use TCP scan.'
+ },
+ 'udp': {
+ 'cmd': '{command} -sU',
+ 'type': 'noarg',
+ 'help': 'Use UDP scan.'
+ },
+ 'skip-ping': {
+ 'cmd': '{command} -Pn',
+ 'type': 'noarg',
+ 'help': 'Skip the Nmap discovery stage altogether.'
+ },
+ 'ipv6': {
+ 'cmd': '{command} -6',
+ 'type': 'noarg',
+ 'help': 'Enable IPv6 scanning.'
+ },
+}
+
+nmap = 'sudo /usr/bin/nmap'
+
+
+class List(list):
+ def first(self):
+ return self.pop(0) if self else ''
+
+ def last(self):
+ return self.pop() if self else ''
+
+ def prepend(self, value):
+ self.insert(0, value)
+
+
+def completion_failure(option: str) -> None:
+ """
+ Shows failure message after TAB when option is wrong
+ :param option: failure option
+ :type str:
+ """
+ sys.stderr.write('\n\n Invalid option: {}\n\n'.format(option))
+ sys.stdout.write('<nocomps>')
+ sys.exit(1)
+
+
+def expansion_failure(option, completions):
+ reason = 'Ambiguous' if completions else 'Invalid'
+ sys.stderr.write(
+ '\n\n {} command: {} [{}]\n\n'.format(reason, ' '.join(sys.argv),
+ option))
+ if completions:
+ sys.stderr.write(' Possible completions:\n ')
+ sys.stderr.write('\n '.join(completions))
+ sys.stderr.write('\n')
+ sys.stdout.write('<nocomps>')
+ sys.exit(1)
+
+
+def complete(prefix):
+ return [o for o in options if o.startswith(prefix)]
+
+
+def convert(command, args):
+ while args:
+ shortname = args.first()
+ longnames = complete(shortname)
+ if len(longnames) != 1:
+ expansion_failure(shortname, longnames)
+ longname = longnames[0]
+ if options[longname]['type'] == 'noarg':
+ command = options[longname]['cmd'].format(
+ command=command, value='')
+ elif not args:
+ sys.exit(f'port-scan: missing argument for {longname} option')
+ else:
+ command = options[longname]['cmd'].format(
+ command=command, value=args.first())
+ return command
+
+
+if __name__ == '__main__':
+ args = List(sys.argv[1:])
+ host = args.first()
+
+ if host == '--get-options-nested':
+ args.first() # pop execute
+ args.first() # pop port-scan
+ args.first() # pop host
+ args.first() # pop <host>
+ usedoptionslist = []
+ while args:
+ option = args.first() # pop option
+ matched = complete(option) # get option parameters
+ usedoptionslist.append(option) # list of used options
+ # Select options
+ if not args:
+ # remove from Possible completions used options
+ for o in usedoptionslist:
+ if o in matched:
+ matched.remove(o)
+ if not matched:
+ sys.stdout.write('<nocomps>')
+ else:
+ sys.stdout.write(' '.join(matched))
+ sys.exit(0)
+
+ if len(matched) > 1:
+ sys.stdout.write(' '.join(matched))
+ sys.exit(0)
+ # If option doesn't have value
+ if matched:
+ if options[matched[0]]['type'] == 'noarg':
+ continue
+ else:
+ # Unexpected option
+ completion_failure(option)
+
+ value = args.first() # pop option's value
+ if not args:
+ matched = complete(option)
+ helplines = options[matched[0]]['type']
+ sys.stdout.write(helplines)
+ sys.exit(0)
+
+ command = convert(nmap, args)
+ call(f'{command} -T4 {host}')
diff --git a/src/systemd/podman.service b/src/systemd/podman.service
new file mode 100644
index 000000000..20a16304b
--- /dev/null
+++ b/src/systemd/podman.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=Podman API Service
+Requires=podman.socket
+After=podman.socket
+Documentation=man:podman-system-service(1)
+StartLimitIntervalSec=0
+
+[Service]
+Delegate=true
+Type=exec
+KillMode=process
+Environment=LOGGING="--log-level=info"
+ExecStart=/usr/bin/podman $LOGGING system service
+
+[Install]
+WantedBy=default.target
diff --git a/src/systemd/podman.socket b/src/systemd/podman.socket
new file mode 100644
index 000000000..397058ee4
--- /dev/null
+++ b/src/systemd/podman.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Podman API Socket
+Documentation=man:podman-system-service(1)
+
+[Socket]
+ListenStream=%t/podman/podman.sock
+SocketMode=0660
+
+[Install]
+WantedBy=sockets.target