2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-01 23:05:29 +00:00

ovn: Gratuitous ARP for centralized NAT rules on a distributed router

This patch extends gratuitous ARP support for NAT addresses so that it
applies to centralized NAT rules on a distributed router, in addition to
the existing gratuitous ARP support for NAT addresses on gateway routers.
Centralized NAT rules have type other than "dnat_and_snat", or have type
"dnat_and_snat" but do not specify external_mac or logical_port.  These
NAT rules apply on the redirect-chassis.

Gratuitous ARP packets for centralized NAT rules on a distributed router
are only generated on the redirect-chassis.  This is achieved by extending
the syntax for "options:nat-addresses" in the southbound database,
allowing the condition 'is_chassis_resident("LPORT_NAME")' to be appended
after the MAC and IP addresses.  This condition is automatically inserted
by ovn-northd when the northbound "options:nat-addresses" is set to
"router" and the peer is a distributed gateway port.

A separate patch will be required to support gratuitous ARP for
distributed NAT rules that specify logical_port and external_mac.  Since
the MAC address differs and the logical port often resides on a different
chassis from the redirect-chassis, these addresses cannot be included in
the same "nat-addresses" string as for centralized NAT rules.

Signed-off-by: Mickey Spiegel <mickeys.dev@gmail.com>
Signed-off-by: Gurucharan Shetty <guru@ovn.org>
This commit is contained in:
Mickey Spiegel
2017-03-30 11:42:21 -07:00
committed by Gurucharan Shetty
parent b6dc6b9349
commit 26b9e08d69
7 changed files with 310 additions and 42 deletions

View File

@@ -37,6 +37,7 @@
#include "lib/dhcp.h" #include "lib/dhcp.h"
#include "ovn-controller.h" #include "ovn-controller.h"
#include "ovn/actions.h" #include "ovn/actions.h"
#include "ovn/lex.h"
#include "ovn/lib/logical-fields.h" #include "ovn/lib/logical-fields.h"
#include "ovn/lib/ovn-dhcp.h" #include "ovn/lib/ovn-dhcp.h"
#include "ovn/lib/ovn-util.h" #include "ovn/lib/ovn-util.h"
@@ -1048,8 +1049,12 @@ send_garp_update(const struct sbrec_port_binding *binding_rec,
ld->localnet_port->logical_port)); ld->localnet_port->logical_port));
volatile struct garp_data *garp = NULL; volatile struct garp_data *garp = NULL;
/* Update GARP for NAT IP if it exists. */ /* Update GARP for NAT IP if it exists. Consider port bindings with type
if (!strcmp(binding_rec->type, "l3gateway")) { * "l3gateway" for logical switch ports attached to gateway routers, and
* port bindings with type "patch" for logical switch ports attached to
* distributed gateway ports. */
if (!strcmp(binding_rec->type, "l3gateway")
|| !strcmp(binding_rec->type, "patch")) {
struct lport_addresses *laddrs = NULL; struct lport_addresses *laddrs = NULL;
laddrs = shash_find_data(nat_addresses, binding_rec->logical_port); laddrs = shash_find_data(nat_addresses, binding_rec->logical_port);
if (!laddrs) { if (!laddrs) {
@@ -1173,6 +1178,7 @@ get_localnet_vifs_l3gwports(const struct ovsrec_bridge *br_int,
if (!iface_rec->n_ofport) { if (!iface_rec->n_ofport) {
continue; continue;
} }
/* Get localnet port with its ofport. */
if (localnet) { if (localnet) {
int64_t ofport = iface_rec->ofport[0]; int64_t ofport = iface_rec->ofport[0];
if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) { if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) {
@@ -1181,6 +1187,7 @@ get_localnet_vifs_l3gwports(const struct ovsrec_bridge *br_int,
simap_put(localnet_ofports, localnet, ofport); simap_put(localnet_ofports, localnet, ofport);
continue; continue;
} }
/* Get localnet vif. */
const char *iface_id = smap_get(&iface_rec->external_ids, const char *iface_id = smap_get(&iface_rec->external_ids,
"iface-id"); "iface-id");
if (!iface_id) { if (!iface_id) {
@@ -1202,24 +1209,105 @@ get_localnet_vifs_l3gwports(const struct ovsrec_bridge *br_int,
const struct local_datapath *ld; const struct local_datapath *ld;
HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
if (!ld->has_local_l3gateway) { if (!ld->localnet_port) {
continue; continue;
} }
/* Get l3gw ports. Consider port bindings with type "l3gateway"
* that connect to gateway routers (if local), and consider port
* bindings of type "patch" since they might connect to
* distributed gateway ports with NAT addresses. */
for (size_t i = 0; i < ld->ldatapath->n_lports; i++) { for (size_t i = 0; i < ld->ldatapath->n_lports; i++) {
const struct sbrec_port_binding *pb = ld->ldatapath->lports[i]; const struct sbrec_port_binding *pb = ld->ldatapath->lports[i];
if (!strcmp(pb->type, "l3gateway") if ((ld->has_local_l3gateway && !strcmp(pb->type, "l3gateway"))
/* && it's on this chassis */) { || !strcmp(pb->type, "patch")) {
sset_add(local_l3gw_ports, pb->logical_port); sset_add(local_l3gw_ports, pb->logical_port);
} }
} }
} }
} }
static bool
pinctrl_is_chassis_resident(const struct lport_index *lports,
const struct sbrec_chassis *chassis,
const char *port_name)
{
const struct sbrec_port_binding *pb
= lport_lookup_by_name(lports, port_name);
return pb && pb->chassis && pb->chassis == chassis;
}
/* Extracts the mac, IPv4 and IPv6 addresses, and logical port from
* 'addresses' which should be of the format 'MAC [IP1 IP2 ..]
* [is_chassis_resident("LPORT_NAME")]', where IPn should be a valid IPv4
* or IPv6 address, and stores them in the 'ipv4_addrs' and 'ipv6_addrs'
* fields of 'laddrs'. The logical port name is stored in 'lport'.
*
* Returns true if at least 'MAC' is found in 'address', false otherwise.
*
* The caller must call destroy_lport_addresses() and free(*lport). */
static bool
extract_addresses_with_port(const char *addresses,
struct lport_addresses *laddrs,
char **lport)
{
int ofs;
if (!extract_addresses(addresses, laddrs, &ofs)) {
return false;
} else if (ofs >= strlen(addresses)) {
return true;
}
struct lexer lexer;
lexer_init(&lexer, addresses + ofs);
lexer_get(&lexer);
if (lexer.error || lexer.token.type != LEX_T_ID
|| !lexer_match_id(&lexer, "is_chassis_resident")) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", addresses);
lexer_destroy(&lexer);
return true;
}
if (!lexer_match(&lexer, LEX_T_LPAREN)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_INFO_RL(&rl, "Syntax error: expecting '(' after "
"'is_chassis_resident' in address '%s'", addresses);
lexer_destroy(&lexer);
return false;
}
if (lexer.token.type != LEX_T_STRING) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_INFO_RL(&rl,
"Syntax error: expecting quoted string after"
" 'is_chassis_resident' in address '%s'", addresses);
lexer_destroy(&lexer);
return false;
}
*lport = xstrdup(lexer.token.s);
lexer_get(&lexer);
if (!lexer_match(&lexer, LEX_T_RPAREN)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_INFO_RL(&rl, "Syntax error: expecting ')' after quoted string in "
"'is_chassis_resident()' in address '%s'",
addresses);
lexer_destroy(&lexer);
return false;
}
lexer_destroy(&lexer);
return true;
}
static void static void
get_nat_addresses_and_keys(struct sset *nat_address_keys, get_nat_addresses_and_keys(struct sset *nat_address_keys,
struct sset *local_l3gw_ports, struct sset *local_l3gw_ports,
const struct lport_index *lports, const struct lport_index *lports,
const struct sbrec_chassis *chassis,
struct shash *nat_addresses) struct shash *nat_addresses)
{ {
const char *gw_port; const char *gw_port;
@@ -1236,10 +1324,23 @@ get_nat_addresses_and_keys(struct sset *nat_address_keys,
} }
struct lport_addresses *laddrs = xmalloc(sizeof *laddrs); struct lport_addresses *laddrs = xmalloc(sizeof *laddrs);
if (!extract_lsp_addresses(nat_addresses_options, laddrs)) { char *lport = NULL;
if (!extract_addresses_with_port(nat_addresses_options, laddrs, &lport)
|| (!lport && !strcmp(pb->type, "patch"))) {
free(laddrs); free(laddrs);
if (lport) {
free(lport);
}
continue; continue;
} else if (lport) {
if (!pinctrl_is_chassis_resident(lports, chassis, lport)) {
free(laddrs);
free(lport);
continue;
}
free(lport);
} }
int i; int i;
for (i = 0; i < laddrs->n_ipv4_addrs; i++) { for (i = 0; i < laddrs->n_ipv4_addrs; i++) {
char *name = xasprintf("%s-%s", pb->logical_port, char *name = xasprintf("%s-%s", pb->logical_port,
@@ -1275,7 +1376,7 @@ send_garp_run(const struct ovsrec_bridge *br_int,
&localnet_vifs, &localnet_ofports, &local_l3gw_ports); &localnet_vifs, &localnet_ofports, &local_l3gw_ports);
get_nat_addresses_and_keys(&nat_ip_keys, &local_l3gw_ports, lports, get_nat_addresses_and_keys(&nat_ip_keys, &local_l3gw_ports, lports,
&nat_addresses); chassis, &nat_addresses);
/* For deleted ports and deleted nat ips, remove from send_garp_data. */ /* For deleted ports and deleted nat ips, remove from send_garp_data. */
struct shash_node *iter, *next; struct shash_node *iter, *next;
SHASH_FOR_EACH_SAFE (iter, next, &send_garp_data) { SHASH_FOR_EACH_SAFE (iter, next, &send_garp_data) {

View File

@@ -83,24 +83,29 @@ is_dynamic_lsp_address(const char *address)
} }
/* Extracts the mac, IPv4 and IPv6 addresses from * 'address' which /* Extracts the mac, IPv4 and IPv6 addresses from * 'address' which
* should be of the format 'MAC [IP1 IP2 ..]" where IPn should be a * should be of the format "MAC [IP1 IP2 ..] .." where IPn should be a
* valid IPv4 or IPv6 address and stores them in the 'ipv4_addrs' and * valid IPv4 or IPv6 address and stores them in the 'ipv4_addrs' and
* 'ipv6_addrs' fields of 'laddrs'. * 'ipv6_addrs' fields of 'laddrs'. There may be additional content in
* 'address' after "MAC [IP1 IP2 .. ]". The value of 'ofs' that is
* returned indicates the offset where that additional content begins.
* *
* Return true if at least 'MAC' is found in 'address', false otherwise. * Returns true if at least 'MAC' is found in 'address', false otherwise.
* *
* The caller must call destroy_lport_addresses(). */ * The caller must call destroy_lport_addresses(). */
bool bool
extract_lsp_addresses(const char *address, struct lport_addresses *laddrs) extract_addresses(const char *address, struct lport_addresses *laddrs,
int *ofs)
{ {
memset(laddrs, 0, sizeof *laddrs); memset(laddrs, 0, sizeof *laddrs);
const char *buf = address; const char *buf = address;
const char *const start = buf;
int buf_index = 0; int buf_index = 0;
const char *buf_end = buf + strlen(address); const char *buf_end = buf + strlen(address);
if (!ovs_scan_len(buf, &buf_index, ETH_ADDR_SCAN_FMT, if (!ovs_scan_len(buf, &buf_index, ETH_ADDR_SCAN_FMT,
ETH_ADDR_SCAN_ARGS(laddrs->ea))) { ETH_ADDR_SCAN_ARGS(laddrs->ea))) {
laddrs->ea = eth_addr_zero; laddrs->ea = eth_addr_zero;
*ofs = 0;
return false; return false;
} }
@@ -129,17 +134,38 @@ extract_lsp_addresses(const char *address, struct lport_addresses *laddrs)
if (!error) { if (!error) {
add_ipv6_netaddr(laddrs, ip6, plen); add_ipv6_netaddr(laddrs, ip6, plen);
} else { } else {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", address);
free(error); free(error);
break; break;
} }
buf += buf_index; buf += buf_index;
} }
*ofs = buf - start;
return true; return true;
} }
/* Extracts the mac, IPv4 and IPv6 addresses from * 'address' which
* should be of the format 'MAC [IP1 IP2 ..]" where IPn should be a
* valid IPv4 or IPv6 address and stores them in the 'ipv4_addrs' and
* 'ipv6_addrs' fields of 'laddrs'.
*
* Return true if at least 'MAC' is found in 'address', false otherwise.
*
* The caller must call destroy_lport_addresses(). */
bool
extract_lsp_addresses(const char *address, struct lport_addresses *laddrs)
{
int ofs;
bool success = extract_addresses(address, laddrs, &ofs);
if (success && ofs < strlen(address)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", address);
}
return success;
}
/* Extracts the mac, IPv4 and IPv6 addresses from the /* Extracts the mac, IPv4 and IPv6 addresses from the
* "nbrec_logical_router_port" parameter 'lrp'. Stores the IPv4 and * "nbrec_logical_router_port" parameter 'lrp'. Stores the IPv4 and
* IPv6 addresses in the 'ipv4_addrs' and 'ipv6_addrs' fields of * IPv6 addresses in the 'ipv4_addrs' and 'ipv6_addrs' fields of

View File

@@ -54,6 +54,8 @@ struct lport_addresses {
}; };
bool is_dynamic_lsp_address(const char *address); bool is_dynamic_lsp_address(const char *address);
bool extract_addresses(const char *address, struct lport_addresses *,
int *ofs);
bool extract_lsp_addresses(const char *address, struct lport_addresses *); bool extract_lsp_addresses(const char *address, struct lport_addresses *);
bool extract_lrp_networks(const struct nbrec_logical_router_port *, bool extract_lrp_networks(const struct nbrec_logical_router_port *,
struct lport_addresses *); struct lport_addresses *);

View File

@@ -1384,6 +1384,15 @@ join_logical_ports(struct northd_context *ctx,
"on L3 gateway router", nbrp->name); "on L3 gateway router", nbrp->name);
continue; continue;
} }
if (od->l3dgw_port || od->l3redirect_port) {
static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_WARN_RL(&rl, "Bad configuration: multiple ports "
"with redirect-chassis on same logical "
"router %s", od->nbr->name);
continue;
}
char *redirect_name = chassis_redirect_name(nbrp->name); char *redirect_name = chassis_redirect_name(nbrp->name);
struct ovn_port *crp = ovn_port_find(ports, redirect_name); struct ovn_port *crp = ovn_port_find(ports, redirect_name);
if (crp) { if (crp) {
@@ -1402,17 +1411,8 @@ join_logical_ports(struct northd_context *ctx,
/* Set l3dgw_port and l3redirect_port in od, for later /* Set l3dgw_port and l3redirect_port in od, for later
* use during flow creation. */ * use during flow creation. */
if (od->l3dgw_port || od->l3redirect_port) { od->l3dgw_port = op;
static struct vlog_rate_limit rl od->l3redirect_port = crp;
= VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_WARN_RL(&rl, "Bad configuration: multiple ports "
"with redirect-chassis on same logical "
"router %s", od->nbr->name);
continue;
} else {
od->l3dgw_port = op;
od->l3redirect_port = crp;
}
} }
} }
} }
@@ -1536,7 +1536,21 @@ get_nat_addresses(const struct ovn_port *op)
free(error); free(error);
continue; continue;
} }
ds_put_format(&addresses, " %s", nat->external_ip);
/* Determine whether this NAT rule satisfies the conditions for
* distributed NAT processing. */
if (op->od->l3redirect_port && !strcmp(nat->type, "dnat_and_snat")
&& nat->logical_port && nat->external_mac) {
/* Distributed NAT rule. */
/* XXX This uses a different MAC address, so it cannot go
* into the same string as centralized NAT external IP
* addresses. Need to change this function to return an
* array of strings. */
} else {
/* Centralized NAT rule, either on gateway router or distributed
* router. */
ds_put_format(&addresses, " %s", nat->external_ip);
}
} }
/* A set to hold all load-balancer vips. */ /* A set to hold all load-balancer vips. */
@@ -1549,6 +1563,13 @@ get_nat_addresses(const struct ovn_port *op)
} }
sset_destroy(&all_ips); sset_destroy(&all_ips);
/* Gratuitous ARP for centralized NAT rules on distributed gateway
* ports should be restricted to the "redirect-chassis". */
if (op->od->l3redirect_port) {
ds_put_format(&addresses, " is_chassis_resident(%s)",
op->od->l3redirect_port->json_key);
}
return ds_steal_cstr(&addresses); return ds_steal_cstr(&addresses);
} }
@@ -1642,14 +1663,17 @@ ovn_port_update_sbrec(const struct ovn_port *op,
const char *nat_addresses = smap_get(&op->nbsp->options, const char *nat_addresses = smap_get(&op->nbsp->options,
"nat-addresses"); "nat-addresses");
if (nat_addresses && !strcmp(nat_addresses, "router")) { if (nat_addresses && !strcmp(nat_addresses, "router")) {
if (op->peer && op->peer->nbrp) { if (op->peer && op->peer->od
&& (chassis || op->peer->od->l3redirect_port)) {
char *nats = get_nat_addresses(op->peer); char *nats = get_nat_addresses(op->peer);
if (nats) { if (nats) {
smap_add(&new, "nat-addresses", nats); smap_add(&new, "nat-addresses", nats);
free(nats); free(nats);
} }
} }
} else if (nat_addresses) { /* Only accept manual specification of ethernet address
* followed by IPv4 addresses on type "l3gateway" ports. */
} else if (nat_addresses && chassis) {
struct lport_addresses laddrs; struct lport_addresses laddrs;
if (!extract_lsp_addresses(nat_addresses, &laddrs)) { if (!extract_lsp_addresses(nat_addresses, &laddrs)) {
static struct vlog_rate_limit rl = static struct vlog_rate_limit rl =

View File

@@ -244,8 +244,11 @@
<column name="options" key="nat-addresses"> <column name="options" key="nat-addresses">
<p> <p>
This is used to send gratuitous ARPs for SNAT and DNAT IP This is used to send gratuitous ARPs for SNAT and DNAT IP
addresses via <code>localnet</code> and is valid for only L3 addresses via the <code>localnet</code> port that is attached
gateway ports. to the same logical switch as this type <code>router</code>
port. This option is specified on a logical switch port that is
connected to a gateway router, or a logical switch port that is
connected to a distributed gateway port on a logical router.
</p> </p>
<p> <p>
@@ -263,6 +266,13 @@
address. address.
</p> </p>
<p>
This form of <ref column="options" key="nat-addresses"/> is
valid for logical switch ports where <ref column="options"
key="router-port"/> is the name of a port on a gateway router,
or the name of a distributed gateway port.
</p>
<p> <p>
Supported only in OVN 2.8 and later. Earlier versions required Supported only in OVN 2.8 and later. Earlier versions required
NAT addresses to be manually synchronized. NAT addresses to be manually synchronized.
@@ -271,10 +281,18 @@
<dt><code>Ethernet address followed by one or more IPv4 addresses</code></dt> <dt><code>Ethernet address followed by one or more IPv4 addresses</code></dt>
<dd> <dd>
Example: <code>80:fa:5b:06:72:b7 158.36.44.22 <p>
158.36.44.24</code>. This would result in generation of Example: <code>80:fa:5b:06:72:b7 158.36.44.22
gratuitous ARPs for IP addresses 158.36.44.22 and 158.36.44.24 158.36.44.24</code>. This would result in generation of
with a MAC address of 80:fa:5b:06:72:b7. 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>
<p>
This form of <ref column="options" key="nat-addresses"/> is
only valid for logical switch ports where <ref column="options"
key="router-port"/> is the name of a port on a gateway router.
</p>
</dd> </dd>
</dl> </dl>
</column> </column>
@@ -1166,6 +1184,14 @@
peer logical switch's destination lookup flow on the peer logical switch's destination lookup flow on the
<code>redirect-chassis</code>. <code>redirect-chassis</code>.
</p> </p>
<p>
When this option is specified and it is desired to generate
gratuitous ARPs for NAT addresses, then the peer logical switch
port's <ref column="options" key="nat-addresses"
table="Logical_Switch_Port"/> should be set to
<code>router</code>.
</p>
</column> </column>
</group> </group>

View File

@@ -1862,6 +1862,21 @@ tcp.flags = RST;
ports must have reversed <ref column="logical_port"/> and ports must have reversed <ref column="logical_port"/> and
<code>peer</code> values. <code>peer</code> values.
</column> </column>
<column name="options" key="nat-addresses">
MAC address of the <code>patch</code> port followed by a list of
SNAT and DNAT external IP addresses, followed by
<code>is_chassis_resident("<var>lport</var>")</code>, where
<var>lport</var> is the name of a logical port on the same chassis
where the corresponding NAT rules are applied. This is used to
send gratuitous ARPs for SNAT and DNAT external IP addresses via
<code>localnet</code>, from the chassis where <var>lport</var>
resides. Example: <code>80:fa:5b:06:72:b7 158.36.44.22
158.36.44.24 is_chassis_resident("foo1")</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 from the chassis
where the logical port "foo1" resides.
</column>
</group> </group>
<group title="L3 Gateway Options"> <group title="L3 Gateway Options">
@@ -1883,15 +1898,14 @@ tcp.flags = RST;
The <code>chassis</code> in which the port resides. The <code>chassis</code> in which the port resides.
</column> </column>
<column name="options" key="nat-addresses"> <column name="options" key="nat-addresses">
MAC address of the <code>l3gateway</code> port followed by a list of MAC address of the <code>l3gateway</code> port followed by a list of
SNAT and DNAT IP addresses. This is used to send gratuitous ARPs for SNAT and DNAT external IP addresses. This is used to send gratuitous
SNAT and DNAT IP addresses via <code>localnet</code> and is valid for ARPs for SNAT and DNAT external IP addresses via <code>localnet</code>.
only L3 gateway ports. Example: <code>80:fa:5b:06:72:b7 158.36.44.22 Example: <code>80:fa:5b:06:72:b7 158.36.44.22 158.36.44.24</code>.
158.36.44.24</code>. This would result in generation of gratuitous This would result in generation of gratuitous ARPs for IP addresses
ARPs for IP addresses 158.36.44.22 and 158.36.44.24 with a MAC 158.36.44.22 and 158.36.44.24 with a MAC address of 80:fa:5b:06:72:b7.
address of 80:fa:5b:06:72:b7. </column>
</column>
</group> </group>
<group title="Localnet Options"> <group title="Localnet Options">

View File

@@ -6660,3 +6660,78 @@ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [hv2-vif1.expected])
OVN_CLEANUP([hv1],[hv2],[hv3]) OVN_CLEANUP([hv1],[hv2],[hv3])
AT_CLEANUP AT_CLEANUP
AT_SETUP([ovn -- send gratuitous arp for NAT rules on distributed router])
AT_SKIP_IF([test $HAVE_PYTHON = no])
ovn_start
# Create logical switches
ovn-nbctl ls-add ls0
ovn-nbctl ls-add ls1
# Create distributed router
ovn-nbctl create Logical_Router name=lr0
# Add distributed gateway port to distributed router
ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24 \
-- set Logical_Router_Port lrp0 options:redirect-chassis="hv2"
ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \
type=router options:router-port=lrp0 addresses="router"
# Add router port to ls1
ovn-nbctl lrp-add lr0 lrp1 f0:00:00:00:00:02 10.0.0.1/24
ovn-nbctl lsp-add ls1 lrp1-rp -- set Logical_Switch_Port lrp1-rp \
type=router options:router-port=lrp1 addresses="router"
# Add logical port for NAT rule
ovn-nbctl lsp-add ls1 foo1
# Add nat-addresses 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.2])
AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 192.168.0.3 10.0.0.3 foo1 f0:00:00:00:00:03])
net_add n1
sim_add hv1
as hv1
ovs-vsctl add-br br-phys
ovn_attach n1 br-phys 192.168.0.1
AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
AT_CHECK([ovs-vsctl add-port br-phys snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap])
sim_add hv2
as hv2
ovs-vsctl add-br br-phys
ovn_attach n1 br-phys 192.168.0.2
# Initially test with no bridge-mapping on hv2, expect to receive no packets
# 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])
# Allow some time for ovn-northd and ovn-controller to catch up.
# XXX This should be more systematic.
sleep 2
# Expect no packets when hv2 bridge-mapping is not present
: > packets
OVN_CHECK_PACKETS([hv1/snoopvif-tx.pcap], [packets])
# Add bridge-mapping on hv2
AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
# Wait for packets to be received.
OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
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
AT_CHECK([sort packets], [0], [expout])
cat packets
OVN_CLEANUP([hv1],[hv2])
AT_CLEANUP