@@ -1567,9 +1567,11 @@ pinctrl_handle_arp(struct rconn *swconn, const struct flow *ip_flow,
const struct ofputil_packet_in *pin,
struct ofpbuf *userdata, const struct ofpbuf *continuation)
{
- /* This action only works for IP packets, and the switch should only send
- * us IP packets this way, but check here just to be sure. */
- if (ip_flow->dl_type != htons(ETH_TYPE_IP)) {
+ uint16_t dl_type = ntohs(ip_flow->dl_type);
+
+ /* This action only works for IPv4 or IPv6 packets, and the switch should
+ * only send us IP packets this way, but check here just to be sure. */
+ if (dl_type != ETH_TYPE_IP && dl_type != ETH_TYPE_IPV6) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "ARP action on non-IP packet (Ethertype %"PRIx16")",
ntohs(ip_flow->dl_type));
@@ -1593,9 +1595,25 @@ pinctrl_handle_arp(struct rconn *swconn, const struct flow *ip_flow,
struct arp_eth_header *arp = dp_packet_l3(&packet);
arp->ar_op = htons(ARP_OP_REQUEST);
arp->ar_sha = ip_flow->dl_src;
- put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
arp->ar_tha = eth_addr_zero;
- put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst);
+
+ /* We might be here without actually currently handling an IPv4 packet.
+ * This can happen in the case where we route IPv6 packets over an IPv4
+ * link.
+ * In these cases we have no destination IPv4 address from the packet that
+ * we can reuse. But we receive the actual destination IPv4 address via
+ * userdata anyway, so what we set for now is irrelevant.
+ * This is just a hope since we do not parse the userdata. If we land here
+ * for whatever reason without being an IPv4 packet and without userdata we
+ * will send out a wrong packet.
+ */
+ if (ip_flow->dl_type == htons(ETH_TYPE_IP)) {
+ put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
+ put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst);
+ } else {
+ put_16aligned_be32(&arp->ar_spa, 0);
+ put_16aligned_be32(&arp->ar_tpa, 0);
+ }
if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
@@ -6620,8 +6638,11 @@ pinctrl_handle_nd_ns(struct rconn *swconn, const struct flow *ip_flow,
struct ofpbuf *userdata,
const struct ofpbuf *continuation)
{
- /* This action only works for IPv6 packets. */
- if (get_dl_type(ip_flow) != htons(ETH_TYPE_IPV6)) {
+ uint16_t dl_type = ntohs(ip_flow->dl_type);
+
+ /* This action only works for IPv4 or IPv6 packets, and the switch should
+ * only send us IP packets this way, but check here just to be sure. */
+ if (dl_type != ETH_TYPE_IP && dl_type != ETH_TYPE_IPV6) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "NS action on non-IPv6 packet");
return;
@@ -6637,8 +6658,23 @@ pinctrl_handle_nd_ns(struct rconn *swconn, const struct flow *ip_flow,
dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
in6_generate_lla(ip_flow->dl_src, &ipv6_src);
+
+ /* We might be here without actually currently handling an IPv6 packet.
+ * This can happen in the case where we route IPv4 packets over an IPv6
+ * link.
+ * In these cases we have no destination IPv6 address from the packet that
+ * we can reuse. But we receive the actual destination IPv6 address via
+ * userdata anyway, so what we pass to compose_nd_ns is irrelevant.
+ * This is just a hope since we do not parse the userdata. If we land here
+ * for whatever reason without being an IPv6 packet and without userdata we
+ * will send out a wrong packet.
+ */
+ struct in6_addr ipv6_dst = IN6ADDR_EXACT_INIT;
+ if (get_dl_type(ip_flow) == htons(ETH_TYPE_IPV6)) {
+ ipv6_dst = ip_flow->ipv6_dst;
+ }
compose_nd_ns(&packet, ip_flow->dl_src, &ipv6_src,
- &ip_flow->ipv6_dst);
+ &ipv6_dst);
/* Reload previous packet metadata and set actions from userdata. */
set_actions_and_enqueue_msg(swconn, &packet,
@@ -1765,7 +1765,7 @@ parse_nested_action(struct action_context *ctx, enum ovnact_type type,
static void
parse_ARP(struct action_context *ctx)
{
- parse_nested_action(ctx, OVNACT_ARP, "ip4", ctx->scope);
+ parse_nested_action(ctx, OVNACT_ARP, "ip", ctx->scope);
}
static void
@@ -1819,7 +1819,7 @@ parse_ND_NA_ROUTER(struct action_context *ctx)
static void
parse_ND_NS(struct action_context *ctx)
{
- parse_nested_action(ctx, OVNACT_ND_NS, "ip6", ctx->scope);
+ parse_nested_action(ctx, OVNACT_ND_NS, "ip", ctx->scope);
}
static void
@@ -14892,7 +14892,8 @@ build_arp_request_flows_for_lrouter(
ds_clear(match);
ds_put_format(match, "eth.dst == 00:00:00:00:00:00 && "
- "ip6 && " REG_NEXT_HOP_IPV6 " == %s",
+ REGBIT_NEXTHOP_IS_IPV4" == 0 && "
+ REG_NEXT_HOP_IPV6 " == %s",
route->nexthop);
struct in6_addr sn_addr;
struct eth_addr eth_dst;
@@ -14922,7 +14923,8 @@ build_arp_request_flows_for_lrouter(
}
ovn_lflow_metered(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
- "eth.dst == 00:00:00:00:00:00 && ip4",
+ "eth.dst == 00:00:00:00:00:00 && "
+ REGBIT_NEXTHOP_IS_IPV4" == 1",
"arp { "
"eth.dst = ff:ff:ff:ff:ff:ff; "
"arp.spa = " REG_SRC_IPV4 "; "
@@ -14934,7 +14936,8 @@ build_arp_request_flows_for_lrouter(
meter_groups),
lflow_ref);
ovn_lflow_metered(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
- "eth.dst == 00:00:00:00:00:00 && ip6",
+ "eth.dst == 00:00:00:00:00:00 && "
+ REGBIT_NEXTHOP_IS_IPV4" == 0",
"nd_ns { "
"nd.target = " REG_NEXT_HOP_IPV6 "; "
"output; "
@@ -6979,10 +6979,10 @@ AT_CHECK([grep -e "lr_in_arp_resolve" lr0flows | ovn_strip_lflows], [0], [dnl
AT_CHECK([grep -e "lr_in_arp_request" lr0flows | ovn_strip_lflows], [0], [dnl
table=??(lr_in_arp_request ), priority=0 , match=(1), action=(output;)
- table=??(lr_in_arp_request ), priority=100 , match=(eth.dst == 00:00:00:00:00:00 && ip4), action=(arp { eth.dst = ff:ff:ff:ff:ff:ff; arp.spa = reg1; arp.tpa = reg0; arp.op = 1; output; }; output;)
- table=??(lr_in_arp_request ), priority=100 , match=(eth.dst == 00:00:00:00:00:00 && ip6), action=(nd_ns { nd.target = xxreg0; output; }; output;)
- table=??(lr_in_arp_request ), priority=200 , match=(eth.dst == 00:00:00:00:00:00 && ip6 && xxreg0 == 2001:db8::10), action=(nd_ns { eth.dst = 33:33:ff:00:00:10; ip6.dst = ff02::1:ff00:10; nd.target = 2001:db8::10; output; }; output;)
- table=??(lr_in_arp_request ), priority=200 , match=(eth.dst == 00:00:00:00:00:00 && ip6 && xxreg0 == 2001:db8::20), action=(nd_ns { eth.dst = 33:33:ff:00:00:20; ip6.dst = ff02::1:ff00:20; nd.target = 2001:db8::20; output; }; output;)
+ table=??(lr_in_arp_request ), priority=100 , match=(eth.dst == 00:00:00:00:00:00 && reg9[[9]] == 0), action=(nd_ns { nd.target = xxreg0; output; }; output;)
+ table=??(lr_in_arp_request ), priority=100 , match=(eth.dst == 00:00:00:00:00:00 && reg9[[9]] == 1), action=(arp { eth.dst = ff:ff:ff:ff:ff:ff; arp.spa = reg1; arp.tpa = reg0; arp.op = 1; output; }; output;)
+ table=??(lr_in_arp_request ), priority=200 , match=(eth.dst == 00:00:00:00:00:00 && reg9[[9]] == 0 && xxreg0 == 2001:db8::10), action=(nd_ns { eth.dst = 33:33:ff:00:00:10; ip6.dst = ff02::1:ff00:10; nd.target = 2001:db8::10; output; }; output;)
+ table=??(lr_in_arp_request ), priority=200 , match=(eth.dst == 00:00:00:00:00:00 && reg9[[9]] == 0 && xxreg0 == 2001:db8::20), action=(nd_ns { eth.dst = 33:33:ff:00:00:20; ip6.dst = ff02::1:ff00:20; nd.target = 2001:db8::20; output; }; output;)
])
AT_CLEANUP
@@ -1396,11 +1396,11 @@ clone { ip4.dst = 255.255.255.255; output; }; next;
# arp
arp { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
encodes as controller(userdata=00.00.00.00.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.OFTABLE_SAVE_INPORT_HEX.00.00.00,pause),resubmit(,OFTABLE_SAVE_INPORT)
- has prereqs ip4
+ has prereqs ip
arp { };
formats as arp { drop; };
encodes as controller(userdata=00.00.00.00.00.00.00.00,pause)
- has prereqs ip4
+ has prereqs ip
# get_arp
get_arp(outport, ip4.dst);
@@ -1564,12 +1564,12 @@ reg9[[8]] = dhcp_relay_resp_chk(192.168.1, 172.16.1.1);
# nd_ns
nd_ns { nd.target = xxreg0; output; };
encodes as controller(userdata=00.00.00.09.00.00.00.00.00.1c.00.18.00.80.00.00.00.00.00.00.00.01.de.10.80.00.3e.10.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.OFTABLE_SAVE_INPORT_HEX.00.00.00,pause)
- has prereqs ip6
+ has prereqs ip
nd_ns { };
formats as nd_ns { drop; };
encodes as controller(userdata=00.00.00.09.00.00.00.00,pause)
- has prereqs ip6
+ has prereqs ip
# nd_na
nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; };
@@ -40144,6 +40144,266 @@ OVN_CLEANUP([hv1],[hv2])
AT_CLEANUP
])
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv4 over IPv6, dynamic])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+ovn_start
+
+# Logical network:
+# Two LRs - R1 and R2 that are connected to ls-transfer in 2001:db8::/64
+# network. R1 has a switchs ls1 (192.168.1.0/24) connected to it.
+# R2 has ls2 (172.16.1.0/24) connected to it.
+
+ls1_lp1_mac="f0:00:00:01:02:03"
+rp_ls1_mac="00:00:00:01:02:03"
+rp_ls2_mac="00:00:00:01:02:04"
+ls2_lp1_mac="f0:00:00:01:02:04"
+
+ls1_lp1_ip="192.168.1.2"
+ls2_lp1_ip="172.16.1.2"
+
+check ovn-nbctl lr-add R1
+check ovn-nbctl lr-add R2
+
+check ovn-nbctl ls-add ls1
+check ovn-nbctl ls-add ls2
+check ovn-nbctl ls-add ls-transfer
+
+# Connect ls1 to R1
+check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24
+check ovn-nbctl set Logical_Router R1 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \
+ options:router-port=ls1 addresses=\"$rp_ls1_mac\"
+
+# Connect ls2 to R2
+check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24
+check ovn-nbctl set Logical_Router R2 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \
+ options:router-port=ls2 addresses=\"$rp_ls2_mac\"
+
+# Connect R1 to R2
+check ovn-nbctl lrp-add R1 R1_ls-transfer 00:00:00:02:03:04 2001:db8::1/64
+check ovn-nbctl lrp-add R2 R2_ls-transfer 00:00:00:02:03:05 2001:db8::2/64
+
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r1 -- \
+ set Logical_Switch_Port ls-transfer_r1 type=router \
+ options:router-port=R1_ls-transfer addresses=\"router\"
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r2 -- \
+ set Logical_Switch_Port ls-transfer_r2 type=router \
+ options:router-port=R2_ls-transfer addresses=\"router\"
+
+AT_CHECK([ovn-nbctl lr-route-add R1 "0.0.0.0/0" 2001:db8::2])
+AT_CHECK([ovn-nbctl lr-route-add R2 "0.0.0.0/0" 2001:db8::1])
+
+# Create logical port ls1-lp1 in ls1
+check ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip"
+
+# Create logical port ls2-lp1 in ls2
+check ovn-nbctl lsp-add ls2 ls2-lp1 \
+-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip"
+
+# Create two hypervisor and create OVS ports corresponding to logical ports.
+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 ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+
+sim_add hv2
+as hv2
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+check ovs-vsctl -- add-port br-int hv2-vif1 -- \
+ set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \
+ options:tx_pcap=hv2/vif1-tx.pcap \
+ options:rxq_pcap=hv2/vif1-rx.pcap \
+ ofport-request=1
+
+
+# Pre-populate the hypervisors' ARP tables so that we don't lose any
+# packets for ARP resolution (native tunneling doesn't queue packets
+# for ARP resolution).
+OVN_POPULATE_ARP
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+wait_for_ports_up
+check ovn-nbctl --wait=hv sync
+
+# Packet to send.
+packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \
+ IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=64)/ \
+ UDP(sport=53, dport=4369)")
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# Packet to Expect
+# The TTL should be decremented by 2.
+expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \
+ IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=62)/ \
+ UDP(sport=53, dport=4369)")
+echo ${expected} > expected
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 172.16.1.2" | wc -l], [0], [1
+])
+
+# Disable the ls2-lp1 port.
+check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 172.16.1.2" | wc -l], [0], [0
+])
+
+# Send the same packet again and it should not be delivered
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# The 2nd packet sent shound not be received.
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+OVN_CLEANUP([hv1],[hv2])
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv6 over IPv4, dynamic])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+ovn_start
+
+# Logical network:
+# Two LRs - R1 and R2 that are connected to ls-transfer in 10.0.0.0/24
+# network. R1 has a switchs ls1 (2001:db8:1::/64) connected to it.
+# R2 has ls2 (2001:db8:2::/64) connected to it.
+
+ls1_lp1_mac="f0:00:00:01:02:03"
+rp_ls1_mac="00:00:00:01:02:03"
+rp_ls2_mac="00:00:00:01:02:04"
+ls2_lp1_mac="f0:00:00:01:02:04"
+
+ls1_lp1_ip="2001:db8:1::2"
+ls2_lp1_ip="2001:db8:2::2"
+
+check ovn-nbctl lr-add R1
+check ovn-nbctl lr-add R2
+
+check ovn-nbctl ls-add ls1
+check ovn-nbctl ls-add ls2
+check ovn-nbctl ls-add ls-transfer
+
+# Connect ls1 to R1
+check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 2001:db8:1::1/64
+check ovn-nbctl set Logical_Router R1 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \
+ options:router-port=ls1 addresses=\"$rp_ls1_mac\"
+
+# Connect ls2 to R2
+check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 2001:db8:2::1/64
+check ovn-nbctl set Logical_Router R2 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \
+ options:router-port=ls2 addresses=\"$rp_ls2_mac\"
+
+# Connect R1 to R2
+check ovn-nbctl lrp-add R1 R1_ls-transfer 00:00:00:02:03:04 10.0.0.1/24
+check ovn-nbctl lrp-add R2 R2_ls-transfer 00:00:00:02:03:05 10.0.0.2/24
+
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r1 -- \
+ set Logical_Switch_Port ls-transfer_r1 type=router \
+ options:router-port=R1_ls-transfer addresses=\"router\"
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r2 -- \
+ set Logical_Switch_Port ls-transfer_r2 type=router \
+ options:router-port=R2_ls-transfer addresses=\"router\"
+
+AT_CHECK([ovn-nbctl lr-route-add R1 "::/0" 10.0.0.2])
+AT_CHECK([ovn-nbctl lr-route-add R2 "::/0" 10.0.0.1])
+
+# Create logical port ls1-lp1 in ls1
+check ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip"
+
+# Create logical port ls2-lp1 in ls2
+check ovn-nbctl lsp-add ls2 ls2-lp1 \
+-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip"
+
+# Create two hypervisor and create OVS ports corresponding to logical ports.
+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 ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+
+sim_add hv2
+as hv2
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+check ovs-vsctl -- add-port br-int hv2-vif1 -- \
+ set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \
+ options:tx_pcap=hv2/vif1-tx.pcap \
+ options:rxq_pcap=hv2/vif1-rx.pcap \
+ ofport-request=1
+
+
+# Pre-populate the hypervisors' ARP tables so that we don't lose any
+# packets for ARP resolution (native tunneling doesn't queue packets
+# for ARP resolution).
+OVN_POPULATE_ARP
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+wait_for_ports_up
+check ovn-nbctl --wait=hv sync
+
+# Packet to send.
+packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \
+ IPv6(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', hlim=64)/ \
+ UDP(sport=53, dport=4369)")
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# Packet to Expect
+# The TTL should be decremented by 2.
+expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \
+ IPv6(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', hlim=62)/ \
+ UDP(sport=53, dport=4369)")
+echo ${expected} > expected
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 2001:db8:2::2" | wc -l], [0], [1
+])
+
+# Disable the ls2-lp1 port.
+check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 2001:db8:2::2" | wc -l], [0], [0
+])
+
+# Send the same packet again and it should not be delivered
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# The 2nd packet sent shound not be received.
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+OVN_CLEANUP([hv1],[hv2])
+
+AT_CLEANUP
+])
+
OVN_FOR_EACH_NORTHD([
AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv4 over IPv6, ECMP])
AT_SKIP_IF([test $HAVE_SCAPY = no])