Skip to content

Commit

Permalink
External IP based NAT: NORTHD changes to use allowed/exempted externa…
Browse files Browse the repository at this point in the history
…l ip

This patch has northd changes which consumes allowed/exempted external ip
configuration per NAT rule in logical flow.

Allowed external ip range adds an additional match criteria in
snat/dnat logical flow rules.

For example, if an allowed_external_ip address set ("abcd")
is configured for following NAT rule.
TYPE             EXTERNAL_IP        LOGICAL_IP
snat             10.15.24.135       50.0.0.10

Then logical flow will look like following:
..(lr_out_snat)...match=(ip && .... && ip4.dst == $abcd), action=(ct_snat(...);)

Exempted external ip range adds an additional flow at priority+1
to bypass the NAT pipeline if external ip is in extempted external
ip address set.
For example, if the same NAT rule mentioned aboe has an
exempted_external_ip address set ("efgh"), then
logical flow will look like following:

..(lr_out_snat), priority=162...match=(ip && .... && ip4.dst == $efgh), action=(next;)
..(lr_out_snat), priority=161...match=(ip && ....), action=(ct_snat(10.15.24.135);)

Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
Signed-off-by: Numan Siddique <numans@ovn.org>
  • Loading branch information
ankursharm authored and numansiddique committed Sep 8, 2020
1 parent 20bc58a commit fc79d69
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 0 deletions.
67 changes: 67 additions & 0 deletions northd/ovn-northd.8.xml
Expand Up @@ -2471,6 +2471,23 @@ icmp6 {
dnat_and_snat and has <code>stateless=true</code> in the
options, then the action would be <code>ip4/6.dst=
(<var>B</var>)</code>.

<p>
If the NAT rule has <code>allowed_ext_ips</code> configured, then
there is an additional match <code>ip4.src == <var>allowed_ext_ips
</var></code>. Similarly, for IPV6, match would be <code>ip6.src ==
<var>allowed_ext_ips</var></code>.
</p>

<p>
If the NAT rule has <code>exempted_ext_ips</code> set, then
there is an additional flow configured at priority 101.
The flow matches if source ip is an <code>exempted_ext_ip</code>
and the action is <code>next; </code>. This flow is used to
bypass the ct_dnat action for a packet originating from
<code>exempted_ext_ips</code>.
</p>

</li>

<li>
Expand Down Expand Up @@ -2515,6 +2532,22 @@ icmp6 {
<code>redirect-chassis</code>.
</p>

<p>
If the NAT rule has <code>allowed_ext_ips</code> configured, then
there is an additional match <code>ip4.src == <var>allowed_ext_ips
</var></code>. Similarly, for IPV6, match would be <code>ip6.src ==
<var>allowed_ext_ips</var></code>.
</p>

<p>
If the NAT rule has <code>exempted_ext_ips</code> set, then
there is an additional flow configured at priority 101.
The flow matches if source ip is an <code>exempted_ext_ip</code>
and the action is <code>next; </code>. This flow is used to
bypass the ct_dnat action for a packet originating from
<code>exempted_ext_ips</code>.
</p>

<p>
A priority-0 logical flow with match <code>1</code> has actions
<code>next;</code>.
Expand Down Expand Up @@ -3301,6 +3334,23 @@ nd_ns {
options, then the action would be <code>ip4/6.src=
(<var>B</var>)</code>.
</p>

<p>
If the NAT rule has <code>allowed_ext_ips</code> configured, then
there is an additional match <code>ip4.dst == <var>allowed_ext_ips
</var></code>. Similarly, for IPV6, match would be <code>ip6.dst ==
<var>allowed_ext_ips</var></code>.
</p>

<p>
If the NAT rule has <code>exempted_ext_ips</code> set, then
there is an additional flow configured at the priority + 1 of
corresponding NAT rule. The flow matches if destination ip
is an <code>exempted_ext_ip</code> and the action is <code>next;
</code>. This flow is used to bypass the ct_snat action for a packet
which is destinted to <code>exempted_ext_ips</code>.
</p>

<p>
A priority-0 logical flow with match <code>1</code> has actions
<code>next;</code>.
Expand Down Expand Up @@ -3343,6 +3393,23 @@ nd_ns {
<var>A</var> in the NAT rule. This allows upstream MAC
learning to point to the correct chassis.
</p>

<p>
If the NAT rule has <code>allowed_ext_ips</code> configured, then
there is an additional match <code>ip4.dst == <var>allowed_ext_ips
</var></code>. Similarly, for IPV6, match would be <code>ip6.dst ==
<var>allowed_ext_ips</var></code>.
</p>

<p>
If the NAT rule has <code>exempted_ext_ips</code> set, then
there is an additional flow configured at the priority + 1 of
corresponding NAT rule. The flow matches if destination ip
is an <code>exempted_ext_ip</code> and the action is <code>next;
</code>. This flow is used to bypass the ct_snat action for a flow
which is destinted to <code>exempted_ext_ips</code>.
</p>

</li>

<li>
Expand Down
103 changes: 103 additions & 0 deletions northd/ovn-northd.c
Expand Up @@ -8283,6 +8283,77 @@ lrouter_nat_is_stateless(const struct nbrec_nat *nat)
return false;
}

/* Handles the match criteria and actions in logical flow
* based on external ip based NAT rule filter.
*
* For ALLOWED_EXT_IPs, we will add an additional match criteria
* of comparing ip*.src/dst with the allowed external ip address set.
*
* For EXEMPTED_EXT_IPs, we will have an additional logical flow
* where we compare ip*.src/dst with the exempted external ip address set
* and action says "next" instead of ct*.
*/
static inline void
lrouter_nat_add_ext_ip_match(struct ovn_datapath *od,
struct hmap *lflows, struct ds *match,
const struct nbrec_nat *nat,
bool is_v6, bool is_src, ovs_be32 mask)
{
struct nbrec_address_set *allowed_ext_ips = nat->allowed_ext_ips;
struct nbrec_address_set *exempted_ext_ips = nat->exempted_ext_ips;
bool is_gw_router = !od->l3dgw_port;

ovs_assert(allowed_ext_ips || exempted_ext_ips);

if (allowed_ext_ips) {
ds_put_format(match, " && ip%s.%s == $%s",
is_v6 ? "6" : "4",
is_src ? "src" : "dst",
allowed_ext_ips->name);
} else if (exempted_ext_ips) {
struct ds match_exempt = DS_EMPTY_INITIALIZER;
enum ovn_stage stage = is_src ? S_ROUTER_IN_DNAT : S_ROUTER_OUT_SNAT;
uint16_t priority;

/* Priority of logical flows corresponding to exempted_ext_ips is
* +1 of the corresponding regulr NAT rule.
* For example, if we have following NAT rule and we associate
* exempted external ips to it:
* "ovn-nbctl lr-nat-add router dnat_and_snat 10.15.24.139 50.0.0.11"
*
* And now we associate exempted external ip address set to it.
* Now corresponding to above rule we will have following logical
* flows:
* lr_out_snat...priority=162, match=(..ip4.dst == $exempt_range),
* action=(next;)
* lr_out_snat...priority=161, match=(..), action=(ct_snat(....);)
*
*/
if (is_src) {
/* S_ROUTER_IN_DNAT uses priority 100 */
priority = 100 + 1;
} else {
/* S_ROUTER_OUT_SNAT uses priority (mask + 1 + 128 + 1) */
priority = count_1bits(ntohl(mask)) + 2;

if (!is_gw_router) {
priority += 128;
}
}

ds_clone(&match_exempt, match);
ds_put_format(&match_exempt, " && ip%s.%s == $%s",
is_v6 ? "6" : "4",
is_src ? "src" : "dst",
exempted_ext_ips->name);

ovn_lflow_add_with_hint(lflows, od, stage, priority,
ds_cstr(&match_exempt), "next;",
&nat->header_);
ds_destroy(&match_exempt);
}
}

/* Builds the logical flow that replies to ARP requests for an 'ip_address'
* owned by the router. The flow is inserted in table S_ROUTER_IN_IP_INPUT
* with the given priority.
Expand Down Expand Up @@ -9344,6 +9415,18 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
bool is_v6 = false;
bool stateless = lrouter_nat_is_stateless(nat);
struct nbrec_address_set *allowed_ext_ips =
nat->allowed_ext_ips;
struct nbrec_address_set *exempted_ext_ips =
nat->exempted_ext_ips;

if (allowed_ext_ips && exempted_ext_ips) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_WARN_RL(&rl, "NAT rule: "UUID_FMT" not applied, since"
"both allowed and exempt external ips set",
UUID_ARGS(&(nat->header_.uuid)));
continue;
}

char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
if (error || mask != OVS_BE32_MAX) {
Expand Down Expand Up @@ -9489,6 +9572,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
is_v6 ? "6" : "4",
nat->external_ip);
ds_clear(&actions);
if (allowed_ext_ips || exempted_ext_ips) {
lrouter_nat_add_ext_ip_match(od, lflows, &match, nat,
is_v6, true, mask);
}

if (dnat_force_snat_ip) {
/* Indicate to the future tables that a DNAT has taken
* place and a force SNAT needs to be done in the
Expand Down Expand Up @@ -9532,6 +9620,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
od->l3redirect_port->json_key);
}
ds_clear(&actions);
if (allowed_ext_ips || exempted_ext_ips) {
lrouter_nat_add_ext_ip_match(od, lflows, &match, nat,
is_v6, true, mask);
}

if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
ds_put_format(&actions, "ip%s.dst=%s; next;",
Expand Down Expand Up @@ -9643,6 +9735,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
nat->logical_ip);
ds_clear(&actions);

if (allowed_ext_ips || exempted_ext_ips) {
lrouter_nat_add_ext_ip_match(od, lflows, &match, nat,
is_v6, false, mask);
}

if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
ds_put_format(&actions, "ip%s.src=%s; next;",
is_v6 ? "6" : "4", nat->external_ip);
Expand Down Expand Up @@ -9682,6 +9779,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
od->l3redirect_port->json_key);
}
ds_clear(&actions);

if (allowed_ext_ips || exempted_ext_ips) {
lrouter_nat_add_ext_ip_match(od, lflows, &match, nat,
is_v6, false, mask);
}

if (distributed) {
ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
ETH_ADDR_ARGS(mac));
Expand Down

0 comments on commit fc79d69

Please sign in to comment.