@@ -329,7 +329,33 @@
Prints the administrative state of <var>port</var>, either
<code>enabled</code> or <code>disabled</code>.
</dd>
+ </dl>
+
+ <h1>Logical Route Commands</h1>
+
+ <dl>
+ <dt><code>lr-route-add</code> <var>router</var> <var>prefix</var> <var>nexthop</var> [<var>port</var>]</dt>
+ <dd>
+ Adds the specified route to <var>router</var>. <var>prefix</var>
+ describes the IP prefix for this route. <var>nexthop</var>
+ specifies the gateway to use for this route. If <var>port</var>
+ is specified, packets that match this route will be sent out
+ that port.
+ </dd>
+ <dt><code>lr-route-del</code> <var>router</var> [<var>prefix</var>]</dt>
+ <dd>
+ Deletes routes from <var>router</var>. If only
+ <var>router</var> is supplied, all the routes from the logical
+ router are deleted. If <var>prefix</var> is also specified,
+ then all the routes that match the prefix will be deleted from
+ the logical router.
+ </dd>
+
+ <dt><code>lr-route-list</code> <var>router</var></dt>
+ <dd>
+ Lists the routes on <var>router</var>.
+ </dd>
</dl>
<h1>Database Commands</h1>
@@ -350,6 +350,13 @@ Logical router port commands:\n\
lrp-get-enabled PORT get administrative state PORT\n\
('enabled' or 'disabled')\n\
\n\
+Route commands:\n\
+ lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\
+ add a route to ROUTER\n\
+ lr-route-del ROUTER [PREFIX]\n\
+ remove routes from ROUTER\n\
+ lr-route-list ROUTER print routes for ROUTER\n\
+\n\
%s\
\n\
Options:\n\
@@ -1269,6 +1276,82 @@ nbctl_lr_list(struct ctl_context *ctx)
free(nodes);
}
+static void
+nbctl_lr_route_add(struct ctl_context *ctx)
+{
+ const struct nbrec_logical_router *lr;
+ lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
+ unsigned int plen;
+ ovs_be32 ipv4;
+ char *error;
+
+ error = ip_parse_cidr(ctx->argv[2], &ipv4, &plen);
+ if (!error) {
+ if (!ip_parse(ctx->argv[3], &ipv4)) {
+ ctl_fatal("bad IPv4 nexthop argument: %s", ctx->argv[3]);
+ }
+ } else {
+ free(error);
+
+ struct in6_addr ipv6;
+ error = ipv6_parse_cidr(ctx->argv[2], &ipv6, &plen);
+ if (!error) {
+ if (!ipv6_parse(ctx->argv[3], &ipv6)) {
+ ctl_fatal("bad IPv6 nexthop argument: %s", ctx->argv[3]);
+ }
+ } else {
+ ctl_fatal("bad prefix argument: %s", error);
+ }
+ }
+
+ struct nbrec_logical_router_static_route *route;
+ route = nbrec_logical_router_static_route_insert(ctx->txn);
+ nbrec_logical_router_static_route_set_ip_prefix(route, ctx->argv[2]);
+ nbrec_logical_router_static_route_set_nexthop(route, ctx->argv[3]);
+ if (ctx->argc == 5) {
+ nbrec_logical_router_static_route_set_output_port(route, ctx->argv[4]);
+ }
+
+ nbrec_logical_router_verify_static_routes(lr);
+ struct nbrec_logical_router_static_route **new_routes
+ = xmalloc(sizeof *new_routes * (lr->n_static_routes + 1));
+ memcpy(new_routes, lr->static_routes,
+ sizeof *new_routes * lr->n_static_routes);
+ new_routes[lr->n_static_routes] = route;
+ nbrec_logical_router_set_static_routes(lr, new_routes,
+ lr->n_static_routes + 1);
+ free(new_routes);
+}
+
+static void
+nbctl_lr_route_del(struct ctl_context *ctx)
+{
+ const struct nbrec_logical_router *lr;
+ lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+ if (ctx->argc == 2) {
+ /* If a prefix is not specified, delete all routes. */
+ nbrec_logical_router_verify_static_routes(lr);
+ nbrec_logical_router_set_static_routes(lr, NULL, 0);
+ return;
+ }
+
+ for (int i = 0; i < lr->n_static_routes; i++) {
+ if (!strcmp(lr->static_routes[i]->ip_prefix, ctx->argv[2])) {
+ struct nbrec_logical_router_static_route **new_routes
+ = xmemdup(lr->static_routes,
+ sizeof *new_routes * lr->n_static_routes);
+
+ new_routes[i] = lr->static_routes[lr->n_static_routes - 1];
+ nbrec_logical_router_verify_static_routes(lr);
+ nbrec_logical_router_set_static_routes(lr, new_routes,
+ lr->n_static_routes - 1);
+ free(new_routes);
+ return;
+ }
+ }
+}
+
static const struct nbrec_logical_router_port *
lrp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist)
{
@@ -1358,9 +1441,9 @@ nbctl_lrp_add(struct ctl_context *ctx)
ctl_fatal("%s: invalid mac address.", ctx->argv[3]);
}
- ovs_be32 ip;
+ ovs_be32 ipv4;
unsigned int plen;
- char *error = ip_parse_cidr(ctx->argv[4], &ip, &plen);
+ char *error = ip_parse_cidr(ctx->argv[4], &ipv4, &plen);
if (error) {
free(error);
struct in6_addr ipv6;
@@ -1501,6 +1584,125 @@ nbctl_lrp_get_enabled(struct ctl_context *ctx)
*lrp->enabled ? "enabled" : "disabled");
}
+struct ipv4_route {
+ int plen;
+ ovs_be32 addr;
+ const struct nbrec_logical_router_static_route *route;
+};
+
+static int
+ipv4_route_cmp(const void *route1_, const void *route2_)
+{
+ const struct ipv4_route *route1p = route1_;
+ const struct ipv4_route *route2p = route2_;
+
+ if (route1p->plen != route2p->plen) {
+ return route1p->plen > route2p->plen ? -1 : 1;
+ } else if (route1p->addr != route2p->addr) {
+ return route1p->addr < route2p->addr ? -1 : 1;
+ } else {
+ return 0;
+ }
+}
+
+struct ipv6_route {
+ int plen;
+ struct in6_addr addr;
+ const struct nbrec_logical_router_static_route *route;
+};
+
+static int
+ipv6_route_cmp(const void *route1_, const void *route2_)
+{
+ const struct ipv6_route *route1p = route1_;
+ const struct ipv6_route *route2p = route2_;
+
+ if (route1p->plen != route2p->plen) {
+ return route1p->plen > route2p->plen ? -1 : 1;
+ }
+ return memcmp(&route1p->addr, &route2p->addr, sizeof(route1p->addr));
+}
+
+static void
+nbctl_lr_route_list(struct ctl_context *ctx)
+{
+ const struct nbrec_logical_router *lr;
+ struct ipv4_route *ipv4_routes;
+ struct ipv6_route *ipv6_routes;
+ size_t n_ipv4_routes = 0;
+ size_t n_ipv6_routes = 0;
+
+ lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+ 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];
+ unsigned int plen;
+ ovs_be32 ipv4;
+ char *error;
+
+ error = ip_parse_cidr(route->ip_prefix, &ipv4, &plen);
+ if (!error) {
+ ipv4_routes[n_ipv4_routes].plen = plen;
+ ipv4_routes[n_ipv4_routes].addr = ipv4;
+ ipv4_routes[n_ipv4_routes].route = route;
+ n_ipv4_routes++;
+ } else {
+ free(error);
+
+ struct in6_addr ipv6;
+ if (!ipv6_parse_cidr(route->ip_prefix, &ipv6, &plen)) {
+ ipv6_routes[n_ipv6_routes].plen = plen;
+ ipv6_routes[n_ipv6_routes].addr = ipv6;
+ ipv6_routes[n_ipv6_routes].route = route;
+ n_ipv6_routes++;
+ } else {
+ /* Invalid prefix. */
+ free(error);
+ continue;
+ }
+ }
+ }
+
+ qsort(ipv4_routes, n_ipv4_routes, sizeof *ipv4_routes, ipv4_route_cmp);
+ qsort(ipv6_routes, n_ipv6_routes, sizeof *ipv6_routes, ipv6_route_cmp);
+
+ if (n_ipv4_routes) {
+ ds_put_cstr(&ctx->output, "IPv4 Routes\n");
+ }
+ for (int i = 0; i < n_ipv4_routes; i++) {
+ const struct nbrec_logical_router_static_route *route
+ = ipv4_routes[i].route;
+ ds_put_format(&ctx->output, "%25s %25s", route->ip_prefix,
+ route->nexthop);
+ if (route->output_port) {
+ ds_put_format(&ctx->output, " %s", route->output_port);
+ }
+ ds_put_char(&ctx->output, '\n');
+ }
+
+ if (n_ipv6_routes) {
+ ds_put_format(&ctx->output, "%sIPv6 Routes\n",
+ n_ipv4_routes ? "\n" : "");
+ }
+ for (int i = 0; i < n_ipv6_routes; i++) {
+ const struct nbrec_logical_router_static_route *route
+ = ipv6_routes[i].route;
+ ds_put_format(&ctx->output, "%25s %25s", route->ip_prefix,
+ route->nexthop);
+ if (route->output_port) {
+ ds_put_format(&ctx->output, " %s", route->output_port);
+ }
+ ds_put_char(&ctx->output, '\n');
+ }
+
+ free(ipv4_routes);
+ free(ipv6_routes);
+}
+
static const struct ctl_table_class tables[] = {
{&nbrec_table_logical_switch,
{{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL},
@@ -1796,6 +1998,14 @@ static const struct ctl_command_syntax nbctl_commands[] = {
{ "lrp-get-enabled", 1, 1, "PORT", NULL, nbctl_lrp_get_enabled,
NULL, "", RO },
+ /* logical router route commands. */
+ { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL,
+ nbctl_lr_route_add, NULL, "", RW },
+ { "lr-route-del", 1, 2, "ROUTER [PREFIX]", NULL, nbctl_lr_route_del,
+ NULL, "", RW },
+ { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL,
+ "", RO },
+
{NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO},
};
@@ -351,3 +351,81 @@ AT_CHECK([ovn-nbctl lrp-set-enabled lrp0 xyzzy], [1], [],
OVN_NBCTL_TEST_STOP
AT_CLEANUP
+
+dnl ---------------------------------------------------------------------
+
+AT_SETUP([ovn-nbctl - routes])
+OVN_NBCTL_TEST_START
+
+AT_CHECK([ovn-nbctl lr-add lr0])
+
+dnl Check IPv4 routes
+AT_CHECK([ovn-nbctl lr-route-add lr0 0.0.0.0/0 192.168.0.1])
+AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0])
+AT_CHECK([ovn-nbctl 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
+ 10.0.0.1/24 11.0.0.1
+ 10.0.1.1/24 11.0.1.1 lp0
+ 0.0.0.0/0 192.168.0.1
+])
+
+AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.1.1/24])
+
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+IPv4 Routes
+ 10.0.0.1/24 11.0.0.1
+ 0.0.0.0/0 192.168.0.1
+])
+
+AT_CHECK([ovn-nbctl lr-route-del lr0])
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+])
+
+dnl Check IPv6 routes
+AT_CHECK([ovn-nbctl lr-route-add lr0 ::/0 2001:0db8:0:f101::1])
+AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0])
+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
+ 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0
+ 2001:0db8:1::/64 2001:0db8:0:f103::1
+ ::/0 2001:0db8:0:f101::1
+])
+
+AT_CHECK([ovn-nbctl lr-route-del lr0 2001:0db8:0::/64])
+
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+IPv6 Routes
+ 2001:0db8:1::/64 2001:0db8:0:f103::1
+ ::/0 2001:0db8:0:f101::1
+])
+
+AT_CHECK([ovn-nbctl lr-route-del lr0])
+AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
+])
+
+dnl Check IPv4 and IPv6 routes
+AT_CHECK([ovn-nbctl lr-route-add lr0 0.0.0.0/0 192.168.0.1])
+AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0])
+AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.1/24 11.0.0.1])
+AT_CHECK([ovn-nbctl lr-route-add lr0 ::/0 2001:0db8:0:f101::1])
+AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0])
+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
+IPv4 Routes
+ 10.0.0.1/24 11.0.0.1
+ 10.0.1.1/24 11.0.1.1 lp0
+ 0.0.0.0/0 192.168.0.1
+
+IPv6 Routes
+ 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0
+ 2001:0db8:1::/64 2001:0db8:0:f103::1
+ ::/0 2001:0db8:0:f101::1
+])
+
+OVN_NBCTL_TEST_STOP
+AT_CLEANUP
@@ -2384,17 +2384,9 @@ ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 20.0.0.1/24 R2_R1
ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 20.0.0.2/24 R1_R2
#install static routes
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
-R1 static_routes @lrt
-
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=172.16.2.0/24 nexthop=20.0.0.2 output_port=R1_R2 -- add Logical_Router \
-R1 static_routes @lrt
-
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
-R2 static_routes @lrt
+ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
+ovn-nbctl lr-route-add R2 172.16.2.0/24 20.0.0.2 R1_R2
+ovn-nbctl lr-route-add R2 192.168.1.0/24 20.0.0.1
# Create logical port foo1 in foo
ovn-nbctl lport-add foo foo1 \
@@ -2649,29 +2641,14 @@ ovn-nbctl lport-add join r3-join -- set Logical_port r3-join type=router \
options:router-port=R3_join addresses='"00:00:04:01:02:05"'
#install static routes
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
-R1 static_routes @lrt
+ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
+ovn-nbctl lr-route-add R1 10.32.1.0/24 20.0.0.3
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=10.32.1.0/24 nexthop=20.0.0.3 -- add Logical_Router \
-R1 static_routes @lrt
+ovn-nbctl lr-route-add R2 192.168.1.0/24 20.0.0.1
+ovn-nbctl lr-route-add R2 10.32.1.0/24 20.0.0.3
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
-R2 static_routes @lrt
-
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=10.32.1.0/24 nexthop=20.0.0.3 -- add Logical_Router \
-R2 static_routes @lrt
-
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
-R3 static_routes @lrt
-
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
-R3 static_routes @lrt
+ovn-nbctl lr-route-add R3 192.168.1.0/24 20.0.0.1
+ovn-nbctl lr-route-add R3 172.16.1.0/24 20.0.0.2
# Create logical port foo1 in foo
ovn-nbctl lport-add foo foo1 \
Signed-off-by: Justin Pettit <jpettit@ovn.org> --- ovn/utilities/ovn-nbctl.8.xml | 26 +++++ ovn/utilities/ovn-nbctl.c | 214 +++++++++++++++++++++++++++++++++++++++++- tests/ovn-nbctl.at | 78 +++++++++++++++ tests/ovn.at | 41 ++------ 4 files changed, 325 insertions(+), 34 deletions(-)