From 6557cd4b8c8cf8f8a7d33dadf5a86b4fe0d6eaa2 Mon Sep 17 00:00:00 2001 From: James Simmons Date: Mon, 7 Aug 2023 09:23:15 -0400 Subject: [PATCH] LU-10391 lnet: migrate router management to Netlink 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 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/50254 Reviewed-by: Frank Sehr Reviewed-by: Chris Horn Reviewed-by: Cyril Bordage Reviewed-by: Oleg Drokin Tested-by: jenkins Tested-by: Maloo --- lnet/lnet/api-ni.c | 218 +++++++++++++++++++++++++++++- lnet/utils/lnetctl.c | 364 ++++++++++++++++++++++++++------------------------- 2 files changed, 404 insertions(+), 178 deletions(-) diff --git a/lnet/lnet/api-ni.c b/lnet/lnet/api-ni.c index c36f004..c41ca00 100644 --- a/lnet/lnet/api-ni.c +++ b/lnet/lnet/api-ni.c @@ -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, diff --git a/lnet/utils/lnetctl.c b/lnet/utils/lnetctl.c index 8ed5755..2088d5f 100644 --- a/lnet/utils/lnetctl.c +++ b/lnet/utils/lnetctl.c @@ -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) -- 1.8.3.1