From 3192a69970abdd15e3193340344c82d8ec28102b Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 28 Apr 2023 16:39:16 +0200 Subject: [PATCH] confd: ietf-interfaces: Initial work on net-do implementation --- src/confd/src/Makefile.am | 2 +- src/confd/src/core.h | 2 + src/confd/src/ietf-interfaces.c | 114 ++++++++++++++- src/confd/src/netdo.c | 237 ++++++++++++++++++++++++++++++++ src/confd/src/netdo.h | 66 +++++++++ 5 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 src/confd/src/netdo.c create mode 100644 src/confd/src/netdo.h diff --git a/src/confd/src/Makefile.am b/src/confd/src/Makefile.am index 89d0ff24..fbb4c392 100644 --- a/src/confd/src/Makefile.am +++ b/src/confd/src/Makefile.am @@ -8,4 +8,4 @@ confd_plugin_la_CFLAGS = $(augeas_CFLAGS) $(jansson_CFLAGS) $(libite_CFLAGS) $( confd_plugin_la_LIBADD = $(augeas_LIBS) $(jansson_LIBS) $(libite_LIBS) $(sysrepo_LIBS) confd_plugin_la_LDFLAGS = -module -avoid-version -shared confd_plugin_la_SOURCES = core.c core.h helpers.c helpers.h srx_module.c srx_module.h srx_val.c srx_val.h \ - ietf-system.c ietf-interfaces.c systemv.c systemv.h lyx.c lyx.h + ietf-system.c ietf-interfaces.c systemv.c systemv.h lyx.c lyx.h netdo.c netdo.h diff --git a/src/confd/src/core.h b/src/confd/src/core.h index aff74617..1b49a9a1 100644 --- a/src/confd/src/core.h +++ b/src/confd/src/core.h @@ -22,6 +22,7 @@ #include "helpers.h" #include "systemv.h" +#include "netdo.h" #ifndef HAVE_VASPRINTF int vasprintf(char **strp, const char *fmt, va_list ap); @@ -63,6 +64,7 @@ struct confd { sr_subscription_ctx_t *sub; augeas *aug; + struct netdo netdo; }; static inline int register_change(sr_session_ctx_t *session, const char *module, const char *xpath, diff --git a/src/confd/src/ietf-interfaces.c b/src/confd/src/ietf-interfaces.c index 211c0c4f..8348645a 100644 --- a/src/confd/src/ietf-interfaces.c +++ b/src/confd/src/ietf-interfaces.c @@ -8,6 +8,8 @@ #include #include "core.h" +#include "lyx.h" +#include "netdo.h" #include "srx_module.h" #include "srx_val.h" @@ -38,7 +40,7 @@ static bool iface_is_phys(const char *ifname) json_t *link; FILE *proc; - proc = popenf("re", "ip -d -j link show dev %s", ifname); + proc = popenf("re", "ip -d -j link show dev %s 2>/dev/null", ifname); if (!proc) goto out; @@ -128,12 +130,114 @@ static int ifchange_cand(sr_session_ctx_t *session, uint32_t sub_id, const char return SR_ERR_OK; } -static int ifchange(sr_session_ctx_t *session, uint32_t sub_id, const char *module, - const char *xpath, sr_event_t event, unsigned request_id, void *priv) +static sr_error_t ifchange_one(struct confd *confd, struct lyd_node *iface) { + const char *ifname = lydx_get_cattr(iface, "name"); + bool is_phys = iface_is_phys(ifname); + enum lydx_op op = lydx_get_op(iface); + const char *attr; + FILE *ip; + + DEBUG("%s(%s) %s", ifname, is_phys ? "phys" : "virt", + (op == LYDX_OP_NONE) ? "mod" : ((op == LYDX_OP_CREATE) ? "add" : "del")); + + if (op == LYDX_OP_DELETE) { + ip = netdo_get_file(&confd->netdo, ifname, NETDO_EXIT, NETDO_IP); + if (!ip) + return SR_ERR_INTERNAL; + + if (is_phys) { + fprintf(ip, "link set dev %s down\n", ifname); + fprintf(ip, "addr flush dev %s\n", ifname); + } else { + fprintf(ip, "link del dev %s\n", ifname); + } + + return SR_ERR_OK; + } + + ip = netdo_get_file(&confd->netdo, ifname, NETDO_INIT, NETDO_IP); + if (!ip) + return SR_ERR_INTERNAL; + + attr = ((op == LYDX_OP_CREATE) && !is_phys) ? "add" : "set"; + fprintf(ip, "link %s dev %s", attr, ifname); + + /* Generic attributes */ + + attr = lydx_get_cattr(iface, "enabled"); + if (!attr && (op == LYDX_OP_CREATE)) + /* When adding an interface to the configuration, we + * need to bring it up, even when "enabled" is not + * explicitly set. + */ + attr = "true"; + + attr = attr ? (!strcmp(attr, "true") ? " up" : " down") : ""; + fprintf(ip, "%s", attr); + + /* Type specific attributes */ + + fputc('\n', ip); + + /* IP Addresses */ + return SR_ERR_OK; } +static int ifchange(sr_session_ctx_t *session, uint32_t sub_id, const char *module, + const char *xpath, sr_event_t event, unsigned request_id, void *_confd) +{ + struct lyd_node *diff, *cifs, *difs, *iface; + struct confd *confd = _confd; + sr_data_t *cfg; + sr_error_t err; + + switch (event) { + case SR_EV_CHANGE: + break; + case SR_EV_ABORT: + return netdo_abort(&confd->netdo); + case SR_EV_DONE: + err = netdo_done(&confd->netdo); + if (err) + return err; + + return systemf("%s", "net do"); + default: + return SR_ERR_OK; + } + + err = sr_get_data(session, "/interfaces/interface//.", 0, 0, 0, &cfg); + if (err) + return err; + + err = srx_get_diff(session, (struct lyd_node **)&diff); + if (err) + goto err_release_data; + + cifs = lydx_get_descendant(cfg->tree, "interfaces", "interface", NULL); + difs = lydx_get_descendant(diff, "interfaces", "interface", NULL); + + err = netdo_change(&confd->netdo, cifs, difs); + if (err) + goto err_free_diff; + + LY_LIST_FOR(difs, iface) { + err = ifchange_one(confd, iface); + if (err) { + netdo_abort(&confd->netdo); + break; + } + } + +err_free_diff: + lyd_free_tree(diff); +err_release_data: + sr_release_data(cfg); + return err; +} + int ietf_interfaces_init(struct confd *confd) { int rc; @@ -142,6 +246,10 @@ int ietf_interfaces_init(struct confd *confd) if (rc) goto fail; + rc = netdo_boot(); + if (rc) + goto fail; + REGISTER_CHANGE(confd->session, "ietf-interfaces", "/ietf-interfaces:interfaces", 0, ifchange, confd, &confd->sub); sr_session_switch_ds(confd->session, SR_DS_CANDIDATE); diff --git a/src/confd/src/netdo.c b/src/confd/src/netdo.c new file mode 100644 index 00000000..372a34f7 --- /dev/null +++ b/src/confd/src/netdo.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include + +#include +#include +#include + +#include "core.h" +#include "lyx.h" +#include "netdo.h" + +const char *netdo_phase_str[] = { +#define netdo_phase_str_gen(_ph, _name, ...) \ + [NETDO_ ## _ph] = _name, + + NETDO_PHASES(netdo_phase_str_gen) +#undef netdo_phase_str_gen +}; + +const char *netdo_cmd_str[] = { +#define netdo_cmd_str_gen(_cmd, _name, ...) \ + [NETDO_ ## _cmd] = _name, + + NETDO_CMDS(netdo_cmd_str_gen) +#undef netdo_cmd_str_gen +}; + +static const char *netpath = "/run/net"; + +static struct netdo_iface *netdo_iface_get(struct netdo *nd, const char *ifname) +{ + struct netdo_iface *iface; + + TAILQ_FOREACH(iface, &nd->ifaces, node) { + if (!strcmp(iface->name, ifname)) + return iface; + } + + return NULL; +} + +static void netdo_iface_del(struct netdo *nd, struct netdo_iface *iface) +{ + enum netdo_phase phase; + enum netdo_cmd cmd; + + for (phase = 0; phase < __NETDO_PHASE_MAX; phase++) { + for (cmd = 0; cmd < __NETDO_CMD_MAX; cmd++) { + if (iface->cmd[phase][cmd]) + fclose(iface->cmd[phase][cmd]); + } + } + + TAILQ_REMOVE(&nd->ifaces, iface, node); + free(iface); +} + +static int netdo_iface_add(struct netdo *nd, const char *ifname) +{ + struct netdo_iface *iface; + + iface = calloc(1, sizeof(*iface)); + if (!iface) + return -ENOMEM; + + strlcpy(iface->name, ifname, sizeof(iface->name)); + + TAILQ_INSERT_TAIL(&nd->ifaces, iface, node); + return 0; +} + +FILE *netdo_get_file(struct netdo *nd, const char *ifname, + enum netdo_phase phase, enum netdo_cmd cmd) +{ + struct netdo_iface *iface; + FILE *fp; + + if (!nd->path[phase][0]) + return NULL; + + iface = netdo_iface_get(nd, ifname); + if (!iface) + return NULL; + + if (iface->cmd[phase][cmd]) + return iface->cmd[phase][cmd]; + + fp = fopenf("w", "%s/%s/%s%s", nd->path[phase], ifname, + netdo_phase_str[phase], netdo_cmd_str[cmd]); + if (!fp) + return NULL; + + switch (cmd) { + case NETDO_ETHTOOL: + if (fputs("#!/bin/sh\n", fp) < 0) { + fclose(fp); + return NULL; + } + break; + default: + break; + } + + iface->cmd[phase][cmd] = fp; + return fp; +} + +static int netdo_open_iface(struct netdo *nd, struct lyd_node *ifdata) +{ + const char *ena = lydx_get_cattr(ifdata, "enabled"); + const char *name = lydx_get_cattr(ifdata, "name"); + char path[PATH_MAX]; + FILE *fp; + + DEBUG("%s", name ? : "INVALID"); + + if (!name || !ena) + return -EINVAL; + + strlcpy(path, nd->path[NETDO_INIT], sizeof(path)); + strlcat(path, "/", sizeof(path)); + strlcat(path, name, sizeof(path)); + + if (makedir(path, 0755)) + return -EIO; + + strlcat(path, "/admin-state", sizeof(path)); + fp = fopen(path, "w"); + if (!fp) + return -EIO; + + if (!strcmp(ena, "true")) + fputs("up\n", fp); + else + fputs("disabled\n", fp); + + fclose(fp); + + return netdo_iface_add(nd, name); +} + +int netdo_change(struct netdo *nd, struct lyd_node *cifs, struct lyd_node *difs) +{ + struct lyd_node *ifdata; + int err = 0; + + memset(nd, 0, sizeof(*nd)); + + nd->next_fp = fopenf("wx", "%s/next", netpath); + if (!nd->next_fp) { + ERROR("Transaction already in progress"); + return -1; + } + + TAILQ_INIT(&nd->ifaces); + + if (readdf(&nd->gen, "%s/gen", netpath)) + nd->gen = 0; + else + nd->gen++; + + snprintf(nd->path[NETDO_INIT], sizeof(nd->path[NETDO_INIT]), + "%s/%d", netpath, nd->gen); + if (makedir(nd->path[NETDO_INIT], 0755)) + return -1; + + if (nd->gen) { + snprintf(nd->path[NETDO_EXIT], sizeof(nd->path[NETDO_EXIT]), + "%s/%d", netpath, nd->gen - 1); + if (access(nd->path[NETDO_EXIT], X_OK)) + return -1; + } + LYX_LIST_FOR_EACH(cifs, ifdata, "interface") { + err = netdo_open_iface(nd, ifdata); + if (err) + break; + } + + LYX_LIST_FOR_EACH(difs, ifdata, "interface") { + if (lydx_get_op(ifdata) != LYDX_OP_DELETE) + continue; + + err = netdo_iface_add(nd, lydx_get_cattr(ifdata, "name")); + if (err) + break; + } + + DEBUG("gen:%d", nd->gen); + return err; +} + +int netdo_done(struct netdo *nd) +{ + struct netdo_iface *iface, *tmp; + + DEBUG(""); + + TAILQ_FOREACH_SAFE(iface, &nd->ifaces, node, tmp) { + netdo_iface_del(nd, iface); + } + + fprintf(nd->next_fp, "%d\n", nd->gen); + fclose(nd->next_fp); + return 0; +} + +int netdo_abort(struct netdo *nd) +{ + struct netdo_iface *iface, *tmp; + + DEBUG(""); + + TAILQ_FOREACH_SAFE(iface, &nd->ifaces, node, tmp) { + netdo_iface_del(nd, iface); + } + + fclose(nd->next_fp); + return systemf("rm -rf %s/next %s", netpath, nd->path[NETDO_INIT]); +} + +int netdo_boot(void) +{ + const char *envnetpath; + + envnetpath = getenv("NET_DIR"); + if (envnetpath) + netpath = envnetpath; + + if (access(netpath, X_OK)) { + if (makedir(netpath, 0755)) + return -1; + } + + return 0; +} diff --git a/src/confd/src/netdo.h b/src/confd/src/netdo.h new file mode 100644 index 00000000..536cc104 --- /dev/null +++ b/src/confd/src/netdo.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef CONFD_NETDO_H_ +#define CONFD_NETDO_H_ + +#include + +#include + +#define NETDO_PHASES(_op) \ + _op(INIT, "init") \ + _op(EXIT, "exit") + +enum netdo_phase { +#define netdo_phase_enum_gen(_ph, ...) \ + NETDO_ ## _ph, + + NETDO_PHASES(netdo_phase_enum_gen) +#undef netdo_phase_enum_gen + + __NETDO_PHASE_MAX +}; + +#define NETDO_CMDS(_op) \ + _op(BRIDGE, ".bridge") \ + _op(ETHTOOL, "-ethtool.sh") \ + _op(IP, ".ip") + +enum netdo_cmd { +#define netdo_cmd_enum_gen(_cmd, ...) \ + NETDO_ ## _cmd, + + NETDO_CMDS(netdo_cmd_enum_gen) +#undef netdo_cmd_enum_gen + + __NETDO_CMD_MAX +}; + +struct netdo_iface { + TAILQ_ENTRY(netdo_iface) node; + + char name[IFNAMSIZ]; + FILE *cmd[__NETDO_PHASE_MAX][__NETDO_CMD_MAX]; +}; + +TAILQ_HEAD(netdo_ifaces, netdo_iface); + +struct netdo { + FILE *next_fp; + int gen; + struct netdo_ifaces ifaces; + + char path[__NETDO_PHASE_MAX][PATH_MAX]; +}; + +FILE *netdo_get_file(struct netdo *nd, const char *ifname, + enum netdo_phase phase, enum netdo_cmd cmd); + +int netdo_change(struct netdo *nd, struct lyd_node *cifs, struct lyd_node *difs); +int netdo_done(struct netdo *nd); +int netdo_abort(struct netdo *nd); + +int netdo_boot(void); + + +#endif /* CONFD_NETDO_H_ */