From 3155489aa23304b718bb1bbd8964db0461a1c3b1 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 28 Sep 2016 23:19:50 +0200 Subject: [PATCH 1/2] lib: "ferr" error-information system This provides an API to pass around extra information for errors, more than a simple return value can carry. This is particularly used for the Cap'n Proto interface to be able to report more useful errors. Signed-off-by: David Lamparter --- lib/ferr.c | 147 ++++++++++++++++++++++++++++++++++++++ lib/ferr.h | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/subdir.am | 2 + 3 files changed, 340 insertions(+) create mode 100644 lib/ferr.c create mode 100644 lib/ferr.h diff --git a/lib/ferr.c b/lib/ferr.c new file mode 100644 index 0000000000..2a039d2089 --- /dev/null +++ b/lib/ferr.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "ferr.h" +#include "vty.h" +#include "jhash.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, ERRINFO, "error information") + +static pthread_key_t errkey; + +static void ferr_free(void *arg) +{ + XFREE(MTYPE_ERRINFO, arg); +} + +static void err_key_init(void) __attribute__((_CONSTRUCTOR(500))); +static void err_key_init(void) +{ + pthread_key_create(&errkey, ferr_free); +} + +static void err_key_fini(void) __attribute__((_DESTRUCTOR(500))); +static void err_key_fini(void) +{ + pthread_key_delete(errkey); +} + +const struct ferr *ferr_get_last(ferr_r errval) +{ + struct ferr *last_error = pthread_getspecific(errkey); + if (!last_error || last_error->kind == 0) + return NULL; + return last_error; +} + +ferr_r ferr_clear(void) +{ + struct ferr *last_error = pthread_getspecific(errkey); + if (last_error) + last_error->kind = 0; + return ferr_ok(); +} + +static ferr_r ferr_set_va(const char *file, int line, const char *func, + enum ferr_kind kind, const char *pathname, int errno_val, + const char *text, va_list va) +{ + struct ferr *error = pthread_getspecific(errkey); + + if (!error) { + error = XCALLOC(MTYPE_ERRINFO, sizeof(*error)); + if (!error) { + /* we're screwed */ + zlog_err("out of memory while allocating error info"); + raise(SIGSEGV); + } + + pthread_setspecific(errkey, error); + } + + error->file = file; + error->line = line; + error->func = func; + error->kind = kind; + + error->unique_id = jhash(text, strlen(text), + jhash(file, strlen(file), 0xd4ed0298)); + + error->errno_val = errno_val; + if (pathname) + snprintf(error->pathname, sizeof(error->pathname), + "%s", pathname); + else + error->pathname[0] = '\0'; + + vsnprintf(error->message, sizeof(error->message), text, va); + return -1; +} + +ferr_r ferr_set_internal(const char *file, int line, const char *func, + enum ferr_kind kind, const char *text, ...) +{ + ferr_r rv; + va_list va; + va_start(va, text); + rv = ferr_set_va(file, line, func, kind, NULL, 0, text, va); + va_end(va); + return rv; +} + +ferr_r ferr_set_internal_ext(const char *file, int line, const char *func, + enum ferr_kind kind, const char *pathname, int errno_val, + const char *text, ...) +{ + ferr_r rv; + va_list va; + va_start(va, text); + rv = ferr_set_va(file, line, func, kind, pathname, errno_val, text, va); + va_end(va); + return rv; +} + +#define REPLACE "$ERR" +void vty_print_error(struct vty *vty, ferr_r err, const char *msg, ...) +{ + char tmpmsg[512], *replacepos; + const struct ferr *last_error = ferr_get_last(err); + + va_list va; + va_start(va, msg); + vsnprintf(tmpmsg, sizeof(tmpmsg), msg, va); + va_end(va); + + replacepos = strstr(tmpmsg, REPLACE); + if (!replacepos) + vty_out(vty, "%s\n", tmpmsg); + else { + replacepos[0] = '\0'; + replacepos += sizeof(REPLACE) - 1; + vty_out(vty, "%s%s%s\n", + tmpmsg, + last_error ? last_error->message : "(no error?)", + replacepos); + } +} + diff --git a/lib/ferr.h b/lib/ferr.h new file mode 100644 index 0000000000..a570637372 --- /dev/null +++ b/lib/ferr.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_FERR_H +#define _FRR_FERR_H + +/*********************************************************** + * scroll down to the end of this file for a full example! * + ***********************************************************/ + +#include +#include +#include + +/* return type when this error indication stuff is used. + * + * guaranteed to have boolean evaluation to "false" when OK, "true" when error + * (i.e. can be changed to pointer in the future if neccessary) + * + * For checking, always use "if (value)", nothing else. + * Do _NOT_ use any integer constant (!= 0), or sign check (< 0). + */ +typedef int ferr_r; + +/* rough category of error indication */ +enum ferr_kind { + /* no error */ + FERR_OK = 0, + + /* something isn't the way it's supposed to be. + * (things that might otherwise be asserts, really) + */ + FERR_CODE_BUG, + + /* user-supplied parameters don't make sense or is inconsistent + * if you can express a rule for it (e.g. "holdtime > 2 * keepalive"), + * it's this category. + */ + FERR_CONFIG_INVALID, + + /* user-supplied parameters don't line up with reality + * (IP address or interface not available, etc.) + * NB: these are really TODOs where the code needs to be fixed to + * respond to future changes! + */ + FERR_CONFIG_REALITY, + + /* out of some system resource (probably memory) + * aka "you didn't spend enough money error" */ + FERR_RESOURCE, + + /* system error (permission denied, etc.) */ + FERR_SYSTEM, + + /* error return from some external library + * (FERR_SYSTEM and FERR_LIBRARY are not strongly distinct) */ + FERR_LIBRARY, +}; + +struct ferr { + /* code location */ + const char *file; + const char *func; + int line; + + enum ferr_kind kind; + + /* unique_id is calculated as a checksum of source filename and error + * message format (*before* calling vsnprintf). Line number and + * function name are not used; this keeps the number reasonably static + * across changes. + */ + uint32_t unique_id; + + char message[384]; + + /* valid if != 0. note "errno" might be preprocessor foobar. */ + int errno_val; + /* valid if pathname[0] != '\0' */ + char pathname[PATH_MAX]; +}; + +/* get error details. + * + * NB: errval/ferr_r does NOT carry the full error information. It's only + * passed around for future API flexibility. ferr_get_last always returns + * the last error set in the current thread. + */ +const struct ferr *ferr_get_last(ferr_r errval); + +/* can optionally be called at strategic locations. + * always returns 0. */ +ferr_r ferr_clear(void); + +/* do NOT call these functions directly. only for macro use! */ +ferr_r ferr_set_internal(const char *file, int line, const char *func, + enum ferr_kind kind, const char *text, ...); +ferr_r ferr_set_internal_ext(const char *file, int line, const char *func, + enum ferr_kind kind, const char *pathname, int errno_val, + const char *text, ...); + +#define ferr_ok() \ + 0 + +/* Report an error. + * + * If you need to do cleanup (free memory, etc.), save the return value in a + * variable of type ferr_r. + * + * Don't put a \n at the end of the error message. + */ +#define ferr_code_bug(...) \ + ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CODE_BUG, \ + __VA_ARGS__) +#define ferr_cfg_invalid(...) \ + ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CONFIG_INVALID, \ + __VA_ARGS__) +#define ferr_cfg_reality(...) \ + ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CONFIG_REALITY, \ + __VA_ARGS__) +#define ferr_cfg_resource(...) \ + ferr_set_internal(__FILE__, __LINE__, __func__, FERR_RESOURCE, \ + __VA_ARGS__) +#define ferr_system(...) \ + ferr_set_internal(__FILE__, __LINE__, __func__, FERR_SYSTEM, \ + __VA_ARGS__) +#define ferr_library(...) \ + ferr_set_internal(__FILE__, __LINE__, __func__, FERR_LIBRARY, \ + __VA_ARGS__) + +/* extended information variants */ +#define ferr_system_errno(...) \ + ferr_set_internal_ext(__FILE__, __LINE__, __func__, FERR_SYSTEM, \ + NULL, errno, __VA_ARGS__) +#define ferr_system_path_errno(path, ...) \ + ferr_set_internal_ext(__FILE__, __LINE__, __func__, FERR_SYSTEM, \ + path, errno, __VA_ARGS__) + +#include "vty.h" +/* print error message to vty; $ERR is replaced by the error's message */ +void vty_print_error(struct vty *vty, ferr_r err, const char *msg, ...); + +#define CMD_FERR_DO(func, action, ...) \ + do { ferr_r cmd_retval = func; \ + if (cmd_retval) { \ + vty_print_error(vty, cmd_retval, __VA_ARGS__); \ + action; \ + } \ + } while (0) + +#define CMD_FERR_RETURN(func, ...) \ + CMD_FERR_DO(func, return CMD_WARNING, __VA_ARGS__) +#define CMD_FERR_GOTO(func, label, ...) \ + CMD_FERR_DO(func, goto label, __VA_ARGS__) + +/* example: + +ferr_r foo_bar_set(struct object *obj, int bar) +{ + if (bar < 1 || bar >= 100) + return ferr_config_invalid("bar setting (%d) must be 0bar = bar; + if (ioctl (obj->fd, bar)) + return ferr_system_errno("couldn't set bar to %d", bar); + + return ferr_ok(); +} + +DEFUN("bla") +{ + CMD_FERR_RETURN(foo_bar_set(obj, atoi(argv[1])), + "command failed: $ERR\n"); + return CMD_SUCCESS; +} + +*/ + +#endif /* _FERR_H */ diff --git a/lib/subdir.am b/lib/subdir.am index d6349ba22d..eaa6cd6ec8 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -17,6 +17,7 @@ lib_libfrr_la_SOURCES = \ lib/csv.c \ lib/distribute.c \ lib/event_counter.c \ + lib/ferr.c \ lib/filter.c \ lib/frr_pthread.c \ lib/getopt.c \ @@ -89,6 +90,7 @@ pkginclude_HEADERS += \ lib/csv.h \ lib/distribute.h \ lib/event_counter.h \ + lib/ferr.h \ lib/fifo.h \ lib/filter.h \ lib/freebsd-queue.h \ From 64dd3ffe7ee01640eb3546c0a01c1d2f35e4e8f6 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 31 Mar 2016 17:18:14 +0200 Subject: [PATCH 2/2] isisd: use ferr_* functions Drop redundant checks & use ferr_* to print CLI error messages. Signed-off-by: David Lamparter --- isisd/isis_circuit.c | 58 ++++++++++++++------------- isisd/isis_circuit.h | 19 ++++----- isisd/isis_vty.c | 93 ++++++++++++++------------------------------ 3 files changed, 70 insertions(+), 100 deletions(-) diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index a1aa87e396..bf39c22e38 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -1190,13 +1190,13 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); } -int isis_circuit_passive_set(struct isis_circuit *circuit, bool passive) +ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive) { if (circuit->is_passive == passive) - return 0; + return ferr_ok(); if (if_is_loopback(circuit->interface) && !passive) - return -1; + return ferr_cfg_invalid("loopback is always passive"); if (circuit->state != C_STATE_UP) { circuit->is_passive = passive; @@ -1207,30 +1207,33 @@ int isis_circuit_passive_set(struct isis_circuit *circuit, bool passive) isis_csm_state_change(ISIS_ENABLE, circuit, area); } - return 0; + return ferr_ok(); } -int isis_circuit_metric_set(struct isis_circuit *circuit, int level, int metric) +ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level, + int metric) { assert(level == IS_LEVEL_1 || level == IS_LEVEL_2); if (metric > MAX_WIDE_LINK_METRIC) - return -1; + return ferr_cfg_invalid("metric %d too large for wide metric", + metric); if (circuit->area && circuit->area->oldmetric && metric > MAX_NARROW_LINK_METRIC) - return -1; + return ferr_cfg_invalid("metric %d too large for narrow metric", + metric); circuit->te_metric[level - 1] = metric; circuit->metric[level - 1] = metric; if (circuit->area) lsp_regenerate_schedule(circuit->area, level, 0); - return 0; + return ferr_ok(); } -int isis_circuit_passwd_unset(struct isis_circuit *circuit) +ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit) { memset(&circuit->passwd, 0, sizeof(circuit->passwd)); - return 0; + return ferr_ok(); } static int isis_circuit_passwd_set(struct isis_circuit *circuit, @@ -1239,50 +1242,50 @@ static int isis_circuit_passwd_set(struct isis_circuit *circuit, int len; if (!passwd) - return -1; + return ferr_code_bug("no circuit password given"); len = strlen(passwd); if (len > 254) - return -1; + return ferr_code_bug( + "circuit password too long (max 254 chars)"); circuit->passwd.len = len; strncpy((char *)circuit->passwd.passwd, passwd, 255); circuit->passwd.type = passwd_type; - return 0; + return ferr_ok(); } -int isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit, - const char *passwd) +ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit, + const char *passwd) { return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_CLEARTXT, passwd); } -int isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, - const char *passwd) +ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, + const char *passwd) { return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_HMAC_MD5, passwd); } + struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", 1, }; -int isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) +ferr_r isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) { + if (circuit->circ_type == circ_type) + return ferr_ok(); + /* Changing the network type to/of loopback or unknown interfaces * is not supported. */ if (circ_type == CIRCUIT_T_UNKNOWN || circ_type == CIRCUIT_T_LOOPBACK || circuit->circ_type == CIRCUIT_T_LOOPBACK) { - if (circuit->circ_type != circ_type) - return -1; - else - return 0; + return ferr_cfg_invalid( + "cannot change network type on unknown interface"); } - if (circuit->circ_type == circ_type) - return 0; - if (circuit->state != C_STATE_UP) { circuit->circ_type = circ_type; circuit->circ_type_config = circ_type; @@ -1290,14 +1293,15 @@ int isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) struct isis_area *area = circuit->area; if (circ_type == CIRCUIT_T_BROADCAST && !if_is_broadcast(circuit->interface)) - return -1; + return ferr_cfg_reality( + "cannot configure non-broadcast interface for broadcast operation"); isis_csm_state_change(ISIS_DISABLE, circuit, area); circuit->circ_type = circ_type; circuit->circ_type_config = circ_type; isis_csm_state_change(ISIS_ENABLE, circuit, area); } - return 0; + return ferr_ok(); } int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid, diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index ad53be4683..5906efd2b8 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -27,6 +27,7 @@ #include "if.h" #include "qobj.h" #include "prefix.h" +#include "ferr.h" #include "isis_constants.h" #include "isis_common.h" @@ -178,18 +179,18 @@ struct isis_circuit *isis_circuit_create(struct isis_area *area, struct interface *ifp); void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, bool ipv6_router); -int isis_circuit_passive_set(struct isis_circuit *circuit, bool passive); +ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive); void isis_circuit_is_type_set(struct isis_circuit *circuit, int is_type); -int isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type); +ferr_r isis_circuit_circ_type_set (struct isis_circuit *circuit, int circ_type); -int isis_circuit_metric_set(struct isis_circuit *circuit, int level, - int metric); +ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level, + int metric); -int isis_circuit_passwd_unset(struct isis_circuit *circuit); -int isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit, - const char *passwd); -int isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, - const char *passwd); +ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit); +ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit, + const char *passwd); +ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, + const char *passwd); int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid, bool enabled); diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c index 17d8ad3c0b..02e28a73fe 100644 --- a/isisd/isis_vty.c +++ b/isisd/isis_vty.c @@ -164,7 +164,8 @@ DEFUN (isis_passive, if (!circuit) return CMD_ERR_NO_MATCH; - isis_circuit_passive_set(circuit, 1); + CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 1), + "Cannot set passive: $ERR"); return CMD_SUCCESS; } @@ -179,12 +180,8 @@ DEFUN (no_isis_passive, if (!circuit) return CMD_ERR_NO_MATCH; - if (if_is_loopback(circuit->interface)) { - vty_out(vty, "Can't set no passive for loopback interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - isis_circuit_passive_set(circuit, 0); + CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 0), + "Cannot set no passive: $ERR"); return CMD_SUCCESS; } @@ -301,7 +298,8 @@ DEFUN (isis_passwd, int idx_encryption = 2; int idx_word = 3; struct isis_circuit *circuit = isis_circuit_lookup(vty); - int rv; + ferr_r rv; + if (!circuit) return CMD_ERR_NO_MATCH; @@ -311,11 +309,8 @@ DEFUN (isis_passwd, else rv = isis_circuit_passwd_cleartext_set(circuit, argv[idx_word]->arg); - if (rv) { - vty_out(vty, "Too long circuit password (>254)\n"); - return CMD_WARNING_CONFIG_FAILED; - } + CMD_FERR_RETURN(rv, "Failed to set circuit password: $ERR"); return CMD_SUCCESS; } @@ -333,8 +328,8 @@ DEFUN (no_isis_passwd, if (!circuit) return CMD_ERR_NO_MATCH; - isis_circuit_passwd_unset(circuit); - + CMD_FERR_RETURN(isis_circuit_passwd_unset(circuit), + "Failed to unset circuit password: $ERR"); return CMD_SUCCESS; } @@ -507,8 +502,10 @@ DEFUN (isis_metric, return CMD_WARNING_CONFIG_FAILED; } - isis_circuit_metric_set(circuit, IS_LEVEL_1, met); - isis_circuit_metric_set(circuit, IS_LEVEL_2, met); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, met), + "Failed to set L1 metric: $ERR"); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, met), + "Failed to set L2 metric: $ERR"); return CMD_SUCCESS; } @@ -525,8 +522,12 @@ DEFUN (no_isis_metric, if (!circuit) return CMD_ERR_NO_MATCH; - isis_circuit_metric_set(circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC); - isis_circuit_metric_set(circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, + DEFAULT_CIRCUIT_METRIC), + "Failed to set L1 metric: $ERR"); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, + DEFAULT_CIRCUIT_METRIC), + "Failed to set L2 metric: $ERR"); return CMD_SUCCESS; } @@ -546,28 +547,8 @@ DEFUN (isis_metric_l1, return CMD_ERR_NO_MATCH; met = atoi(argv[idx_number]->arg); - - /* RFC3787 section 5.1 */ - if (circuit->area && circuit->area->oldmetric == 1 - && met > MAX_NARROW_LINK_METRIC) { - vty_out(vty, - "Invalid metric %d - should be <0-63> " - "when narrow metric type enabled\n", - met); - return CMD_WARNING_CONFIG_FAILED; - } - - /* RFC4444 */ - if (circuit->area && circuit->area->newmetric == 1 - && met > MAX_WIDE_LINK_METRIC) { - vty_out(vty, - "Invalid metric %d - should be <0-16777215> " - "when wide metric type enabled\n", - met); - return CMD_WARNING_CONFIG_FAILED; - } - - isis_circuit_metric_set(circuit, IS_LEVEL_1, met); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, met), + "Failed to set L1 metric: $ERR"); return CMD_SUCCESS; } @@ -585,7 +566,9 @@ DEFUN (no_isis_metric_l1, if (!circuit) return CMD_ERR_NO_MATCH; - isis_circuit_metric_set(circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, + DEFAULT_CIRCUIT_METRIC), + "Failed to set L1 metric: $ERR"); return CMD_SUCCESS; } @@ -605,28 +588,8 @@ DEFUN (isis_metric_l2, return CMD_ERR_NO_MATCH; met = atoi(argv[idx_number]->arg); - - /* RFC3787 section 5.1 */ - if (circuit->area && circuit->area->oldmetric == 1 - && met > MAX_NARROW_LINK_METRIC) { - vty_out(vty, - "Invalid metric %d - should be <0-63> " - "when narrow metric type enabled\n", - met); - return CMD_WARNING_CONFIG_FAILED; - } - - /* RFC4444 */ - if (circuit->area && circuit->area->newmetric == 1 - && met > MAX_WIDE_LINK_METRIC) { - vty_out(vty, - "Invalid metric %d - should be <0-16777215> " - "when wide metric type enabled\n", - met); - return CMD_WARNING_CONFIG_FAILED; - } - - isis_circuit_metric_set(circuit, IS_LEVEL_2, met); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, met), + "Failed to set L2 metric: $ERR"); return CMD_SUCCESS; } @@ -644,7 +607,9 @@ DEFUN (no_isis_metric_l2, if (!circuit) return CMD_ERR_NO_MATCH; - isis_circuit_metric_set(circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, + DEFAULT_CIRCUIT_METRIC), + "Failed to set L2 metric: $ERR"); return CMD_SUCCESS; }