diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 081536ab4d..16f586db12 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -1459,7 +1459,9 @@ output;
For each enabled router port P with Ethernet address
E, a priority-50 flow that matches inport ==
P && (eth.mcast || eth.dst ==
- E
), with action next;
.
+ E), stores the router port ethernet address
+ and advances to next table, with action
+ xreg0[0..47]=E; next;
.
@@ -1479,7 +1481,7 @@ output;
a priority-50 flow that matches inport == GW
&& eth.dst == E
, where GW
is the logical router gateway port, with action
- next;
.
+ xreg0[0..47]=E; next;
.
@@ -1742,10 +1744,10 @@ next;
eth.dst = eth.src; -eth.src = E; +eth.src = xreg0[0..47]; arp.op = 2; /* ARP reply. */ arp.tha = arp.sha; -arp.sha = E; +arp.sha = xreg0[0..47]; arp.tpa = arp.spa; arp.spa = A; outport = P; @@ -1794,10 +1796,10 @@ output;nd_na_router { - eth.src = E; + eth.src = xreg0[0..47]; ip6.src = A; nd.target = A; - nd.tll = E; + nd.tll = xreg0[0..47]; outport = inport; flags.loopback = 1; output; @@ -1834,10 +1836,10 @@ nd_na_router {eth.dst = eth.src; -eth.src = E; +eth.src = xreg0[0..47]; arp.op = 2; /* ARP reply. */ arp.tha = arp.sha; -arp.sha = E; +arp.sha = xreg0[0..47]; arp.tpa = arp.spa; arp.spa = A; outport = P; @@ -1866,8 +1868,8 @@ output;eth.dst = eth.src; nd_na { - eth.src = E; - nd.tll = E; + eth.src = xreg0[0..47]; + nd.tll = xreg0[0..47]; ip6.src = A; nd.target = A; outport = P; diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index a1448ddee0..6a28864a66 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -222,6 +222,11 @@ enum ovn_stage { #define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[2]" #define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[3]" +/* Register to store the eth address associated to a router port for packets + * received in S_ROUTER_IN_ADMISSION. + */ +#define REG_INPORT_ETH_ADDR "xreg0[0..47]" + /* Register for ECMP bucket selection. */ #define REG_ECMP_GROUP_ID "reg8[0..15]" #define REG_ECMP_MEMBER_ID "reg8[16..31]" @@ -245,36 +250,42 @@ enum ovn_stage { * +---------+-------------------------------------+ * * Logical Router pipeline: - * +-----+--------------------------+---+---------------+ - * | R0 | REGBIT_ND_RA_OPTS_RESULT | | | - * | | (= IN_ND_RA_OPTIONS) | | | - * | | NEXT_HOP_IPV4 | X | | - * | | (>= IP_INPUT) | X | | - * +-----+--------------------------+ R | | - * | R1 | SRC_IPV4 for ARP-REQ | E | NEXT_HOP_IPV6 | - * | | (>= IP_INPUT) | G | (>= IP_INPUT) | - * +-----+--------------------------+ 0 | | - * | R2 | UNUSED | | | - * +-----+--------------------------+ | | - * | R3 | UNUSED | | | - * +-----+--------------------------+---+---------------+ - * | R4 | UNUSED | | | - * +-----+--------------------------+ X | | - * | R5 | UNUSED | X |SRC_IPV6 for NS| - * +-----+--------------------------+ R | (>= IP_INPUT) | - * | R6 | UNUSED | E | | - * +-----+--------------------------+ G | | - * | R7 | UNUSED | 1 | | - * +-----+--------------------------+---+---------------+ - * | R8 | ECMP_GROUP_ID | - * | | ECMP_MEMBER_ID | - * +-----+--------------------------+ - * | | REGBIT_{ | - * | | EGRESS_LOOPBACK/ | - * | R9 | PKT_LARGER/ | - * | | LOOKUP_NEIGHBOR_RESULT/| - * | | SKIP_LOOKUP_NEIGHBOR} | - * +-----+--------------------------+ + * +-----+--------------------------+---+-----------------+---+---------------+ + * | R0 | REGBIT_ND_RA_OPTS_RESULT | | | | | + * | | (= IN_ND_RA_OPTIONS) | X | | | | + * | | NEXT_HOP_IPV4 | R | | | | + * | | (>= IP_INPUT) | E | INPORT_ETH_ADDR | X | | + * +-----+--------------------------+ G | (< IP_INPUT) | X | | + * | R1 | SRC_IPV4 for ARP-REQ | 0 | | R | | + * | | (>= IP_INPUT) | | | E | NEXT_HOP_IPV6 | + * +-----+--------------------------+---+-----------------+ G | (>= IP_INPUT) | + * | R2 | UNUSED | X | | 0 | | + * | | | R | | | | + * +-----+--------------------------+ E | UNUSED | | | + * | R3 | UNUSED | G | | | | + * | | | 1 | | | | + * +-----+--------------------------+---+-----------------+---+---------------+ + * | R4 | UNUSED | X | | | | + * | | | R | | | | + * +-----+--------------------------+ E | UNUSED | X | | + * | R5 | UNUSED | G | | X | | + * | | | 2 | | R |SRC_IPV6 for NS| + * +-----+--------------------------+---+-----------------+ E | (>= IP_INPUT) | + * | R6 | UNUSED | X | | G | | + * | | | R | | 1 | | + * +-----+--------------------------+ E | UNUSED | | | + * | R7 | UNUSED | G | | | | + * | | | 3 | | | | + * +-----+--------------------------+---+-----------------+---+---------------+ + * | R8 | ECMP_GROUP_ID | | | + * | | ECMP_MEMBER_ID | X | | + * +-----+--------------------------+ R | | + * | | REGBIT_{ | E | | + * | | EGRESS_LOOPBACK/ | G | UNUSED | + * | R9 | PKT_LARGER/ | 4 | | + * | | LOOKUP_NEIGHBOR_RESULT/| | | + * | | SKIP_LOOKUP_NEIGHBOR} | | | + * +-----+--------------------------+---+-----------------+ * */ @@ -8005,10 +8016,19 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } + /* Store the ethernet address of the port receiving the packet. + * This will save us from having to match on inport further down in + * the pipeline. + */ + ds_clear(&actions); + ds_put_format(&actions, REG_INPORT_ETH_ADDR " = %s; next;", + op->lrp_networks.ea_s); + ds_clear(&match); ds_put_format(&match, "eth.mcast && inport == %s", op->json_key); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50, - ds_cstr(&match), "next;", &op->nbrp->header_); + ds_cstr(&match), ds_cstr(&actions), + &op->nbrp->header_); ds_clear(&match); ds_put_format(&match, "eth.dst == %s && inport == %s", @@ -8021,7 +8041,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, op->od->l3redirect_port->json_key); } ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50, - ds_cstr(&match), "next;", &op->nbrp->header_); + ds_cstr(&match), ds_cstr(&actions), + &op->nbrp->header_); } /* Logical router ingress table 1: LOOKUP_NEIGHBOR and @@ -8288,17 +8309,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_clear(&actions); ds_put_format(&actions, "eth.dst = eth.src; " - "eth.src = %s; " + "eth.src = " REG_INPORT_ETH_ADDR "; " "arp.op = 2; /* ARP reply */ " "arp.tha = arp.sha; " - "arp.sha = %s; " + "arp.sha = " REG_INPORT_ETH_ADDR "; " "arp.tpa = arp.spa; " "arp.spa = %s; " "outport = %s; " "flags.loopback = 1; " "output;", - op->lrp_networks.ea_s, - op->lrp_networks.ea_s, op->lrp_networks.ipv4_addrs[i].addr_s, op->json_key); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, @@ -8325,17 +8344,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_clear(&actions); ds_put_format(&actions, "eth.dst = eth.src; " - "eth.src = %s; " + "eth.src = " REG_INPORT_ETH_ADDR "; " "arp.op = 2; /* ARP reply */ " "arp.tha = arp.sha; " - "arp.sha = %s; " + "arp.sha = " REG_INPORT_ETH_ADDR "; " "arp.tpa = arp.spa; " "arp.spa = %s; " "outport = %s; " "flags.loopback = 1; " "output;", - op->lrp_networks.ea_s, - op->lrp_networks.ea_s, ip_address, op->json_key); @@ -8356,18 +8373,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_clear(&actions); ds_put_format(&actions, "nd_na { " - "eth.src = %s; " + "eth.src = " REG_INPORT_ETH_ADDR "; " "ip6.src = %s; " "nd.target = %s; " - "nd.tll = %s; " + "nd.tll = " REG_INPORT_ETH_ADDR "; " "outport = inport; " "flags.loopback = 1; " "output; " "};", - op->lrp_networks.ea_s, ip_address, - ip_address, - op->lrp_networks.ea_s); + ip_address); ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, ds_cstr(&match), ds_cstr(&actions)); @@ -8490,18 +8505,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, nat->logical_port); } else { if (is_v6) { - ds_put_format(&actions, - "eth.src = %s; " - "nd.tll = %s; ", - op->lrp_networks.ea_s, - op->lrp_networks.ea_s); + ds_put_cstr(&actions, + "eth.src = " REG_INPORT_ETH_ADDR "; " + "nd.tll = " REG_INPORT_ETH_ADDR "; "); } else { - ds_put_format(&actions, - "eth.src = %s; " - "arp.sha = %s; ", - op->lrp_networks.ea_s, - op->lrp_networks.ea_s); + ds_put_cstr(&actions, + "eth.src = "REG_INPORT_ETH_ADDR "; " + "arp.sha = " REG_INPORT_ETH_ADDR "; "); } /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s * should only be sent from the "redirect-chassis", so that @@ -8515,17 +8526,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } } else { if (is_v6) { - ds_put_format(&actions, - "eth.src = %s; " - "nd.tll = %s; ", - op->lrp_networks.ea_s, - op->lrp_networks.ea_s); + ds_put_cstr(&actions, + "eth.src = " REG_INPORT_ETH_ADDR "; " + "nd.tll = " REG_INPORT_ETH_ADDR "; "); } else { ds_put_format(&actions, - "eth.src = %s; " - "arp.sha = %s; ", - op->lrp_networks.ea_s, - op->lrp_networks.ea_s); + "eth.src = " REG_INPORT_ETH_ADDR "; " + "arp.sha = " REG_INPORT_ETH_ADDR "; "); } } if (is_v6) { @@ -8743,18 +8750,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_clear(&actions); ds_put_format(&actions, "nd_na_router { " - "eth.src = %s; " + "eth.src = " REG_INPORT_ETH_ADDR "; " "ip6.src = %s; " "nd.target = %s; " - "nd.tll = %s; " + "nd.tll = " REG_INPORT_ETH_ADDR "; " "outport = inport; " "flags.loopback = 1; " "output; " "};", - op->lrp_networks.ea_s, - op->lrp_networks.ipv6_addrs[i].addr_s, op->lrp_networks.ipv6_addrs[i].addr_s, - op->lrp_networks.ea_s); + op->lrp_networks.ipv6_addrs[i].addr_s); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, ds_cstr(&match), ds_cstr(&actions), &op->nbrp->header_); @@ -9256,6 +9261,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * on the l3dgw_port instance where nat->logical_port is * resident. */ if (distributed) { + /* Store the ethernet address of the port receiving the packet. + * This will save us from having to match on inport further + * down in the pipeline. + */ + ds_clear(&actions); + ds_put_format(&actions, REG_INPORT_ETH_ADDR " = %s; next;", + od->l3dgw_port->lrp_networks.ea_s); + ds_clear(&match); ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT" && inport == %s" @@ -9264,7 +9277,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, od->l3dgw_port->json_key, nat->logical_port); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ADMISSION, 50, - ds_cstr(&match), "next;", + ds_cstr(&match), ds_cstr(&actions), &nat->header_); } diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 87eb42eebc..a03dbf4f33 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -1624,3 +1624,143 @@ AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 ]) AT_CLEANUP + +AT_SETUP([ovn -- check router ARP/NS responder]) +ovn_start + +ovn-sbctl chassis-add ch geneve 127.0.0.1 + +ovn-nbctl lr-add lr +ovn-nbctl lrp-add lr lrp-public 00:00:00:00:01:00 43.43.43.1/24 +ovn-nbctl lrp-add lr lrp 00:00:00:00:00:01 42.42.42.1/24 + +ovn-nbctl ls-add ls +ovn-nbctl lsp-add ls ls-rp +ovn-nbctl lsp-set-type ls-rp router +ovn-nbctl lsp-set-addresses ls-rp router +ovn-nbctl lsp-set-options ls-rp router-port=lrp +ovn-nbctl lsp-add ls ls-vm + +ovn-nbctl set logical_router lr options:chassis=ch +ovn-nbctl lr-nat-add lr dnat_and_snat 43.43.43.2 42.42.42.2 +ovn-nbctl lr-nat-add lr dnat 43.43.43.3 42.42.42.3 +ovn-nbctl lr-nat-add lr dnat_and_snat 43.43.43.4 42.42.42.4 ls-vm 00:00:00:00:00:02 + +ovn-nbctl --wait=sb sync + +# Ingress router port ETH address is stored in lr_in_admission. +AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_admission.*xreg0\[[0..47\]]" | sort], [0], [dnl + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.dst == 00:00:00:00:00:01 && inport == "lrp"), dnl +action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;) + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.dst == 00:00:00:00:01:00 && inport == "lrp-public"), dnl +action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;) + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.mcast && inport == "lrp"), dnl +action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;) + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.mcast && inport == "lrp-public"), dnl +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.spa == 42.42.42.0/24 && arp.tpa == 42.42.42.1 && arp.op == 1), 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 = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && nd_ns && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && 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.spa == 43.43.43.0/24 && arp.tpa == 43.43.43.1 && arp.op == 1), 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 = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && nd_ns && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && 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; };) +]) + +# xreg0[0..47] isn't used anywhere else. +AT_CHECK([ovn-sbctl lflow-list | grep "xreg0\[[0..47\]]" | grep -vE 'lr_in_admission|lr_in_ip_input'], [1], []) + +# Test chassis redirect port. +ovn-nbctl remove logical_router lr options chassis +ovn-nbctl lrp-set-gateway-chassis lrp-public ch +ovn-nbctl --wait=sb sync + +# Ingress router port ETH address is stored in lr_in_admission. +AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_admission.*xreg0\[[0..47\]]" | sort], [0], [dnl + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.dst == 00:00:00:00:00:01 && inport == "lrp"), dnl +action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;) + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.dst == 00:00:00:00:00:02 && inport == "lrp-public" && is_chassis_resident("ls-vm")), dnl +action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;) + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.dst == 00:00:00:00:01:00 && inport == "lrp-public" && is_chassis_resident("cr-lrp-public")), dnl +action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;) + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.mcast && inport == "lrp"), dnl +action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;) + table=0 (lr_in_admission ), priority=50 , dnl +match=(eth.mcast && inport == "lrp-public"), dnl +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. +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.spa == 42.42.42.0/24 && arp.tpa == 42.42.42.1 && arp.op == 1), 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 = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && nd_ns && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && 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.spa == 43.43.43.0/24 && arp.tpa == 43.43.43.1 && arp.op == 1), 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 = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && arp.tpa == 43.43.43.2 && arp.op == 1 && is_chassis_resident("cr-lrp-public")), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && arp.tpa == 43.43.43.3 && arp.op == 1 && is_chassis_resident("cr-lrp-public")), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && arp.tpa == 43.43.43.4 && arp.op == 1 && is_chassis_resident("ls-vm")), dnl +action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = 00:00:00:00:00:02; arp.sha = 00:00:00:00:00:02; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp-public"; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp-public" && nd_ns && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && 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. +AT_CHECK([ovn-sbctl lflow-list | grep "xreg0\[[0..47\]]" | grep -vE 'lr_in_admission|lr_in_ip_input'], [1], []) + +AT_CLEANUP