The option activation-strategy supported only "rarp", add "garp" and
"na" to supported protocols. At the same time change to option to
accept comma separated list, which allows CMS to set any combination
of the supported protocols for activation.

Reported-at: https://issues.redhat.com/browse/FDP-1042
Signed-off-by: Ales Musil <amu...@redhat.com>
---
v2: Rebase on top of current main.
    Revert back the rarp match behavior.
    Adjust the action name.
    Address documentation nits.
---
 NEWS                  |   3 +
 controller/physical.c | 203 +++++++++++++++++++----
 controller/pinctrl.c  |   8 +-
 include/ovn/actions.h |   4 +-
 lib/actions.c         |   2 +-
 ovn-nb.xml            |  10 +-
 ovn-sb.xml            |  17 +-
 tests/ovn.at          | 362 ++++++++++++++++++++++--------------------
 8 files changed, 391 insertions(+), 218 deletions(-)

diff --git a/NEWS b/NEWS
index 656176d20..c1ee2fcb5 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,9 @@ Post v25.03.0
      external-ids, this option allows to specify if ovn-controller should
      perform cleanup when exiting. The "--restart" exit always has priority
      to keep the backward compatibility.
+   - Add additional protocol support for options:activation-strategy. The new
+    supported protocols are gARP and NA. The option now supports a comma
+    separated to specify selected group of protocols.
 
 OVN v25.03.0 - 07 Mar 2025
 --------------------------
diff --git a/controller/physical.c b/controller/physical.c
index 83c979d4c..435dede79 100644
--- a/controller/physical.c
+++ b/controller/physical.c
@@ -1228,33 +1228,146 @@ enum access_type {
     PORT_HA_REMOTE,
 };
 
+enum activation_strategy {
+    ACTIVATION_RARP = 1 << 0,
+    ACTIVATION_GARP = 1 << 1,
+    ACTIVATION_NA = 1 << 2,
+    ACTIVATION_IP4 = ACTIVATION_GARP | ACTIVATION_RARP,
+    ACTIVATION_IP6 = ACTIVATION_NA,
+};
+
 static void
 setup_rarp_activation_strategy(const struct sbrec_port_binding *binding,
                                ofp_port_t ofport, struct zone_ids *zone_ids,
+                               struct ofpbuf *ofpacts,
                                struct ovn_desired_flow_table *flow_table)
 {
     struct match match = MATCH_CATCHALL_INITIALIZER;
-    uint64_t stub[1024 / 8];
-    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
 
     /* Unblock the port on ingress RARP. */
     match_set_dl_type(&match, htons(ETH_TYPE_RARP));
     match_set_in_port(&match, ofport);
 
-    load_logical_ingress_metadata(binding, zone_ids, 0, NULL, &ofpacts, true);
+    load_logical_ingress_metadata(binding, zone_ids, 0, NULL, ofpacts, true);
+    encode_controller_op(ACTION_OPCODE_ACTIVATION_STRATEGY,
+                         NX_CTLR_NO_METER, ofpacts);
+    put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts);
 
-    encode_controller_op(ACTION_OPCODE_ACTIVATION_STRATEGY_RARP,
-                         NX_CTLR_NO_METER, &ofpacts);
+    ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1010,
+                    binding->header_.uuid.parts[0],
+                    &match, ofpacts, &binding->header_.uuid);
+    ofpbuf_clear(ofpacts);
+}
 
-    put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
+static void
+setup_arp_activation_strategy(const struct sbrec_port_binding *binding,
+                              ofp_port_t  ofport, struct zone_ids *zone_ids,
+                              struct ofpbuf *ofpacts, struct eth_addr mac,
+                              ovs_be32 ip,
+                              struct ovn_desired_flow_table *flow_table)
+{
+    struct match match = MATCH_CATCHALL_INITIALIZER;
+
+    /* match: arp/rarp, in_port=<OFPORT>, dl_src=<MAC>, arp_sha=<MAC>,
+     * arp_spa=<IP>, arp_tpa=<IP> */
+    match_set_dl_type(&match, htons(ETH_TYPE_ARP));
+    match_set_in_port(&match, ofport);
+    match_set_dl_src(&match, mac);
+    match_set_arp_sha(&match, mac);
+    match_set_arp_spa_masked(&match, ip, OVS_BE32_MAX);
+    match_set_arp_tpa_masked(&match, ip, OVS_BE32_MAX);
+
+    load_logical_ingress_metadata(binding, zone_ids, 0, NULL, ofpacts, true);
+    /* Unblock the traffic when it matches specified strategy. */
+    encode_controller_op(ACTION_OPCODE_ACTIVATION_STRATEGY,
+                         NX_CTLR_NO_METER, ofpacts);
+    put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts);
 
     ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1010,
                     binding->header_.uuid.parts[0],
-                    &match, &ofpacts, &binding->header_.uuid);
-    ofpbuf_clear(&ofpacts);
+                    &match, ofpacts, &binding->header_.uuid);
+    ofpbuf_clear(ofpacts);
+}
+
+static void
+setup_nd_na_activation_strategy(const struct sbrec_port_binding *binding,
+                                ofp_port_t  ofport, struct zone_ids *zone_ids,
+                                struct ofpbuf *ofpacts, struct eth_addr mac,
+                                const struct in6_addr *ip,
+                                struct ovn_desired_flow_table *flow_table)
+{
+    struct match match = MATCH_CATCHALL_INITIALIZER;
+
+    /* match: icmp6, in_port=<OFPORT>, icmp_type=136, icmp_code=0,
+     * ipv6_src=<IP>, nd_target=<IP>, nd_tll=<MAC> */
+    match_set_dl_type(&match, htons(ETH_TYPE_IPV6));
+    match_set_in_port(&match, ofport);
+    match_set_nw_proto(&match, IPPROTO_ICMPV6);
+    match_set_icmp_type(&match, 136);
+    match_set_icmp_code(&match, 0);
+    match_set_ipv6_src(&match, ip);
+    match_set_nd_target(&match, ip);
+    match_set_arp_tha(&match, mac);
+
+    load_logical_ingress_metadata(binding, zone_ids, 0, NULL, ofpacts, true);
+    /* Unblock the traffic when it matches specified strategy. */
+    encode_controller_op(ACTION_OPCODE_ACTIVATION_STRATEGY,
+                         NX_CTLR_NO_METER, ofpacts);
+    put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts);
+
+    ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1010,
+                    binding->header_.uuid.parts[0],
+                    &match, ofpacts, &binding->header_.uuid);
+    ofpbuf_clear(ofpacts);
+}
+
+static void
+setup_activation_strategy_flows(const struct sbrec_port_binding *binding,
+                                ofp_port_t ofport, struct zone_ids *zone_ids,
+                                uint32_t activation_strategies,
+                                const struct lport_addresses *addresses,
+                                struct ovn_desired_flow_table *flow_table)
+{
+    uint64_t stub[1024 / 8];
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
+
+    bool ip4_activation = (activation_strategies & ACTIVATION_IP4) != 0;
+    bool ip6_activation = (activation_strategies & ACTIVATION_IP6) != 0;
+
+    if (activation_strategies & ACTIVATION_RARP) {
+        setup_rarp_activation_strategy(binding, ofport, zone_ids, &ofpacts,
+                                       flow_table);
+    }
+
+    for (size_t i = 0; ip4_activation && i < addresses->n_ipv4_addrs; i++) {
+        const struct ipv4_netaddr *address = &addresses->ipv4_addrs[i];
+        if (activation_strategies & ACTIVATION_GARP) {
+            setup_arp_activation_strategy(binding, ofport, zone_ids, &ofpacts,
+                                          addresses->ea, address->addr,
+                                          flow_table);
+        }
+    }
+
+    /* Always add LLA address activation if there is any IPv6 address. */
+    if (ip6_activation && addresses->n_ipv6_addrs) {
+        struct in6_addr lla;
+        in6_generate_lla(addresses->ea, &lla);
+
+        setup_nd_na_activation_strategy(binding, ofport, zone_ids, &ofpacts,
+                                        addresses->ea, &lla, flow_table);
+    }
+
+    for (size_t i = 0; ip6_activation && i < addresses->n_ipv6_addrs; i++) {
+        const struct ipv6_netaddr *address = &addresses->ipv6_addrs[i];
+        if (activation_strategies & ACTIVATION_NA) {
+            setup_nd_na_activation_strategy(binding, ofport, zone_ids,
+                                            &ofpacts, addresses->ea,
+                                            &address->addr, flow_table);
+        }
+    }
 
     /* Block all non-RARP traffic for the port, both directions. */
-    match_init_catchall(&match);
+    struct match match = MATCH_CATCHALL_INITIALIZER;
     match_set_in_port(&match, ofport);
 
     ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1000,
@@ -1274,6 +1387,36 @@ setup_rarp_activation_strategy(const struct 
sbrec_port_binding *binding,
     ofpbuf_uninit(&ofpacts);
 }
 
+static uint32_t
+pb_parse_activation_strategy(const struct sbrec_port_binding *pb)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+    uint32_t strategies = 0;
+
+    const char *strategy = smap_get(&pb->options, "activation-strategy");
+    if (strategy) {
+        char *save_ptr;
+        char *tokstr = xstrdup(strategy);
+        for (const char *name = strtok_r(tokstr, ",", &save_ptr);
+             name != NULL;
+             name = strtok_r(NULL, ",", &save_ptr)) {
+            if (!strcmp(name, "rarp")) {
+                strategies |= ACTIVATION_RARP;
+            } else if (!strcmp(name, "garp")) {
+                strategies |= ACTIVATION_GARP;
+            } else if (!strcmp(name, "na")) {
+                strategies |= ACTIVATION_NA;
+            } else {
+                VLOG_WARN_RL(&rl, "Unknown activation strategy defined for "
+                             "port %s: %s", pb->logical_port, name);
+            }
+        }
+        free(tokstr);
+    }
+
+    return strategies;
+}
+
 static void
 setup_activation_strategy(const struct sbrec_port_binding *binding,
                           const struct sbrec_chassis *chassis,
@@ -1281,28 +1424,28 @@ setup_activation_strategy(const struct 
sbrec_port_binding *binding,
                           ofp_port_t ofport, struct zone_ids *zone_ids,
                           struct ovn_desired_flow_table *flow_table)
 {
-    for (size_t i = 0; i < binding->n_additional_chassis; i++) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        if (binding->additional_chassis[i] == chassis) {
-            const char *strategy = smap_get(&binding->options,
-                                            "activation-strategy");
-            if (strategy
-                    && !lport_is_activated_by_activation_strategy(binding,
-                                                                  chassis)
-                    && !pinctrl_is_port_activated(dp_key, port_key)) {
-                if (!strcmp(strategy, "rarp")) {
-                    setup_rarp_activation_strategy(binding, ofport,
-                                                   zone_ids, flow_table);
-                } else {
-                    VLOG_WARN_RL(&rl,
-                                 "Unknown activation strategy defined for "
-                                 "port %s: %s",
-                                 binding->logical_port, strategy);
-                    return;
-                }
-            }
-            return;
+    if (!is_additional_chassis(binding, chassis)) {
+        return;
+    }
+
+    if (lport_is_activated_by_activation_strategy(binding, chassis) ||
+        pinctrl_is_port_activated(dp_key, port_key)) {
+        return;
+    }
+
+    uint32_t strategies = pb_parse_activation_strategy(binding);
+    if (!strategies) {
+        return;
+    }
+
+    for (size_t i = 0; i < binding->n_mac; i++) {
+        struct lport_addresses addresses;
+        if (!extract_lsp_addresses(binding->mac[i], &addresses)) {
+            continue;
         }
+        setup_activation_strategy_flows(binding, ofport, zone_ids, strategies,
+                                        &addresses, flow_table);
+        destroy_lport_addresses(&addresses);
     }
 }
 
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 1481b2547..bdb619b4d 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -211,7 +211,7 @@ static void wait_put_mac_bindings(struct ovsdb_idl_txn 
*ovnsb_idl_txn);
 static void send_mac_binding_buffered_pkts(struct rconn *swconn)
     OVS_REQUIRES(pinctrl_mutex);
 
-static void pinctrl_rarp_activation_strategy_handler(const struct match *md);
+static void pinctrl_activation_strategy_handler(const struct match *md);
 
 static void pinctrl_split_buf_action_handler(
         struct rconn *swconn, struct dp_packet *pkt,
@@ -3802,9 +3802,9 @@ process_packet_in(struct rconn *swconn, const struct 
ofp_header *msg)
         ovs_mutex_unlock(&pinctrl_mutex);
         break;
 
-    case ACTION_OPCODE_ACTIVATION_STRATEGY_RARP:
+    case ACTION_OPCODE_ACTIVATION_STRATEGY:
         ovs_mutex_lock(&pinctrl_mutex);
-        pinctrl_rarp_activation_strategy_handler(&pin.flow_metadata);
+        pinctrl_activation_strategy_handler(&pin.flow_metadata);
         ovs_mutex_unlock(&pinctrl_mutex);
         break;
 
@@ -9014,7 +9014,7 @@ tag_port_as_activated_in_engine(struct activated_port 
*ap) {
 }
 
 static void
-pinctrl_rarp_activation_strategy_handler(const struct match *md)
+pinctrl_activation_strategy_handler(const struct match *md)
     OVS_REQUIRES(pinctrl_mutex)
 {
     /* Tag the port as activated in-memory. */
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 90366b649..b555e9a51 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -788,8 +788,8 @@ OVNACTS
     /* put_fdb(inport, eth.src). */                                           \
     ACTION_OPCODE(PUT_FDB)                                                    \
                                                                               \
-    /* activation_strategy_rarp() */                                          \
-    ACTION_OPCODE(ACTIVATION_STRATEGY_RARP)                                   \
+    /* activation_strategy() */                                          \
+    ACTION_OPCODE(ACTIVATION_STRATEGY)                                   \
                                                                               \
     /* split buffer action. */                                                \
     ACTION_OPCODE(SPLIT_BUF_ACTION)                                           \
diff --git a/lib/actions.c b/lib/actions.c
index 158c367ae..003b1e060 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1967,7 +1967,7 @@ is_paused_nested_action(enum action_opcode opcode)
     case ACTION_OPCODE_DHCP6_SERVER:
     case ACTION_OPCODE_HANDLE_SVC_CHECK:
     case ACTION_OPCODE_BFD_MSG:
-    case ACTION_OPCODE_ACTIVATION_STRATEGY_RARP:
+    case ACTION_OPCODE_ACTIVATION_STRATEGY:
     case ACTION_OPCODE_SPLIT_BUF_ACTION:
     case ACTION_OPCODE_DHCP_RELAY_REQ_CHK:
     case ACTION_OPCODE_DHCP_RELAY_RESP_CHK:
diff --git a/ovn-nb.xml b/ovn-nb.xml
index ff5f2f249..98821a364 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -1360,10 +1360,12 @@
           <ref column="requested-chassis"/>, specifies an activation strategy
           for all additional chassis. By default, no activation strategy is
           used, meaning additional port locations are immediately available for
-          use. When set to "rarp", the port is blocked for ingress and egress
-          communication until a RARP packet is sent from a new location. The
-          "rarp" strategy is useful in live migration scenarios for virtual
-          machines.
+          use. The option supports a comma separated list where you can combine
+          3 protocols, "rarp", "garp" and "na". When any of the protocols is
+          set, the port is blocked for ingress and egress communication until
+          a specified protocol packet is sent from a new location. The
+          activation strategy is useful in live migration scenarios for
+          virtual machines.
         </column>
 
         <column name="options" key="iface-id-ver">
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 39acb81a4..c1247151d 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -3784,13 +3784,16 @@ tcp.flags = RST;
       </column>
 
       <column name="options" key="activation-strategy">
-        If used with multiple chassis set in <ref column="requested-chassis"/>,
-        specifies an activation strategy for all additional chassis. By
-        default, no activation strategy is used, meaning additional port
-        locations are immediately available for use. When set to "rarp", the
-        port is blocked for ingress and egress communication until a RARP
-        packet is sent from a new location. The "rarp" strategy is useful
-        in live migration scenarios for virtual machines.
+        If used with multiple chassis set in
+        <ref column="requested-chassis"/>, specifies an activation strategy
+        for all additional chassis. By default, no activation strategy is
+        used, meaning additional port locations are immediately available for
+        use. The option supports a comma separated list where you can combine
+        3 protocols, "rarp", "garp" and "na". When any of the protocols is
+        set, the port is blocked for ingress and egress communication until
+        a specified protocol packet is sent from a new location. The
+        activation strategy is useful in live migration scenarios for
+        virtual machines.
       </column>
 
       <column name="options" key="additional-chassis-activated">
diff --git a/tests/ovn.at b/tests/ovn.at
index afde2576f..435974b5a 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -16521,211 +16521,233 @@ MULTICHASSIS_PATH_MTU_DISCOVERY_TEST([ipv6], 
[geneve], [1404])
 MULTICHASSIS_PATH_MTU_DISCOVERY_TEST([ipv4], [vxlan], [1432])
 MULTICHASSIS_PATH_MTU_DISCOVERY_TEST([ipv6], [vxlan], [1412])
 
-OVN_FOR_EACH_NORTHD([
-AT_SETUP([options:activation-strategy for logical port])
-AT_KEYWORDS([multi-chassis])
-AT_KEYWORDS([slowtest])
-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.11
-
-sim_add hv2
-as hv2
-check ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.12
-
-sim_add hv3
-as hv3
-check ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.13
-
-# Disable local ARP responder to pass ARP requests through tunnels
-check ovn-nbctl ls-add ls0 -- add Logical_Switch ls0 other_config 
vlan-passthru=true
-
-check ovn-nbctl lsp-add ls0 migrator
-check ovn-nbctl lsp-set-options migrator requested-chassis=hv1,hv2 \
-                                         activation-strategy=rarp
-
-check ovn-nbctl lsp-add ls0 first
-check ovn-nbctl lsp-set-options first requested-chassis=hv1
-check ovn-nbctl lsp-add ls0 second
-check ovn-nbctl lsp-set-options second requested-chassis=hv2
-check ovn-nbctl lsp-add ls0 outside
-check ovn-nbctl lsp-set-options outside requested-chassis=hv3
-
-check ovn-nbctl lsp-set-addresses migrator "00:00:00:00:00:10 10.0.0.10"
-check ovn-nbctl lsp-set-addresses first "00:00:00:00:00:01 10.0.0.1"
-check ovn-nbctl lsp-set-addresses second "00:00:00:00:00:02 10.0.0.2"
-check ovn-nbctl lsp-set-addresses outside "00:00:00:00:00:03 10.0.0.3"
-
-for hv in hv1 hv2; do
-    as $hv check ovs-vsctl -- add-port br-int migrator -- \
-        set Interface migrator external-ids:iface-id=migrator \
-                               options:tx_pcap=$hv/migrator-tx.pcap \
-                               options:rxq_pcap=$hv/migrator-rx.pcap
-done
-
-as hv1 check ovs-vsctl -- add-port br-int first -- \
-    set Interface first external-ids:iface-id=first
-as hv2 check ovs-vsctl -- add-port br-int second -- \
-    set Interface second external-ids:iface-id=second
-as hv3 check ovs-vsctl -- add-port br-int outside -- \
-    set Interface outside external-ids:iface-id=outside
-
-for hv in hv1 hv2 hv3; do
-    wait_row_count Chassis 1 name=$hv
-done
-hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
-hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
-hv3_uuid=$(fetch_column Chassis _uuid name=hv3)
-
-wait_column "$hv1_uuid" Port_Binding chassis logical_port=migrator
-wait_column "$hv1_uuid" Port_Binding requested_chassis logical_port=migrator
-wait_column "$hv2_uuid" Port_Binding additional_chassis logical_port=migrator
-wait_column "$hv2_uuid" Port_Binding requested_additional_chassis 
logical_port=migrator
+m4_define([ACTIVATION_STRATEGY_TEST],
+  [OVN_FOR_EACH_NORTHD([
+    AT_SETUP([options:activation-strategy=$1 for logical port])
+    AT_KEYWORDS([multi-chassis])
+    AT_KEYWORDS([slowtest])
+    AT_SKIP_IF([test $HAVE_SCAPY = no])
+    ovn_start
 
-wait_column "$hv1_uuid" Port_Binding chassis logical_port=first
-wait_column "$hv2_uuid" Port_Binding chassis logical_port=second
-wait_column "$hv3_uuid" Port_Binding chassis logical_port=outside
+    net_add n1
 
-OVN_POPULATE_ARP
+    sim_add hv1
+    as hv1
+    check ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.0.11
 
-send_arp() {
-    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
-    local 
request=${eth_dst}${eth_src}08060001080006040001${eth_src}${spa}${eth_dst}${tpa}
-    as ${hv} ovs-appctl netdev-dummy/receive $inport $request
-    echo "${request}"
-}
+    sim_add hv2
+    as hv2
+    check ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.0.12
 
-send_rarp() {
-    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
-    local 
request=${eth_dst}${eth_src}80350001080006040001${eth_src}${spa}${eth_dst}${tpa}
-    as ${hv} ovs-appctl netdev-dummy/receive $inport $request
-    echo "${request}"
-}
+    sim_add hv3
+    as hv3
+    check ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.0.13
+
+    # Disable local ARP responder to pass ARP requests through tunnels
+    check ovn-nbctl ls-add ls0 -- add Logical_Switch ls0 other_config 
vlan-passthru=true
+
+    check ovn-nbctl lsp-add ls0 migrator
+    check ovn-nbctl lsp-set-options migrator requested-chassis=hv1,hv2 \
+                                             activation-strategy=$1
+
+    check ovn-nbctl lsp-add ls0 first
+    check ovn-nbctl lsp-set-options first requested-chassis=hv1
+    check ovn-nbctl lsp-add ls0 second
+    check ovn-nbctl lsp-set-options second requested-chassis=hv2
+    check ovn-nbctl lsp-add ls0 outside
+    check ovn-nbctl lsp-set-options outside requested-chassis=hv3
+
+    check ovn-nbctl lsp-set-addresses migrator "00:00:00:00:00:10 10.0.0.10 
fd10::10"
+    check ovn-nbctl lsp-set-addresses first "00:00:00:00:00:01 10.0.0.1"
+    check ovn-nbctl lsp-set-addresses second "00:00:00:00:00:02 10.0.0.2"
+    check ovn-nbctl lsp-set-addresses outside "00:00:00:00:00:03 10.0.0.3"
+
+    for hv in hv1 hv2; do
+        as $hv check ovs-vsctl -- add-port br-int migrator -- \
+            set Interface migrator external-ids:iface-id=migrator \
+                                   options:tx_pcap=$hv/migrator-tx.pcap \
+                                   options:rxq_pcap=$hv/migrator-rx.pcap
+    done
 
-reset_env() {
-    as hv1 reset_pcap_file migrator hv1/migrator
-    as hv2 reset_pcap_file migrator hv2/migrator
-    as hv1 reset_pcap_file first hv1/first
-    as hv2 reset_pcap_file second hv2/second
-    as hv3 reset_pcap_file outside hv3/outside
+    as hv1 check ovs-vsctl -- add-port br-int first -- \
+        set Interface first external-ids:iface-id=first
+    as hv2 check ovs-vsctl -- add-port br-int second -- \
+        set Interface second external-ids:iface-id=second
+    as hv3 check ovs-vsctl -- add-port br-int outside -- \
+        set Interface outside external-ids:iface-id=outside
 
-    for port in hv1/migrator hv2/migrator hv1/first hv2/second hv3/outside; do
-        : > $port.expected
+    for hv in hv1 hv2 hv3; do
+        wait_row_count Chassis 1 name=$hv
     done
-}
+    hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
+    hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
+    hv3_uuid=$(fetch_column Chassis _uuid name=hv3)
+
+    wait_column "$hv1_uuid" Port_Binding chassis logical_port=migrator
+    wait_column "$hv1_uuid" Port_Binding requested_chassis 
logical_port=migrator
+    wait_column "$hv2_uuid" Port_Binding additional_chassis 
logical_port=migrator
+    wait_column "$hv2_uuid" Port_Binding requested_additional_chassis 
logical_port=migrator
+
+    wait_column "$hv1_uuid" Port_Binding chassis logical_port=first
+    wait_column "$hv2_uuid" Port_Binding chassis logical_port=second
+    wait_column "$hv3_uuid" Port_Binding chassis logical_port=outside
+
+    OVN_POPULATE_ARP
+
+    send_arp() {
+        local hv=${1} inport=${2} op=${3} eth_src=${4} eth_dst=${5} spa=${6} 
tpa=${7}
+        local request=$(fmt_pkt "Ether(dst='${eth_dst}', src='${eth_src}')/ \
+                        ARP(op=${op}, hwsrc='${eth_src}', hwdst='${eth_dst}', \
+                        psrc='${spa}', pdst='${tpa}')")
+        as ${hv} ovs-appctl netdev-dummy/receive $inport $request
+        echo "${request}"
+    }
+
+    send_rarp() {
+        local hv=${1} inport=${2} eth_src=${3} spa=${4}
+        local request=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', 
src='${eth_src}', type=0x8035)/ \
+                        ARP(op=1, hwsrc='${eth_src}', 
hwdst='00:00:00:00:00:00', \
+                        psrc='${spa}', pdst='${spa}')")
+        as ${hv} ovs-appctl netdev-dummy/receive $inport $request
+        echo "${request}"
+    }
+
+    send_na() {
+        local hv=${1} inport=${2} eth_src=${3} spa=${4}
+        local request=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', 
src='${eth_src}')/ \
+                                 IPv6(dst='ff02::1', src='${spa}')/ \
+                                 ICMPv6ND_NA(tgt='${spa}')/ \
+                                 ICMPv6NDOptDstLLAddr(lladdr='${eth_src}')")
+        as ${hv} ovs-appctl netdev-dummy/receive $inport $request
+        echo "${request}"
+    }
+
+
+    reset_env() {
+        as hv1 reset_pcap_file migrator hv1/migrator
+        as hv2 reset_pcap_file migrator hv2/migrator
+        as hv1 reset_pcap_file first hv1/first
+        as hv2 reset_pcap_file second hv2/second
+        as hv3 reset_pcap_file outside hv3/outside
+
+        for port in hv1/migrator hv2/migrator hv1/first hv2/second 
hv3/outside; do
+            : > $port.expected
+        done
+    }
 
-check_packets() {
-    OVN_CHECK_PACKETS([hv1/migrator-tx.pcap], [hv1/migrator.expected])
-    OVN_CHECK_PACKETS([hv2/migrator-tx.pcap], [hv2/migrator.expected])
-    OVN_CHECK_PACKETS([hv3/outside-tx.pcap], [hv3/outside.expected])
-    OVN_CHECK_PACKETS([hv1/first-tx.pcap], [hv1/first.expected])
-    OVN_CHECK_PACKETS([hv2/second-tx.pcap], [hv2/second.expected])
-}
+    check_packets() {
+        OVN_CHECK_PACKETS([hv1/migrator-tx.pcap], [hv1/migrator.expected])
+        OVN_CHECK_PACKETS([hv2/migrator-tx.pcap], [hv2/migrator.expected])
+        OVN_CHECK_PACKETS([hv3/outside-tx.pcap], [hv3/outside.expected])
+        OVN_CHECK_PACKETS([hv1/first-tx.pcap], [hv1/first.expected])
+        OVN_CHECK_PACKETS([hv2/second-tx.pcap], [hv2/second.expected])
+    }
 
-migrator_spa=$(ip_to_hex 10 0 0 10)
-first_spa=$(ip_to_hex 10 0 0 1)
-second_spa=$(ip_to_hex 10 0 0 2)
-outside_spa=$(ip_to_hex 10 0 0 3)
+    reset_env
 
-reset_env
+    # Packet from hv3:Outside arrives to hv1:Migrator
+    # hv3:Outside cannot reach hv2:Migrator because it is blocked by 
activation strategy
+    request=$(send_arp hv3 outside 1 "00:00:00:00:00:03" "00:00:00:00:00:10" 
"10.0.0.3" "10.0.0.10")
+    echo $request >> hv1/migrator.expected
 
-# Packet from hv3:Outside arrives to hv1:Migrator
-# hv3:Outside cannot reach hv2:Migrator because it is blocked by RARP strategy
-request=$(send_arp hv3 outside 000000000003 000000000010 $outside_spa 
$migrator_spa)
-echo $request >> hv1/migrator.expected
+    # Packet from hv1:First arrives to hv1:Migrator
+    # hv1:First cannot reach hv2:Migrator because it is blocked by activation 
strategy
+    request=$(send_arp hv1 first 1 "00:00:00:00:00:01" "00:00:00:00:00:10" 
"10.0.0.1" "10.0.0.10")
+    echo $request >> hv1/migrator.expected
 
-# Packet from hv1:First arrives to hv1:Migrator
-# hv1:First cannot reach hv2:Migrator because it is blocked by RARP strategy
-request=$(send_arp hv1 first 000000000001 000000000010 $first_spa 
$migrator_spa)
-echo $request >> hv1/migrator.expected
+    # Packet from hv2:Second arrives to hv1:Migrator
+    # hv2:Second cannot reach hv2:Migrator because it is blocked by activation 
strategy
+    request=$(send_arp hv2 second 1 "00:00:00:00:00:02" "00:00:00:00:00:10" 
"10.0.0.2" "10.0.0.10")
+    echo $request >> hv1/migrator.expected
 
-# Packet from hv2:Second arrives to hv1:Migrator
-# hv2:Second cannot reach hv2:Migrator because it is blocked by RARP strategy
-request=$(send_arp hv2 second 000000000002 000000000010 $second_spa 
$migrator_spa)
-echo $request >> hv1/migrator.expected
+    check_packets
+    reset_env
 
-check_packets
-reset_env
+    # Packet from hv1:Migrator arrives to hv3:Outside
+    request=$(send_arp hv1 migrator 1 "00:00:00:00:00:10" "00:00:00:00:00:03" 
"10.0.0.10" "10.0.0.3")
+    echo $request >> hv3/outside.expected
 
-# Packet from hv1:Migrator arrives to hv3:Outside
-request=$(send_arp hv1 migrator 000000000010 000000000003 $migrator_spa 
$outside_spa)
-echo $request >> hv3/outside.expected
+    # Packet from hv1:Migrator arrives to hv1:First
+    request=$(send_arp hv1 migrator 1 "00:00:00:00:00:10" "00:00:00:00:00:01" 
"10.0.0.10" "10.0.0.1")
+    echo $request >> hv1/first.expected
 
-# Packet from hv1:Migrator arrives to hv1:First
-request=$(send_arp hv1 migrator 000000000010 000000000001 $migrator_spa 
$first_spa)
-echo $request >> hv1/first.expected
+    # Packet from hv1:Migrator arrives to hv2:Second
+    request=$(send_arp hv1 migrator 1 "00:00:00:00:00:10" "00:00:00:00:00:02" 
"10.0.0.10" "10.0.0.2")
+    echo $request >> hv2/second.expected
 
-# Packet from hv1:Migrator arrives to hv2:Second
-request=$(send_arp hv1 migrator 000000000010 000000000002 $migrator_spa 
$second_spa)
-echo $request >> hv2/second.expected
+    check_packets
+    reset_env
 
-check_packets
-reset_env
+    # hv2:Migrator cannot reach to hv3:Outside because it is blocked by 
activation strategy
+    request=$(send_arp hv2 migrator 1 "00:00:00:00:00:10" "00:00:00:00:00:03" 
"10.0.0.10" "10.0.0.3")
 
-# hv2:Migrator cannot reach to hv3:Outside because it is blocked by RARP 
strategy
-request=$(send_arp hv2 migrator 000000000010 000000000003 $migrator_spa 
$outside_spa)
+    check_packets
+    reset_env
 
-check_packets
-reset_env
+    AT_CHECK([ovn-sbctl find port_binding logical_port=migrator | grep -q 
additional-chassis-activated], [1])
 
-AT_CHECK([ovn-sbctl find port_binding logical_port=migrator | grep -q 
additional-chassis-activated], [1])
+    # Now activate hv2:Migrator location
+    if [[ "$1" == "rarp" ]]; then
+        request=$(send_rarp hv2 migrator "00:00:00:00:00:10" "10.0.0.10")
+    elif [[ "$1" == "na" ]]; then
+        request=$(send_na hv2 migrator "00:00:00:00:00:10" "fd10::10")
+    else
+        request=$(send_arp hv2 migrator 1 "00:00:00:00:00:10" 
"ff:ff:ff:ff:ff:ff" "10.0.0.10" "10.0.0.10")
+    fi
 
-# Now activate hv2:Migrator location
-request=$(send_rarp hv2 migrator 000000000010 ffffffffffff $migrator_spa 
$migrator_spa)
+    # RARP was reinjected into the pipeline
+    echo $request >> hv3/outside.expected
+    echo $request >> hv1/first.expected
+    echo $request >> hv2/second.expected
 
-# RARP was reinjected into the pipeline
-echo $request >> hv3/outside.expected
-echo $request >> hv1/first.expected
-echo $request >> hv2/second.expected
+    check_packets
+    reset_env
 
-check_packets
-reset_env
+    pb_uuid=$(ovn-sbctl --bare --columns _uuid find Port_Binding 
logical_port=migrator)
+    OVS_WAIT_UNTIL([test xhv2 = x$(ovn-sbctl get Port_Binding $pb_uuid 
options:additional-chassis-activated | tr -d '""')])
 
-pb_uuid=$(ovn-sbctl --bare --columns _uuid find Port_Binding 
logical_port=migrator)
-OVS_WAIT_UNTIL([test xhv2 = x$(ovn-sbctl get Port_Binding $pb_uuid 
options:additional-chassis-activated | tr -d '""')])
+    # Now packet arrives to both locations
+    request=$(send_arp hv3 outside 1 "00:00:00:00:00:03" "00:00:00:00:00:10" 
"10.0.0.3" "10.0.0.10")
+    echo $request >> hv1/migrator.expected
+    echo $request >> hv2/migrator.expected
 
-# Now packet arrives to both locations
-request=$(send_arp hv3 outside 000000000003 000000000010 $outside_spa 
$migrator_spa)
-echo $request >> hv1/migrator.expected
-echo $request >> hv2/migrator.expected
+    check_packets
+    reset_env
 
-check_packets
-reset_env
+    # Packet from hv1:Migrator still arrives to hv3:Outside
+    request=$(send_arp hv1 migrator 1 "00:00:00:00:00:10" "00:00:00:00:00:03" 
"10.0.0.10" "10.0.0.3")
+    echo $request >> hv3/outside.expected
 
-# Packet from hv1:Migrator still arrives to hv3:Outside
-request=$(send_arp hv1 migrator 000000000010 000000000003 $migrator_spa 
$outside_spa)
-echo $request >> hv3/outside.expected
+    check_packets
+    reset_env
 
-check_packets
-reset_env
+    # hv2:Migrator can now reach to hv3:Outside because it was activated
+    request=$(send_arp hv2 migrator 1 "00:00:00:00:00:10" "00:00:00:00:00:03" 
"10.0.0.10" "10.0.0.3")
+    echo $request >> hv3/outside.expected
 
-# hv2:Migrator can now reach to hv3:Outside because RARP strategy activated it
-request=$(send_arp hv2 migrator 000000000010 000000000003 $migrator_spa 
$outside_spa)
-echo $request >> hv3/outside.expected
+    check_packets
 
-check_packets
+    # complete port migration and check that -activated flag is reset
+    check ovn-nbctl lsp-set-options migrator requested-chassis=hv2
+    OVS_WAIT_UNTIL([test x = x$(ovn-sbctl get Port_Binding $pb_uuid 
options:additional-chassis-activated)])
 
-# complete port migration and check that -activated flag is reset
-check ovn-nbctl lsp-set-options migrator requested-chassis=hv2
-OVS_WAIT_UNTIL([test x = x$(ovn-sbctl get Port_Binding $pb_uuid 
options:additional-chassis-activated)])
+    # ct_zone allocation differs between I+P and recompute.
+    # As migrrator has iface-id set on hv1 but is bound to hv2, we would hit
+    # some ct-zone related differences in flows before and after recompute.
+    # Avoid/hide this by removing iface-id on hv1.
+    as hv1 check ovs-vsctl remove Interface migrator external-ids iface-id
 
-# ct_zone allocation differs between I+P and recompute.
-# As migrrator has iface-id set on hv1 but is bound to hv2, we would hit
-# some ct-zone related differences in flows before and after recompute.
-# Avoid/hide this by removing iface-id on hv1.
-as hv1 check ovs-vsctl remove Interface migrator external-ids iface-id
+    OVN_CLEANUP([hv1],[hv2],[hv3])
 
-OVN_CLEANUP([hv1],[hv2],[hv3])
+    AT_CLEANUP
+])])
 
-AT_CLEANUP
-])
+ACTIVATION_STRATEGY_TEST([rarp])
+ACTIVATION_STRATEGY_TEST([garp])
+ACTIVATION_STRATEGY_TEST([na])
 
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([options:activation-strategy=rarp is not waiting for southbound db])
-- 
2.48.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to