diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 2ebce79e5..3bce69fc4 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -1,942 +1,946 @@
 <?xml version="1.0"?>
 <interfaceDefinition>
   <node name="firewall" owner="${vyos_conf_scripts_dir}/firewall.py">
     <properties>
       <priority>199</priority>
       <help>Firewall</help>
     </properties>
     <children>
       <leafNode name="all-ping">
         <properties>
           <help>Policy for handling of all IPv4 ICMP echo requests</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable processing of all IPv4 ICMP echo requests</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable processing of all IPv4 ICMP echo requests</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>enable</defaultValue>
       </leafNode>
       <leafNode name="broadcast-ping">
         <properties>
           <help>Policy for handling broadcast IPv4 ICMP echo and timestamp requests</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable processing of broadcast IPv4 ICMP echo/timestamp requests</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable processing of broadcast IPv4 ICMP echo/timestamp requests</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <leafNode name="config-trap">
         <properties>
           <help>SNMP trap generation on firewall configuration changes</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable sending SNMP trap on firewall configuration change</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable sending SNMP trap on firewall configuration change</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <node name="group">
         <properties>
           <help>Firewall group</help>
         </properties>
         <children>
           <tagNode name="address-group">
             <properties>
               <help>Firewall address-group</help>
               <constraint>
                 <regex>[a-zA-Z0-9][\w\-\.]*</regex>
               </constraint>
             </properties>
             <children>
               <leafNode name="address">
                 <properties>
                   <help>Address-group member</help>
                   <valueHelp>
                     <format>ipv4</format>
                     <description>IPv4 address to match</description>
                   </valueHelp>
                   <valueHelp>
                     <format>ipv4range</format>
                     <description>IPv4 range to match (e.g. 10.0.0.1-10.0.0.200)</description>
                   </valueHelp>
                   <constraint>
                     <validator name="ipv4-address"/>
                     <validator name="ipv4-range"/>
                   </constraint>
                   <multi/>
                 </properties>
               </leafNode>
               <leafNode name="include">
                 <properties>
                   <help>Include another address-group</help>
                   <completionHelp>
                     <path>firewall group address-group</path>
                   </completionHelp>
                   <multi/>
                 </properties>
               </leafNode>
               #include <include/generic-description.xml.i>
             </children>
           </tagNode>
           <tagNode name="domain-group">
             <properties>
               <help>Firewall domain-group</help>
               <constraint>
                 <regex>[a-zA-Z_][a-zA-Z0-9][\w\-\.]*</regex>
               </constraint>
               <constraintErrorMessage>Name of domain-group can only contain alpha-numeric letters, hyphen, underscores and not start with numeric</constraintErrorMessage>
             </properties>
             <children>
               <leafNode name="address">
                 <properties>
                   <help>Domain-group member</help>
                   <valueHelp>
                     <format>txt</format>
                     <description>Domain address to match</description>
                   </valueHelp>
                   <constraint>
                     <validator name="fqdn"/>
                   </constraint>
                   <multi/>
                 </properties>
               </leafNode>
               #include <include/generic-description.xml.i>
             </children>
           </tagNode>
           <tagNode name="ipv6-address-group">
             <properties>
               <help>Firewall ipv6-address-group</help>
               <constraint>
                 <regex>[a-zA-Z0-9][\w\-\.]*</regex>
               </constraint>
             </properties>
             <children>
               <leafNode name="address">
                 <properties>
                   <help>Address-group member</help>
                   <valueHelp>
                     <format>ipv6</format>
                     <description>IPv6 address to match</description>
                   </valueHelp>
                   <valueHelp>
                     <format>ipv6range</format>
                     <description>IPv6 range to match (e.g. 2002::1-2002::ff)</description>
                   </valueHelp>
                   <constraint>
                     <validator name="ipv6-address"/>
                     <validator name="ipv6-range"/>
                   </constraint>
                   <multi/>
                 </properties>
               </leafNode>
               <leafNode name="include">
                 <properties>
                   <help>Include another ipv6-address-group</help>
                   <completionHelp>
                     <path>firewall group ipv6-address-group</path>
                   </completionHelp>
                   <multi/>
                 </properties>
               </leafNode>
               #include <include/generic-description.xml.i>
             </children>
           </tagNode>
           <tagNode name="ipv6-network-group">
             <properties>
               <help>Firewall ipv6-network-group</help>
               <constraint>
                 <regex>[a-zA-Z0-9][\w\-\.]*</regex>
               </constraint>
             </properties>
             <children>
               #include <include/generic-description.xml.i>
               <leafNode name="network">
                 <properties>
                   <help>Network-group member</help>
                   <valueHelp>
                     <format>ipv6net</format>
                     <description>IPv6 address to match</description>
                   </valueHelp>
                   <constraint>
                     <validator name="ipv6-prefix"/>
                   </constraint>
                   <multi/>
                 </properties>
               </leafNode>
               <leafNode name="include">
                 <properties>
                   <help>Include another ipv6-network-group</help>
                   <completionHelp>
                     <path>firewall group ipv6-network-group</path>
                   </completionHelp>
                   <multi/>
                 </properties>
               </leafNode>
             </children>
           </tagNode>
           <tagNode name="mac-group">
             <properties>
               <help>Firewall mac-group</help>
               <constraint>
                 <regex>[a-zA-Z0-9][\w\-\.]*</regex>
               </constraint>
             </properties>
             <children>
               #include <include/generic-description.xml.i>
               <leafNode name="mac-address">
                 <properties>
                   <help>Mac-group member</help>
                   <valueHelp>
                     <format>macaddr</format>
                     <description>MAC address to match</description>
                   </valueHelp>
                   <constraint>
                     <validator name="mac-address"/>
                   </constraint>
                   <multi/>
                 </properties>
               </leafNode>
               <leafNode name="include">
                 <properties>
                   <help>Include another mac-group</help>
                   <completionHelp>
                     <path>firewall group mac-group</path>
                   </completionHelp>
                   <multi/>
                 </properties>
               </leafNode>
             </children>
           </tagNode>
           <tagNode name="network-group">
             <properties>
               <help>Firewall network-group</help>
               <constraint>
                 <regex>[a-zA-Z0-9][\w\-\.]*</regex>
               </constraint>
             </properties>
             <children>
               #include <include/generic-description.xml.i>
               <leafNode name="network">
                 <properties>
                   <help>Network-group member</help>
                   <valueHelp>
                     <format>ipv4net</format>
                     <description>IPv4 Subnet to match</description>
                   </valueHelp>
                   <constraint>
                     <validator name="ipv4-prefix"/>
                   </constraint>
                   <multi/>
                 </properties>
               </leafNode>
               <leafNode name="include">
                 <properties>
                   <help>Include another network-group</help>
                   <completionHelp>
                     <path>firewall group network-group</path>
                   </completionHelp>
                   <multi/>
                 </properties>
               </leafNode>
             </children>
           </tagNode>
           <tagNode name="port-group">
             <properties>
               <help>Firewall port-group</help>
               <constraint>
                 <regex>[a-zA-Z0-9][\w\-\.]*</regex>
               </constraint>
             </properties>
             <children>
               #include <include/generic-description.xml.i>
               <leafNode name="port">
                 <properties>
                   <help>Port-group member</help>
                   <valueHelp>
                     <format>txt</format>
                     <description>Named port (any name in /etc/services, e.g., http)</description>
                   </valueHelp>
                   <valueHelp>
                     <format>u32:1-65535</format>
                     <description>Numbered port</description>
                   </valueHelp>
                   <valueHelp>
                     <format>start-end</format>
                     <description>Numbered port range (e.g. 1001-1050)</description>
                   </valueHelp>
                   <multi/>
                   <constraint>
                     <validator name="port-range"/>
                   </constraint>
                 </properties>
               </leafNode>
               <leafNode name="include">
                 <properties>
                   <help>Include another port-group</help>
                   <completionHelp>
                     <path>firewall group port-group</path>
                   </completionHelp>
                   <multi/>
                 </properties>
               </leafNode>
             </children>
           </tagNode>
         </children>
       </node>
       <tagNode name="interface">
         <properties>
           <help>Interface name to apply firewall configuration</help>
           <completionHelp>
             <script>${vyos_completion_dir}/list_interfaces.py</script>
           </completionHelp>
         </properties>
         <children>
           <node name="in">
             <properties>
               <help>Forwarded packets on inbound interface</help>
             </properties>
             <children>
               #include <include/firewall/name.xml.i>
             </children>
           </node>
           <node name="out">
             <properties>
               <help>Forwarded packets on outbound interface</help>
             </properties>
             <children>
               #include <include/firewall/name.xml.i>
             </children>
           </node>
           <node name="local">
             <properties>
               <help>Packets destined for this router</help>
             </properties>
             <children>
               #include <include/firewall/name.xml.i>
             </children>
           </node>
         </children>
       </tagNode>
       <leafNode name="ip-src-route">
         <properties>
           <help>Policy for handling IPv4 packets with source route option</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable processing of IPv4 packets with source route option</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable processing of IPv4 packets with source route option</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <tagNode name="ipv6-name">
         <properties>
           <help>IPv6 firewall rule-set name</help>
           <constraint>
             <regex>[a-zA-Z0-9][\w\-\.]*</regex>
           </constraint>
         </properties>
         <children>
           #include <include/firewall/default-action.xml.i>
           #include <include/firewall/enable-default-log.xml.i>
           #include <include/generic-description.xml.i>
           <leafNode name="default-jump-target">
             <properties>
               <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
               <completionHelp>
                 <path>firewall ipv6-name</path>
               </completionHelp>
             </properties>
           </leafNode>
           <tagNode name="rule">
             <properties>
               <help>Firewall rule number (IPv6)</help>
               <valueHelp>
                 <format>u32:1-999999</format>
                 <description>Number for this Firewall rule</description>
               </valueHelp>
               <constraint>
                 <validator name="numeric" argument="--range 1-999999"/>
               </constraint>
               <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
             </properties>
             <children>
               #include <include/firewall/action.xml.i>
               #include <include/generic-description.xml.i>
               <node name="destination">
                 <properties>
                   <help>Destination parameters</help>
                 </properties>
                 <children>
                   #include <include/firewall/address-ipv6.xml.i>
                   #include <include/firewall/fqdn.xml.i>
                   #include <include/firewall/geoip.xml.i>
                   #include <include/firewall/source-destination-group-ipv6.xml.i>
                   #include <include/firewall/port.xml.i>
+                  #include <include/firewall/address-mask-ipv6.xml.i>
                 </children>
               </node>
               <node name="source">
                 <properties>
                   <help>Source parameters</help>
                 </properties>
                 <children>
                   #include <include/firewall/address-ipv6.xml.i>
                   #include <include/firewall/fqdn.xml.i>
                   #include <include/firewall/geoip.xml.i>
                   #include <include/firewall/source-destination-group-ipv6.xml.i>
                   #include <include/firewall/port.xml.i>
+                  #include <include/firewall/address-mask-ipv6.xml.i>
                 </children>
               </node>
               #include <include/firewall/common-rule.xml.i>
               #include <include/firewall/dscp.xml.i>
               #include <include/firewall/packet-length.xml.i>
               #include <include/firewall/hop-limit.xml.i>
               <node name="icmpv6">
                 <properties>
                   <help>ICMPv6 type and code information</help>
                 </properties>
                 <children>
                   <leafNode name="code">
                     <properties>
                       <help>ICMPv6 code</help>
                       <valueHelp>
                         <format>u32:0-255</format>
                         <description>ICMPv6 code (0-255)</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 0-255"/>
                       </constraint>
                     </properties>
                   </leafNode>
                   <leafNode name="type">
                     <properties>
                       <help>ICMPv6 type</help>
                       <valueHelp>
                         <format>u32:0-255</format>
                         <description>ICMPv6 type (0-255)</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 0-255"/>
                       </constraint>
                     </properties>
                   </leafNode>
                   #include <include/firewall/icmpv6-type-name.xml.i>
                 </children>
               </node>
               <leafNode name="jump-target">
                 <properties>
                   <help>Set jump target. Action jump must be defined to use this setting</help>
                   <completionHelp>
                     <path>firewall ipv6-name</path>
                   </completionHelp>
                 </properties>
               </leafNode>
             </children>
           </tagNode>
         </children>
       </tagNode>
       <leafNode name="ipv6-receive-redirects">
         <properties>
           <help>Policy for handling received ICMPv6 redirect messages</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable processing of received ICMPv6 redirect messages</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable processing of received ICMPv6 redirect messages</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <leafNode name="ipv6-src-route">
         <properties>
           <help>Policy for handling IPv6 packets with routing extension header</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable processing of IPv6 packets with routing header type 2</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable processing of IPv6 packets with routing header</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <leafNode name="log-martians">
         <properties>
           <help>Policy for logging IPv4 packets with invalid addresses</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable logging of IPv4 packets with invalid addresses</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable logging of Ipv4 packets with invalid addresses</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>enable</defaultValue>
       </leafNode>
       <tagNode name="name">
         <properties>
           <help>IPv4 firewall rule-set name</help>
           <constraint>
             <regex>[a-zA-Z0-9][\w\-\.]*</regex>
           </constraint>
         </properties>
         <children>
           #include <include/firewall/default-action.xml.i>
           #include <include/firewall/enable-default-log.xml.i>
           #include <include/generic-description.xml.i>
           <leafNode name="default-jump-target">
             <properties>
               <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
               <completionHelp>
                 <path>firewall name</path>
               </completionHelp>
             </properties>
           </leafNode>
           <tagNode name="rule">
             <properties>
               <help>Firewall rule number (IPv4)</help>
               <valueHelp>
                 <format>u32:1-999999</format>
                 <description>Number for this Firewall rule</description>
               </valueHelp>
               <constraint>
                 <validator name="numeric" argument="--range 1-999999"/>
               </constraint>
               <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
             </properties>
             <children>
               #include <include/firewall/action.xml.i>
               #include <include/generic-description.xml.i>
               <node name="destination">
                 <properties>
                   <help>Destination parameters</help>
                 </properties>
                 <children>
                   #include <include/firewall/address.xml.i>
                   #include <include/firewall/fqdn.xml.i>
                   #include <include/firewall/geoip.xml.i>
                   #include <include/firewall/source-destination-group.xml.i>
                   #include <include/firewall/port.xml.i>
+                  #include <include/firewall/address-mask.xml.i>
                 </children>
               </node>
               <node name="source">
                 <properties>
                   <help>Source parameters</help>
                 </properties>
                 <children>
                   #include <include/firewall/address.xml.i>
                   #include <include/firewall/fqdn.xml.i>
                   #include <include/firewall/geoip.xml.i>
                   #include <include/firewall/source-destination-group.xml.i>
                   #include <include/firewall/port.xml.i>
+                  #include <include/firewall/address-mask.xml.i>
                 </children>
               </node>
               #include <include/firewall/common-rule.xml.i>
               #include <include/firewall/dscp.xml.i>
               #include <include/firewall/packet-length.xml.i>
               <node name="icmp">
                 <properties>
                   <help>ICMP type and code information</help>
                 </properties>
                 <children>
                   <leafNode name="code">
                     <properties>
                       <help>ICMP code</help>
                       <valueHelp>
                         <format>u32:0-255</format>
                         <description>ICMP code (0-255)</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 0-255"/>
                       </constraint>
                     </properties>
                   </leafNode>
                   <leafNode name="type">
                     <properties>
                       <help>ICMP type</help>
                       <valueHelp>
                         <format>u32:0-255</format>
                         <description>ICMP type (0-255)</description>
                       </valueHelp>
                       <constraint>
                         <validator name="numeric" argument="--range 0-255"/>
                       </constraint>
                     </properties>
                   </leafNode>
                   #include <include/firewall/icmp-type-name.xml.i>
                 </children>
               </node>
               <leafNode name="jump-target">
                 <properties>
                   <help>Set jump target. Action jump must be defined to use this setting</help>
                   <completionHelp>
                     <path>firewall name</path>
                   </completionHelp>
                 </properties>
               </leafNode>
               #include <include/firewall/ttl.xml.i>
             </children>
           </tagNode>
         </children>
       </tagNode>
       <leafNode name="receive-redirects">
         <properties>
           <help>Policy for handling received IPv4 ICMP redirect messages</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable processing of received IPv4 ICMP redirect messages</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable processing of received IPv4 ICMP redirect messages</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <leafNode name="resolver-cache">
         <properties>
           <help>Retains last successful value if domain resolution fails</help>
           <valueless/>
         </properties>
       </leafNode>
       <leafNode name="resolver-interval">
         <properties>
           <help>Domain resolver update interval</help>
           <valueHelp>
             <format>u32:10-3600</format>
             <description>Interval (seconds)</description>
           </valueHelp>
           <constraint>
             <validator name="numeric" argument="--range 10-3600"/>
           </constraint>
         </properties>
         <defaultValue>300</defaultValue>
       </leafNode>
       <leafNode name="send-redirects">
         <properties>
           <help>Policy for sending IPv4 ICMP redirect messages</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable sending IPv4 ICMP redirect messages</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable sending IPv4 ICMP redirect messages</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>enable</defaultValue>
       </leafNode>
       <leafNode name="source-validation">
         <properties>
           <help>Policy for source validation by reversed path, as specified in RFC3704</help>
           <completionHelp>
             <list>strict loose disable</list>
           </completionHelp>
           <valueHelp>
             <format>strict</format>
             <description>Enable Strict Reverse Path Forwarding as defined in RFC3704</description>
           </valueHelp>
           <valueHelp>
             <format>loose</format>
             <description>Enable Loose Reverse Path Forwarding as defined in RFC3704</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>No source validation</description>
           </valueHelp>
           <constraint>
             <regex>(strict|loose|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <node name="state-policy">
         <properties>
           <help>Global firewall state-policy</help>
         </properties>
         <children>
           <node name="established">
             <properties>
               <help>Global firewall policy for packets part of an established connection</help>
             </properties>
             <children>
               #include <include/firewall/action-accept-drop-reject.xml.i>
               #include <include/firewall/log.xml.i>
               #include <include/firewall/rule-log-level.xml.i>
             </children>
           </node>
           <node name="invalid">
             <properties>
               <help>Global firewall policy for packets part of an invalid connection</help>
             </properties>
             <children>
               #include <include/firewall/action-accept-drop-reject.xml.i>
               #include <include/firewall/log.xml.i>
               #include <include/firewall/rule-log-level.xml.i>
             </children>
           </node>
           <node name="related">
             <properties>
               <help>Global firewall policy for packets part of a related connection</help>
             </properties>
             <children>
               #include <include/firewall/action-accept-drop-reject.xml.i>
               #include <include/firewall/log.xml.i>
               #include <include/firewall/rule-log-level.xml.i>
             </children>
           </node>
         </children>
       </node>
       <leafNode name="syn-cookies">
         <properties>
           <help>Policy for using TCP SYN cookies with IPv4</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable use of TCP SYN cookies with IPv4</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable use of TCP SYN cookies with IPv4</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>enable</defaultValue>
       </leafNode>
       <leafNode name="twa-hazards-protection">
         <properties>
           <help>RFC1337 TCP TIME-WAIT assasination hazards protection</help>
           <completionHelp>
             <list>enable disable</list>
           </completionHelp>
           <valueHelp>
             <format>enable</format>
             <description>Enable RFC1337 TIME-WAIT hazards protection</description>
           </valueHelp>
           <valueHelp>
             <format>disable</format>
             <description>Disable RFC1337 TIME-WAIT hazards protection</description>
           </valueHelp>
           <constraint>
             <regex>(enable|disable)</regex>
           </constraint>
         </properties>
         <defaultValue>disable</defaultValue>
       </leafNode>
       <tagNode name="zone">
         <properties>
           <help>Zone-policy</help>
           <valueHelp>
             <format>txt</format>
             <description>Zone name</description>
           </valueHelp>
           <constraint>
             <regex>[a-zA-Z0-9][\w\-\.]*</regex>
           </constraint>
         </properties>
         <children>
           #include <include/generic-description.xml.i>
           #include <include/firewall/enable-default-log.xml.i>
           <leafNode name="default-action">
             <properties>
               <help>Default-action for traffic coming into this zone</help>
               <completionHelp>
                 <list>drop reject</list>
               </completionHelp>
               <valueHelp>
                 <format>drop</format>
                 <description>Drop silently</description>
               </valueHelp>
               <valueHelp>
                 <format>reject</format>
                 <description>Drop and notify source</description>
               </valueHelp>
               <constraint>
                 <regex>(drop|reject)</regex>
               </constraint>
             </properties>
             <defaultValue>drop</defaultValue>
           </leafNode>
           <tagNode name="from">
             <properties>
               <help>Zone from which to filter traffic</help>
               <completionHelp>
                 <path>zone-policy zone</path>
               </completionHelp>
             </properties>
             <children>
               <node name="firewall">
                 <properties>
                   <help>Firewall options</help>
                 </properties>
                 <children>
                   <leafNode name="ipv6-name">
                     <properties>
                       <help>IPv6 firewall ruleset</help>
                       <completionHelp>
                         <path>firewall ipv6-name</path>
                       </completionHelp>
                     </properties>
                   </leafNode>
                   <leafNode name="name">
                     <properties>
                       <help>IPv4 firewall ruleset</help>
                       <completionHelp>
                         <path>firewall name</path>
                       </completionHelp>
                     </properties>
                   </leafNode>
                 </children>
               </node>
             </children>
           </tagNode>
           <leafNode name="interface">
             <properties>
               <help>Interface associated with zone</help>
               <valueHelp>
                 <format>txt</format>
                 <description>Interface associated with zone</description>
               </valueHelp>
               <completionHelp>
                 <script>${vyos_completion_dir}/list_interfaces.py</script>
               </completionHelp>
               <multi/>
             </properties>
           </leafNode>
           <node name="intra-zone-filtering">
             <properties>
               <help>Intra-zone filtering</help>
             </properties>
             <children>
               <leafNode name="action">
                 <properties>
                   <help>Action for intra-zone traffic</help>
                   <completionHelp>
                     <list>accept drop</list>
                   </completionHelp>
                   <valueHelp>
                     <format>accept</format>
                     <description>Accept traffic</description>
                   </valueHelp>
                   <valueHelp>
                     <format>drop</format>
                     <description>Drop silently</description>
                   </valueHelp>
                   <constraint>
                     <regex>(accept|drop)</regex>
                   </constraint>
                 </properties>
               </leafNode>
               <node name="firewall">
                 <properties>
                   <help>Use the specified firewall chain</help>
                 </properties>
                 <children>
                   <leafNode name="ipv6-name">
                     <properties>
                       <help>IPv6 firewall ruleset</help>
                       <completionHelp>
                         <path>firewall ipv6-name</path>
                       </completionHelp>
                     </properties>
                   </leafNode>
                   <leafNode name="name">
                     <properties>
                       <help>IPv4 firewall ruleset</help>
                       <completionHelp>
                         <path>firewall name</path>
                       </completionHelp>
                     </properties>
                   </leafNode>
                 </children>
               </node>
             </children>
           </node>
           <leafNode name="local-zone">
             <properties>
               <help>Zone to be local-zone</help>
               <valueless/>
             </properties>
           </leafNode>
         </children>
       </tagNode>
     </children>
   </node>
 </interfaceDefinition>
diff --git a/interface-definitions/include/firewall/address-mask-ipv6.xml.i b/interface-definitions/include/firewall/address-mask-ipv6.xml.i
new file mode 100644
index 000000000..8c0483209
--- /dev/null
+++ b/interface-definitions/include/firewall/address-mask-ipv6.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from firewall/address-mask-ipv6.xml.i -->
+<leafNode name="address-mask">
+  <properties>
+    <help>IP mask</help>
+    <valueHelp>
+      <format>ipv6</format>
+      <description>IP mask to apply</description>
+    </valueHelp>
+    <constraint>
+      <validator name="ipv6"/>
+    </constraint>
+  </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/address-mask.xml.i b/interface-definitions/include/firewall/address-mask.xml.i
new file mode 100644
index 000000000..7f6f17d1e
--- /dev/null
+++ b/interface-definitions/include/firewall/address-mask.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from firewall/address-mask.xml.i -->
+<leafNode name="address-mask">
+  <properties>
+    <help>IP mask</help>
+    <valueHelp>
+      <format>ipv4</format>
+      <description>IPv4 mask to apply</description>
+    </valueHelp>
+    <constraint>
+      <validator name="ipv4-address"/>
+    </constraint>
+  </properties>
+</leafNode>
+<!-- include end -->
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 59ec4948f..48263eef5 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -1,482 +1,493 @@
 #!/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 <http://www.gnu.org/licenses/>.
 
 import csv
 import gzip
 import os
 import re
 
 from pathlib import Path
 from socket import AF_INET
 from socket import AF_INET6
 from socket import getaddrinfo
 from time import strftime
 
 from vyos.remote import download
 from vyos.template import is_ipv4
 from vyos.template import render
 from vyos.util import call
 from vyos.util import cmd
 from vyos.util import dict_search_args
 from vyos.util import dict_search_recursive
 from vyos.util import run
 
 # Domain Resolver
 
 def fqdn_config_parse(firewall):
     firewall['ip_fqdn'] = {}
     firewall['ip6_fqdn'] = {}
 
     for domain, path in dict_search_recursive(firewall, 'fqdn'):
         fw_name = path[1] # name/ipv6-name
         rule = path[3] # rule id
         suffix = path[4][0] # source/destination (1 char)
         set_name = f'{fw_name}_{rule}_{suffix}'
             
         if path[0] == 'name':
             firewall['ip_fqdn'][set_name] = domain
         elif path[0] == 'ipv6_name':
             firewall['ip6_fqdn'][set_name] = domain
 
 def fqdn_resolve(fqdn, ipv6=False):
     try:
         res = getaddrinfo(fqdn, None, AF_INET6 if ipv6 else AF_INET)
         return set(item[4][0] for item in res)
     except:
         return None
 
 # End Domain Resolver
 
 def find_nftables_rule(table, chain, rule_matches=[]):
     # Find rule in table/chain that matches all criteria and return the handle
     results = cmd(f'sudo nft -a list chain {table} {chain}').split("\n")
     for line in results:
         if all(rule_match in line for rule_match in rule_matches):
             handle_search = re.search('handle (\d+)', line)
             if handle_search:
                 return handle_search[1]
     return None
 
 def remove_nftables_rule(table, chain, handle):
     cmd(f'sudo nft delete rule {table} {chain} handle {handle}')
 
 # Functions below used by template generation
 
 def nft_action(vyos_action):
     if vyos_action == 'accept':
         return 'return'
     return vyos_action
 
 def parse_rule(rule_conf, fw_name, rule_id, ip_name):
     output = []
     def_suffix = '6' if ip_name == 'ip6' else ''
 
     if 'state' in rule_conf and rule_conf['state']:
         states = ",".join([s for s, v in rule_conf['state'].items() if v == 'enable'])
 
         if states:
             output.append(f'ct state {{{states}}}')
 
     if 'connection_status' in rule_conf and rule_conf['connection_status']:
         status = rule_conf['connection_status']
         if status['nat'] == 'destination':
             nat_status = '{dnat}'
             output.append(f'ct status {nat_status}')
         if status['nat'] == 'source':
             nat_status = '{snat}'
             output.append(f'ct status {nat_status}')
 
     if 'protocol' in rule_conf and rule_conf['protocol'] != 'all':
         proto = rule_conf['protocol']
         operator = ''
         if proto[0] == '!':
             operator = '!='
             proto = proto[1:]
         if proto == 'tcp_udp':
             proto = '{tcp, udp}'
         output.append(f'meta l4proto {operator} {proto}')
 
     for side in ['destination', 'source']:
         if side in rule_conf:
             prefix = side[0]
             side_conf = rule_conf[side]
+            address_mask = side_conf.get('address_mask', None)
 
             if 'address' in side_conf:
                 suffix = side_conf['address']
-                if suffix[0] == '!':
-                    suffix = f'!= {suffix[1:]}'
-                output.append(f'{ip_name} {prefix}addr {suffix}')
+                operator = ''
+                exclude = suffix[0] == '!'
+                if exclude:
+                    operator = '!= '
+                    suffix = suffix[1:]
+                if address_mask:
+                    operator = '!=' if exclude else '=='
+                    operator = f'& {address_mask} {operator} '
+                output.append(f'{ip_name} {prefix}addr {operator}{suffix}')
 
             if 'fqdn' in side_conf:
                 fqdn = side_conf['fqdn']
                 operator = ''
                 if fqdn[0] == '!':
                     operator = '!='
                 output.append(f'{ip_name} {prefix}addr {operator} @FQDN_{fw_name}_{rule_id}_{prefix}')
 
             if dict_search_args(side_conf, 'geoip', 'country_code'):
                 operator = ''
                 if dict_search_args(side_conf, 'geoip', 'inverse_match') != None:
                     operator = '!='
                 output.append(f'{ip_name} {prefix}addr {operator} @GEOIP_CC_{fw_name}_{rule_id}')
 
             if 'mac_address' in side_conf:
                 suffix = side_conf["mac_address"]
                 if suffix[0] == '!':
                     suffix = f'!= {suffix[1:]}'
                 output.append(f'ether {prefix}addr {suffix}')
 
             if 'port' in side_conf:
                 proto = rule_conf['protocol']
                 port = side_conf['port'].split(',')
 
                 ports = []
                 negated_ports = []
 
                 for p in port:
                     if p[0] == '!':
                         negated_ports.append(p[1:])
                     else:
                         ports.append(p)
 
                 if proto == 'tcp_udp':
                     proto = 'th'
 
                 if ports:
                     ports_str = ','.join(ports)
                     output.append(f'{proto} {prefix}port {{{ports_str}}}')
 
                 if negated_ports:
                     negated_ports_str = ','.join(negated_ports)
                     output.append(f'{proto} {prefix}port != {{{negated_ports_str}}}')
 
             if 'group' in side_conf:
                 group = side_conf['group']
                 if 'address_group' in group:
                     group_name = group['address_group']
                     operator = ''
-                    if group_name[0] == '!':
+                    exclude = group_name[0] == "!"
+                    if exclude:
                         operator = '!='
                         group_name = group_name[1:]
+                    if address_mask:
+                        operator = '!=' if exclude else '=='
+                        operator = f'& {address_mask} {operator}'
                     output.append(f'{ip_name} {prefix}addr {operator} @A{def_suffix}_{group_name}')
                 # Generate firewall group domain-group
                 elif 'domain_group' in group:
                     group_name = group['domain_group']
                     operator = ''
                     if group_name[0] == '!':
                         operator = '!='
                         group_name = group_name[1:]
                     output.append(f'{ip_name} {prefix}addr {operator} @D_{group_name}')
                 elif 'network_group' in group:
                     group_name = group['network_group']
                     operator = ''
                     if group_name[0] == '!':
                         operator = '!='
                         group_name = group_name[1:]
                     output.append(f'{ip_name} {prefix}addr {operator} @N{def_suffix}_{group_name}')
                 if 'mac_group' in group:
                     group_name = group['mac_group']
                     operator = ''
                     if group_name[0] == '!':
                         operator = '!='
                         group_name = group_name[1:]
                     output.append(f'ether {prefix}addr {operator} @M_{group_name}')
                 if 'port_group' in group:
                     proto = rule_conf['protocol']
                     group_name = group['port_group']
 
                     if proto == 'tcp_udp':
                         proto = 'th'
 
                     operator = ''
                     if group_name[0] == '!':
                         operator = '!='
                         group_name = group_name[1:]
 
                     output.append(f'{proto} {prefix}port {operator} @P_{group_name}')
 
     if 'log' in rule_conf and rule_conf['log'] == 'enable':
         action = rule_conf['action'] if 'action' in rule_conf else 'accept'
         output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}]"')
 
         if 'log_level' in rule_conf:
             log_level = rule_conf['log_level']
             output.append(f'level {log_level}')
 
 
     if 'hop_limit' in rule_conf:
         operators = {'eq': '==', 'gt': '>', 'lt': '<'}
         for op, operator in operators.items():
             if op in rule_conf['hop_limit']:
                 value = rule_conf['hop_limit'][op]
                 output.append(f'ip6 hoplimit {operator} {value}')
 
     if 'inbound_interface' in rule_conf:
         iiface = rule_conf['inbound_interface']
         output.append(f'iifname {iiface}')
 
     if 'outbound_interface' in rule_conf:
         oiface = rule_conf['outbound_interface']
         output.append(f'oifname {oiface}')
 
     if 'ttl' in rule_conf:
         operators = {'eq': '==', 'gt': '>', 'lt': '<'}
         for op, operator in operators.items():
             if op in rule_conf['ttl']:
                 value = rule_conf['ttl'][op]
                 output.append(f'ip ttl {operator} {value}')
 
     for icmp in ['icmp', 'icmpv6']:
         if icmp in rule_conf:
             if 'type_name' in rule_conf[icmp]:
                 output.append(icmp + ' type ' + rule_conf[icmp]['type_name'])
             else:
                 if 'code' in rule_conf[icmp]:
                     output.append(icmp + ' code ' + rule_conf[icmp]['code'])
                 if 'type' in rule_conf[icmp]:
                     output.append(icmp + ' type ' + rule_conf[icmp]['type'])
 
 
     if 'packet_length' in rule_conf:
         lengths_str = ','.join(rule_conf['packet_length'])
         output.append(f'ip{def_suffix} length {{{lengths_str}}}')
 
     if 'packet_length_exclude' in rule_conf:
         negated_lengths_str = ','.join(rule_conf['packet_length_exclude'])
         output.append(f'ip{def_suffix} length != {{{negated_lengths_str}}}')
 
     if 'dscp' in rule_conf:
         dscp_str = ','.join(rule_conf['dscp'])
         output.append(f'ip{def_suffix} dscp {{{dscp_str}}}')
 
     if 'dscp_exclude' in rule_conf:
         negated_dscp_str = ','.join(rule_conf['dscp_exclude'])
         output.append(f'ip{def_suffix} dscp != {{{negated_dscp_str}}}')
 
     if 'ipsec' in rule_conf:
         if 'match_ipsec' in rule_conf['ipsec']:
             output.append('meta ipsec == 1')
         if 'match_non_ipsec' in rule_conf['ipsec']:
             output.append('meta ipsec == 0')
 
     if 'fragment' in rule_conf:
         # Checking for fragmentation after priority -400 is not possible,
         # so we use a priority -450 hook to set a mark
         if 'match_frag' in rule_conf['fragment']:
             output.append('meta mark 0xffff1')
         if 'match_non_frag' in rule_conf['fragment']:
             output.append('meta mark != 0xffff1')
 
     if 'limit' in rule_conf:
         if 'rate' in rule_conf['limit']:
             output.append(f'limit rate {rule_conf["limit"]["rate"]}')
             if 'burst' in rule_conf['limit']:
                 output.append(f'burst {rule_conf["limit"]["burst"]} packets')
 
     if 'recent' in rule_conf:
         count = rule_conf['recent']['count']
         time = rule_conf['recent']['time']
         output.append(f'add @RECENT{def_suffix}_{fw_name}_{rule_id} {{ {ip_name} saddr limit rate over {count}/{time} burst {count} packets }}')
 
     if 'time' in rule_conf:
         output.append(parse_time(rule_conf['time']))
 
     tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
     if tcp_flags:
         output.append(parse_tcp_flags(tcp_flags))
 
     # TCP MSS
     tcp_mss = dict_search_args(rule_conf, 'tcp', 'mss')
     if tcp_mss:
         output.append(f'tcp option maxseg size {tcp_mss}')
 
     output.append('counter')
 
     if 'set' in rule_conf:
         output.append(parse_policy_set(rule_conf['set'], def_suffix))
 
     if 'action' in rule_conf:
         output.append(nft_action(rule_conf['action']))
         if 'jump' in rule_conf['action']:
             target = rule_conf['jump_target']
             output.append(f'NAME{def_suffix}_{target}')
 
     else:
         output.append('return')
 
     output.append(f'comment "{fw_name}-{rule_id}"')
     return " ".join(output)
 
 def parse_tcp_flags(flags):
     include = [flag for flag in flags if flag != 'not']
     exclude = list(flags['not']) if 'not' in flags else []
     return f'tcp flags & ({"|".join(include + exclude)}) == {"|".join(include) if include else "0x0"}'
 
 def parse_time(time):
     out = []
     if 'startdate' in time:
         start = time['startdate']
         if 'T' not in start and 'starttime' in time:
             start += f' {time["starttime"]}'
         out.append(f'time >= "{start}"')
     if 'starttime' in time and 'startdate' not in time:
         out.append(f'hour >= "{time["starttime"]}"')
     if 'stopdate' in time:
         stop = time['stopdate']
         if 'T' not in stop and 'stoptime' in time:
             stop += f' {time["stoptime"]}'
         out.append(f'time < "{stop}"')
     if 'stoptime' in time and 'stopdate' not in time:
         out.append(f'hour < "{time["stoptime"]}"')
     if 'weekdays' in time:
         days = time['weekdays'].split(",")
         out_days = [f'"{day}"' for day in days if day[0] != '!']
         out.append(f'day {{{",".join(out_days)}}}')
     return " ".join(out)
 
 def parse_policy_set(set_conf, def_suffix):
     out = []
     if 'dscp' in set_conf:
         dscp = set_conf['dscp']
         out.append(f'ip{def_suffix} dscp set {dscp}')
     if 'mark' in set_conf:
         mark = set_conf['mark']
         out.append(f'meta mark set {mark}')
     if 'table' in set_conf:
         table = set_conf['table']
         if table == 'main':
             table = '254'
         mark = 0x7FFFFFFF - int(table)
         out.append(f'meta mark set {mark}')
     if 'tcp_mss' in set_conf:
         mss = set_conf['tcp_mss']
         out.append(f'tcp option maxseg size set {mss}')
     return " ".join(out)
 
 # GeoIP
 
 nftables_geoip_conf = '/run/nftables-geoip.conf'
 geoip_database = '/usr/share/vyos-geoip/dbip-country-lite.csv.gz'
 geoip_lock_file = '/run/vyos-geoip.lock'
 
 def geoip_load_data(codes=[]):
     data = None
 
     if not os.path.exists(geoip_database):
         return []
 
     try:
         with gzip.open(geoip_database, mode='rt') as csv_fh:
             reader = csv.reader(csv_fh)
             out = []
             for start, end, code in reader:
                 if code.lower() in codes:
                     out.append([start, end, code.lower()])
             return out
     except:
         print('Error: Failed to open GeoIP database')
     return []
 
 def geoip_download_data():
     url = 'https://download.db-ip.com/free/dbip-country-lite-{}.csv.gz'.format(strftime("%Y-%m"))
     try:
         dirname = os.path.dirname(geoip_database)
         if not os.path.exists(dirname):
             os.mkdir(dirname)
 
         download(geoip_database, url)
         print("Downloaded GeoIP database")
         return True
     except:
         print("Error: Failed to download GeoIP database")
     return False
 
 class GeoIPLock(object):
     def __init__(self, file):
         self.file = file
 
     def __enter__(self):
         if os.path.exists(self.file):
             return False
 
         Path(self.file).touch()
         return True
 
     def __exit__(self, exc_type, exc_value, tb):
         os.unlink(self.file)
 
 def geoip_update(firewall, force=False):
     with GeoIPLock(geoip_lock_file) as lock:
         if not lock:
             print("Script is already running")
             return False
 
         if not firewall:
             print("Firewall is not configured")
             return True
 
         if not os.path.exists(geoip_database):
             if not geoip_download_data():
                 return False
         elif force:
             geoip_download_data()
 
         ipv4_codes = {}
         ipv6_codes = {}
 
         ipv4_sets = {}
         ipv6_sets = {}
 
         # Map country codes to set names
         for codes, path in dict_search_recursive(firewall, 'country_code'):
             set_name = f'GEOIP_CC_{path[1]}_{path[3]}'
             if path[0] == 'name':
                 for code in codes:
                     ipv4_codes.setdefault(code, []).append(set_name)
             elif path[0] == 'ipv6_name':
                 for code in codes:
                     ipv6_codes.setdefault(code, []).append(set_name)
 
         if not ipv4_codes and not ipv6_codes:
             if force:
                 print("GeoIP not in use by firewall")
             return True
 
         geoip_data = geoip_load_data([*ipv4_codes, *ipv6_codes])
 
         # Iterate IP blocks to assign to sets
         for start, end, code in geoip_data:
             ipv4 = is_ipv4(start)
             if code in ipv4_codes and ipv4:
                 ip_range = f'{start}-{end}' if start != end else start
                 for setname in ipv4_codes[code]:
                     ipv4_sets.setdefault(setname, []).append(ip_range)
             if code in ipv6_codes and not ipv4:
                 ip_range = f'{start}-{end}' if start != end else start
                 for setname in ipv6_codes[code]:
                     ipv6_sets.setdefault(setname, []).append(ip_range)
 
         render(nftables_geoip_conf, 'firewall/nftables-geoip-update.j2', {
             'ipv4_sets': ipv4_sets,
             'ipv6_sets': ipv6_sets
         })
 
         result = run(f'nft -f {nftables_geoip_conf}')
         if result != 0:
             print('Error: GeoIP failed to update firewall')
             return False
 
         return True
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index e172e086d..09b520b72 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -1,471 +1,538 @@
 #!/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 <http://www.gnu.org/licenses/>.
 
 import unittest
 
 from glob import glob
 from time import sleep
 
 from base_vyostest_shim import VyOSUnitTestSHIM
 
 from vyos.configsession import ConfigSessionError
 from vyos.util import cmd
 from vyos.util import run
 
 sysfs_config = {
     'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'default': '0', 'test_value': 'disable'},
     'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'default': '1', 'test_value': 'enable'},
     'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route', 'default': '0', 'test_value': 'enable'},
     'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
     'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'default': '-1', 'test_value': 'enable'},
     'log_martians': {'sysfs': '/proc/sys/net/ipv4/conf/all/log_martians', 'default': '1', 'test_value': 'disable'},
     'receive_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
     'send_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/send_redirects', 'default': '1', 'test_value': 'disable'},
     'syn_cookies': {'sysfs': '/proc/sys/net/ipv4/tcp_syncookies', 'default': '1', 'test_value': 'disable'},
     'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337', 'default': '0', 'test_value': 'enable'}
 }
 
 class TestFirewall(VyOSUnitTestSHIM.TestCase):
     @classmethod
     def setUpClass(cls):
         super(TestFirewall, cls).setUpClass()
 
         # ensure we can also run this test on a live system - so lets clean
         # out the current configuration :)
         cls.cli_delete(cls, ['firewall'])
 
     @classmethod
     def tearDownClass(cls):
         super(TestFirewall, cls).tearDownClass()
 
     def tearDown(self):
         self.cli_delete(['firewall'])
         self.cli_commit()
 
         # Verify chains/sets are cleaned up from nftables
         nftables_search = [
             ['set M_smoketest_mac'],
             ['set N_smoketest_network'],
             ['set P_smoketest_port'],
             ['set D_smoketest_domain'],
             ['set RECENT_smoketest_4'],
             ['chain NAME_smoketest']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter', inverse=True)
 
     def verify_nftables(self, nftables_search, table, inverse=False, args=''):
         nftables_output = cmd(f'sudo nft {args} 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 wait_for_domain_resolver(self, table, set_name, element, max_wait=10):
         # Resolver no longer blocks commit, need to wait for daemon to populate set
         count = 0
         while count < max_wait:
             code = run(f'sudo nft get element {table} {set_name} {{ {element} }}')
             if code == 0:
                 return True
             count += 1
             sleep(1)
         return False
 
     def test_geoip(self):
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'drop'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'se'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'gb'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'de'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'fr'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'inverse-match'])
 
         self.cli_commit()
 
         nftables_search = [
             ['ip saddr @GEOIP_CC_smoketest_1', 'drop'],
             ['ip saddr != @GEOIP_CC_smoketest_2', 'return']
         ]
 
         # -t prevents 1000+ GeoIP elements being returned
         self.verify_nftables(nftables_search, 'ip vyos_filter', args='-t')
 
     def test_groups(self):
         hostmap_path = ['system', 'static-host-mapping', 'host-name']
         example_org = ['192.0.2.8', '192.0.2.10', '192.0.2.11']
 
         self.cli_set(hostmap_path + ['example.com', 'inet', '192.0.2.5'])
         for ips in example_org:
             self.cli_set(hostmap_path + ['example.org', 'inet', ips])
 
         self.cli_commit()
 
         self.cli_set(['firewall', 'group', 'mac-group', 'smoketest_mac', 'mac-address', '00:01:02:03:04:05'])
         self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'network', '172.16.99.0/24'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '53'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '123'])
         self.cli_set(['firewall', 'group', 'domain-group', 'smoketest_domain', 'address', 'example.com'])
         self.cli_set(['firewall', 'group', 'domain-group', 'smoketest_domain', 'address', 'example.org'])
 
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'group', 'mac-group', 'smoketest_mac'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'source', 'group', 'domain-group', 'smoketest_domain'])
 
         self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
 
         self.cli_commit()
 
         self.wait_for_domain_resolver('ip vyos_filter', 'D_smoketest_domain', '192.0.2.5')
 
         nftables_search = [
             ['iifname "eth0"', 'jump NAME_smoketest'],
             ['ip saddr @N_smoketest_network', 'ip daddr 172.16.10.10', 'th dport @P_smoketest_port', 'return'],
             ['elements = { 172.16.99.0/24 }'],
             ['elements = { 53, 123 }'],
             ['ether saddr @M_smoketest_mac', 'return'],
             ['elements = { 00:01:02:03:04:05 }'],
             ['set D_smoketest_domain'],
             ['elements = { 192.0.2.5, 192.0.2.8,'],
             ['192.0.2.10, 192.0.2.11 }'],
             ['ip saddr @D_smoketest_domain', 'return']
         ]
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
         self.cli_delete(['system', 'static-host-mapping'])
         self.cli_commit()
 
     def test_nested_groups(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(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '53'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port1', 'port', '123'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port1', 'include', 'smoketest_port'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network1'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port1'])
         self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
 
         self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
 
         self.cli_commit()
 
         # Test circular includes
         self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'include', 'smoketest_network1'])
         with self.assertRaises(ConfigSessionError):
             self.cli_commit()
 
         self.cli_delete(['firewall', 'group', 'network-group', 'smoketest_network', 'include', 'smoketest_network1'])
 
         nftables_search = [
             ['iifname "eth0"', 'jump NAME_smoketest'],
             ['ip saddr @N_smoketest_network1', 'th dport @P_smoketest_port1', 'return'],
             ['elements = { 172.16.99.0/24, 172.16.101.0/24 }'],
             ['elements = { 53, 123 }']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
     def test_ipv4_basic_rules(self):
         name = 'smoketest'
         interface = 'eth0'
         mss_range = '501-1460'
 
         self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
         self.cli_set(['firewall', 'name', name, 'enable-default-log'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'log', 'enable'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'log-level', 'debug'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'ttl', 'eq', '15'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'protocol', 'tcp'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'destination', 'port', '8888'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'log', 'enable'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'log-level', 'err'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'ttl', 'gt', '102'])
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'protocol', 'tcp'])
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'destination', 'port', '22'])
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'limit', 'rate', '5/minute'])
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'log', 'disable'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'action', 'drop'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'protocol', 'tcp'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'destination', 'port', '22'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'count', '10'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'time', 'minute'])
         self.cli_set(['firewall', 'name', name, 'rule', '5', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '5', 'protocol', 'tcp'])
         self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'flags', 'syn'])
         self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'mss', mss_range])
         self.cli_set(['firewall', 'name', name, 'rule', '5', 'inbound-interface', interface])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'return'])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'protocol', 'gre'])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'outbound-interface', interface])
 
         self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
 
         self.cli_commit()
 
         nftables_search = [
             [f'iifname "{interface}"', f'jump NAME_{name}'],
             ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" level debug', 'ip ttl 15', 'return'],
             ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" level err', 'ip ttl > 102', 'reject'],
             ['tcp dport 22', 'limit rate 5/minute', 'return'],
             ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop'],
             ['tcp dport 22', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'drop'],
             ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface}"'],
             ['meta l4proto gre', f'oifname "{interface}"', 'return']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
     def test_ipv4_advanced(self):
         name = 'smoketest-adv'
         name2 = 'smoketest-adv2'
         interface = 'eth0'
 
         self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
         self.cli_set(['firewall', 'name', name, 'enable-default-log'])
 
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '64'])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '512'])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '1024'])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '17'])
         self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '52'])
 
         self.cli_set(['firewall', 'name', name, 'rule', '7', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length', '1-30000'])
         self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length-exclude', '60000-65535'])
         self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp', '3-11'])
         self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp-exclude', '21-25'])
 
         self.cli_set(['firewall', 'name', name2, 'default-action', 'jump'])
         self.cli_set(['firewall', 'name', name2, 'default-jump-target', name])
         self.cli_set(['firewall', 'name', name2, 'enable-default-log'])
         self.cli_set(['firewall', 'name', name2, 'rule', '1', 'source', 'address', '198.51.100.1'])
         self.cli_set(['firewall', 'name', name2, 'rule', '1', 'action', 'jump'])
         self.cli_set(['firewall', 'name', name2, 'rule', '1', 'jump-target', name])
 
         self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
 
         self.cli_commit()
 
         nftables_search = [
             [f'iifname "{interface}"', f'jump NAME_{name}'],
             ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', 'return'],
             ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'],
             [f'log prefix "[{name}-default-D]"', 'drop'],
             ['ip saddr 198.51.100.1', f'jump NAME_{name}'],
             [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
+    def test_ipv4_mask(self):
+        name = 'smoketest-mask'
+        interface = 'eth0'
+
+        self.cli_set(['firewall', 'group', 'address-group', 'mask_group', 'address', '1.1.1.1'])
+
+        self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'name', name, 'enable-default-log'])
+
+        self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'drop'])
+        self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2'])
+        self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address-mask', '0.0.255.255'])
+
+        self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'accept'])
+        self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address', '!0.0.3.4'])
+        self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address-mask', '0.0.255.255'])
+
+        self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'drop'])
+        self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+        self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'address-mask', '0.0.255.255'])
+
+        self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+
+        self.cli_commit()
+
+        nftables_search = [
+            [f'daddr & 0.0.255.255 == 0.0.1.2'],
+            [f'saddr & 0.0.255.255 != 0.0.3.4'],
+            [f'saddr & 0.0.255.255 == @A_mask_group']
+        ]
+
+        self.verify_nftables(nftables_search, 'ip vyos_filter')
+
+
     def test_ipv6_basic_rules(self):
         name = 'v6-smoketest'
         interface = 'eth0'
 
         self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
         self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
 
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'action', 'accept'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'source', 'address', '2002::1'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '2002::1:1'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log', 'enable'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log-level', 'crit'])
 
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'reject'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'protocol', 'tcp_udp'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'destination', 'port', '8888'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'inbound-interface', interface])
 
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'return'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'protocol', 'gre'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'outbound-interface', interface])
 
         self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
 
         self.cli_commit()
 
         nftables_search = [
             [f'iifname "{interface}"', f'jump NAME6_{name}'],
             ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" level crit', 'return'],
             ['meta l4proto { tcp, udp }', 'th dport 8888', f'iifname "{interface}"', 'reject'],
             ['meta l4proto gre', f'oifname "{interface}"', 'return'],
             ['smoketest default-action', f'log prefix "[{name}-default-D]"', 'drop']
         ]
 
         self.verify_nftables(nftables_search, 'ip6 vyos_filter')
 
     def test_ipv6_advanced(self):
         name = 'v6-smoketest-adv'
         name2 = 'v6-smoketest-adv2'
         interface = 'eth0'
 
         self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
         self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
 
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'accept'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '65'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '513'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '1025'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '18'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '53'])
 
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'action', 'accept'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length', '1-1999'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length-exclude', '60000-65535'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp', '4-14'])
         self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp-exclude', '31-35'])
 
         self.cli_set(['firewall', 'ipv6-name', name2, 'default-action', 'jump'])
         self.cli_set(['firewall', 'ipv6-name', name2, 'default-jump-target', name])
         self.cli_set(['firewall', 'ipv6-name', name2, 'enable-default-log'])
         self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'source', 'address', '2001:db8::/64'])
         self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'action', 'jump'])
         self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'jump-target', name])
 
         self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
 
         self.cli_commit()
 
         nftables_search = [
             [f'iifname "{interface}"', f'jump NAME6_{name}'],
             ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'return'],
             ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'return'],
             [f'log prefix "[{name}-default-D]"', 'drop'],
             ['ip6 saddr 2001:db8::/64', f'jump NAME6_{name}'],
             [f'log prefix "[{name2}-default-J]"', f'jump NAME6_{name}']
         ]
 
         self.verify_nftables(nftables_search, 'ip6 vyos_filter')
 
+    def test_ipv6_mask(self):
+        name = 'v6-smoketest-mask'
+        interface = 'eth0'
+
+        self.cli_set(['firewall', 'group', 'ipv6-address-group', 'mask_group', 'address', '::beef'])
+
+        self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
+
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'action', 'drop'])
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444'])
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address-mask', '::ffff:ffff:ffff:ffff'])
+
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'accept'])
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address', '!::aaaa:bbbb:cccc:dddd'])
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'drop'])
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+
+        self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+
+        self.cli_commit()
+
+        nftables_search = [
+            ['daddr & ::ffff:ffff:ffff:ffff == ::1111:2222:3333:4444'],
+            ['saddr & ::ffff:ffff:ffff:ffff != ::aaaa:bbbb:cccc:dddd'],
+            ['saddr & ::ffff:ffff:ffff:ffff == @A6_mask_group']
+        ]
+
+        self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+
     def test_state_policy(self):
         self.cli_set(['firewall', 'state-policy', 'established', 'action', 'accept'])
         self.cli_set(['firewall', 'state-policy', 'related', 'action', 'accept'])
         self.cli_set(['firewall', 'state-policy', 'invalid', 'action', 'drop'])
 
         self.cli_commit()
 
         chains = {
             'ip vyos_filter': ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
             'ip6 vyos_filter': ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
         }
 
         for table in ['ip vyos_filter', 'ip6 vyos_filter']:
             for chain in chains[table]:
                 nftables_output = cmd(f'sudo nft list chain {table} {chain}')
                 self.assertTrue('jump VYOS_STATE_POLICY' in nftables_output)
 
     def test_ipv4_state_and_status_rules(self):
         name = 'smoketest-state'
         interface = 'eth0'
 
         self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'state', 'established', 'enable'])
         self.cli_set(['firewall', 'name', name, 'rule', '1', 'state', 'related', 'enable'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject'])
         self.cli_set(['firewall', 'name', name, 'rule', '2', 'state', 'invalid', 'enable'])
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'state', 'new', 'enable'])
 
         self.cli_set(['firewall', 'name', name, 'rule', '3', 'connection-status', 'nat', 'destination'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'action', 'accept'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'state', 'new', 'enable'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'state', 'established', 'enable'])
         self.cli_set(['firewall', 'name', name, 'rule', '4', 'connection-status', 'nat', 'source'])
 
         self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
 
         self.cli_commit()
 
         nftables_search = [
             [f'iifname "{interface}"', f'jump NAME_{name}'],
             ['ct state { established, related }', 'return'],
             ['ct state invalid', 'reject'],
             ['ct state new', 'ct status dnat', 'return'],
             ['ct state { established, new }', 'ct status snat', 'return'],
             ['drop', f'comment "{name} default-action drop"']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
     def test_sysfs(self):
         for name, conf in sysfs_config.items():
             paths = glob(conf['sysfs'])
             for path in paths:
                 with open(path, 'r') as f:
                     self.assertEqual(f.read().strip(), conf['default'], msg=path)
 
             self.cli_set(['firewall', name.replace("_", "-"), conf['test_value']])
 
         self.cli_commit()
 
         for name, conf in sysfs_config.items():
             paths = glob(conf['sysfs'])
             for path in paths:
                 with open(path, 'r') as f:
                     self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
 
     def test_zone_basic(self):
         self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
         self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
         self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
         self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
         self.cli_set(['firewall', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
 
         self.cli_commit()
 
         nftables_search = [
             ['chain VZONE_smoketest-eth0'],
             ['chain VZONE_smoketest-local_IN'],
             ['chain VZONE_smoketest-local_OUT'],
             ['oifname "eth0"', 'jump VZONE_smoketest-eth0'],
             ['jump VZONE_smoketest-local_IN'],
             ['jump VZONE_smoketest-local_OUT'],
             ['iifname "eth0"', 'jump NAME_smoketest'],
             ['oifname "eth0"', 'jump NAME_smoketest']
         ]
 
         nftables_output = cmd('sudo nft list table ip vyos_filter')
 
         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(matched)
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)