Whamcloud - gitweb
LU-10391 lnet: migrate router management to Netlink 54/50254/31
authorJames Simmons <jsimmons@infradead.org>
Mon, 7 Aug 2023 13:23:15 +0000 (09:23 -0400)
committerOleg Drokin <green@whamcloud.com>
Fri, 3 Nov 2023 04:01:03 +0000 (04:01 +0000)
Add the doit Netlink handling for routes. The creation and
deletion of routes can now work with large NIDs using the
Netlink API.

Test-Parameters: trivial testlist=sanity-lnet
Change-Id: Ia9fc64785962ce3e74abeef9c77e8b2df0789dd7
Signed-off-by: James Simmons <jsimmons@infradead.org>
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/50254
Reviewed-by: Frank Sehr <fsehr@whamcloud.com>
Reviewed-by: Chris Horn <chris.horn@hpe.com>
Reviewed-by: Cyril Bordage <cbordage@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
lnet/lnet/api-ni.c
lnet/utils/lnetctl.c

index c36f004..c41ca00 100644 (file)
@@ -6402,8 +6402,14 @@ static int lnet_route_show_start(struct netlink_callback *cb)
                                                               "failed to get route param");
                                                GOTO(report_err, rc);
                                        }
+
+                                       rc = libcfs_strnid(&tmp.lrp_gateway, strim(gw));
+                                       if (rc < 0) {
+                                               NL_SET_ERR_MSG(extack,
+                                                              "cannot parse gateway");
+                                               GOTO(report_err, rc = -ENODEV);
+                                       }
                                        rc = 0;
-                                       libcfs_strnid(&tmp.lrp_gateway, strim(gw));
                                } else if (nla_strcmp(route, "hop") == 0) {
                                        route = nla_next(route, &rem2);
                                        if (nla_type(route) !=
@@ -7210,6 +7216,214 @@ static int lnet_old_peer_ni_show_dump(struct sk_buff *msg,
 }
 #endif
 
+static int lnet_route_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nlmsghdr *nlh = nlmsg_hdr(skb);
+       struct genlmsghdr *gnlh = nlmsg_data(nlh);
+       struct nlattr *params = genlmsg_data(gnlh);
+       int msg_len, rem, rc = 0;
+       struct nlattr *attr;
+
+       mutex_lock(&the_lnet.ln_api_mutex);
+       if (the_lnet.ln_state != LNET_STATE_RUNNING) {
+               GENL_SET_ERR_MSG(info, "Network is down");
+               mutex_unlock(&the_lnet.ln_api_mutex);
+               return -ENETDOWN;
+       }
+
+       msg_len = genlmsg_len(gnlh);
+       if (!msg_len) {
+               GENL_SET_ERR_MSG(info, "no configuration");
+               mutex_unlock(&the_lnet.ln_api_mutex);
+               return -ENOMSG;
+       }
+
+       if (!(nla_type(params) & LN_SCALAR_ATTR_LIST)) {
+               GENL_SET_ERR_MSG(info, "invalid configuration");
+               mutex_unlock(&the_lnet.ln_api_mutex);
+               return -EINVAL;
+       }
+
+       nla_for_each_nested(attr, params, rem) {
+               u32 net_id = LNET_NET_ANY, hops = LNET_UNDEFINED_HOPS;
+               u32 priority = 0, sensitivity = 1;
+               struct lnet_nid gw_nid = LNET_ANY_NID;
+               struct nlattr *route_prop;
+               int rem2;
+
+               if (nla_type(attr) != LN_SCALAR_ATTR_LIST)
+                       continue;
+
+               nla_for_each_nested(route_prop, attr, rem2) {
+                       char tmp[LNET_NIDSTR_SIZE];
+                       ssize_t len;
+                       s64 num;
+
+                       if (nla_type(route_prop) != LN_SCALAR_ATTR_VALUE)
+                               continue;
+
+                       if (nla_strcmp(route_prop, "net") == 0) {
+                               route_prop = nla_next(route_prop, &rem2);
+                               if (nla_type(route_prop) !=
+                                   LN_SCALAR_ATTR_VALUE) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "net is invalid key");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+
+                               len = nla_strscpy(tmp, route_prop, sizeof(tmp));
+                               if (len < 0) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "net key string is invalid");
+                                       GOTO(report_err, rc = len);
+                               }
+
+                               net_id = libcfs_str2net(tmp);
+                               if (!net_id) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "cannot parse remote net");
+                                       GOTO(report_err, rc = -ENODEV);
+                               }
+
+                               if (LNET_NETTYP(net_id) == LOLND) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "setting @lo not allowed");
+                                       GOTO(report_err, rc = -EACCES);
+                               }
+
+                               if (net_id == LNET_NET_ANY) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "setting LNET_NET_ANY not allowed");
+                                       GOTO(report_err, rc = -ENXIO);
+                               }
+                       } else if (nla_strcmp(route_prop, "gateway") == 0) {
+                               route_prop = nla_next(route_prop, &rem2);
+                               if (nla_type(route_prop) !=
+                                   LN_SCALAR_ATTR_VALUE) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "gateway is invalid key");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+
+                               len = nla_strscpy(tmp, route_prop, sizeof(tmp));
+                               if (len < 0) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "gateway string is invalid");
+                                       GOTO(report_err, rc = len);
+                               }
+
+                               rc = libcfs_strnid(&gw_nid, strim(tmp));
+                               if (rc < 0) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "cannot parse gateway");
+                                       GOTO(report_err, rc = -ENODEV);
+                               }
+                       } else if (nla_strcmp(route_prop, "hop") == 0) {
+                               route_prop = nla_next(route_prop, &rem2);
+                               if (nla_type(route_prop) !=
+                                   LN_SCALAR_ATTR_INT_VALUE) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "hop has invalid key");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+
+                               hops = nla_get_s64(route_prop);
+                               if (hops < 1 || hops > 255) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "invalid hop count must be between 1 and 255");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+                       } else if (nla_strcmp(route_prop, "priority") == 0) {
+                               route_prop = nla_next(route_prop, &rem2);
+                               if (nla_type(route_prop) !=
+                                   LN_SCALAR_ATTR_INT_VALUE) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "priority has invalid key");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+
+                               num = nla_get_s64(route_prop);
+                               if (num < 0) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "invalid priority, must not be negative");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+                               priority = num;
+                       } else if (nla_strcmp(route_prop,
+                                             "health_sensitivity") == 0) {
+                               route_prop = nla_next(route_prop, &rem2);
+                               if (nla_type(route_prop) !=
+                                   LN_SCALAR_ATTR_INT_VALUE) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "sensitivity has invalid key");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+
+                               num = nla_get_s64(route_prop);
+                               if (num < 1) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "invalid health sensitivity, must be 1 or greater");
+                                       GOTO(report_err, rc = -EINVAL);
+                               }
+                               sensitivity = num;
+                       }
+               }
+
+               if (net_id == LNET_NET_ANY) {
+                       GENL_SET_ERR_MSG(info,
+                                        "missing mandatory parameter: network");
+                       GOTO(report_err, rc = -ENODEV);
+               }
+
+               if (LNET_NID_IS_ANY(&gw_nid)) {
+                       GENL_SET_ERR_MSG(info,
+                                        "missing mandatory parameter: gateway");
+                       GOTO(report_err, rc = -ENODEV);
+               }
+
+               if (info->nlhdr->nlmsg_flags & NLM_F_CREATE) {
+                       rc = lnet_add_route(net_id, hops, &gw_nid, priority,
+                                           sensitivity);
+                       if (rc < 0) {
+                               switch (rc) {
+                               case -EINVAL:
+                                       GENL_SET_ERR_MSG(info,
+                                                        "invalid settings for route creation");
+                                       break;
+                               case -EHOSTUNREACH:
+                                       GENL_SET_ERR_MSG(info,
+                                                        "No interface configured on the same net as gateway");
+                                       break;
+                               case -ESHUTDOWN:
+                                       GENL_SET_ERR_MSG(info,
+                                                        "Network is down");
+                                       break;
+                               case -EEXIST:
+                                       GENL_SET_ERR_MSG(info,
+                                                        "Route already exists or the specified network is local");
+                                       break;
+                               default:
+                                       GENL_SET_ERR_MSG(info,
+                                                        "failed to create route");
+                                       break;
+                               }
+                               GOTO(report_err, rc);
+                       }
+               } else if (!(info->nlhdr->nlmsg_flags & NLM_F_CREATE)) {
+                       rc = lnet_del_route(net_id, &gw_nid);
+                       if (rc < 0) {
+                               GENL_SET_ERR_MSG(info,
+                                                "failed to delete route");
+                               GOTO(report_err, rc);
+                       }
+               }
+       }
+report_err:
+       mutex_unlock(&the_lnet.ln_api_mutex);
+
+       return rc;
+}
+
 static inline struct lnet_genl_ping_list *
 lnet_ping_dump_ctx(struct netlink_callback *cb)
 {
@@ -7594,6 +7808,7 @@ static const struct genl_ops lnet_genl_ops[] = {
        },
        {
                .cmd            = LNET_CMD_ROUTES,
+               .flags          = GENL_ADMIN_PERM,
 #ifdef HAVE_NETLINK_CALLBACK_START
                .start          = lnet_route_show_start,
                .dumpit         = lnet_route_show_dump,
@@ -7601,6 +7816,7 @@ static const struct genl_ops lnet_genl_ops[] = {
                .dumpit         = lnet_old_route_show_dump,
 #endif
                .done           = lnet_route_show_done,
+               .doit           = lnet_route_cmd,
        },
        {
                .cmd            = LNET_CMD_PING,
index 8ed5755..2088d5f 100644 (file)
@@ -1134,9 +1134,135 @@ static int jt_unconfig_lnet(int argc, char **argv)
        return rc;
 }
 
+static int yaml_lnet_router_gateways(yaml_emitter_t *output, const char *nw,
+                                    const char *gw, int hops, int prio,
+                                    int sen)
+{
+       char num[INT_STRING_LEN];
+       yaml_event_t event;
+       int rc;
+
+       yaml_mapping_start_event_initialize(&event, NULL,
+                                           (yaml_char_t *)YAML_MAP_TAG, 1,
+                                           YAML_BLOCK_MAPPING_STYLE);
+       rc = yaml_emitter_emit(output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       if (nw) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"net",
+                                            strlen("net"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)nw,
+                                            strlen(nw), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+       }
+
+       if (gw) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"gateway",
+                                            strlen("gateway"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)gw,
+                                            strlen(gw), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+       }
+
+       if (hops != -1) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"hop",
+                                            strlen("hop"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               snprintf(num, sizeof(num), "%d", hops);
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+       }
+
+       if (prio != -1) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"priority",
+                                            strlen("priority"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               snprintf(num, sizeof(num), "%d", prio);
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+       }
+
+       if (sen != -1) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"health_sensitivity",
+                                            strlen("health_sensitivity"),
+                                            1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               snprintf(num, sizeof(num), "%d", sen);
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+       }
+
+       yaml_mapping_end_event_initialize(&event);
+       rc = yaml_emitter_emit(output, &event);
+emitter_error:
+       return rc;
+}
+
 static int yaml_lnet_route(char *nw, char *gw, int hops, int prio, int sen,
                           int version, int flags)
 {
+       struct nid_node head, *entry;
        struct nl_sock *sk = NULL;
        const char *msg = NULL;
        yaml_emitter_t output;
@@ -1144,6 +1270,13 @@ static int yaml_lnet_route(char *nw, char *gw, int hops, int prio, int sen,
        yaml_event_t event;
        int rc;
 
+       if (!(flags & NLM_F_DUMP) && (!nw || !gw)) {
+               fprintf(stdout, "missing mandatory parameters:'%s'\n",
+                       (!nw && !gw) ? "net , gateway" :
+                       !nw ? "net" : "gateway");
+               return -EINVAL;
+       }
+
        /* Create Netlink emitter to send request to kernel */
        sk = nl_socket_alloc();
        if (!sk)
@@ -1196,8 +1329,20 @@ static int yaml_lnet_route(char *nw, char *gw, int hops, int prio, int sen,
        if (rc == 0)
                goto emitter_error;
 
-       if (nw || gw || hops != -1 || prio != -1) {
-               char num[INT_STRING_LEN];
+       /* NLM_F_DUMP can have no arguments */
+       if (nw || gw) {
+               NL_INIT_LIST_HEAD(&head.children);
+               nl_init_list_head(&head.list);
+               if (gw) {
+                       rc = lustre_lnet_parse_nid_range(&head, gw, &msg);
+                       if (rc < 0) {
+                               lustre_lnet_free_list(&head);
+                               yaml_emitter_delete(&output);
+                               errno = rc;
+                               rc = 0;
+                               goto free_reply;
+                       }
+               }
 
                yaml_sequence_start_event_initialize(&event, NULL,
                                                     (yaml_char_t *)YAML_SEQ_TAG,
@@ -1207,171 +1352,18 @@ static int yaml_lnet_route(char *nw, char *gw, int hops, int prio, int sen,
                if (rc == 0)
                        goto emitter_error;
 
-               if (nw) {
-                       yaml_mapping_start_event_initialize(&event, NULL,
-                                                           (yaml_char_t *)YAML_MAP_TAG,
-                                                           1,
-                                                           YAML_BLOCK_MAPPING_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_STR_TAG,
-                                                    (yaml_char_t *)"net",
-                                                    strlen("net"), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_STR_TAG,
-                                                    (yaml_char_t *)nw,
-                                                    strlen(nw), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_mapping_end_event_initialize(&event);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-               }
-
-               if (gw) {
-                       yaml_mapping_start_event_initialize(&event, NULL,
-                                                           (yaml_char_t *)YAML_MAP_TAG,
-                                                           1,
-                                                           YAML_BLOCK_MAPPING_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_STR_TAG,
-                                                    (yaml_char_t *)"gateway",
-                                                    strlen("gateway"), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_STR_TAG,
-                                                    (yaml_char_t *)gw,
-                                                    strlen(gw), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_mapping_end_event_initialize(&event);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-               }
-
-               if (hops != -1) {
-                       yaml_mapping_start_event_initialize(&event, NULL,
-                                                           (yaml_char_t *)YAML_MAP_TAG,
-                                                           1,
-                                                           YAML_BLOCK_MAPPING_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_STR_TAG,
-                                                    (yaml_char_t *)"hop",
-                                                    strlen("hop"), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       snprintf(num, sizeof(num), "%d", hops);
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_INT_TAG,
-                                                    (yaml_char_t *)num,
-                                                    strlen(num), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_mapping_end_event_initialize(&event);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-               }
-
-               if (prio != -1) {
-                       yaml_mapping_start_event_initialize(&event, NULL,
-                                                           (yaml_char_t *)YAML_MAP_TAG,
-                                                           1,
-                                                           YAML_BLOCK_MAPPING_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_STR_TAG,
-                                                    (yaml_char_t *)"priority",
-                                                    strlen("priority"), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       snprintf(num, sizeof(num), "%d", prio);
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_INT_TAG,
-                                                    (yaml_char_t *)num,
-                                                    strlen(num), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_mapping_end_event_initialize(&event);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-               }
-
-               if (sen != -1) {
-                       yaml_mapping_start_event_initialize(&event, NULL,
-                                                           (yaml_char_t *)YAML_MAP_TAG,
-                                                           1,
-                                                           YAML_BLOCK_MAPPING_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_STR_TAG,
-                                                    (yaml_char_t *)"health_sensitivity",
-                                                    strlen("health_sensitivity"),
-                                                    1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
-
-                       snprintf(num, sizeof(num), "%d", sen);
-                       yaml_scalar_event_initialize(&event, NULL,
-                                                    (yaml_char_t *)YAML_INT_TAG,
-                                                    (yaml_char_t *)num,
-                                                    strlen(num), 1, 0,
-                                                    YAML_PLAIN_SCALAR_STYLE);
-                       rc = yaml_emitter_emit(&output, &event);
-                       if (rc == 0)
-                               goto emitter_error;
+               if (!nl_list_empty(&head.children)) {
+                       nl_list_for_each_entry(entry, &head.children, list) {
+                               const char *nid = entry->nidstr;
 
-                       yaml_mapping_end_event_initialize(&event);
-                       rc = yaml_emitter_emit(&output, &event);
+                               rc = yaml_lnet_router_gateways(&output, nw, nid,
+                                                              hops, prio, sen);
+                               if (rc == 0)
+                                       goto emitter_error;
+                       }
+               } else {
+                       rc = yaml_lnet_router_gateways(&output, nw, NULL, hops,
+                                                      prio, sen);
                        if (rc == 0)
                                goto emitter_error;
                }
@@ -1410,12 +1402,13 @@ emitter_error:
                yaml_document_t errmsg;
 
                rc = yaml_parser_load(&reply, &errmsg);
-               if (rc == 1 && flags == NLM_F_DUMP) {
+               if (rc == 1 && (flags & NLM_F_DUMP)) {
                        yaml_emitter_t debug;
 
                        rc = yaml_emitter_initialize(&debug);
                        if (rc == 1) {
-                               yaml_emitter_set_indent(&debug, 6);
+                               yaml_emitter_set_indent(&debug,
+                                                       LNET_DEFAULT_INDENT);
                                yaml_emitter_set_output_file(&debug,
                                                             stdout);
                                rc = yaml_emitter_dump(&debug, &errmsg);
@@ -1450,13 +1443,14 @@ static int jt_add_route(int argc, char **argv)
 
        const char *const short_options = "n:g:c:p:";
        static const struct option long_options[] = {
-       { .name = "net",       .has_arg = required_argument, .val = 'n' },
-       { .name = "gateway",   .has_arg = required_argument, .val = 'g' },
-       { .name = "hop",       .has_arg = required_argument, .val = 'c' },
-       { .name = "hop-count", .has_arg = required_argument, .val = 'c' },
-       { .name = "priority",  .has_arg = required_argument, .val = 'p' },
-       { .name = "health_sensitivity",  .has_arg = required_argument, .val = 's' },
-       { .name = NULL } };
+               { .name = "net",       .has_arg = required_argument, .val = 'n' },
+               { .name = "gateway",   .has_arg = required_argument, .val = 'g' },
+               { .name = "hop",       .has_arg = required_argument, .val = 'c' },
+               { .name = "hop-count", .has_arg = required_argument, .val = 'c' },
+               { .name = "priority",  .has_arg = required_argument, .val = 'p' },
+               { .name = "health_sensitivity",  .has_arg = required_argument, .val = 's' },
+               { .name = NULL }
+       };
 
        rc = check_cmd(route_cmds, "route", "add", 0, argc, argv);
        if (rc)
@@ -1503,6 +1497,14 @@ static int jt_add_route(int argc, char **argv)
                }
        }
 
+       rc = yaml_lnet_route(network, gateway, hop, prio, sen,
+                            LNET_GENL_VERSION, NLM_F_CREATE);
+       if (rc <= 0) {
+               if (rc == -EOPNOTSUPP)
+                       goto old_api;
+               return rc;
+       }
+old_api:
        rc = lustre_lnet_config_route(network, gateway, hop, prio, sen, -1,
                                      &err_rc);
 
@@ -2267,12 +2269,12 @@ static int jt_del_route(int argc, char **argv)
        char *network = NULL, *gateway = NULL;
        struct cYAML *err_rc = NULL;
        int rc, opt;
-
        const char *const short_options = "n:g:";
        static const struct option long_options[] = {
                { .name = "net",     .has_arg = required_argument, .val = 'n' },
                { .name = "gateway", .has_arg = required_argument, .val = 'g' },
-               { .name = NULL } };
+               { .name = NULL }
+       };
 
        rc = check_cmd(route_cmds, "route", "del", 0, argc, argv);
        if (rc)
@@ -2294,6 +2296,14 @@ static int jt_del_route(int argc, char **argv)
                }
        }
 
+       rc = yaml_lnet_route(network, gateway, -1, -1, -1, LNET_GENL_VERSION,
+                            0);
+       if (rc <= 0) {
+               if (rc == -EOPNOTSUPP)
+                       goto old_api;
+               return rc;
+       }
+old_api:
        rc = lustre_lnet_del_route(network, gateway, -1, &err_rc);
 
        if (rc != LUSTRE_CFG_RC_NO_ERR)