mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-01 13:23:01 +02:00
confd: ietf-interfaces: Initial work on net-do implementation
This commit is contained in:
committed by
Joachim Wiberg
parent
a5b4c04d08
commit
3192a69970
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
Reference in New Issue
Block a user