Signed-off-by: Justin Pettit <jpet...@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(-)
diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml index c085274..ba6a1ed 100644 --- a/ovn/utilities/ovn-nbctl.8.xml +++ b/ovn/utilities/ovn-nbctl.8.xml @@ -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> diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index 493f1e8..eeed70f 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -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}, }; diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 2e8fc17..73792b3 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -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 diff --git a/tests/ovn.at b/tests/ovn.at index 0b9d3d1..652f0a6 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -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 \ -- 1.9.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev