The RFC defines a Virtual Router Redundancy Protocol [0], in order
for that protocol to work the workload might "spoof" MAC address
within ARP or ND request/response. This wasn't allowed as the port
security is specifically designed against spoofing and checks if
the port security MAC address is the same for source of ARP/ND
and the inner source/target address. To make the port security
compliant add a special literal which when specified will allow
user to add any/all MAC addresses defined by VRRPv3. The traffic
from and to those additional MAC addresses will be allowed as
well as permutations of ARP/ND inner MACs combined with the
physical MAC as a source.

[0] https://datatracker.ietf.org/doc/html/rfc9568
Reported-at: https://issues.redhat.com/browse/FDP-2979
Signed-off-by: Ales Musil <[email protected]>
---
v4: Rebase on top of latest main.
    Update the RFC url.
    Add Jacob's ack.
v5: Rebase on top of latest main.
    Address nits pointed out by Dumitru.
    Add extra test for invalid VRRPv3 MAC.
    Update the wording in documentation.
    Remove acks as the code changed.
    Do not populate flows for physical MAC when VRRP is specified.
---
 NEWS               |   3 +
 controller/lflow.c | 932 +++++++++++++++++++++++++++++----------------
 ovn-nb.xml         |  55 +++
 tests/ovn.at       | 779 +++++++++++++++++++++++++++++++++++++
 4 files changed, 1446 insertions(+), 323 deletions(-)

diff --git a/NEWS b/NEWS
index 2a2b5e12d..7c7458224 100644
--- a/NEWS
+++ b/NEWS
@@ -98,6 +98,9 @@ Post v25.09.0
      reserving an unused IP from the backend's subnet. This change allows
      using LRP IPs directly, eliminating the need to reserve additional IPs
      per backend port.
+   - Add support for special port_security prefix "VRRPv3". This prefix allows
+     CMS to specify one physical MAC and multiple VRRPv3 MAC addresses.
+     The VRRPv3 MAC also accepts masked format.
 
 OVN v25.09.0 - xxx xx xxxx
 --------------------------
diff --git a/controller/lflow.c b/controller/lflow.c
index 94fd8807c..904de8ff0 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -2340,6 +2340,157 @@ add_port_sec_flows(const struct shash *binding_lports,
     }
 }
 
+struct masked_ip4_addr {
+    ovs_be32 addr;
+    ovs_be32 mask;
+    ovs_be32 bcast;
+};
+
+struct masked_ip6_addr {
+    struct in6_addr addr;
+    struct in6_addr mask;
+};
+
+struct masked_eth_addr {
+    struct eth_addr addr;
+    struct eth_addr mask;
+};
+
+struct port_security_addresses {
+    struct eth_addr phys_addr;
+    /* Vector of 'struct masked_eth_addr'. */
+    struct vector vrrp4;
+    /* Vector of 'struct masked_eth_addr'. */
+    struct vector vrrp6;
+    /* Vector of 'struct masked_ip4_addr' .*/
+    struct vector ip4;
+    /* Vector of 'struct masked_ip6_addr' .*/
+    struct vector ip6;
+};
+
+static void
+port_security_addresses_init(struct port_security_addresses *ps_addr)
+{
+    *ps_addr = (struct port_security_addresses) {
+        .phys_addr = eth_addr_zero,
+        .vrrp4 = VECTOR_EMPTY_INITIALIZER(struct masked_eth_addr),
+        .vrrp6 = VECTOR_EMPTY_INITIALIZER(struct masked_eth_addr),
+        .ip4 = VECTOR_EMPTY_INITIALIZER(struct masked_ip4_addr),
+        .ip6 = VECTOR_EMPTY_INITIALIZER(struct masked_ip6_addr),
+    };
+}
+
+static void
+port_security_addresses_clear(struct port_security_addresses *ps_addr)
+{
+    vector_clear(&ps_addr->vrrp4);
+    vector_clear(&ps_addr->vrrp6);
+    vector_clear(&ps_addr->ip4);
+    vector_clear(&ps_addr->ip6);
+}
+
+static void
+port_security_addresses_destroy(struct port_security_addresses *ps_addr)
+{
+    vector_destroy(&ps_addr->vrrp4);
+    vector_destroy(&ps_addr->vrrp6);
+    vector_destroy(&ps_addr->ip4);
+    vector_destroy(&ps_addr->ip6);
+}
+
+static const struct masked_eth_addr maddr_any_vrrp4 = {
+    .addr = ETH_ADDR_C(00,00,5e,00,01,00),
+    .mask = ETH_ADDR_C(ff,ff,ff,ff,ff,00)
+};
+static const struct masked_eth_addr maddr_any_vrrp6 = {
+    .addr = ETH_ADDR_C(00,00,5e,00,02,00),
+    .mask = ETH_ADDR_C(ff,ff,ff,ff,ff,00)
+};
+
+static bool
+port_security_addresses_add_vrrp_mac(struct port_security_addresses *ps_addr,
+                                     struct eth_addr mac, unsigned int plen)
+{
+    /* Only the last byte contains ID for VRRPv3. */
+    if (plen < 40) {
+        return false;
+    }
+
+    /* If the masked portion is non-zero, the host can only use
+     * the specified MAC address.  If zero, the host is allowed
+     * to use any MAC address within the mask.
+     */
+    struct eth_addr mask = eth_addr_create_mask(plen);
+    struct masked_eth_addr maddr = (struct masked_eth_addr) {
+        .addr = mac,
+        .mask = (eth_addr_to_uint64(mac) & ~eth_addr_to_uint64(mask))
+              ? eth_addr_exact : mask,
+    };
+
+    /* The exact match on VRRPv3 MAC ending with zero is not allowed, the
+     * id is starting from 1. */
+    if (plen == 48) {
+        if (eth_addr_equals(mac, maddr_any_vrrp4.addr) ||
+            eth_addr_equals(mac, maddr_any_vrrp6.addr)) {
+            return false;
+        }
+    }
+
+    if (eth_addr_equal_except(maddr_any_vrrp4.addr, mac,
+                              maddr_any_vrrp4.mask)) {
+        vector_push(&ps_addr->vrrp4, &maddr);
+        return true;
+    }
+
+    if (eth_addr_equal_except(maddr_any_vrrp6.addr, mac,
+                              maddr_any_vrrp6.mask)) {
+        vector_push(&ps_addr->vrrp6, &maddr);
+        return true;
+    }
+
+    return false;
+}
+
+static bool
+port_security_addresses_add_ip(struct port_security_addresses *ps_addr,
+                               struct in6_addr ip, unsigned int plen)
+{
+    /* When the netmask is applied, if the host portion is
+     * non-zero, the host can only use the specified
+     * address.  If zero, the host is allowed to use any
+     * address in the subnet. Also add broadcast in the special case
+     * of matching only the specified address.
+     */
+    if (IN6_IS_ADDR_V4MAPPED(&ip)) {
+        if (plen > 32) {
+            return false;
+        }
+
+        ovs_be32 addr = in6_addr_get_mapped_ipv4(&ip);
+        ovs_be32 mask = be32_prefix_mask(plen);
+
+        struct masked_ip4_addr maddr = (struct masked_ip4_addr) {
+            .addr = addr,
+            .mask = (addr & ~mask) ? OVS_BE32_MAX : mask,
+            .bcast = (addr & ~mask) ? (addr | ~mask) : htonl(0),
+        };
+        vector_push(&ps_addr->ip4, &maddr);
+    } else {
+        if (plen > 128) {
+            return false;
+        }
+
+        struct in6_addr mask = ipv6_create_mask(plen);
+        struct masked_ip6_addr maddr = (struct masked_ip6_addr) {
+            .addr = ip,
+            .mask = !ipv6_addr_is_host_zero(&ip, &mask) ? in6addr_exact : mask,
+        };
+        vector_push(&ps_addr->ip6, &maddr);
+    }
+
+    return true;
+}
+
 static void
 reset_match_for_port_sec_flows(const struct sbrec_port_binding *pb,
                                enum mf_field_id reg_id, struct match *match)
@@ -2446,24 +2597,39 @@ build_in_port_sec_default_flows(const struct 
sbrec_port_binding *pb,
                     &pb->header_.uuid);
 }
 
+static void
+build_out_port_sec_default_flows(const struct sbrec_port_binding *pb,
+                                struct match *m, struct ofpbuf *ofpacts,
+                                struct ovn_desired_flow_table *flow_table)
+{
+    /* Add the below logical flow equivalent OF rules in 'out_port_sec_nd'
+     * table.
+     * priority: 80
+     * match - "outport == pb->logical_port"
+     * action - "port_sec_failed = 1;"
+     * descrption: "Drop all traffic"
+     */
+    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
+    build_port_sec_deny_action(ofpacts);
+    ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 80,
+                    pb->header_.uuid.parts[0], m, ofpacts,
+                    &pb->header_.uuid);
+}
+
 static void
 build_in_port_sec_no_ip_flows(const struct sbrec_port_binding *pb,
-                              struct lport_addresses *ps_addr,
+                              struct eth_addr mac, struct eth_addr mask,
                               struct match *m, struct ofpbuf *ofpacts,
                               struct ovn_desired_flow_table *flow_table)
 {
-    if (ps_addr->n_ipv4_addrs || ps_addr->n_ipv6_addrs) {
-        return;
-    }
-
     /* Add the below logical flow equivalent OF rules in 'in_port_sec' table.
      * priority: 90
-     * match - "inport == pb->logical_port && eth.src == ps_addr.ea"
+     * match - "inport == pb->logical_port && eth.src == mac/mask"
      * action - "next;"
      * description: "Advance the packet for ARP/ND check"
      */
     reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-    match_set_dl_src(m, ps_addr->ea);
+    match_set_dl_src_masked(m, mac, mask);
     build_port_sec_adv_nd_check(ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90,
                     pb->header_.uuid.parts[0], m, ofpacts,
@@ -2472,43 +2638,26 @@ build_in_port_sec_no_ip_flows(const struct 
sbrec_port_binding *pb,
 
 static void
 build_in_port_sec_ip4_flows(const struct sbrec_port_binding *pb,
-                           struct lport_addresses *ps_addr,
-                           struct match *m, struct ofpbuf *ofpacts,
-                           struct ovn_desired_flow_table *flow_table)
+                            struct eth_addr mac, struct eth_addr mask,
+                            const struct vector *ip4_addrs,
+                            struct match *m, struct ofpbuf *ofpacts,
+                            struct ovn_desired_flow_table *flow_table)
 {
-    if (!ps_addr->n_ipv4_addrs) {
-        /* If no IPv4 addresses, then 'pb' is not allowed to send IPv4 traffic.
-         * build_in_port_sec_default_flows() takes care of this scenario. */
-        return;
-    }
-
     /* Advance all traffic from the port security eth address for ND check. */
     build_port_sec_allow_action(ofpacts);
+    reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
+    match_set_dl_src_masked(m, mac, mask);
+    match_set_dl_type(m, htons(ETH_TYPE_IP));
 
     /* Add the below logical flow equivalent OF rules in in_port_sec.
      * priority: 90
-     * match - "inport == pb->port && eth.src == ps_addr.ea &&
-     *         ip4.src == {ps_addr.ipv4_addrs}"
+     * match - "inport == pb->port && eth.src == mac/mask &&
+     *         ip4.src == {ip4}"
      * action - "port_sec_failed = 0;"
      */
-    for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) {
-        reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-        match_set_dl_src(m, ps_addr->ea);
-        match_set_dl_type(m, htons(ETH_TYPE_IP));
-
-        ovs_be32 mask = ps_addr->ipv4_addrs[j].mask;
-        /* When the netmask is applied, if the host portion is
-         * non-zero, the host can only use the specified
-         * address.  If zero, the host is allowed to use any
-         * address in the subnet.
-         */
-        if (ps_addr->ipv4_addrs[j].plen == 32 ||
-                ps_addr->ipv4_addrs[j].addr & ~mask) {
-            match_set_nw_src(m, ps_addr->ipv4_addrs[j].addr);
-        } else {
-            match_set_nw_src_masked(m, ps_addr->ipv4_addrs[j].addr, mask);
-        }
-
+    const struct masked_ip4_addr *ip;
+    VECTOR_FOR_EACH_PTR (ip4_addrs, ip) {
+        match_set_nw_src_masked(m, ip->addr, ip->mask);
         ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90,
                         pb->header_.uuid.parts[0], m, ofpacts,
                         &pb->header_.uuid);
@@ -2516,20 +2665,14 @@ build_in_port_sec_ip4_flows(const struct 
sbrec_port_binding *pb,
 
     /* Add the below logical flow equivalent OF rules in in_port_sec.
      * priority: 90
-     * match - "inport == pb->port && eth.src == ps_addr.ea &&
+     * match - "inport == pb->port && eth.src == mac/mask &&
      *          ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 &&
      *          udp.src == 67 && udp.dst == 68"
      * action - "port_sec_failed = 0;"
      * description: "Allow the DHCP requests."
      */
-    reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-    match_set_dl_src(m, ps_addr->ea);
-    match_set_dl_type(m, htons(ETH_TYPE_IP));
-
-    ovs_be32 ip4 = htonl(0);
-    match_set_nw_src(m, ip4);
-    ip4 = htonl(0xffffffff);
-    match_set_nw_dst(m, ip4);
+    match_set_nw_src(m, htonl(0));
+    match_set_nw_dst(m, htonl(0xffffffff));
     match_set_nw_proto(m, IPPROTO_UDP);
     match_set_tp_src(m, htons(68));
     match_set_tp_dst(m, htons(67));
@@ -2542,33 +2685,42 @@ build_in_port_sec_ip4_flows(const struct 
sbrec_port_binding *pb,
 /* Adds the OF rules to allow ARP packets in 'in_port_sec_nd' table. */
 static void
 build_in_port_sec_arp_flows(const struct sbrec_port_binding *pb,
-                           struct lport_addresses *ps_addr,
-                           struct match *m, struct ofpbuf *ofpacts,
-                           struct ovn_desired_flow_table *flow_table)
+                            struct eth_addr phys_mac,
+                            const struct vector *ip4_addrs,
+                            const struct vector *vrrp4_addrs,
+                            bool is_vrrp, struct match *m,
+                            struct ofpbuf *ofpacts,
+                            struct ovn_desired_flow_table *flow_table)
 {
-    if (!ps_addr->n_ipv4_addrs && ps_addr->n_ipv6_addrs) {
-        /* No ARP is allowed as only IPv6 addresses are configured. */
-        return;
-    }
-
     build_port_sec_allow_action(ofpacts);
+    reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
+    match_set_dl_src(m, phys_mac);
+    match_set_dl_type(m, htons(ETH_TYPE_ARP));
 
-    if (!ps_addr->n_ipv4_addrs) {
+    if (vector_is_empty(ip4_addrs)) {
         /* No IPv4 addresses.
          * Add the below logical flow equivalent OF rules in 'in_port_sec_nd'
          * table.
          * priority: 90
-         * match - "inport == pb->port && eth.src == ps_addr.ea &&
-         *          arp && arp.sha == ps_addr.ea"
+         * match - "inport == pb->port && eth.src == phys_mac &&
+         *          arp && arp.sha == {phys_mac, vrrp4_addrs}"
          * action - "port_sec_failed = 0;"
          */
-        reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-        match_set_dl_src(m, ps_addr->ea);
-        match_set_dl_type(m, htons(ETH_TYPE_ARP));
-        match_set_arp_sha(m, ps_addr->ea);
-        ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
-                        pb->header_.uuid.parts[0], m, ofpacts,
-                        &pb->header_.uuid);
+
+        if (!is_vrrp) {
+            match_set_arp_sha(m, phys_mac);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
+        }
+
+        struct masked_eth_addr *mmac;
+        VECTOR_FOR_EACH_PTR (vrrp4_addrs, mmac) {
+            match_set_arp_sha_masked(m, mmac->addr, mmac->mask);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
+        }
     }
 
     /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd'
@@ -2578,74 +2730,62 @@ build_in_port_sec_arp_flows(const struct 
sbrec_port_binding *pb,
      *         arp && arp.sha == ps_addr.ea && arp.spa == {ps_addr.ipv4_addrs}"
      * action - "port_sec_failed = 0;"
      */
-    for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) {
-        reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-        match_set_dl_src(m, ps_addr->ea);
-        match_set_dl_type(m, htons(ETH_TYPE_ARP));
-        match_set_arp_sha(m, ps_addr->ea);
-
-        ovs_be32 mask = ps_addr->ipv4_addrs[j].mask;
-        if (ps_addr->ipv4_addrs[j].plen == 32 ||
-                ps_addr->ipv4_addrs[j].addr & ~mask) {
-            match_set_nw_src(m, ps_addr->ipv4_addrs[j].addr);
-        } else {
-            match_set_nw_src_masked(m, ps_addr->ipv4_addrs[j].addr, mask);
+    const struct masked_ip4_addr *ip;
+    VECTOR_FOR_EACH_PTR (ip4_addrs, ip) {
+        match_set_nw_src_masked(m, ip->addr, ip->mask);
+
+        if (!is_vrrp) {
+            match_set_arp_sha(m, phys_mac);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
+        }
+
+        struct masked_eth_addr *mmac;
+        VECTOR_FOR_EACH_PTR (vrrp4_addrs, mmac) {
+            match_set_arp_sha_masked(m, mmac->addr, mmac->mask);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
         }
-        ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
-                        pb->header_.uuid.parts[0], m, ofpacts,
-                        &pb->header_.uuid);
     }
 }
 
 static void
 build_in_port_sec_ip6_flows(const struct sbrec_port_binding *pb,
-                           struct lport_addresses *ps_addr,
-                           struct match *m, struct ofpbuf *ofpacts,
-                           struct ovn_desired_flow_table *flow_table)
+                            struct eth_addr mac, struct eth_addr mask,
+                            const struct vector *ip6_addrs,
+                            struct match *m, struct ofpbuf *ofpacts,
+                            struct ovn_desired_flow_table *flow_table)
 {
-    if (!ps_addr->n_ipv6_addrs) {
-        /* If no IPv6 addresses, then 'pb' is not allowed to send IPv6 traffic.
-         * build_in_port_sec_default_flows() takes care of this scenario. */
-        return;
-    }
-
     /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd'
      * table.
      * priority: 90
-     * match - "inport == pb->port && eth.src == ps_addr.ea &&
+     * match - "inport == pb->port && eth.src == mac/mask &&
      *         ip6.src == {ps_addr.ipv6_addrs, lla}"
      * action - "next;"
      * description - Advance the packet for Neighbor Solicit/Adv check.
      */
     build_port_sec_adv_nd_check(ofpacts);
+    reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
+    match_set_dl_src_masked(m, mac, mask);
+    match_set_dl_type(m, htons(ETH_TYPE_IPV6));
 
-    for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) {
-        reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-        match_set_dl_src(m, ps_addr->ea);
-        match_set_dl_type(m, htons(ETH_TYPE_IPV6));
-
-        if (ps_addr->ipv6_addrs[j].plen == 128
-            || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr,
-                                        &ps_addr->ipv6_addrs[j].mask)) {
-            match_set_ipv6_src(m, &ps_addr->ipv6_addrs[j].addr);
-        } else {
-            match_set_ipv6_src_masked(m, &ps_addr->ipv6_addrs[j].network,
-                                        &ps_addr->ipv6_addrs[j].mask);
-        }
-
+    const struct masked_ip6_addr *ip;
+    VECTOR_FOR_EACH_PTR (ip6_addrs, ip) {
+        match_set_ipv6_src_masked(m, &ip->addr, &ip->mask);
         ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90,
                         pb->header_.uuid.parts[0], m, ofpacts,
                         &pb->header_.uuid);
     }
 
-    reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-    match_set_dl_src(m, ps_addr->ea);
-    match_set_dl_type(m, htons(ETH_TYPE_IPV6));
 
     struct in6_addr lla;
-    in6_generate_lla(ps_addr->ea, &lla);
-    match_set_ipv6_src(m, &lla);
+    in6_generate_lla(mac, &lla);
+    unsigned int plen = 128 - 48 + eth_addr_get_prefix_len(mask);
+    struct in6_addr lla_mask = ipv6_create_mask(plen);
 
+    match_set_ipv6_src_masked(m, &lla, &lla_mask);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
@@ -2660,11 +2800,12 @@ build_in_port_sec_ip6_flows(const struct 
sbrec_port_binding *pb,
      */
     build_port_sec_allow_action(ofpacts);
     match_set_ipv6_src(m, &in6addr_any);
-    struct in6_addr ip6, mask;
-    char *err = ipv6_parse_masked("ff02::/16", &ip6, &mask);
+
+    struct in6_addr ip6, ip_mask;
+    char *err = ipv6_parse_masked("ff02::/16", &ip6, &ip_mask);
     ovs_assert(!err);
 
-    match_set_ipv6_dst_masked(m, &ip6, &mask);
+    match_set_ipv6_dst_masked(m, &ip6, &ip_mask);
     match_set_nw_proto(m, IPPROTO_ICMPV6);
     match_set_icmp_type(m, 131);
     match_set_icmp_code(m, 0);
@@ -2697,99 +2838,118 @@ build_in_port_sec_ip6_flows(const struct 
sbrec_port_binding *pb,
  * 'in_port_sec_nd' table. */
 static void
 build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb,
-                           struct lport_addresses *ps_addr,
-                           struct match *m, struct ofpbuf *ofpacts,
+                           struct eth_addr phys_mac,
+                           const struct vector *ip6_addrs,
+                           const struct vector *vrrp6_addrs,
+                           bool is_vrrp, struct match *m,
+                           struct ofpbuf *ofpacts,
                            struct ovn_desired_flow_table *flow_table)
 {
     build_port_sec_allow_action(ofpacts);
+    reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
+    match_set_dl_src(m, phys_mac);
+    match_set_dl_type(m, htons(ETH_TYPE_IPV6));
+    match_set_nw_proto(m, IPPROTO_ICMPV6);
+    match_set_icmp_code(m, 0);
+    match_set_nw_ttl(m, 255);
 
     /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd'
      * table.
      * priority: 90
-     * match - "inport == pb->port && eth.src == ps_addr.ea &&
-     *          icmp6 && icmp6.code == 135 && icmp6.type == 0 &&
-     *          ip6.tll == 255 && nd.sll == {00:00:00:00:00:00, ps_addr.ea}"
+     * match - "inport == pb->port && eth.src == phys_mac &&
+     *          icmp6 && icmp6.type == 135 && icmp6.code == 0 &&
+     *          ip6.tll == 255 &&
+     *          nd.sll == {00:00:00:00:00:00, phys_mac, vrrp6_addrs}"
      * action - "port_sec_failed = 0;"
      */
-    reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-    match_set_dl_type(m, htons(ETH_TYPE_IPV6));
-    match_set_nw_proto(m, IPPROTO_ICMPV6);
-    match_set_dl_src(m, ps_addr->ea);
-    match_set_nw_ttl(m, 255);
+
     match_set_icmp_type(m, 135);
-    match_set_icmp_code(m, 0);
 
     match_set_arp_sha(m, eth_addr_zero);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
 
-    match_set_arp_sha(m, ps_addr->ea);
-    ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
-                    pb->header_.uuid.parts[0], m, ofpacts,
-                    &pb->header_.uuid);
+    if (!is_vrrp) {
+        match_set_arp_sha(m, phys_mac);
+        ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                        pb->header_.uuid.parts[0], m, ofpacts,
+                        &pb->header_.uuid);
+    }
 
+    struct masked_eth_addr *mmac;
+    VECTOR_FOR_EACH_PTR (vrrp6_addrs, mmac) {
+        match_set_arp_sha_masked(m, mmac->addr, mmac->mask);
+        ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                        pb->header_.uuid.parts[0], m, ofpacts,
+                        &pb->header_.uuid);
+    }
+
+    match_set_arp_sha_masked(m, eth_addr_zero, eth_addr_zero);
     match_set_icmp_type(m, 136);
-    match_set_icmp_code(m, 0);
-    if (ps_addr->n_ipv6_addrs) {
+    if (!vector_is_empty(ip6_addrs)) {
         /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd'
          * table if IPv6 addresses are configured.
          * priority: 90
-         * match - "inport == pb->port && eth.src == ps_addr.ea && icmp6 &&
-         *          icmp6.code == 136 && icmp6.type == 0 && ip6.tll == 255 &&
-         *          nd.tll == {00:00:00:00:00:00, ps_addr.ea} &&
-         *          nd.target == {ps_addr.ipv6_addrs, lla}"
+         * match - "inport == pb->port && eth.src == phys_mac && icmp6 &&
+         *          icmp6.type == 136 && icmp6.code == 0 && ip6.tll == 255 &&
+         *          nd.tll == {00:00:00:00:00:00, phys_mac, vrrp6_addrs} &&
+         *          nd.target == {lla, ip6_addrs}"
          * action - "port_sec_failed = 0;"
          */
         struct in6_addr lla;
-        in6_generate_lla(ps_addr->ea, &lla);
-        match_set_arp_tha(m, eth_addr_zero);
-
-        match_set_nd_target(m, &lla);
-        ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
-                        pb->header_.uuid.parts[0], m, ofpacts,
-                        &pb->header_.uuid);
-        match_set_arp_tha(m, ps_addr->ea);
+        in6_generate_lla(phys_mac, &lla);
         match_set_nd_target(m, &lla);
+
+        match_set_arp_tha(m, eth_addr_zero);
         ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
                         pb->header_.uuid.parts[0], m, ofpacts,
                         &pb->header_.uuid);
 
-        for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) {
-            reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m);
-            match_set_dl_src(m, ps_addr->ea);
-            match_set_dl_type(m, htons(ETH_TYPE_IPV6));
-            match_set_nw_proto(m, IPPROTO_ICMPV6);
-            match_set_nw_ttl(m, 255);
-            match_set_icmp_type(m, 136);
-            match_set_icmp_code(m, 0);
-            match_set_arp_tha(m, eth_addr_zero);
-
-            if (ps_addr->ipv6_addrs[j].plen == 128
-                || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr,
-                                            &ps_addr->ipv6_addrs[j].mask)) {
-                match_set_nd_target(m, &ps_addr->ipv6_addrs[j].addr);
-            } else {
-                match_set_nd_target_masked(m, &ps_addr->ipv6_addrs[j].network,
-                                           &ps_addr->ipv6_addrs[j].mask);
-            }
+        if (!is_vrrp) {
+            match_set_arp_tha(m, phys_mac);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
+        }
 
+        VECTOR_FOR_EACH_PTR (vrrp6_addrs, mmac) {
+            match_set_arp_tha_masked(m, mmac->addr, mmac->mask);
             ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
                             pb->header_.uuid.parts[0], m, ofpacts,
                             &pb->header_.uuid);
+        }
 
-            match_set_arp_tha(m, ps_addr->ea);
+        const struct masked_ip6_addr *ip;
+        VECTOR_FOR_EACH_PTR (ip6_addrs, ip) {
+            match_set_nd_target_masked(m, &ip->addr, &ip->mask);
+
+            match_set_arp_tha(m, eth_addr_zero);
             ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
                             pb->header_.uuid.parts[0], m, ofpacts,
                             &pb->header_.uuid);
+
+            if (!is_vrrp) {
+                match_set_arp_tha(m, phys_mac);
+                ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                                pb->header_.uuid.parts[0], m, ofpacts,
+                                &pb->header_.uuid);
+            }
+
+            VECTOR_FOR_EACH_PTR (vrrp6_addrs, mmac) {
+                match_set_arp_tha_masked(m, mmac->addr, mmac->mask);
+                ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                                pb->header_.uuid.parts[0], m, ofpacts,
+                                &pb->header_.uuid);
+            }
         }
     } else {
         /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd'
          * table if no IPv6 addresses are configured.
          * priority: 90
-         * match - "inport == pb->port && eth.src == ps_addr.ea && icmp6 &&
+         * match - "inport == pb->port && eth.src == phys_mac && icmp6 &&
          *          icmp6.code == 136 && icmp6.type == 0 && ip6.tll == 255 &&
-         *          nd.tll == {00:00:00:00:00:00, ps_addr.ea}"
+         *          nd.tll == {00:00:00:00:00:00, phys_mac, vrrp6_addrs}"
          * action - "port_sec_failed = 0;"
          */
         match_set_arp_tha(m, eth_addr_zero);
@@ -2797,27 +2957,36 @@ build_in_port_sec_nd_flows(const struct 
sbrec_port_binding *pb,
                         pb->header_.uuid.parts[0], m, ofpacts,
                         &pb->header_.uuid);
 
-        match_set_arp_tha(m, ps_addr->ea);
-        ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
-                        pb->header_.uuid.parts[0], m, ofpacts,
-                        &pb->header_.uuid);
+        if (!is_vrrp) {
+            match_set_arp_tha(m, phys_mac);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
+        }
+
+        VECTOR_FOR_EACH_PTR (vrrp6_addrs, mmac) {
+            match_set_arp_tha_masked(m, mmac->addr, mmac->mask);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
+        }
     }
 }
 
 static void
 build_out_port_sec_no_ip_flows(const struct sbrec_port_binding *pb,
-                               struct lport_addresses *ps_addr,
+                               struct eth_addr mac, struct eth_addr mask,
                                struct match *m, struct ofpbuf *ofpacts,
                                struct ovn_desired_flow_table *flow_table)
 {
     /* Add the below logical flow equivalent OF rules in 'out_port_sec' table.
      * priority: 85
-     * match - "outport == pb->logical_port && eth.dst == ps_addr.ea"
+     * match - "outport == pb->logical_port && eth.dst == mac/mask"
      * action - "port_sec_failed = 0;"
      * description: "Allow the packet if eth.dst matches."
      */
     reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
-    match_set_dl_dst(m, ps_addr->ea);
+    match_set_dl_dst_masked(m, mac, mask);
     build_port_sec_allow_action(ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 85,
                     pb->header_.uuid.parts[0], m, ofpacts,
@@ -2826,97 +2995,70 @@ build_out_port_sec_no_ip_flows(const struct 
sbrec_port_binding *pb,
 
 static void
 build_out_port_sec_ip4_flows(const struct sbrec_port_binding *pb,
-                            struct lport_addresses *ps_addr,
-                            struct match *m, struct ofpbuf *ofpacts,
-                            struct ovn_desired_flow_table *flow_table)
+                             struct eth_addr mac, struct eth_addr mask,
+                             const struct vector *ip4_addrs,
+                             struct match *m, struct ofpbuf *ofpacts,
+                             struct ovn_desired_flow_table *flow_table)
 {
-    if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) {
-         /* No IPv4 and no IPv6 addresses in the port security.
-          * Both IPv4 and IPv6 traffic should be delivered to the
-          * lport. build_out_port_sec_no_ip_flows() takes care of
-          * adding the required flow(s) to allow. */
-        return;
-    }
+    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
+    match_set_dl_dst_masked(m, mac, mask);
+    match_set_dl_type(m, htons(ETH_TYPE_IP));
 
     /* Add the below logical flow equivalent OF rules in 'out_port_sec' table.
      * priority: 90
-     * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && ip4"
+     * match - "outport == pb->logical_port && eth.dst == mac/mask && ip4"
      * action - "port_sec_failed = 1;"
      * description: Default drop IPv4 packets.  If IPv4 addresses are
      *              configured, then higher priority flows are added
      *              to allow specific IPv4 packets.
      */
-    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
-    match_set_dl_dst(m, ps_addr->ea);
-    match_set_dl_type(m, htons(ETH_TYPE_IP));
+
     build_port_sec_deny_action(ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 90,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
 
-    if (!ps_addr->n_ipv4_addrs) {
+    if (vector_is_empty(ip4_addrs)) {
         return;
     }
 
+    build_port_sec_allow_action(ofpacts);
     /* Add the below logical flow equivalent OF rules in 'out_port_sec' table.
      * priority: 95
-     * match - "outport == pb->logical_port && eth.dst == ps_addr.ea &&
-     *          ip4.dst == {ps_addr.ipv4_addrs, 255.255.255.255, 224.0.0.0/4},"
+     * match - "outport == pb->logical_port && eth.dst == mac/mask &&
+     *          ip4.dst == {ip4_addrs, 255.255.255.255, 224.0.0.0/4},"
      * action - "port_sec_failed = 0;"
      */
-    build_port_sec_allow_action(ofpacts);
-    for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) {
-        reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
-        match_set_dl_dst(m, ps_addr->ea);
-        match_set_dl_type(m, htons(ETH_TYPE_IP));
-        ovs_be32 mask = ps_addr->ipv4_addrs[j].mask;
-        if (ps_addr->ipv4_addrs[j].plen == 32
-                || ps_addr->ipv4_addrs[j].addr & ~mask) {
-
-            if (ps_addr->ipv4_addrs[j].plen != 32) {
-                /* Special case to allow bcast traffic.
-                 * Eg. If ps_addr is 10.0.0.4/24, then add the below flow
-                 * priority: 95
-                 * match - "outport == pb->logical_port &&
-                 *          eth.dst == ps_addr.ea &&
-                 *          ip4.dst == 10.0.0.255"
-                 * action - "port_sec_failed = 0;"
-                 */
-                ovs_be32 bcast_addr;
-                ovs_assert(ip_parse(ps_addr->ipv4_addrs[j].bcast_s,
-                                    &bcast_addr));
-                match_set_nw_dst(m, bcast_addr);
-                ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
-                                pb->header_.uuid.parts[0], m, ofpacts,
-                                &pb->header_.uuid);
-            }
-
-            match_set_nw_dst(m, ps_addr->ipv4_addrs[j].addr);
-        } else {
-            /* host portion is zero */
-            match_set_nw_dst_masked(m, ps_addr->ipv4_addrs[j].addr,
-                                    mask);
-        }
-
+    const struct masked_ip4_addr *ip;
+    VECTOR_FOR_EACH_PTR (ip4_addrs, ip) {
+        match_set_nw_dst_masked(m, ip->addr, ip->mask);
         ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
                         pb->header_.uuid.parts[0], m, ofpacts,
                         &pb->header_.uuid);
-    }
 
-    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
-    match_set_dl_dst(m, ps_addr->ea);
-    match_set_dl_type(m, htons(ETH_TYPE_IP));
+        if (ip->bcast) {
+            /* Special case to allow bcast traffic.
+             * Eg. If address is 10.0.0.4/24, then add the below flow
+             * priority: 95
+             * match - "outport == pb->logical_port &&
+             *          eth.dst == ps_addr.ea &&
+             *          ip4.dst == 10.0.0.255"
+             * action - "port_sec_failed = 0;"
+             */
+            match_set_nw_dst(m, ip->bcast);
+            ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
+                            pb->header_.uuid.parts[0], m, ofpacts,
+                            &pb->header_.uuid);
+        }
+    }
 
-    ovs_be32 ip4 = htonl(0xffffffff);
-    match_set_nw_dst(m, ip4);
+    match_set_nw_dst(m, htonl(0xffffffff));
     ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
 
     /* Allow 224.0.0.0/4 traffic. */
-    ip4 = htonl(0xe0000000);
-    ovs_be32 mask = htonl(0xf0000000);
-    match_set_nw_dst_masked(m, ip4, mask);
+    match_set_nw_dst_masked(m, htonl(0xe0000000), htonl(0xf0000000));
     ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
@@ -2924,112 +3066,270 @@ build_out_port_sec_ip4_flows(const struct 
sbrec_port_binding *pb,
 
 static void
 build_out_port_sec_ip6_flows(const struct sbrec_port_binding *pb,
-                            struct lport_addresses *ps_addr,
-                            struct match *m, struct ofpbuf *ofpacts,
-                            struct ovn_desired_flow_table *flow_table)
+                             struct eth_addr mac, struct eth_addr mask,
+                             const struct vector *ip6_addrs,
+                             struct match *m, struct ofpbuf *ofpacts,
+                             struct ovn_desired_flow_table *flow_table)
 {
-    if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) {
-        /* No IPv4 and no IPv6 addresses in the port security.
-         * Both IPv4 and IPv6 traffic should be delivered to the
-         * lport. build_out_port_sec_no_ip_flows() takes care of
-         * adding the required flow(s) to allow. */
-        return;
-    }
+    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
+    match_set_dl_dst_masked(m, mac, mask);
+    match_set_dl_type(m, htons(ETH_TYPE_IPV6));
 
     /* Add the below logical flow equivalent OF rules in 'out_port_sec' table.
      * priority: 90
-     * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && ip6"
+     * match - "outport == pb->logical_port && eth.dst == mac/mask && ip6"
      * action - "port_sec_failed = 1;"
      * description: Default drop IPv6 packets.  If IPv6 addresses are
      *              configured, then higher priority flows are added
      *              to allow specific IPv6 packets.
      */
-    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
-    match_set_dl_dst(m, ps_addr->ea);
-    match_set_dl_type(m, htons(ETH_TYPE_IPV6));
     build_port_sec_deny_action(ofpacts);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 90,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
 
-    if (!ps_addr->n_ipv6_addrs) {
+    if (vector_is_empty(ip6_addrs)) {
         return;
     }
 
+    build_port_sec_allow_action(ofpacts);
     /* Add the below logical flow equivalent OF rules in 'out_port_sec' table.
      * priority: 95
-     * match - "outport == pb->logical_port && eth.dst == ps_addr.ea &&
-     *          ip6.dst == {ps_addr.ipv6_addrs, lla, ff00::/8},"
+     * match - "outport == pb->logical_port && eth.dst == mac/mask &&
+     *          ip6.dst == {mac/mask, ip6_addrs, lla, ff00::/8},"
      * action - "port_sec_failed = 0;"
      */
-    build_port_sec_allow_action(ofpacts);
-    for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) {
-        reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
-        match_set_dl_dst(m, ps_addr->ea);
-        match_set_dl_type(m, htons(ETH_TYPE_IPV6));
-
-        if (ps_addr->ipv6_addrs[j].plen == 128
-            || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr,
-                                        &ps_addr->ipv6_addrs[j].mask)) {
-            match_set_ipv6_dst(m, &ps_addr->ipv6_addrs[j].addr);
-        } else {
-            match_set_ipv6_dst_masked(m, &ps_addr->ipv6_addrs[j].network,
-                                      &ps_addr->ipv6_addrs[j].mask);
-        }
-
+    const struct masked_ip6_addr *ip;
+    VECTOR_FOR_EACH_PTR (ip6_addrs, ip) {
+        match_set_ipv6_dst_masked(m, &ip->addr, &ip->mask);
         ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
                         pb->header_.uuid.parts[0], m, ofpacts,
                         &pb->header_.uuid);
     }
 
     struct in6_addr lla;
-    in6_generate_lla(ps_addr->ea, &lla);
+    in6_generate_lla(mac, &lla);
+    unsigned int plen = 128 - 48 + eth_addr_get_prefix_len(mask);
+    struct in6_addr lla_mask = ipv6_create_mask(plen);
 
-    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m);
-    match_set_dl_dst(m, ps_addr->ea);
-    match_set_dl_type(m, htons(ETH_TYPE_IPV6));
-    match_set_ipv6_dst(m, &lla);
+    match_set_ipv6_dst_masked(m, &lla, &lla_mask);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
 
-    struct in6_addr ip6, mask;
-    char *err = ipv6_parse_masked("ff00::/8", &ip6, &mask);
+    struct in6_addr ip6, ip_mask;
+    char *err = ipv6_parse_masked("ff00::/8", &ip6, &ip_mask);
     ovs_assert(!err);
 
-    match_set_ipv6_dst_masked(m, &ip6, &mask);
+    match_set_ipv6_dst_masked(m, &ip6, &ip_mask);
     ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95,
                     pb->header_.uuid.parts[0], m, ofpacts,
                     &pb->header_.uuid);
 }
 
 static void
-consider_port_sec_flows(const struct sbrec_port_binding *pb,
-                        struct ovn_desired_flow_table *flow_table)
+build_port_sec_entry_flows(const struct sbrec_port_binding *pb,
+                           const struct port_security_addresses *ps_addr,
+                           struct match *m, struct ofpbuf *ofpacts,
+                           struct ovn_desired_flow_table *flow_table)
 {
-    if (!pb->n_port_security) {
-        return;
+    /* Input no-ip flows. */
+    if (vector_is_empty(&ps_addr->ip4) && vector_is_empty(&ps_addr->ip6)) {
+        build_in_port_sec_no_ip_flows(pb, ps_addr->phys_addr, eth_addr_exact,
+                                      m, ofpacts, flow_table);
     }
 
-    struct lport_addresses *ps_addrs;   /* Port security addresses. */
-    size_t n_ps_addrs = 0;
+    /* Input IPv4 flows. */
+    if (!vector_is_empty(&ps_addr->ip4)) {
+        build_in_port_sec_ip4_flows(pb, ps_addr->phys_addr, eth_addr_exact,
+                                    &ps_addr->ip4, m, ofpacts, flow_table);
+    }
 
-    ps_addrs = xmalloc(sizeof *ps_addrs * pb->n_port_security);
-    for (size_t i = 0; i < pb->n_port_security; i++) {
-        if (!extract_lsp_addresses(pb->port_security[i],
-                                    &ps_addrs[n_ps_addrs])) {
-            static struct vlog_rate_limit rl
-                = VLOG_RATE_LIMIT_INIT(1, 1);
-            VLOG_WARN_RL(&rl, "invalid syntax '%s' in port "
-                         "security. No MAC address found",
-                         pb->port_security[i]);
-            continue;
+    /* Input ARP flows. */
+    if (!vector_is_empty(&ps_addr->ip4) || vector_is_empty(&ps_addr->ip6)) {
+        build_in_port_sec_arp_flows(pb, ps_addr->phys_addr, &ps_addr->ip4,
+                                    &ps_addr->vrrp4, false, m, ofpacts,
+                                    flow_table);
+    }
+
+    /* Input Ipv6 flows. */
+    if (!vector_is_empty(&ps_addr->ip6)) {
+        build_in_port_sec_ip6_flows(pb, ps_addr->phys_addr, eth_addr_exact,
+                                    &ps_addr->ip6, m, ofpacts, flow_table);
+    }
+
+    /* Input ND flows. */
+    build_in_port_sec_nd_flows(pb, ps_addr->phys_addr, &ps_addr->ip6,
+                               &ps_addr->vrrp6, false, m, ofpacts,
+                               flow_table);
+
+    /* Output no-ip flows. */
+    build_out_port_sec_no_ip_flows(pb, ps_addr->phys_addr, eth_addr_exact,
+                                    m, ofpacts, flow_table);
+
+    /* Output IPv4 flows. */
+    if (!vector_is_empty(&ps_addr->ip4) || !vector_is_empty(&ps_addr->ip6)) {
+        build_out_port_sec_ip4_flows(pb, ps_addr->phys_addr, eth_addr_exact,
+                                     &ps_addr->ip4, m, ofpacts, flow_table);
+    }
+
+    /* Output Ipv6 flows. */
+    if (!vector_is_empty(&ps_addr->ip4) || !vector_is_empty(&ps_addr->ip6)) {
+        build_out_port_sec_ip6_flows(pb, ps_addr->phys_addr, eth_addr_exact,
+                                     &ps_addr->ip6, m, ofpacts, flow_table);
+    }
+}
+
+static void
+build_port_sec_entry_vrrp_flows(const struct sbrec_port_binding *pb,
+                                const struct port_security_addresses *ps_addr,
+                                struct match *m, struct ofpbuf *ofpacts,
+                                struct ovn_desired_flow_table *flow_table)
+{
+    const struct masked_eth_addr *maddr;
+
+    /* Input no-ip flows. */
+    if (vector_is_empty(&ps_addr->ip4) && vector_is_empty(&ps_addr->ip6)) {
+        VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) {
+            build_in_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask,
+                                          m, ofpacts, flow_table);
+        }
+
+        VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) {
+            build_in_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask,
+                                          m, ofpacts, flow_table);
         }
-        n_ps_addrs++;
     }
 
-    if (!n_ps_addrs) {
-        free(ps_addrs);
+    /* Input IPv4 flows. */
+    if (!vector_is_empty(&ps_addr->ip4)) {
+        VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) {
+            build_in_port_sec_ip4_flows(pb, maddr->addr, maddr->mask,
+                                        &ps_addr->ip4, m, ofpacts, flow_table);
+        }
+    }
+
+    /* Input ARP flows. */
+    if (!vector_is_empty(&ps_addr->ip4) || vector_is_empty(&ps_addr->ip6)) {
+        build_in_port_sec_arp_flows(pb, ps_addr->phys_addr, &ps_addr->ip4,
+                                    &ps_addr->vrrp4, true, m, ofpacts,
+                                    flow_table);
+    }
+
+    /* Input Ipv6 flows. */
+    if (!vector_is_empty(&ps_addr->ip6)) {
+        VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) {
+            build_in_port_sec_ip6_flows(pb, maddr->addr, maddr->mask,
+                                        &ps_addr->ip6, m, ofpacts, flow_table);
+        }
+    }
+
+    /* Input ND flows. */
+    build_in_port_sec_nd_flows(pb, ps_addr->phys_addr, &ps_addr->ip6,
+                               &ps_addr->vrrp6, true, m, ofpacts,
+                               flow_table);
+
+    /* Output no-ip flows. */
+    VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) {
+        build_out_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask,
+                                       m, ofpacts, flow_table);
+    }
+
+    VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) {
+        build_out_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask,
+                                       m, ofpacts, flow_table);
+    }
+
+    /* Output IPv4 flows. */
+    if (!vector_is_empty(&ps_addr->ip4) || !vector_is_empty(&ps_addr->ip6)) {
+        VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) {
+            build_out_port_sec_ip4_flows(pb, maddr->addr, maddr->mask,
+                                         &ps_addr->ip4, m, ofpacts,
+                                         flow_table);
+        }
+    }
+
+    /* Output Ipv6 flows. */
+    if (!vector_is_empty(&ps_addr->ip4) || !vector_is_empty(&ps_addr->ip6)) {
+        VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) {
+            build_out_port_sec_ip6_flows(pb, maddr->addr, maddr->mask,
+                                         &ps_addr->ip6, m, ofpacts,
+                                         flow_table);
+        }
+    }
+}
+
+static bool
+port_security_addresses_parse_entry(const char *entry, const char *lsp,
+                                    struct port_security_addresses *ps_addr)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+    bool vrrpv3 = !strncmp(entry, "VRRPv3", 6);
+    int n = vrrpv3 ? 7 : 0;
+
+    if (!ovs_scan_len(entry, &n, ETH_ADDR_SCAN_FMT,
+                      ETH_ADDR_SCAN_ARGS(ps_addr->phys_addr))) {
+        VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security for LSP %s: "
+                     "No MAC address found", entry, lsp);
+        return false;
+    }
+
+    bool ok = true;
+
+    /* Only MAC address is provided. */
+    if (!entry[n]) {
+        goto vrrp_check;
+    }
+
+    char *save_ptr = NULL;
+    char *tokstr = xstrdup(entry + n);
+    for (char *token = strtok_r(tokstr, " ", &save_ptr);
+         token != NULL;
+         token = strtok_r(NULL, " ", &save_ptr)) {
+        struct eth_addr mac;
+        struct in6_addr ip;
+        unsigned int plen;
+
+        if (vrrpv3 && eth_addr_parse_masked(token, &mac, &plen)) {
+            if (!port_security_addresses_add_vrrp_mac(ps_addr, mac, plen)) {
+                VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security for"
+                             " LSP %s: Invalid VRRPv3 MAC", token, lsp);
+                ok = false;
+                break;
+            }
+        } else if (ip46_parse_cidr(token, &ip, &plen)) {
+            if (!port_security_addresses_add_ip(ps_addr, ip, plen)) {
+                VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security for"
+                             " LSP %s: Invalid IP", token, lsp);
+                ok = false;
+                break;
+            }
+        } else {
+            VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security for"
+                         " LSP %s: Invalid IP or MAC", token, lsp);
+            ok = false;
+            break;
+        }
+    }
+
+    free(tokstr);
+
+vrrp_check:
+    if (vrrpv3 && vector_is_empty(&ps_addr->vrrp4) &&
+        vector_is_empty(&ps_addr->vrrp6)) {
+        vector_push(&ps_addr->vrrp4, &maddr_any_vrrp4);
+        vector_push(&ps_addr->vrrp6, &maddr_any_vrrp6);
+    }
+
+    return ok;
+}
+
+static void
+consider_port_sec_flows(const struct sbrec_port_binding *pb,
+                        struct ovn_desired_flow_table *flow_table)
+{
+    if (!pb->n_port_security) {
         return;
     }
 
@@ -3037,48 +3337,34 @@ consider_port_sec_flows(const struct sbrec_port_binding 
*pb,
     uint64_t stub[1024 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
 
-    build_in_port_sec_default_flows(pb, &match, &ofpacts, flow_table);
+    bool flows_installed = false;
+    struct port_security_addresses ps_addr;
+    port_security_addresses_init(&ps_addr);
 
-    for (size_t i = 0; i < n_ps_addrs; i++) {
-        build_in_port_sec_no_ip_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                      flow_table);
-        build_in_port_sec_ip4_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                    flow_table);
-        build_in_port_sec_arp_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                    flow_table);
-        build_in_port_sec_ip6_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                    flow_table);
-        build_in_port_sec_nd_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                   flow_table);
-    }
+    for (size_t i = 0; i < pb->n_port_security; i++) {
+        if (port_security_addresses_parse_entry(pb->port_security[i],
+                                                pb->logical_port, &ps_addr)) {
+            if (vector_is_empty(&ps_addr.vrrp4) &&
+                vector_is_empty(&ps_addr.vrrp6)) {
+                build_port_sec_entry_flows(pb, &ps_addr, &match,
+                                           &ofpacts, flow_table);
 
-    /* Out port security. */
+            } else {
+                build_port_sec_entry_vrrp_flows(pb, &ps_addr, &match,
+                                                &ofpacts, flow_table);
+            }
 
-    /* Add the below logical flow equivalent OF rules in 'out_port_sec_nd'
-     * table.
-     * priority: 80
-     * match - "outport == pb->logical_port"
-     * action - "port_sec_failed = 1;"
-     * descrption: "Drop all traffic"
-     */
-    reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, &match);
-    build_port_sec_deny_action(&ofpacts);
-    ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 80,
-                    pb->header_.uuid.parts[0], &match, &ofpacts,
-                    &pb->header_.uuid);
+            flows_installed = true;
+        }
 
-    for (size_t i = 0; i < n_ps_addrs; i++) {
-        build_out_port_sec_no_ip_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                       flow_table);
-        build_out_port_sec_ip4_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                       flow_table);
-        build_out_port_sec_ip6_flows(pb, &ps_addrs[i], &match, &ofpacts,
-                                       flow_table);
+        port_security_addresses_clear(&ps_addr);
     }
 
-    ofpbuf_uninit(&ofpacts);
-    for (size_t i = 0; i < n_ps_addrs; i++) {
-        destroy_lport_addresses(&ps_addrs[i]);
+    if (flows_installed) {
+        build_in_port_sec_default_flows(pb, &match, &ofpacts, flow_table);
+        build_out_port_sec_default_flows(pb, &match, &ofpacts, flow_table);
     }
-    free(ps_addrs);
+
+    ofpbuf_uninit(&ofpacts);
+    port_security_addresses_destroy(&ps_addr);
 }
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 1acbf202b..a9c68e1a4 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2057,6 +2057,21 @@
           addresses within an element may be space or comma separated.
         </p>
 
+        <p>
+          This also supports the special prefix "VRRPv3" that allows
+          specification of single physical MAC and multiple VRRPv3 MAC
+          addresses. As for non VRRPv3 entries, multiple IP addresses can be
+          associated with the specified MACs. "VRRPv3" with a single
+          physical MAC translates to allowing traffic for the whole "VRRPv3"
+          range of MACs. See more in the examples.
+
+          When the port security configuration entry contains the VRRPv3 label,
+          the behavior for physical MAC (the first specified) is different.
+          The installed flows will allow traffic from to VRRP MACs + IPs.
+          The physical MAC is there to properly allow ARP/ND with given
+          VRRP MACs.
+        </p>
+
         <p>
           This column is provided as a convenience to cloud management systems,
           but all of the features that it implements can be implemented as ACLs
@@ -2102,6 +2117,46 @@
             255.255.255.255, and any address in 224.0.0.0/4.  The host may not
             send or receive any IPv6 (including IPv6 Neighbor Discovery) 
traffic.
           </dd>
+
+          <dt><code>"VRRPv3 &lt;PHYSICAL_MAC&gt;"</code></dt>
+          <dd>
+            The host may also send ARP/ND packets with physical
+            MAC as a source and the inner SHA/TLL/SLL all the VRRPv3
+            MACs, 00:00:5e:00:01:{VRID}/40 for IPv4 and
+            00:00:5E:00:02:{VRID}/40 for IPv6.
+          </dd>
+
+          <dt><code>"VRRPv3 &lt;PHYSICAL_MAC&gt;
+                    &lt;VRRPV3_MACv4_1&gt;/[&lt;MASK1&gt;]
+                    &lt;VRRPV3_MACv4_N&gt;
+                    &lt;VRRPV3_MACv6_1&gt;/[&lt;MASK2&gt;]
+                    &lt;VRRPV3_MACv6_N&gt;"</code></dt>
+          <dd>
+            The host may also send ARP/ND packets with physical
+            MAC as a source and the inner SHA/TLL/SLL all the VRRPv3
+            MACs, 00:00:5e:00:01:{VRID} for IPv4 and
+            00:00:5E:00:02:{VRID} for IPv6. The VRRPv3 MACs can be provided
+            with a mask with prefix between /40 and /48.
+          </dd>
+
+          <dt><code>"VRRPv3 &lt;PHYSICAL_MAC&gt;
+            &lt;VRRPV3_MACv4_1&gt;/[&lt;MASK1&gt;]
+            &lt;VRRPV3_MACv4_N&gt;
+            &lt;VRRPV3_MACv6_1&gt;/[&lt;MASK2&gt;]
+            &lt;VRRPV3_MACv6_N&gt;
+            &lt;VRRPV3_IPv4_1&gt;/[&lt;MASK1&gt;]
+            &lt;VRRPV3_IPv4_N&gt;
+            &lt;VRRPV3_IPv6_1&gt;/[&lt;MASK2&gt;]
+            &lt;VRRPV3_IPv6_N&gt;"</code></dt>
+          <dd>
+            The host may also send ARP/ND packets with physical
+            MAC as a source and the inner SHA/TLL/SLL all the VRRPv3
+            MACs, 00:00:5e:00:01:{VRID} for IPv4 and
+            00:00:5E:00:02:{VRID} for IPv6. The VRRPv3 MACs can be provided
+            with a mask with prefix between /40 and /48. The provided IP
+            addresses further restrict the inner IP for ARP/ND. As well
+            as traffic to/from given VRRPv3 MACs and the provided IPs.
+          </dd>
         </dl>
       </column>
     </group>
diff --git a/tests/ovn.at b/tests/ovn.at
index 961b6ecf7..28f53a491 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -44611,3 +44611,782 @@ check ovn-nbctl --wait=hv lsp-set-type down_ext 
localnet
 OVN_CLEANUP([hv1],[hv2])
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([Port security - VRRPv3 ARP/ND])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+ovn_start
+net_add n1
+sim_add hv1
+as hv1
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+
+check ovn-nbctl ls-add ls \
+    -- set logical_switch ls other-config:requested-tnl-key=1
+
+check ovn-nbctl lsp-add ls lsp1
+check ovn-nbctl lsp-set-addresses lsp1 "00:00:00:00:10:01 192.168.10.1 
fd10::1" \
+    -- set logical_switch_port lsp1 options:requested-tnl-key=1
+
+check ovn-nbctl lsp-add ls lsp2
+check ovn-nbctl lsp-set-addresses lsp2 "00:00:00:00:10:02 192.168.10.2 
fd10::2" \
+    -- set logical_switch_port lsp2 options:requested-tnl-key=2
+
+check ovs-vsctl -- add-port br-int vif1 -- \
+      set interface vif1 external-ids:iface-id=lsp1 \
+      options:tx_pcap=hv1/vif1-tx.pcap \
+      options:rxq_pcap=hv1/vif1-rx.pcap
+
+check ovs-vsctl -- add-port br-int vif2 -- \
+      set interface vif2 external-ids:iface-id=lsp2 \
+      options:tx_pcap=hv1/vif2-tx.pcap \
+      options:rxq_pcap=hv1/vif2-rx.pcap
+
+wait_for_ports_up
+
+test_arp() {
+    local dropped=$1
+
+    packet=$(fmt_pkt "
+        Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:01') /
+        ARP(op=1, hwsrc='00:00:5e:00:01:05', hwdst='ff:ff:ff:ff:ff:ff', 
psrc='192.168.10.1', pdst='192.168.10.2')
+    ")
+    check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
+
+    packet=$(fmt_pkt "
+            Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:01') /
+            ARP(op=2, hwsrc='00:00:5e:00:01:05', hwdst='ff:ff:ff:ff:ff:ff', 
psrc='192.168.10.1', pdst='192.168.10.1')
+    ")
+    check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
+
+    if [[ "$dropped" != "yes" ]]; then
+        echo $packet >> vif2.expected
+        packet=$(fmt_pkt "
+            Ether(dst='00:00:00:00:10:01', src='00:00:00:00:10:02') /
+            ARP(op=2, hwsrc='00:00:00:00:10:02', hwdst='00:00:5e:00:01:05', 
psrc='192.168.10.2', pdst='192.168.10.1')
+        ")
+        echo $packet >> vif1.expected
+    fi
+}
+test_nd() {
+    local dropped=$1
+
+    packet=$(fmt_pkt "
+        Ether(dst='33:33:ff:00:00:01', src='00:00:00:00:10:01') /
+        IPv6(src='fd10::1', dst='ff02::1:ff00:2') /
+        ICMPv6ND_NS(tgt='fd10::2') /
+        ICMPv6NDOptSrcLLAddr(lladdr='00:00:5e:00:02:05')
+    ")
+    check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
+
+    packet=$(fmt_pkt "
+        Ether(dst='33:33:ff:00:00:01', src='00:00:00:00:10:01') /
+        IPv6(src='fd10::1', dst='ff02::1:ff00:1') /
+        ICMPv6ND_NA(tgt='fd10::1') /
+        ICMPv6NDOptDstLLAddr(lladdr='00:00:5e:00:02:05')
+    ")
+    check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
+
+    if [[ "$dropped" != "yes" ]]; then
+        echo $packet >> vif2.expected
+
+        packet=$(fmt_pkt "
+                Ether(dst='00:00:00:00:10:01', src='00:00:00:00:10:02') /
+                IPv6(src='fd10::2', dst='fd10::1') /
+                ICMPv6ND_NA(tgt='fd10::2', R=0, S=1, O=1) /
+                ICMPv6NDOptDstLLAddr(lladdr='00:00:00:00:10:02')
+        ")
+        echo $packet >> vif1.expected
+    fi
+}
+
+reset_pcap_and_expected() {
+    reset_pcap_file vif1 hv1/vif1
+    reset_pcap_file vif2 hv1/vif2
+
+    : > vif1.expected
+    : > vif2.expected
+}
+
+AS_BOX([Without port security])
+reset_pcap_and_expected
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+
+AS_BOX([With MAC only port security])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01"
+
+test_arp yes
+test_nd yes
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC + IP port security])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 
192.168.10.1 fd10::1"
+
+test_arp yes
+test_nd yes
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+
+
+AS_BOX([With MAC only port security, VRRPv3=any])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" 
"VRRPv3 00:00:00:00:10:01"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00 
actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC + IP port security, VRRPv3=any])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 
192.168.10.1 fd10::1" "VRRPv3 00:00:00:00:10:01 192.168.10.1 fd10::1"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_src=fe80::200:5eff:fe00:200/120
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00
 actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00
 actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:00,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_dst=fe80::200:5eff:fe00:200/120
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:00,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC only port security, VRRPv3 valid IPv4])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" 
"VRRPv3 00:00:00:00:10:01 00:00:5e:00:01:05"
+
+test_arp no
+test_nd yes
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:05 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:5e:00:01:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05 
actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC + IP port security, VRRPv3 valid IPv4])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 
192.168.10.1 fd10::1" "VRRPv3 00:00:00:00:10:01 00:00:5e:00:01:05 192.168.10.1 
fd10::1"
+
+test_arp no
+test_nd yes
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:05,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:05,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:5e:00:01:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC only port security, VRRPv3 valid IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" 
"VRRPv3 00:00:00:00:10:01 00:00:5e:00:02:05"
+
+test_arp yes
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05 
actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC + IP port security, VRRPv3 valid IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 
192.168.10.1 fd10::1" "VRRPv3 00:00:00:00:10:01 00:00:5e:00:02:05 192.168.10.1 
fd10::1"
+
+test_arp yes
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=fe80::200:5eff:fe00:205
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05,ipv6_dst=fe80::200:5eff:fe00:205
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC only port security, VRRPv3 valid IPv4 + IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" 
"VRRPv3 00:00:00:00:10:01 00:00:5e:00:01:05 00:00:5e:00:02:05"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:05 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:5e:00:01:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05 
actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC + IP port security, VRRPv3 valid IPv4 + IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 
192.168.10.1 fd10::1" "VRRPv3 00:00:00:00:10:01 00:00:5e:00:01:05 
00:00:5e:00:02:05 192.168.10.1 fd10::1"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:05,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:05,ipv6_src=fe80::200:5eff:fe00:205
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:05,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:5e:00:01:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:5e:00:02:05
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:05,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05,ipv6_dst=fe80::200:5eff:fe00:205
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:05,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC only port security, VRRPv3 masked IPv4 + IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" 
"VRRPv3 00:00:00:00:10:01 00:00:5e:00:01:00/45 00:00:5e:00:02:00/45"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8 
actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC + IP port security, VRRPv3 masked IPv4 + IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 
192.168.10.1 fd10::1" "VRRPv3 00:00:00:00:10:01 00:00:5e:00:01:00/45 
00:00:5e:00:02:00/45 192.168.10.1 fd10::1"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_src=fe80::200:5eff:fe00:200/125
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8
 actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8
 actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:00/ff:ff:ff:ff:ff:f8,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_dst=fe80::200:5eff:fe00:200/125
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:00/ff:ff:ff:ff:ff:f8,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC + IP port security, VRRPv3 valid but different IPv4 + IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 
192.168.10.1 fd10::1" "VRRPv3 00:00:00:00:10:01 00:00:5e:00:01:01 
00:00:5e:00:02:01 192.168.10.1 fd10::1"
+
+test_arp yes
+test_nd yes
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:01,nw_src=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:01,ipv6_src=fd10::1
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:02:01,ipv6_src=fe80::200:5eff:fe00:201
 actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, 
priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:5e:00:01:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 
actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND)
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, priority=80,arp,reg14=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:5e:00:01:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:5e:00:02:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:5e:00:02:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_IN_PORT_SEC_ND, 
priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:5e:00:02:01
 actions=load:0->NXM_NX_REG10[[12]]
+])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl
+ table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:01 
actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:01 
actions=load:0x1->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:01,nw_dst=192.168.10.1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:01,nw_dst=224.0.0.0/4
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:01:01,nw_dst=255.255.255.255
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:01,ipv6_dst=fd10::1
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:01,ipv6_dst=fe80::200:5eff:fe00:201
 actions=load:0->NXM_NX_REG10[[12]]
+ table=OFTABLE_CHK_OUT_PORT_SEC, 
priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:5e:00:02:01,ipv6_dst=ff00::/8
 actions=load:0->NXM_NX_REG10[[12]]
+])
+
+AS_BOX([With MAC only port security, VRRPv3 invalid IPv4])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "VRRPv3 00:00:00:00:10:01 
00:00:5e:00:01:00"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+
+AS_BOX([With MAC only port security, VRRPv3 invalid IPv6])
+reset_pcap_and_expected
+check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "VRRPv3 00:00:00:00:10:01 
00:00:5e:00:02:00"
+
+test_arp no
+test_nd no
+
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected])
+
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | 
ofctl_strip_all | sort | grep -v NXST_FLOW], [1])
+
+OVN_CLEANUP([hv1
+/Invalid VRRPv3 MAC/d])
+AT_CLEANUP
+])
-- 
2.52.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to