Currently in OVN, the "nat-addresses" in the "options" column of a logical switch port of type "router" must be specified manually. Typically the user would specify as "nat-addresses" all of the NAT external IP addresses and load balancer IP addresses that have already been specified separately on the router.
This patch allows the logical switch port's "nat-addresses" to be specified as the string "router". When ovn-northd sees this string, it automatically copies the following into the southbound Port_Binding's "nat-addresses" in the "options" column: The options:router-port's MAC address. Each NAT external IP address (of any NAT type) specified on the logical router of options:router-port. Each load balancer IP address specified on the logical router of options:router-port. This will cause the controller where the gateway router resides to issue gratuitous ARPs for each NAT external IP address and for each load balancer IP address specified on the gateway router. This patch is written as if it will be included in OVS 2.7. If it is deferred to OVS 2.8, then the OVS version mentioned in ovn-nb.xml will need to be updated from OVS 2.7 to OVS 2.8. Signed-off-by: Mickey Spiegel <mickeys....@gmail.com> --- ovn/northd/ovn-northd.c | 114 ++++++++++++++++++++++++++++++++++++++---------- ovn/ovn-nb.xml | 42 +++++++++++++++--- tests/ovn.at | 60 +++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 31 deletions(-) diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 8e5a8ce..5b0a235 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -1436,6 +1436,86 @@ 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); + +static void +get_router_load_balancer_ips(const struct ovn_datapath *od, + struct sset *all_ips) +{ + if (!od->nbr) { + return; + } + + for (int i = 0; i < od->nbr->n_load_balancer; i++) { + struct nbrec_load_balancer *lb = od->nbr->load_balancer[i]; + struct smap *vips = &lb->vips; + struct smap_node *node; + + SMAP_FOR_EACH (node, vips) { + /* 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); + if (!ip_address) { + continue; + } + + if (!sset_contains(all_ips, ip_address)) { + sset_add(all_ips, ip_address); + } + + free(ip_address); + } + } +} + +/* Returns a string consisting of the port's MAC address followed by the + * external IP addresses of all NAT rules defined on that router and the + * VIPs of all load balancers defined on that router. */ +static char * +get_nat_addresses(const struct ovn_port *op) +{ + struct eth_addr mac; + if (!op->nbrp || !op->od || !op->od->nbr + || (!op->od->nbr->n_nat && !op->od->nbr->n_load_balancer) + || !eth_addr_from_string(op->nbrp->mac, &mac)) { + return NULL; + } + + struct ds addresses = DS_EMPTY_INITIALIZER; + ds_put_format(&addresses, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac)); + + /* Get NAT IP addresses. */ + for (int i = 0; i < op->od->nbr->n_nat; i++) { + const struct nbrec_nat *nat; + nat = op->od->nbr->nat[i]; + + ovs_be32 ip, mask; + + char *error = ip_parse_masked(nat->external_ip, &ip, &mask); + if (error || mask != OVS_BE32_MAX) { + free(error); + continue; + } + ds_put_format(&addresses, " %s", nat->external_ip); + } + + /* 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); + + const char *ip_address; + SSET_FOR_EACH(ip_address, &all_ips) { + ds_put_format(&addresses, " %s", ip_address); + } + sset_destroy(&all_ips); + + return ds_steal_cstr(&addresses); +} + +static void ovn_port_update_sbrec(const struct ovn_port *op, struct hmap *chassis_qdisc_queues) { @@ -1524,7 +1604,15 @@ ovn_port_update_sbrec(const struct ovn_port *op, const char *nat_addresses = smap_get(&op->nbsp->options, "nat-addresses"); - if (nat_addresses) { + if (nat_addresses && !strcmp(nat_addresses, "router")) { + if (op->peer && op->peer->nbrp) { + char *nats = get_nat_addresses(op->peer); + if (nats) { + smap_add(&new, "nat-addresses", nats); + free(nats); + } + } + } else if (nat_addresses) { struct lport_addresses laddrs; if (!extract_lsp_addresses(nat_addresses, &laddrs)) { static struct vlog_rate_limit rl = @@ -3869,29 +3957,7 @@ 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); - - for (int i = 0; i < op->od->nbr->n_load_balancer; i++) { - struct nbrec_load_balancer *lb = op->od->nbr->load_balancer[i]; - struct smap *vips = &lb->vips; - struct smap_node *node; - - SMAP_FOR_EACH (node, vips) { - /* 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); - if (!ip_address) { - continue; - } - - if (!sset_contains(&all_ips, ip_address)) { - sset_add(&all_ips, ip_address); - } - - free(ip_address); - } - } + get_router_load_balancer_ips(op->od, &all_ips); const char *ip_address; SSET_FOR_EACH(ip_address, &all_ips) { diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 268354a..29fd9d2 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -242,13 +242,41 @@ </column> <column name="options" key="nat-addresses"> - MAC address of the <code>router-port</code> followed by a list of - SNAT and DNAT IP addresses. This is used to send gratuitous ARPs for - SNAT and DNAT IP addresses via <code>localnet</code> and is valid for - only L3 gateway ports. Example: <code>80:fa:5b:06:72:b7 158.36.44.22 - 158.36.44.24</code>. This would result in generation of gratuitous - ARPs for IP addresses 158.36.44.22 and 158.36.44.24 with a MAC - address of 80:fa:5b:06:72:b7. + <p> + This is used to send gratuitous ARPs for SNAT and DNAT IP + addresses via <code>localnet</code> and is valid for only L3 + gateway ports. + </p> + + <p> + This must take one of the following forms: + </p> + + <dl> + <dt><code>router</code></dt> + <dd> + <p> + Gratuitous ARPs will be sent for all SNAT and DNAT external IP + addresses and for all load balancer IP addresses defined on the + <ref column="options" key="router-port"/>'s logical router, + using the <ref column="options" key="router-port"/>'s MAC + address. + </p> + + <p> + Supported only in OVN 2.7 and later. Earlier versions required + NAT addresses to be manually synchronized. + </p> + </dd> + + <dt><code>Ethernet address followed by one or more IPv4 addresses</code></dt> + <dd> + Example: <code>80:fa:5b:06:72:b7 158.36.44.22 + 158.36.44.24</code>. This would result in generation of + gratuitous ARPs for IP addresses 158.36.44.22 and 158.36.44.24 + with a MAC address of 80:fa:5b:06:72:b7. + </dd> + </dl> </column> </group> diff --git a/tests/ovn.at b/tests/ovn.at index 6eed020..febe00a 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -5219,6 +5219,66 @@ OVN_CLEANUP([hv1]) AT_CLEANUP +AT_SETUP([ovn -- send gratuitous arp with nat-addresses router in localnet]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start +# Create logical switch +ovn-nbctl ls-add ls0 +# Create gateway router +ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1 +# Add router port to gateway router +ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24 +ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \ + type=router options:router-port=lrp0-rp addresses='"f0:00:00:00:00:01"' +# Add nat-address option +ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" +# Add NAT rules +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.1 10.0.0.0/24]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 192.168.0.2 10.0.0.1]) +# Add load balancers +AT_CHECK([ovn-nbctl lb-add lb0 192.168.0.3:80 10.0.0.2:80,10.0.0.3:80]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0]) +AT_CHECK([ovn-nbctl lb-add lb1 192.168.0.3:8080 10.0.0.2:8080,10.0.0.3:8080]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1]) + +net_add n1 +sim_add hv1 +as hv1 +ovs-vsctl \ + -- add-br br-phys \ + -- add-br br-eth0 + +ovn_attach n1 br-phys 192.168.0.1 + +AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0]) +AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap]) + +# Create a localnet port. +AT_CHECK([ovn-nbctl lsp-add ls0 ln_port]) +AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) +AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) +AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) + + +# Wait for packet to be received. +OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 50]) +trim_zeros() { + sed 's/\(00\)\{1,\}$//' +} +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets +expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001" +echo $expected > expout +expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002" +echo $expected >> expout +expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80003000000000000c0a80003" +echo $expected >> expout +AT_CHECK([sort packets], [0], [expout]) +cat packets + +OVN_CLEANUP([hv1]) + +AT_CLEANUP + AT_SETUP([ovn -- delete mac bindings]) ovn_start net_add n1 -- 1.9.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev