diff options
Diffstat (limited to 'lib/if.c')
| -rw-r--r-- | lib/if.c | 550 |
1 files changed, 366 insertions, 184 deletions
@@ -34,6 +34,10 @@ #include "table.h" #include "buffer.h" #include "log.h" +#include "northbound_cli.h" +#ifndef VTYSH_EXTRACT_PL +#include "lib/if_clippy.c" +#endif DEFINE_MTYPE(LIB, IF, "Interface") DEFINE_MTYPE_STATIC(LIB, CONNECTED, "Connected") @@ -160,14 +164,14 @@ struct interface *if_create(const char *name, vrf_id_t vrf_id) /* Create new interface structure. */ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) { - struct vrf *vrf; + struct vrf *old_vrf, *vrf; /* remove interface from old master vrf list */ - vrf = vrf_lookup_by_id(ifp->vrf_id); - if (vrf) { - IFNAME_RB_REMOVE(vrf, ifp); + old_vrf = vrf_lookup_by_id(ifp->vrf_id); + if (old_vrf) { + IFNAME_RB_REMOVE(old_vrf, ifp); if (ifp->ifindex != IFINDEX_INTERNAL) - IFINDEX_RB_REMOVE(vrf, ifp); + IFINDEX_RB_REMOVE(old_vrf, ifp); } ifp->vrf_id = vrf_id; @@ -176,6 +180,25 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) IFNAME_RB_INSERT(vrf, ifp); if (ifp->ifindex != IFINDEX_INTERNAL) IFINDEX_RB_INSERT(vrf, ifp); + + /* + * HACK: Change the interface VRF in the running configuration directly, + * bypassing the northbound layer. This is necessary to avoid deleting + * the interface and readding it in the new VRF, which would have + * several implications. + */ + if (yang_module_find("frr-interface")) { + struct lyd_node *if_dnode; + + if_dnode = yang_dnode_get( + running_config->dnode, + "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", + ifp->name, old_vrf->name); + if (if_dnode) { + yang_dnode_change_leaf(if_dnode, vrf->name); + running_config->version++; + } + } } @@ -369,49 +392,31 @@ struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id) /* Get interface by name if given name interface doesn't exist create one. */ -struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty) +struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) { - struct interface *ifp = NULL; + struct interface *ifp; - if (vrf_is_mapped_on_netns(vrf_lookup_by_id(vrf_id))) { + switch (vrf_get_backend()) { + case VRF_BACKEND_NETNS: ifp = if_lookup_by_name(name, vrf_id); if (ifp) return ifp; - if (vty) { - /* If the interface command was entered in vty without a - * VRF (passed as VRF_DEFAULT), search an interface with - * this name in all VRs - */ - if (vrf_id == VRF_DEFAULT) - return if_lookup_by_name_all_vrf(name); - return NULL; - } return if_create(name, vrf_id); - } - /* vrf is based on vrf-lite */ - ifp = if_lookup_by_name_all_vrf(name); - if (ifp) { - if (ifp->vrf_id == vrf_id) - return ifp; - /* Found a match on a different VRF. If the interface command - * was entered in vty without a VRF (passed as VRF_DEFAULT), - * accept the ifp we found. If a vrf was entered and there is a - * mismatch, reject it if from vty. If it came from the kernel - * or by way of zclient, believe it and update the ifp - * accordingly. - */ - if (vty) { - if (vrf_id == VRF_DEFAULT) + case VRF_BACKEND_VRF_LITE: + ifp = if_lookup_by_name_all_vrf(name); + if (ifp) { + if (ifp->vrf_id == vrf_id) return ifp; - return NULL; + /* If it came from the kernel or by way of zclient, + * believe it and update the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; } - /* If it came from the kernel or by way of zclient, believe it - * and update the ifp accordingly. - */ - if_update_to_new_vrf(ifp, vrf_id); - return ifp; + return if_create(name, vrf_id); } - return if_create(name, vrf_id); + + return NULL; } void if_set_index(struct interface *ifp, ifindex_t ifindex) @@ -577,37 +582,6 @@ void if_dump_all(void) if_dump(ifp); } -DEFUN (interface_desc, - interface_desc_cmd, - "description LINE...", - "Interface specific description\n" - "Characters describing this interface\n") -{ - int idx_line = 1; - VTY_DECLVAR_CONTEXT(interface, ifp); - - if (ifp->desc) - XFREE(MTYPE_TMP, ifp->desc); - ifp->desc = argv_concat(argv, argc, idx_line); - - return CMD_SUCCESS; -} - -DEFUN (no_interface_desc, - no_interface_desc_cmd, - "no description", - NO_STR - "Interface specific description\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - - if (ifp->desc) - XFREE(MTYPE_TMP, ifp->desc); - ifp->desc = NULL; - - return CMD_SUCCESS; -} - #ifdef SUNOS_5 /* Need to handle upgrade from SUNWzebra to Quagga. SUNWzebra created * a seperate struct interface for each logical interface, so config @@ -642,123 +616,10 @@ static struct interface *if_sunwzebra_get(const char *name, vrf_id_t vrf_id) if (cp) *cp = '\0'; - return if_get_by_name(name, vrf_id, 1); + return if_get_by_name(name, vrf_id); } #endif /* SUNOS_5 */ -DEFUN_NOSH (interface, - interface_cmd, - "interface IFNAME [vrf NAME]", - "Select an interface to configure\n" - "Interface's name\n" - VRF_CMD_HELP_STR) -{ - int idx_ifname = 1; - int idx_vrf = 3; - const char *ifname = argv[idx_ifname]->arg; - const char *vrfname = - (argc > 2) ? argv[idx_vrf]->arg : VRF_DEFAULT_NAME; - - struct interface *ifp; - vrf_id_t vrf_id = VRF_DEFAULT; - - if (strlen(ifname) > INTERFACE_NAMSIZ) { - vty_out(vty, - "%% Interface name %s is invalid: length exceeds " - "%d characters\n", - ifname, INTERFACE_NAMSIZ); - return CMD_WARNING_CONFIG_FAILED; - } - - /*Pending: need proper vrf name based lookup/(possible creation of VRF) - Imagine forward reference of a vrf by name in this interface config */ - if (vrfname) - VRF_GET_ID(vrf_id, vrfname, false); - -#ifdef SUNOS_5 - ifp = if_sunwzebra_get(ifname, vrf_id); -#else - ifp = if_get_by_name(ifname, vrf_id, 1); -#endif /* SUNOS_5 */ - - if (!ifp) { - vty_out(vty, "%% interface %s not in %s vrf\n", ifname, - vrfname); - return CMD_WARNING_CONFIG_FAILED; - } - VTY_PUSH_CONTEXT(INTERFACE_NODE, ifp); - - return CMD_SUCCESS; -} - -DEFUN (no_interface, - no_interface_cmd, - "no interface IFNAME [vrf NAME]", - NO_STR - "Delete a pseudo interface's configuration\n" - "Interface's name\n" - VRF_CMD_HELP_STR) -{ - int idx_vrf = 4; - const char *ifname = argv[2]->arg; - const char *vrfname = (argc > 3) ? argv[idx_vrf]->arg : NULL; - - // deleting interface - struct interface *ifp; - vrf_id_t vrf_id = VRF_DEFAULT; - - if (argc > 3) - VRF_GET_ID(vrf_id, vrfname, false); - - ifp = if_lookup_by_name(ifname, vrf_id); - - if (ifp == NULL) { - vty_out(vty, "%% Interface %s does not exist\n", ifname); - return CMD_WARNING_CONFIG_FAILED; - } - - if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - vty_out(vty, "%% Only inactive interfaces can be deleted\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if_delete(ifp); - - return CMD_SUCCESS; -} - -static void if_autocomplete(vector comps, struct cmd_token *token) -{ - struct interface *ifp; - struct vrf *vrf = NULL; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - FOR_ALL_INTERFACES (vrf, ifp) { - vector_set(comps, XSTRDUP(MTYPE_COMPLETION, ifp->name)); - } - } -} - -static const struct cmd_variable_handler if_var_handlers[] = { - {/* "interface NAME" */ - .varname = "interface", - .completions = if_autocomplete}, - {.tokenname = "IFNAME", .completions = if_autocomplete}, - {.tokenname = "INTERFACE", .completions = if_autocomplete}, - {.completions = NULL}}; - -void if_cmd_init(void) -{ - cmd_variable_handler_register(if_var_handlers); - - install_element(CONFIG_NODE, &interface_cmd); - install_element(CONFIG_NODE, &no_interface_cmd); - - install_default(INTERFACE_NODE); - install_element(INTERFACE_NODE, &interface_desc_cmd); - install_element(INTERFACE_NODE, &no_interface_desc_cmd); -} - #if 0 /* For debug purpose. */ DEFUN (show_address, @@ -1211,3 +1072,324 @@ void if_link_params_free(struct interface *ifp) XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params); ifp->link_params = NULL; } + +/* ----------- CLI commands ----------- */ + +/* + * XPath: /frr-interface:lib/interface + */ +DEFPY_NOSH (interface, + interface_cmd, + "interface IFNAME [vrf NAME$vrfname]", + "Select an interface to configure\n" + "Interface's name\n" + VRF_CMD_HELP_STR) +{ + char xpath_list[XPATH_MAXLEN]; + vrf_id_t vrf_id; + struct interface *ifp; + int ret; + + if (!vrfname) + vrfname = VRF_DEFAULT_NAME; + + /* + * This command requires special handling to maintain backward + * compatibility. If a VRF name is not specified, it means we're willing + * to accept any interface with the given name on any VRF. If no + * interface is found, then a new one should be created on the default + * VRF. + */ + VRF_GET_ID(vrf_id, vrfname, false); + ifp = if_lookup_by_name_all_vrf(ifname); + if (ifp && ifp->vrf_id != vrf_id) { + struct vrf *vrf; + + /* + * Special case 1: a VRF name was specified, but the found + * interface is associated to different VRF. Reject the command. + */ + if (vrf_id != VRF_DEFAULT) { + vty_out(vty, "%% interface %s not in %s vrf\n", ifname, + vrfname); + return CMD_WARNING_CONFIG_FAILED; + } + + /* + * Special case 2: a VRF name was *not* specified, and the found + * interface is associated to a VRF other than the default one. + * Update vrf_id and vrfname to account for that. + */ + vrf = vrf_lookup_by_id(ifp->vrf_id); + assert(vrf); + vrf_id = ifp->vrf_id; + vrfname = vrf->name; + } + + snprintf(xpath_list, sizeof(xpath_list), + "/frr-interface:lib/interface[name='%s'][vrf='%s']", ifname, + vrfname); + + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + ret = nb_cli_apply_changes(vty, xpath_list); + if (ret == CMD_SUCCESS) { + VTY_PUSH_XPATH(INTERFACE_NODE, xpath_list); + + /* + * For backward compatibility with old commands we still need + * to use the qobj infrastructure. This can be removed once + * all interface-level commands are converted to the new + * northbound model. + */ + ifp = if_lookup_by_name(ifname, vrf_id); + if (ifp) + VTY_PUSH_CONTEXT(INTERFACE_NODE, ifp); + } + + return ret; +} + +DEFPY (no_interface, + no_interface_cmd, + "no interface IFNAME [vrf NAME$vrfname]", + NO_STR + "Delete a pseudo interface's configuration\n" + "Interface's name\n" + VRF_CMD_HELP_STR) +{ + if (!vrfname) + vrfname = VRF_DEFAULT_NAME; + + nb_cli_enqueue_change(vty, ".", NB_OP_DELETE, NULL); + + return nb_cli_apply_changes( + vty, "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifname, vrfname); +} + +static void cli_show_interface(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "./vrf"); + + vty_out(vty, "!\n"); + vty_out(vty, "interface %s", yang_dnode_get_string(dnode, "./name")); + if (!strmatch(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + vty_out(vty, "\n"); +} + +/* + * XPath: /frr-interface:lib/interface/description + */ +DEFPY (interface_desc, + interface_desc_cmd, + "description LINE...", + "Interface specific description\n" + "Characters describing this interface\n") +{ + char *desc; + int ret; + + desc = argv_concat(argv, argc, 1); + nb_cli_enqueue_change(vty, "./description", NB_OP_MODIFY, desc); + ret = nb_cli_apply_changes(vty, NULL); + XFREE(MTYPE_TMP, desc); + + return ret; +} + +DEFPY (no_interface_desc, + no_interface_desc_cmd, + "no description", + NO_STR + "Interface specific description\n") +{ + nb_cli_enqueue_change(vty, "./description", NB_OP_DELETE, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +static void cli_show_interface_desc(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL)); +} + +/* Interface autocomplete. */ +static void if_autocomplete(vector comps, struct cmd_token *token) +{ + struct interface *ifp; + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) { + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, ifp->name)); + } + } +} + +static const struct cmd_variable_handler if_var_handlers[] = { + {/* "interface NAME" */ + .varname = "interface", + .completions = if_autocomplete}, + {.tokenname = "IFNAME", .completions = if_autocomplete}, + {.tokenname = "INTERFACE", .completions = if_autocomplete}, + {.completions = NULL}}; + +void if_cmd_init(void) +{ + cmd_variable_handler_register(if_var_handlers); + + install_element(CONFIG_NODE, &interface_cmd); + install_element(CONFIG_NODE, &no_interface_cmd); + + install_default(INTERFACE_NODE); + install_element(INTERFACE_NODE, &interface_desc_cmd); + install_element(INTERFACE_NODE, &no_interface_desc_cmd); +} + +/* ------- Northbound callbacks ------- */ + +/* + * XPath: /frr-interface:lib/interface + */ +static int lib_interface_create(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + const char *ifname; + const char *vrfname; + struct vrf *vrf; + struct interface *ifp; + + ifname = yang_dnode_get_string(dnode, "./name"); + vrfname = yang_dnode_get_string(dnode, "./vrf"); + + switch (event) { + case NB_EV_VALIDATE: + vrf = vrf_lookup_by_name(vrfname); + if (!vrf) { + zlog_warn("%s: VRF %s doesn't exist", __func__, + vrfname); + return NB_ERR_VALIDATION; + } + if (vrf->vrf_id == VRF_UNKNOWN) { + zlog_warn("%s: VRF %s is not active", __func__, + vrf->name); + return NB_ERR_VALIDATION; + } + if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) { + ifp = if_lookup_by_name_all_vrf(ifname); + if (ifp && ifp->vrf_id != vrf->vrf_id) { + zlog_warn( + "%s: interface %s already exists in another VRF", + __func__, ifp->name); + return NB_ERR_VALIDATION; + } + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrf = vrf_lookup_by_name(vrfname); + assert(vrf); +#ifdef SUNOS_5 + ifp = if_sunwzebra_get(ifname, vrf->vrf_id); +#else + ifp = if_get_by_name(ifname, vrf->vrf_id); +#endif /* SUNOS_5 */ + yang_dnode_set_entry(dnode, ifp); + break; + } + + return NB_OK; +} + +static int lib_interface_delete(enum nb_event event, + const struct lyd_node *dnode) +{ + struct interface *ifp; + + ifp = yang_dnode_get_entry(dnode, true); + + switch (event) { + case NB_EV_VALIDATE: + if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { + zlog_warn("%s: only inactive interfaces can be deleted", + __func__); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if_delete(ifp); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/description + */ +static int lib_interface_description_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct interface *ifp; + const char *description; + + if (event != NB_EV_APPLY) + return NB_OK; + + ifp = yang_dnode_get_entry(dnode, true); + if (ifp->desc) + XFREE(MTYPE_TMP, ifp->desc); + description = yang_dnode_get_string(dnode, NULL); + ifp->desc = XSTRDUP(MTYPE_TMP, description); + + return NB_OK; +} + +static int lib_interface_description_delete(enum nb_event event, + const struct lyd_node *dnode) +{ + struct interface *ifp; + + if (event != NB_EV_APPLY) + return NB_OK; + + ifp = yang_dnode_get_entry(dnode, true); + if (ifp->desc) + XFREE(MTYPE_TMP, ifp->desc); + + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_interface_info = { + .name = "frr-interface", + .nodes = { + { + .xpath = "/frr-interface:lib/interface", + .cbs.create = lib_interface_create, + .cbs.delete = lib_interface_delete, + .cbs.cli_show = cli_show_interface, + }, + { + .xpath = "/frr-interface:lib/interface/description", + .cbs.modify = lib_interface_description_modify, + .cbs.delete = lib_interface_description_delete, + .cbs.cli_show = cli_show_interface_desc, + }, + { + .xpath = NULL, + }, + } +}; |
