@@ -331,25 +331,45 @@
</dd>
</dl>
- <h1>Logical Route Commands</h1>
+ <h1>Logical Router Static Route Commands</h1>
<dl>
- <dt><code>lr-route-add</code> <var>router</var> <var>prefix</var> <var>ne
+ <dt>[<code>--may-exist</code>] <code>lr-route-add</code> <var>router</var
<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.
+ <p>
+ Adds the specified route to <var>router</var>.
+ <var>prefix</var> describes an IPv4 or IPv6 prefix for this
+ route, such as <code>192.168.100.0/24</code>.
+ <var>nexthop</var> specifies the gateway to use for this
+ route, which should be the IP address of one of
+ <var>router</var> logical router ports or the IP address of a
+ logical port. If <var>port</var> is specified, packets that
+ match this route will be sent out that port. When
+ <var>port</var> is omitted, OVN infers the output port based
+ on <var>nexthop</var>.
+ </p>
+
+ <p>
+ It is an error if a route with <var>prefix</var> already exists,
+ unless <code>--may-exist</code> is specified.
+ </p>
</dd>
- <dt><code>lr-route-del</code> <var>router</var> [<var>prefix</var>]</dt>
+ <dt>[<code>--if-exists</code>] <code>lr-route-del</code> <var>router</var
<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.
+ <p>
+ 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.
+ </p>
+
+ <p>
+ It is an error if <code>prefix</code> is specified and there
+ is no matching route entry, unless <code>--if-exists</code> is
+ specified.
+ </p>
</dd>
<dt><code>lr-route-list</code> <var>router</var></dt>
@@ -1275,39 +1275,123 @@ nbctl_lr_list(struct ctl_context *ctx)
smap_destroy(&lrs);
free(nodes);
}
+
+/* The caller must free the returned string. */
+static char *
+normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen)
+{
+ ovs_be32 network = ipv4 & be32_prefix_mask(plen);
+ if (plen == 32) {
+ return xasprintf(IP_FMT, IP_ARGS(network));
+ } else {
+ return xasprintf(IP_FMT"/%d", IP_ARGS(network), plen);
+ }
+}
+
+/* The caller must free the returned string. */
+static char *
+normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen)
+{
+ char network_s[INET6_ADDRSTRLEN];
+
+ struct in6_addr mask = ipv6_create_mask(plen);
+ struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask);
+
+ inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN);
+ if (plen == 128) {
+ return xasprintf("%s", network_s);
+ } else {
+ return xasprintf("%s/%d", network_s, plen);
+ }
+}
+
+/* The caller must free the returned string. */
+static char *
+normalize_prefix_str(const char *orig_prefix)
+{
+ unsigned int plen;
+ ovs_be32 ipv4;
+ char *error;
+
+ error = ip_parse_cidr(orig_prefix, &ipv4, &plen);
+ if (!error) {
+ return normalize_ipv4_prefix(ipv4, plen);
+ } else {
+ struct in6_addr ipv6;
+ free(error);
+
+ error = ipv6_parse_cidr(orig_prefix, &ipv6, &plen);
+ if (error) {
+ free(error);
+ return NULL;
+ }
+ return normalize_ipv6_prefix(ipv6, plen);
+ }
+}
^L
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;
+ char *prefix, *next_hop;
- error = ip_parse_cidr(ctx->argv[2], &ipv4, &plen);
- if (!error) {
- if (!ip_parse(ctx->argv[3], &ipv4)) {
+ prefix = normalize_prefix_str(ctx->argv[2]);
+ if (!prefix) {
+ ctl_fatal("bad prefix argument: %s", ctx->argv[2]);
+ }
+
+ next_hop = normalize_prefix_str(ctx->argv[3]);
+ if (!next_hop) {
+ ctl_fatal("bad next hop argument: %s", ctx->argv[3]);
+ }
+
+ if (strchr(prefix, '.')) {
+ ovs_be32 hop_ipv4;
+ if (!ip_parse(ctx->argv[3], &hop_ipv4)) {
ctl_fatal("bad IPv4 nexthop argument: %s", ctx->argv[3]);
}
} else {
- free(error);
+ struct in6_addr hop_ipv6;
+ if (!ipv6_parse(ctx->argv[3], &hop_ipv6)) {
+ ctl_fatal("bad IPv6 nexthop argument: %s", ctx->argv[3]);
+ }
+ }
- 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);
+ bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+ for (int i = 0; i < lr->n_static_routes; i++) {
+ char *rt_prefix;
+
+ rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix);
+ if (!rt_prefix) {
+ /* Ignore existing prefix we couldn't parse. */
+ continue;
+ }
+
+ if (strcmp(rt_prefix, prefix)) {
+ free(rt_prefix);
+ continue;
}
+
+ if (!may_exist) {
+ ctl_fatal("duplicate prefix: %s", prefix);
+ }
+
+ /* Update the next hop for an existing route. */
+ nbrec_logical_router_static_route_set_ip_prefix(lr->static_routes[i],
+ prefix);
+ nbrec_logical_router_static_route_set_nexthop(lr->static_routes[i],
+ next_hop);
+ free(rt_prefix);
+ free(next_hop);
+ free(prefix);
+ return;
}
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]);
+ nbrec_logical_router_static_route_set_ip_prefix(route, prefix);
+ nbrec_logical_router_static_route_set_nexthop(route, next_hop);
if (ctx->argc == 5) {
nbrec_logical_router_static_route_set_output_port(route, ctx->argv[4]);
}
@@ -1321,6 +1405,8 @@ nbctl_lr_route_add(struct ctl_context *ctx)
nbrec_logical_router_set_static_routes(lr, new_routes,
lr->n_static_routes + 1);
free(new_routes);
+ free(next_hop);
+ free(prefix);
}
static void
@@ -1331,13 +1417,23 @@ nbctl_lr_route_del(struct ctl_context *ctx)
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;
}
+ char *prefix = normalize_prefix_str(ctx->argv[2]);
+ if (!prefix) {
+ ctl_fatal("bad prefix argument: %s", ctx->argv[2]);
+ }
+
for (int i = 0; i < lr->n_static_routes; i++) {
- if (!strcmp(lr->static_routes[i]->ip_prefix, ctx->argv[2])) {
+ char *rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix)
+ if (!rt_prefix) {
+ /* Ignore existing prefix we couldn't parse. */
+ continue;
+ }
+
+ if (!strcmp(prefix, rt_prefix)) {
struct nbrec_logical_router_static_route **new_routes
= xmemdup(lr->static_routes,
sizeof *new_routes * lr->n_static_routes);
@@ -1347,9 +1443,17 @@ nbctl_lr_route_del(struct ctl_context *ctx)
nbrec_logical_router_set_static_routes(lr, new_routes,
lr->n_static_routes - 1);
free(new_routes);
+ free(rt_prefix);
+ free(prefix);
return;
}
+ free(rt_prefix);
+ }
+
+ if (!shash_find(&ctx->options, "--if-exists")) {
+ ctl_fatal("no matching prefix: %s", prefix);
}
+ free(prefix);
}
^L
static const struct nbrec_logical_router_port *
@@ -1624,6 +1728,22 @@ ipv6_route_cmp(const void *route1_, const void *route2_)
}
static void
+print_route(const struct nbrec_logical_router_static_route *route, struct ds *s
+{
+
+ char *prefix = normalize_prefix_str(route->ip_prefix);
+ char *next_hop = normalize_prefix_str(route->nexthop);
+ ds_put_format(s, "%25s %25s", prefix, next_hop);
+ free(prefix);
+ free(next_hop);
+
+ if (route->output_port) {
+ ds_put_format(s, " %s", route->output_port);
+ }
+ ds_put_char(s, '\n');
+}
+
+static void
nbctl_lr_route_list(struct ctl_context *ctx)
{
const struct nbrec_logical_router *lr;
@@ -1661,6 +1781,9 @@ nbctl_lr_route_list(struct ctl_context *ctx)
n_ipv6_routes++;
} else {
/* Invalid prefix. */
+ VLOG_WARN("router "UUID_FMT" (%s) has invalid prefix: %s",
+ UUID_ARGS(&lr->header_.uuid), lr->name,
+ route->ip_prefix);
free(error);
continue;
}
@@ -1674,14 +1797,7 @@ nbctl_lr_route_list(struct ctl_context *ctx)
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');
+ print_route(ipv4_routes[i].route, &ctx->output);
}
if (n_ipv6_routes) {
@@ -1689,14 +1805,7 @@ nbctl_lr_route_list(struct ctl_context *ctx)
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');
+ print_route(ipv6_routes[i].route, &ctx->output);
}
free(ipv4_routes);
@@ -2000,9 +2109,9 @@ static const struct ctl_command_syntax nbctl_commands[] =
/* logical router route commands. */
{ "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL,
- nbctl_lr_route_add, NULL, "", RW },
+ nbctl_lr_route_add, NULL, "--may-exist", RW },
{ "lr-route-del", 1, 2, "ROUTER [PREFIX]", NULL, nbctl_lr_route_del,
- NULL, "", RW },
+ NULL, "--if-exists", RW },
{ "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL,
"", RO },
@@ -361,21 +361,33 @@ 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-add lr0 10.0.1.0/24 11.0.1.1 lp0])
+AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.1/24 11.0.0.2])
+
+dnl Add overlapping route with 10.0.0.1/24
+AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24 11.0.0.1], [1], [],
+ [ovn-nbctl: duplicate prefix: 10.0.0.0/24
+])
+AT_CHECK([ovn-nbctl --may-exist lr-route-add lr0 10.0.0.111/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
+ 10.0.0.0/24 11.0.0.1
+ 10.0.1.0/24 11.0.1.1 lp0
0.0.0.0/0 192.168.0.1
])
+dnl Delete non-existent prefix
+AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.2.1/24], [1], [],
+ [ovn-nbctl: no matching prefix: 10.0.2.0/24
+])
+AT_CHECK([ovn-nbctl --if-exists lr-route-del lr0 10.0.2.1/24])
+
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
+ 10.0.0.0/24 11.0.0.1
0.0.0.0/0 192.168.0.1
])
@@ -390,17 +402,17 @@ AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001
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
+ 2001:db8::/64 2001:db8:0:f102::1 lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1
+ ::/0 2001:db8: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
+ 2001:db8:1::/64 2001:db8:0:f103::1
+ ::/0 2001:db8:0:f101::1
])
AT_CHECK([ovn-nbctl lr-route-del lr0])
@@ -417,14 +429,14 @@ AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001
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
+ 10.0.0.0/24 11.0.0.1
+ 10.0.1.0/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
+ 2001:db8::/64 2001:db8:0:f102::1 lp0
+ 2001:db8:1::/64 2001:db8:0:f103::1
+ ::/0 2001:db8:0:f101::1
])
OVN_NBCTL_TEST_STOP