[ovs-dev] [PATCH 5/8] ovn-nbctl: Add static route commands.

Justin Pettit jpettit at ovn.org
Fri Jun 10 23:09:31 UTC 2016


> On Jun 9, 2016, at 3:17 PM, Ben Pfaff <blp at ovn.org> wrote:
> 
> On Thu, Jun 09, 2016 at 12:12:39AM -0700, Justin Pettit wrote:
>> Signed-off-by: Justin Pettit <jpettit at ovn.org>
> 
> I think the documentation can be improved.  Suggested incremental
> appended.
> 
> It's not actually necessary to call
> nbrec_logical_router_verify_static_routes() when deleting all routes,
> because the result of deleting all of them does not depend on what was
> there before.  (This comment is petty.)

Always happy to delete some code.  Thanks.

> Most of our *-add and *-del commands fail, by default, if there is
> already a duplicate or if there is nothing matching to delete,
> respectively, unless given --may-exist or --if-exists, respectively, but
> these new commands do not behave that way.  (In particular adding a
> duplicate static route seems like annoying behavior.)

That's fair.  While adding this functionality, I decided to normalize the IP addresses, too, which makes the output more consistent.

> It would be nice for lr-route-list to warn about an invalid route
> prefix.  Just ignoring them is likely to confuse users.

Good suggestion.

This was a pretty invasive change, so I've appended an incremental for a sanity check.  Let me know if you'd prefer me to just send it as a v2.

--Justin


-=-=-=-=-=-=-=-

diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
index ba6a1ed..ab166b7 100644
--- a/ovn/utilities/ovn-nbctl.8.xml
+++ b/ovn/utilities/ovn-nbctl.8.xml
@@ -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>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index eeed70f..8656e10 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -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 },
 
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
index 73792b3..74855b0 100644
--- a/tests/ovn-nbctl.at
+++ b/tests/ovn-nbctl.at
@@ -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





More information about the dev mailing list