'passthrough' expects source network in 'logical_ip' column and destination network in 'external_ip' column. For the traffic that goes from source to destination 'passthrough' disables NAT by adding a priority-100 flow with a match of 'ip && ip4.src == A && ip4.dst == B' and an action of 'next;'.
Signed-off-by: Rostyslav Fridman rostyslav_frid...@epam.com<mailto:rostyslav_frid...@epam.com> I have created a PR with this patch at https://github.com/openvswitch/ovs/pull/285 --- diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index aab8f6974..d269c872a 100644 --- a/ovn/northd/ovn-northd.8.xml +++ b/ovn/northd/ovn-northd.8.xml @@ -2384,6 +2384,15 @@ nd_ns { <code>flags.force_snat_for_lb == 1 && ip</code> with an action <code>ct_snat(<var>B</var>);</code>. </p> + <p> + For each configuration in the OVN Northbound database, that asks + NOT to change the source IP address of a packet with address + <var>A</var> is going to destination <var>B</var> or NOT to change + the source IP address of a packet thath belongs to network <var>A</var> + that is going to destination <var>B</var>, a priority-100 flow with + a match of <code>ip && ip4.src == <var>A</var> && + ip4.dst == <var>B</var></code> and an action of <code>next;</code>. + </p> <p> For each configuration in the OVN Northbound database, that asks to change the source IP address of a packet from an IP address of diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index de0c06d4b..7d3c222b1 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -6002,6 +6002,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, nat = op->od->nbr->nat[i]; + if (!strcmp(nat->type, "passthrough")) { + continue; + } + ovs_be32 ip; if (!ip_parse(nat->external_ip, &ip) || !ip) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); @@ -6359,19 +6363,34 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ovs_be32 ip, mask; - char *error = ip_parse_masked(nat->external_ip, &ip, &mask); - if (error || mask != OVS_BE32_MAX) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad external ip %s for nat", - nat->external_ip); - free(error); - continue; + /* Check the validity of nat->external_ip. 'external_ip' can + * be a subnet when the type is "passthrough". */ + if (!strcmp(nat->type, "passthrough")) { + char *error = ip_parse_masked(nat->external_ip, &ip, &mask); + if (error) { + static struct vlog_rate_limit rl + = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad external ip %s for passthrough", + nat->external_ip); + free(error); + continue; + } + } else { + char *error = ip_parse_masked(nat->external_ip, &ip, &mask); + if (error || mask != OVS_BE32_MAX) { + static struct vlog_rate_limit rl + = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad external ip %s for nat", + nat->external_ip); + free(error); + continue; + } } /* Check the validity of nat->logical_ip. 'logical_ip' can - * be a subnet when the type is "snat". */ - error = ip_parse_masked(nat->logical_ip, &ip, &mask); - if (!strcmp(nat->type, "snat")) { + * be a subnet when the type is "snat" or "passthrough". */ + char *error = ip_parse_masked(nat->logical_ip, &ip, &mask); + if (!strcmp(nat->type, "snat") || !strcmp(nat->type, "passthrough")) { if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); @@ -6546,6 +6565,17 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_cstr(&match), ds_cstr(&actions)); } + /* Egress SNAT table: Skip packets that have a specific 'passthrough' + * rule. */ + if (!strcmp(nat->type, "passthrough")) { + ds_clear(&match); + ds_put_format(&match, "ip && ip4.src == %s && ip4.dst == %s", + nat->logical_ip, + nat->external_ip); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100, + ds_cstr(&match), "next;"); + } + /* Egress SNAT table: Packets enter the egress pipeline with * source ip address that needs to be SNATted to a external ip * address. */ diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema index 2c87cbba7..520783227 100644 --- a/ovn/ovn-nb.ovsschema +++ b/ovn/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "5.16.0", - "cksum": "923459061 23095", + "version": "5.16.1", + "cksum": "2460088216 23171", "tables": { "NB_Global": { "columns": { @@ -343,6 +343,7 @@ "type": {"type": {"key": {"type": "string", "enum": ["set", ["dnat", "snat", + "passthrough", "dnat_and_snat" ]]}}}, "external_ids": { diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index cbaa9495f..203141a86 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -2029,6 +2029,14 @@ <ref column="logical_ip"/> is SNATed into the IP address in <ref column="external_ip"/>. </li> + <li> + When <ref column="type"/> is <code>passthrough</code>, IP packets + with their source IP address that either matches the IP address + in <ref column="logical_ip"/> or is in the network provided by + <ref column="logical_ip"/> is not SNATed to destination IP address + in <ref column="external_ip"/> or destination network provided by + <ref column="external_ip"/> and is allowed to pass-through. + </li> <li> When <ref column="type"/> is <code>dnat_and_snat</code>, the externally visible IP address <ref column="external_ip"/> is diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index e86ab7f7a..a96abef44 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -3832,6 +3832,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx) const char *external_ip = ctx->argv[3]; const char *logical_ip = ctx->argv[4]; char *new_logical_ip = NULL; + char *new_external_ip = NULL; char *error = lr_by_name_or_uuid(ctx, ctx->argv[1], true, &lr); if (error) { @@ -3840,20 +3841,32 @@ nbctl_lr_nat_add(struct ctl_context *ctx) } if (strcmp(nat_type, "dnat") && strcmp(nat_type, "snat") - && strcmp(nat_type, "dnat_and_snat")) { - ctl_error(ctx, "%s: type must be one of \"dnat\", \"snat\" and " + && strcmp(nat_type, "dnat_and_snat") && strcmp(nat_type, "passthrough")) { + ctl_error(ctx, "%s: type must be one of \"dnat\", \"snat\", \"passthrough\" and " "\"dnat_and_snat\".", nat_type); return; } ovs_be32 ipv4 = 0; unsigned int plen; - if (!ip_parse(external_ip, &ipv4)) { - ctl_error(ctx, "%s: should be an IPv4 address.", external_ip); - return; + if (strcmp("passthrough", nat_type)) { + if (!ip_parse(external_ip, &ipv4)) { + ctl_error(ctx, "%s: should be an IPv4 address.", external_ip); + return; + } + new_external_ip = xstrdup(external_ip); + } else { + error = ip_parse_cidr(external_ip, &ipv4, &plen); + if (error) { + free(error); + ctl_error(ctx, "%s: should be an IPv4 address or network.", + external_ip); + return; + } + new_external_ip = normalize_ipv4_prefix(ipv4, plen); } - if (strcmp("snat", nat_type)) { + if (strcmp("snat", nat_type) && strcmp("passthrough", nat_type)) { if (!ip_parse(logical_ip, &ipv4)) { ctl_error(ctx, "%s: should be an IPv4 address.", logical_ip); return; @@ -3911,9 +3924,9 @@ nbctl_lr_nat_add(struct ctl_context *ctx) for (size_t i = 0; i < lr->n_nat; i++) { const struct nbrec_nat *nat = lr->nat[i]; if (!strcmp(nat_type, nat->type)) { - if (!strcmp(is_snat ? new_logical_ip : external_ip, + if (!strcmp(is_snat ? new_logical_ip : new_external_ip, is_snat ? nat->logical_ip : nat->external_ip)) { - if (!strcmp(is_snat ? external_ip : new_logical_ip, + if (!strcmp(is_snat ? new_external_ip : new_logical_ip, is_snat ? nat->external_ip : nat->logical_ip)) { if (may_exist) { nbrec_nat_verify_logical_port(nat); @@ -3925,7 +3938,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx) } ctl_error(ctx, "%s, %s: a NAT with this external_ip " "and logical_ip already exists", - external_ip, new_logical_ip); + new_external_ip, new_logical_ip); free(new_logical_ip); return; } else { @@ -3933,7 +3946,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx) "already exists", nat_type, is_snat ? "logical_ip" : "external_ip", - is_snat ? new_logical_ip : external_ip); + is_snat ? new_logical_ip : new_external_ip); free(new_logical_ip); return; } @@ -3944,7 +3957,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx) /* Create the NAT. */ struct nbrec_nat *nat = nbrec_nat_insert(ctx->txn); nbrec_nat_set_type(nat, nat_type); - nbrec_nat_set_external_ip(nat, external_ip); + nbrec_nat_set_external_ip(nat, new_external_ip); nbrec_nat_set_logical_ip(nat, new_logical_ip); if (logical_port && external_mac) { nbrec_nat_set_logical_port(nat, logical_port); @@ -3982,8 +3995,8 @@ nbctl_lr_nat_del(struct ctl_context *ctx) const char *nat_type = ctx->argv[2]; if (strcmp(nat_type, "dnat") && strcmp(nat_type, "snat") - && strcmp(nat_type, "dnat_and_snat")) { - ctl_error(ctx, "%s: type must be one of \"dnat\", \"snat\" and " + && strcmp(nat_type, "dnat_and_snat") && strcmp(nat_type, "passthrough")) { + ctl_error(ctx, "%s: type must be one of \"dnat\", \"snat\", \"passthrough\" and " "\"dnat_and_snat\".", nat_type); return; } diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 18c5c1d42..2eee466ee 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -404,7 +404,7 @@ dnl --------------------------------------------------------------------- OVN_NBCTL_TEST([ovn_nbctl_nats], [NATs], [ AT_CHECK([ovn-nbctl lr-add lr0]) AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [], -[ovn-nbctl: snatt: type must be one of "dnat", "snat" and "dnat_and_snat". +[ovn-nbctl: snatt: type must be one of "dnat", "snat", "passthrough" and "dnat_and_snat". ]) AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2a 192.168.1.2], [1], [], [ovn-nbctl: 30.0.0.2a: should be an IPv4 address. _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev