From 75a1b231da674c089289658b8ef0fdd93071fc05 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Fri, 14 Apr 2023 12:46:31 +0200 Subject: [PATCH] src/net: add commands; do, up/down with optional ifname(s) Calling net prevsiouly meant `net do`, the new separation is to be able to add `net up` and `net down` commands, with optional interface args. The idea is to support the same semantics as `ifup -a` of `ifup foo`. Signed-off-by: Joachim Wiberg --- src/net/main.c | 246 ++++++++++++++++++++++++++++++++++++++++++------ src/net/test.sh | 2 +- 2 files changed, 216 insertions(+), 32 deletions(-) diff --git a/src/net/main.c b/src/net/main.c index ca90c5ea..fd5a624e 100644 --- a/src/net/main.c +++ b/src/net/main.c @@ -4,11 +4,26 @@ #include #include #include +#include #define _PATH_NET "/run/net" #define dbg(fmt, args...) if (debug) warnx(fmt, ##args) #define log(fmt, args...) if (verbose) warnx(fmt, ##args) +typedef enum { + INVAL = -1, + DO = 0, + UP = 1, + DOWN = 2, +} cmd_t; + +struct iface { + TAILQ_ENTRY(iface) link; + char ifname[IFNAMSIZ]; +}; + +static TAILQ_HEAD(iflist, iface) iface_list = TAILQ_HEAD_INITIALIZER(iface_list); + static char **handled; static int if_num; static int debug; @@ -140,6 +155,7 @@ static int dir_filter(const char *file) char *files[] = { "deps", "rdeps", + "admin-state", }; size_t i; @@ -316,39 +332,9 @@ static void pipe_init(void) err(1, "failed starting bridge command pipe"); } -static int usage(int code) -{ - printf("Usage: %s [-dh]\n" - "\n" - "Options:\n" - " -d Debug\n" - " -h This help text\n" - " -v Verbose, show actions taken\n" - "\n", __progname); - - return code; -} - -int main(int argc, char *argv[]) +static const char *getnet(void) { const char *net = _PATH_NET; - char curr[512], next[512]; - int rc, c; - - while ((c = getopt(argc, argv, "dhv")) != EOF) { - switch (c) { - case 'd': - debug = 1; - break; - case 'h': - return usage(0); - case 'v': - verbose = 1; - break; - default: - return usage(1); - } - } if (getenv("NET_DIR")) net = getenv("NET_DIR"); @@ -357,6 +343,120 @@ int main(int argc, char *argv[]) err(1, "makedir"); } + return net; +} + +static void addif(char *ifname) +{ + struct iface *entry; + + entry = malloc(sizeof(*entry)); + if (!entry) + err(1, "malloc"); + + strlcpy(entry->ifname, ifname, sizeof(entry->ifname)); + TAILQ_INSERT_TAIL(&iface_list, entry, link); +} + +static void freeifs(void) +{ + struct iface *iface, *tmp; + + TAILQ_FOREACH_SAFE(iface, &iface_list, link, tmp) { + TAILQ_REMOVE(&iface_list, iface, link); + free(iface); + } +} + +/* build list from current generation */ +static void getifs(void) +{ + const char *net = getnet(); + char **files; + int num; + + num = dir(net, NULL, dir_filter, &files, 0); + for (int i = 0; i < num; i++) + addif(files[i]); +} + +static char *ifadmin(const char *ifname, char *buf, size_t len) +{ + const char *net = getnet(); + + if (!net) + return NULL; + + snprintf(buf, len, "%s/%s/admin-state", net, ifname); + return buf; +} + +/* is the interface eligible for being taken up/down? */ +static int allowed(const char *ifname) +{ + char buf[128]; + int rc = 1; + FILE *fp; + + if (!ifadmin(ifname, buf, sizeof(buf))) + goto fail; + + fp = fopen(buf, "r"); + if (fp) { + if (fgets(buf, sizeof(buf), fp)) { + chomp(buf); + if (!strcmp(buf, "disabled")) + rc = 0; + } + fclose(fp); + } +fail: + return rc; +} + +static int ifupdown(int updown) +{ + struct iface *iface; + int rc = 0; + + if (TAILQ_EMPTY(&iface_list)) + getifs(); + + TAILQ_FOREACH(iface, &iface_list, link) { + const char *action; + int result; + + if (!allowed(iface->ifname)) + continue; + + action = updown ? "up" : "down"; + result = systemf("ip link set %s %s", iface->ifname, action); + if (!result) { + char buf[128]; + FILE *fp; + + if (!ifadmin(iface->ifname, buf, sizeof(buf))) + continue; + + fp = fopen(buf, "w"); + if (fp) { + fprintf(fp, "%s\n", action); + fclose(fp); + } + } + + rc += result; + } + + return rc; +} + +static int activate_next(void) +{ + const char *net = getnet(); + char curr[512], next[512]; + int rc; + pipe_init(); if ((rc = load_gen(net, "next", next, sizeof(next)))) { @@ -390,3 +490,87 @@ int main(int argc, char *argv[]) return 0; } + +static int act(cmd_t cmd) +{ + switch (cmd) { + case DO: + return activate_next(); + case UP: + return ifupdown(1); + case DOWN: + return ifupdown(0); + default: + break; + } + + freeifs(); + + return EX_USAGE; +} + +static int usage(int code) +{ + printf("Usage: %s [-dh] [do | (up | down [ifname ...])]\n" + "\n" + "Options:\n" + " -a Act on all interfaces, ignored, for compat only.\n" + " -d Debug\n" + " -h This help text\n" + " -v Verbose, show actions taken\n" + "\n" + "Commands:\n" + " do Activate next network generation\n" + " up Bring up one/many or all interfaces in the current generation\n" + " down Take down one/many or all interfaces in the current generation\n" + "\n" + "Args:\n" + " ifname Zero, one, or many interface names to act on.\n" + "\n", __progname); + + return code; +} + +int main(int argc, char *argv[]) +{ + cmd_t cmd = INVAL; + int c; + + while ((c = getopt(argc, argv, "adhv")) != EOF) { + switch (c) { + case 'a': + /* compat with ifup/ifdown */ + break; + case 'd': + debug = 1; + break; + case 'h': + return usage(0); + case 'v': + verbose = 1; + break; + default: + return usage(1); + } + } + + for (c = optind; c < argc; c++) { + if (cmd == INVAL) { + if (!strcmp("do", argv[c])) + cmd = DO; + if (!strcmp("up", argv[c])) + cmd = UP; + if (!strcmp("down", argv[c])) + cmd = DOWN; + + continue; + } + + addif(argv[c]); + } + + if (cmd == INVAL) + return usage(EX_USAGE); + + return act(cmd); +} diff --git a/src/net/test.sh b/src/net/test.sh index e39b069a..837f728b 100755 --- a/src/net/test.sh +++ b/src/net/test.sh @@ -15,7 +15,7 @@ gensh() check() { - if ./net -v; then + if ./net -v do; then printf "\n[\033[1;32m OK \033[0m] Checking %s\n" "$1" return 0 fi