Add LR option to commit all traffic that is not already commit by either NAT or LB. This ensures that the traffic is tracked, and we don't erroneously commit reply traffic, or reply traffic is not marked as invalid. The option is effective only on GW routers.
To achieve the commit we need to perform lookup on every packet that goes through LR pipeline whenever there is stateful NAT. The SNAT lookup requires additional flag as the unSNAT is happening in ingress pipeline and at that point we need to know if the packet is reply or not. This is not required for DNAT, because unDNAT stage happens in egress. Reported-at: https://issues.redhat.com/browse/FDP-787 Signed-off-by: Ales Musil <[email protected]> --- NEWS | 2 + include/ovn/logical-fields.h | 4 + lib/logical-fields.c | 4 + northd/northd.c | 144 ++++++++++- northd/northd.h | 39 +-- ovn-nb.xml | 8 + tests/ovn-northd.at | 251 +++++++++++++++++++ tests/system-ovn-kmod.at | 467 +++++++++++++++++++++++++++++++++++ 8 files changed, 896 insertions(+), 23 deletions(-) diff --git a/NEWS b/NEWS index 2f0c965a7..027d54088 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,8 @@ Post v24.09.0 - Improved handling of IPv6 traffic by enabling address prefix tracking in OVS for both IPv4 and IPv6 addresses, whenever possible, reducing the amount of IPv6 datapath flows. + - Add "options:commit-all" to LR, that enables commit of all traffic + when LR is stateful. OVN v24.09.0 - 13 Sep 2024 -------------------------- diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h index 6a87fc386..8e292980a 100644 --- a/include/ovn/logical-fields.h +++ b/include/ovn/logical-fields.h @@ -88,6 +88,7 @@ enum mff_log_flags_bits { MLF_ICMP_SNAT_BIT = 17, MLF_OVERRIDE_LOCAL_ONLY_BIT = 18, MLF_FROM_CTRL_BIT = 19, + MLF_UNSNAT_NEW_BIT = 20, }; /* MFF_LOG_FLAGS_REG flag assignments */ @@ -145,6 +146,9 @@ enum mff_log_flags { MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT), MLF_OVERRIDE_LOCAL_ONLY = (1 << MLF_OVERRIDE_LOCAL_ONLY_BIT), + + /* Indicate that the packet didn't go through unSNAT. */ + MLF_UNSNAT_NEW = (1 << MLF_UNSNAT_NEW_BIT), }; /* OVN logical fields diff --git a/lib/logical-fields.c b/lib/logical-fields.c index f49a0a79d..1026a8c9e 100644 --- a/lib/logical-fields.c +++ b/lib/logical-fields.c @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab) flags_str); snprintf(flags_str, sizeof flags_str, "flags[%d]", MLF_RX_FROM_TUNNEL_BIT); expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL, flags_str); + snprintf(flags_str, sizeof flags_str, "flags[%d]", + MLF_UNSNAT_NEW_BIT); + expr_symtab_add_subfield(symtab, "flags.unsnat_new", NULL, + flags_str); snprintf(flags_str, sizeof flags_str, "flags[%d]", MLF_FROM_CTRL_BIT); expr_symtab_add_subfield(symtab, "flags.from_ctrl", NULL, flags_str); diff --git a/northd/northd.c b/northd/northd.c index 3ff4326e6..d1f20a2a4 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -16323,7 +16323,8 @@ build_lrouter_out_snat_flow(struct lflow_table *lflows, * properly tracked so we can decide whether to perform SNAT on traffic * exiting the network. */ if (features->ct_commit_to_zone && features->ct_next_zone && - nat_entry->type == SNAT && !od->is_gw_router) { + nat_entry->type == SNAT && !od->is_gw_router && + !smap_get_bool(&od->nbr->options, "commit-all", false)) { /* For traffic that comes from SNAT network, initiate CT state before * entering S_ROUTER_OUT_SNAT to allow matching on various CT states. */ @@ -16627,6 +16628,8 @@ static void build_lr_nat_defrag_and_lb_default_flows( /* Packets are allowed by default. */ ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 0, "1", "next;", lflow_ref); ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 0, "1", "next;", lflow_ref); + ovn_lflow_add(lflows, od, S_ROUTER_IN_POST_UNSNAT, 0, "1", "next;", + lflow_ref); ovn_lflow_add(lflows, od, S_ROUTER_OUT_CHECK_DNAT_LOCAL, 0, "1", REGBIT_DST_NAT_IP_LOCAL" = 0; next;", lflow_ref); ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 0, "1", "next;", lflow_ref); @@ -16655,6 +16658,103 @@ static void build_lr_nat_defrag_and_lb_default_flows( lflow_ref); } +static void +build_gw_lrouter_commit_all(const struct ovn_datapath *od, + struct lflow_table *lflows, + bool stateful_snat, bool stateful_dnat, + const struct chassis_features *features, + struct lflow_ref *lflow_ref) +{ + ovs_assert(od->is_gw_router); + if (!(features->ct_commit_to_zone && features->ct_next_zone)) { + return; + } + + if (stateful_dnat) { + ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10, + "ip && (!ct.trk || !ct.rpl)", + "ct_next(dnat);", lflow_ref); + ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10, + "ip && ct.new", "ct_commit_to_zone(dnat);", lflow_ref); + } + + if (stateful_snat) { + /* We would lose the CT state especially the ct.new flag if we have + * mixed SNAT and DNAT on single LR. In order to know if we actually + * can commit into SNAT zone keep the flag in register. The SNAT flows + * in the egress pipeline can then check the flag and commit + * based on that. */ + ovn_lflow_add(lflows, od, S_ROUTER_IN_POST_UNSNAT, 10, + "ip && (!ct.trk || ct.new)", + "flags.unsnat_new = 1; next;", lflow_ref); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10, + "ip && (!ct.trk || !ct.rpl) && " + "flags.unsnat_new == 1", "ct_next(snat);", + lflow_ref); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10, + "ip && ct.new && flags.unsnat_new == 1", + "ct_commit_to_zone(snat);", lflow_ref); + } +} + +static void +build_dgp_lrouter_commit_all(const struct ovn_datapath *od, + const struct ovn_port *l3dgw_port, + struct lflow_table *lflows, + bool stateful_snat, bool stateful_dnat, + const struct chassis_features *features, + struct ds *match, struct lflow_ref *lflow_ref) +{ + ovs_assert(od->n_l3dgw_ports); + if (!(features->ct_commit_to_zone && features->ct_next_zone)) { + return; + } + if (stateful_dnat) { + ds_clear(match); + ds_put_format(match, "ip && (!ct.trk || !ct.rpl) && " + "inport == %s && is_chassis_resident(%s)", + l3dgw_port->json_key, l3dgw_port->cr_port->json_key); + ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10, ds_cstr(match), + "ct_next(dnat);", lflow_ref); + + ds_clear(match); + ds_put_format(match, "ip && ct.new && inport == %s && " + "is_chassis_resident(%s)", l3dgw_port->json_key, + l3dgw_port->cr_port->json_key); + ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10, ds_cstr(match), + "ct_commit_to_zone(dnat);", lflow_ref); + } + + if (stateful_snat) { + /* We would lose the CT state especially the ct.new flag if we have + * mixed SNAT and DNAT on single LR. In order to know if we actually + * can commit into SNAT zone keep the flag in register. The SNAT flows + * in the egress pipeline can then check the flag and commit + * based on that. */ + ds_clear(match); + ds_put_format(match, "ip && (!ct.trk || ct.new) && " + "inport == %s && is_chassis_resident(%s)", + l3dgw_port->json_key, l3dgw_port->cr_port->json_key); + ovn_lflow_add(lflows, od, S_ROUTER_IN_POST_UNSNAT, 10, ds_cstr(match), + "flags.unsnat_new = 1; next;", lflow_ref); + + ds_clear(match); + ds_put_format(match, "ip && (!ct.trk || !ct.rpl) && " + "flags.unsnat_new == 1 && outport == %s && " + "is_chassis_resident(%s)", l3dgw_port->json_key, + l3dgw_port->cr_port->json_key); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10, + ds_cstr(match), "ct_next(snat);", lflow_ref); + + ds_clear(match); + ds_put_format(match, "ip && ct.new && flags.unsnat_new == 1 && " + "outport == %s && is_chassis_resident(%s)", + l3dgw_port->json_key, l3dgw_port->cr_port->json_key); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10, ds_cstr(match), + "ct_commit_to_zone(snat);", lflow_ref); + } +} + static void build_lrouter_nat_defrag_and_lb( const struct lr_stateful_record *lr_stateful_rec, @@ -16665,6 +16765,8 @@ build_lrouter_nat_defrag_and_lb( const struct chassis_features *features, struct lflow_ref *lflow_ref) { + + bool commit_all = smap_get_bool(&od->nbr->options, "commit-all", false); /* Ingress DNAT (Priority 50/70). * * Allow traffic that is related to an existing conntrack entry. @@ -16739,9 +16841,11 @@ build_lrouter_nat_defrag_and_lb( ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50, "ip", "flags.loopback = 1; ct_dnat;", lflow_ref); - ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50, - "ip && ct.new", "ct_commit { } ; next; ", - lflow_ref); + if (!commit_all) { + ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50, + "ip && ct.new", "ct_commit { } ; next; ", + lflow_ref); + } } /* NAT rules are only valid on Gateway routers and routers with @@ -16759,6 +16863,9 @@ build_lrouter_nat_defrag_and_lb( !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs); bool lb_force_snat_ip = !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs); + bool stateful_dnat = lr_stateful_rec->has_lb_vip; + bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip || + lrnat_rec->lb_force_snat_router_ip); for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) { struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i]; @@ -16777,6 +16884,21 @@ build_lrouter_nat_defrag_and_lb( continue; } + if (!stateless) { + switch (nat_entry->type) { + case DNAT: + stateful_dnat = true; + break; + case SNAT: + stateful_snat = true; + break; + case DNAT_AND_SNAT: + stateful_snat = true; + stateful_dnat = true; + break; + } + } + /* S_ROUTER_IN_UNSNAT * Ingress UNSNAT table: It is for already established connections' * reverse traffic. i.e., SNAT has already been done in egress @@ -16989,6 +17111,20 @@ build_lrouter_nat_defrag_and_lb( } } + if (commit_all) { + if (od->is_gw_router) { + build_gw_lrouter_commit_all(od, lflows, stateful_snat, + stateful_dnat, features, lflow_ref); + } + + for (size_t i = 0; i < od->n_l3dgw_ports; i++) { + struct ovn_port *l3dgw_port = od->l3dgw_ports[i]; + build_dgp_lrouter_commit_all(od, l3dgw_port, lflows, + stateful_snat, stateful_dnat, + features, match, lflow_ref); + } + } + if (use_common_zone && od->nbr->n_nat) { ds_clear(match); ds_put_cstr(match, "ip && ct_mark.natted == 1"); diff --git a/northd/northd.h b/northd/northd.h index 9457a7be6..8b1010bdf 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -481,27 +481,28 @@ enum ovn_stage { PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") \ PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_REQ, 4, "lr_in_dhcp_relay_req") \ PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \ - PIPELINE_STAGE(ROUTER, IN, DEFRAG, 6, "lr_in_defrag") \ - PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 7, "lr_in_lb_aff_check") \ - PIPELINE_STAGE(ROUTER, IN, DNAT, 8, "lr_in_dnat") \ - PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 9, "lr_in_lb_aff_learn") \ - PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 10, "lr_in_ecmp_stateful") \ - PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 11, "lr_in_nd_ra_options") \ - PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 12, "lr_in_nd_ra_response") \ - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 13, "lr_in_ip_routing_pre") \ - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 14, "lr_in_ip_routing") \ - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 15, "lr_in_ip_routing_ecmp") \ - PIPELINE_STAGE(ROUTER, IN, POLICY, 16, "lr_in_policy") \ - PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 17, "lr_in_policy_ecmp") \ - PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP_CHK, 18, \ + PIPELINE_STAGE(ROUTER, IN, POST_UNSNAT, 6, "lr_in_post_unsnat") \ + PIPELINE_STAGE(ROUTER, IN, DEFRAG, 7, "lr_in_defrag") \ + PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 8, "lr_in_lb_aff_check") \ + PIPELINE_STAGE(ROUTER, IN, DNAT, 9, "lr_in_dnat") \ + PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 10, "lr_in_lb_aff_learn") \ + PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 11, "lr_in_ecmp_stateful") \ + PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 12, "lr_in_nd_ra_options") \ + PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 13, "lr_in_nd_ra_response") \ + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 14, "lr_in_ip_routing_pre") \ + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 15, "lr_in_ip_routing") \ + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 16, "lr_in_ip_routing_ecmp") \ + PIPELINE_STAGE(ROUTER, IN, POLICY, 17, "lr_in_policy") \ + PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 18, "lr_in_policy_ecmp") \ + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP_CHK, 19, \ "lr_in_dhcp_relay_resp_chk") \ - PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP, 19, \ + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP, 20, \ "lr_in_dhcp_relay_resp") \ - PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 20, "lr_in_arp_resolve") \ - PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 21, "lr_in_chk_pkt_len") \ - PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 22, "lr_in_larger_pkts") \ - PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 23, "lr_in_gw_redirect") \ - PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 24, "lr_in_arp_request") \ + PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 21, "lr_in_arp_resolve") \ + PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 22, "lr_in_chk_pkt_len") \ + PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 23, "lr_in_larger_pkts") \ + PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 24, "lr_in_gw_redirect") \ + PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 25, "lr_in_arp_request") \ \ /* Logical router egress stages. */ \ PIPELINE_STAGE(ROUTER, OUT, CHECK_DNAT_LOCAL, 0, \ diff --git a/ovn-nb.xml b/ovn-nb.xml index d82f9872b..6b0ce2901 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -2955,6 +2955,14 @@ or option is not present the limit is not set and the zone limit is derived from OvS default datapath limit. </column> + + <column name="options" key="commit-all" type='{"type": "boolean"}'> + When enabled the LR will commit traffic in a zone that is stateful. + The traffic is not commited to both zones but it is selective based + whether there is stateful DNAT/SNAT or both. The commit all will + prevent issues with <code>ct.inv</code> packets as it will prevent + the commit of reply traffic, which could happen in some cases. + </column> </group> <group title="Common Columns"> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index df646ec68..8fdcabc93 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -14445,3 +14445,254 @@ AT_CHECK([ovn-sbctl lflow-list S1 | grep ls_out_acl_action | grep priority=500 | AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD_NO_HV_PARALLELIZATION([ +AT_SETUP([ovn -- LR commit-all]) +ovn_start + +check ovn-nbctl ls-add sw0 +check ovn-nbctl lr-add lr0 +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 +check ovn-nbctl lsp-add sw0 sw0-lr0 +check ovn-nbctl lsp-set-type sw0-lr0 router +check ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01 +check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 +check ovn-nbctl set logical_router lr0 options:commit-all="true" +check ovn-nbctl --wait=sb sync + +check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \ + -- set chassis gw1 other_config:ct-commit-to-zone="true" \ + -- set chassis gw1 other_config:ct-next-zone="true" + +ovn-sbctl dump-flows lr0 > lr0flows +AT_CAPTURE_FILE([lr0flows]) + +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_post_unsnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) +]) + + +# Create a distributed gw port on lr0 +check ovn-nbctl ls-add public +check ovn-nbctl lrp-add lr0 lr0-public 00:00:00:00:ff:02 172.168.0.10/24 +check ovn-nbctl lrp-set-gateway-chassis lr0-public gw1 + +check ovn-nbctl lsp-add public public-lr0 \ + -- set Logical_Switch_Port public-lr0 \ + type=router options:router-port=lr0-public \ + -- lsp-set-addresses public-lr0 router + +# Add SNAT +check ovn-nbctl lr-nat-add lr0 snat 172.168.0.10 10.0.0.0/24 + +ovn-sbctl dump-flows lr0 > lr0flows +AT_CAPTURE_FILE([lr0flows]) + +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_post_unsnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_post_unsnat ), priority=10 , match=(ip && (!ct.trk || ct.new) && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(flags.unsnat_new = 1; next;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_undnat ), priority=10 , match=(ip && (!ct.trk || !ct.rpl) && flags.unsnat_new == 1 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_next(snat);) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=10 , match=(ip && ct.new && flags.unsnat_new == 1 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_commit_to_zone(snat);) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);) +]) + +check ovn-nbctl lr-nat-del lr0 + +# Add LB to lr0 +check ovn-nbctl lb-add lb0 172.168.0.100:8082 "10.0.0.50:82,10.0.0.60:82" +check ovn-nbctl lr-lb-add lr0 lb0 +check ovn-nbctl --wait=sb sync + +ovn-sbctl dump-flows lr0 > lr0flows +AT_CAPTURE_FILE([lr0flows]) + +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_post_unsnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) + table=??(lr_in_defrag ), priority=10 , match=(ip && (!ct.trk || !ct.rpl) && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_next(dnat);) + table=??(lr_in_defrag ), priority=100 , match=(ip && ip4.dst == 172.168.0.100), action=(ct_dnat;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_dnat ), priority=10 , match=(ip && ct.new && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_commit_to_zone(dnat);) + table=??(lr_in_dnat ), priority=120 , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082 && is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);) + table=??(lr_in_dnat ), priority=50 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;) + table=??(lr_in_dnat ), priority=50 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; ct_commit_nat;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) +]) + +# Add SNAT again +check ovn-nbctl lr-nat-add lr0 snat 172.168.0.10 10.0.0.0/24 +check ovn-nbctl --wait=sb sync + +ovn-sbctl dump-flows lr0 > lr0flows +AT_CAPTURE_FILE([lr0flows]) + +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_post_unsnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_post_unsnat ), priority=10 , match=(ip && (!ct.trk || ct.new) && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(flags.unsnat_new = 1; next;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) + table=??(lr_in_defrag ), priority=10 , match=(ip && (!ct.trk || !ct.rpl) && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_next(dnat);) + table=??(lr_in_defrag ), priority=100 , match=(ip && ip4.dst == 172.168.0.100), action=(ct_dnat;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_dnat ), priority=10 , match=(ip && ct.new && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_commit_to_zone(dnat);) + table=??(lr_in_dnat ), priority=120 , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082 && is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);) + table=??(lr_in_dnat ), priority=50 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;) + table=??(lr_in_dnat ), priority=50 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; ct_commit_nat;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_undnat ), priority=10 , match=(ip && (!ct.trk || !ct.rpl) && flags.unsnat_new == 1 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_next(snat);) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=10 , match=(ip && ct.new && flags.unsnat_new == 1 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_commit_to_zone(snat);) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);) +]) + +# Make the logical router as Gateway router +check ovn-nbctl lrp-del-gateway-chassis lr0-public gw1 +check ovn-nbctl set logical_router lr0 options:chassis=gw1 +check ovn-nbctl --wait=sb sync + +ovn-sbctl dump-flows lr0 > lr0flows +AT_CAPTURE_FILE([lr0flows]) + +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_post_unsnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_post_unsnat ), priority=10 , match=(ip && (!ct.trk || ct.new)), action=(flags.unsnat_new = 1; next;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) + table=??(lr_in_defrag ), priority=10 , match=(ip && (!ct.trk || !ct.rpl)), action=(ct_next(dnat);) + table=??(lr_in_defrag ), priority=100 , match=(ip && ip4.dst == 172.168.0.100), action=(ct_dnat;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_dnat ), priority=10 , match=(ip && ct.new), action=(ct_commit_to_zone(dnat);) + table=??(lr_in_dnat ), priority=120 , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082), action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);) + table=??(lr_in_dnat ), priority=50 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;) + table=??(lr_in_dnat ), priority=50 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; ct_commit_nat;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_undnat ), priority=10 , match=(ip && (!ct.trk || !ct.rpl) && flags.unsnat_new == 1), action=(ct_next(snat);) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=10 , match=(ip && ct.new && flags.unsnat_new == 1), action=(ct_commit_to_zone(snat);) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=25 , match=(ip && ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);) +]) + +# Disable commit all for the router +check ovn-nbctl remove logical_router lr0 options commit-all +check ovn-nbctl --wait=sb sync + +ovn-sbctl dump-flows lr0 > lr0flows +AT_CAPTURE_FILE([lr0flows]) + +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_post_unsnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) + table=??(lr_in_defrag ), priority=100 , match=(ip && ip4.dst == 172.168.0.100), action=(ct_dnat;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_dnat ), priority=120 , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082), action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);) + table=??(lr_in_dnat ), priority=50 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;) + table=??(lr_in_dnat ), priority=50 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.est && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; next;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; ct_commit_nat;) + table=??(lr_in_dnat ), priority=70 , match=(ct.rel && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; ct_commit_nat;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_undnat ), priority=50 , match=(ip && ct.new), action=(ct_commit { } ; next; ) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=25 , match=(ip && ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);) +]) + +AT_CLEANUP +]) diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at index f7745b979..ba6bee27d 100644 --- a/tests/system-ovn-kmod.at +++ b/tests/system-ovn-kmod.at @@ -1172,3 +1172,470 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([LR DGP - commit-all]) +AT_KEYWORDS([ovnnat]) + +CHECK_CONNTRACK() +CHECK_CONNTRACK_NAT() +ovn_start +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +# Set external-ids in br-int needed for ovn-controller +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 ovn-controller +start_daemon ovn-controller + +# Logical network: +# One LR R1 with switches foo (192.168.1.0/24 fd11::/64), bar (192.168.2.0/24 fd12::/64), +# and alice (172.16.1.0/24 fd20::/64) connected to it. The port between R1 and +# alice is the router gateway port where the R1 NAT rules are applied. +# +# foo -- R1 -- alice +# | +# bar ---- + +check ovn-nbctl lr-add R1 +check ovn-nbctl set logical_router R1 options:commit-all="true" + +check ovn-nbctl ls-add foo +check ovn-nbctl ls-add bar +check ovn-nbctl ls-add alice + +check ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd11::1/64 +check ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 fd12::1/64 +check ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 fd20::1/64 \ + -- lrp-set-gateway-chassis alice hv1 + +# Connect foo to R1 +check ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ + type=router options:router-port=foo \ + -- lsp-set-addresses rp-foo router + +# Connect bar to R1 +check ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ + type=router options:router-port=bar \ + -- lsp-set-addresses rp-bar router + +# Connect alice to R1 +check ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ + type=router options:router-port=alice \ + -- lsp-set-addresses rp-alice router + +# Logical port 'foo1' in switch 'foo'. +ADD_NAMESPACES(foo1) +ADD_VETH(foo1, foo1, br-int, "fd11::2", "f0:00:00:01:02:03", \ + "fd11::1", "nodad", "192.168.1.2/24", "192.168.1.1") +check ovn-nbctl lsp-add foo foo1 \ +-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2 fd11::2" + +# Logical port 'foo2' in switch 'foo'. +ADD_NAMESPACES(foo2) +ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \ + "fd11::1", "nodad", "192.168.1.3/24", "192.168.1.1") +check ovn-nbctl lsp-add foo foo2 \ +-- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3 fd11::3" + +# Logical port 'bar1' in switch 'bar'. +ADD_NAMESPACES(bar1) +ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \ + "fd12::1", "nodad", "192.168.2.2/24", "192.168.2.1") +check ovn-nbctl lsp-add bar bar1 \ +-- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2 fd12::2" + +# Logical port 'alice1' in switch 'alice'. +ADD_NAMESPACES(alice1) +ADD_VETH(alice1, alice1, br-int, "fd20::2/64", "f0:00:00:01:02:05", \ + "fd20::1", "nodad", "172.16.1.2/24", "172.16.1.1") +check ovn-nbctl lsp-add alice alice1 \ +-- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2 fd20::2" + +# Add DNAT and SNAT rules +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04 +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.2.2 bar1 00:00:02:02:03:05 +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02:03:04 +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd12::2 bar1 00:00:02:02:03:05 + +# Add a SNAT rule +check ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.1.0/24 +check ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64 + +check ovn-nbctl --wait=hv sync +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)']) +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)']) + + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(192.168.2.2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=172.16.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(fd12::2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd20::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::2,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(172.16.1.2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.4,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 fd20::2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(fd20::2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd12::2,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd20::4,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +# East-West NAT: 'foo1' pings 'bar1' using 172.16.1.4. +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Check conntrack entries. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(172.16.1.4) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared> +icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Check conntrack entries. First SNAT of 'foo1' address happens. +# Then DNAT of 'bar1' address happens (listed first below). +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared> +icmpv6,orig=(src=fd20::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Check conntrack entries. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(172.16.1.1) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared> +icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +# East-West NAT: 'foo2' pings 'bar1' using fd20::4. +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared> +icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +OVS_APP_EXIT_AND_WAIT([ovn-controller]) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d +/connection dropped.*/d"]) +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([LR GW router - commit-all]) +AT_KEYWORDS([ovnnat]) + +CHECK_CONNTRACK() +CHECK_CONNTRACK_NAT() +ovn_start +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +# Set external-ids in br-int needed for ovn-controller +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 ovn-controller +start_daemon ovn-controller + +# Logical network: +# One LR R1 with switches foo (192.168.1.0/24 fd11::/64), bar (192.168.2.0/24 fd12::/64), +# and alice (172.16.1.0/24 fd20::/64) connected to it. The port between R1 and +# alice is the router gateway port where the R1 NAT rules are applied. +# +# foo -- R1 -- alice +# | +# bar ---- + +check ovn-nbctl lr-add R1 +check ovn-nbctl set logical_router R1 options:commit-all="true" + +check ovn-nbctl ls-add foo +check ovn-nbctl ls-add bar +check ovn-nbctl ls-add alice + +check ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd11::1/64 +check ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 fd12::1/64 +check ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 fd20::1/64 \ + -- set logical_router R1 options:chassis="hv1" + +# Connect foo to R1 +check ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ + type=router options:router-port=foo \ + -- lsp-set-addresses rp-foo router + +# Connect bar to R1 +check ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ + type=router options:router-port=bar \ + -- lsp-set-addresses rp-bar router + +# Connect alice to R1 +check ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ + type=router options:router-port=alice \ + -- lsp-set-addresses rp-alice router + +# Logical port 'foo1' in switch 'foo'. +ADD_NAMESPACES(foo1) +ADD_VETH(foo1, foo1, br-int, "fd11::2", "f0:00:00:01:02:03", \ + "fd11::1", "nodad", "192.168.1.2/24", "192.168.1.1") +check ovn-nbctl lsp-add foo foo1 \ +-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2 fd11::2" + +# Logical port 'foo2' in switch 'foo'. +ADD_NAMESPACES(foo2) +ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \ + "fd11::1", "nodad", "192.168.1.3/24", "192.168.1.1") +check ovn-nbctl lsp-add foo foo2 \ +-- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3 fd11::3" + +# Logical port 'bar1' in switch 'bar'. +ADD_NAMESPACES(bar1) +ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \ + "fd12::1", "nodad", "192.168.2.2/24", "192.168.2.1") +check ovn-nbctl lsp-add bar bar1 \ +-- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2 fd12::2" + +# Logical port 'alice1' in switch 'alice'. +ADD_NAMESPACES(alice1) +ADD_VETH(alice1, alice1, br-int, "fd20::2/64", "f0:00:00:01:02:05", \ + "fd20::1", "nodad", "172.16.1.2/24", "172.16.1.1") +check ovn-nbctl lsp-add alice alice1 \ +-- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2 fd20::2" + +# Add DNAT and SNAT rules +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04 +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.2.2 bar1 00:00:02:02:03:05 +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02:03:04 +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd12::2 bar1 00:00:02:02:03:05 + +# Add a SNAT rule +check ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.1.0/24 +check ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64 + +check ovn-nbctl --wait=hv sync +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)']) +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)']) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(192.168.2.2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(fd12::2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared> +icmpv6,orig=(src=fd11::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(192.168.2.2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(fd12::2) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::3,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared> +icmpv6,orig=(src=fd11::3,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 192.168.1.3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(192.168.1.3) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=0,code=0),zone=<cleared> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 fd11::3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(fd11::3) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd12::2,dst=fd11::3,id=<cleared>,type=128,code=0),reply=(src=fd11::3,dst=fd12::2,id=<cleared>,type=129,code=0),zone=<cleared> +icmpv6,orig=(src=fd12::2,dst=fd11::3,id=<cleared>,type=128,code=0),reply=(src=fd11::3,dst=fd20::4,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Check conntrack entries. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(172.16.1.4) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(172.16.1.3) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Check conntrack entries. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +# East-West NAT: 'foo2' pings 'bar1' using 172.16.1.4. +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Check conntrack entries. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(172.16.1.1) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +# East-West NAT: 'foo2' pings 'bar1' using fd20::4. +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Check conntrack entries. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::3,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \ +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl +icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared> +]) + +OVS_APP_EXIT_AND_WAIT([ovn-controller]) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d +/connection dropped.*/d"]) +AT_CLEANUP +]) -- 2.47.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
