An ICMP echo request sent to a logical router port's own IP is answered by the in-pipeline echo responder in the logical router's IP input stage, and the generated reply re-enters the connected logical switch with inport set to the router port. Router port traffic is exempt from conntrack (skip_port_from_conntrack()), so the reply arrives at the ACL eval stage untracked, never matches the built-in est-allow flow, and is dropped by a stateful ACL set that only allows egress traffic. A VM with such a security group therefore cannot ping its router interface.
The exemption exists for transit traffic, where the request and the reply are processed on different chassis and per-chassis conntrack zones cannot correlate them. Router-terminated echo traffic is different: the reply is generated with flags.loopback = 1 in the logical router pipeline, so both the request and the reply are processed on the same chassis and the router port's zone is local. Add per-router-port flows at priority 111 (above the priority-110 skip flows) in the pre-ACL stages that send echo requests destined to the router port's own address and echo replies sourced from it through conntrack in that zone. The reply then matches the existing est-allow flow and is allowed, and pings to the router can still be blocked by dropping the echo request direction with a higher-priority ACL, like any other established connection. Transit traffic is not matched and keeps the exemption. Router ports, however, had no conntrack zone allocated: the zone lookups in the physical pipeline (the egress zone load for the router port and the logical switch ingress zone load at the patch handoff) key off the switch-side patch port name, and ovn-controller only allocated zones for bound logical ports and per-datapath NAT zones. Extend ct_zones_update() to also assign a zone for the switch-side port of every router port on a local logical switch (one additional zone per router port per chassis with a local datapath). The ct_zones incremental processing handler cannot track those zones incrementally, so make it fall back to a full ct_zones recompute for patch ports instead of deleting their zones. Reported-at: https://redhat.atlassian.net/browse/FDP-4509 Assisted-by: GLM 5.3, OpenCode Signed-off-by: Jacob Tanenbaum <[email protected]> diff --git a/NEWS b/NEWS index 4176f8d7f..11d79c0c2 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,14 @@ Post v26.09.0 -------------- + - ICMP echo replies generated by the logical router are now tracked + by conntrack in the router port's zone, so they are established + reply traffic instead of untracked packets. Stateful ACLs with + only egress allow-related rules no longer drop a VM's ping to its + router port's IP address. Pings to the router can still be + blocked by dropping the echo request direction with a + higher-priority ACL. To support this, ovn-controller now + allocates a conntrack zone for the router port of every local + logical switch (one additional zone per router port per chassis). - Learn ARP sender hardware address (arp.sha) and IPv6 Neighbor Discovery link-layer addresses (nd.tll, nd.sll) into the FDB table in addition to eth.src. This allows VRRP virtual MACs to diff --git a/controller/ct-zone.c b/controller/ct-zone.c index a4f94375f..b54011d82 100644 --- a/controller/ct-zone.c +++ b/controller/ct-zone.c @@ -210,6 +210,16 @@ ct_zones_update(const struct simap *local_lports, } free(dnat); free(snat); + + /* The router ports of a local logical switch need zones assigned + * so that traffic terminated by the connected logical router can + * be tracked in the router port's zone on this chassis. */ + if (ld->is_switch) { + const struct peer_ports *peers; + VECTOR_FOR_EACH_PTR (&ld->peer_ports, peers) { + sset_add(&all_users, peers->local->logical_port); + } + } } ct_zones_parse_range(ovs_table, &min_ct_zone, &max_ct_zone); diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 7bc3139ab..56c917e64 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -2873,16 +2873,15 @@ ct_zones_runtime_data_handler(struct engine_node *node, void *data) continue; } - /* For patch ports without ACL flag, delete the ct_zone. */ + /* The CT zone of a patch port without the ACL flag depends + * on the local datapaths of the connected switches, which is + * not tracked incrementally. Fall back to a full recompute + * of ct_zones so that zones for router ports are assigned + * and released consistently with ct_zones_update(). */ if (!strcmp(t_lport->pb->type, "patch") && !smap_get_bool(&t_lport->pb->options, "enable_router_port_acl", false)) { - updated |= ct_zone_handle_port_update(&ct_zones_data->ctx, - t_lport->pb, - false, &scan_start, - min_ct_zone, - max_ct_zone); - continue; + return EN_UNHANDLED; } bool port_updated = t_lport->tracked_type == TRACKED_RESOURCE_NEW || diff --git a/northd/northd.c b/northd/northd.c index 3f59894bd..169da9c34 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -6928,6 +6928,75 @@ skip_port_from_conntrack(const struct ovn_datapath *od, struct ovn_port *op, free(egress_match); } +static void op_put_v4_networks(struct ds *ds, const struct ovn_port *op, + bool add_bcast); +static void op_put_v6_networks(struct ds *ds, const struct ovn_port *op); + +/* Router-terminated ICMP echo request/reply pairs must be tracked by + * conntrack even though other traffic to and from a router port is + * exempt from it (see skip_port_from_conntrack()). + * + * The flows match only echo requests whose destination is the router + * port's own address and echo replies whose source is the router + * port's own address, so transit traffic through the router port is + * not affected. They run at priority 111, above the priority-110 + * skip flows installed by skip_port_from_conntrack(). */ +static void +build_rport_echo_conntrack_flows(const struct ovn_datapath *od, + struct ovn_port *op, + struct lflow_table *lflows, + struct lflow_ref *lflow_ref) +{ + ovs_assert(op->peer && op->peer->nbrp); + + struct ds match = DS_EMPTY_INITIALIZER; + + if (op->peer->lrp_networks.n_ipv4_addrs) { + ds_put_format(&match, "inport == %s && ip4 && ip4.src == ", + op->json_key); + op_put_v4_networks(&match, op->peer, false); + ds_put_cstr(&match, " && icmp4.type == 0 && icmp4.code == 0"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 111, ds_cstr(&match), + REGBIT_CONNTRACK_DEFRAG" = 1; next;", lflow_ref, + WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + ds_clear(&match); + ds_put_format(&match, "outport == %s && ip4 && ip4.dst == ", + op->json_key); + op_put_v4_networks(&match, op->peer, false); + ds_put_cstr(&match, " && icmp4.type == 8 && icmp4.code == 0"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 111, ds_cstr(&match), + REGBIT_CONNTRACK_DEFRAG" = 1; next;", lflow_ref, + WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + } + + if (op->peer->lrp_networks.n_ipv6_addrs) { + ds_clear(&match); + ds_put_format(&match, "inport == %s && ip6 && ip6.src == ", + op->json_key); + op_put_v6_networks(&match, op->peer); + ds_put_cstr(&match, " && icmp6.type == 129 && icmp6.code == 0"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 111, ds_cstr(&match), + REGBIT_CONNTRACK_DEFRAG" = 1; next;", lflow_ref, + WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + + ds_clear(&match); + ds_put_format(&match, "outport == %s && ip6 && ip6.dst == ", + op->json_key); + op_put_v6_networks(&match, op->peer); + ds_put_cstr(&match, " && icmp6.type == 128 && icmp6.code == 0"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 111, ds_cstr(&match), + REGBIT_CONNTRACK_DEFRAG" = 1; next;", lflow_ref, + WITH_IO_PORT(op->key), + WITH_HINT(&op->nbsp->header_)); + } + + ds_destroy(&match); +} + /* Skip conntrack for traffic from/to EVPN remote VTEPs. * Remote VTEPs do not have conntrack zones assigned, so * conntrack lookups would return +trk+inv and cause drops. */ @@ -7042,6 +7111,9 @@ build_ls_stateful_rec_pre_acls( skip_port_from_conntrack(od, op, true, S_SWITCH_IN_PRE_ACL, S_SWITCH_OUT_PRE_ACL, 110, lflows, lflow_ref); + if (op->peer && op->peer->nbrp) { + build_rport_echo_conntrack_flows(od, op, lflows, lflow_ref); + } } VECTOR_FOR_EACH (&od->switch_ports, op) { skip_port_from_conntrack(od, op, true, diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 93af177ab..a8e870ffc 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -13947,12 +13947,16 @@ AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | ovn_strip_lflows], [0], [dnl table=??(ls_in_pre_acl ), priority=110 , match=(eth.mcast), action=(next;) table=??(ls_in_pre_acl ), priority=110 , match=(ip && inport == "S1-R1"), action=(next;) table=??(ls_in_pre_acl ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "S1-R1" && ip4 && ip4.src == 172.16.1.1 && icmp4.type == 0 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "S1-R1" && ip6 && ip6.src == fe80::ac:10ff:fe01:1 && icmp6.type == 129 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) table=??(ls_out_pre_acl ), priority=0 , match=(1), action=(next;) table=??(ls_out_pre_acl ), priority=100 , match=(ip), action=(reg0[[0]] = 1; next;) table=??(ls_out_pre_acl ), priority=110 , match=(eth.mcast), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(eth.src == $svc_monitor_mac), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(ip && outport == "S1-R1"), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "S1-R1" && ip4 && ip4.dst == 172.16.1.1 && icmp4.type == 8 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "S1-R1" && ip6 && ip6.dst == fe80::ac:10ff:fe01:1 && icmp6.type == 128 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) ]) # Enable 'enable_router_port_acl' and check the flows @@ -13994,12 +13998,16 @@ AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | ovn_strip_lflows], [0], [dnl table=??(ls_in_pre_acl ), priority=110 , match=(eth.mcast), action=(next;) table=??(ls_in_pre_acl ), priority=110 , match=(ip && inport == "S1-R1"), action=(next;) table=??(ls_in_pre_acl ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "S1-R1" && ip4 && ip4.src == 172.16.1.1 && icmp4.type == 0 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "S1-R1" && ip6 && ip6.src == fe80::ac:10ff:fe01:1 && icmp6.type == 129 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) table=??(ls_out_pre_acl ), priority=0 , match=(1), action=(next;) table=??(ls_out_pre_acl ), priority=100 , match=(ip), action=(reg0[[0]] = 1; next;) table=??(ls_out_pre_acl ), priority=110 , match=(eth.mcast), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(eth.src == $svc_monitor_mac), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(ip && outport == "S1-R1"), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "S1-R1" && ip4 && ip4.dst == 172.16.1.1 && icmp4.type == 8 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "S1-R1" && ip6 && ip6.dst == fe80::ac:10ff:fe01:1 && icmp6.type == 128 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) ]) # Clear the option 'enable_router_port_acl' and check the flows. Before that enable the option. @@ -14027,12 +14035,16 @@ AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | ovn_strip_lflows], [0], [dnl table=??(ls_in_pre_acl ), priority=110 , match=(eth.mcast), action=(next;) table=??(ls_in_pre_acl ), priority=110 , match=(ip && inport == "S1-R1"), action=(next;) table=??(ls_in_pre_acl ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "S1-R1" && ip4 && ip4.src == 172.16.1.1 && icmp4.type == 0 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "S1-R1" && ip6 && ip6.src == fe80::ac:10ff:fe01:1 && icmp6.type == 129 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) table=??(ls_out_pre_acl ), priority=0 , match=(1), action=(next;) table=??(ls_out_pre_acl ), priority=100 , match=(ip), action=(reg0[[0]] = 1; next;) table=??(ls_out_pre_acl ), priority=110 , match=(eth.mcast), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(eth.src == $svc_monitor_mac), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(ip && outport == "S1-R1"), action=(next;) table=??(ls_out_pre_acl ), priority=110 , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "S1-R1" && ip4 && ip4.dst == 172.16.1.1 && icmp4.type == 8 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "S1-R1" && ip6 && ip6.dst == fe80::ac:10ff:fe01:1 && icmp6.type == 128 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) ]) OVN_CLEANUP_NORTHD @@ -24122,6 +24134,54 @@ AT_CHECK([grep "ls1-to-spine" ls1flows | ovn_strip_lflows | sort], [0], [dnl OVN_CLEANUP_NORTHD AT_CLEANUP +AT_SETUP([Conntrack flows for router port ICMP echo]) +ovn_start + +check ovn-nbctl lr-add lr0 +check ovn-nbctl lrp-add lr0 lr0-ls0 00:00:00:00:00:01 10.0.0.1/24 fd00::1/64 +check ovn-nbctl ls-add ls0 +check ovn-nbctl lsp-add-router-port ls0 ls0-lr0 lr0-ls0 +check ovn-nbctl lsp-add ls0 vm1 -- \ + lsp-set-addresses vm1 "f0:00:00:00:00:01 10.0.0.10 fd00::10" + +# Stateful security group with only egress allow-all rules: +# egress is allowed, ingress is dropped. +check ovn-nbctl acl-add ls0 from-lport 100 "inport == \"vm1\" && ip" allow-related +check ovn-nbctl acl-add ls0 to-lport 100 "outport == \"vm1\" && ip" drop + +check ovn-nbctl --wait=sb sync + +AT_CHECK([ovn-sbctl dump-flows ls0 > ls0flows]) +AT_CAPTURE_FILE([ls0flows]) + +AT_CHECK([grep "ls0-lr0" ls0flows | ovn_strip_lflows | sort], [0], [dnl + table=??(ls_in_arp_rsp ), priority=100 , match=(arp.tpa == 10.0.0.1 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff && inport == "ls0-lr0"), action=(next;) + table=??(ls_in_arp_rsp ), priority=100 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:1 && nd.target == fd00::1 && inport == "ls0-lr0"), action=(next;) + table=??(ls_in_arp_rsp ), priority=100 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:1 && nd.target == fe80::200:ff:fe00:1 && inport == "ls0-lr0"), action=(next;) + table=??(ls_in_check_port_sec), priority=70 , match=(inport == "ls0-lr0"), action=(reg0[[18]] = 1; next;) + table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "ls0-lr0"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.1), action=(clone {outport = "ls0-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd00::1), action=(clone {outport = "ls0-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::200:ff:fe00:1), action=(clone {outport = "ls0-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_pre_acl ), priority=110 , match=(ip && inport == "ls0-lr0"), action=(next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "ls0-lr0" && ip4 && ip4.src == 10.0.0.1 && icmp4.type == 0 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_in_pre_acl ), priority=111 , match=(inport == "ls0-lr0" && ip6 && ip6.src == {fd00::1, fe80::200:ff:fe00:1} && icmp6.type == 129 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_in_pre_lb ), priority=110 , match=(ip && inport == "ls0-lr0"), action=(next;) + table=??(ls_out_pre_acl ), priority=110 , match=(ip && outport == "ls0-lr0"), action=(next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "ls0-lr0" && ip4 && ip4.dst == 10.0.0.1 && icmp4.type == 8 && icmp4.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_out_pre_acl ), priority=111 , match=(outport == "ls0-lr0" && ip6 && ip6.dst == {fd00::1, fe80::200:ff:fe00:1} && icmp6.type == 128 && icmp6.code == 0), action=(reg0[[0]] = 1; next;) + table=??(ls_out_pre_lb ), priority=110 , match=(ip && outport == "ls0-lr0"), action=(next;) +]) + +# There must be no special-case allow flows for router echo replies in the +# ACL eval tables; the built-in est-allow flow must still be present. +AT_CHECK([grep 'ls_in_acl_eval' ls0flows | grep 'ls0-lr0' | grep -q icmp], [1]) +AT_CHECK([grep 'ls_out_acl_eval' ls0flows | grep 'ls0-lr0' | grep -q icmp], [1]) +AT_CHECK([grep 'ls_in_acl_eval' ls0flows | grep -q 'ct.est && !ct.rel && ct.rpl && ct_mark.blocked == 0']) + +OVN_CLEANUP_NORTHD +AT_CLEANUP + AT_SETUP([Logical Switch Port Health Check - lflow/service monitor synchronization]) ovn_start diff --git a/tests/ovn.at b/tests/ovn.at index 780149467..4f80ecc1e 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -37204,9 +37204,11 @@ AT_CHECK([as hv1 ovn-appctl -t ovn-controller ct-zone-list | \ grep sw0-port2 -c], [0], [1 ]) -# There should be no ct-zone id allocated for sw0-lr0 and lr0-sw0 +# There should be a ct zone for the switch-side patch port sw0-lr0, but +# not for the router-side patch port lr0-sw0. AT_CHECK([as hv1 ovn-appctl -t ovn-controller ct-zone-list | \ -grep sw0-lr0], [1], []) +grep sw0-lr0 -c], [0], [1 +]) AT_CHECK([as hv1 ovn-appctl -t ovn-controller ct-zone-list | \ grep lr0-sw0], [1], []) @@ -37225,12 +37227,17 @@ grep sw1-port1 -c], [0], [1 # Attach sw1 to lr0 check ovn-nbctl lsp-add-router-port sw1 sw1-lr0 lr0-sw1 -# There should be no ct-zone id allocated for sw1-lr0 +# There is no ct-zone id allocated for the switch-side patch port +# sw1-lr0 while its router port does not exist yet. AT_CHECK([as hv1 ovn-appctl -t ovn-controller ct-zone-list | \ grep sw1-lr0], [1], []) check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 +# There should be a ct zone for the switch-side patch port sw1-lr0 +OVS_WAIT_UNTIL([test `as hv1 ovn-appctl -t ovn-controller ct-zone-list | \ +grep sw1-lr0 -c` -eq 1]) + # There should be no ct-zone id allocated for lr0-sw1 AT_CHECK([as hv1 ovn-appctl -t ovn-controller ct-zone-list | \ grep lr0-sw1], [1], []) @@ -43878,8 +43885,10 @@ check ovn-nbctl --wait=hv sync AS_BOX([With patch ports]) zone_list_hv1=$(as hv1 ovn-appctl -t ovn-controller ct-zone-list | sort) AT_CHECK([echo "$zone_list_hv1" | sed "s/ [[0-9]]*/ ??/" | sort], [0], [dnl +ls1-r1 ?? ls1_dnat ?? ls1_snat ?? +ls2-r1 ?? ls2_dnat ?? ls2_snat ?? lsp1 ?? @@ -43889,8 +43898,10 @@ r1_snat ?? zone_list_hv2=$(as hv2 ovn-appctl -t ovn-controller ct-zone-list | sort) AT_CHECK([echo "$zone_list_hv2" | sed "s/ [[0-9]]*/ ??/" | sort], [0], [dnl +ls1-r1 ?? ls1_dnat ?? ls1_snat ?? +ls2-r1 ?? ls2_dnat ?? ls2_snat ?? lsp2 ?? @@ -43956,8 +43967,10 @@ AS_BOX([With patch ports again]) check ovn-nbctl --wait=hv remove Logical_Router r1 options chassis=hv2 zone_list_hv1=$(as hv1 ovn-appctl -t ovn-controller ct-zone-list | sort) AT_CHECK([echo "$zone_list_hv1" | sed "s/ [[0-9]]*/ ??/" | sort], [0], [dnl +ls1-r1 ?? ls1_dnat ?? ls1_snat ?? +ls2-r1 ?? ls2_dnat ?? ls2_snat ?? lsp1 ?? @@ -43967,8 +43980,10 @@ r1_snat ?? zone_list_hv2=$(as hv2 ovn-appctl -t ovn-controller ct-zone-list | sort) AT_CHECK([echo "$zone_list_hv2" | sed "s/ [[0-9]]*/ ??/" | sort], [0], [dnl +ls1-r1 ?? ls1_dnat ?? ls1_snat ?? +ls2-r1 ?? ls2_dnat ?? ls2_snat ?? lsp2 ?? diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 3cb4b858a..b041d6385 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -21242,8 +21242,11 @@ AT_CHECK([grep "ext_lr" stdout], [0], [dnl ext_lr ?? ]) -# Check no CT zone for ls_lr. -AT_CHECK([grep "ls_lr" stdout], [1], []) +# Check that the switch-side patch port ls_lr has a CT zone allocated. +AT_CHECK([ovn-appctl -t ovn-controller ct-zone-list | sed "s/ [[0-9]]*/ ??/"], [0], [stdout]) +AT_CHECK([grep "ls_lr" stdout], [0], [dnl +ls_lr ?? +]) # Generate ICMP traffic to create conntrack entries. NS_CHECK_EXEC([vm1], [ping -q -c 2 -W 1 172.16.0.100 | FORMAT_PING], \ @@ -21261,10 +21264,12 @@ icmp,orig=(src=192.168.100.11,dst=172.16.0.100,id=<cleared>,type=8,code=0),reply NETNS_DAEMONIZE([external], [ncat -l -k 172.16.0.100 80], [external.pid]) NS_CHECK_EXEC([vm1], [ncat -z 172.16.0.100 80], [1]) -# Remove enable_router_port_acl and verify CT zone release. +# Remove enable_router_port_acl. The CT zone for ext_lr is kept: with +# router echo traffic tracked in the router port's zone, switch-side +# patch ports of local logical switches always have a CT zone. check ovn-nbctl lsp-set-options ext_lr router-port=lr_ext check ovn-nbctl --wait=sb sync -AT_CHECK([ovn-appctl -t ovn-controller ct-zone-list | grep "ext_lr"], [1], []) +AT_CHECK([ovn-appctl -t ovn-controller ct-zone-list | grep "ext_lr"], [0], [ignore]) # Re-enable enable_router_port_acl and verify CT zone reallocation. check ovn-nbctl lsp-set-options ext_lr router-port=lr_ext enable_router_port_acl=true @@ -24110,3 +24115,80 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ping to router port with stateful ACL]) +AT_KEYWORDS([acl]) + +CHECK_CONNTRACK() +ovn_start +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +check ovs-vsctl \ + -- set Open_vSwitch . external-ids:system-id=hv1 \ + -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true + +start_daemon ovn-controller + +check ovn-nbctl lr-add lr0 +check ovn-nbctl lrp-add lr0 lr0-ls0 00:00:00:00:00:01 10.0.0.1/24 fd00::1/64 +check ovn-nbctl ls-add ls0 +check ovn-nbctl lsp-add-router-port ls0 ls0-lr0 lr0-ls0 +check ovn-nbctl lsp-add ls0 vm1 +check ovn-nbctl lsp-set-addresses vm1 "f0:00:00:00:00:01 10.0.0.10 fd00::10" + +dnl Stateful security group with only egress allow-all rules: +dnl egress is allowed, ingress is dropped. +check ovn-nbctl acl-add ls0 from-lport 100 "inport == \"vm1\" && ip" allow-related +check ovn-nbctl acl-add ls0 to-lport 100 "outport == \"vm1\" && ip" drop + +ADD_NAMESPACES(vm1) +ADD_VETH(vm1, vm1, br-int, "10.0.0.10/24", "f0:00:00:00:00:01", "10.0.0.1") +NS_CHECK_EXEC([vm1], [ip -6 addr add fd00::10/64 dev vm1 nodad]) + +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# The router port must have a conntrack zone allocated on this chassis: +# the router echo request and reply are tracked in that zone. +OVS_WAIT_UNTIL([ovn-appctl -t ovn-controller ct-zone-list | grep -q ls0-lr0]) + +dnl The router echo reply is conntrack-established in the router port's +dnl zone, so the stateful SG's built-in est-allow lets it through even +dnl though ingress has no allow rule. +NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.1 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +NS_CHECK_EXEC([vm1], [ping -6 -q -c 3 -i 0.3 -w 2 fd00::1 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +dnl A higher-priority ACL that drops the echo request direction must +dnl block pings to the router, like any other established connection. +check ovn-nbctl --wait=hv acl-add ls0 from-lport 101 "inport == \"vm1\" && ip4 && icmp4.type == 8 && ip4.dst == 10.0.0.1" drop + +NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.1 | FORMAT_PING], [0], [dnl +7 packets transmitted, 0 received, 100% packet loss, time 0ms +]) + +check ovn-nbctl --wait=hv acl-add ls0 from-lport 101 "inport == \"vm1\" && ip6 && icmp6.type == 128 && ip6.dst == fd00::1" drop + +NS_CHECK_EXEC([vm1], [ping -6 -q -c 3 -i 0.3 -w 2 fd00::1 | FORMAT_PING], [0], [dnl +7 packets transmitted, 0 received, 100% packet loss, time 0ms +]) + +OVN_CLEANUP_CONTROLLER([hv1]) + +OVN_CLEANUP_NORTHD + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d +/connection dropped.*/d"]) + +AT_CLEANUP +]) -- 2.56.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
