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