From afab54226927711c719e8012c43333273f581ac7 Mon Sep 17 00:00:00 2001
From: Numan Siddique
+ For each NAT entry of a distributed logical router (with
+ distributed gateway router port) of type snat
,
+ a priorirty-120 flow with the match inport == P
+ && ip4.src == A
advances the packet to
+ the next pipeline, where P is the distributed logical
+ router port and A is the external_ip
set
+ in the NAT entry. If A is an IPv6 address, then
+ ip6.src
is used for the match.
+
+ The above flow is required to handle the routing of the East/west NAT + traffic. +
+
L3 admission control: A priority-100 flow drops packets that match
@@ -1977,21 +1995,6 @@ icmp6 {
redirect-chassis
.
- For each configuration in the OVN Northbound database, that asks
- to change the source IP address of a packet from A to
- B, a priority-50 flow matches
- ip && ip4.dst == B
or
- ip && ip6.dst == B
- with an action
- REGBIT_NAT_REDIRECT = 1; next;
. This flow is for
- east/west traffic to a NAT destination IPv4/IPv6 address. By
- setting the REGBIT_NAT_REDIRECT
flag, in the
- ingress table Gateway Redirect
this will trigger a
- redirect to the instance of the gateway port on the
- redirect-chassis
.
-
A priority-0 logical flow with match 1
has actions
next;
.
@@ -2147,20 +2150,6 @@ icmp6 {
redirect-chassis
.
- For each configuration in the OVN Northbound database, that asks
- to change the destination IP address of a packet from A to
- B, a priority-50 flow matches ip &&
- ip4.dst == B
or ip &&
- ip6.dst == B
with an action
- REGBIT_NAT_REDIRECT = 1; next;
. This flow is for
- east/west traffic to a NAT destination IPv4/IPv6 address. By
- setting the REGBIT_NAT_REDIRECT
flag, in the
- ingress table Gateway Redirect
this will trigger a
- redirect to the instance of the gateway port on the
- redirect-chassis
.
-
A priority-0 logical flow with match 1
has actions
next;
.
@@ -2285,54 +2274,6 @@ output;
- For distributed logical routers where one of the logical router
- ports specifies a redirect-chassis
, a priority-400
- logical flow for each ip source/destination couple that matches the
- dnat_and_snat
NAT rules configured. These flows will
- allow to properly forward traffic to the external connections if
- available and avoid sending it through the tunnel.
- Assuming the two following NAT rules have been configured:
-
-external_ip{0,1} = EIP{0,1}; -external_mac{0,1} = MAC{0,1}; -logical_ip{0,1} = LIP{0,1}; -- -
- the following action will be applied: -
- --eth.dst = MAC0; -eth.src = MAC1; -reg0 = ip4.dst; /* xxreg0 = ip6.dst; in the IPv6 case */ -reg1 = EIP1; /* xxreg1 in the IPv6 case */ -outport =- -redirect-chassis-port
; -REGBIT_DISTRIBUTED_NAT = 1; next;
. -
- Morover a priority-400 logical flow is configured for each
- dnat_and_snat
NAT rule configured in order to
- not send traffic for local FIP through the overlay tunnels
- but manage it in the local hypervisor
-
- For distributed logical routers where one of the logical router
- ports specifies a redirect-chassis
, a priority-300
- logical flow with match REGBIT_NAT_REDIRECT == 1
has
- actions ip.ttl--; next;
. The outport
- will be set later in the Gateway Redirect table.
-
IPv4 routing table. For each route to IPv4 network N with @@ -2427,23 +2368,6 @@ next;
- For distributed logical routers where one of the logical router
- ports specifies a redirect-chassis
, a priority-400
- logical flow with match REGBIT_DISTRIBUTED_NAT == 1
- has action next;
-
- For distributed logical routers where one of the logical router
- ports specifies a redirect-chassis
, a priority-200
- logical flow with match REGBIT_NAT_REDIRECT == 1
has
- actions eth.dst = E; next;
, where
- E is the ethernet address of the router's distributed
- gateway port.
-
Static MAC bindings. MAC bindings can be known statically based on @@ -2518,6 +2442,35 @@ next;
+ Static MAC bindings from NAT entries. MAC bindings can also be known
+ for the entries in the NAT
table. Below flows are
+ programmed for distributed logical routers i.e with a distributed
+ router port.
+
+ For each row in the NAT
table with IPv4 address
+ A in the column of
+ table, a priority-100
+ flow with the match outport === P &&
+ reg0 == A
has actions eth.dst = E;
+ next;
, where P
is the distributed logical router
+ port, E is the Ethernet address if set in the
+ column
+ of table for of type
+ dnat_and_snat
, otherwise the Ethernet address of the
+ distributed logical router port.
+
+ For IPv6 NAT entries, same flows are added, but using the register
+ xxreg0
for the match.
+
Dynamic MAC bindings. These flows resolve MAC-to-IP bindings @@ -2640,20 +2593,6 @@ icmp4 {
REGBIT_DISTRIBUTED_NAT == 1
has action
- next;
- REGBIT_NAT_REDIRECT == 1
has actions
- outport = CR; next;
, where CR
- is the chassisredirect
port representing the instance
- of the logical router distributed gateway port on the
- redirect-chassis
.
- outport == GW &&
@@ -2945,19 +2884,6 @@ nd_ns {
ports specifies a redirect-chassis
.
-
- Earlier in the ingress pipeline, some east-west traffic was
- redirected to the chassisredirect
port, based on
- flows in the UNSNAT
and DNAT
ingress
- tables setting the REGBIT_NAT_REDIRECT
flag, which
- then triggered a match to a flow in the
- Gateway Redirect
ingress table. The intention was
- not to actually send traffic out the distributed gateway port
- instance on the redirect-chassis
. This traffic was
- sent to the distributed gateway port instance in order for DNAT
- and/or SNAT processing to be applied.
-
-
While UNDNAT and SNAT processing have already occurred by this
point, this traffic needs to be forced through egress loopback on
@@ -2973,23 +2899,20 @@ nd_ns {
-
-
- For each dnat_and_snat
NAT rule couple in the
- OVN Northbound database on a distributed router,
- a priority-200 logical with match
- ip4.dst == external_ip0 &&
- ip4.src == external_ip1
, has action
- next;
-
-
For each NAT rule in the OVN Northbound database on a
distributed router, a priority-100 logical flow with match
ip4.dst == E &&
- outport == GW
, where E is the
- external IP address specified in the NAT rule, and GW
- is the logical router distributed gateway port, with the
- following actions:
+ outport == GW &&
+ is_chassis_resident(P)
, where E is the
+ external IP address specified in the NAT rule, GW
+ is the logical router distributed gateway port. For dnat_and_snat
+ NAT rule, P is the logical port specified in the NAT rule.
+ If column of
+ table is NOT set, then
+ P is the chassisredirect port
of
+ GW with the following actions:
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 58213742d..e2df94f4b 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -205,17 +205,16 @@ enum ovn_stage { #define REGBIT_ND_RA_OPTS_RESULT "reg0[5]" /* Register definitions for switches and routers. */ -#define REGBIT_NAT_REDIRECT "reg9[0]" + /* Indicate that this packet has been recirculated using egress * loopback. This allows certain checks to be bypassed, such as a * logical router dropping packets with source IP address equals * one of the logical router's own IP addresses. */ -#define REGBIT_EGRESS_LOOPBACK "reg9[1]" -#define REGBIT_DISTRIBUTED_NAT "reg9[2]" +#define REGBIT_EGRESS_LOOPBACK "reg9[0]" /* Register to store the result of check_pkt_larger action. */ -#define REGBIT_PKT_LARGER "reg9[3]" -#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]" -#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]" +#define REGBIT_PKT_LARGER "reg9[1]" +#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[2]" +#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[3]" #define FLAGBIT_NOT_VXLAN "flags[1] == 0" @@ -6599,128 +6598,6 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od, ds_destroy(&actions); } -static void -add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op) -{ - struct ds actions = DS_EMPTY_INITIALIZER; - struct ds match = DS_EMPTY_INITIALIZER; - - if (!op->od->l3dgw_port) { - return; - } - - if (!op->peer || !op->peer->od->nbs) { - return; - } - - for (size_t i = 0; i < op->od->nbr->n_nat; i++) { - const struct nbrec_nat *nat = op->od->nbr->nat[i]; - bool found = false; - struct eth_addr mac; - - if (strcmp(nat->type, "dnat_and_snat") || - !nat->external_mac || - !eth_addr_from_string(nat->external_mac, &mac) || - !nat->external_ip || !nat->logical_port) { - continue; - } - - const struct ovn_datapath *peer_dp = op->peer->od; - for (size_t j = 0; j < peer_dp->nbs->n_ports; j++) { - if (!strcmp(peer_dp->nbs->ports[j]->name, nat->logical_port)) { - found = true; - break; - } - } - if (!found) { - continue; - } - - /* Determine if we need to create IPv4 or IPv6 flows */ - ovs_be32 ip; - struct in6_addr ipv6; - int family = AF_INET; - if (!ip_parse(nat->external_ip, &ip) || !ip) { - family = AF_INET6; - if (!ipv6_parse(nat->external_ip, &ipv6)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration " - "for router %s", nat->external_ip, op->key); - /* We'll create IPv6 flows anyway, but the address - * is probably bogus ... */ - } - } - - ds_put_format(&match, "inport == %s && " - "ip%s.src == %s && ip%s.dst == %s", - op->json_key, - family == AF_INET ? "4" : "6", - nat->logical_ip, - family == AF_INET ? "4" : "6", - nat->external_ip); - ds_put_format(&actions, "outport = %s; eth.dst = %s; " - REGBIT_DISTRIBUTED_NAT" = 1; " - REGBIT_NAT_REDIRECT" = 0; next;", - op->od->l3dgw_port->json_key, - nat->external_mac); - ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400, - ds_cstr(&match), ds_cstr(&actions)); - ds_clear(&match); - ds_clear(&actions); - - for (size_t j = 0; j < op->od->nbr->n_nat; j++) { - const struct nbrec_nat *nat2 = op->od->nbr->nat[j]; - struct eth_addr mac2; - - if (nat == nat2 || strcmp(nat2->type, "dnat_and_snat") || - !nat2->external_mac || - !eth_addr_from_string(nat2->external_mac, &mac2) || - !nat2->external_ip) { - continue; - } - - family = AF_INET; - if (!ip_parse(nat2->external_ip, &ip) || !ip) { - family = AF_INET6; - if (!ipv6_parse(nat2->external_ip, &ipv6)) { - static struct vlog_rate_limit rl = - VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration " - "for router %s", nat2->external_ip, op->key); - /* We'll create IPv6 flows anyway, but the address - * is probably bogus ... */ - } - } - - ds_put_format(&match, "inport == %s && " - "ip%s.src == %s && ip%s.dst == %s", - op->json_key, - family == AF_INET ? "4" : "6", - nat->logical_ip, - family == AF_INET ? "4" : "6", - nat2->external_ip); - ds_put_format(&actions, "outport = %s; " - "eth.src = %s; eth.dst = %s; " - "%sreg0 = ip%s.dst; %sreg1 = %s; " - REGBIT_DISTRIBUTED_NAT" = 1; " - REGBIT_NAT_REDIRECT" = 0; next;", - op->od->l3dgw_port->json_key, - op->od->l3dgw_port->lrp_networks.ea_s, - nat2->external_mac, - family == AF_INET ? "" : "xx", - family == AF_INET ? "4" : "6", - family == AF_INET ? "" : "xx", - nat->external_ip); - ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400, - ds_cstr(&match), ds_cstr(&actions)); - ds_clear(&match); - ds_clear(&actions); - } - } - ds_destroy(&match); - ds_destroy(&actions); -} - static void add_route(struct hmap *lflows, const struct ovn_port *op, const char *lrp_addr_s, const char *network_s, int plen, @@ -8144,17 +8021,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100, ds_cstr(&match), ds_cstr(&actions)); - - /* Traffic received on other router ports must be - * redirected to the central instance of the l3dgw_port - * for NAT processing. */ - ds_clear(&match); - ds_put_format(&match, "ip && ip%s.dst == %s", - is_v6 ? "6" : "4", - nat->external_ip); - ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50, - ds_cstr(&match), - REGBIT_NAT_REDIRECT" = 1; next;"); } } @@ -8220,18 +8086,33 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100, ds_cstr(&match), ds_cstr(&actions)); + } + } - /* Traffic received on other router ports must be - * redirected to the central instance of the l3dgw_port - * for NAT processing. */ + /* ARP resolve for NAT IPs. */ + if (od->l3dgw_port) { + if (!strcmp(nat->type, "snat")) { ds_clear(&match); - ds_put_format(&match, "ip && ip%s.dst == %s", - is_v6 ? "6" : "4", - nat->external_ip); - ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50, - ds_cstr(&match), - REGBIT_NAT_REDIRECT" = 1; next;"); + ds_put_format( + &match, "inport == %s && %s == %s", + od->l3dgw_port->json_key, + is_v6 ? "ip6.src" : "ip4.src", nat->external_ip); + ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 120, + ds_cstr(&match), "next;"); } + + ds_clear(&match); + ds_put_format( + &match, "outport == %s && %s == %s", + od->l3dgw_port->json_key, + is_v6 ? "xxreg0" : "reg0", nat->external_ip); + ds_clear(&actions); + ds_put_format( + &actions, "eth.dst = %s; next;", + distributed ? nat->external_mac : + od->l3dgw_port->lrp_networks.ea_s); + ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 100, + ds_cstr(&match), ds_cstr(&actions)); } /* Egress UNDNAT table: It is for already established connections' @@ -8378,49 +8259,19 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * ingress pipeline with inport = outport. */ if (od->l3dgw_port) { /* Distributed router. */ - if (!strcmp(nat->type, "dnat_and_snat") && - nat->external_mac && nat->external_ip && - eth_addr_from_string(nat->external_mac, &mac)) { - for (int j = 0; j < od->nbr->n_nat; j++) { - const struct nbrec_nat *nat2 = od->nbr->nat[j]; - - if (nat2 == nat || - strcmp(nat2->type, "dnat_and_snat") || - !nat2->external_mac || !nat2->external_ip) { - continue; - } - - ds_clear(&match); - ds_put_format(&match, "is_chassis_resident(\"%s\") && " - "ip%s.src == %s && ip%s.dst == %s", - nat->logical_port, - is_v6 ? "6" : "4", nat2->external_ip, - is_v6 ? "6" : "4", nat->external_ip); - ds_clear(&actions); - ds_put_format(&actions, - "inport = outport; outport = \"\"; " - "flags = 0; flags.loopback = 1; " - REGBIT_EGRESS_LOOPBACK" = 1; " - "next(pipeline=ingress, table=0); "); - ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 300, - ds_cstr(&match), ds_cstr(&actions)); - - ds_clear(&match); - ds_put_format(&match, - "ip%s.src == %s && ip%s.dst == %s", - is_v6 ? "6" : "4", nat2->external_ip, - is_v6 ? "6" : "4", nat->external_ip); - ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200, - ds_cstr(&match), "next;"); - ds_clear(&match); - } - } - ds_clear(&match); ds_put_format(&match, "ip%s.dst == %s && outport == %s", is_v6 ? "6" : "4", nat->external_ip, od->l3dgw_port->json_key); + if (!distributed) { + ds_put_format(&match, " && is_chassis_resident(%s)", + od->l3redirect_port->json_key); + } else { + ds_put_format(&match, " && is_chassis_resident(\"%s\")", + nat->logical_port); + } + ds_clear(&actions); ds_put_format(&actions, "clone { ct_clear; " @@ -8491,40 +8342,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * we can do it here, saving a future re-circulation. */ ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50, "ip", "flags.loopback = 1; ct_dnat;"); - } else { - ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 400, - REGBIT_DISTRIBUTED_NAT" == 1", "next;"); - - /* For NAT on a distributed router, add flows to Ingress - * IP Routing table, Ingress ARP Resolution table, and - * Ingress Gateway Redirect Table that are not specific to a - * NAT rule. */ - - /* The highest priority IN_IP_ROUTING rule matches packets - * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages), - * with action "ip.ttl--; next;". The IN_GW_REDIRECT table - * will take care of setting the outport. */ - ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 300, - REGBIT_NAT_REDIRECT" == 1", "ip.ttl--; next;"); - - /* The highest priority IN_ARP_RESOLVE rule matches packets - * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages), - * then sets eth.dst to the distributed gateway port's - * ethernet address. */ - ds_clear(&actions); - ds_put_format(&actions, "eth.dst = %s; next;", - od->l3dgw_port->lrp_networks.ea_s); - ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 200, - REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions)); - - /* The highest priority IN_GW_REDIRECT rule redirects packets - * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages) to - * the central instance of the l3dgw_port for NAT processing. */ - ds_clear(&actions); - ds_put_format(&actions, "outport = %s; next;", - od->l3redirect_port->json_key); - ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200, - REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions)); } /* Load balancing and packet defrag are only valid on @@ -8720,9 +8537,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } - /* create logical flows for DVR floating IPs */ - add_distributed_nat_routes(lflows, op); - for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s, op->lrp_networks.ipv4_addrs[i].network_s, @@ -9252,9 +9066,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } if (od->l3dgw_port && od->l3redirect_port) { - ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300, - REGBIT_DISTRIBUTED_NAT" == 1", "next;"); - /* For traffic with outport == l3dgw_port, if the * packet did not match any higher priority redirect * rule, then the traffic is redirected to the central diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index da566f900..3e4120ac5 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -990,7 +990,7 @@ echo "CR-LRP UUID is: " $uuid # IPV4 ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11 -OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ wc -l`]) AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2 @@ -1008,7 +1008,7 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [0 ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1 ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11 -OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ wc -l`]) AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0 @@ -1027,7 +1027,7 @@ ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1 # IPV6 ovn-nbctl lr-nat-add R1 dnat_and_snat fd01::1 fd11::2 -OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ wc -l`]) AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2 @@ -1045,7 +1045,7 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [0 ovn-nbctl lr-nat-del R1 dnat_and_snat fd01::1 ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2 -OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ wc -l`]) AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0 -- 2.24.1