Skip to content

Commit

Permalink
northd: centralized reply lb traffic even if FIP is defined
Browse files Browse the repository at this point in the history
In the current codebase for distributed gw router port use-case,
it is not possible to add a load balancer that redirects the traffic
to a back-end if it is used as internal IP of a FIP NAT rule since
the reply traffic is never centralized. Fix the issue centralizing the
traffic if it is the reply packet for the load balancer.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2023609
Acked-by: Mark Michelson <mmichels@redhat.com>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
  • Loading branch information
LorenzoBianconi authored and dceara committed Jun 14, 2023
1 parent 8c30ba1 commit 73016fe
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 1 deletion.
20 changes: 19 additions & 1 deletion northd/northd.c
Original file line number Diff line number Diff line change
Expand Up @@ -11242,6 +11242,7 @@ struct lrouter_nat_lb_flows_ctx {

struct ds *new_match;
struct ds *undnat_match;
struct ds *gw_redir_action;

struct ovn_lb_vip *lb_vip;
struct ovn_northd_lb *lb;
Expand Down Expand Up @@ -11309,6 +11310,20 @@ build_distr_lrouter_nat_flows_for_lb(struct lrouter_nat_lb_flows_ctx *ctx,
return;
}

/* We need to centralize the LB traffic to properly perform
* the undnat stage.
*/
ds_put_format(ctx->undnat_match, ") && outport == %s", dgp->json_key);
ds_clear(ctx->gw_redir_action);
ds_put_format(ctx->gw_redir_action, "outport = %s; next;",
dgp->cr_port->json_key);

ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_IN_GW_REDIRECT,
200, ds_cstr(ctx->undnat_match),
ds_cstr(ctx->gw_redir_action),
&ctx->lb->nlb->header_);
ds_truncate(ctx->undnat_match, undnat_match_len);

ds_put_format(ctx->undnat_match, ") && (inport == %s || outport == %s)"
" && is_chassis_resident(%s)", dgp->json_key, dgp->json_key,
dgp->cr_port->json_key);
Expand Down Expand Up @@ -11379,6 +11394,7 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
struct ds force_snat_act = DS_EMPTY_INITIALIZER;
struct ds undnat_match = DS_EMPTY_INITIALIZER;
struct ds unsnat_match = DS_EMPTY_INITIALIZER;
struct ds gw_redir_action = DS_EMPTY_INITIALIZER;

ds_clear(match);
ds_clear(action);
Expand Down Expand Up @@ -11437,7 +11453,8 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
.lflows = lflows,
.meter_groups = meter_groups,
.new_match = match,
.undnat_match = &undnat_match
.undnat_match = &undnat_match,
.gw_redir_action = &gw_redir_action,
};

ctx.new_action[LROUTER_NAT_LB_FLOW_NORMAL] = ds_cstr(action);
Expand Down Expand Up @@ -11531,6 +11548,7 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
ds_destroy(&undnat_match);
ds_destroy(&skip_snat_act);
ds_destroy(&force_snat_act);
ds_destroy(&gw_redir_action);

for (size_t i = 0; i < LROUTER_NAT_LB_FLOW_MAX + 2; i++) {
bitmap_free(dp_bitmap[i]);
Expand Down
16 changes: 16 additions & 0 deletions northd/ovn-northd.8.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4588,6 +4588,22 @@ icmp6 {
</p>

<ul>
<li>
For all the configured load balancing rules that include an IPv4
address <var>VIP</var>, and a list of IPv4 backend addresses
<var>B0</var>, <var>B1</var> .. <var>Bn</var> defined for the
<var>VIP</var> a priority-200 flow is added that matches <code>
ip4 &amp;&amp; (ip4.src == <var>B0</var> || ip4.src == <var>B1</var>
|| ... || ip4.src == <var>Bn</var>)</code> with an action <code>
outport = <var>CR</var>; next;</code> where <var>CR</var> is the
<code>chassisredirect</code> port representing the instance of the
logical router distributed gateway port on the gateway chassis.
If the backend IPv4 address <var>Bx</var> is also configured with
L4 port <var>PORT</var> of protocol <var>P</var>, then the match
also includes <code>P.src</code> == <var>PORT</var>.
Similar flows are added for IPv6.
</li>

<li>
For each NAT rule in the OVN Northbound database that can
be handled in a distributed manner, a priority-100 logical
Expand Down
50 changes: 50 additions & 0 deletions tests/ovn-northd.at
Original file line number Diff line number Diff line change
Expand Up @@ -9558,3 +9558,53 @@ AT_CHECK([test $fail_count -le 3])
OVN_CLEANUP([hv1])
AT_CLEANUP
])

OVN_FOR_EACH_NORTHD_NO_HV([
AT_SETUP([check fip and lb flows])
AT_KEYWORDS([fip-lb-flows])
ovn_start

check ovn-nbctl lr-add R1
check ovn-nbctl lrp-add R1 R1-S0 02:ac:10:01:00:01 10.0.0.1/24 1000::a/64
check ovn-nbctl lrp-add R1 R1-PUB 02:ac:20:01:01:01 172.16.0.1/24 3000::a/64
check ovn-nbctl lrp-set-gateway-chassis R1-PUB hv1 20

check ovn-nbctl ls-add S0
check ovn-nbctl lsp-add S0 S0-R1
check ovn-nbctl lsp-set-type S0-R1 router
check ovn-nbctl lsp-set-addresses S0-R1 02:ac:10:01:00:01
check ovn-nbctl lsp-set-options S0-R1 router-port=R1-S0
check ovn-nbctl lsp-add S0 S0-P0
check ovn-nbctl lsp-set-addresses S0-P0 "50:54:00:00:00:03 10.0.0.3 1000::3"

check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.0.110 10.0.0.3 S0-P0 30:54:00:00:00:03
check ovn-nbctl lr-nat-add R1 dnat_and_snat 3000::c 1000::3 S0-P0 40:54:00:00:00:03

ovn-sbctl dump-flows R1 > R1flows
AT_CAPTURE_FILE([R1flows])

AT_CHECK([grep "lr_in_gw_redirect" R1flows | sed s'/table=../table=??/' |sort], [0], [dnl
table=??(lr_in_gw_redirect ), priority=0 , match=(1), action=(next;)
table=??(lr_in_gw_redirect ), priority=100 , match=(ip4.src == 10.0.0.3 && outport == "R1-PUB" && is_chassis_resident("S0-P0")), action=(eth.src = 30:54:00:00:00:03; reg1 = 172.16.0.110; next;)
table=??(lr_in_gw_redirect ), priority=100 , match=(ip6.src == 1000::3 && outport == "R1-PUB" && is_chassis_resident("S0-P0")), action=(eth.src = 40:54:00:00:00:03; xxreg1 = 3000::c; next;)
table=??(lr_in_gw_redirect ), priority=50 , match=(outport == "R1-PUB"), action=(outport = "cr-R1-PUB"; next;)
])

check ovn-nbctl lb-add lb_tcp4 172.16.0.100:50001 10.0.0.2:50001,10.0.0.3:50001,10.0.0.4:50001 tcp
check ovn-nbctl lb-add lb_tcp6 [[1000::1]]:50001 [[1000::3]]:8080
check ovn-nbctl --wait=sb lr-lb-add R1 lb_tcp4
check ovn-nbctl --wait=sb lr-lb-add R1 lb_tcp6

ovn-sbctl dump-flows R1 > R1flows
AT_CAPTURE_FILE([R1flows])
AT_CHECK([grep "lr_in_gw_redirect" R1flows |sed s'/table=../table=??/' |sort], [0], [dnl
table=??(lr_in_gw_redirect ), priority=0 , match=(1), action=(next;)
table=??(lr_in_gw_redirect ), priority=100 , match=(ip4.src == 10.0.0.3 && outport == "R1-PUB" && is_chassis_resident("S0-P0")), action=(eth.src = 30:54:00:00:00:03; reg1 = 172.16.0.110; next;)
table=??(lr_in_gw_redirect ), priority=100 , match=(ip6.src == 1000::3 && outport == "R1-PUB" && is_chassis_resident("S0-P0")), action=(eth.src = 40:54:00:00:00:03; xxreg1 = 3000::c; next;)
table=??(lr_in_gw_redirect ), priority=200 , match=(ip4 && ((ip4.src == 10.0.0.2 && tcp.src == 50001) || (ip4.src == 10.0.0.3 && tcp.src == 50001) || (ip4.src == 10.0.0.4 && tcp.src == 50001)) && outport == "R1-PUB"), action=(outport = "cr-R1-PUB"; next;)
table=??(lr_in_gw_redirect ), priority=200 , match=(ip6 && ((ip6.src == 1000::3 && tcp.src == 8080)) && outport == "R1-PUB"), action=(outport = "cr-R1-PUB"; next;)
table=??(lr_in_gw_redirect ), priority=50 , match=(outport == "R1-PUB"), action=(outport = "cr-R1-PUB"; next;)
])

AT_CLEANUP
])
118 changes: 118 additions & 0 deletions tests/ovn.at
Original file line number Diff line number Diff line change
Expand Up @@ -36340,3 +36340,121 @@ wait_row_count fdb 0 mac='"00:00:00:00:10:20"'
OVN_CLEANUP([hv1])
AT_CLEANUP
])

OVN_FOR_EACH_NORTHD([
AT_SETUP([DNAT_SNAT and LB traffic])
AT_KEYWORDS([dnat-snat-lb])
AT_SKIP_IF([test $HAVE_SCAPY = no])
ovn_start

test_ip_req_packet() {
local src_mac="50:54:00:00:00:88"
local dst_mac="$1"
local src_ip="172.168.0.200"
local dst_ip="$2"

local packet=$(fmt_pkt "Ether(dst='${dst_mac}', src='${src_mac}')/
IP(dst='${dst_ip}', src='${src_ip}')/ \
UDP(sport=53, dport=4369)")

as hv1 reset_pcap_file hv1-vif1 hv1/vif1
as hv2 reset_pcap_file hv2-vif1 hv2/vif1
check as hv2 ovs-appctl netdev-dummy/receive hv2-vif1 $packet
}

test_ip_rep_packet() {
local src_mac="50:54:00:00:00:01"
local dst_mac="00:00:00:00:ff:01"
local src_ip="10.0.0.2"
local dst_ip="172.168.0.200"

local packet=$(fmt_pkt "Ether(dst='${dst_mac}', src='${src_mac}')/
IP(dst='${dst_ip}', src='${src_ip}')/ \
UDP(sport=4369, dport=53)")

check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
}

net_add n

sim_add hv1
as hv1
check ovs-vsctl add-br br-phys
ovn_attach n br-phys 192.168.0.1
check ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
check ovs-vsctl -- add-port br-int hv1-vif1 -- \
set interface hv1-vif1 external-ids:iface-id=sw0-port1 \
options:tx_pcap=hv1/vif1-tx.pcap \
options:rxq_pcap=hv1/vif1-rx.pcap

sim_add hv2
as hv2
check ovs-vsctl add-br br-phys
ovn_attach n br-phys 192.168.0.2
check ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
check ovs-vsctl -- add-port br-int hv2-vif1 -- \
set interface hv2-vif1 external-ids:iface-id=public-port1 \
options:tx_pcap=hv2/vif1-tx.pcap \
options:rxq_pcap=hv2/vif1-rx.pcap

check ovn-nbctl ls-add sw0
check ovn-nbctl lsp-add sw0 sw0-port1
check ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.2"

check ovn-nbctl ls-add public
check ovn-nbctl lsp-add public ln-public
check ovn-nbctl lsp-set-type ln-public localnet
check ovn-nbctl lsp-set-addresses ln-public unknown
check ovn-nbctl lsp-set-options ln-public network_name=phys
check ovn-nbctl lsp-add public public-port1
check ovn-nbctl lsp-set-addresses public-port1 "50:54:00:00:00:88 172.168.0.200"

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 router
check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
check ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.1/24
check ovn-nbctl lsp-add public public-lr0
check ovn-nbctl lsp-set-type public-lr0 router
check ovn-nbctl lsp-set-addresses public-lr0 router
check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public

check ovn-nbctl lrp-set-gateway-chassis lr0-public hv2 20

check ovn-nbctl lr-nat-add lr0 snat 172.168.0.1 10.0.0.0/24
check ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.168.0.110 10.0.0.2 sw0-port1 f0:00:00:01:02:03

wait_for_ports_up
OVN_POPULATE_ARP

# send UDP request to the FIP associated to sw0-port1
test_ip_req_packet "f0:00:00:01:02:03" "172.168.0.110"
OVS_WAIT_UNTIL([test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | wc -l) -ge 1])
# send UDP reply from sw0-port1
test_ip_rep_packet
# packet sent by the FIP
fip_packet=$(fmt_pkt "Ether(dst='50:54:00:00:00:88', src='f0:00:00:01:02:03')/
IP(dst='172.168.0.200', src='172.168.0.110', ttl=63)/ \
UDP(sport=4369, dport=53)")
echo $fip_packet > expected
OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv2/vif1-tx.pcap], [expected])

check ovn-nbctl lb-add lb0 172.168.0.1:4369 10.0.0.2:4369 udp
check ovn-nbctl lr-lb-add lr0 lb0

# send UDP request to the load-balancer VIP
test_ip_req_packet "00:00:20:20:12:13" "172.168.0.1"
OVS_WAIT_UNTIL([test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | wc -l) -ge 1])
# send UDP reply from sw0-port1
test_ip_rep_packet
# packet sent by the load balancer VIP
packet=$(fmt_pkt "Ether(dst='50:54:00:00:00:88', src='00:00:20:20:12:13')/
IP(dst='172.168.0.200', src='172.168.0.1', ttl=63)/ \
UDP(sport=4369, dport=53)")
echo $packet > expected
OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv2/vif1-tx.pcap], [expected])

AT_CLEANUP
])

0 comments on commit 73016fe

Please sign in to comment.