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)