]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/if.c
Merge pull request #3356 from opensourcerouting/router-id-loopbacks
[mirror_frr.git] / lib / if.c
index a03c9da6f91e5aa6329e662206909d10e758e6aa..e02c89b9acea3e0b4daa114c739bc89057c5374e 100644 (file)
--- a/lib/if.c
+++ b/lib/if.c
 #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")
@@ -63,7 +67,7 @@ int ptm_enable = 0;
  * before all numbers.  Examples: de0 < de1, de100 < fxp0 < xl0, devpty <
  * devpty0, de0 < del0
  */
-int if_cmp_name_func(char *p1, char *p2)
+int if_cmp_name_func(const char *p1, const char *p2)
 {
        unsigned int l1, l2;
        long int x1, x2;
@@ -99,8 +103,8 @@ int if_cmp_name_func(char *p1, char *p2)
                if (!*p2)
                        return 1;
 
-               x1 = strtol(p1, &p1, 10);
-               x2 = strtol(p2, &p2, 10);
+               x1 = strtol(p1, (char **)&p1, 10);
+               x2 = strtol(p2, (char **)&p2, 10);
 
                /* let's compare numbers now */
                if (x1 < x2)
@@ -121,7 +125,7 @@ int if_cmp_name_func(char *p1, char *p2)
 static int if_cmp_func(const struct interface *ifp1,
                       const struct interface *ifp2)
 {
-       return if_cmp_name_func((char *)ifp1->name, (char *)ifp2->name);
+       return if_cmp_name_func(ifp1->name, ifp2->name);
 }
 
 static int if_cmp_index_func(const struct interface *ifp1,
@@ -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++;
+               }
+       }
 }
 
 
@@ -206,8 +229,8 @@ void if_delete(struct interface *ifp)
 
        if_delete_retain(ifp);
 
-       list_delete_and_null(&ifp->connected);
-       list_delete_and_null(&ifp->nbr_connected);
+       list_delete(&ifp->connected);
+       list_delete(&ifp->nbr_connected);
 
        if_link_params_free(ifp);
 
@@ -369,39 +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;
 
-       ifp = if_lookup_by_name(name, vrf_id);
-       if (ifp)
-               return ifp;
-       /* Not Found on same 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.
-        */
-       ifp = if_lookup_by_name_all_vrf(name);
-       if (!ifp)
+       switch (vrf_get_backend()) {
+       case VRF_BACKEND_NETNS:
+               ifp = if_lookup_by_name(name, vrf_id);
+               if (ifp)
+                       return ifp;
                return if_create(name, vrf_id);
-       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;
+                       /* 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 NULL;
-       }
-       /* if vrf backend uses NETNS, then
-        * this should not be considered as an update
-        * then create the new interface
-        */
-       if (ifp->vrf_id != vrf_id && vrf_is_mapped_on_netns(
-                                       vrf_lookup_by_id(vrf_id)))
+               }
                return if_create(name, vrf_id);
-       /* 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 NULL;
 }
 
 void if_set_index(struct interface *ifp, ifindex_t ifindex)
@@ -567,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
@@ -632,121 +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 : NULL;
-
-       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\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,
@@ -1149,7 +1022,7 @@ const char *if_link_type_str(enum zebra_link_type llt)
                llts(ZEBRA_LLT_IEEE802154, "IEEE 802.15.4");
                llts(ZEBRA_LLT_IEEE802154_PHY, "IEEE 802.15.4 Phy");
        default:
-               flog_err(LIB_ERR_DEVELOPMENT, "Unknown value %d", llt);
+               flog_err(EC_LIB_DEVELOPMENT, "Unknown value %d", llt);
                return "Unknown type!";
 #undef llts
        }
@@ -1199,3 +1072,348 @@ 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];
+       struct cli_config_change changes[] = {
+               {
+                       .xpath = ".",
+                       .operation = NB_OP_CREATE,
+               },
+       };
+       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);
+
+       ret = nb_cli_cfg_change(vty, xpath_list, changes, array_size(changes));
+       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)
+{
+       char xpath_list[XPATH_MAXLEN];
+       struct cli_config_change changes[] = {
+               {
+                       .xpath = ".",
+                       .operation = NB_OP_DELETE,
+               },
+       };
+
+       if (!vrfname)
+               vrfname = VRF_DEFAULT_NAME;
+
+       snprintf(xpath_list, sizeof(xpath_list),
+                "/frr-interface:lib/interface[name='%s'][vrf='%s']", ifname,
+                vrfname);
+
+       return nb_cli_cfg_change(vty, xpath_list, changes, array_size(changes));
+}
+
+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")
+{
+       struct cli_config_change changes[] = {
+               {
+                       .xpath = "./description",
+                       .operation = NB_OP_MODIFY,
+               },
+       };
+       char *desc;
+       int ret;
+
+       desc = argv_concat(argv, argc, 1);
+       changes[0].value = desc;
+       ret = nb_cli_cfg_change(vty, NULL, changes, array_size(changes));
+       XFREE(MTYPE_TMP, desc);
+
+       return ret;
+}
+
+DEFPY  (no_interface_desc,
+       no_interface_desc_cmd,
+       "no description",
+       NO_STR
+       "Interface specific description\n")
+{
+       struct cli_config_change changes[] = {
+               {
+                       .xpath = "./description",
+                       .operation = NB_OP_DELETE,
+               },
+       };
+
+       return nb_cli_cfg_change(vty, NULL, changes, array_size(changes));
+}
+
+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);
+
+       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);
+       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);
+       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,
+               },
+       }
+};