@@ -1829,9 +1829,8 @@ nd_na_router {
IPv4: For a configured DNAT IP address or a load balancer
IPv4 VIP <var>A</var>, for each router port <var>P</var> with
Ethernet address <var>E</var>, a priority-90 flow matches
- <code>inport == <var>P</var> && arp.op == 1 &&
- arp.tpa == <var>A</var></code> (ARP request)
- with the following actions:
+ <code>arp.op == 1 && arp.tpa == <var>A</var></code>
+ (ARP request) with the following actions:
</p>
<pre>
@@ -1848,6 +1847,11 @@ output;
</pre>
<p>
+ IPv4: For a configured load balancer IPv4 VIP, a similar flow is
+ added with the additional match <code>inport == <var>P</var></code>.
+ </p>
+
+ <p>
If the router port <var>P</var> is a distributed gateway router
port, then the <code>is_chassis_resident(<var>P</var>)</code> is
also added in the match condition for the load balancer IPv4
@@ -1894,9 +1898,11 @@ nd_na {
<ul>
<li>
If the corresponding NAT rule cannot be handled in a
- distributed manner, then this flow is only programmed on
+ distributed manner, then a priority-92 flow is programmed on
the gateway port instance on the
- <code>redirect-chassis</code>. This behavior avoids
+ <code>redirect-chassis</code>. A priority-91 drop flow is
+ programmed on the other chassis when ARP requests/NS packets
+ are received on the gateway port. This behavior avoids
generation of multiple ARP responses from different chassis,
and allows upstream MAC learning to point to the
<code>redirect-chassis</code>.
@@ -8049,7 +8049,7 @@ lrouter_nat_is_stateless(const struct nbrec_nat *nat)
static void
build_lrouter_arp_flow(struct ovn_datapath *od, struct ovn_port *op,
const char *ip_address, const char *eth_addr,
- struct ds *extra_match, uint16_t priority,
+ struct ds *extra_match, bool drop, uint16_t priority,
const struct ovsdb_idl_row *hint,
struct hmap *lflows)
{
@@ -8065,20 +8065,24 @@ build_lrouter_arp_flow(struct ovn_datapath *od, struct ovn_port *op,
if (extra_match && ds_last(extra_match) != EOF) {
ds_put_format(&match, " && %s", ds_cstr(extra_match));
}
- ds_put_format(&actions,
- "eth.dst = eth.src; "
- "eth.src = %s; "
- "arp.op = 2; /* ARP reply */ "
- "arp.tha = arp.sha; "
- "arp.sha = %s; "
- "arp.tpa = arp.spa; "
- "arp.spa = %s; "
- "outport = inport; "
- "flags.loopback = 1; "
- "output;",
- eth_addr,
- eth_addr,
- ip_address);
+ if (drop) {
+ ds_put_format(&actions, "drop;");
+ } else {
+ ds_put_format(&actions,
+ "eth.dst = eth.src; "
+ "eth.src = %s; "
+ "arp.op = 2; /* ARP reply */ "
+ "arp.tha = arp.sha; "
+ "arp.sha = %s; "
+ "arp.tpa = arp.spa; "
+ "arp.spa = %s; "
+ "outport = inport; "
+ "flags.loopback = 1; "
+ "output;",
+ eth_addr,
+ eth_addr,
+ ip_address);
+ }
ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority,
ds_cstr(&match), ds_cstr(&actions), hint);
@@ -8097,7 +8101,7 @@ static void
build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op,
const char *action, const char *ip_address,
const char *sn_ip_address, const char *eth_addr,
- struct ds *extra_match, uint16_t priority,
+ struct ds *extra_match, bool drop, uint16_t priority,
const struct ovsdb_idl_row *hint,
struct hmap *lflows)
{
@@ -8119,21 +8123,25 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op,
ds_put_format(&match, " && %s", ds_cstr(extra_match));
}
- ds_put_format(&actions,
- "%s { "
- "eth.src = %s; "
- "ip6.src = %s; "
- "nd.target = %s; "
- "nd.tll = %s; "
- "outport = inport; "
- "flags.loopback = 1; "
- "output; "
- "};",
- action,
- eth_addr,
- ip_address,
- ip_address,
- eth_addr);
+ if (drop) {
+ ds_put_format(&actions, "drop;");
+ } else {
+ ds_put_format(&actions,
+ "%s { "
+ "eth.src = %s; "
+ "ip6.src = %s; "
+ "nd.target = %s; "
+ "nd.tll = %s; "
+ "outport = inport; "
+ "flags.loopback = 1; "
+ "output; "
+ "};",
+ action,
+ eth_addr,
+ ip_address,
+ ip_address,
+ eth_addr);
+ }
ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority,
ds_cstr(&match), ds_cstr(&actions), hint);
@@ -8323,7 +8331,41 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
"ip4.dst == 0.0.0.0/8",
"drop;");
- /* Priority-90 flows reply to ARP requests and ND packets. */
+ /* Priority-90-92 flows handle ARP requests and ND packets. Most are
+ * per logical port but DNAT addresses can be handled per datapath
+ * for non gateway router ports.
+ */
+ for (int i = 0; i < od->nbr->n_nat; i++) {
+ struct ovn_nat *nat_entry = &od->nat_entries[i];
+ const struct nbrec_nat *nat = nat_entry->nb;
+
+ /* Skip entries we failed to parse. */
+ if (!nat_entry_is_valid(nat_entry)) {
+ continue;
+ }
+
+ if (!strcmp(nat->type, "snat")) {
+ continue;
+ }
+
+ /* Priority 91 and 92 flows are added for each gateway router
+ * port to handle the special cases. In case we get the packet
+ * on a regular port, just reply with the port's ETH address.
+ */
+ struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
+ if (nat_entry_is_v6(nat_entry)) {
+ build_lrouter_nd_flow(od, NULL, "nd_na",
+ ext_addrs->ipv6_addrs[0].addr_s,
+ ext_addrs->ipv6_addrs[0].sn_addr_s,
+ REG_INPORT_ETH_ADDR, NULL, false, 90,
+ &nat->header_, lflows);
+ } else {
+ build_lrouter_arp_flow(od, NULL,
+ ext_addrs->ipv4_addrs[0].addr_s,
+ REG_INPORT_ETH_ADDR, NULL, false, 90,
+ &nat->header_, lflows);
+ }
+ }
/* Drop ARP packets (priority 85). ARP request packets for router's own
* IPs are handled with priority-90 flows.
@@ -8473,7 +8515,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
build_lrouter_arp_flow(op->od, op,
op->lrp_networks.ipv4_addrs[i].addr_s,
- REG_INPORT_ETH_ADDR, &match, 90,
+ REG_INPORT_ETH_ADDR, &match, false, 90,
&op->nbrp->header_, lflows);
}
@@ -8492,7 +8534,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
build_lrouter_arp_flow(op->od, op,
ip_address, REG_INPORT_ETH_ADDR,
- &match, 90, NULL, lflows);
+ &match, false, 90, NULL, lflows);
}
SSET_FOR_EACH (ip_address, &all_ips_v6) {
@@ -8504,108 +8546,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
build_lrouter_nd_flow(op->od, op, "nd_na",
ip_address, NULL, REG_INPORT_ETH_ADDR,
- &match, 90, NULL, lflows);
+ &match, false, 90, NULL, lflows);
}
sset_destroy(&all_ips_v4);
sset_destroy(&all_ips_v6);
- /* A gateway router can have 2 SNAT IP addresses to force DNATed and
- * LBed traffic respectively to be SNATed. In addition, there can be
- * a number of SNAT rules in the NAT table. */
- struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips
- * (op->od->nbr->n_nat + 2));
- size_t n_snat_ips = 0;
-
- struct v46_ip snat_ip;
- const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat",
- &snat_ip);
- if (dnat_force_snat_ip) {
- snat_ips[n_snat_ips++] = snat_ip;
- }
-
- const char *lb_force_snat_ip = get_force_snat_ip(op->od, "lb",
- &snat_ip);
- if (lb_force_snat_ip) {
- snat_ips[n_snat_ips++] = snat_ip;
- }
-
- for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
- struct ovn_nat *nat_entry = &op->od->nat_entries[i];
- const struct nbrec_nat *nat = nat_entry->nb;
-
- /* Skip entries we failed to parse. */
- if (!nat_entry_is_valid(nat_entry)) {
- continue;
- }
-
- if (!strcmp(nat->type, "snat")) {
- if (nat_entry_is_v6(nat_entry)) {
- struct in6_addr *ipv6 =
- &nat_entry->ext_addrs.ipv6_addrs[0].addr;
-
- snat_ips[n_snat_ips].family = AF_INET6;
- snat_ips[n_snat_ips++].ipv6 = *ipv6;
- } else {
- ovs_be32 ip = nat_entry->ext_addrs.ipv4_addrs[0].addr;
- snat_ips[n_snat_ips].family = AF_INET;
- snat_ips[n_snat_ips++].ipv4 = ip;
- }
- continue;
- }
-
- /* Mac address to use when replying to ARP/NS. */
- const char *mac_s = REG_INPORT_ETH_ADDR;
-
- /* ARP / ND handling for external IP addresses.
- *
- * DNAT IP addresses are external IP addresses that need ARP
- * handling. */
- ds_clear(&match);
-
- if (op->od->l3dgw_port && op == op->od->l3dgw_port) {
- struct eth_addr mac;
- if (nat->external_mac &&
- eth_addr_from_string(nat->external_mac, &mac)
- && nat->logical_port) {
- /* distributed NAT case, use nat->external_mac */
- mac_s = nat->external_mac;
- /* Traffic with eth.src = nat->external_mac should only be
- * sent from the chassis where nat->logical_port is
- * resident, so that upstream MAC learning points to the
- * correct chassis. Also need to avoid generation of
- * multiple ARP responses from different chassis. */
- ds_put_format(&match, "is_chassis_resident(\"%s\")",
- nat->logical_port);
- } else {
- mac_s = REG_INPORT_ETH_ADDR;
- /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
- * should only be sent from the "redirect-chassis", so that
- * upstream MAC learning points to the "redirect-chassis".
- * Also need to avoid generation of multiple ARP responses
- * from different chassis. */
- if (op->od->l3redirect_port) {
- ds_put_format(&match, "is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
- }
- }
- }
-
- struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
- if (nat_entry_is_v6(nat_entry)) {
- build_lrouter_nd_flow(op->od, op, "nd_na",
- ext_addrs->ipv6_addrs[0].addr_s,
- ext_addrs->ipv6_addrs[0].sn_addr_s,
- mac_s, &match, 90,
- &nat->header_, lflows);
- } else {
- build_lrouter_arp_flow(op->od, op,
- ext_addrs->ipv4_addrs[0].addr_s,
- mac_s, &match, 90,
- &nat->header_, lflows);
- }
- }
-
if (!smap_get(&op->od->nbr->options, "chassis")
&& !op->od->l3dgw_port) {
/* UDP/TCP port unreachable. */
@@ -8654,6 +8600,50 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
}
}
+ /* A gateway router can have 2 SNAT IP addresses to force DNATed and
+ * LBed traffic respectively to be SNATed. In addition, there can be
+ * a number of SNAT rules in the NAT table. */
+ struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips
+ * (op->od->nbr->n_nat + 2));
+ size_t n_snat_ips = 0;
+
+ struct v46_ip snat_ip;
+ const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat",
+ &snat_ip);
+ if (dnat_force_snat_ip) {
+ snat_ips[n_snat_ips++] = snat_ip;
+ }
+
+ const char *lb_force_snat_ip = get_force_snat_ip(op->od, "lb",
+ &snat_ip);
+ if (lb_force_snat_ip) {
+ snat_ips[n_snat_ips++] = snat_ip;
+ }
+
+ for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
+ struct ovn_nat *nat_entry = &op->od->nat_entries[i];
+ const struct nbrec_nat *nat = nat_entry->nb;
+
+ /* Skip entries we failed to parse. */
+ if (!nat_entry_is_valid(nat_entry)) {
+ continue;
+ }
+
+ if (!strcmp(nat->type, "snat")) {
+ if (nat_entry_is_v6(nat_entry)) {
+ struct in6_addr *ipv6 =
+ &nat_entry->ext_addrs.ipv6_addrs[0].addr;
+
+ snat_ips[n_snat_ips].family = AF_INET6;
+ snat_ips[n_snat_ips++].ipv6 = *ipv6;
+ } else {
+ ovs_be32 ip = nat_entry->ext_addrs.ipv4_addrs[0].addr;
+ snat_ips[n_snat_ips].family = AF_INET;
+ snat_ips[n_snat_ips++].ipv4 = ip;
+ }
+ }
+ }
+
ds_clear(&match);
ds_put_cstr(&match, "ip4.dst == {");
bool has_drop_ips = false;
@@ -8715,6 +8705,90 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
}
free(snat_ips);
+
+ /* ARP/NS packets are taken care of per router. The only exception
+ * is on the l3dgw_port where we might need to use a different
+ * ETH address.
+ */
+ if (op != op->od->l3dgw_port) {
+ continue;
+ }
+
+ for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
+ struct ovn_nat *nat_entry = &op->od->nat_entries[i];
+ const struct nbrec_nat *nat = nat_entry->nb;
+
+ /* Skip entries we failed to parse. */
+ if (!nat_entry_is_valid(nat_entry)) {
+ continue;
+ }
+
+ if (!strcmp(nat->type, "snat")) {
+ continue;
+ }
+
+ /* Mac address to use when replying to ARP/NS. */
+ const char *mac_s = REG_INPORT_ETH_ADDR;
+
+ /* ARP / ND handling for external IP addresses.
+ *
+ * DNAT IP addresses are external IP addresses that need ARP
+ * handling. */
+
+ struct eth_addr mac;
+
+ ds_clear(&match);
+ if (nat->external_mac &&
+ eth_addr_from_string(nat->external_mac, &mac)
+ && nat->logical_port) {
+ /* distributed NAT case, use nat->external_mac */
+ mac_s = nat->external_mac;
+ /* Traffic with eth.src = nat->external_mac should only be
+ * sent from the chassis where nat->logical_port is
+ * resident, so that upstream MAC learning points to the
+ * correct chassis. Also need to avoid generation of
+ * multiple ARP responses from different chassis. */
+ ds_put_format(&match, "is_chassis_resident(\"%s\")",
+ nat->logical_port);
+ } else {
+ mac_s = REG_INPORT_ETH_ADDR;
+ /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
+ * should only be sent from the "redirect-chassis", so that
+ * upstream MAC learning points to the "redirect-chassis".
+ * Also need to avoid generation of multiple ARP responses
+ * from different chassis. */
+ if (op->od->l3redirect_port) {
+ ds_put_format(&match, "is_chassis_resident(\"%s\")",
+ op->od->l3redirect_port->json_key);
+ }
+ }
+
+ /* Respond to ARP/NS requests on the chassis that binds the gw
+ * port. Drop the ARP/NS requests on other chassis.
+ */
+ struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
+ if (nat_entry_is_v6(nat_entry)) {
+ build_lrouter_nd_flow(op->od, op, "nd_na",
+ ext_addrs->ipv6_addrs[0].addr_s,
+ ext_addrs->ipv6_addrs[0].sn_addr_s,
+ mac_s, &match, false, 92,
+ &nat->header_, lflows);
+ build_lrouter_nd_flow(op->od, op, "nd_na",
+ ext_addrs->ipv6_addrs[0].addr_s,
+ ext_addrs->ipv6_addrs[0].sn_addr_s,
+ mac_s, NULL, true, 91,
+ &nat->header_, lflows);
+ } else {
+ build_lrouter_arp_flow(op->od, op,
+ ext_addrs->ipv4_addrs[0].addr_s,
+ mac_s, &match, false, 92,
+ &nat->header_, lflows);
+ build_lrouter_arp_flow(op->od, op,
+ ext_addrs->ipv4_addrs[0].addr_s,
+ mac_s, NULL, true, 91,
+ &nat->header_, lflows);
+ }
+ }
}
/* DHCPv6 reply handling */
@@ -8789,7 +8863,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
build_lrouter_nd_flow(op->od, op, "nd_na_router",
op->lrp_networks.ipv6_addrs[i].addr_s,
op->lrp_networks.ipv6_addrs[i].sn_addr_s,
- REG_INPORT_ETH_ADDR, &match, 90,
+ REG_INPORT_ETH_ADDR, &match, false, 90,
&op->nbrp->header_, lflows);
}
@@ -1667,33 +1667,24 @@ action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;)
# Ingress router port ETH address is used for ARP reply/NA in lr_in_ip_input.
AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl
-action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl
+match=(arp.op == 1 && arp.tpa == 43.43.43.2), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl
+match=(arp.op == 1 && arp.tpa == 43.43.43.3), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl
+match=(arp.op == 1 && arp.tpa == 43.43.43.4), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
+match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl
+action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;)
+ table=3 (lr_in_ip_input ), priority=90 , dnl
match=(inport == "lrp" && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd_ns && nd.target == fe80::200:ff:fe00:1), dnl
action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
table=3 (lr_in_ip_input ), priority=90 , dnl
match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.1 && arp.spa == 43.43.43.0/24), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.1; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl
-action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl
-action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl
-action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100), dnl
action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
])
@@ -1727,37 +1718,55 @@ action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;)
# Ingress router port is used for ARP reply/NA in lr_in_ip_input.
# xxreg0[0..47] is used unless external_mac is set.
+# Priority 90 flows (per router).
AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl
-action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl
+match=(arp.op == 1 && arp.tpa == 43.43.43.2), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl
+match=(arp.op == 1 && arp.tpa == 43.43.43.3), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl
+match=(arp.op == 1 && arp.tpa == 43.43.43.4), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
+match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl
+action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;)
+ table=3 (lr_in_ip_input ), priority=90 , dnl
match=(inport == "lrp" && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd_ns && nd.target == fe80::200:ff:fe00:1), dnl
action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
table=3 (lr_in_ip_input ), priority=90 , dnl
match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.1 && arp.spa == 43.43.43.0/24), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.1; outport = inport; flags.loopback = 1; output;)
table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident("cr-lrp-public")), dnl
+match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100 && is_chassis_resident("cr-lrp-public")), dnl
+action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
+])
+
+# Priority 91 drop flows (per distributed gw port), if port is not resident.
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=91" | grep "arp\|nd" | sort], [0], [dnl
+ table=3 (lr_in_ip_input ), priority=91 , dnl
+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl
+action=(drop;)
+ table=3 (lr_in_ip_input ), priority=91 , dnl
+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl
+action=(drop;)
+ table=3 (lr_in_ip_input ), priority=91 , dnl
+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl
+action=(drop;)
+])
+
+# Priority 92 ARP/NS responders (per distributed gw port), if port is resident.
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=92" | grep "arp\|nd" | sort], [0], [dnl
+ table=3 (lr_in_ip_input ), priority=92 , dnl
+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident(""cr-lrp-public"")), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3 && is_chassis_resident("cr-lrp-public")), dnl
+ table=3 (lr_in_ip_input ), priority=92 , dnl
+match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3 && is_chassis_resident(""cr-lrp-public"")), dnl
action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
+ table=3 (lr_in_ip_input ), priority=92 , dnl
match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4 && is_chassis_resident("ls-vm")), dnl
action=(eth.dst = eth.src; eth.src = 00:00:00:00:00:02; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 00:00:00:00:00:02; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;)
- table=3 (lr_in_ip_input ), priority=90 , dnl
-match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100 && is_chassis_resident("cr-lrp-public")), dnl
-action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
])
# xreg0[0..47] isn't used anywhere else.
@@ -19162,7 +19162,7 @@ OVS_WAIT_UNTIL([
send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121)
# Verify that the ARP request is replied to from hv1 and not hv2.
-match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.121,arp_op=1"
+match_arp_req="priority=92.*${match_r1_metadata}.*arp_tpa=10.0.0.121,arp_op=1"
as hv1
OVS_WAIT_UNTIL([
@@ -19182,7 +19182,7 @@ OVS_WAIT_UNTIL([
send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 122)
# Verify that the ARP request is replied to from hv2 and not hv1.
-match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.122,arp_op=1"
+match_arp_req="priority=92.*${match_r1_metadata}.*arp_tpa=10.0.0.122,arp_op=1"
as hv2
OVS_WAIT_UNTIL([
@@ -19226,7 +19226,7 @@ dst_ipv6=00100000000000000000000000000121
send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72dd
# Verify that the ND_NS is replied to from hv1 and not hv2.
-match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::121"
+match_nd_ns="priority=92.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::121"
as hv1
OVS_WAIT_UNTIL([
@@ -19248,7 +19248,7 @@ dst_ipv6=00100000000000000000000000000122
send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72db
# Verify that the ND_NS is replied to from hv2 and not hv1.
-match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::122"
+match_nd_ns="priority=92.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::122"
as hv2
OVS_WAIT_UNTIL([