@@ -1436,6 +1436,88 @@ 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.
+ *
+ * The caller must free the returned string with free(). */
+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 +1606,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 =
@@ -3898,29 +3988,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) {
@@ -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>
@@ -5285,6 +5285,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