confd: ietf-interfaces: Initial work on net-do implementation

This commit is contained in:
Tobias Waldekranz
2023-05-12 14:24:41 +02:00
committed by Joachim Wiberg
parent a5b4c04d08
commit 3192a69970
5 changed files with 417 additions and 4 deletions
+1 -1
View File
@@ -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
+2
View File
@@ -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,
+111 -3
View File
@@ -8,6 +8,8 @@
#include <net/if.h>
#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);
+237
View File
@@ -0,0 +1,237 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <errno.h>
#include <stdarg.h>
#include <libite/lite.h>
#include <libite/queue.h>
#include <libyang/libyang.h>
#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;
}
+66
View File
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef CONFD_NETDO_H_
#define CONFD_NETDO_H_
#include <stdio.h>
#include <net/if.h>
#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_ */