[ovs-dev] [PATCH v12 6/6] ovn: specify options:nat-addresses as "router"

Guru Shetty guru at ovn.org
Fri Jan 27 18:20:49 UTC 2017


On 26 January 2017 at 01:20, Mickey Spiegel <mickeys.dev at gmail.com> wrote:

> 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.dev at 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 e24ff8f..efb8a74 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. */
>
A comment that the called has to free the returned string is useful.

Also, the load balancer IP address can also be the IP address of the router
itself. For e.g: when it is ROUTER_IP:port. I guess, that is not a problem
when a GARP is sent for the router IP too.

Acked-by: Gurucharan Shetty <guru at ovn.org>




> +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 2af46b6..57fbc98 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="fffffffffffff0000000000108060001080006040001f00000000001c0a8
> 0001000000000000c0a80001"
> +echo $expected > expout
> +expected="fffffffffffff0000000000108060001080006040001f00000000001c0a8
> 0002000000000000c0a80002"
> +echo $expected >> expout
> +expected="fffffffffffff0000000000108060001080006040001f00000000001c0a8
> 0003000000000000c0a80003"
> +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
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>


More information about the dev mailing list