@@ -767,7 +767,7 @@ ip_address_and_port_from_lb_key(const char *key, char **ip_address,
/* Increment this for any logical flow changes, if an existing OVN action is
* modified or a stage is added to a logical pipeline. */
-#define OVN_INTERNAL_MINOR_VER 2
+#define OVN_INTERNAL_MINOR_VER 3
/* Returns the OVN version. The caller must free the returned value. */
char *
@@ -148,15 +148,16 @@ enum ovn_stage {
PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 7, "lr_in_ecmp_stateful") \
PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 8, "lr_in_nd_ra_options") \
PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 9, "lr_in_nd_ra_response") \
- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 10, "lr_in_ip_routing") \
- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 11, "lr_in_ip_routing_ecmp") \
- PIPELINE_STAGE(ROUTER, IN, POLICY, 12, "lr_in_policy") \
- PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 13, "lr_in_policy_ecmp") \
- PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 14, "lr_in_arp_resolve") \
- PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 15, "lr_in_chk_pkt_len") \
- PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 16, "lr_in_larger_pkts") \
- PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 17, "lr_in_gw_redirect") \
- PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 18, "lr_in_arp_request") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 10, "lr_in_ip_routing_pre") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 11, "lr_in_ip_routing") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 12, "lr_in_ip_routing_ecmp") \
+ PIPELINE_STAGE(ROUTER, IN, POLICY, 13, "lr_in_policy") \
+ PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 14, "lr_in_policy_ecmp") \
+ PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 15, "lr_in_arp_resolve") \
+ PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 16, "lr_in_chk_pkt_len") \
+ PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 17, "lr_in_larger_pkts") \
+ PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 18, "lr_in_gw_redirect") \
+ PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 19, "lr_in_arp_request") \
\
/* Logical router egress stages. */ \
PIPELINE_STAGE(ROUTER, OUT, UNDNAT, 0, "lr_out_undnat") \
@@ -225,6 +226,7 @@ enum ovn_stage {
#define REG_NEXT_HOP_IPV6 "xxreg0"
#define REG_SRC_IPV4 "reg1"
#define REG_SRC_IPV6 "xxreg1"
+#define REG_ROUTE_TABLE_ID "reg7"
#define REG_ORIG_TP_DPORT_ROUTER "reg9[16..31]"
@@ -287,8 +289,9 @@ enum ovn_stage {
* | R6 | UNUSED | X | | G | IN_IP_ROUTING)|
* | | | R | | 1 | |
* +-----+--------------------------+ E | UNUSED | | |
- * | R7 | UNUSED | G | | | |
- * | | | 3 | | | |
+ * | R7 | ROUTE_TABLE_ID | G | | | |
+ * | | (>= IN_IP_ROUTING_PRE && | 3 | | | |
+ * | | <= IN_IP_ROUTING) | | | | |
* +-----+--------------------------+---+-----------------+---+---------------+
* | R8 | ECMP_GROUP_ID | | |
* | | ECMP_MEMBER_ID | X | |
@@ -8691,11 +8694,72 @@ cleanup:
ds_destroy(&actions);
}
+static uint32_t
+route_table_add(struct simap *route_tables, const char *route_table_name)
+{
+ /* route table ids start from 1 */
+ uint32_t rtb_id = simap_count(route_tables) + 1;
+
+ if (rtb_id == UINT16_MAX) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "too many route tables for Logical Router.");
+ return 0;
+ }
+
+ if (!simap_put(route_tables, route_table_name, rtb_id)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "Route table id unexpectedly appeared");
+ }
+
+ return rtb_id;
+}
+
+static uint32_t
+get_route_table_id(struct simap *route_tables, const char *route_table_name)
+{
+ if (!route_table_name || !strlen(route_table_name)) {
+ return 0;
+ }
+
+ uint32_t rtb_id = simap_get(route_tables, route_table_name);
+ if (!rtb_id) {
+ rtb_id = route_table_add(route_tables, route_table_name);
+ }
+
+ return rtb_id;
+}
+
+static void
+build_route_table_lflow(struct ovn_datapath *od, struct hmap *lflows,
+ struct nbrec_logical_router_port *lrp,
+ struct simap *route_tables)
+{
+ struct ds match = DS_EMPTY_INITIALIZER;
+ struct ds actions = DS_EMPTY_INITIALIZER;
+
+ const char *route_table_name = smap_get(&lrp->options, "route_table");
+ uint32_t rtb_id = get_route_table_id(route_tables, route_table_name);
+ if (!rtb_id) {
+ return;
+ }
+
+ ds_put_format(&match, "inport == \"%s\"", lrp->name);
+ ds_put_format(&actions, "%s = %d; next;",
+ REG_ROUTE_TABLE_ID, rtb_id);
+
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING_PRE, 100,
+ ds_cstr(&match), ds_cstr(&actions));
+
+ ds_destroy(&match);
+ ds_destroy(&actions);
+}
+
struct parsed_route {
struct ovs_list list_node;
struct in6_addr prefix;
unsigned int plen;
bool is_src_route;
+ uint32_t route_table_id;
uint32_t hash;
const struct nbrec_logical_router_static_route *route;
bool ecmp_symmetric_reply;
@@ -8720,7 +8784,7 @@ find_static_route_outport(struct ovn_datapath *od, const struct hmap *ports,
* Otherwise return NULL. */
static struct parsed_route *
parsed_routes_add(struct ovn_datapath *od, const struct hmap *ports,
- struct ovs_list *routes,
+ struct ovs_list *routes, struct simap *route_tables,
const struct nbrec_logical_router_static_route *route,
const struct hmap *bfd_connections)
{
@@ -8802,6 +8866,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *ports,
struct parsed_route *pr = xzalloc(sizeof *pr);
pr->prefix = prefix;
pr->plen = plen;
+ pr->route_table_id = get_route_table_id(route_tables, route->route_table);
pr->is_src_route = (route->policy && !strcmp(route->policy,
"src-ip"));
pr->hash = route_hash(pr);
@@ -8836,6 +8901,7 @@ struct ecmp_groups_node {
unsigned int plen;
bool is_src_route;
const char *origin;
+ uint32_t route_table_id;
uint16_t route_count;
struct ovs_list route_list; /* Contains ecmp_route_list_node */
};
@@ -8844,7 +8910,7 @@ static void
ecmp_groups_add_route(struct ecmp_groups_node *group,
const struct parsed_route *route)
{
- if (group->route_count == UINT16_MAX) {
+ if (group->route_count == UINT16_MAX) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
VLOG_WARN_RL(&rl, "too many routes in a single ecmp group.");
return;
@@ -8874,6 +8940,7 @@ ecmp_groups_add(struct hmap *ecmp_groups,
eg->plen = route->plen;
eg->is_src_route = route->is_src_route;
eg->origin = smap_get_def(&route->route->options, "origin", "");
+ eg->route_table_id = route->route_table_id;
ovs_list_init(&eg->route_list);
ecmp_groups_add_route(eg, route);
@@ -8887,7 +8954,8 @@ ecmp_groups_find(struct hmap *ecmp_groups, struct parsed_route *route)
HMAP_FOR_EACH_WITH_HASH (eg, hmap_node, route->hash, ecmp_groups) {
if (ipv6_addr_equals(&eg->prefix, &route->prefix) &&
eg->plen == route->plen &&
- eg->is_src_route == route->is_src_route) {
+ eg->is_src_route == route->is_src_route &&
+ eg->route_table_id == route->route_table_id) {
return eg;
}
}
@@ -8934,7 +9002,8 @@ unique_routes_remove(struct hmap *unique_routes,
HMAP_FOR_EACH_WITH_HASH (ur, hmap_node, route->hash, unique_routes) {
if (ipv6_addr_equals(&route->prefix, &ur->route->prefix) &&
route->plen == ur->route->plen &&
- route->is_src_route == ur->route->is_src_route) {
+ route->is_src_route == ur->route->is_src_route &&
+ route->route_table_id == ur->route->route_table_id) {
hmap_remove(unique_routes, &ur->hmap_node);
const struct parsed_route *existed_route = ur->route;
free(ur);
@@ -8972,9 +9041,9 @@ build_route_prefix_s(const struct in6_addr *prefix, unsigned int plen)
}
static void
-build_route_match(const struct ovn_port *op_inport, const char *network_s,
- int plen, bool is_src_route, bool is_ipv4, struct ds *match,
- uint16_t *priority, int ofs)
+build_route_match(const struct ovn_port *op_inport, uint32_t rtb_id,
+ const char *network_s, int plen, bool is_src_route,
+ bool is_ipv4, struct ds *match, uint16_t *priority, int ofs)
{
const char *dir;
/* The priority here is calculated to implement longest-prefix-match
@@ -8991,6 +9060,9 @@ build_route_match(const struct ovn_port *op_inport, const char *network_s,
if (op_inport) {
ds_put_format(match, "inport == %s && ", op_inport->json_key);
}
+ if (rtb_id || ofs == ROUTE_PRIO_OFFSET_STATIC) {
+ ds_put_format(match, "%s == %d && ", REG_ROUTE_TABLE_ID, rtb_id);
+ }
ds_put_format(match, "ip%s.%s == %s/%d", is_ipv4 ? "4" : "6", dir,
network_s, plen);
}
@@ -9161,8 +9233,8 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
char *prefix_s = build_route_prefix_s(&eg->prefix, eg->plen);
int ofs = !strcmp(eg->origin, ROUTE_ORIGIN_CONNECTED) ?
ROUTE_PRIO_OFFSET_CONNECTED: ROUTE_PRIO_OFFSET_STATIC;
- build_route_match(NULL, prefix_s, eg->plen, eg->is_src_route, is_ipv4,
- &route_match, &priority, ofs);
+ build_route_match(NULL, eg->route_table_id, prefix_s, eg->plen,
+ eg->is_src_route, is_ipv4, &route_match, &priority, ofs);
free(prefix_s);
struct ds actions = DS_EMPTY_INITIALIZER;
@@ -9237,8 +9309,9 @@ static void
add_route(struct hmap *lflows, struct ovn_datapath *od,
const struct ovn_port *op, const char *lrp_addr_s,
const char *network_s, int plen, const char *gateway,
- bool is_src_route, const struct ovsdb_idl_row *stage_hint,
- bool is_discard_route, int ofs)
+ bool is_src_route, const uint32_t rtb_id,
+ const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
+ int ofs)
{
bool is_ipv4 = strchr(network_s, '.') ? true : false;
struct ds match = DS_EMPTY_INITIALIZER;
@@ -9253,8 +9326,8 @@ add_route(struct hmap *lflows, struct ovn_datapath *od,
op_inport = op;
}
}
- build_route_match(op_inport, network_s, plen, is_src_route, is_ipv4,
- &match, &priority, ofs);
+ build_route_match(op_inport, rtb_id, network_s, plen, is_src_route,
+ is_ipv4, &match, &priority, ofs);
struct ds common_actions = DS_EMPTY_INITIALIZER;
struct ds actions = DS_EMPTY_INITIALIZER;
@@ -9321,8 +9394,8 @@ build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
char *prefix_s = build_route_prefix_s(&route_->prefix, route_->plen);
add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
lrp_addr_s, prefix_s, route_->plen, route->nexthop,
- route_->is_src_route, &route->header_, route_->is_discard_route,
- ofs);
+ route_->is_src_route, route_->route_table_id, &route->header_,
+ route_->is_discard_route, ofs);
free(prefix_s);
}
@@ -10709,6 +10782,18 @@ build_ND_RA_flows_for_lrouter(struct ovn_datapath *od, struct hmap *lflows)
}
}
+/* Logical router ingress table IP_ROUTING_PRE:
+ * by default goto next. (priority 0). */
+static void
+build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
+ struct hmap *lflows)
+{
+ if (od->nbr) {
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING_PRE, 0, "1",
+ REG_ROUTE_TABLE_ID" = 0; next;");
+ }
+}
+
/* Logical router ingress table IP_ROUTING : IP Routing.
*
* A packet that arrives at this table is an IP packet that should be
@@ -10734,14 +10819,14 @@ build_ip_routing_flows_for_lrouter_port(
for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
op->lrp_networks.ipv4_addrs[i].network_s,
- op->lrp_networks.ipv4_addrs[i].plen, NULL, false,
+ op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
&op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED);
}
for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
op->lrp_networks.ipv6_addrs[i].network_s,
- op->lrp_networks.ipv6_addrs[i].plen, NULL, false,
+ op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
&op->nbrp->header_, false, ROUTE_PRIO_OFFSET_CONNECTED);
}
} else if (lsp_is_router(op->nbsp)) {
@@ -10764,7 +10849,7 @@ build_ip_routing_flows_for_lrouter_port(
add_route(lflows, peer->od, peer,
peer->lrp_networks.ipv4_addrs[0].addr_s,
laddrs->ipv4_addrs[k].network_s,
- laddrs->ipv4_addrs[k].plen, NULL, false,
+ laddrs->ipv4_addrs[k].plen, NULL, false, 0,
&peer->nbrp->header_, false,
ROUTE_PRIO_OFFSET_CONNECTED);
}
@@ -10785,10 +10870,17 @@ build_static_route_flows_for_lrouter(
struct hmap ecmp_groups = HMAP_INITIALIZER(&ecmp_groups);
struct hmap unique_routes = HMAP_INITIALIZER(&unique_routes);
struct ovs_list parsed_routes = OVS_LIST_INITIALIZER(&parsed_routes);
+ struct simap route_tables = SIMAP_INITIALIZER(&route_tables);
struct ecmp_groups_node *group;
+
+ for (int i = 0; i < od->nbr->n_ports; i++) {
+ build_route_table_lflow(od, lflows, od->nbr->ports[i],
+ &route_tables);
+ }
+
for (int i = 0; i < od->nbr->n_static_routes; i++) {
struct parsed_route *route =
- parsed_routes_add(od, ports, &parsed_routes,
+ parsed_routes_add(od, ports, &parsed_routes, &route_tables,
od->nbr->static_routes[i], bfd_connections);
if (!route) {
continue;
@@ -10821,6 +10913,7 @@ build_static_route_flows_for_lrouter(
ecmp_groups_destroy(&ecmp_groups);
unique_routes_destroy(&unique_routes);
parsed_routes_destroy(&parsed_routes);
+ simap_destroy(&route_tables);
}
}
@@ -13019,6 +13112,7 @@ build_lswitch_and_lrouter_iterate_by_od(struct ovn_datapath *od,
build_neigh_learning_flows_for_lrouter(od, lsi->lflows, &lsi->match,
&lsi->actions, lsi->meter_groups);
build_ND_RA_flows_for_lrouter(od, lsi->lflows);
+ build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows);
build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->ports,
lsi->bfd_connections);
build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
@@ -3291,7 +3291,37 @@ output;
</li>
</ul>
- <h3>Ingress Table 10: IP Routing</h3>
+ <h3>Ingress Table 10: IP Routing Pre</h3>
+
+ <p>
+ If a packet arrived at this table from Logical Router Port <var>P</var>
+ which has <code>options:route_table</code> value set, a logical flow with
+ match <code>inport == "<var>P</var>"</code> with priority 100 and action
+ setting unique-generated per-datapath 32-bit value (non-zero) in OVS
+ register 7. This register's value is checked in next table. If packet
+ didn't match any configured inport (<var><main></var> route table),
+ register 7 value is set to 0.
+ </p>
+
+ <p>
+ This table contains the following logical flows:
+ </p>
+
+ <ul>
+ <li>
+ <p>
+ Priority-100 flow with match <code>inport == "LRP_NAME"</code> value
+ and action, which set route table identifier in reg7.
+ </p>
+
+ <p>
+ A priority-0 logical flow with match <code>1</code> has actions
+ <code>reg7 = 0; next;</code>.
+ </p>
+ </li>
+ </ul>
+
+ <h3>Ingress Table 11: IP Routing</h3>
<p>
A packet that arrives at this table is an IP packet that should be
@@ -3331,14 +3361,14 @@ output;
<ul>
<li>
<p>
- Priority-10550 flow that drops IPv6 Router Solicitation/Advertisement
+ Priority-550 flow that drops IPv6 Router Solicitation/Advertisement
packets that were not processed in previous tables.
</p>
</li>
<li>
<p>
- Priority-10500 flows that match IP multicast traffic destined to
+ Priority-500 flows that match IP multicast traffic destined to
groups registered on any of the attached switches and sets
<code>outport</code> to the associated multicast group that will
eventually flood the traffic to all interested attached logical
@@ -3348,7 +3378,7 @@ output;
<li>
<p>
- Priority-10450 flow that matches unregistered IP multicast traffic
+ Priority-450 flow that matches unregistered IP multicast traffic
and sets <code>outport</code> to the <code>MC_STATIC</code>
multicast group, which <code>ovn-northd</code> populates with the
logical ports that have
@@ -3362,11 +3392,10 @@ output;
<p>
IPv4 routing table. For each route to IPv4 network <var>N</var> with
netmask <var>M</var>, on router port <var>P</var> with IP address
- <var>A</var> and Ethernet address <var>E</var>, a logical flow with
- match <code>ip4.dst == <var>N</var>/<var>M</var></code>, whose
- priority is the number of 1-bits in <var>M</var> multiplied by 3 +
- route offset: 0 for src-ip route policy, 1 for dst-ip static routes.
- This flow has the following actions:
+ <var>A</var> and Ethernet
+ address <var>E</var>, a logical flow with match <code>ip4.dst ==
+ <var>N</var>/<var>M</var></code>, whose priority is the number of
+ 1-bits in <var>M</var>, has the following actions:
</p>
<pre>
@@ -3399,9 +3428,8 @@ next;
<var>P</var> with IP address <var>A</var> and Ethernet address
<var>E</var>, a logical flow with match in CIDR notation
<code>ip6.dst == <var>N</var>/<var>M</var></code>,
- whose priority is the integer value of <var>M</var> multiplied by 3 +
- route offset: 0 for src-ip route policy, 1 for dst-ip static routes.
- This flow has the following actions:
+ whose priority is the integer value of <var>M</var>, has the
+ following actions:
</p>
<pre>
@@ -3430,6 +3458,20 @@ next;
If the address <var>A</var> is in the link-local scope, the
route will be limited to sending on the ingress port.
</p>
+
+ <p>
+ For each static route the <code>reg7 == id &&</code> is
+ prefixed in logical flow match portion. For routes with
+ <code>route_table</code> value set a unique non-zero id is used.
+ For routes within <code><main></code> route table (no route
+ table set), this id value is 0.
+ </p>
+ <p>
+ For each <var>connected</var> route (route to the LRP's subnet CIDR)
+ the logical flow match portion has no
+ <code>reg7 == id &&</code> prefix to have route to LRP's
+ subnets in all routing tables.
+ </p>
</li>
<li>
@@ -3456,7 +3498,7 @@ select(reg8[16..31], <var>MID1</var>, <var>MID2</var>, ...);
</li>
</ul>
- <h3>Ingress Table 11: IP_ROUTING_ECMP</h3>
+ <h3>Ingress Table 12: IP_ROUTING_ECMP</h3>
<p>
This table implements the second part of IP routing for ECMP routes
@@ -3508,7 +3550,7 @@ outport = <var>P</var>;
</li>
</ul>
- <h3>Ingress Table 12: Router policies</h3>
+ <h3>Ingress Table 13: Router policies</h3>
<p>
This table adds flows for the logical router policies configured
on the logical router. Please see the
@@ -3580,7 +3622,7 @@ next;
</li>
</ul>
- <h3>Ingress Table 13: ECMP handling for router policies</h3>
+ <h3>Ingress Table 14: ECMP handling for router policies</h3>
<p>
This table handles the ECMP for the router policies configured
with multiple nexthops.
@@ -3624,7 +3666,7 @@ outport = <var>P</var>
</li>
</ul>
- <h3>Ingress Table 14: ARP/ND Resolution</h3>
+ <h3>Ingress Table 15: ARP/ND Resolution</h3>
<p>
Any packet that reaches this table is an IP packet whose next-hop
@@ -3815,7 +3857,7 @@ outport = <var>P</var>
</ul>
- <h3>Ingress Table 15: Check packet length</h3>
+ <h3>Ingress Table 16: Check packet length</h3>
<p>
For distributed logical routers or gateway routers with gateway
@@ -3845,7 +3887,7 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
and advances to the next table.
</p>
- <h3>Ingress Table 16: Handle larger packets</h3>
+ <h3>Ingress Table 17: Handle larger packets</h3>
<p>
For distributed logical routers or gateway routers with gateway port
@@ -3908,7 +3950,7 @@ icmp6 {
and advances to the next table.
</p>
- <h3>Ingress Table 17: Gateway Redirect</h3>
+ <h3>Ingress Table 18: Gateway Redirect</h3>
<p>
For distributed logical routers where one or more of the logical router
@@ -3955,7 +3997,7 @@ icmp6 {
</li>
</ul>
- <h3>Ingress Table 18: ARP Request</h3>
+ <h3>Ingress Table 19: ARP Request</h3>
<p>
In the common case where the Ethernet destination has been resolved, this
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
- "version": "5.33.1",
- "cksum": "1931852754 30731",
+ "version": "5.34.1",
+ "cksum": "2177334725 30782",
"tables": {
"NB_Global": {
"columns": {
@@ -407,6 +407,7 @@
"isRoot": false},
"Logical_Router_Static_Route": {
"columns": {
+ "route_table": {"type": "string"},
"ip_prefix": {"type": "string"},
"policy": {"type": {"key": {"type": "string",
"enum": ["set", ["src-ip",
@@ -2820,6 +2820,14 @@
prefix according to RFC3663
</p>
</column>
+
+ <column name="options" key="route_table">
+ Designates lookup Logical_Router_Static_Routes with specified
+ <code>route_table</code> value. Routes to directly connected networks
+ from same Logical Router and routes without <code>route_table</code>
+ option set have higher priority than routes with
+ <code>route_table</code> option set.
+ </column>
</group>
<group title="Attachment">
@@ -2939,6 +2947,28 @@
</p>
</column>
+ <column name="route_table">
+ <p>
+ Any string to place route to separate routing table. If Logical Router
+ Port has configured value in <ref table="Logical_Router_Port"
+ column="options" key="route_table"/> other than empty string, OVN
+ performs route lookup for all packets entering Logical Router ingress
+ pipeline from this port in the following manner:
+ </p>
+
+ <ul>
+ <li>
+ 1. First lookup among "global" routes: routes without
+ <code>route_table</code> value set and routes to directly connected
+ networks.
+ </li>
+ <li>
+ 2. Next lookup among routes with same <code>route_table</code> value
+ as specified in LRP's options:route_table field.
+ </li>
+ </ul>
+ </column>
+
<column name="external_ids" key="ic-learned-route">
<code>ovn-ic</code> populates this key if the route is learned from the
global <ref db="OVN_IC_Southbound"/> database. In this case the value
@@ -282,6 +282,7 @@ done
AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.11.1.0/24 169.254.0.1 dst-ip
10.11.2.0/24 169.254.100.2 dst-ip (learned)
10.22.1.0/24 169.254.0.2 src-ip
@@ -300,6 +301,7 @@ ovn_as az1 ovn-nbctl set nb_global . options:ic-route-learn=false
OVS_WAIT_WHILE([ovn_as az1 ovn-nbctl lr-route-list lr1 | grep learned])
AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.11.1.0/24 169.254.0.1 dst-ip
10.22.1.0/24 169.254.0.2 src-ip
])
@@ -315,6 +317,7 @@ ovn_as az1 ovn-nbctl set nb_global . options:ic-route-adv=false
OVS_WAIT_WHILE([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned])
AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.11.2.0/24 169.254.0.1 dst-ip
10.22.2.0/24 169.254.0.2 src-ip
])
@@ -333,6 +336,7 @@ done
# Default route should NOT get advertised or learned, by default.
AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.11.1.0/24 169.254.100.1 dst-ip (learned)
10.11.2.0/24 169.254.0.1 dst-ip
10.22.2.0/24 169.254.0.2 src-ip
@@ -1528,6 +1528,7 @@ AT_CHECK([ovn-nbctl --ecmp --policy=src-ip lr-route-add lr0 20.0.0.0/24 11.0.0.1
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.1 dst-ip
10.0.1.0/24 11.0.1.1 dst-ip lp0
10.0.10.0/24 dst-ip lp0
@@ -1542,6 +1543,7 @@ AT_CHECK([ovn-nbctl lrp-add lr0 lp1 f0:00:00:00:00:02 11.0.0.254/24])
AT_CHECK([ovn-nbctl --may-exist lr-route-add lr0 10.0.0.111/24 11.0.0.1 lp1])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.1 dst-ip lp1
10.0.1.0/24 11.0.1.1 dst-ip lp0
10.0.10.0/24 dst-ip lp0
@@ -1572,6 +1574,7 @@ AT_CHECK([ovn-nbctl --policy=src-ip lr-route-del lr0 9.16.1.0/24])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.1 dst-ip lp1
10.0.10.0/24 dst-ip lp0
10.0.0.0/24 11.0.0.2 src-ip
@@ -1583,6 +1586,7 @@ AT_CHECK([ovn-nbctl --policy=dst-ip lr-route-del lr0 10.0.0.0/24])
AT_CHECK([ovn-nbctl --policy=src-ip lr-route-del lr0 10.0.0.0/24])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.10.0/24 dst-ip lp0
0.0.0.0/0 192.168.0.1 dst-ip
])
@@ -1593,6 +1597,7 @@ AT_CHECK([ovn-nbctl --policy=src-ip lr-route-add lr0 10.0.0.0/24 11.0.0.2])
AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.0.0/24])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.10.0/24 dst-ip lp0
0.0.0.0/0 192.168.0.1 dst-ip
])
@@ -1609,6 +1614,7 @@ AT_CHECK([ovn-nbctl --ecmp lr-route-add lr0 10.0.0.0/24 11.0.0.3])
AT_CHECK([ovn-nbctl --ecmp lr-route-add lr0 10.0.0.0/24 11.0.0.4 lp0])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.1 dst-ip ecmp
10.0.0.0/24 11.0.0.2 dst-ip ecmp
10.0.0.0/24 11.0.0.3 dst-ip ecmp
@@ -1623,6 +1629,7 @@ dnl Delete ecmp routes
AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.0.0/24 11.0.0.1])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.2 dst-ip ecmp
10.0.0.0/24 11.0.0.3 dst-ip ecmp
10.0.0.0/24 11.0.0.4 dst-ip lp0 ecmp
@@ -1630,12 +1637,14 @@ IPv4 Routes
AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.0.0/24 11.0.0.2])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.3 dst-ip ecmp
10.0.0.0/24 11.0.0.4 dst-ip lp0 ecmp
])
AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.0.0/24 11.0.0.4 lp0])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.3 dst-ip
])
AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.0.0/24 11.0.0.3])
@@ -1649,6 +1658,7 @@ AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv6 Routes
+Route Table <main>:
2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
::/0 2001:db8:0:f101::1 dst-ip
@@ -1658,6 +1668,7 @@ AT_CHECK([ovn-nbctl lr-route-del lr0 2001:0db8:0::/64])
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv6 Routes
+Route Table <main>:
2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
::/0 2001:db8:0:f101::1 dst-ip
])
@@ -1685,11 +1696,13 @@ AT_CHECK([ovn-nbctl --may-exist --ecmp-symmetric-reply lr-route-add lr0 2003:0db
AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
IPv4 Routes
+Route Table <main>:
10.0.0.0/24 11.0.0.1 dst-ip
10.0.1.0/24 11.0.1.1 dst-ip lp0
0.0.0.0/0 192.168.0.1 dst-ip
IPv6 Routes
+Route Table <main>:
2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
2001:db8:1::/64 2001:db8:0:f103::1 dst-ip ecmp
2001:db8:1::/64 2001:db8:0:f103::2 dst-ip ecmp
@@ -1704,7 +1717,188 @@ AT_CHECK([ovn-nbctl lrp-add lr0 lr0-p0 00:00:01:01:02:03 192.168.10.1/24])
bfd_uuid=$(ovn-nbctl create bfd logical_port=lr0-p0 dst_ip=100.0.0.50 status=down min_tx=250 min_rx=250 detect_mult=10)
AT_CHECK([ovn-nbctl lr-route-add lr0 100.0.0.0/24 192.168.0.1])
route_uuid=$(fetch_column nb:logical_router_static_route _uuid ip_prefix="100.0.0.0/24")
-AT_CHECK([ovn-nbctl set logical_router_static_route $route_uuid bfd=$bfd_uuid])])
+AT_CHECK([ovn-nbctl set logical_router_static_route $route_uuid bfd=$bfd_uuid])
+
+check ovn-nbctl lr-route-del lr0
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+])
+
+dnl Check IPv4 routes in route table
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 0.0.0.0/0 192.168.0.1
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 10.0.0.1/24 11.0.0.1
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+IPv4 Routes
+Route Table rtb-1:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+])
+
+check ovn-nbctl lr-route-del lr0
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+])
+
+dnl Check IPv6 routes in route table
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 0:0:0:0:0:0:0:0/0 2001:0db8:0:f101::1
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1
+
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+IPv6 Routes
+Route Table rtb-1:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+])
+
+dnl Check IPv4 and IPv6 routes in route table
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 0.0.0.0/0 192.168.0.1
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 10.0.0.1/24 11.0.0.1
+
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+IPv4 Routes
+Route Table rtb-1:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+IPv6 Routes
+Route Table rtb-1:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+])
+
+# Add routes in another route table
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 0.0.0.0/0 192.168.0.1
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 10.0.0.1/24 11.0.0.1
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 0:0:0:0:0:0:0:0/0 2001:0db8:0:f101::1
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1
+
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+IPv4 Routes
+Route Table rtb-1:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+Route Table rtb-2:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+IPv6 Routes
+Route Table rtb-1:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+
+Route Table rtb-2:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+])
+
+# Add routes to <main> route table
+check ovn-nbctl lr-route-add lr0 0.0.0.0/0 192.168.0.1
+check ovn-nbctl lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0
+check ovn-nbctl lr-route-add lr0 10.0.0.1/24 11.0.0.1
+check ovn-nbctl lr-route-add lr0 0:0:0:0:0:0:0:0/0 2001:0db8:0:f101::1
+check ovn-nbctl lr-route-add lr0 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0
+check check ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1
+
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+IPv4 Routes
+Route Table <main>:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+Route Table rtb-1:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+Route Table rtb-2:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+IPv6 Routes
+Route Table <main>:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+
+Route Table rtb-1:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+
+Route Table rtb-2:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+])
+
+# delete IPv4 route from rtb-1
+check ovn-nbctl --route-table=rtb-1 lr-route-del lr0 10.0.0.0/24
+AT_CHECK([ovn-nbctl --route-table=rtb-1 lr-route-list lr0], [0], [dnl
+IPv4 Routes
+Route Table rtb-1:
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+IPv6 Routes
+Route Table rtb-1:
+ 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+])
+
+# delete IPv6 route from rtb-2
+check ovn-nbctl --route-table=rtb-2 lr-route-del lr0 2001:db8::/64
+AT_CHECK([ovn-nbctl --route-table=rtb-2 lr-route-list lr0], [0], [dnl
+IPv4 Routes
+Route Table rtb-2:
+ 10.0.0.0/24 11.0.0.1 dst-ip
+ 10.0.1.0/24 11.0.1.1 dst-ip lp0
+ 0.0.0.0/0 192.168.0.1 dst-ip
+
+IPv6 Routes
+Route Table rtb-2:
+ 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
+ ::/0 2001:db8:0:f101::1 dst-ip
+])
+
+check ovn-nbctl lr-route-del lr0
+
+# ECMP route in route table
+check ovn-nbctl --route-table=rtb1 lr-route-add lr0 0.0.0.0/0 192.168.0.1
+check ovn-nbctl --ecmp --route-table=rtb1 lr-route-add lr0 0.0.0.0/0 192.168.0.2
+
+# Negative route table case: same prefix
+AT_CHECK([ovn-nbctl --route-table=rtb1 lr-route-add lr0 0.0.0.0/0 192.168.0.1], [1], [], [dnl
+ovn-nbctl: duplicate prefix: 0.0.0.0/0 (policy: dst-ip). Use option --ecmp to allow this for ECMP routing.
+])
+
+# Negative route table case: same prefix & nexthop with ecmp
+AT_CHECK([ovn-nbctl --ecmp --route-table=rtb1 lr-route-add lr0 0.0.0.0/0 192.168.0.2], [1], [], [dnl
+ovn-nbctl: duplicate nexthop for the same ECMP route
+])
+
+# Add routes to <main> route table
+check ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:00:01 1.1.1.1/24
+check ovn-nbctl lrp-set-options lrp0 route_table=rtb1
+AT_CHECK([ovn-nbctl get logical-router-port lrp0 options:route_table], [0], [dnl
+rtb1
+])
+check `ovn-nbctl show lr0 | grep lrp0 -A3 | grep route_table=rtb1`
+])
dnl ---------------------------------------------------------------------
@@ -5430,7 +5430,7 @@ check ovn-nbctl --wait=sb --ecmp-symmetric-reply lr-route-add lr0 1.0.0.1 192.16
ovn-sbctl dump-flows lr0 > lr0flows
AT_CHECK([grep -e "lr_in_ip_routing.*select" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
- table=??(lr_in_ip_routing ), priority=97 , match=(ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=97 , match=(reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = select(1, 2);)
])
AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed 's/192\.168\.0\..0/192.168.0.??/' | sed 's/table=../table=??/' | sort], [0], [dnl
table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.??; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;)
@@ -5443,7 +5443,7 @@ check ovn-nbctl --wait=sb --ecmp-symmetric-reply lr-route-add lr0 1.0.0.1 192.16
ovn-sbctl dump-flows lr0 > lr0flows
AT_CHECK([grep -e "lr_in_ip_routing.*select" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
- table=??(lr_in_ip_routing ), priority=97 , match=(ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=97 , match=(reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = select(1, 2);)
])
AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed 's/192\.168\.0\..0/192.168.0.??/' | sed 's/table=../table=??/' | sort], [0], [dnl
table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.??; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;)
@@ -5458,14 +5458,14 @@ check ovn-nbctl --wait=sb lr-route-add lr0 1.0.0.0/24 192.168.0.10
ovn-sbctl dump-flows lr0 > lr0flows
AT_CHECK([grep -e "lr_in_ip_routing.*192.168.0.10" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
- table=??(lr_in_ip_routing ), priority=73 , match=(ip4.dst == 1.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=73 , match=(reg7 == 0 && ip4.dst == 1.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;)
])
check ovn-nbctl --wait=sb lr-route-add lr0 2.0.0.0/24 lr0-public
ovn-sbctl dump-flows lr0 > lr0flows
AT_CHECK([grep -e "lr_in_ip_routing.*2.0.0.0" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
- table=??(lr_in_ip_routing ), priority=73 , match=(ip4.dst == 2.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=73 , match=(reg7 == 0 && ip4.dst == 2.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;)
])
AT_CLEANUP
@@ -5655,3 +5655,69 @@ ct_dnat /* assuming no un-dnat entry, so no change */ /* default (use --ct to cu
AT_CLEANUP
])
+
+AT_SETUP([route tables -- flows])
+AT_KEYWORDS([route-tables-flows])
+ovn_start
+
+check ovn-nbctl lr-add lr0
+check ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:00:01 192.168.0.1/24
+check ovn-nbctl lrp-add lr0 lrp1 00:00:00:00:01:01 192.168.1.1/24
+check ovn-nbctl lrp-add lr0 lrp2 00:00:00:00:02:01 192.168.2.1/24
+check ovn-nbctl lrp-set-options lrp1 route_table=rtb-1
+check ovn-nbctl lrp-set-options lrp2 route_table=rtb-2
+
+check ovn-nbctl lr-route-add lr0 0.0.0.0/0 192.168.0.10
+check ovn-nbctl --route-table=rtb-1 lr-route-add lr0 192.168.0.0/24 192.168.1.10
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 0.0.0.0/0 192.168.0.10
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 1.1.1.1/32 192.168.0.20
+check ovn-nbctl --route-table=rtb-2 lr-route-add lr0 2.2.2.2/32 192.168.0.30
+check ovn-nbctl --route-table=rtb-2 --ecmp lr-route-add lr0 2.2.2.2/32 192.168.0.31
+check ovn-nbctl --wait=sb sync
+
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CAPTURE_FILE([lr0flows])
+
+AT_CHECK([grep -e "lr_in_ip_routing_pre.*match=(1)" lr0flows | sed 's/table=../table=??/'], [0], [dnl
+ table=??(lr_in_ip_routing_pre), priority=0 , match=(1), action=(reg7 = 0; next;)
+])
+
+p1_reg=$(grep -oP "lr_in_ip_routing_pre.*lrp1.*action=\(reg7 = \K." lr0flows)
+p2_reg=$(grep -oP "lr_in_ip_routing_pre.*lrp2.*action=\(reg7 = \K." lr0flows)
+echo $p1_reg
+echo $p2_reg
+
+# exact register values are not predictable
+if [[ $p1_reg -eq 2 ] && [ $p2_reg -eq 1 ]]; then
+ echo "swap reg values in dump"
+ sed -i -r s'/^(.*lrp2.*action=\(reg7 = )(1)(.*)/\12\3/g' lr0flows # "reg7 = 1" -> "reg7 = 2"
+ sed -i -r s'/^(.*lrp1.*action=\(reg7 = )(2)(.*)/\11\3/g' lr0flows # "reg7 = 2" -> "reg7 = 1"
+ sed -i -r s'/^(.*match=\(reg7 == )(2)( &&.*lrp1.*)/\11\3/g' lr0flows # "reg7 == 2" -> "reg7 == 1"
+ sed -i -r s'/^(.*match=\(reg7 == )(1)( &&.*lrp0.*)/\12\3/g' lr0flows # "reg7 == 1" -> "reg7 == 2"
+fi
+
+check test "$p1_reg" != "$p2_reg" -a $((p1_reg * p2_reg)) -eq 2
+
+AT_CHECK([grep "lr_in_ip_routing_pre" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
+ table=??(lr_in_ip_routing_pre), priority=0 , match=(1), action=(reg7 = 0; next;)
+ table=??(lr_in_ip_routing_pre), priority=100 , match=(inport == "lrp1"), action=(reg7 = 1; next;)
+ table=??(lr_in_ip_routing_pre), priority=100 , match=(inport == "lrp2"), action=(reg7 = 2; next;)
+])
+
+grep -e "(lr_in_ip_routing ).*outport" lr0flows
+
+AT_CHECK([grep -e "(lr_in_ip_routing ).*outport" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
+ table=??(lr_in_ip_routing ), priority=1 , match=(reg7 == 0 && ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=1 , match=(reg7 == 2 && ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=194 , match=(inport == "lrp0" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=194 , match=(inport == "lrp1" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:101; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=194 , match=(inport == "lrp2" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:ff:fe00:201; eth.src = 00:00:00:00:02:01; outport = "lrp2"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=73 , match=(reg7 == 1 && ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.1.10; reg1 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=74 , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=74 , match=(ip4.dst == 192.168.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 = 192.168.1.1; eth.src = 00:00:00:00:01:01; outport = "lrp1"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=74 , match=(ip4.dst == 192.168.2.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 = 192.168.2.1; eth.src = 00:00:00:00:02:01; outport = "lrp2"; flags.loopback = 1; next;)
+ table=??(lr_in_ip_routing ), priority=97 , match=(reg7 == 2 && ip4.dst == 1.1.1.1/32), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.20; reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0"; flags.loopback = 1; next;)
+])
+
+AT_CLEANUP
+])
@@ -18257,7 +18257,7 @@ eth_dst=00000000ff01
ip_src=$(ip_to_hex 10 0 0 10)
ip_dst=$(ip_to_hex 172 168 0 101)
send_icmp_packet 1 1 $eth_src $eth_dst $ip_src $ip_dst c4c9 0000000000000000000000
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | awk '/table=25, n_packets=1, n_bytes=45/{print $7" "$8}'],[0],[dnl
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | awk '/table=26, n_packets=1, n_bytes=45/{print $7" "$8}'],[0],[dnl
priority=80,ip,reg15=0x3,metadata=0x3,nw_src=10.0.0.10 actions=drop
])
@@ -22698,6 +22698,538 @@ OVN_CLEANUP([hv1])
AT_CLEANUP
])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([route tables -- <main> route table routes])
+ovn_start
+
+# Logical network:
+# ls1 (192.168.1.0/24) - lrp-lr1-ls1 - lr1 - lrp-lr1-ls2 - ls2 (192.168.2.0/24)
+#
+# ls1 has lsp11 (192.168.1.11) and
+# ls2 has lsp21 (192.168.2.21) and lsp22 (192.168.2.22)
+#
+# lrp-lr1-ls1 set options:route_table=rtb-1
+#
+# Static routes on lr1:
+# 0.0.0.0/0 nexthop 192.168.2.21
+# 1.1.1.1/32 nexthop 192.168.2.22 route_table=rtb-1
+#
+# Test 1:
+# lsp11 send packet to 2.2.2.2
+#
+# Expected result:
+# each port should receive no traffic
+#
+# Test 2:
+# lsp11 send packet to 1.1.1.1
+#
+# Expected result:
+# lsp22 should receive traffic, lsp21 should not
+#
+# Test 3:
+# lsp21 send packet to 1.1.1.1
+#
+# Expected result:
+# lsp11 should receive traffic, lsp21 should not receive any traffic
+
+ovn-nbctl lr-add lr1
+
+for i in 1 2; do
+ ovn-nbctl ls-add ls${i}
+ ovn-nbctl lrp-add lr1 lrp-lr1-ls${i} 00:00:00:01:0${i}:01 192.168.${i}.1/24
+ ovn-nbctl lsp-add ls${i} lsp-ls${i}-lr1 -- lsp-set-type lsp-ls${i}-lr1 router \
+ -- lsp-set-options lsp-ls${i}-lr1 router-port=lrp-lr1-ls${i} \
+ -- lsp-set-addresses lsp-ls${i}-lr1 router
+done
+
+# install static routes
+ovn-nbctl lr-route-add lr1 0.0.0.0/0 192.168.2.21
+ovn-nbctl --route-table=rtb-1 lr-route-add lr1 1.1.1.1/32 192.168.2.22
+
+# set lrp-lr1-ls1 route table
+ovn-nbctl lrp-set-options lrp-lr1-ls1 route_table=rtb-1
+
+# Create logical ports
+ovn-nbctl lsp-add ls1 lsp11 -- \
+ lsp-set-addresses lsp11 "f0:00:00:00:01:11 192.168.1.11"
+ovn-nbctl lsp-add ls2 lsp21 -- \
+ lsp-set-addresses lsp21 "f0:00:00:00:02:21 192.168.2.21"
+ovn-nbctl lsp-add ls2 lsp22 -- \
+ lsp-set-addresses lsp22 "f0:00:00:00:02:22 192.168.2.22"
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=lsp11 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
+ set interface hv1-vif2 external-ids:iface-id=lsp21 \
+ options:tx_pcap=hv1/vif2-tx.pcap \
+ options:rxq_pcap=hv1/vif2-rx.pcap \
+ ofport-request=2
+
+ovs-vsctl -- add-port br-int hv1-vif3 -- \
+ set interface hv1-vif3 external-ids:iface-id=lsp22 \
+ options:tx_pcap=hv1/vif3-tx.pcap \
+ options:rxq_pcap=hv1/vif3-rx.pcap \
+ ofport-request=3
+
+# wait for earlier changes to take effect
+check ovn-nbctl --wait=hv sync
+wait_for_ports_up
+
+# Test 1
+packet="inport==\"lsp11\" && eth.src==f0:00:00:00:01:11 && eth.dst==00:00:00:01:01:01 &&
+ ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==2.2.2.2 && icmp"
+AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+# Assume no packets go neither to lsp21 nor to lsp22.
+> expected_lsp21
+> expected_lsp22
+
+ovn-sbctl dump-flows lr1
+
+
+OVS_WAIT_UNTIL([
+ rcv_n1=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > lsp21.packets && cat lsp21.packets | wc -l`
+ rcv_n2=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap > lsp22.packets && cat lsp22.packets | wc -l`
+ echo $rcv_n1 $rcv_n2
+ test $rcv_n1 -eq 0 -a $rcv_n2 -eq 0])
+
+for i in 1 2; do
+ sort expected_lsp2$i > expout
+ AT_CHECK([cat lsp2${i}.packets | sort], [0], [expout])
+done
+
+# cleanup pcap
+for i in 2 3; do
+ ovs-vsctl remove interface hv1-vif$i options tx_pcap
+ > hv1/vif$i-tx.pcap
+ ovs-vsctl set interface hv1-vif$i options:tx_pcap=hv1/vif$i-tx.pcap
+done
+
+# Test 2
+packet="inport==\"lsp11\" && eth.src==f0:00:00:00:01:11 && eth.dst==00:00:00:01:01:01 &&
+ ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==1.1.1.1 && icmp"
+AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+# Assume all packets go to lsp22.
+exp_packet="eth.src==00:00:00:01:02:01 && eth.dst==f0:00:00:00:02:22 &&
+ ip4 && ip.ttl==63 && ip4.src==192.168.1.11 && ip4.dst==1.1.1.1 && icmp"
+echo $exp_packet | ovstest test-ovn expr-to-packets > expected_lsp22
+> expected_lsp21
+
+# lsp21 should recieve 1 packet and lsp22 should recieve no packets
+OVS_WAIT_UNTIL([
+ rcv_n1=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > lsp21.packets && cat lsp21.packets | wc -l`
+ rcv_n2=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap > lsp22.packets && cat lsp22.packets | wc -l`
+ echo $rcv_n1 $rcv_n2
+ test $rcv_n1 -eq 0 -a $rcv_n2 -eq 1])
+
+for i in 1 2; do
+ sort expected_lsp2$i > expout
+ AT_CHECK([cat lsp2${i}.packets | sort], [0], [expout])
+done
+
+# cleanup pcap
+for i in 2 3; do
+ ovs-vsctl remove interface hv1-vif$i options tx_pcap
+ > hv1/vif$i-tx.pcap
+ ovs-vsctl set interface hv1-vif$i options:tx_pcap=hv1/vif$i-tx.pcap
+done
+
+# Test 3
+packet="inport==\"lsp21\" && eth.src==f0:00:00:00:02:21 && eth.dst==00:00:00:01:02:01 &&
+ ip4 && ip.ttl==64 && ip4.src==192.168.2.21 && ip4.dst==2.2.2.2 && icmp"
+AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+# Assume all packets go to lsp21.
+exp_packet="eth.src==00:00:00:01:02:01 && eth.dst==f0:00:00:00:02:21 &&
+ ip4 && ip.ttl==63 && ip4.src==192.168.2.21 && ip4.dst==2.2.2.2 && icmp"
+echo $exp_packet | ovstest test-ovn expr-to-packets > expected_lsp21
+> expected_lsp22
+
+# lsp21 should recieve 1 packet and lsp22 should recieve no packets
+OVS_WAIT_UNTIL([
+ rcv_n1=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > lsp21.packets && cat lsp21.packets | wc -l`
+ rcv_n2=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap > lsp22.packets && cat lsp22.packets | wc -l`
+ echo $rcv_n1 $rcv_n2
+ test $rcv_n1 -eq 1 -a $rcv_n2 -eq 0])
+
+for i in 1 2; do
+ sort expected_lsp2$i > expout
+ AT_CHECK([cat lsp2${i}.packets | sort], [0], [expout])
+done
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
+
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([route tables -- directly connected routes])
+ovn_start
+
+# Logical network:
+# ls1 (192.168.1.0/24) - lrp-lr1-ls1 - lr1 - lrp-lr1-ls2 - ls2 (192.168.2.0/24)
+#
+# ls1 has lsp11 (192.168.1.11) and ls2 has lsp21 (192.168.2.21)
+#
+# lrp-lr1-ls1 set options:route_table=rtb-1
+#
+# Static routes on lr1:
+# 192.168.2.128/25 nexthop 192.168.1.11 route_table=rtb-1
+#
+# Test 1:
+# lsp11 send packet to 192.168.2.21
+#
+# Expected result:
+# lsp21 should receive traffic, lsp11 should not receive any traffic
+#
+# Test 2:
+# lsp11 send packet to 192.168.2.200
+#
+# Expected result:
+# lsp11 should receive traffic, lsp21 should not receive any traffic
+
+ovn-nbctl lr-add lr1
+
+for i in 1 2; do
+ ovn-nbctl ls-add ls${i}
+ ovn-nbctl lrp-add lr1 lrp-lr1-ls${i} 00:00:00:01:0${i}:01 192.168.${i}.1/24
+ ovn-nbctl lsp-add ls${i} lsp-ls${i}-lr1 -- lsp-set-type lsp-ls${i}-lr1 router \
+ -- lsp-set-options lsp-ls${i}-lr1 router-port=lrp-lr1-ls${i} \
+ -- lsp-set-addresses lsp-ls${i}-lr1 router
+done
+
+# install static route, which is more specific than directly-connected route
+ovn-nbctl --route-table=rtb-1 lr-route-add lr1 192.168.2.128/25 192.168.1.11
+
+# set lrp-lr1-ls1 route table
+ovn-nbctl lrp-set-options lrp-lr1-ls1 route_table=rtb-1
+
+# Create logical ports
+ovn-nbctl lsp-add ls1 lsp11 -- \
+ lsp-set-addresses lsp11 "f0:00:00:00:01:11 192.168.1.11"
+ovn-nbctl lsp-add ls2 lsp21 -- \
+ lsp-set-addresses lsp21 "f0:00:00:00:02:21 192.168.2.21"
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=lsp11 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
+ set interface hv1-vif2 external-ids:iface-id=lsp21 \
+ options:tx_pcap=hv1/vif2-tx.pcap \
+ options:rxq_pcap=hv1/vif2-rx.pcap \
+ ofport-request=2
+
+# wait for earlier changes to take effect
+check ovn-nbctl --wait=hv sync
+wait_for_ports_up
+
+# test 1
+packet="inport==\"lsp11\" && eth.src==f0:00:00:00:01:11 && eth.dst==00:00:00:01:01:01 &&
+ ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.2.21 && icmp"
+AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+# Assume all packets go to lsp21.
+exp_packet="eth.src==00:00:00:01:02:01 && eth.dst==f0:00:00:00:02:21 && ip4 &&
+ ip.ttl==63 && ip4.src==192.168.1.11 && ip4.dst==192.168.2.21 && icmp"
+echo $exp_packet | ovstest test-ovn expr-to-packets > expected_lsp21
+> expected_lsp11
+
+# lsp21 should recieve 1 icmp packet and lsp11 should recieve no packets
+OVS_WAIT_UNTIL([
+ rcv_n11=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > lsp11.packets && cat lsp11.packets | wc -l`
+ rcv_n21=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > lsp21.packets && cat lsp21.packets | wc -l`
+ echo $rcv_n11 $rcv_n21
+ test $rcv_n11 -eq 0 -a $rcv_n21 -eq 1])
+
+for i in 11 21; do
+ sort expected_lsp$i > expout
+ AT_CHECK([cat lsp${i}.packets | sort], [0], [expout])
+done
+
+for i in 11 21; do
+ > lsp${i}.packets
+done
+
+# flush tx pcaps:
+ovs-vsctl remove interface hv1-vif1 options tx_pcap
+ovs-vsctl remove interface hv1-vif2 options tx_pcap
+rm -f hv1/vif1-tx.pcap hv1/vif2-tx.pcap
+ovs-vsctl set interface hv1-vif1 options:tx_pcap=hv1/vif1-tx.pcap
+ovs-vsctl set interface hv1-vif2 options:tx_pcap=hv1/vif2-tx.pcap
+
+# test 2
+packet="inport==\"lsp11\" && eth.src==f0:00:00:00:01:11 && eth.dst==00:00:00:01:01:01 &&
+ ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.2.200 && icmp"
+AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+# Assume all packets go to lsp11.
+exp_packet="eth.src==00:00:00:01:01:01 && eth.dst==f0:00:00:00:01:11 && ip4 &&
+ ip.ttl==63 && ip4.src==192.168.1.11 && ip4.dst==192.168.2.200 && icmp"
+echo $exp_packet | ovstest test-ovn expr-to-packets > expected_lsp11
+> expected_lsp21
+
+# lsp11 should recieve 1 icmp packet and lsp21 should recieve no packets
+OVS_WAIT_UNTIL([
+ rcv_n11=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > lsp11.packets && cat lsp11.packets | wc -l`
+ rcv_n21=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > lsp21.packets && cat lsp21.packets | wc -l`
+ echo $rcv_n11 $rcv_n21
+ test $rcv_n11 -eq 1 -a $rcv_n21 -eq 0])
+
+for i in 11 21; do
+ echo I=$i
+ sort expected_lsp$i > expout
+ AT_CHECK([cat lsp${i}.packets | sort], [0], [expout])
+done
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
+
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([route tables -- overlapping subnets])
+ovn_start
+
+# Logical network:
+#
+# ls1 (192.168.1.0/24) - lrp-lr1-ls1 -\ /- lrp-lr1-ls2 - ls2 (192.168.2.0/24)
+# lr1
+# ls3 (192.168.3.0/24) - lrp-lr1-ls3 -/ \- lrp-lr1-ls4 - ls4 (192.168.4.0/24)
+#
+# ls1 has lsp11 (192.168.1.11)
+# ls2 has lsp21 (192.168.2.21)
+# ls3 has lsp31 (192.168.3.31)
+# ls4 has lsp41 (192.168.4.41)
+#
+# lrp-lr1-ls1 set options:route_table=rtb-1
+#
+# Static routes on lr1:
+# 10.0.0.0/24 nexthop 192.168.3.31 route_table=rtb-1
+# 10.0.0.0/24 nexthop 192.168.4.41 (<main> route table)
+#
+# Test 1:
+# lsp11 send packet to 10.0.0.1
+#
+# Expected result:
+# lsp31 should receive traffic, lsp41 should not receive any traffic
+#
+# Test 2:
+# lsp21 send packet to 10.0.0.1
+#
+# Expected result:
+# lsp41 should receive traffic, lsp31 should not receive any traffic
+
+ovn-nbctl lr-add lr1
+
+# Create logical topology
+for i in $(seq 1 4); do
+ ovn-nbctl ls-add ls${i}
+ ovn-nbctl lrp-add lr1 lrp-lr1-ls${i} 00:00:00:01:0${i}:01 192.168.${i}.1/24
+ ovn-nbctl lsp-add ls${i} lsp-ls${i}-lr1 -- lsp-set-type lsp-ls${i}-lr1 router \
+ -- lsp-set-options lsp-ls${i}-lr1 router-port=lrp-lr1-ls${i} \
+ -- lsp-set-addresses lsp-ls${i}-lr1 router
+ ovn-nbctl lsp-add ls$i lsp${i}1 -- \
+ lsp-set-addresses lsp${i}1 "f0:00:00:00:0${i}:1${i} 192.168.${i}.${i}1"
+done
+
+# install static routes
+ovn-nbctl --route-table=rtb-1 lr-route-add lr1 10.0.0.0/24 192.168.3.31
+ovn-nbctl lr-route-add lr1 10.0.0.0/24 192.168.4.41
+
+# set lrp-lr1-ls1 route table
+ovn-nbctl lrp-set-options lrp-lr1-ls1 route_table=rtb-1
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+
+for i in $(seq 1 4); do
+ ovs-vsctl -- add-port br-int hv1-vif${i} -- \
+ set interface hv1-vif${i} external-ids:iface-id=lsp${i}1 \
+ options:tx_pcap=hv1/vif${i}-tx.pcap \
+ options:rxq_pcap=hv1/vif${i}-rx.pcap \
+ ofport-request=${i}
+done
+
+# wait for earlier changes to take effect
+check ovn-nbctl --wait=hv sync
+wait_for_ports_up
+
+# lsp31 should recieve packet coming from lsp11
+# lsp41 should recieve packet coming from lsp21
+for i in $(seq 1 2); do
+ di=$(( i + 2)) # dst index
+ ri=$(( 5 - i)) # reverse index
+ packet="inport==\"lsp${i}1\" && eth.src==f0:00:00:00:0${i}:1${i} &&
+ eth.dst==00:00:00:01:0${i}:01 && ip4 && ip.ttl==64 &&
+ ip4.src==192.168.${i}.${i}1 && ip4.dst==10.0.0.1 && icmp"
+ AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+ # Assume all packets go to lsp${di}1.
+ exp_packet="eth.src==00:00:00:01:0${di}:01 && eth.dst==f0:00:00:00:0${di}:1${di} &&
+ ip4 && ip.ttl==63 && ip4.src==192.168.${i}.${i}1 && ip4.dst==10.0.0.1 && icmp"
+ echo $exp_packet | ovstest test-ovn expr-to-packets >> expected_lsp${di}1
+ > expected_lsp${ri}1
+
+ OVS_WAIT_UNTIL([
+ rcv_n1=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif${di}-tx.pcap > lsp${di}1.packets && cat lsp${di}1.packets | wc -l`
+ rcv_n2=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif${ri}-tx.pcap > lsp${ri}1.packets && cat lsp${ri}1.packets | wc -l`
+ echo $rcv_n1 $rcv_n2
+ test $rcv_n1 -eq 1 -a $rcv_n2 -eq 0])
+
+ for j in "${di}1" "${ri}1"; do
+ sort expected_lsp${j} > expout
+ AT_CHECK([cat lsp${j}.packets | sort], [0], [expout])
+ done
+
+ # cleanup tx pcap files
+ for j in "${di}1" "${ri}1"; do
+ ovs-vsctl -- remove interface hv1-vif${di} options tx_pcap
+ > hv1/vif${di}-tx.pcap
+ ovs-vsctl -- set interface hv1-vif${di} external-ids:iface-id=lsp${di}1 \
+ options:tx_pcap=hv1/vif${di}-tx.pcap
+ done
+done
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
+
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([route tables IPv6 -- overlapping subnets])
+ovn_start
+
+# Logical network:
+#
+# ls1 (2001:db8:1::/64) - lrp-lr1-ls1 -\ /- lrp-lr1-ls2 - ls2 (2001:db8:2::/64)
+# lr1
+# ls3 (2001:db8:3::/64) - lrp-lr1-ls3 -/ \- lrp-lr1-ls4 - ls4 (2001:db8:4::/64)
+#
+# ls1 has lsp11 (2001:db8:1::11)
+# ls2 has lsp21 (2001:db8:2::21)
+# ls3 has lsp31 (2001:db8:3::31)
+# ls4 has lsp41 (2001:db8:4::41)
+#
+# lrp-lr1-ls1 set options:route_table=rtb-1
+# lrp-lr1-ls2 set options:route_table=rtb-2
+#
+# Static routes on lr1:
+# 2001:db8:2000::/64 nexthop 2001:db8:3::31 route_table=rtb-1
+# 2001:db8:2000::/64 nexthop 2001:db8:3::41 route_table=rtb-2
+#
+# Test 1:
+# lsp11 send packet to 2001:db8:2000::1
+#
+# Expected result:
+# lsp31 should receive traffic, lsp41 should not receive any traffic
+#
+# Test 2:
+# lsp21 send packet to 2001:db8:2000::1
+#
+# Expected result:
+# lsp41 should receive traffic, lsp31 should not receive any traffic
+
+ovn-nbctl lr-add lr1
+
+# Create logical topology
+for i in $(seq 1 4); do
+ ovn-nbctl ls-add ls${i}
+ ovn-nbctl lrp-add lr1 lrp-lr1-ls${i} 00:00:00:01:0${i}:01 2001:db8:${i}::1/64
+ ovn-nbctl lsp-add ls${i} lsp-ls${i}-lr1 -- lsp-set-type lsp-ls${i}-lr1 router \
+ -- lsp-set-options lsp-ls${i}-lr1 router-port=lrp-lr1-ls${i} \
+ -- lsp-set-addresses lsp-ls${i}-lr1 router
+ ovn-nbctl lsp-add ls$i lsp${i}1 -- \
+ lsp-set-addresses lsp${i}1 "f0:00:00:00:0${i}:1${i} 2001:db8:${i}::${i}1"
+done
+
+# install static routes
+ovn-nbctl --route-table=rtb-1 lr-route-add lr1 2001:db8:2000::/64 2001:db8:3::31
+ovn-nbctl --route-table=rtb-2 lr-route-add lr1 2001:db8:2000::/64 2001:db8:4::41
+
+# set lrp-lr1-ls{1,2} route tables
+ovn-nbctl lrp-set-options lrp-lr1-ls1 route_table=rtb-1
+ovn-nbctl lrp-set-options lrp-lr1-ls2 route_table=rtb-2
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+
+for i in $(seq 1 4); do
+ ovs-vsctl -- add-port br-int hv1-vif${i} -- \
+ set interface hv1-vif${i} external-ids:iface-id=lsp${i}1 \
+ options:tx_pcap=hv1/vif${i}-tx.pcap \
+ options:rxq_pcap=hv1/vif${i}-rx.pcap \
+ ofport-request=${i}
+done
+
+# wait for earlier changes to take effect
+AT_CHECK([ovn-nbctl --timeout=3 --wait=hv sync], [0], [ignore])
+
+# lsp31 should recieve packet coming from lsp11
+# lsp41 should recieve packet coming from lsp21
+for i in $(seq 1 2); do
+ di=$(( i + 2)) # dst index
+ ri=$(( 5 - i)) # reverse index
+ packet="inport==\"lsp${i}1\" && eth.src==f0:00:00:00:0${i}:1${i} &&
+ eth.dst==00:00:00:01:0${i}:01 && ip6 && ip.ttl==64 &&
+ ip6.src==2001:db8:${i}::${i}1 && ip6.dst==2001:db8:2000::1 && icmp6"
+ AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+ # Assume all packets go to lsp${di}1.
+ exp_packet="eth.src==00:00:00:01:0${di}:01 && eth.dst==f0:00:00:00:0${di}:1${di} && ip6 &&
+ ip.ttl==63 && ip6.src==2001:db8:${i}::${i}1 && ip6.dst==2001:db8:2000::1 && icmp6"
+ echo $exp_packet | ovstest test-ovn expr-to-packets >> expected_lsp${di}1
+ > expected_lsp${ri}1
+
+ OVS_WAIT_UNTIL([
+ rcv_n1=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif${di}-tx.pcap > lsp${di}1.packets && cat lsp${di}1.packets | wc -l`
+ rcv_n2=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif${ri}-tx.pcap > lsp${ri}1.packets && cat lsp${ri}1.packets | wc -l`
+ echo $rcv_n1 $rcv_n2
+ test $rcv_n1 -eq 1 -a $rcv_n2 -eq 0])
+
+ for j in "${di}1" "${ri}1"; do
+ sort expected_lsp${j} > expout
+ AT_CHECK([cat lsp${j}.packets | sort], [0], [expout])
+ done
+
+ # cleanup tx pcap files
+ for j in "${di}1" "${ri}1"; do
+ ovs-vsctl -- remove interface hv1-vif${di} options tx_pcap
+ > hv1/vif${di}-tx.pcap
+ ovs-vsctl -- set interface hv1-vif${di} external-ids:iface-id=lsp${di}1 \
+ options:tx_pcap=hv1/vif${di}-tx.pcap
+ done
+done
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
+
+
OVN_FOR_EACH_NORTHD([
AT_SETUP([forwarding group: 3 HVs, 1 LR, 2 LS])
AT_KEYWORDS([forwarding-group])
@@ -23453,7 +23985,7 @@ ovn-sbctl dump-flows > sbflows
AT_CAPTURE_FILE([sbflows])
AT_CAPTURE_FILE([offlows])
OVS_WAIT_UNTIL([
- as hv1 ovs-ofctl dump-flows br-int table=20 > offlows
+ as hv1 ovs-ofctl dump-flows br-int table=21 > offlows
test $(grep -c "load:0x64->NXM_NX_PKT_MARK" offlows) = 1 && \
test $(grep -c "load:0x3->NXM_NX_PKT_MARK" offlows) = 1 && \
test $(grep -c "load:0x4->NXM_NX_PKT_MARK" offlows) = 1 && \
@@ -23546,12 +24078,12 @@ send_ipv4_pkt hv1 hv1-vif1 505400000003 00000000ff01 \
$(ip_to_hex 10 0 0 3) $(ip_to_hex 172 168 0 120)
OVS_WAIT_UNTIL([
- test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
+ test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=21 | \
grep "load:0x2->NXM_NX_PKT_MARK" -c)
])
AT_CHECK([
- test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=20 | \
+ test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=21 | \
grep "load:0x64->NXM_NX_PKT_MARK" -c)
])
@@ -24278,7 +24810,7 @@ AT_CHECK([
grep "priority=100" | \
grep -c "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))"
- grep table=22 hv${hv}flows | \
+ grep table=23 hv${hv}flows | \
grep "priority=200" | \
grep -c "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]"
done; :], [0], [dnl
@@ -24403,7 +24935,7 @@ AT_CHECK([
grep "priority=100" | \
grep -c "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))"
- grep table=22 hv${hv}flows | \
+ grep table=23 hv${hv}flows | \
grep "priority=200" | \
grep -c "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]"
done; :], [0], [dnl
@@ -25025,7 +25557,7 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep "actions=controller" | grep
])
# The packet should've been dropped in the lr_in_arp_resolve stage.
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=22, n_packets=1,.* priority=1,ip,metadata=0x${sw_key},nw_dst=10.0.1.1 actions=drop" -c], [0], [dnl
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=23, n_packets=1,.* priority=1,ip,metadata=0x${sw_key},nw_dst=10.0.1.1 actions=drop" -c], [0], [dnl
1
])
@@ -329,6 +329,8 @@ Logical router port commands:\n\
add logical port PORT on ROUTER\n\
lrp-set-gateway-chassis PORT CHASSIS [PRIORITY]\n\
set gateway chassis for port PORT\n\
+ lrp-set-options PORT KEY=VALUE [KEY=VALUE]...\n\
+ set router port options\n\
lrp-del-gateway-chassis PORT CHASSIS\n\
delete gateway chassis from port PORT\n\
lrp-get-gateway-chassis PORT\n\
@@ -351,11 +353,17 @@ Logical router port commands:\n\
('overlay' or 'bridged')\n\
\n\
Route commands:\n\
- [--policy=POLICY] [--ecmp] [--ecmp-symmetric-reply] lr-route-add ROUTER \n\
- PREFIX NEXTHOP [PORT]\n\
+ [--policy=POLICY]\n\
+ [--ecmp]\n\
+ [--ecmp-symmetric-reply]\n\
+ [--route-table=ROUTE_TABLE]\n\
+ lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\
add a route to ROUTER\n\
- [--policy=POLICY] lr-route-del ROUTER [PREFIX [NEXTHOP [PORT]]]\n\
+ [--policy=POLICY]\n\
+ [--route-table=ROUTE_TABLE]\n\
+ lr-route-del ROUTER [PREFIX [NEXTHOP [PORT]]]\n\
remove routes from ROUTER\n\
+ [--route-table=ROUTE_TABLE]\n\
lr-route-list ROUTER print routes for ROUTER\n\
\n\
Policy commands:\n\
@@ -743,6 +751,11 @@ print_lr(const struct nbrec_logical_router *lr, struct ds *s)
ds_put_cstr(s, "]\n");
}
+ const char *route_table = smap_get(&lrp->options, "route_table");
+ if (route_table) {
+ ds_put_format(s, " route-table: %s\n", route_table);
+ }
+
if (lrp->n_gateway_chassis) {
const struct nbrec_gateway_chassis **gcs;
@@ -862,6 +875,7 @@ nbctl_pre_show(struct ctl_context *ctx)
ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_mac);
ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_networks);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_options);
ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_gateway_chassis);
ovsdb_idl_add_column(ctx->idl, &nbrec_gateway_chassis_col_chassis_name);
@@ -4020,11 +4034,19 @@ nbctl_lr_policy_list(struct ctl_context *ctx)
static struct nbrec_logical_router_static_route *
nbctl_lr_get_route(const struct nbrec_logical_router *lr, char *prefix,
- char *next_hop, bool is_src_route, bool ecmp)
+ char *next_hop, bool is_src_route, bool ecmp,
+ char *route_table)
{
for (int i = 0; i < lr->n_static_routes; i++) {
struct nbrec_logical_router_static_route *route = lr->static_routes[i];
+ /* Strict compare for route_table.
+ * If route_table was not specified,
+ * lookup for routes with empty route_table value. */
+ if (strcmp(route->route_table, route_table ? route_table : "")) {
+ continue;
+ }
+
/* Compare route policy. */
char *nb_policy = route->policy;
bool nb_is_src_route = false;
@@ -4080,6 +4102,8 @@ nbctl_pre_lr_route_add(struct ctl_context *ctx)
&nbrec_logical_router_static_route_col_bfd);
ovsdb_idl_add_column(ctx->idl,
&nbrec_logical_router_static_route_col_options);
+ ovsdb_idl_add_column(ctx->idl,
+ &nbrec_logical_router_static_route_col_route_table);
}
static char * OVS_WARN_UNUSED_RESULT
@@ -4110,6 +4134,7 @@ nbctl_lr_route_add(struct ctl_context *ctx)
}
}
+ char *route_table = shash_find_data(&ctx->options, "--route-table");
bool v6_prefix = false;
prefix = normalize_ipv4_prefix_str(ctx->argv[2]);
if (!prefix) {
@@ -4186,7 +4211,8 @@ nbctl_lr_route_add(struct ctl_context *ctx)
bool ecmp = shash_find(&ctx->options, "--ecmp") != NULL ||
ecmp_symmetric_reply;
struct nbrec_logical_router_static_route *route =
- nbctl_lr_get_route(lr, prefix, next_hop, is_src_route, ecmp);
+ nbctl_lr_get_route(lr, prefix, next_hop, is_src_route, ecmp,
+ route_table);
/* Validations for nexthop = "discard" */
if (is_discard_route) {
@@ -4250,7 +4276,8 @@ nbctl_lr_route_add(struct ctl_context *ctx)
}
struct nbrec_logical_router_static_route *discard_route =
- nbctl_lr_get_route(lr, prefix, "discard", is_src_route, true);
+ nbctl_lr_get_route(lr, prefix, "discard", is_src_route, true,
+ route_table);
if (discard_route) {
ctl_error(ctx, "discard nexthop for the same ECMP route exists.");
goto cleanup;
@@ -4266,6 +4293,9 @@ nbctl_lr_route_add(struct ctl_context *ctx)
if (policy) {
nbrec_logical_router_static_route_set_policy(route, policy);
}
+ if (route_table) {
+ nbrec_logical_router_static_route_set_route_table(route, route_table);
+ }
if (ecmp_symmetric_reply) {
const struct smap options = SMAP_CONST1(&options,
@@ -4309,6 +4339,8 @@ nbctl_pre_lr_route_del(struct ctl_context *ctx)
&nbrec_logical_router_static_route_col_nexthop);
ovsdb_idl_add_column(ctx->idl,
&nbrec_logical_router_static_route_col_output_port);
+ ovsdb_idl_add_column(ctx->idl,
+ &nbrec_logical_router_static_route_col_route_table);
}
@@ -4322,6 +4354,7 @@ nbctl_lr_route_del(struct ctl_context *ctx)
return;
}
+ const char *route_table = shash_find_data(&ctx->options, "--route-table");
const char *policy = shash_find_data(&ctx->options, "--policy");
bool is_src_route = false;
if (policy) {
@@ -4412,6 +4445,14 @@ nbctl_lr_route_del(struct ctl_context *ctx)
}
}
+ /* Strict compare for route_table.
+ * If route_table was not specified,
+ * lookup for routes with empty route_table value. */
+ if (strcmp(lr->static_routes[i]->route_table,
+ route_table ? route_table : "")) {
+ continue;
+ }
+
/* Compare output_port, if specified. */
if (output_port) {
char *rt_output_port = lr->static_routes[i]->output_port;
@@ -5132,6 +5173,41 @@ nbctl_pre_lrp_del_gateway_chassis(struct ctl_context *ctx)
ovsdb_idl_add_column(ctx->idl, &nbrec_gateway_chassis_col_chassis_name);
}
+static void
+nbctl_pre_lrp_options(struct ctl_context *ctx)
+{
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_options);
+}
+
+static void
+nbctl_lrp_set_options(struct ctl_context *ctx)
+{
+ const char *id = ctx->argv[1];
+ const struct nbrec_logical_router_port *lrp = NULL;
+ size_t i;
+ struct smap options = SMAP_INITIALIZER(&options);
+
+ char *error = lrp_by_name_or_uuid(ctx, id, true, &lrp);
+ if (error) {
+ ctx->error = error;
+ return;
+ }
+ for (i = 2; i < ctx->argc; i++) {
+ char *key, *value;
+ value = xstrdup(ctx->argv[i]);
+ key = strsep(&value, "=");
+ if (value) {
+ smap_add(&options, key, value);
+ }
+ free(key);
+ }
+
+ nbrec_logical_router_port_set_options(lrp, &options);
+
+ smap_destroy(&options);
+}
+
/* Removes logical router port 'lrp->gateway_chassis[idx]'. */
static void
remove_gc(const struct nbrec_logical_router_port *lrp, size_t idx)
@@ -5908,6 +5984,7 @@ route_cmp_details(const struct nbrec_logical_router_static_route *r1,
}
return r1->output_port ? 1 : -1;
}
+
struct ipv4_route {
int priority;
ovs_be32 addr;
@@ -5917,6 +5994,11 @@ struct ipv4_route {
static int
__ipv4_route_cmp(const struct ipv4_route *r1, const struct ipv4_route *r2)
{
+ int rtb_cmp = strcmp(r1->route->route_table,
+ r2->route->route_table);
+ if (rtb_cmp) {
+ return rtb_cmp;
+ }
if (r1->priority != r2->priority) {
return r1->priority > r2->priority ? -1 : 1;
}
@@ -5948,6 +6030,11 @@ struct ipv6_route {
static int
__ipv6_route_cmp(const struct ipv6_route *r1, const struct ipv6_route *r2)
{
+ int rtb_cmp = strcmp(r1->route->route_table,
+ r2->route->route_table);
+ if (rtb_cmp) {
+ return rtb_cmp;
+ }
if (r1->priority != r2->priority) {
return r1->priority > r2->priority ? -1 : 1;
}
@@ -6035,6 +6122,8 @@ nbctl_pre_lr_route_list(struct ctl_context *ctx)
&nbrec_logical_router_static_route_col_options);
ovsdb_idl_add_column(ctx->idl,
&nbrec_logical_router_static_route_col_bfd);
+ ovsdb_idl_add_column(ctx->idl,
+ &nbrec_logical_router_static_route_col_route_table);
}
static void
@@ -6052,12 +6141,17 @@ nbctl_lr_route_list(struct ctl_context *ctx)
return;
}
+ char *route_table = shash_find_data(&ctx->options, "--route-table");
+
ipv4_routes = xmalloc(sizeof *ipv4_routes * lr->n_static_routes);
ipv6_routes = xmalloc(sizeof *ipv6_routes * lr->n_static_routes);
for (int i = 0; i < lr->n_static_routes; i++) {
const struct nbrec_logical_router_static_route *route
= lr->static_routes[i];
+ if (route_table && strcmp(route->route_table, route_table)) {
+ continue;
+ }
unsigned int plen;
ovs_be32 ipv4;
const char *policy = route->policy ? route->policy : "dst-ip";
@@ -6098,6 +6192,7 @@ nbctl_lr_route_list(struct ctl_context *ctx)
if (n_ipv4_routes) {
ds_put_cstr(&ctx->output, "IPv4 Routes\n");
}
+ const struct nbrec_logical_router_static_route *route;
for (int i = 0; i < n_ipv4_routes; i++) {
bool ecmp = false;
if (i < n_ipv4_routes - 1 &&
@@ -6108,6 +6203,15 @@ nbctl_lr_route_list(struct ctl_context *ctx)
&ipv4_routes[i - 1])) {
ecmp = true;
}
+
+ route = ipv4_routes[i].route;
+ if (!i || (i > 0 && strcmp(route->route_table,
+ ipv4_routes[i - 1].route->route_table))) {
+ ds_put_format(&ctx->output, "%sRoute Table %s:\n", i ? "\n" : "",
+ strlen(route->route_table) ? route->route_table
+ : "<main>");
+ }
+
print_route(ipv4_routes[i].route, &ctx->output, ecmp);
}
@@ -6125,6 +6229,15 @@ nbctl_lr_route_list(struct ctl_context *ctx)
&ipv6_routes[i - 1])) {
ecmp = true;
}
+
+ route = ipv6_routes[i].route;
+ if (!i || (i > 0 && strcmp(route->route_table,
+ ipv6_routes[i - 1].route->route_table))) {
+ ds_put_format(&ctx->output, "%sRoute Table %s:\n", i ? "\n" : "",
+ strlen(route->route_table) ? route->route_table
+ : "<main>");
+ }
+
print_route(ipv6_routes[i].route, &ctx->output, ecmp);
}
@@ -6946,6 +7059,8 @@ static const struct ctl_command_syntax nbctl_commands[] = {
"PORT CHASSIS [PRIORITY]",
nbctl_pre_lrp_set_gateway_chassis, nbctl_lrp_set_gateway_chassis,
NULL, "--may-exist", RW },
+ { "lrp-set-options", 1, INT_MAX, "PORT KEY=VALUE [KEY=VALUE]...",
+ nbctl_pre_lrp_options, nbctl_lrp_set_options, NULL, "", RW },
{ "lrp-del-gateway-chassis", 2, 2, "PORT CHASSIS",
nbctl_pre_lrp_del_gateway_chassis, nbctl_lrp_del_gateway_chassis,
NULL, "", RW },
@@ -6969,12 +7084,13 @@ static const struct ctl_command_syntax nbctl_commands[] = {
/* logical router route commands. */
{ "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]",
nbctl_pre_lr_route_add, nbctl_lr_route_add, NULL,
- "--may-exist,--ecmp,--ecmp-symmetric-reply,--policy=,--bfd?", RW },
+ "--may-exist,--ecmp,--ecmp-symmetric-reply,--policy=,--route-table=,--bfd?",
+ RW },
{ "lr-route-del", 1, 4, "ROUTER [PREFIX [NEXTHOP [PORT]]]",
nbctl_pre_lr_route_del, nbctl_lr_route_del, NULL,
- "--if-exists,--policy=", RW },
+ "--if-exists,--policy=,--route-table=", RW },
{ "lr-route-list", 1, 1, "ROUTER", nbctl_pre_lr_route_list,
- nbctl_lr_route_list, NULL, "", RO },
+ nbctl_lr_route_list, NULL, "--route-table=", RO },
/* Policy commands */
{ "lr-policy-add", 4, INT_MAX,