|
|
bbaaef |
From 0ed8c91b86b0d4bc01b4795155b25e85df1c6a9d Mon Sep 17 00:00:00 2001
|
|
|
bbaaef |
From: Russell Bryant <russell@ovn.org>
|
|
|
bbaaef |
Date: Fri, 25 Oct 2019 16:47:21 -0400
|
|
|
bbaaef |
Subject: [PATCH 4/5] northd: Add lflows for IPv6 NAT.
|
|
|
bbaaef |
|
|
|
bbaaef |
Signed-off-by: Russell Bryant <russell@ovn.org>
|
|
|
bbaaef |
Acked-by: Numan Siddique <numans@ovn.org>
|
|
|
bbaaef |
---
|
|
|
bbaaef |
ovn/northd/ovn-northd.8.xml | 233 ++++++++++++----------
|
|
|
bbaaef |
ovn/northd/ovn-northd.c | 384 +++++++++++++++++++++++++++---------
|
|
|
bbaaef |
2 files changed, 418 insertions(+), 199 deletions(-)
|
|
|
bbaaef |
|
|
|
bbaaef |
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
|
|
|
bbaaef |
index b9ae28e14..38e6ecec5 100644
|
|
|
bbaaef |
--- a/ovn/northd/ovn-northd.8.xml
|
|
|
bbaaef |
+++ b/ovn/northd/ovn-northd.8.xml
|
|
|
bbaaef |
@@ -1498,11 +1498,55 @@ output;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
- These flows reply to ARP requests for the virtual IP addresses
|
|
|
bbaaef |
- configured in the router for DNAT or load balancing. For a
|
|
|
bbaaef |
- configured DNAT IP address or a load balancer IPv4 VIP A,
|
|
|
bbaaef |
- for each router port P with Ethernet
|
|
|
bbaaef |
- address E, a priority-90 flow matches
|
|
|
bbaaef |
+ Reply to IPv6 Neighbor Solicitations. These flows reply to
|
|
|
bbaaef |
+ Neighbor Solicitation requests for the router's own IPv6
|
|
|
bbaaef |
+ address and populate the logical router's mac binding table.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ For each router port P that
|
|
|
bbaaef |
+ owns IPv6 address A, solicited node address S,
|
|
|
bbaaef |
+ and Ethernet address E, a priority-90 flow matches
|
|
|
bbaaef |
+ inport == P &&
|
|
|
bbaaef |
+ nd_ns && ip6.dst == {A, E} &&
|
|
|
bbaaef |
+ nd.target == A with the following actions:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+nd_na_router {
|
|
|
bbaaef |
+ eth.src = E;
|
|
|
bbaaef |
+ ip6.src = A;
|
|
|
bbaaef |
+ nd.target = A;
|
|
|
bbaaef |
+ nd.tll = E;
|
|
|
bbaaef |
+ outport = inport;
|
|
|
bbaaef |
+ flags.loopback = 1;
|
|
|
bbaaef |
+ output;
|
|
|
bbaaef |
+};
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ For the gateway port on a distributed logical router (where
|
|
|
bbaaef |
+ one of the logical router ports specifies a
|
|
|
bbaaef |
+ redirect-chassis ), the above flows replying to
|
|
|
bbaaef |
+ IPv6 Neighbor Solicitations are only programmed on the
|
|
|
bbaaef |
+ gateway port instance on the redirect-chassis .
|
|
|
bbaaef |
+ This behavior avoids generation of multiple replies from
|
|
|
bbaaef |
+ different chassis, and allows upstream MAC learning to point
|
|
|
bbaaef |
+ to the redirect-chassis .
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ These flows reply to ARP requests or IPv6 neighbor solicitation
|
|
|
bbaaef |
+ for the virtual IP addresses configured in the router for DNAT
|
|
|
bbaaef |
+ or load balancing.
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ IPv4: For a configured DNAT IP address or a load balancer
|
|
|
bbaaef |
+ IPv4 VIP A, for each router port P with
|
|
|
bbaaef |
+ Ethernet address E, a priority-90 flow matches
|
|
|
bbaaef |
inport == P && arp.op == 1 &&
|
|
|
bbaaef |
arp.tpa == A (ARP request)
|
|
|
bbaaef |
with the following actions:
|
|
|
bbaaef |
@@ -1521,6 +1565,30 @@ flags.loopback = 1;
|
|
|
bbaaef |
output;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ IPv6: For a configured DNAT IP address or a load balancer
|
|
|
bbaaef |
+ IPv6 VIP A, solicited node address S,
|
|
|
bbaaef |
+ for each router port P with
|
|
|
bbaaef |
+ Ethernet address E, a priority-90 flow matches
|
|
|
bbaaef |
+ inport == P && nd_ns &&
|
|
|
bbaaef |
+ ip6.dst == {A, S} &&
|
|
|
bbaaef |
+ nd.target == A
|
|
|
bbaaef |
+ with the following actions:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+eth.dst = eth.src;
|
|
|
bbaaef |
+nd_na {
|
|
|
bbaaef |
+ eth.src = E;
|
|
|
bbaaef |
+ nd.tll = E;
|
|
|
bbaaef |
+ ip6.src = A;
|
|
|
bbaaef |
+ nd.target = A;
|
|
|
bbaaef |
+ outport = P;
|
|
|
bbaaef |
+ flags.loopback = 1;
|
|
|
bbaaef |
+ output;
|
|
|
bbaaef |
+}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
|
|
|
bbaaef |
For the gateway port on a distributed logical router with NAT
|
|
|
bbaaef |
(where one of the logical router ports specifies a
|
|
|
bbaaef |
@@ -1557,6 +1625,15 @@ eth.src = external_mac;
|
|
|
bbaaef |
arp.sha = external_mac;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ or in the case of IPv6 neighbor solicition:
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+eth.src = external_mac;
|
|
|
bbaaef |
+nd.tll = external_mac;
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+
|
|
|
bbaaef |
|
|
|
bbaaef |
This behavior avoids generation of multiple ARP responses
|
|
|
bbaaef |
from different chassis, and allows upstream MAC learning to
|
|
|
bbaaef |
@@ -1566,68 +1643,6 @@ arp.sha = external_mac;
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- Reply to IPv6 Neighbor Solicitations. These flows reply to
|
|
|
bbaaef |
- Neighbor Solicitation requests for the router's own IPv6
|
|
|
bbaaef |
- address and load balancing IPv6 VIPs and populate the logical
|
|
|
bbaaef |
- router's mac binding table.
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- For each router port P that
|
|
|
bbaaef |
- owns IPv6 address A, solicited node address S,
|
|
|
bbaaef |
- and Ethernet address E, a priority-90 flow matches
|
|
|
bbaaef |
- inport == P &&
|
|
|
bbaaef |
- nd_ns && ip6.dst == {A, E} &&
|
|
|
bbaaef |
- nd.target == A with the following actions:
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-nd_na_router {
|
|
|
bbaaef |
- eth.src = E;
|
|
|
bbaaef |
- ip6.src = A;
|
|
|
bbaaef |
- nd.target = A;
|
|
|
bbaaef |
- nd.tll = E;
|
|
|
bbaaef |
- outport = inport;
|
|
|
bbaaef |
- flags.loopback = 1;
|
|
|
bbaaef |
- output;
|
|
|
bbaaef |
-};
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- For each router port P that has load balancing VIP
|
|
|
bbaaef |
- A, solicited node address S, and Ethernet
|
|
|
bbaaef |
- address E, a priority-90 flow matches
|
|
|
bbaaef |
- inport == P &&
|
|
|
bbaaef |
- nd_ns && ip6.dst == {A, E} &&
|
|
|
bbaaef |
- nd.target == A with the following actions:
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-nd_na {
|
|
|
bbaaef |
- eth.src = E;
|
|
|
bbaaef |
- ip6.src = A;
|
|
|
bbaaef |
- nd.target = A;
|
|
|
bbaaef |
- nd.tll = E;
|
|
|
bbaaef |
- outport = inport;
|
|
|
bbaaef |
- flags.loopback = 1;
|
|
|
bbaaef |
- output;
|
|
|
bbaaef |
-};
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- For the gateway port on a distributed logical router (where
|
|
|
bbaaef |
- one of the logical router ports specifies a
|
|
|
bbaaef |
- redirect-chassis ), the above flows replying to
|
|
|
bbaaef |
- IPv6 Neighbor Solicitations are only programmed on the
|
|
|
bbaaef |
- gateway port instance on the redirect-chassis .
|
|
|
bbaaef |
- This behavior avoids generation of multiple replies from
|
|
|
bbaaef |
- different chassis, and allows upstream MAC learning to point
|
|
|
bbaaef |
- to the redirect-chassis .
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
-
|
|
|
bbaaef |
|
|
|
bbaaef |
Priority-85 flows which drops the ARP and IPv6 Neighbor Discovery
|
|
|
bbaaef |
packets.
|
|
|
bbaaef |
@@ -1681,10 +1696,11 @@ nd_na {
|
|
|
bbaaef |
handled by one of the flows above, which amounts to ICMP (other than
|
|
|
bbaaef |
echo requests) and fragments with nonzero offsets. For each IP address
|
|
|
bbaaef |
A owned by the router, a priority-60 flow matches
|
|
|
bbaaef |
- ip4.dst == A and drops the traffic. An
|
|
|
bbaaef |
- exception is made and the above flow is not added if the router
|
|
|
bbaaef |
- port's own IP address is used to SNAT packets passing through that
|
|
|
bbaaef |
- router.
|
|
|
bbaaef |
+ ip4.dst == A or
|
|
|
bbaaef |
+ ip6.dst == A
|
|
|
bbaaef |
+ and drops the traffic. An exception is made and the above flow
|
|
|
bbaaef |
+ is not added if the router port's own IP address is used to SNAT
|
|
|
bbaaef |
+ packets passing through that router.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1778,23 +1794,26 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
If the Gateway router has been configured to force SNAT any
|
|
|
bbaaef |
previously DNATted packets to B, a priority-110 flow
|
|
|
bbaaef |
- matches ip && ip4.dst == B with
|
|
|
bbaaef |
- an action ct_snat; .
|
|
|
bbaaef |
+ matches ip && ip4.dst == B or
|
|
|
bbaaef |
+ ip && ip6.dst == B
|
|
|
bbaaef |
+ with an action ct_snat; .
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
If the Gateway router has been configured to force SNAT any
|
|
|
bbaaef |
previously load-balanced packets to B, a priority-100 flow
|
|
|
bbaaef |
- matches ip && ip4.dst == B with
|
|
|
bbaaef |
- an action ct_snat; .
|
|
|
bbaaef |
+ matches ip && ip4.dst == B or
|
|
|
bbaaef |
+ ip && ip6.dst == B
|
|
|
bbaaef |
+ with an action ct_snat; .
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
For each NAT configuration in the OVN Northbound database, that asks
|
|
|
bbaaef |
to change the source IP address of a packet from A to
|
|
|
bbaaef |
- B, a priority-90 flow matches ip &&
|
|
|
bbaaef |
- ip4.dst == B with an action
|
|
|
bbaaef |
- ct_snat; .
|
|
|
bbaaef |
+ B, a priority-90 flow matches
|
|
|
bbaaef |
+ ip && ip4.dst == B or
|
|
|
bbaaef |
+ ip && ip6.dst == B
|
|
|
bbaaef |
+ with an action ct_snat; .
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1812,7 +1831,9 @@ icmp6 {
|
|
|
bbaaef |
For each configuration in the OVN Northbound database, that asks
|
|
|
bbaaef |
to change the source IP address of a packet from A to
|
|
|
bbaaef |
B, a priority-100 flow matches ip &&
|
|
|
bbaaef |
- ip4.dst == B && inport == GW,
|
|
|
bbaaef |
+ ip4.dst == B && inport == GW or
|
|
|
bbaaef |
+ ip &&
|
|
|
bbaaef |
+ ip6.dst == B && inport == GW
|
|
|
bbaaef |
where GW is the logical router gateway port, with an
|
|
|
bbaaef |
action ct_snat; .
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1826,10 +1847,12 @@ icmp6 {
|
|
|
bbaaef |
|
|
|
bbaaef |
For each configuration in the OVN Northbound database, that asks
|
|
|
bbaaef |
to change the source IP address of a packet from A to
|
|
|
bbaaef |
- B, a priority-50 flow matches ip &&
|
|
|
bbaaef |
- ip4.dst == B with an action
|
|
|
bbaaef |
+ B, a priority-50 flow matches
|
|
|
bbaaef |
+ ip && ip4.dst == B or
|
|
|
bbaaef |
+ ip && ip6.dst == B
|
|
|
bbaaef |
+ with an action
|
|
|
bbaaef |
REGBIT_NAT_REDIRECT = 1; next; . This flow is for
|
|
|
bbaaef |
- east/west traffic to a NAT destination IPv4 address. By
|
|
|
bbaaef |
+ east/west traffic to a NAT destination IPv4/IPv6 address. By
|
|
|
bbaaef |
setting the REGBIT_NAT_REDIRECT flag, in the
|
|
|
bbaaef |
ingress table Gateway Redirect this will trigger a
|
|
|
bbaaef |
redirect to the instance of the gateway port on the
|
|
|
bbaaef |
@@ -1875,25 +1898,27 @@ icmp6 {
|
|
|
bbaaef |
For all the configured load balancing rules for a Gateway router or
|
|
|
bbaaef |
Router with gateway port in OVN_Northbound database that
|
|
|
bbaaef |
includes a L4 port PORT of protocol P and IPv4
|
|
|
bbaaef |
- address VIP, a priority-120 flow that matches on
|
|
|
bbaaef |
+ or IPv6 address VIP, a priority-120 flow that matches on
|
|
|
bbaaef |
ct.new && ip && ip4.dst == VIP
|
|
|
bbaaef |
&& P && P.dst == PORT
|
|
|
bbaaef |
- with an action of ct_lb(args) ,
|
|
|
bbaaef |
- where args contains comma separated IPv4 addresses (and
|
|
|
bbaaef |
- optional port numbers) to load balance to. If the router is configured
|
|
|
bbaaef |
- to force SNAT any load-balanced packets, the above action will be
|
|
|
bbaaef |
- replaced by flags.force_snat_for_lb = 1;
|
|
|
bbaaef |
+ (ip6.dst == VIP in the IPv6 case)
|
|
|
bbaaef |
+ with an action of ct_lb(args) ,
|
|
|
bbaaef |
+ where args contains comma separated IPv4 or IPv6 addresses
|
|
|
bbaaef |
+ (and optional port numbers) to load balance to. If the router is
|
|
|
bbaaef |
+ configured to force SNAT any load-balanced packets, the above action
|
|
|
bbaaef |
+ will be replaced by flags.force_snat_for_lb = 1;
|
|
|
bbaaef |
ct_lb(args);.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
For all the configured load balancing rules for a router in
|
|
|
bbaaef |
OVN_Northbound database that includes a L4 port
|
|
|
bbaaef |
- PORT of protocol P and IPv4 address
|
|
|
bbaaef |
+ PORT of protocol P and IPv4 or IPv6 address
|
|
|
bbaaef |
VIP, a priority-120 flow that matches on
|
|
|
bbaaef |
ct.est && ip && ip4.dst == VIP
|
|
|
bbaaef |
&& P && P.dst == PORT
|
|
|
bbaaef |
- with an action of ct_dnat; . If the router is
|
|
|
bbaaef |
+ (ip6.dst == VIP in the IPv6 case)
|
|
|
bbaaef |
+ with an action of ct_dnat; . If the router is
|
|
|
bbaaef |
configured to force SNAT any load-balanced packets, the above action
|
|
|
bbaaef |
will be replaced by flags.force_snat_for_lb = 1; ct_dnat; .
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1903,11 +1928,13 @@ icmp6 {
|
|
|
bbaaef |
OVN_Northbound database that includes just an IP address
|
|
|
bbaaef |
VIP to match on, a priority-110 flow that matches on
|
|
|
bbaaef |
ct.new && ip && ip4.dst ==
|
|
|
bbaaef |
- VIP with an action of
|
|
|
bbaaef |
+ VIP (ip6.dst == VIP in the
|
|
|
bbaaef |
+ IPv6 case) with an action of
|
|
|
bbaaef |
ct_lb(args) , where args contains
|
|
|
bbaaef |
- comma separated IPv4 addresses. If the router is configured to force
|
|
|
bbaaef |
- SNAT any load-balanced packets, the above action will be replaced by
|
|
|
bbaaef |
- flags.force_snat_for_lb = 1; ct_lb(args); .
|
|
|
bbaaef |
+ comma separated IPv4 or IPv6 addresses. If the router is configured
|
|
|
bbaaef |
+ to force SNAT any load-balanced packets, the above action will be
|
|
|
bbaaef |
+ replaced by flags.force_snat_for_lb = 1;
|
|
|
bbaaef |
+ ct_lb(args);.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1915,7 +1942,8 @@ icmp6 {
|
|
|
bbaaef |
OVN_Northbound database that includes just an IP address
|
|
|
bbaaef |
VIP to match on, a priority-110 flow that matches on
|
|
|
bbaaef |
ct.est && ip && ip4.dst ==
|
|
|
bbaaef |
- VIP with an action of ct_dnat; .
|
|
|
bbaaef |
+ VIP (or ip6.dst == VIP )
|
|
|
bbaaef |
+ with an action of ct_dnat; .
|
|
|
bbaaef |
If the router is configured to force SNAT any load-balanced
|
|
|
bbaaef |
packets, the above action will be replaced by
|
|
|
bbaaef |
flags.force_snat_for_lb = 1; ct_dnat; .
|
|
|
bbaaef |
@@ -1929,7 +1957,8 @@ icmp6 {
|
|
|
bbaaef |
For each configuration in the OVN Northbound database, that asks
|
|
|
bbaaef |
to change the destination IP address of a packet from A to
|
|
|
bbaaef |
B, a priority-100 flow matches ip &&
|
|
|
bbaaef |
- ip4.dst == A with an action
|
|
|
bbaaef |
+ ip4.dst == A or ip &&
|
|
|
bbaaef |
+ ip6.dst == A with an action
|
|
|
bbaaef |
flags.loopback = 1; ct_dnat(B); . If the
|
|
|
bbaaef |
Gateway router is configured to force SNAT any DNATed packet,
|
|
|
bbaaef |
the above action will be replaced by
|
|
|
bbaaef |
@@ -1966,7 +1995,8 @@ icmp6 {
|
|
|
bbaaef |
B, a priority-100 flow matches ip &&
|
|
|
bbaaef |
ip4.dst == B && inport == GW,
|
|
|
bbaaef |
where GW is the logical router gateway port, with an
|
|
|
bbaaef |
- action ct_dnat(B); .
|
|
|
bbaaef |
+ action ct_dnat(B); . The match will
|
|
|
bbaaef |
+ include ip6.dst == B in the IPv6 case.
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -1979,9 +2009,10 @@ icmp6 {
|
|
|
bbaaef |
For each configuration in the OVN Northbound database, that asks
|
|
|
bbaaef |
to change the destination IP address of a packet from A to
|
|
|
bbaaef |
B, a priority-50 flow matches ip &&
|
|
|
bbaaef |
- ip4.dst == B with an action
|
|
|
bbaaef |
+ ip4.dst == B or ip &&
|
|
|
bbaaef |
+ ip6.dst == B with an action
|
|
|
bbaaef |
REGBIT_NAT_REDIRECT = 1; next; . This flow is for
|
|
|
bbaaef |
- east/west traffic to a NAT destination IPv4 address. By
|
|
|
bbaaef |
+ east/west traffic to a NAT destination IPv4/IPv6 address. By
|
|
|
bbaaef |
setting the REGBIT_NAT_REDIRECT flag, in the
|
|
|
bbaaef |
ingress table Gateway Redirect this will trigger a
|
|
|
bbaaef |
redirect to the instance of the gateway port on the
|
|
|
bbaaef |
@@ -2136,8 +2167,8 @@ logical_ip{0,1} = LIP{0,1};
|
|
|
bbaaef |
|
|
|
bbaaef |
eth.dst = MAC0;
|
|
|
bbaaef |
eth.src = MAC1;
|
|
|
bbaaef |
-reg0 = ip4.dst;
|
|
|
bbaaef |
-reg1 = EIP1;
|
|
|
bbaaef |
+reg0 = ip4.dst; /* xxreg0 = ip6.dst; in the IPv6 case */
|
|
|
bbaaef |
+reg1 = EIP1; /* xxreg1 in the IPv6 case */
|
|
|
bbaaef |
outport = redirect-chassis-port ;
|
|
|
bbaaef |
REGBIT_DISTRIBUTED_NAT = 1; next; .
|
|
|
bbaaef |
|
|
|
bbaaef |
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
|
|
|
bbaaef |
index 7cfeb60be..84b2a9ff1 100644
|
|
|
bbaaef |
--- a/ovn/northd/ovn-northd.c
|
|
|
bbaaef |
+++ b/ovn/northd/ovn-northd.c
|
|
|
bbaaef |
@@ -66,6 +66,15 @@ struct northd_context {
|
|
|
bbaaef |
struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
|
|
|
bbaaef |
};
|
|
|
bbaaef |
|
|
|
bbaaef |
+/* An IPv4 or IPv6 address */
|
|
|
bbaaef |
+struct v46_ip {
|
|
|
bbaaef |
+ int family;
|
|
|
bbaaef |
+ union {
|
|
|
bbaaef |
+ ovs_be32 ipv4;
|
|
|
bbaaef |
+ struct in6_addr ipv6;
|
|
|
bbaaef |
+ };
|
|
|
bbaaef |
+};
|
|
|
bbaaef |
+
|
|
|
bbaaef |
static const char *ovnnb_db;
|
|
|
bbaaef |
static const char *ovnsb_db;
|
|
|
bbaaef |
static const char *unixctl_path;
|
|
|
bbaaef |
@@ -2266,6 +2275,15 @@ get_nat_addresses(const struct ovn_port *op, size_t *n)
|
|
|
bbaaef |
break;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
}
|
|
|
bbaaef |
+ if (!is_router_ip) {
|
|
|
bbaaef |
+ for (size_t j = 0; j < op->lrp_networks.n_ipv6_addrs; j++) {
|
|
|
bbaaef |
+ if (!strcmp(nat->external_ip,
|
|
|
bbaaef |
+ op->lrp_networks.ipv6_addrs[j].addr_s)) {
|
|
|
bbaaef |
+ is_router_ip = true;
|
|
|
bbaaef |
+ break;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
|
|
|
bbaaef |
if (!is_router_ip) {
|
|
|
bbaaef |
ds_put_format(&c_addresses, " %s", nat->external_ip);
|
|
|
bbaaef |
@@ -6013,9 +6031,28 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
+ /* Determine if we need to create IPv4 or IPv6 flows */
|
|
|
bbaaef |
+ ovs_be32 ip;
|
|
|
bbaaef |
+ struct in6_addr ipv6;
|
|
|
bbaaef |
+ int family = AF_INET;
|
|
|
bbaaef |
+ if (!ip_parse(nat->external_ip, &ip) || !ip) {
|
|
|
bbaaef |
+ family = AF_INET6;
|
|
|
bbaaef |
+ if (!ipv6_parse(nat->external_ip, &ipv6)) {
|
|
|
bbaaef |
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
+ VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
|
|
|
bbaaef |
+ "for router %s", nat->external_ip, op->key);
|
|
|
bbaaef |
+ /* We'll create IPv6 flows anyway, but the address
|
|
|
bbaaef |
+ * is probably bogus ... */
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
ds_put_format(&match, "inport == %s && "
|
|
|
bbaaef |
- "ip4.src == %s && ip4.dst == %s",
|
|
|
bbaaef |
- op->json_key, nat->logical_ip, nat->external_ip);
|
|
|
bbaaef |
+ "ip%s.src == %s && ip%s.dst == %s",
|
|
|
bbaaef |
+ op->json_key,
|
|
|
bbaaef |
+ family == AF_INET ? "4" : "6",
|
|
|
bbaaef |
+ nat->logical_ip,
|
|
|
bbaaef |
+ family == AF_INET ? "4" : "6",
|
|
|
bbaaef |
+ nat->external_ip);
|
|
|
bbaaef |
ds_put_format(&actions, "outport = %s; eth.dst = %s; "
|
|
|
bbaaef |
REGBIT_DISTRIBUTED_NAT" = 1; "
|
|
|
bbaaef |
REGBIT_NAT_REDIRECT" = 0; next;",
|
|
|
bbaaef |
@@ -6033,17 +6070,38 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
|
|
|
bbaaef |
!nat2->external_mac || !nat2->external_ip)
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
|
|
|
bbaaef |
+ family = AF_INET;
|
|
|
bbaaef |
+ if (!ip_parse(nat2->external_ip, &ip) || !ip) {
|
|
|
bbaaef |
+ family = AF_INET6;
|
|
|
bbaaef |
+ if (!ipv6_parse(nat2->external_ip, &ipv6)) {
|
|
|
bbaaef |
+ static struct vlog_rate_limit rl =
|
|
|
bbaaef |
+ VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
+ VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
|
|
|
bbaaef |
+ "for router %s", nat2->external_ip, op->key);
|
|
|
bbaaef |
+ /* We'll create IPv6 flows anyway, but the address
|
|
|
bbaaef |
+ * is probably bogus ... */
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
ds_put_format(&match, "inport == %s && "
|
|
|
bbaaef |
- "ip4.src == %s && ip4.dst == %s",
|
|
|
bbaaef |
- op->json_key, nat->logical_ip, nat2->external_ip);
|
|
|
bbaaef |
+ "ip%s.src == %s && ip%s.dst == %s",
|
|
|
bbaaef |
+ op->json_key,
|
|
|
bbaaef |
+ family == AF_INET ? "4" : "6",
|
|
|
bbaaef |
+ nat->logical_ip,
|
|
|
bbaaef |
+ family == AF_INET ? "4" : "6",
|
|
|
bbaaef |
+ nat2->external_ip);
|
|
|
bbaaef |
ds_put_format(&actions, "outport = %s; "
|
|
|
bbaaef |
"eth.src = %s; eth.dst = %s; "
|
|
|
bbaaef |
- "reg0 = ip4.dst; reg1 = %s; "
|
|
|
bbaaef |
+ "%sreg0 = ip%s.dst; %sreg1 = %s; "
|
|
|
bbaaef |
REGBIT_DISTRIBUTED_NAT" = 1; "
|
|
|
bbaaef |
REGBIT_NAT_REDIRECT" = 0; next;",
|
|
|
bbaaef |
op->od->l3dgw_port->json_key,
|
|
|
bbaaef |
op->od->l3dgw_port->lrp_networks.ea_s,
|
|
|
bbaaef |
- nat2->external_mac, nat->external_ip);
|
|
|
bbaaef |
+ nat2->external_mac,
|
|
|
bbaaef |
+ family == AF_INET ? "" : "xx",
|
|
|
bbaaef |
+ family == AF_INET ? "4" : "6",
|
|
|
bbaaef |
+ family == AF_INET ? "" : "xx",
|
|
|
bbaaef |
+ nat->external_ip);
|
|
|
bbaaef |
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
|
|
|
bbaaef |
ds_cstr(&match), ds_cstr(&actions));
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
@@ -6280,7 +6338,8 @@ op_put_v6_networks(struct ds *ds, const struct ovn_port *op)
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
static const char *
|
|
|
bbaaef |
-get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ovs_be32 *ip)
|
|
|
bbaaef |
+get_force_snat_ip(struct ovn_datapath *od, const char *key_type,
|
|
|
bbaaef |
+ struct v46_ip *ip)
|
|
|
bbaaef |
{
|
|
|
bbaaef |
char *key = xasprintf("%s_force_snat_ip", key_type);
|
|
|
bbaaef |
const char *ip_address = smap_get(&od->nbr->options, key);
|
|
|
bbaaef |
@@ -6288,19 +6347,27 @@ get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ovs_be32 *ip)
|
|
|
bbaaef |
|
|
|
bbaaef |
if (ip_address) {
|
|
|
bbaaef |
ovs_be32 mask;
|
|
|
bbaaef |
- char *error = ip_parse_masked(ip_address, ip, &mask);
|
|
|
bbaaef |
+ ip->family = AF_INET;
|
|
|
bbaaef |
+ char *error = ip_parse_masked(ip_address, &ip->ipv4, &mask);
|
|
|
bbaaef |
if (error || mask != OVS_BE32_MAX) {
|
|
|
bbaaef |
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
- VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
|
|
|
bbaaef |
- ip_address, UUID_ARGS(&od->key));
|
|
|
bbaaef |
free(error);
|
|
|
bbaaef |
- *ip = 0;
|
|
|
bbaaef |
- return NULL;
|
|
|
bbaaef |
+ struct in6_addr mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
|
|
|
bbaaef |
+ ip->family = AF_INET6;
|
|
|
bbaaef |
+ error = ipv6_parse_masked(ip_address, &ip->ipv6, &mask_v6);
|
|
|
bbaaef |
+ if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) {
|
|
|
bbaaef |
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
+ VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
|
|
|
bbaaef |
+ ip_address, UUID_ARGS(&od->key));
|
|
|
bbaaef |
+ memset(ip, 0, sizeof *ip);
|
|
|
bbaaef |
+ ip->family = AF_UNSPEC;
|
|
|
bbaaef |
+ return NULL;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
}
|
|
|
bbaaef |
return ip_address;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- *ip = 0;
|
|
|
bbaaef |
+ memset(ip, 0, sizeof *ip);
|
|
|
bbaaef |
+ ip->family = AF_UNSPEC;
|
|
|
bbaaef |
return NULL;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -6866,11 +6933,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
/* A gateway router can have 2 SNAT IP addresses to force DNATed and
|
|
|
bbaaef |
* LBed traffic respectively to be SNATed. In addition, there can be
|
|
|
bbaaef |
* a number of SNAT rules in the NAT table. */
|
|
|
bbaaef |
- ovs_be32 *snat_ips = xmalloc(sizeof *snat_ips *
|
|
|
bbaaef |
- (op->od->nbr->n_nat + 2));
|
|
|
bbaaef |
+ struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips
|
|
|
bbaaef |
+ * (op->od->nbr->n_nat + 2));
|
|
|
bbaaef |
size_t n_snat_ips = 0;
|
|
|
bbaaef |
|
|
|
bbaaef |
- ovs_be32 snat_ip;
|
|
|
bbaaef |
+ struct v46_ip snat_ip;
|
|
|
bbaaef |
const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat",
|
|
|
bbaaef |
&snat_ip);
|
|
|
bbaaef |
if (dnat_force_snat_ip) {
|
|
|
bbaaef |
@@ -6889,44 +6956,85 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
nat = op->od->nbr->nat[i];
|
|
|
bbaaef |
|
|
|
bbaaef |
ovs_be32 ip;
|
|
|
bbaaef |
+ struct in6_addr ipv6;
|
|
|
bbaaef |
+ bool is_v6 = false;
|
|
|
bbaaef |
if (!ip_parse(nat->external_ip, &ip) || !ip) {
|
|
|
bbaaef |
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
- VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
|
|
|
bbaaef |
- "for router %s", nat->external_ip, op->key);
|
|
|
bbaaef |
- continue;
|
|
|
bbaaef |
+ if (!ipv6_parse(nat->external_ip, &ipv6)) {
|
|
|
bbaaef |
+ static struct vlog_rate_limit rl =
|
|
|
bbaaef |
+ VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
+ VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
|
|
|
bbaaef |
+ "for router %s", nat->external_ip, op->key);
|
|
|
bbaaef |
+ continue;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ is_v6 = true;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
if (!strcmp(nat->type, "snat")) {
|
|
|
bbaaef |
- snat_ips[n_snat_ips++] = ip;
|
|
|
bbaaef |
+ if (is_v6) {
|
|
|
bbaaef |
+ snat_ips[n_snat_ips].family = AF_INET6;
|
|
|
bbaaef |
+ snat_ips[n_snat_ips++].ipv6 = ipv6;
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ snat_ips[n_snat_ips].family = AF_INET;
|
|
|
bbaaef |
+ snat_ips[n_snat_ips++].ipv4 = ip;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- /* ARP handling for external IP addresses.
|
|
|
bbaaef |
+ /* ARP / ND handling for external IP addresses.
|
|
|
bbaaef |
*
|
|
|
bbaaef |
* DNAT IP addresses are external IP addresses that need ARP
|
|
|
bbaaef |
* handling. */
|
|
|
bbaaef |
+ char addr_s[INET6_ADDRSTRLEN + 1];
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match,
|
|
|
bbaaef |
- "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
|
|
|
bbaaef |
- op->json_key, IP_ARGS(ip));
|
|
|
bbaaef |
-
|
|
|
bbaaef |
ds_clear(&actions);
|
|
|
bbaaef |
- ds_put_format(&actions,
|
|
|
bbaaef |
- "eth.dst = eth.src; "
|
|
|
bbaaef |
- "arp.op = 2; /* ARP reply */ "
|
|
|
bbaaef |
- "arp.tha = arp.sha; ");
|
|
|
bbaaef |
+ if (is_v6) {
|
|
|
bbaaef |
+ /* For ND solicitations, we need to listen for both the
|
|
|
bbaaef |
+ * unicast IPv6 address and its all-nodes multicast address,
|
|
|
bbaaef |
+ * but always respond with the unicast IPv6 address. */
|
|
|
bbaaef |
+ char sn_addr_s[INET6_ADDRSTRLEN + 1];
|
|
|
bbaaef |
+ struct in6_addr sn_addr;
|
|
|
bbaaef |
+ in6_addr_solicited_node(&sn_addr, &ipv6);
|
|
|
bbaaef |
+ ipv6_string_mapped(sn_addr_s, &sn_addr);
|
|
|
bbaaef |
+ ipv6_string_mapped(addr_s, &ipv6);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ds_put_format(&match, "inport == %s && "
|
|
|
bbaaef |
+ "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
|
|
|
bbaaef |
+ op->json_key, addr_s, sn_addr_s, addr_s);
|
|
|
bbaaef |
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.dst = eth.src; "
|
|
|
bbaaef |
+ "nd_na { ");
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ ds_put_format(&match,
|
|
|
bbaaef |
+ "inport == %s "
|
|
|
bbaaef |
+ "&& arp.tpa == "IP_FMT" && arp.op == 1",
|
|
|
bbaaef |
+ op->json_key, IP_ARGS(ip));
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.dst = eth.src; "
|
|
|
bbaaef |
+ "arp.op = 2; /* ARP reply */ "
|
|
|
bbaaef |
+ "arp.tha = arp.sha; ");
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
if (op->od->l3dgw_port && op == op->od->l3dgw_port) {
|
|
|
bbaaef |
struct eth_addr mac;
|
|
|
bbaaef |
if (nat->external_mac &&
|
|
|
bbaaef |
eth_addr_from_string(nat->external_mac, &mac)
|
|
|
bbaaef |
&& nat->logical_port) {
|
|
|
bbaaef |
/* distributed NAT case, use nat->external_mac */
|
|
|
bbaaef |
- ds_put_format(&actions,
|
|
|
bbaaef |
- "eth.src = "ETH_ADDR_FMT"; "
|
|
|
bbaaef |
- "arp.sha = "ETH_ADDR_FMT"; ",
|
|
|
bbaaef |
- ETH_ADDR_ARGS(mac),
|
|
|
bbaaef |
- ETH_ADDR_ARGS(mac));
|
|
|
bbaaef |
+ if (is_v6) {
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.src = "ETH_ADDR_FMT"; "
|
|
|
bbaaef |
+ "nd.tll = "ETH_ADDR_FMT"; ",
|
|
|
bbaaef |
+ ETH_ADDR_ARGS(mac),
|
|
|
bbaaef |
+ ETH_ADDR_ARGS(mac));
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.src = "ETH_ADDR_FMT"; "
|
|
|
bbaaef |
+ "arp.sha = "ETH_ADDR_FMT"; ",
|
|
|
bbaaef |
+ ETH_ADDR_ARGS(mac),
|
|
|
bbaaef |
+ ETH_ADDR_ARGS(mac));
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
/* Traffic with eth.src = nat->external_mac should only be
|
|
|
bbaaef |
* sent from the chassis where nat->logical_port is
|
|
|
bbaaef |
* resident, so that upstream MAC learning points to the
|
|
|
bbaaef |
@@ -6935,11 +7043,20 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
ds_put_format(&match, " && is_chassis_resident(\"%s\")",
|
|
|
bbaaef |
nat->logical_port);
|
|
|
bbaaef |
} else {
|
|
|
bbaaef |
- ds_put_format(&actions,
|
|
|
bbaaef |
- "eth.src = %s; "
|
|
|
bbaaef |
- "arp.sha = %s; ",
|
|
|
bbaaef |
- op->lrp_networks.ea_s,
|
|
|
bbaaef |
- op->lrp_networks.ea_s);
|
|
|
bbaaef |
+ if (is_v6) {
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.src = %s; "
|
|
|
bbaaef |
+ "nd.tll = %s; ",
|
|
|
bbaaef |
+ op->lrp_networks.ea_s,
|
|
|
bbaaef |
+ op->lrp_networks.ea_s);
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.src = %s; "
|
|
|
bbaaef |
+ "arp.sha = %s; ",
|
|
|
bbaaef |
+ op->lrp_networks.ea_s,
|
|
|
bbaaef |
+ op->lrp_networks.ea_s);
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
/* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
|
|
|
bbaaef |
* should only be sent from the "redirect-chassis", so that
|
|
|
bbaaef |
* upstream MAC learning points to the "redirect-chassis".
|
|
|
bbaaef |
@@ -6950,21 +7067,40 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
op->od->l3redirect_port->json_key);
|
|
|
bbaaef |
}
|
|
|
bbaaef |
}
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ if (is_v6) {
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.src = %s; "
|
|
|
bbaaef |
+ "nd.tll = %s; ",
|
|
|
bbaaef |
+ op->lrp_networks.ea_s,
|
|
|
bbaaef |
+ op->lrp_networks.ea_s);
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "eth.src = %s; "
|
|
|
bbaaef |
+ "arp.sha = %s; ",
|
|
|
bbaaef |
+ op->lrp_networks.ea_s,
|
|
|
bbaaef |
+ op->lrp_networks.ea_s);
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ if (is_v6) {
|
|
|
bbaaef |
+ ds_put_format(&actions,
|
|
|
bbaaef |
+ "ip6.src = %s; "
|
|
|
bbaaef |
+ "nd.target = %s; "
|
|
|
bbaaef |
+ "outport = %s; "
|
|
|
bbaaef |
+ "flags.loopback = 1; "
|
|
|
bbaaef |
+ "output; "
|
|
|
bbaaef |
+ "};",
|
|
|
bbaaef |
+ addr_s, addr_s, op->json_key);
|
|
|
bbaaef |
} else {
|
|
|
bbaaef |
ds_put_format(&actions,
|
|
|
bbaaef |
- "eth.src = %s; "
|
|
|
bbaaef |
- "arp.sha = %s; ",
|
|
|
bbaaef |
- op->lrp_networks.ea_s,
|
|
|
bbaaef |
- op->lrp_networks.ea_s);
|
|
|
bbaaef |
+ "arp.tpa = arp.spa; "
|
|
|
bbaaef |
+ "arp.spa = "IP_FMT"; "
|
|
|
bbaaef |
+ "outport = %s; "
|
|
|
bbaaef |
+ "flags.loopback = 1; "
|
|
|
bbaaef |
+ "output;",
|
|
|
bbaaef |
+ IP_ARGS(ip),
|
|
|
bbaaef |
+ op->json_key);
|
|
|
bbaaef |
}
|
|
|
bbaaef |
- ds_put_format(&actions,
|
|
|
bbaaef |
- "arp.tpa = arp.spa; "
|
|
|
bbaaef |
- "arp.spa = "IP_FMT"; "
|
|
|
bbaaef |
- "outport = %s; "
|
|
|
bbaaef |
- "flags.loopback = 1; "
|
|
|
bbaaef |
- "output;",
|
|
|
bbaaef |
- IP_ARGS(ip),
|
|
|
bbaaef |
- op->json_key);
|
|
|
bbaaef |
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
|
|
|
bbaaef |
ds_cstr(&match), ds_cstr(&actions));
|
|
|
bbaaef |
}
|
|
|
bbaaef |
@@ -7021,7 +7157,36 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
bool snat_ip_is_router_ip = false;
|
|
|
bbaaef |
for (int j = 0; j < n_snat_ips; j++) {
|
|
|
bbaaef |
/* Packets to SNAT IPs should not be dropped. */
|
|
|
bbaaef |
- if (op->lrp_networks.ipv4_addrs[i].addr == snat_ips[j]) {
|
|
|
bbaaef |
+ if (snat_ips[j].family == AF_INET
|
|
|
bbaaef |
+ && op->lrp_networks.ipv4_addrs[i].addr
|
|
|
bbaaef |
+ == snat_ips[j].ipv4) {
|
|
|
bbaaef |
+ snat_ip_is_router_ip = true;
|
|
|
bbaaef |
+ break;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ if (snat_ip_is_router_ip) {
|
|
|
bbaaef |
+ continue;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ ds_put_format(&match, "%s, ",
|
|
|
bbaaef |
+ op->lrp_networks.ipv4_addrs[i].addr_s);
|
|
|
bbaaef |
+ has_drop_ips = true;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ if (has_drop_ips) {
|
|
|
bbaaef |
+ ds_chomp(&match, ' ');
|
|
|
bbaaef |
+ ds_chomp(&match, ',');
|
|
|
bbaaef |
+ ds_put_cstr(&match, "} || ip6.dst == {");
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ ds_clear(&match);
|
|
|
bbaaef |
+ ds_put_cstr(&match, "ip6.dst == {");
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+
|
|
|
bbaaef |
+ for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
|
|
|
bbaaef |
+ bool snat_ip_is_router_ip = false;
|
|
|
bbaaef |
+ for (int j = 0; j < n_snat_ips; j++) {
|
|
|
bbaaef |
+ /* Packets to SNAT IPs should not be dropped. */
|
|
|
bbaaef |
+ if (snat_ips[j].family == AF_INET6
|
|
|
bbaaef |
+ && !memcmp(&op->lrp_networks.ipv6_addrs[i].addr,
|
|
|
bbaaef |
+ &snat_ips[j].ipv6, sizeof snat_ips[j].ipv6)) {
|
|
|
bbaaef |
snat_ip_is_router_ip = true;
|
|
|
bbaaef |
break;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
@@ -7030,9 +7195,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
ds_put_format(&match, "%s, ",
|
|
|
bbaaef |
- op->lrp_networks.ipv4_addrs[i].addr_s);
|
|
|
bbaaef |
+ op->lrp_networks.ipv6_addrs[i].addr_s);
|
|
|
bbaaef |
has_drop_ips = true;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
+
|
|
|
bbaaef |
ds_chomp(&match, ' ');
|
|
|
bbaaef |
ds_chomp(&match, ',');
|
|
|
bbaaef |
ds_put_cstr(&match, "}");
|
|
|
bbaaef |
@@ -7059,14 +7225,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
if (op->lrp_networks.n_ipv6_addrs) {
|
|
|
bbaaef |
- /* L3 admission control: drop packets that originate from an
|
|
|
bbaaef |
- * IPv6 address owned by the router (priority 100). */
|
|
|
bbaaef |
- ds_clear(&match);
|
|
|
bbaaef |
- ds_put_cstr(&match, "ip6.src == ");
|
|
|
bbaaef |
- op_put_v6_networks(&match, op);
|
|
|
bbaaef |
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
|
|
|
bbaaef |
- ds_cstr(&match), "drop;");
|
|
|
bbaaef |
-
|
|
|
bbaaef |
/* ICMPv6 echo reply. These flows reply to echo requests
|
|
|
bbaaef |
* received for the router's IP address. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
@@ -7083,13 +7241,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
"next; ");
|
|
|
bbaaef |
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
|
|
|
bbaaef |
ds_cstr(&match), ds_cstr(&actions));
|
|
|
bbaaef |
-
|
|
|
bbaaef |
- /* Drop IPv6 traffic to this router. */
|
|
|
bbaaef |
- ds_clear(&match);
|
|
|
bbaaef |
- ds_put_cstr(&match, "ip6.dst == ");
|
|
|
bbaaef |
- op_put_v6_networks(&match, op);
|
|
|
bbaaef |
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
|
|
|
bbaaef |
- ds_cstr(&match), "drop;");
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
/* ND reply. These flows reply to ND solicitations for the
|
|
|
bbaaef |
@@ -7231,11 +7382,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
- ovs_be32 snat_ip;
|
|
|
bbaaef |
+ struct v46_ip snat_ip, lb_snat_ip;
|
|
|
bbaaef |
const char *dnat_force_snat_ip = get_force_snat_ip(od, "dnat",
|
|
|
bbaaef |
&snat_ip);
|
|
|
bbaaef |
const char *lb_force_snat_ip = get_force_snat_ip(od, "lb",
|
|
|
bbaaef |
- &snat_ip);
|
|
|
bbaaef |
+ &lb_snat_ip);
|
|
|
bbaaef |
|
|
|
bbaaef |
for (int i = 0; i < od->nbr->n_nat; i++) {
|
|
|
bbaaef |
const struct nbrec_nat *nat;
|
|
|
bbaaef |
@@ -7243,21 +7394,38 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
nat = od->nbr->nat[i];
|
|
|
bbaaef |
|
|
|
bbaaef |
ovs_be32 ip, mask;
|
|
|
bbaaef |
+ struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
|
|
|
bbaaef |
+ bool is_v6 = false;
|
|
|
bbaaef |
|
|
|
bbaaef |
char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
|
|
|
bbaaef |
if (error || mask != OVS_BE32_MAX) {
|
|
|
bbaaef |
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
- VLOG_WARN_RL(&rl, "bad external ip %s for nat",
|
|
|
bbaaef |
- nat->external_ip);
|
|
|
bbaaef |
free(error);
|
|
|
bbaaef |
- continue;
|
|
|
bbaaef |
+ error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6);
|
|
|
bbaaef |
+ if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) {
|
|
|
bbaaef |
+ /* Invalid for both IPv4 and IPv6 */
|
|
|
bbaaef |
+ static struct vlog_rate_limit rl =
|
|
|
bbaaef |
+ VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
+ VLOG_WARN_RL(&rl, "bad external ip %s for nat",
|
|
|
bbaaef |
+ nat->external_ip);
|
|
|
bbaaef |
+ free(error);
|
|
|
bbaaef |
+ continue;
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
+ /* It was an invalid IPv4 address, but valid IPv6.
|
|
|
bbaaef |
+ * Treat the rest of the handling of this NAT rule
|
|
|
bbaaef |
+ * as IPv6. */
|
|
|
bbaaef |
+ is_v6 = true;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
/* Check the validity of nat->logical_ip. 'logical_ip' can
|
|
|
bbaaef |
* be a subnet when the type is "snat". */
|
|
|
bbaaef |
- error = ip_parse_masked(nat->logical_ip, &ip, &mask);
|
|
|
bbaaef |
+ if (is_v6) {
|
|
|
bbaaef |
+ error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6);
|
|
|
bbaaef |
+ } else {
|
|
|
bbaaef |
+ error = ip_parse_masked(nat->logical_ip, &ip, &mask);
|
|
|
bbaaef |
+ }
|
|
|
bbaaef |
if (!strcmp(nat->type, "snat")) {
|
|
|
bbaaef |
if (error) {
|
|
|
bbaaef |
+ /* Invalid for both IPv4 and IPv6 */
|
|
|
bbaaef |
static struct vlog_rate_limit rl =
|
|
|
bbaaef |
VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat "
|
|
|
bbaaef |
@@ -7267,7 +7435,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
continue;
|
|
|
bbaaef |
}
|
|
|
bbaaef |
} else {
|
|
|
bbaaef |
- if (error || mask != OVS_BE32_MAX) {
|
|
|
bbaaef |
+ if (error || (!is_v6 && mask != OVS_BE32_MAX)
|
|
|
bbaaef |
+ || (is_v6 && memcmp(&mask_v6, &v6_exact,
|
|
|
bbaaef |
+ sizeof mask_v6))) {
|
|
|
bbaaef |
+ /* Invalid for both IPv4 and IPv6 */
|
|
|
bbaaef |
static struct vlog_rate_limit rl =
|
|
|
bbaaef |
VLOG_RATE_LIMIT_INIT(5, 1);
|
|
|
bbaaef |
VLOG_WARN_RL(&rl, "bad ip %s for dnat in router "
|
|
|
bbaaef |
@@ -7308,7 +7479,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
if (!od->l3dgw_port) {
|
|
|
bbaaef |
/* Gateway router. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s",
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->external_ip);
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
|
|
|
bbaaef |
ds_cstr(&match), "ct_snat;");
|
|
|
bbaaef |
@@ -7317,8 +7489,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
|
|
|
bbaaef |
/* Traffic received on l3dgw_port is subject to NAT. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s"
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s"
|
|
|
bbaaef |
" && inport == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->external_ip,
|
|
|
bbaaef |
od->l3dgw_port->json_key);
|
|
|
bbaaef |
if (!distributed && od->l3redirect_port) {
|
|
|
bbaaef |
@@ -7334,7 +7507,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
* redirected to the central instance of the l3dgw_port
|
|
|
bbaaef |
* for NAT processing. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s",
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->external_ip);
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
|
|
|
bbaaef |
ds_cstr(&match),
|
|
|
bbaaef |
@@ -7353,7 +7527,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
* We need to set flags.loopback because the router can
|
|
|
bbaaef |
* send the packet back through the same interface. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s",
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->external_ip);
|
|
|
bbaaef |
ds_clear(&actions);
|
|
|
bbaaef |
if (dnat_force_snat_ip) {
|
|
|
bbaaef |
@@ -7372,8 +7547,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
|
|
|
bbaaef |
/* Traffic received on l3dgw_port is subject to NAT. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s"
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s"
|
|
|
bbaaef |
" && inport == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->external_ip,
|
|
|
bbaaef |
od->l3dgw_port->json_key);
|
|
|
bbaaef |
if (!distributed && od->l3redirect_port) {
|
|
|
bbaaef |
@@ -7392,7 +7568,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
* redirected to the central instance of the l3dgw_port
|
|
|
bbaaef |
* for NAT processing. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s",
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->external_ip);
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
|
|
|
bbaaef |
ds_cstr(&match),
|
|
|
bbaaef |
@@ -7411,8 +7588,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
if (od->l3dgw_port && (!strcmp(nat->type, "dnat")
|
|
|
bbaaef |
|| !strcmp(nat->type, "dnat_and_snat"))) {
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.src == %s"
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.src == %s"
|
|
|
bbaaef |
" && outport == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->logical_ip,
|
|
|
bbaaef |
od->l3dgw_port->json_key);
|
|
|
bbaaef |
if (!distributed && od->l3redirect_port) {
|
|
|
bbaaef |
@@ -7439,7 +7617,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
if (!od->l3dgw_port) {
|
|
|
bbaaef |
/* Gateway router. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.src == %s",
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.src == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->logical_ip);
|
|
|
bbaaef |
ds_clear(&actions);
|
|
|
bbaaef |
ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
|
|
|
bbaaef |
@@ -7455,8 +7634,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
|
|
|
bbaaef |
/* Distributed router. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.src == %s"
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.src == %s"
|
|
|
bbaaef |
" && outport == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->logical_ip,
|
|
|
bbaaef |
od->l3dgw_port->json_key);
|
|
|
bbaaef |
if (!distributed && od->l3redirect_port) {
|
|
|
bbaaef |
@@ -7505,7 +7685,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
* can be applied in a distributed manner. */
|
|
|
bbaaef |
if (distributed) {
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip4.src == %s && outport == %s",
|
|
|
bbaaef |
+ ds_put_format(&match, "ip%s.src == %s && outport == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->logical_ip,
|
|
|
bbaaef |
od->l3dgw_port->json_key);
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
|
|
|
bbaaef |
@@ -7532,9 +7713,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
ds_put_format(&match, "is_chassis_resident(\"%s\") && "
|
|
|
bbaaef |
- "ip4.src == %s && ip4.dst == %s",
|
|
|
bbaaef |
- nat->logical_port, nat2->external_ip,
|
|
|
bbaaef |
- nat->external_ip);
|
|
|
bbaaef |
+ "ip%s.src == %s && ip%s.dst == %s",
|
|
|
bbaaef |
+ nat->logical_port,
|
|
|
bbaaef |
+ is_v6 ? "6" : "4", nat2->external_ip,
|
|
|
bbaaef |
+ is_v6 ? "6" : "4", nat->external_ip);
|
|
|
bbaaef |
ds_clear(&actions);
|
|
|
bbaaef |
ds_put_format(&actions,
|
|
|
bbaaef |
"inport = outport; outport = \"\"; "
|
|
|
bbaaef |
@@ -7546,8 +7728,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
ds_put_format(&match,
|
|
|
bbaaef |
- "ip4.src == %s && ip4.dst == %s",
|
|
|
bbaaef |
- nat2->external_ip, nat->external_ip);
|
|
|
bbaaef |
+ "ip%s.src == %s && ip%s.dst == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4", nat2->external_ip,
|
|
|
bbaaef |
+ is_v6 ? "6" : "4", nat->external_ip);
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200,
|
|
|
bbaaef |
ds_cstr(&match), "next;");
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
@@ -7555,7 +7738,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
}
|
|
|
bbaaef |
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip4.dst == %s && outport == %s",
|
|
|
bbaaef |
+ ds_put_format(&match, "ip%s.dst == %s && outport == %s",
|
|
|
bbaaef |
+ is_v6 ? "6" : "4",
|
|
|
bbaaef |
nat->external_ip,
|
|
|
bbaaef |
od->l3dgw_port->json_key);
|
|
|
bbaaef |
ds_clear(&actions);
|
|
|
bbaaef |
@@ -7579,7 +7763,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
* gateway router (as set in options:dnat_force_snat_ip) is seen,
|
|
|
bbaaef |
* UNSNAT it. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s", dnat_force_snat_ip);
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s",
|
|
|
bbaaef |
+ snat_ip.family == AF_INET ? "4" : "6",
|
|
|
bbaaef |
+ dnat_force_snat_ip);
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110,
|
|
|
bbaaef |
ds_cstr(&match), "ct_snat;");
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -7598,7 +7784,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
* gateway router (as set in options:lb_force_snat_ip) is seen,
|
|
|
bbaaef |
* UNSNAT it. */
|
|
|
bbaaef |
ds_clear(&match);
|
|
|
bbaaef |
- ds_put_format(&match, "ip && ip4.dst == %s", lb_force_snat_ip);
|
|
|
bbaaef |
+ ds_put_format(&match, "ip && ip%s.dst == %s",
|
|
|
bbaaef |
+ lb_snat_ip.family == AF_INET ? "4" : "6",
|
|
|
bbaaef |
+ lb_force_snat_ip);
|
|
|
bbaaef |
ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
|
|
|
bbaaef |
ds_cstr(&match), "ct_snat;");
|
|
|
bbaaef |
|
|
|
bbaaef |
@@ -7700,7 +7888,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
if (addr_family == AF_INET) {
|
|
|
bbaaef |
ds_put_format(&match, "ip && ip4.dst == %s",
|
|
|
bbaaef |
ip_address);
|
|
|
bbaaef |
- } else {
|
|
|
bbaaef |
+ } else if (addr_family == AF_INET6) {
|
|
|
bbaaef |
ds_put_format(&match, "ip && ip6.dst == %s",
|
|
|
bbaaef |
ip_address);
|
|
|
bbaaef |
}
|
|
|
bbaaef |
@@ -7720,7 +7908,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
|
|
|
bbaaef |
if (addr_family == AF_INET) {
|
|
|
bbaaef |
ds_put_format(&match, "ip && ip4.dst == %s",
|
|
|
bbaaef |
ip_address);
|
|
|
bbaaef |
- } else {
|
|
|
bbaaef |
+ } else if (addr_family == AF_INET6) {
|
|
|
bbaaef |
ds_put_format(&match, "ip && ip6.dst == %s",
|
|
|
bbaaef |
ip_address);
|
|
|
bbaaef |
}
|
|
|
bbaaef |
--
|
|
|
bbaaef |
2.23.0
|
|
|
bbaaef |
|