diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2
index 40118930b..6cb3b2f95 100644
--- a/data/templates/firewall/nftables-policy.j2
+++ b/data/templates/firewall/nftables-policy.j2
@@ -1,56 +1,65 @@
#!/usr/sbin/nft -f
{% import 'firewall/nftables-defines.j2' as group_tmpl %}
-{% if cleanup_commands is vyos_defined %}
-{% for command in cleanup_commands %}
-{{ command }}
-{% endfor %}
+{% if first_install is not vyos_defined %}
+delete table ip vyos_mangle
+delete table ip6 vyos_mangle
{% endif %}
-
-table ip mangle {
-{% if first_install is vyos_defined %}
+table ip vyos_mangle {
chain VYOS_PBR_PREROUTING {
type filter hook prerouting priority -150; policy accept;
+{% if route is vyos_defined %}
+{% for route_text, conf in route.items() if conf.interface is vyos_defined %}
+ iifname { {{ ",".join(conf.interface) }} } counter jump VYOS_PBR_{{ route_text }}
+{% endfor %}
+{% endif %}
}
+
chain VYOS_PBR_POSTROUTING {
type filter hook postrouting priority -150; policy accept;
}
-{% endif %}
+
{% if route is vyos_defined %}
{% for route_text, conf in route.items() %}
chain VYOS_PBR_{{ route_text }} {
{% if conf.rule is vyos_defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
{{ rule_conf | nft_rule(route_text, rule_id, 'ip') }}
{% endfor %}
{% endif %}
}
{% endfor %}
{% endif %}
{{ group_tmpl.groups(firewall_group, False) }}
}
-table ip6 mangle {
-{% if first_install is vyos_defined %}
+table ip6 vyos_mangle {
chain VYOS_PBR6_PREROUTING {
type filter hook prerouting priority -150; policy accept;
+{% if route6 is vyos_defined %}
+{% for route_text, conf in route6.items() if conf.interface is vyos_defined %}
+ iifname { {{ ",".join(conf.interface) }} } counter jump VYOS_PBR6_{{ route_text }}
+{% endfor %}
+{% endif %}
}
+
chain VYOS_PBR6_POSTROUTING {
type filter hook postrouting priority -150; policy accept;
}
-{% endif %}
+
{% if route6 is vyos_defined %}
{% for route_text, conf in route6.items() %}
chain VYOS_PBR6_{{ route_text }} {
{% if conf.rule is vyos_defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
{{ rule_conf | nft_rule(route_text, rule_id, 'ip6') }}
{% endfor %}
{% endif %}
}
{% endfor %}
{% endif %}
+
{{ group_tmpl.groups(firewall_group, True) }}
}
diff --git a/interface-definitions/include/interface/interface-policy-vif-c.xml.i b/interface-definitions/include/interface/interface-policy-vif-c.xml.i
deleted file mode 100644
index 866fcd5c0..000000000
--- a/interface-definitions/include/interface/interface-policy-vif-c.xml.i
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- 620
- Policy route options
-
-
-
-
- IPv4 policy route ruleset for interface
-
- policy route
-
-
-
-
-
- IPv6 policy route ruleset for interface
-
- policy route6
-
-
-
-
-
-
diff --git a/interface-definitions/include/interface/interface-policy-vif.xml.i b/interface-definitions/include/interface/interface-policy-vif.xml.i
deleted file mode 100644
index 83510fe59..000000000
--- a/interface-definitions/include/interface/interface-policy-vif.xml.i
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- 620
- Policy route options
-
-
-
-
- IPv4 policy route ruleset for interface
-
- policy route
-
-
-
-
-
- IPv6 policy route ruleset for interface
-
- policy route6
-
-
-
-
-
-
diff --git a/interface-definitions/include/interface/interface-policy.xml.i b/interface-definitions/include/interface/interface-policy.xml.i
deleted file mode 100644
index 42a8fd009..000000000
--- a/interface-definitions/include/interface/interface-policy.xml.i
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- 620
- Policy route options
-
-
-
-
- IPv4 policy route ruleset for interface
-
- policy route
-
-
-
-
-
- IPv6 policy route ruleset for interface
-
- policy route6
-
-
-
-
-
-
diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i
index 916349ade..6d50d7238 100644
--- a/interface-definitions/include/interface/vif-s.xml.i
+++ b/interface-definitions/include/interface/vif-s.xml.i
@@ -1,77 +1,75 @@
QinQ TAG-S Virtual Local Area Network (VLAN) ID
u32:0-4094
QinQ Virtual Local Area Network (VLAN) ID
VLAN ID must be between 0 and 4094
#include
#include
#include
#include
#include
#include
- #include
Protocol used for service VLAN (default: 802.1ad)
802.1ad 802.1q
802.1ad
Provider Bridging (IEEE 802.1ad, Q-inQ), ethertype 0x88a8
802.1q
VLAN-tagged frame (IEEE 802.1q), ethertype 0x8100
(802.1q|802.1ad)
Ethertype must be 802.1ad or 802.1q
802.1ad
#include
#include
#include
#include
#include
QinQ TAG-C Virtual Local Area Network (VLAN) ID
VLAN ID must be between 0 and 4094
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- #include
#include
#include
diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i
index 73a8c98ff..3f8f113ea 100644
--- a/interface-definitions/include/interface/vif.xml.i
+++ b/interface-definitions/include/interface/vif.xml.i
@@ -1,57 +1,56 @@
Virtual Local Area Network (VLAN) ID
u32:0-4094
Virtual Local Area Network (VLAN) ID
VLAN ID must be between 0 and 4094
#include
#include
#include
#include
#include
#include
- #include
VLAN egress QoS
txt
Format for qos mapping, e.g.: '0:1 1:6 7:6'
[:0-7 ]+
QoS mapping should be in the format of '0:7 2:3' with numbers 0-9
VLAN ingress QoS
txt
Format for qos mapping, e.g.: '0:1 1:6 7:6'
[:0-7 ]+
QoS mapping should be in the format of '0:7 2:3' with numbers 0-9
#include
#include
#include
#include
#include
#include
#include
diff --git a/interface-definitions/include/version/policy-version.xml.i b/interface-definitions/include/version/policy-version.xml.i
index 89bde20c7..f1494eaa3 100644
--- a/interface-definitions/include/version/policy-version.xml.i
+++ b/interface-definitions/include/version/policy-version.xml.i
@@ -1,3 +1,3 @@
-
+
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 41e4a68a8..96e0e5d89 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -1,234 +1,233 @@
Bonding Interface/Link Aggregation
320
bond[0-9]+
Bonding interface must be named bondN
bondN
Bonding interface name
#include
ARP link monitoring parameters
ARP link monitoring interval
u32
Specifies the ARP link monitoring frequency in milliseconds
IP address used for ARP monitoring
ipv4
Specify IPv4 address of ARP requests when interval is enabled
#include
#include
#include
#include
#include
#include
#include
- #include
Bonding transmit hash policy
layer2 layer2+3 layer3+4 encap2+3 encap3+4
layer2
use MAC addresses to generate the hash
layer2+3
combine MAC address and IP address to make hash
layer3+4
combine IP address and port to make hash
encap2+3
combine encapsulated MAC address and IP address to make hash
encap3+4
combine encapsulated IP address and port to make hash
(layer2\+3|layer3\+4|layer2|encap2\+3|encap3\+4)
hash-policy must be layer2 layer2+3 layer3+4 encap2+3 or encap3+4
layer2
#include
#include
#include
Specifies the MII link monitoring frequency in milliseconds
u32:0
Disable MII link monitoring
u32:50-1000
MII link monitoring frequency in milliseconds
100
Minimum number of member interfaces required up before enabling bond
u32:0-16
Minimum number of member interfaces required up before enabling bond
0
Rate in which we will ask our link partner to transmit LACPDU packets
slow fast
slow
Request partner to transmit LACPDUs every 30 seconds
fast
Request partner to transmit LACPDUs every 1 second
(slow|fast)
slow
Bonding mode
802.3ad active-backup broadcast round-robin transmit-load-balance adaptive-load-balance xor-hash
802.3ad
IEEE 802.3ad Dynamic link aggregation
active-backup
Fault tolerant: only one slave in the bond is active
broadcast
Fault tolerant: transmits everything on all slave interfaces
round-robin
Load balance: transmit packets in sequential order
transmit-load-balance
Load balance: adapts based on transmit load and speed
adaptive-load-balance
Load balance: adapts based on transmit and receive plus ARP
xor-hash
Distribute based on MAC address
(802.3ad|active-backup|broadcast|round-robin|transmit-load-balance|adaptive-load-balance|xor-hash)
mode must be 802.3ad, active-backup, broadcast, round-robin, transmit-load-balance, adaptive-load-balance, or xor
802.3ad
Bridge member interfaces
Member interface name
txt
Interface name
#include
Primary device interface
txt
Interface name
#include
#include
#include
#include
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index d633077d9..d52e213b6 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -1,224 +1,223 @@
Bridge Interface
310
br[0-9]+
Bridge interface must be named brN
brN
Bridge interface name
#include
MAC address aging interval
u32:0
Disable MAC address learning (always flood)
u32:10-1000000
MAC address aging time in seconds
300
#include
#include
#include
#include
#include
#include
#include
- #include
Forwarding delay
u32:0-200
Spanning Tree Protocol forwarding delay in seconds
Forwarding delay must be between 0 and 200 seconds
14
Hello packet advertisement interval
u32:1-10
Spanning Tree Protocol hello advertisement interval in seconds
Bridge Hello interval must be between 1 and 10 seconds
2
Internet Group Management Protocol (IGMP) and Multicast Listener Discovery (MLD) settings
Enable IGMP/MLD querier
Enable IGMP/MLD snooping
#include
#include
#include
#include
Enable VLAN aware bridge
Interval at which neighbor bridges are removed
u32:1-40
Bridge maximum aging time in seconds
Bridge max aging value must be between 1 and 40 seconds
20
Bridge member interfaces
Member interface name
Specify VLAN id which should natively be present on the link
u32:1-4094
Virtual Local Area Network (VLAN) ID
VLAN ID must be between 1 and 4094
Specify VLAN id which is allowed in this trunk interface
<id>
VLAN id allowed to pass this interface
<idN>-<idM>
VLAN id range allowed on this interface (use '-' as delimiter)
not a valid VLAN ID value or range
Bridge port cost
u32:1-65535
Path cost value for Spanning Tree Protocol
Path cost value must be between 1 and 65535
100
Bridge port priority
u32:0-63
Bridge port priority
Port priority value must be between 0 and 63
32
Port is isolated (also known as Private-VLAN)
Priority for this bridge
u32:0-65535
Bridge priority
Bridge priority must be between 0 and 65535 (multiples of 4096)
32768
Enable spanning tree protocol
#include
#include
diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in
index fb36741f7..eb525b547 100644
--- a/interface-definitions/interfaces-dummy.xml.in
+++ b/interface-definitions/interfaces-dummy.xml.in
@@ -1,39 +1,38 @@
Dummy Interface
300
dum[0-9]+
Dummy interface must be named dumN
dumN
Dummy interface name
#include
#include
#include
- #include
IPv4 routing parameters
#include
#include
#include
#include
#include
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 77f130e1c..e9ae0acfe 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -1,213 +1,212 @@
Network interfaces
Ethernet Interface
318
ethN
Ethernet interface name
((eth|lan)[0-9]+|(eno|ens|enp|enx).+)
Invalid Ethernet interface name
#include
#include
#include
#include
Disable Ethernet flow control (pause frames)
#include
#include
- #include
Duplex mode
auto half full
auto
Auto negotiation
half
Half duplex
full
Full duplex
(auto|half|full)
duplex must be auto, half or full
auto
#include
#include
#include
#include
#include
#include
#include
Configurable offload options
Enable Generic Receive Offload
Enable Generic Segmentation Offload
Enable Large Receive Offload
Enable Receive Packet Steering
Enable Receive Flow Steering
Enable Scatter-Gather
Enable TCP Segmentation Offloading
Link speed
auto 10 100 1000 2500 5000 10000 25000 40000 50000 100000
auto
Auto negotiation
10
10 Mbit/sec
100
100 Mbit/sec
1000
1 Gbit/sec
2500
2.5 Gbit/sec
5000
5 Gbit/sec
10000
10 Gbit/sec
25000
25 Gbit/sec
40000
40 Gbit/sec
50000
50 Gbit/sec
100000
100 Gbit/sec
(auto|10|100|1000|2500|5000|10000|25000|40000|50000|100000)
Speed must be auto, 10, 100, 1000, 2500, 5000, 10000, 25000, 40000, 50000 or 100000
auto
Shared buffer between the device driver and NIC
RX ring buffer
u32:80-16384
ring buffer size
TX ring buffer
u32:80-16384
ring buffer size
#include
#include
#include
#include
#include
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index b959c787d..f8e9909f8 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -1,60 +1,59 @@
Generic Network Virtualization Encapsulation (GENEVE) Interface
460
gnv[0-9]+
GENEVE interface must be named gnvN
gnvN
GENEVE interface name
#include
#include
#include
#include
#include
#include
#include
- #include
GENEVE tunnel parameters
IPv4 specific tunnel parameters
#include
#include
#include
IPv6 specific tunnel parameters
#include
#include
#include
#include
#include
diff --git a/interface-definitions/interfaces-input.xml.in b/interface-definitions/interfaces-input.xml.in
index d01c760f8..97502d954 100644
--- a/interface-definitions/interfaces-input.xml.in
+++ b/interface-definitions/interfaces-input.xml.in
@@ -1,28 +1,27 @@
Input Functional Block (IFB) interface name
310
ifb[0-9]+
Input interface must be named ifbN
ifbN
Input interface name
#include
#include
- #include
#include
diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in
index bde68dd5a..0ebc3253d 100644
--- a/interface-definitions/interfaces-l2tpv3.xml.in
+++ b/interface-definitions/interfaces-l2tpv3.xml.in
@@ -1,132 +1,131 @@
Layer 2 Tunnel Protocol Version 3 (L2TPv3) Interface
485
l2tpeth[0-9]+
L2TPv3 interface must be named l2tpethN
l2tpethN
L2TPv3 interface name
#include
#include
UDP destination port for L2TPv3 tunnel
u32:1-65535
Numeric IP port
5000
#include
- #include
Encapsulation type
udp ip
udp
UDP encapsulation
ip
IP encapsulation
(udp|ip)
Encapsulation must be UDP or IP
udp
#include
#include
#include
#include
#include
1488
Peer session identifier
u32:1-429496729
L2TPv3 peer session identifier
Peer tunnel identifier
u32:1-429496729
L2TPv3 peer tunnel identifier
#include
Session identifier
u32:1-429496729
L2TPv3 session identifier
UDP source port for L2TPv3 tunnel
u32:1-65535
Numeric IP port
5000
Local tunnel identifier
u32:1-429496729
L2TPv3 local tunnel identifier
#include
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index 5c9f4cd76..441236ec2 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -1,132 +1,131 @@
MACsec Interface (802.1ae)
461
macsec[0-9]+
MACsec interface must be named macsecN
macsecN
MACsec interface name
#include
#include
#include
#include
#include
- #include
#include
Security/Encryption Settings
Cipher suite used
gcm-aes-128 gcm-aes-256
gcm-aes-128
Galois/Counter Mode of AES cipher with 128-bit key
gcm-aes-256
Galois/Counter Mode of AES cipher with 256-bit key
(gcm-aes-128|gcm-aes-256)
Enable optional MACsec encryption
MACsec Key Agreement protocol (MKA)
Secure Connectivity Association Key
txt
16-byte (128-bit) hex-string (32 hex-digits) for gcm-aes-128 or 32-byte (256-bit) hex-string (64 hex-digits) for gcm-aes-256
[A-Fa-f0-9]{32}
[A-Fa-f0-9]{64}
Secure Connectivity Association Key Name
txt
32-byte (256-bit) hex-string (64 hex-digits)
[A-Fa-f0-9]{64}
Priority of MACsec Key Agreement protocol (MKA) actor
u32:0-255
MACsec Key Agreement protocol (MKA) priority
255
IEEE 802.1X/MACsec replay protection window
u32:0
No replay window, strict check
u32:1-4294967295
Number of packets that could be misordered
#include
#include
#include
1460
#include
#include
#include
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index 3876e31da..7cfb9ee7a 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -1,820 +1,819 @@
OpenVPN Tunnel Interface
460
vtun[0-9]+
OpenVPN tunnel interface must be named vtunN
vtunN
OpenVPN interface name
Authentication options
OpenVPN password used for authentication
OpenVPN username used for authentication
#include
- #include
OpenVPN interface device-type
tun tap
tun
TUN device, required for OSI layer 3
tap
TAP device, required for OSI layer 2
(tun|tap)
tun
#include
Data Encryption settings
Standard Data Encryption Algorithm
none des 3des bf128 bf256 aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm
none
Disable encryption
des
DES algorithm
3des
DES algorithm with triple encryption
bf128
Blowfish algorithm with 128-bit key
bf256
Blowfish algorithm with 256-bit key
aes128
AES algorithm with 128-bit key CBC
aes128gcm
AES algorithm with 128-bit key GCM
aes192
AES algorithm with 192-bit key CBC
aes192gcm
AES algorithm with 192-bit key GCM
aes256
AES algorithm with 256-bit key CBC
aes256gcm
AES algorithm with 256-bit key GCM
(none|des|3des|bf128|bf256|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)
Cipher negotiation list for use in server or client mode
none des 3des aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm
none
Disable encryption
des
DES algorithm
3des
DES algorithm with triple encryption
aes128
AES algorithm with 128-bit key CBC
aes128gcm
AES algorithm with 128-bit key GCM
aes192
AES algorithm with 192-bit key CBC
aes192gcm
AES algorithm with 192-bit key GCM
aes256
AES algorithm with 256-bit key CBC
aes256gcm
AES algorithm with 256-bit key GCM
(none|des|3des|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)
#include
#include
#include
Hashing Algorithm
md5 sha1 sha256 sha384 sha512
md5
MD5 algorithm
sha1
SHA-1 algorithm
sha256
SHA-256 algorithm
sha384
SHA-384 algorithm
sha512
SHA-512 algorithm
(md5|sha1|sha256|sha384|sha512)
Keepalive helper options
Maximum number of keepalive packet failures
u32:0-1000
Maximum number of keepalive packet failures
60
Keepalive packet interval in seconds
u32:0-600
Keepalive packet interval (seconds)
10
Local IP address of tunnel (IPv4 or IPv6)
Subnet-mask for local IP address of tunnel (IPv4 only)
Local IP address to accept connections (all if not set)
ipv4
Local IPv4 address
ipv6
Local IPv6 address
Local port number to accept connections
u32:1-65535
Numeric IP port
OpenVPN mode of operation
site-to-site client server
site-to-site
Site-to-site mode
client
Client in client-server mode
server
Server in client-server mode
(site-to-site|client|server)
Additional OpenVPN options. You must use the syntax of openvpn.conf in this text-field. Using this without proper knowledge may result in a crashed OpenVPN server. Check system log to look for errors.
Do not close and reopen interface (TUN/TAP device) on client restarts
OpenVPN communication protocol
udp tcp-passive tcp-active
udp
UDP
tcp-passive
TCP and accepts connections passively
tcp-active
TCP and initiates connections actively
(udp|tcp-passive|tcp-active)
udp
IP address of remote end of tunnel
ipv4
Remote end IPv4 address
ipv6
Remote end IPv6 address
Remote host to connect to (dynamic if not set)
ipv4
IPv4 address of remote host
ipv6
IPv6 address of remote host
txt
Hostname of remote host
Remote port number to connect to
u32:1-65535
Numeric IP port
OpenVPN tunnel to be used as the default route
Tunnel endpoints are on the same subnet
Server-mode options
Client-specific settings
name
Client common-name in the certificate
#include
IP address of the client
ipv4
Client IPv4 address
ipv6
Client IPv6 address
Route to be pushed to the client
ipv4net
IPv4 network and prefix length
ipv6net
IPv6 network and prefix length
Subnet belonging to the client (iroute)
ipv4net
IPv4 network and prefix length belonging to the client
ipv6net
IPv6 network and prefix length belonging to the client
Pool of client IPv4 addresses
#include
First IP address in the pool
ipv4
IPv4 address
Last IP address in the pool
ipv4
IPv4 address
Subnet mask pushed to dynamic clients. If not set the server subnet mask will be used. Only used with topology subnet or device type tap. Not used with bridged interfaces.
ipv4
IPv4 subnet mask
Pool of client IPv6 addresses
Client IPv6 pool base address with optional prefix length
ipv6net
Client IPv6 pool base address with optional prefix length (defaults: base = server subnet + 0x1000, prefix length = server prefix length)
#include
DNS suffix to be pushed to all clients
txt
Domain Name Server suffix
Number of maximum client connections
u32:1-4096
Number of concurrent clients
#include
Route to be pushed to all clients
ipv4net
IPv4 network and prefix length
ipv6net
IPv6 network and prefix length
Set metric for this route
u32:0-4294967295
Metric for this route
0
Reject connections from clients that are not explicitly configured
Server-mode subnet (from which client IPs are allocated)
ipv4net
IPv4 network and prefix length
ipv6net
IPv6 network and prefix length
Topology for clients
net30 point-to-point subnet
net30
net30 topology
point-to-point
Point-to-point topology
subnet
Subnet topology
(subnet|point-to-point|net30)
net30
multi-factor authentication
Time-based one-time passwords
Maximum allowed clock slop in seconds
1-65535
Seconds
180
Time drift in seconds
1-65535
Seconds
0
Step value for totp in seconds
1-65535
Seconds
30
Number of digits to use for totp hash
1-65535
Seconds
6
Expect password as result of a challenge response protocol
disable enable
disable
Disable challenge-response
enable
Enable chalenge-response
(disable|enable)
enable
Secret key shared with remote end of tunnel
pki openvpn shared-secret
Transport Layer Security (TLS) options
TLS shared secret key for tls-auth
pki openvpn shared-secret
#include
#include
Diffie Hellman parameters (server only)
pki dh
Static key to use to authenticate control channel
pki openvpn shared-secret
Specify the minimum required TLS version
1.0 1.1 1.2 1.3
1.0
TLS v1.0
1.1
TLS v1.1
1.2
TLS v1.2
1.3
TLS v1.3
(1.0|1.1|1.2|1.3)
TLS negotiation role
active passive
active
Initiate TLS negotiation actively
passive
Wait for incoming TLS connection
(active|passive)
Use fast LZO compression on this TUN/TAP interface
#include
#include
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index 84f76a7ee..719060fa9 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -1,119 +1,118 @@
Point-to-Point Protocol over Ethernet (PPPoE) Interface
322
pppoe[0-9]+
PPPoE interface must be named pppoeN
pppoeN
PPPoE dialer interface name
#include
#include
#include
- #include
#include
#include
#include
#include
#include
Delay before disconnecting idle session (in seconds)
u32:0-86400
Idle timeout in seconds
Timeout must be in range 0 to 86400
IPv4 routing parameters
#include
#include
#include
IPv6 routing parameters
IPv6 address configuration modes
#include
#include
#include
#include
IPv4 address of local end of the PPPoE link
ipv4
Address of local end of the PPPoE link
#include
#include
1492
Do not use DNS servers provided by the peer
IPv4 address of remote end of the PPPoE link
ipv4
Address of remote end of the PPPoE link
Service name, only connect to access concentrators advertising this
[a-zA-Z0-9]+
Service name must be alphanumeric only
#include
#include
diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in
index 4eb9bf111..2fe07ffd5 100644
--- a/interface-definitions/interfaces-pseudo-ethernet.xml.in
+++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in
@@ -1,69 +1,68 @@
Pseudo Ethernet Interface (Macvlan)
321
peth[0-9]+
Pseudo Ethernet interface must be named pethN
pethN
Pseudo Ethernet interface name
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- #include
Receive mode (default: private)
private vepa bridge passthru
private
No communication with other pseudo-devices
vepa
Virtual Ethernet Port Aggregator reflective relay
bridge
Simple bridge between pseudo-devices
passthru
Promicious mode passthrough of underlying device
(private|vepa|bridge|passthru)
mode must be private, vepa, bridge or passthru
private
#include
#include
#include
#include
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index fe49d337a..333a5b178 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -1,296 +1,295 @@
Tunnel interface
380
tun[0-9]+
tunnel interface must be named tunN
tunN
Tunnel interface name
#include
#include
#include
#include
#include
1476
#include
#include
#include
#include
#include
- #include
6rd network prefix
ipv6
IPv6 address and prefix length
6rd relay prefix
ipv4net
IPv4 prefix of interface for 6rd
Encapsulation of this tunnel interface
erspan gre gretap ip6erspan ip6gre ip6gretap ip6ip6 ipip ipip6 sit
erspan
Encapsulated Remote Switched Port Analyzer
gre
Generic Routing Encapsulation (network layer)
gretap
Generic Routing Encapsulation (datalink layer)
ip6erspan
Encapsulated Remote Switched Port Analyzer over IPv6
ip6gre
GRE over IPv6 (network layer)
ip6gretap
GRE over IPv6 (datalink layer)
ip6ip6
IPv6 in IPv6 encapsulation
ipip
IPv4 in IPv4 encapsulation
ipip6
IPv4 in IP6 encapsulation
sit
Simple Internet Transition (IPv6 in IPv4)
(erspan|gre|gretap|ip6erspan|ip6gre|ip6gretap|ip6ip6|ipip|ipip6|sit)
Invalid encapsulation, must be one of: erspan, gre, gretap, ip6erspan, ip6gre, ip6gretap, ipip, sit, ipip6 or ip6ip6
#include
Multicast operation over tunnel
enable disable
enable
Enable multicast
disable
Disable multicast (default)
(enable|disable)
Must be 'disable' or 'enable'
Tunnel parameters
ERSPAN tunnel parameters
Mirrored traffic direction
ingress egress
ingress
Mirror ingress traffic
egress
Mirror egress traffic
(ingress|egress)
Unique identifier of an ERSPAN engine within a system
u32:0-1048575
Unique identifier of an ERSPAN engine
ERSPAN version 1 index field
u32:0-63
Platform-depedent field for specifying port number and direction
Protocol version
1 2
1
ERSPAN Type II
2
ERSPAN Type III
1
IPv4-specific tunnel parameters
Disable path MTU discovery
Ignore the DF (don't fragment) bit
#include
#include
#include
64
IPv6-specific tunnel parameters
Set fixed encapsulation limit
none
u32:0-255
Encapsulation limit
none
Disable encapsulation limit
(none)
Tunnel encaplimit must be 0-255 or none
4
#include
Hoplimit
u32:0-255
Hop limit
hop limit must be between 0-255
64
Traffic class (Tclass)
0x0-0x0fffff
Traffic class, 'inherit' or hex value
(0x){0,1}(0?[0-9A-Fa-f]{1,2})
Must be 'inherit' or a number
inherit
#include
#include
diff --git a/interface-definitions/interfaces-vti.xml.in b/interface-definitions/interfaces-vti.xml.in
index eeaea0dc3..11f001dc0 100644
--- a/interface-definitions/interfaces-vti.xml.in
+++ b/interface-definitions/interfaces-vti.xml.in
@@ -1,33 +1,32 @@
Virtual Tunnel Interface (XFRM)
381
vti[0-9]+
VTI interface must be named vtiN
vtiN
VTI interface name
#include
#include
#include
#include
#include
#include
#include
#include
#include
- #include
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 4902ff36d..331f930d3 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -1,109 +1,108 @@
Virtual Extensible LAN (VXLAN) Interface
460
vxlan[0-9]+
VXLAN interface must be named vxlanN
vxlanN
VXLAN interface name
#include
#include
#include
Use external control plane
Enable Generic Protocol extension (VXLAN-GPE)
Multicast group address for VXLAN interface
ipv4
Multicast IPv4 group address
ipv6
Multicast IPv6 group address
Multicast IPv4/IPv6 address required
#include
#include
#include
#include
#include
- #include
1450
VXLAN tunnel parameters
IPv4 specific tunnel parameters
#include
#include
#include
16
IPv6 specific tunnel parameters
#include
Do not add unknown addresses into forwarding database
#include
8472
#include
#include
#include
#include
#include
#include
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index 23f50d146..35e223588 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -1,128 +1,127 @@
WireGuard Interface
459
wg[0-9]+
WireGuard interface must be named wgN
wgN
WireGuard interface name
#include
#include
#include
#include
#include
- #include
#include
1420
#include
#include
A 32-bit fwmark value set on all outgoing packets
number
value which marks the packet for QoS/shaper
0
Base64 encoded private key
[0-9a-zA-Z\+/]{43}=
Key is not valid 44-character (32-bytes) base64
peer alias
[^ ]{1,100}
peer alias too long (limit 100 characters)
#include
base64 encoded public key
[0-9a-zA-Z\+/]{43}=
Key is not valid 44-character (32-bytes) base64
base64 encoded preshared key
[0-9a-zA-Z\+/]{43}=
Key is not valid 44-character (32-bytes) base64
IP addresses allowed to traverse the peer
IP address of tunnel endpoint
ipv4
IPv4 address of remote tunnel endpoint
ipv6
IPv6 address of remote tunnel endpoint
#include
Interval to send keepalive messages
u32:1-65535
Interval in seconds
#include
#include
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index 9e7fc29bc..5271df624 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -1,789 +1,788 @@
Wireless (WiFi/WLAN) Network Interface
318
wlan[0-9]+
Wireless interface must be named wlanN
wlanN
Wireless (WiFi/WLAN) interface name
#include
- #include
HT and VHT capabilities for your card
HT (High Throughput) settings
40MHz intolerance, use 20MHz only!
Enable WMM-PS unscheduled automatic power aave delivery [U-APSD]
Supported channel set width
ht20 ht40+ ht40-
ht20
Supported channel set width both 20 MHz only
ht40+
Supported channel set width both 20 MHz and 40 MHz with secondary channel above primary channel
ht40-
Supported channel set width both 20 MHz and 40 MHz with secondary channel below primary channel
(ht20|ht40\+|ht40-)
Enable HT-delayed block ack
Enable DSSS_CCK-40
Enable HT-greenfield
Enable LDPC coding capability
Enable L-SIG TXOP protection capability
Set maximum A-MSDU length
3839 7935
3839
Set maximum A-MSDU length to 3839 octets
7935
Set maximum A-MSDU length to 7935 octets
(3839|7935)
Short GI capabilities
20 40
20
Short GI for 20 MHz
40
Short GI for 40 MHz
(20|40)
Spatial Multiplexing Power Save (SMPS) settings
static dynamic
static
STATIC Spatial Multiplexing (SM) Power Save
dynamic
DYNAMIC Spatial Multiplexing (SM) Power Save
(static|dynamic)
Support for sending and receiving PPDU using STBC (Space Time Block Coding)
Enable receiving PPDU using STBC (Space Time Block Coding)
[1-3]+
Number of spacial streams that can use RX STBC
[1-3]+
Invalid capability item
Enable sending PPDU using STBC (Space Time Block Coding)
Require stations to support HT PHY (reject association if they do not)
Require stations to support VHT PHY (reject association if they do not)
VHT (Very High Throughput) settings
Number of antennas on this card
u32:1-8
Number of antennas for this card
Set if antenna pattern does not change during the lifetime of an association
Beamforming capabilities
single-user-beamformer single-user-beamformee multi-user-beamformer multi-user-beamformee
single-user-beamformer
Support for operation as single user beamformer
single-user-beamformee
Support for operation as single user beamformee
multi-user-beamformer
Support for operation as multi user beamformer
multi-user-beamformee
Support for operation as multi user beamformee
(single-user-beamformer|single-user-beamformee|multi-user-beamformer|multi-user-beamformee)
VHT operating channel center frequency
VHT operating channel center frequency - center freq 1 (for use with 80, 80+80 and 160 modes)
u32:34-173
5Ghz (802.11 a/h/j/n/ac) center channel index (use 42 for primary 80MHz channel 36)
Channel center value must be between 34 and 173
VHT operating channel center frequency - center freq 2 (for use with the 80+80 mode)
u32:34-173
5Ghz (802.11 a/h/j/n/ac) center channel index (use 58 for primary 80MHz channel 52)
Channel center value must be between 34 and 173
VHT operating Channel width
0 1 2 3
0
20 or 40 MHz channel width
1
80 MHz channel width
2
160 MHz channel width
3
80+80 MHz channel width
Enable LDPC (Low Density Parity Check) coding capability
VHT link adaptation capabilities
unsolicited both
unsolicited
Station provides only unsolicited VHT MFB
both
Station can provide VHT MFB in response to VHT MRQ and unsolicited VHT MFB
(unsolicited|both)
Invalid capability item
Set the maximum length of A-MPDU pre-EOF padding that the station can receive
u32:0-7
Maximum length of A-MPDU pre-EOF padding = 2 pow(13 + x) -1 octets
Increase Maximum MPDU length to 7991 or 11454 octets (otherwise: 3895 octets)
7991 11454
7991
ncrease Maximum MPDU length to 7991 octets
11454
ncrease Maximum MPDU length to 11454 octets
(7991|11454)
Short GI capabilities
80 160
80
Short GI for 80 MHz
160
Short GI for 160 MHz
(80|160)
Support for sending and receiving PPDU using STBC (Space Time Block Coding)
Enable receiving PPDU using STBC (Space Time Block Coding)
[1-4]+
Number of spacial streams that can use RX STBC
[1-4]+
Invalid capability item
Enable sending PPDU using STBC (Space Time Block Coding)
Enable VHT TXOP Power Save Mode
Station supports receiving VHT variant HT Control field
Wireless radio channel
0
Automatic Channel Selection (ACS)
u32:1-14
2.4Ghz (802.11 b/g/n) Channel
u32:34-173
5Ghz (802.11 a/h/j/n/ac) Channel
0
Indicate country in which device is operating
us eu jp de uk cn es fr ru
txt
ISO/IEC 3166-1 Country Code
[a-z][a-z]
Invalid ISO/IEC 3166-1 Country Code
#include
#include
#include
Disable broadcast of SSID from access-point
#include
#include
#include
Disassociate stations based on excessive transmission failures
#include
#include
#include
Isolate stations on the AP so they cannot see each other
#include
Maximum number of wireless radio stations. Excess stations will be rejected upon authentication request.
u32:1-2007
Number of allowed stations
Number of stations must be between 1 and 2007
Management Frame Protection (MFP) according to IEEE 802.11w
disabled optional required
disabled
no MFP
optional
MFP optional
required
MFP enforced
(disabled|optional|required)
disabled
Wireless radio mode
a b g n ac
a
802.11a - 54 Mbits/sec
b
802.11b - 11 Mbits/sec
g
802.11g - 54 Mbits/sec
n
802.11n - 600 Mbits/sec
ac
802.11ac - 1300 Mbits/sec
(a|b|g|n|ac)
g
#include
Wireless physical device
phy0
Transmission power reduction in dBm
u32:0-255
TX power reduction in dBm
dBm value must be between 0 and 255
Wireless security settings
Wired Equivalent Privacy (WEP) parameters
WEP encryption key
txt
Wired Equivalent Privacy key
([a-fA-F0-9]{10}|[a-fA-F0-9]{26}|[a-fA-F0-9]{32})
Invalid WEP key
Wifi Protected Access (WPA) parameters
Cipher suite for WPA unicast packets
GCMP-256 GCMP CCMP-256 CCMP TKIP
GCMP-256
AES in Galois/counter mode with 256-bit key
GCMP
AES in Galois/counter mode with 128-bit key
CCMP-256
AES in Counter mode with CBC-MAC with 256-bit key
CCMP
AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] (supported on all WPA2 APs)
TKIP
Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)
Invalid cipher selection
Cipher suite for WPA multicast and broadcast packets
GCMP-256 GCMP CCMP-256 CCMP TKIP
GCMP-256
AES in Galois/counter mode with 256-bit key
GCMP
AES in Galois/counter mode with 128-bit key
CCMP-256
AES in Counter mode with CBC-MAC with 256-bit key
CCMP
AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] (supported on all WPA2 APs)
TKIP
Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)
Invalid group cipher selection
WPA mode
wpa wpa2 wpa+wpa2 wpa3
wpa
WPA (IEEE 802.11i/D3.0)
wpa2
WPA2 (full IEEE 802.11i/RSN)
wpa+wpa2
Allow both WPA and WPA2
(wpa|wpa2|wpa\+wpa2|wpa3)
Unknown WPA mode
wpa+wpa2
WPA personal shared pass phrase. If you are using special characters in the WPA passphrase then single quotes are required.
txt
Passphrase of at least 8 but not more than 63 printable characters
.{8,63}
Invalid WPA pass phrase, must be 8 to 63 printable characters!
#include
Enable RADIUS server to receive accounting info
Wireless access-point service set identifier (SSID)
.{1,32}
Invalid SSID
Wireless device type for this interface
access-point station monitor
access-point
Access-point forwards packets between other nodes
station
Connects to another access point
monitor
Passively monitor all packets on the frequency/channel
(access-point|station|monitor)
Type must be access-point, station or monitor
monitor
#include
#include
#include
diff --git a/interface-definitions/interfaces-wwan.xml.in b/interface-definitions/interfaces-wwan.xml.in
index b0b8367dc..758784540 100644
--- a/interface-definitions/interfaces-wwan.xml.in
+++ b/interface-definitions/interfaces-wwan.xml.in
@@ -1,49 +1,48 @@
Wireless Modem (WWAN) Interface
350
wwan[0-9]+
Wireless Modem interface must be named wwanN
wwanN
Wireless Wide Area Network interface name
#include
Access Point Name (APN)
#include
#include
#include
#include
#include
#include
#include
#include
1430
#include
#include
#include
- #include
#include
#include
diff --git a/interface-definitions/policy-route.xml.in b/interface-definitions/policy-route.xml.in
index 44b96c2e6..48a5bf7d1 100644
--- a/interface-definitions/policy-route.xml.in
+++ b/interface-definitions/policy-route.xml.in
@@ -1,113 +1,115 @@
Policy route rule set name for IPv6
[a-zA-Z0-9][\w\-\.]*
201
#include
+ #include
#include
Policy rule number
u32:1-999999
Number of policy rule
Policy rule number must be between 1 and 999999
Destination parameters
#include
#include
#include
Source parameters
#include
#include
#include
#include
#include
#include
#include
#include
Policy route rule set name for IPv4
[a-zA-Z0-9][\w\-\.]*
201
#include
+ #include
#include
Policy rule number
u32:1-999999
Number of policy rule
Policy rule number must be between 1 and 999999
Destination parameters
#include
#include
#include
Source parameters
#include
#include
#include
#include
#include
#include
#include
#include
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index 046e385bb..11b3c678e 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -1,238 +1,246 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021-2022 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 .
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.util import cmd
mark = '100'
table_mark_offset = 0x7fffffff
table_id = '101'
interface = 'eth0'
interface_ip = '172.16.10.1/24'
class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestPolicyRoute, cls).setUpClass()
cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip])
cls.cli_set(cls, ['protocols', 'static', 'table', table_id, 'route', '0.0.0.0/0', 'interface', interface])
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip])
cls.cli_delete(cls, ['protocols', 'static', 'table', table_id])
super(TestPolicyRoute, cls).tearDownClass()
def tearDown(self):
- self.cli_delete(['interfaces', 'ethernet', interface, 'policy'])
self.cli_delete(['policy', 'route'])
self.cli_delete(['policy', 'route6'])
self.cli_commit()
+ # Verify nftables cleanup
nftables_search = [
['set N_smoketest_network'],
['set N_smoketest_network1'],
['chain VYOS_PBR_smoketest']
]
- self.verify_nftables(nftables_search, 'ip mangle', inverse=True)
+ self.verify_nftables(nftables_search, 'ip vyos_mangle', inverse=True)
+
+ # Verify ip rule cleanup
+ ip_rule_search = [
+ ['fwmark ' + hex(table_mark_offset - int(table_id)), 'lookup ' + table_id]
+ ]
+
+ self.verify_rules(ip_rule_search, inverse=True)
def verify_nftables(self, nftables_search, table, inverse=False):
nftables_output = cmd(f'sudo nft list table {table}')
for search in nftables_search:
matched = False
for line in nftables_output.split("\n"):
if all(item in line for item in search):
matched = True
break
self.assertTrue(not matched if inverse else matched, msg=search)
+ def verify_rules(self, rules_search, inverse=False):
+ rule_output = cmd('ip rule show')
+
+ for search in rules_search:
+ matched = False
+ for line in rule_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(not matched if inverse else matched, msg=search)
+
def test_pbr_group(self):
self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'network', '172.16.99.0/24'])
self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network1', 'network', '172.16.101.0/24'])
self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network1', 'include', 'smoketest_network'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'group', 'network-group', 'smoketest_network1'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'mark', mark])
-
- self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])
+ self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_commit()
nftables_search = [
[f'iifname "{interface}"','jump VYOS_PBR_smoketest'],
['ip daddr @N_smoketest_network1', 'ip saddr @N_smoketest_network'],
]
- self.verify_nftables(nftables_search, 'ip mangle')
+ self.verify_nftables(nftables_search, 'ip vyos_mangle')
self.cli_delete(['firewall'])
def test_pbr_mark(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'address', '172.16.20.10'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'mark', mark])
-
- self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])
+ self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_commit()
mark_hex = "{0:#010x}".format(int(mark))
nftables_search = [
[f'iifname "{interface}"','jump VYOS_PBR_smoketest'],
['ip daddr 172.16.10.10', 'ip saddr 172.16.20.10', 'meta mark set ' + mark_hex],
]
- self.verify_nftables(nftables_search, 'ip mangle')
+ self.verify_nftables(nftables_search, 'ip vyos_mangle')
def test_pbr_table(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'port', '8888'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'syn'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'tcp_udp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'destination', 'port', '8888'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'set', 'table', table_id])
- self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])
- self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route6', 'smoketest6'])
+ self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface])
self.cli_commit()
mark_hex = "{0:#010x}".format(table_mark_offset - int(table_id))
# IPv4
nftables_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'],
['tcp flags syn / syn,ack', 'tcp dport 8888', 'meta mark set ' + mark_hex]
]
- self.verify_nftables(nftables_search, 'ip mangle')
+ self.verify_nftables(nftables_search, 'ip vyos_mangle')
# IPv6
nftables6_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'],
['meta l4proto { tcp, udp }', 'th dport 8888', 'meta mark set ' + mark_hex]
]
- self.verify_nftables(nftables6_search, 'ip6 mangle')
+ self.verify_nftables(nftables6_search, 'ip6 vyos_mangle')
# IP rule fwmark -> table
ip_rule_search = [
['fwmark ' + hex(table_mark_offset - int(table_id)), 'lookup ' + table_id]
]
- ip_rule_output = cmd('ip rule show')
-
- for search in ip_rule_search:
- matched = False
- for line in ip_rule_output.split("\n"):
- if all(item in line for item in search):
- matched = True
- break
- self.assertTrue(matched)
+ self.verify_rules(ip_rule_search)
def test_pbr_matching_criteria(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'udp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'action', 'drop'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'tcp', 'flags', 'syn'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'source', 'address', '198.51.100.0/24'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'destination', 'port', '22'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'state', 'new', 'enable'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'ttl', 'gt', '2'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'protocol', 'icmp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'icmp', 'type-name', 'echo-request'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '128'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '1024-2048'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'log', 'enable'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'dscp', '41'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'dscp', '57-59'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'udp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'action', 'drop'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'protocol', 'tcp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'tcp', 'flags', 'syn'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'source', 'address', '2001:db8::0/64'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'protocol', 'tcp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'destination', 'port', '22'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'state', 'new', 'enable'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'hop-limit', 'gt', '2'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'protocol', 'icmpv6'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'icmpv6', 'type', 'echo-request'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '128'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '1024-2048'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'log', 'enable'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '61'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '14-19'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'set', 'table', table_id])
- self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])
- self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route6', 'smoketest6'])
+ self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface])
self.cli_commit()
mark_hex = "{0:#010x}".format(table_mark_offset - int(table_id))
# IPv4
nftables_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'],
['meta l4proto udp', 'drop'],
['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex],
['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark set ' + mark_hex],
['meta l4proto icmp', 'log prefix "[smoketest-4-A]"', 'icmp type echo-request', 'ip length { 128, 1024-2048 }', 'meta mark set ' + mark_hex],
['ip dscp { 0x29, 0x39-0x3b }', 'meta mark set ' + mark_hex]
]
- self.verify_nftables(nftables_search, 'ip mangle')
+ self.verify_nftables(nftables_search, 'ip vyos_mangle')
# IPv6
nftables6_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'],
['meta l4proto udp', 'drop'],
['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex],
['ct state new', 'tcp dport 22', 'ip6 saddr 2001:db8::/64', 'ip6 hoplimit > 2', 'meta mark set ' + mark_hex],
['meta l4proto ipv6-icmp', 'log prefix "[smoketest6-4-A]"', 'icmpv6 type echo-request', 'ip6 length != { 128, 1024-2048 }', 'meta mark set ' + mark_hex],
['ip6 dscp != { 0x0e-0x13, 0x3d }', 'meta mark set ' + mark_hex]
]
- self.verify_nftables(nftables6_search, 'ip6 mangle')
+ self.verify_nftables(nftables6_search, 'ip6 vyos_mangle')
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/policy-route-interface.py b/src/conf_mode/policy-route-interface.py
deleted file mode 100755
index 58c5fd93d..000000000
--- a/src/conf_mode/policy-route-interface.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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 .
-
-import os
-import re
-
-from sys import argv
-from sys import exit
-
-from vyos.config import Config
-from vyos.ifconfig import Section
-from vyos.template import render
-from vyos.util import cmd
-from vyos.util import run
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
-
- ifname = argv[1]
- ifpath = Section.get_config_path(ifname)
- if_policy_path = f'interfaces {ifpath} policy'
-
- if_policy = conf.get_config_dict(if_policy_path, key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
- if_policy['ifname'] = ifname
- if_policy['policy'] = conf.get_config_dict(['policy'], key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
- return if_policy
-
-def verify_chain(table, chain):
- # Verify policy route applied
- code = run(f'nft list chain {table} {chain}')
- return code == 0
-
-def verify(if_policy):
- # bail out early - looks like removal from running config
- if not if_policy:
- return None
-
- for route in ['route', 'route6']:
- if route in if_policy:
- if route not in if_policy['policy']:
- raise ConfigError('Policy route not configured')
-
- route_name = if_policy[route]
-
- if route_name not in if_policy['policy'][route]:
- raise ConfigError(f'Invalid policy route name "{name}"')
-
- nft_prefix = 'VYOS_PBR6_' if route == 'route6' else 'VYOS_PBR_'
- nft_table = 'ip6 mangle' if route == 'route6' else 'ip mangle'
-
- if not verify_chain(nft_table, nft_prefix + route_name):
- raise ConfigError('Policy route did not apply')
-
- return None
-
-def generate(if_policy):
- return None
-
-def cleanup_rule(table, chain, ifname, new_name=None):
- results = cmd(f'nft -a list chain {table} {chain}').split("\n")
- retval = None
- for line in results:
- if f'ifname "{ifname}"' in line:
- if new_name and f'jump {new_name}' in line:
- # new_name is used to clear rules for any previously referenced chains
- # returns true when rule exists and doesn't need to be created
- retval = True
- continue
-
- handle_search = re.search('handle (\d+)', line)
- if handle_search:
- cmd(f'nft delete rule {table} {chain} handle {handle_search[1]}')
- return retval
-
-def apply(if_policy):
- ifname = if_policy['ifname']
-
- route_chain = 'VYOS_PBR_PREROUTING'
- ipv6_route_chain = 'VYOS_PBR6_PREROUTING'
-
- if 'route' in if_policy:
- name = 'VYOS_PBR_' + if_policy['route']
- rule_exists = cleanup_rule('ip mangle', route_chain, ifname, name)
-
- if not rule_exists:
- cmd(f'nft insert rule ip mangle {route_chain} iifname {ifname} counter jump {name}')
- else:
- cleanup_rule('ip mangle', route_chain, ifname)
-
- if 'route6' in if_policy:
- name = 'VYOS_PBR6_' + if_policy['route6']
- rule_exists = cleanup_rule('ip6 mangle', ipv6_route_chain, ifname, name)
-
- if not rule_exists:
- cmd(f'nft insert rule ip6 mangle {ipv6_route_chain} iifname {ifname} counter jump {name}')
- else:
- cleanup_rule('ip6 mangle', ipv6_route_chain, ifname)
-
- 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/policy-route.py b/src/conf_mode/policy-route.py
index 00539b9c7..1d016695e 100755
--- a/src/conf_mode/policy-route.py
+++ b/src/conf_mode/policy-route.py
@@ -1,294 +1,194 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021 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 .
import os
-import re
from json import loads
from sys import exit
from vyos.base import Warning
from vyos.config import Config
from vyos.template import render
from vyos.util import cmd
from vyos.util import dict_search_args
-from vyos.util import dict_search_recursive
from vyos.util import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
mark_offset = 0x7FFFFFFF
nftables_conf = '/run/nftables_policy.conf'
-ROUTE_PREFIX = 'VYOS_PBR_'
-ROUTE6_PREFIX = 'VYOS_PBR6_'
-
-preserve_chains = [
- 'VYOS_PBR_PREROUTING',
- 'VYOS_PBR_POSTROUTING',
- 'VYOS_PBR6_PREROUTING',
- 'VYOS_PBR6_POSTROUTING'
-]
-
valid_groups = [
'address_group',
+ 'domain_group',
'network_group',
'port_group'
]
-group_set_prefix = {
- 'A_': 'address_group',
- 'A6_': 'ipv6_address_group',
-# 'D_': 'domain_group',
- 'M_': 'mac_group',
- 'N_': 'network_group',
- 'N6_': 'ipv6_network_group',
- 'P_': 'port_group'
-}
-
-def get_policy_interfaces(conf):
- out = {}
- interfaces = conf.get_config_dict(['interfaces'], key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
- def find_interfaces(iftype_conf, output={}, prefix=''):
- for ifname, if_conf in iftype_conf.items():
- if 'policy' in if_conf:
- output[prefix + ifname] = if_conf['policy']
- for vif in ['vif', 'vif_s', 'vif_c']:
- if vif in if_conf:
- output.update(find_interfaces(if_conf[vif], output, f'{prefix}{ifname}.'))
- return output
- for iftype, iftype_conf in interfaces.items():
- out.update(find_interfaces(iftype_conf))
- return out
-
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
base = ['policy']
policy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
no_tag_node_value_mangle=True)
policy['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True,
no_tag_node_value_mangle=True)
- policy['interfaces'] = get_policy_interfaces(conf)
return policy
def verify_rule(policy, name, rule_conf, ipv6, rule_id):
icmp = 'icmp' if not ipv6 else 'icmpv6'
if icmp in rule_conf:
icmp_defined = False
if 'type_name' in rule_conf[icmp]:
icmp_defined = True
if 'code' in rule_conf[icmp] or 'type' in rule_conf[icmp]:
raise ConfigError(f'{name} rule {rule_id}: Cannot use ICMP type/code with ICMP type-name')
if 'code' in rule_conf[icmp]:
icmp_defined = True
if 'type' not in rule_conf[icmp]:
raise ConfigError(f'{name} rule {rule_id}: ICMP code can only be defined if ICMP type is defined')
if 'type' in rule_conf[icmp]:
icmp_defined = True
if icmp_defined and 'protocol' not in rule_conf or rule_conf['protocol'] != icmp:
raise ConfigError(f'{name} rule {rule_id}: ICMP type/code or type-name can only be defined if protocol is ICMP')
if 'set' in rule_conf:
if 'tcp_mss' in rule_conf['set']:
tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
if not tcp_flags or 'syn' not in tcp_flags:
raise ConfigError(f'{name} rule {rule_id}: TCP SYN flag must be set to modify TCP-MSS')
tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
if tcp_flags:
if dict_search_args(rule_conf, 'protocol') != 'tcp':
raise ConfigError('Protocol must be tcp when specifying tcp flags')
not_flags = dict_search_args(rule_conf, 'tcp', 'flags', 'not')
if not_flags:
duplicates = [flag for flag in tcp_flags if flag in not_flags]
if duplicates:
raise ConfigError(f'Cannot match a tcp flag as set and not set')
for side in ['destination', 'source']:
if side in rule_conf:
side_conf = rule_conf[side]
if 'group' in side_conf:
- if {'address_group', 'network_group'} <= set(side_conf['group']):
- raise ConfigError('Only one address-group or network-group can be specified')
+ if len({'address_group', 'domain_group', 'network_group'} & set(side_conf['group'])) > 1:
+ raise ConfigError('Only one address-group, domain-group or network-group can be specified')
for group in valid_groups:
if group in side_conf['group']:
group_name = side_conf['group'][group]
if group_name.startswith('!'):
group_name = group_name[1:]
fw_group = f'ipv6_{group}' if ipv6 and group in ['address_group', 'network_group'] else group
error_group = fw_group.replace("_", "-")
group_obj = dict_search_args(policy['firewall_group'], fw_group, group_name)
if group_obj is None:
raise ConfigError(f'Invalid {error_group} "{group_name}" on policy route rule')
if not group_obj:
Warning(f'{error_group} "{group_name}" has no members')
if 'port' in side_conf or dict_search_args(side_conf, 'group', 'port_group'):
if 'protocol' not in rule_conf:
raise ConfigError('Protocol must be defined if specifying a port or port-group')
if rule_conf['protocol'] not in ['tcp', 'udp', 'tcp_udp']:
raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port or port-group')
def verify(policy):
for route in ['route', 'route6']:
ipv6 = route == 'route6'
if route in policy:
for name, pol_conf in policy[route].items():
if 'rule' in pol_conf:
for rule_id, rule_conf in pol_conf['rule'].items():
verify_rule(policy, name, rule_conf, ipv6, rule_id)
- for ifname, if_policy in policy['interfaces'].items():
- name = dict_search_args(if_policy, 'route')
- ipv6_name = dict_search_args(if_policy, 'route6')
-
- if name and not dict_search_args(policy, 'route', name):
- raise ConfigError(f'Policy route "{name}" is still referenced on interface {ifname}')
-
- if ipv6_name and not dict_search_args(policy, 'route6', ipv6_name):
- raise ConfigError(f'Policy route6 "{ipv6_name}" is still referenced on interface {ifname}')
-
return None
-def cleanup_commands(policy):
- commands = []
- commands_chains = []
- commands_sets = []
- for table in ['ip mangle', 'ip6 mangle']:
- route_node = 'route' if table == 'ip mangle' else 'route6'
- chain_prefix = ROUTE_PREFIX if table == 'ip mangle' else ROUTE6_PREFIX
-
- json_str = cmd(f'nft -t -j list table {table}')
- obj = loads(json_str)
- if 'nftables' not in obj:
- continue
- for item in obj['nftables']:
- if 'chain' in item:
- chain = item['chain']['name']
- if chain in preserve_chains or not chain.startswith("VYOS_PBR"):
- continue
-
- if dict_search_args(policy, route_node, chain.replace(chain_prefix, "", 1)) != None:
- commands.append(f'flush chain {table} {chain}')
- else:
- commands_chains.append(f'delete chain {table} {chain}')
-
- if 'rule' in item:
- rule = item['rule']
- chain = rule['chain']
- handle = rule['handle']
-
- if chain not in preserve_chains:
- continue
-
- target, _ = next(dict_search_recursive(rule['expr'], 'target'))
-
- if target.startswith(chain_prefix):
- if dict_search_args(policy, route_node, target.replace(chain_prefix, "", 1)) == None:
- commands.append(f'delete rule {table} {chain} handle {handle}')
-
- if 'set' in item:
- set_name = item['set']['name']
-
- for prefix, group_type in group_set_prefix.items():
- if set_name.startswith(prefix):
- group_name = set_name.replace(prefix, "", 1)
- if dict_search_args(policy, 'firewall_group', group_type, group_name) != None:
- commands_sets.append(f'flush set {table} {set_name}')
- else:
- commands_sets.append(f'delete set {table} {set_name}')
-
- return commands + commands_chains + commands_sets
-
def generate(policy):
if not os.path.exists(nftables_conf):
policy['first_install'] = True
- else:
- policy['cleanup_commands'] = cleanup_commands(policy)
render(nftables_conf, 'firewall/nftables-policy.j2', policy)
return None
def apply_table_marks(policy):
for route in ['route', 'route6']:
if route in policy:
cmd_str = 'ip' if route == 'route' else 'ip -6'
tables = []
for name, pol_conf in policy[route].items():
if 'rule' in pol_conf:
for rule_id, rule_conf in pol_conf['rule'].items():
set_table = dict_search_args(rule_conf, 'set', 'table')
if set_table:
if set_table == 'main':
set_table = '254'
if set_table in tables:
continue
tables.append(set_table)
table_mark = mark_offset - int(set_table)
cmd(f'{cmd_str} rule add pref {set_table} fwmark {table_mark} table {set_table}')
def cleanup_table_marks():
for cmd_str in ['ip', 'ip -6']:
json_rules = cmd(f'{cmd_str} -j -N rule list')
rules = loads(json_rules)
for rule in rules:
if 'fwmark' not in rule or 'table' not in rule:
continue
fwmark = rule['fwmark']
table = int(rule['table'])
if fwmark[:2] == '0x':
fwmark = int(fwmark, 16)
if (int(fwmark) == (mark_offset - table)):
cmd(f'{cmd_str} rule del fwmark {fwmark} table {table}')
def apply(policy):
install_result = run(f'nft -f {nftables_conf}')
if install_result == 1:
raise ConfigError('Failed to apply policy based routing')
if 'first_install' not in policy:
cleanup_table_marks()
apply_table_marks(policy)
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/helpers/vyos-domain-resolver.py b/src/helpers/vyos-domain-resolver.py
index 035c208b2..e31d9238e 100755
--- a/src/helpers/vyos-domain-resolver.py
+++ b/src/helpers/vyos-domain-resolver.py
@@ -1,183 +1,183 @@
#!/usr/bin/env python3
#
# Copyright (C) 2022 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 .
import json
import os
import time
from vyos.configdict import dict_merge
from vyos.configquery import ConfigTreeQuery
from vyos.firewall import fqdn_config_parse
from vyos.firewall import fqdn_resolve
from vyos.util import cmd
from vyos.util import commit_in_progress
from vyos.util import dict_search_args
from vyos.util import run
from vyos.xml import defaults
base = ['firewall']
timeout = 300
cache = False
domain_state = {}
ipv4_tables = {
- 'ip mangle',
+ 'ip vyos_mangle',
'ip vyos_filter',
'ip vyos_nat'
}
ipv6_tables = {
- 'ip6 mangle',
+ 'ip6 vyos_mangle',
'ip6 vyos_filter'
}
def get_config(conf):
firewall = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
no_tag_node_value_mangle=True)
default_values = defaults(base)
for tmp in ['name', 'ipv6_name']:
if tmp in default_values:
del default_values[tmp]
if 'zone' in default_values:
del default_values['zone']
firewall = dict_merge(default_values, firewall)
global timeout, cache
if 'resolver_interval' in firewall:
timeout = int(firewall['resolver_interval'])
if 'resolver_cache' in firewall:
cache = True
fqdn_config_parse(firewall)
return firewall
def resolve(domains, ipv6=False):
global domain_state
ip_list = set()
for domain in domains:
resolved = fqdn_resolve(domain, ipv6=ipv6)
if resolved and cache:
domain_state[domain] = resolved
elif not resolved:
if domain not in domain_state:
continue
resolved = domain_state[domain]
ip_list = ip_list | resolved
return ip_list
def nft_output(table, set_name, ip_list):
output = [f'flush set {table} {set_name}']
if ip_list:
ip_str = ','.join(ip_list)
output.append(f'add element {table} {set_name} {{ {ip_str} }}')
return output
def nft_valid_sets():
try:
valid_sets = []
sets_json = cmd('nft -j list sets')
sets_obj = json.loads(sets_json)
for obj in sets_obj['nftables']:
if 'set' in obj:
family = obj['set']['family']
table = obj['set']['table']
name = obj['set']['name']
valid_sets.append((f'{family} {table}', name))
return valid_sets
except:
return []
def update(firewall):
conf_lines = []
count = 0
valid_sets = nft_valid_sets()
domain_groups = dict_search_args(firewall, 'group', 'domain_group')
if domain_groups:
for set_name, domain_config in domain_groups.items():
if 'address' not in domain_config:
continue
nft_set_name = f'D_{set_name}'
domains = domain_config['address']
ip_list = resolve(domains, ipv6=False)
for table in ipv4_tables:
if (table, nft_set_name) in valid_sets:
conf_lines += nft_output(table, nft_set_name, ip_list)
ip6_list = resolve(domains, ipv6=True)
for table in ipv6_tables:
if (table, nft_set_name) in valid_sets:
conf_lines += nft_output(table, nft_set_name, ip6_list)
count += 1
for set_name, domain in firewall['ip_fqdn'].items():
table = 'ip vyos_filter'
nft_set_name = f'FQDN_{set_name}'
ip_list = resolve([domain], ipv6=False)
if (table, nft_set_name) in valid_sets:
conf_lines += nft_output(table, nft_set_name, ip_list)
count += 1
for set_name, domain in firewall['ip6_fqdn'].items():
table = 'ip6 vyos_filter'
nft_set_name = f'FQDN_{set_name}'
ip_list = resolve([domain], ipv6=True)
if (table, nft_set_name) in valid_sets:
conf_lines += nft_output(table, nft_set_name, ip_list)
count += 1
nft_conf_str = "\n".join(conf_lines) + "\n"
code = run(f'nft -f -', input=nft_conf_str)
print(f'Updated {count} sets - result: {code}')
if __name__ == '__main__':
print(f'VyOS domain resolver')
count = 1
while commit_in_progress():
if ( count % 60 == 0 ):
print(f'Commit still in progress after {count}s - waiting')
count += 1
time.sleep(1)
conf = ConfigTreeQuery()
firewall = get_config(conf)
print(f'interval: {timeout}s - cache: {cache}')
while True:
update(firewall)
time.sleep(timeout)
diff --git a/src/migration-scripts/policy/4-to-5 b/src/migration-scripts/policy/4-to-5
new file mode 100755
index 000000000..33c9e6ade
--- /dev/null
+++ b/src/migration-scripts/policy/4-to-5
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 .
+
+# T2199: Migrate interface policy nodes to policy route interface
+
+import re
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+from vyos.ifconfig import Section
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base4 = ['policy', 'route']
+base6 = ['policy', 'route6']
+config = ConfigTree(config_file)
+
+if not config.exists(base4) and not config.exists(base6):
+ # Nothing to do
+ exit(0)
+
+def migrate_interface(config, iftype, ifname, vif=None, vifs=None, vifc=None):
+ if_path = ['interfaces', iftype, ifname]
+ ifname_full = ifname
+
+ if vif:
+ if_path += ['vif', vif]
+ ifname_full = f'{ifname}.{vif}'
+ elif vifs:
+ if_path += ['vif-s', vifs]
+ ifname_full = f'{ifname}.{vifs}'
+ if vifc:
+ if_path += ['vif-c', vifc]
+ ifname_full = f'{ifname}.{vifs}.{vifc}'
+
+ if not config.exists(if_path + ['policy']):
+ return
+
+ if config.exists(if_path + ['policy', 'route']):
+ route_name = config.return_value(if_path + ['policy', 'route'])
+ config.set(base4 + [route_name, 'interface'], value=ifname_full, replace=False)
+
+ if config.exists(if_path + ['policy', 'route6']):
+ route_name = config.return_value(if_path + ['policy', 'route6'])
+ config.set(base6 + [route_name, 'interface'], value=ifname_full, replace=False)
+
+ config.delete(if_path + ['policy'])
+
+for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ migrate_interface(config, iftype, ifname)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ migrate_interface(config, iftype, ifname, vif=vif)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ migrate_interface(config, iftype, ifname, vifs=vifs)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ migrate_interface(config, iftype, ifname, vifs=vifs, vifc=vifc)
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/op_mode/policy_route.py b/src/op_mode/policy_route.py
index 5be40082f..5953786f3 100755
--- a/src/op_mode/policy_route.py
+++ b/src/op_mode/policy_route.py
@@ -1,189 +1,149 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021 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 .
import argparse
import re
import tabulate
from vyos.config import Config
from vyos.util import cmd
from vyos.util import dict_search_args
-def get_policy_interfaces(conf, policy, name=None, ipv6=False):
- interfaces = conf.get_config_dict(['interfaces'], key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
- routes = ['route', 'route6']
-
- def parse_if(ifname, if_conf):
- if 'policy' in if_conf:
- for route in routes:
- if route in if_conf['policy']:
- route_name = if_conf['policy'][route]
- name_str = f'({ifname},{route})'
-
- if not name:
- policy[route][route_name]['interface'].append(name_str)
- elif not ipv6 and name == route_name:
- policy['interface'].append(name_str)
-
- for iftype in ['vif', 'vif_s', 'vif_c']:
- if iftype in if_conf:
- for vifname, vif_conf in if_conf[iftype].items():
- parse_if(f'{ifname}.{vifname}', vif_conf)
-
- for iftype, iftype_conf in interfaces.items():
- for ifname, if_conf in iftype_conf.items():
- parse_if(ifname, if_conf)
-
-def get_config_policy(conf, name=None, ipv6=False, interfaces=True):
+def get_config_policy(conf, name=None, ipv6=False):
config_path = ['policy']
if name:
config_path += ['route6' if ipv6 else 'route', name]
policy = conf.get_config_dict(config_path, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
- if policy and interfaces:
- if name:
- policy['interface'] = []
- else:
- if 'route' in policy:
- for route_name, route_conf in policy['route'].items():
- route_conf['interface'] = []
-
- if 'route6' in policy:
- for route_name, route_conf in policy['route6'].items():
- route_conf['interface'] = []
-
- get_policy_interfaces(conf, policy, name, ipv6)
return policy
def get_nftables_details(name, ipv6=False):
suffix = '6' if ipv6 else ''
command = f'sudo nft list chain ip{suffix} mangle VYOS_PBR{suffix}_{name}'
try:
results = cmd(command)
except:
return {}
out = {}
for line in results.split('\n'):
comment_search = re.search(rf'{name}[\- ](\d+|default-action)', line)
if not comment_search:
continue
rule = {}
rule_id = comment_search[1]
counter_search = re.search(r'counter packets (\d+) bytes (\d+)', line)
if counter_search:
rule['packets'] = counter_search[1]
rule['bytes'] = counter_search[2]
rule['conditions'] = re.sub(r'(\b(counter packets \d+ bytes \d+|drop|reject|return|log)\b|comment "[\w\-]+")', '', line).strip()
out[rule_id] = rule
return out
def output_policy_route(name, route_conf, ipv6=False, single_rule_id=None):
ip_str = 'IPv6' if ipv6 else 'IPv4'
print(f'\n---------------------------------\n{ip_str} Policy Route "{name}"\n')
if route_conf['interface']:
print('Active on: {0}\n'.format(" ".join(route_conf['interface'])))
details = get_nftables_details(name, ipv6)
rows = []
if 'rule' in route_conf:
for rule_id, rule_conf in route_conf['rule'].items():
if single_rule_id and rule_id != single_rule_id:
continue
if 'disable' in rule_conf:
continue
action = rule_conf['action'] if 'action' in rule_conf else 'set'
protocol = rule_conf['protocol'] if 'protocol' in rule_conf else 'all'
row = [rule_id, action, protocol]
if rule_id in details:
rule_details = details[rule_id]
row.append(rule_details.get('packets', 0))
row.append(rule_details.get('bytes', 0))
row.append(rule_details['conditions'])
rows.append(row)
if 'default_action' in route_conf and not single_rule_id:
row = ['default', route_conf['default_action'], 'all']
if 'default-action' in details:
rule_details = details['default-action']
row.append(rule_details.get('packets', 0))
row.append(rule_details.get('bytes', 0))
rows.append(row)
if rows:
header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
print(tabulate.tabulate(rows, header) + '\n')
def show_policy(ipv6=False):
print('Ruleset Information')
conf = Config()
policy = get_config_policy(conf)
if not policy:
return
if not ipv6 and 'route' in policy:
for route, route_conf in policy['route'].items():
output_policy_route(route, route_conf, ipv6=False)
if ipv6 and 'route6' in policy:
for route, route_conf in policy['route6'].items():
output_policy_route(route, route_conf, ipv6=True)
def show_policy_name(name, ipv6=False):
print('Ruleset Information')
conf = Config()
policy = get_config_policy(conf, name, ipv6)
if policy:
output_policy_route(name, policy, ipv6)
def show_policy_rule(name, rule_id, ipv6=False):
print('Rule Information')
conf = Config()
policy = get_config_policy(conf, name, ipv6)
if policy:
output_policy_route(name, policy, ipv6, rule_id)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--action', help='Action', required=False)
parser.add_argument('--name', help='Policy name', required=False, action='store', nargs='?', default='')
parser.add_argument('--rule', help='Policy Rule ID', required=False)
parser.add_argument('--ipv6', help='IPv6 toggle', action='store_true')
args = parser.parse_args()
if args.action == 'show':
if not args.rule:
show_policy_name(args.name, args.ipv6)
else:
show_policy_rule(args.name, args.rule, args.ipv6)
elif args.action == 'show_all':
show_policy(args.ipv6)