summaryrefslogtreecommitdiff
path: root/ovn
diff options
context:
space:
mode:
authorMark Michelson <mmichels@redhat.com>2017-11-02 11:13:58 -0500
committerBen Pfaff <blp@ovn.org>2017-11-02 10:52:26 -0700
commit485d373bf6e934d1842f4542a1eaf163b733c983 (patch)
tree0585d24160d3caeec48869802bf598bba4baf444 /ovn
parent9d236afafb7817df8a4e9fe6a46f00109e3b86d9 (diff)
downloadopenvswitch-485d373bf6e934d1842f4542a1eaf163b733c983.tar.gz
ovn: Allow northd to install IPv6 ct_lb logical flows.
For this commit, ovn-northd will now accept both IPv4 and IPv6 addresses in the northbound database for a load balancer VIP or destination addresses. For IPv4, the behavior remains the same. For IPv6, the following logical flows will be added to the southbound database: * An ND_NA response for incoming ND_NS requests for the load balancer VIP. * A ct_lb flow with the configured IPv6 addresses. The ovn-northd manpage has been updated to indicate what flows are added for load balancers with IPv6 VIPs. Signed-off-by: Mark Michelson <mmichels@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'ovn')
-rw-r--r--ovn/northd/ovn-northd.8.xml68
-rw-r--r--ovn/northd/ovn-northd.c182
-rw-r--r--ovn/ovn-nb.xml22
3 files changed, 163 insertions, 109 deletions
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index e9799e18a..b31106521 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -243,8 +243,10 @@
balancing rules with virtual IP addresses (and ports) are configured in
<code>OVN_Northbound</code> database for a logical switch datapath, a
priority-100 flow is added for each configured virtual IP address
- <var>VIP</var> with a match <code>ip &amp;&amp; ip4.dst == <var>VIP</var>
- </code> that sets an action <code>reg0[0] = 1; next;</code> to act as a
+ <var>VIP</var>. For IPv4 <var>VIPs</var>, the match is <code>ip
+ &amp;&amp; ip4.dst == <var>VIP</var></code>. For IPv6 <var>VIPs</var>,
+ the match is <code>ip &amp;&amp; ip6.dst == <var>VIP</var></code>. The
+ flow sets an action <code>reg0[0] = 1; next;</code> to act as a
hint for table <code>Pre-stateful</code> to send IP packets to the
connection tracker for packet de-fragmentation before eventually
advancing to ingress table <code>LB</code>.
@@ -383,21 +385,29 @@
<li>
For all the configured load balancing rules for a switch in
<code>OVN_Northbound</code> database that includes a L4 port
- <var>PORT</var> of protocol <var>P</var> and IPv4 address
- <var>VIP</var>, a priority-120 flow that matches on
- <code>ct.new &amp;&amp; ip &amp;&amp; ip4.dst == <var>VIP
- </var>&amp;&amp; <var>P</var> &amp;&amp; <var>P</var>.dst == <var>PORT
- </var></code> with an action of <code>ct_lb(<var>args</var>)</code>,
- where <var>args</var> contains comma separated IPv4 addresses (and
- optional port numbers) to load balance to.
+ <var>PORT</var> of protocol <var>P</var> and IP address
+ <var>VIP</var>, a priority-120 flow is added. For IPv4 <var>VIPs
+ </var>, the flow matches <code>ct.new &amp;&amp; ip &amp;&amp;
+ ip4.dst == <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp;
+ <var>P</var>.dst == <var>PORT</var></code>. For IPv6 <var>VIPs</var>,
+ the flow matches <code>ct.new &amp;&amp; ip &amp;&amp; ip6.dst == <var>
+ VIP </var>&amp;&amp; <var>P</var> &amp;&amp; <var>P</var>.dst == <var>
+ PORT</var></code>. The flow's action is <code>ct_lb(<var>args</var>)
+ </code>, where <var>args</var> contains comma separated IP addresses
+ (and optional port numbers) to load balance to. The address family of
+ the IP addresses of <var>args</var> is the same as the address family
+ of <var>VIP</var>
</li>
<li>
For all the configured load balancing rules for a switch in
<code>OVN_Northbound</code> database that includes just an IP address
- <var>VIP</var> to match on, a priority-110 flow that matches on
- <code>ct.new &amp;&amp; ip &amp;&amp; ip4.dst == <var>VIP</var></code>
- with an action of <code>ct_lb(<var>args</var>)</code>, where
- <var>args</var> contains comma separated IPv4 addresses.
+ <var>VIP</var> to match on, OVN adds a priority-110 flow. For IPv4
+ <var>VIPs</var>, the flow matches <code>ct.new &amp;&amp; ip &amp;&amp;
+ ip4.dst == <var>VIP</var></code>. For IPv6 <var>VIPs</var>,
+ the flow matches <code>ct.new &amp;&amp; ip &amp;&amp; ip6.dst == <var>
+ VIP</var></code>. The action on this flow is <code>
+ ct_lb(<var>args</var>)</code>, where <var>args</var> contains comma
+ separated IP addresses of the same address family as <var>VIP</var>.
</li>
<li>
A priority-100 flow commits packets to connection tracker using
@@ -1113,7 +1123,7 @@ output;
<p>
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 VIP <var>A</var>,
+ configured DNAT IP address or a load balancer IPv4 VIP <var>A</var>,
for each router port <var>P</var> with Ethernet
address <var>E</var>, a priority-90 flow matches
<code>inport == <var>P</var> &amp;&amp; arp.op == 1 &amp;&amp;
@@ -1190,13 +1200,13 @@ arp.sha = <var>external_mac</var>;
<p>
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 <var>P</var> that owns IPv6 address
- <var>A</var>, solicited node address <var>S</var>, and
- Ethernet address <var>E</var>, a priority-90 flow matches
- <code>inport == <var>P</var> &amp;&amp; nd_ns &amp;&amp;
- ip6.dst == {<var>A</var>, <var>E</var>} &amp;&amp; nd.target
- == <var>A</var></code> with the following actions:
+ address and load balancing IPv6 VIPs and populate the logical
+ router's mac binding table. For each router port <var>P</var> that
+ owns IPv6 address or has load balancing VIP <var>A</var>, solicited
+ node address <var>S</var>, and Ethernet address <var>E</var>, a
+ priority-90 flow matches <code>inport == <var>P</var> &amp;&amp;
+ nd_ns &amp;&amp; ip6.dst == {<var>A</var>, <var>E</var>} &amp;&amp;
+ nd.target == <var>A</var></code> with the following actions:
</p>
<pre>
@@ -1364,10 +1374,12 @@ icmp4 {
to the next table. If load balancing rules with virtual IP addresses
(and ports) are configured in <code>OVN_Northbound</code> database for a
Gateway router, a priority-100 flow is added for each configured virtual
- IP address <var>VIP</var> with a match <code>ip &amp;&amp;
- ip4.dst == <var>VIP</var></code> that sets an action
- <code>ct_next;</code> to send IP packets to the connection tracker for
- packet de-fragmentation and tracking before sending it to the next table.
+ IP address <var>VIP</var>. For IPv4 <var>VIPs</var> the flow matches
+ <code>ip &amp;&amp; ip4.dst == <var>VIP</var></code>. For IPv6
+ <var>VIPs</var>, the flow matches <code>ip &amp;&amp; ip6.dst ==
+ <var>VIP</var></code>. The flow uses the action <code>ct_next;</code>
+ to send IP packets to the connection tracker for packet de-fragmentation
+ and tracking before sending it to the next table.
</p>
<h3>Ingress Table 3: UNSNAT</h3>
@@ -1464,7 +1476,8 @@ icmp4 {
<p>
Following load balancing DNAT flows are added for Gateway router or
Router with gateway port. These flows are programmed only on the
- <code>redirect-chassis</code>.
+ <code>redirect-chassis</code>. These flows do not get programmed for
+ load balancers with IPv6 <var>VIPs</var>.
</p>
<ul>
@@ -1910,7 +1923,8 @@ arp {
router gateway port with an action <code>ct_dnat;</code>. If the
backend IPv4 address <var>B</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>.
+ match also includes <code>P.src</code> == <var>PORT</var>. These
+ flows are not added for load balancers with IPv6 <var>VIPs</var>.
</p>
<p>
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 268bd60d6..6732ad003 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1562,11 +1562,11 @@ join_logical_ports(struct northd_context *ctx,
static void
ip_address_and_port_from_lb_key(const char *key, char **ip_address,
- uint16_t *port);
+ uint16_t *port, int *addr_family);
static void
get_router_load_balancer_ips(const struct ovn_datapath *od,
- struct sset *all_ips)
+ struct sset *all_ips, int *addr_family)
{
if (!od->nbr) {
return;
@@ -1582,7 +1582,8 @@ get_router_load_balancer_ips(const struct ovn_datapath *od,
char *ip_address = NULL;
uint16_t port;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ addr_family);
if (!ip_address) {
continue;
}
@@ -1659,7 +1660,8 @@ get_nat_addresses(const struct ovn_port *op, size_t *n)
/* A set to hold all load-balancer vips. */
struct sset all_ips = SSET_INITIALIZER(&all_ips);
- get_router_load_balancer_ips(op->od, &all_ips);
+ int addr_family;
+ get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
const char *ip_address;
SSET_FOR_EACH (ip_address, &all_ips) {
@@ -2902,44 +2904,33 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
* 'ip_address'. */
static void
ip_address_and_port_from_lb_key(const char *key, char **ip_address,
- uint16_t *port)
+ uint16_t *port, int *addr_family)
{
- char *ip_str, *start, *next;
- *ip_address = NULL;
- *port = 0;
+ struct sockaddr_storage ss;
+ char ip_addr_buf[INET6_ADDRSTRLEN];
+ char *error;
- next = start = xstrdup(key);
- ip_str = strsep(&next, ":");
- if (!ip_str || !ip_str[0]) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
- free(start);
- return;
- }
-
- ovs_be32 ip, mask;
- char *error = ip_parse_masked(ip_str, &ip, &mask);
- if (error || mask != OVS_BE32_MAX) {
+ error = ipv46_parse(key, PORT_OPTIONAL, &ss);
+ if (error) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
- free(start);
+ VLOG_WARN_RL(&rl, "bad ip address or port for load balancer key %s",
+ key);
free(error);
return;
}
- int l4_port = 0;
- if (next && next[0]) {
- if (!str_to_int(next, 0, &l4_port) || l4_port < 0 || l4_port > 65535) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip port for load balancer key %s", key);
- free(start);
- return;
- }
+ if (ss.ss_family == AF_INET) {
+ struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ss);
+ *port = sin->sin_port == 0 ? 0 : ntohs(sin->sin_port);
+ inet_ntop(AF_INET, &sin->sin_addr, ip_addr_buf, sizeof ip_addr_buf);
+ } else {
+ struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, &ss);
+ *port = sin6->sin6_port == 0 ? 0 : ntohs(sin6->sin6_port);
+ inet_ntop(AF_INET6, &sin6->sin6_addr, ip_addr_buf, sizeof ip_addr_buf);
}
- *port = l4_port;
- *ip_address = strdup(ip_str);
- free(start);
+ *ip_address = xstrdup(ip_addr_buf);
+ *addr_family = ss.ss_family;
}
/*
@@ -2967,6 +2958,7 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
struct sset all_ips = SSET_INITIALIZER(&all_ips);
bool vip_configured = false;
+ int addr_family = AF_INET;
for (int i = 0; i < od->nbs->n_load_balancer; i++) {
struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
struct smap *vips = &lb->vips;
@@ -2978,7 +2970,8 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
uint16_t port;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
continue;
}
@@ -3000,7 +2993,13 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
* packet to conntrack for defragmentation. */
const char *ip_address;
SSET_FOR_EACH(ip_address, &all_ips) {
- char *match = xasprintf("ip && ip4.dst == %s", ip_address);
+ char *match;
+
+ if (addr_family == AF_INET) {
+ match = xasprintf("ip && ip4.dst == %s", ip_address);
+ } else {
+ match = xasprintf("ip && ip6.dst == %s", ip_address);
+ }
ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;");
free(match);
@@ -3458,10 +3457,12 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows)
SMAP_FOR_EACH (node, vips) {
uint16_t port = 0;
+ int addr_family;
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
continue;
}
@@ -3469,7 +3470,11 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows)
/* New connections in Ingress table. */
char *action = xasprintf("ct_lb(%s);", node->value);
struct ds match = DS_EMPTY_INITIALIZER;
- ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address);
+ if (addr_family == AF_INET) {
+ ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address);
+ } else {
+ ds_put_format(&match, "ct.new && ip6.dst == %s", ip_address);
+ }
if (port) {
if (lb->protocol && !strcmp(lb->protocol, "udp")) {
ds_put_format(&match, " && udp.dst == %d", port);
@@ -4352,7 +4357,7 @@ static void
add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
struct ds *match, struct ds *actions, int priority,
const char *lb_force_snat_ip, char *backend_ips,
- bool is_udp)
+ bool is_udp, int addr_family)
{
/* A match and actions for new connections. */
char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
@@ -4380,7 +4385,8 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
free(new_match);
free(est_match);
- if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips) {
+ if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips
+ || addr_family != AF_INET) {
return;
}
@@ -4397,7 +4403,9 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
while (ip_str && ip_str[0]) {
char *ip_address = NULL;
uint16_t port = 0;
- ip_address_and_port_from_lb_key(ip_str, &ip_address, &port);
+ int addr_family;
+ ip_address_and_port_from_lb_key(ip_str, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
break;
}
@@ -4635,36 +4643,55 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
/* A set to hold all load-balancer vips that need ARP responses. */
struct sset all_ips = SSET_INITIALIZER(&all_ips);
- get_router_load_balancer_ips(op->od, &all_ips);
+ int addr_family;
+ get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
const char *ip_address;
SSET_FOR_EACH(ip_address, &all_ips) {
- ovs_be32 ip;
- if (!ip_parse(ip_address, &ip) || !ip) {
- continue;
- }
-
ds_clear(&match);
- ds_put_format(&match,
- "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
- op->json_key, IP_ARGS(ip));
+ if (addr_family == AF_INET) {
+ ds_put_format(&match,
+ "inport == %s && arp.tpa == %s && arp.op == 1",
+ op->json_key, ip_address);
+ } else {
+ ds_put_format(&match,
+ "inport == %s && nd_ns && nd.target == %s",
+ op->json_key, ip_address);
+ }
ds_clear(&actions);
- ds_put_format(&actions,
+ if (addr_family == AF_INET) {
+ ds_put_format(&actions,
"eth.dst = eth.src; "
"eth.src = %s; "
"arp.op = 2; /* ARP reply */ "
"arp.tha = arp.sha; "
"arp.sha = %s; "
"arp.tpa = arp.spa; "
- "arp.spa = "IP_FMT"; "
+ "arp.spa = %s; "
"outport = %s; "
"flags.loopback = 1; "
"output;",
op->lrp_networks.ea_s,
op->lrp_networks.ea_s,
- IP_ARGS(ip),
+ ip_address,
op->json_key);
+ } else {
+ ds_put_format(&actions,
+ "nd_na { "
+ "eth.src = %s; "
+ "ip6.src = %s; "
+ "nd.target = %s; "
+ "nd.tll = %s; "
+ "outport = inport; "
+ "flags.loopback = 1; "
+ "output; "
+ "};",
+ op->lrp_networks.ea_s,
+ ip_address,
+ ip_address,
+ op->lrp_networks.ea_s);
+ }
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
ds_cstr(&match), ds_cstr(&actions));
}
@@ -5328,16 +5355,36 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
SMAP_FOR_EACH (node, vips) {
uint16_t port = 0;
+ int addr_family;
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
continue;
}
if (!sset_contains(&all_ips, ip_address)) {
sset_add(&all_ips, ip_address);
+ /* If there are any load balancing rules, we should send
+ * the packet to conntrack for defragmentation and
+ * tracking. This helps with two things.
+ *
+ * 1. With tracking, we can send only new connections to
+ * pick a DNAT ip address from a group.
+ * 2. If there are L4 ports in load balancing rules, we
+ * need the defragmentation to match on L4 ports. */
+ ds_clear(&match);
+ if (addr_family == AF_INET) {
+ ds_put_format(&match, "ip && ip4.dst == %s",
+ ip_address);
+ } else {
+ ds_put_format(&match, "ip && ip6.dst == %s",
+ ip_address);
+ }
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
+ 100, ds_cstr(&match), "ct_next;");
}
/* Higher priority rules are added for load-balancing in DNAT
@@ -5349,8 +5396,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
ds_put_format(&actions, "ct_lb(%s);", node->value);
ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s",
- ip_address);
+ if (addr_family == AF_INET) {
+ ds_put_format(&match, "ip && ip4.dst == %s",
+ ip_address);
+ } else {
+ ds_put_format(&match, "ip && ip6.dst == %s",
+ ip_address);
+ }
free(ip_address);
int prio = 110;
@@ -5372,26 +5424,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
od->l3redirect_port->json_key);
}
add_router_lb_flow(lflows, od, &match, &actions, prio,
- lb_force_snat_ip, node->value, is_udp);
+ lb_force_snat_ip, node->value, is_udp,
+ addr_family);
}
}
-
- /* If there are any load balancing rules, we should send the
- * packet to conntrack for defragmentation and tracking. This helps
- * with two things.
- *
- * 1. With tracking, we can send only new connections to pick a
- * DNAT ip address from a group.
- * 2. If there are L4 ports in load balancing rules, we need the
- * defragmentation to match on L4 ports. */
- const char *ip_address;
- SSET_FOR_EACH(ip_address, &all_ips) {
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s", ip_address);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
- 100, ds_cstr(&match), "ct_next;");
- }
-
sset_destroy(&all_ips);
}
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index 9869d7ed7..a6afc9583 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -113,8 +113,8 @@
</column>
<column name="load_balancer">
- Load balance a virtual ipv4 address to a set of logical port endpoint
- ipv4 addresses.
+ Load balance a virtual ip address to a set of logical port endpoint
+ ip addresses.
</column>
<column name="acls">
@@ -925,16 +925,20 @@
<column name="vips">
<p>
- A map of virtual IPv4 addresses (and an optional port number with
+ A map of virtual IP addresses (and an optional port number with
<code>:</code> as a separator) associated with this load balancer and
- their corresponding endpoint IPv4 addresses (and optional port numbers
+ their corresponding endpoint IP addresses (and optional port numbers
with <code>:</code> as separators) separated by commas. If
the destination IP address (and port number) of a packet leaving a
- container or a VM matches the virtual IPv4 address (and port number)
+ container or a VM matches the virtual IP address (and port number)
provided here as a key, then OVN will statefully replace the
- destination IP address by one of the provided IPv4 address (and port
- number) in this map as a value. Examples for keys are "192.168.1.4"
- and "172.16.1.8:80". Examples for value are "10.0.0.1, 10.0.0.2" and
+ destination IP address by one of the provided IP address (and port
+ number) in this map as a value. IPv4 and IPv6 addresses are supported
+ for load balancing; however a VIP of one address family may not be
+ mapped to a destination IP address of a different family. If
+ specifying an IPv6 address with a port, the address portion must be
+ enclosed in square brackets. Examples for keys are "192.168.1.4" and
+ "[fd0f::1]:8800". Examples for value are "10.0.0.1, 10.0.0.2" and
"20.0.0.10:8800, 20.0.0.11:8800".
</p>
</column>
@@ -1116,7 +1120,7 @@
</column>
<column name="load_balancer">
- Load balance a virtual ipv4 address to a set of logical port ipv4
+ Load balance a virtual ip address to a set of logical port ip
addresses. Load balancer rules only work on the Gateway routers.
</column>