diff --git a/src/net/Makefile b/src/net/Makefile index 348f67ae..5c14a66e 100644 --- a/src/net/Makefile +++ b/src/net/Makefile @@ -1,8 +1,12 @@ LDLIBS = -lite +EXEC = net net: main.o $(CC) -o $@ $^ $(LDLIBS) +check: $(EXEC) + ./test.sh + clean: $(RM) net diff --git a/src/net/README.md b/src/net/README.md index c86d6f12..55625148 100644 --- a/src/net/README.md +++ b/src/net/README.md @@ -7,24 +7,35 @@ a previously configured state to the next while attempting to keep the amount of changes needed to a minimum. -Overview --------- +Concept Overview +---------------- net applies a configuration from `/run/net/next`, where `next` is a file -that holds the number of the current generation. It is up to the user -to create the below structure with commands to run. Consider the case -when `next` contains `0`, net reads all interfaces, in dependency order, -from `/run/net/0/` and runs all the `ip-link` and `ip-addr` commands. -When it is done, it writes `0` to the file `/run/net/gen` and removes -the `next` file to confirm the next generation has been activated. +that holds the number of the current generation. It is up to the user, +i.e., sysrepo, to create the below tree structure with commands to run. -Note: it is currently up to the user to remove any old generation. +The setup we start with and later move `eth4` from `lag0` to `br0`. + + vlan1 + ______/____ + [____br0____] + / / \ \ + eth1 eth2 eth3 lag0 + / \ + eth4 eth5 + +Consider the case when `next` contains `0`, net reads all interfaces, in +dependency order, from `/run/net/0/` running all `ip-link` and `ip-addr` +commands. When done, it writes `0` to the file `/run/net/gen` and then +removes the `next` file to confirm the generation has been activated. + +**Note:** it is currently up to the user to remove any old generation. /run/net/0/ |-- lo/ | |-- deps/ - | |-- ip-link - | `-- ip-addr + | |-- ip-link.up + | `-- ip-addr.up |-- br0/ | |-- deps/ | | |-- eth1 -> ../../eth1 @@ -37,6 +48,9 @@ Note: it is currently up to the user to remove any old generation. | |-- deps/ | |-- ip-link.up | `-- ip-addr.up + |-- ethX/ + | |-- ... + : : |-- lag0/ | |-- deps/ | | |-- eth4 -> ../../eth4 @@ -50,30 +64,67 @@ Note: it is currently up to the user to remove any old generation. `-- ip-addr.up The `deps/` sub-directory for each of the interfaces contains symlinks -to all interfaces that this interface depends on. I.e., when bringing -networking up or down these dependent interfaces are evaluated in order -creating a dependency tree: +to any interfaces this interface may depend on. I.e., those interfaces +are evaluated first. - vlan1 - _____/____ - [___br0____] - / / \ \ - eth1 eth2 eth3 lag0 - / \ - eth4 eth5 - -Essentially, all leaves must be set up before their parents. +Essentially, all leaves must be set up before their parents. Moving a +leaf from one parent to another, e.g., from lag0 to br0, is tricky, it +involves traversing the previous dependency order when removing leaves, +and traversing the next dependency order when addning, see next section +for an example. -Concepts --------- +Example +------- -Conceptually, net is built to complement sysrepo. The idea is to listen -to added, deleted, modified states in sysrepo changes to a candidate -configuration and generate the `next` generation. +net is built to complement sysrepo. The idea is to listen to any added, +deleted, modified states in sysrepo changes to a candidate configuration +and generate the `next` generation. However, as mentioned previously, +when moving leaves between parents we must do so in the dependency order +of the current generation. +So, the user (sysrepo) needs to add `.dn` scripts in the current tree, +and `.up` scripts in the next. - /run/net// +In our example, interface `eth4` is moved from `lag0` to `br0`, so we +need to run `eth4/ip-link.dn` in the current generation first to remove +`eth4` from `lag0` before its `ip-link.up` script in the next generation +sets `eth4` as a bridge member instead. + +We traverse the current generation and execute all `.dn` scripts: + + /run/net// + |-- lo/ + | `-- deps/ + |-- br0/ + | |-- deps/ + | | |-- eth1 -> ../../eth1 + | | |-- eth2 -> ../../eth2 + | | |-- eth3 -> ../../eth3 + | | `-- lag0 -> ../../lag0 + | `-- ip-link.up + |-- eth0/ + | |-- deps/ + | |-- ip-link.up + | `-- ip-addr.up + |-- eth4/ + | |-- deps/ + | |-- ip-link.dn + | `-- ip-link.up + |-- lag0/ + | |-- deps/ + | | |-- eth4 -> ../../eth4 + | | `-- eth5 -> ../../eth5 + | `-- ip-link.up + `-- vlan1/ + |-- deps/ + | `-- br0 -> ../../br0 + |-- ip-link.up + `-- ip-addr.up + +Now we can run all the `.up` scripts in the next generation: + + /run/net// |-- lo/ | |-- deps/ | |-- ip-link.up @@ -105,11 +156,3 @@ configuration and generate the `next` generation. `-- ip-addr.up -Interfaces can be evaluated in any order. The `deps/` directory of each -is interface is always evaluated first. For each dependency, `foo.dn` -is evaluated first and `foo.up` is evaluated last. Any `ip-link` script -is also evaluated before any `ip-addr` script. - -In the case above, interface `eth4` has been moved from `lag0` to `br0`, -so we need to run `eth4/ip-link.dn` to remove `eth4` from `lag0` before -its `ip-link.up` script sets `eth4` as a bridge member instead. diff --git a/src/net/gen.sh b/src/net/gen.sh deleted file mode 100755 index eaacf6fe..00000000 --- a/src/net/gen.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# Generate test directory structure for verifying the net tool - -gensh() -{ - cat <<-EOF >"$1" - #!/bin/sh - echo "Running \$0 ..." - EOF - chmod +x "$1" -} - -mkdir -p /tmp/net/1 -echo 1 > /tmp/net/next - -mkdir -p /tmp/net/1/lo/deps -mkdir -p /tmp/net/1/eth0/deps -mkdir -p /tmp/net/1/eth1/deps -mkdir -p /tmp/net/1/eth2/deps - -mkdir -p /tmp/net/1/br0/deps -ln -sf ../../eth1 /tmp/net/1/br0/deps/ -ln -sf ../../eth2 /tmp/net/1/br0/deps/ - -mkdir -p /tmp/net/1/vlan1/deps -ln -sf ../../br0 /tmp/net/1/vlan1/deps/ - -gensh /tmp/net/1/lo/ip-link.up -gensh /tmp/net/1/eth0/ip-link.up -gensh /tmp/net/1/eth1/ip-link.up -gensh /tmp/net/1/eth2/ip-link.up -gensh /tmp/net/1/br0/ip-link.up -gensh /tmp/net/1/vlan1/ip-link.up diff --git a/src/net/main.c b/src/net/main.c index 0eae4307..8b0943c8 100644 --- a/src/net/main.c +++ b/src/net/main.c @@ -1,11 +1,12 @@ #include #include #include +#include #include #include #define _PATH_NET "/run/net" -#define DEBUG 1 +#define DEBUG 0 #define dbg(fmt, args...) if (DEBUG) warnx(fmt, ##args) static char **handled; @@ -51,7 +52,7 @@ static int if_find(char *ifname) return 0; } -static int deps(char *ipath, char *ifname, char *action) +static int deps(char *ipath, char *ifname, const char *action) { char path[strlen(ipath) + 42]; int num, rc = -1; @@ -94,47 +95,94 @@ done: return rc; } -static int activate(const char *net, char *gen) +static int iter(char *path, size_t len, const char *action) +{ + char **files; + int rc = 0; + int num; + + num = dir(path, NULL, NULL, &files, 0); + if_alloc(num); + + for (int j = 0; j < num; j++) { + char *ifname = files[j]; + char ipath[len]; + + snprintf(ipath, sizeof(ipath), "%s/%s", path, ifname); + dbg("Calling deps(%s, %s, %s)", ipath, ifname, action); + rc += deps(ipath, ifname, action); + free(ifname); + } + + if_free(); + + return rc; +} + +static int deactivate(const char *net, char *gen) { char path[strlen(net) + strlen(gen) + 5 + IFNAMSIZ]; char *action[] = { "ip-addr.dn", "ip-link.dn", - "ip-link.up", - "ip-addr.up", }; - char **files; int rc = 0; snprintf(path, sizeof(path), "%s/%s", net, gen); - - for (size_t i = 0; i < NELEMS(action); i++) { - char *act = action[i]; - int num; - - num = dir(path, NULL, NULL, &files, 0); - if_alloc(num); - - for (int j = 0; j < num; j++) { - char ipath[sizeof(path)]; - char *ifname = files[j]; - - snprintf(ipath, sizeof(ipath), "%s/%s", path, ifname); - rc += deps(ipath, ifname, act); - free(ifname); - } - - if_free(); - } + for (size_t i = 0; i < NELEMS(action); i++) + rc += iter(path, sizeof(path), action[i]); return rc; } +static int activate(const char *net, char *gen) +{ + char path[strlen(net) + strlen(gen) + 5 + IFNAMSIZ]; + char *action[] = { + "ip-link.up", + "ip-addr.up", + }; + int rc = 0; + + snprintf(path, sizeof(path), "%s/%s", net, gen); + for (size_t i = 0; i < NELEMS(action); i++) + rc += iter(path, sizeof(path), action[i]); + + return rc; +} + +static int load_gen(const char *net, char *gen, char *buf, size_t len) +{ + FILE *fp; + + fp = fopenf("r", "%s/%s", net, gen); + if (!fp) + return EX_OSFILE; + if (!fgets(buf, len, fp)) + return EX_IOERR; + fclose(fp); + chomp(buf); + + return 0; +} + +static int save_gen(const char *net, char *gen, char *buf) +{ + FILE *fp; + + fp = fopenf("w", "%s/%s", net, gen); + if (!fp) + return -1; + fprintf(fp, "%s\n", buf); + fclose(fp); + + return 0; +} + int main(void) { const char *net = _PATH_NET; - char next[512]; - FILE *fp; + char curr[512], next[512]; int rc; if (getenv("NET_DIR")) @@ -144,24 +192,31 @@ int main(void) err(1, "makedir"); } - fp = fopenf("r", "%s/next", net); - if (!fp) + if ((rc = load_gen(net, "next", next, sizeof(next)))) { + if (rc == EX_IOERR) + warnx("missing next generation"); exit(0); /* nothing to do */ + } - if (!fgets(next, sizeof(next), fp)) - err(1, "missing next generation"); - fclose(fp); + if ((rc = load_gen(net, "gen", curr, sizeof(curr)))) { + if (rc == EX_IOERR) + errx(rc, "missing current generation"); + /* no current generation */ + } else { + rc = deactivate(net, curr); + if (rc) + err(1, "failed deactivating current generation"); + } - rc = activate(net, chomp(next)); + rc = activate(net, next); if (rc) err(1, "failed activating next generation"); - fp = fopenf("w", "%s/gen", net); - if (!fp) + if (save_gen(net, "gen", next)) err(1, "next generation applied, failed current"); - fprintf(fp, "%s\n", next); - fclose(fp); + if (fremove("%s/next", net)) + err(1, "failed removing %s/next", net); - return fremove("%s/next", net); + return 0; } diff --git a/src/net/test.sh b/src/net/test.sh new file mode 100755 index 00000000..c008f527 --- /dev/null +++ b/src/net/test.sh @@ -0,0 +1,101 @@ +#!/bin/sh +# Generate test directory structure for verifying the net tool +set -e + +NET_DIR=/tmp/net +export NET_DIR + +gensh() +{ + cat <<-EOF >"$1" + #!/bin/sh + echo "Running \$0 ..." + EOF + chmod +x "$1" +} + +check() +{ + if ./net; then + printf "\n[\033[1;32m OK \033[0m] Checking %s\n" "$1" + return 0 + fi + + printf "\n[\033[1;31FAIL\033[0m] Checking %s\n" "$1" + return 1 +} + +echo "Verify that all leaves and dependencies are" +echo "evaluated first:" +echo " vlan1" +echo " ______/____" +echo " [____br0____]" +echo " / / \\ \\" +echo "lo eth0 eth1 eth2 eth3 lag0" +echo " / \\" +echo " eth4 eth5" +printf "_______________________________________________\n\n" + +mkdir -p $NET_DIR/0 +echo 0 > $NET_DIR/next + +mkdir -p $NET_DIR/0/lo/deps +gensh $NET_DIR/0/lo/ip-link.up + +mkdir -p $NET_DIR/0/eth0/deps +mkdir -p $NET_DIR/0/eth1/deps +mkdir -p $NET_DIR/0/eth2/deps +mkdir -p $NET_DIR/0/eth3/deps +mkdir -p $NET_DIR/0/eth4/deps + +gensh $NET_DIR/0/eth0/ip-link.up +gensh $NET_DIR/0/eth1/ip-link.up +gensh $NET_DIR/0/eth2/ip-link.up +gensh $NET_DIR/0/eth3/ip-link.up +gensh $NET_DIR/0/eth4/ip-link.up + +mkdir -p $NET_DIR/0/lag0/deps +ln -sf ../../eth3 $NET_DIR/0/lag0/deps/ +ln -sf ../../eth4 $NET_DIR/0/lag0/deps/ +gensh $NET_DIR/0/lag0/ip-link.up + +mkdir -p $NET_DIR/0/br0/deps +ln -sf ../../eth1 $NET_DIR/0/br0/deps/ +ln -sf ../../eth2 $NET_DIR/0/br0/deps/ +ln -sf ../../lag0 $NET_DIR/0/br0/deps/ +gensh $NET_DIR/0/br0/ip-link.up + +mkdir -p $NET_DIR/0/vlan1/deps +ln -sf ../../br0 $NET_DIR/0/vlan1/deps/ +gensh $NET_DIR/0/vlan1/ip-link.up + +check "initial startup, gen 0" + +printf "_______________________________________________\n\n" +mkdir -p $NET_DIR/1 + +mkdir -p $NET_DIR/1/lo/deps +mkdir -p $NET_DIR/1/eth0/deps +mkdir -p $NET_DIR/1/eth1/deps +mkdir -p $NET_DIR/1/eth2/deps +mkdir -p $NET_DIR/1/eth3/deps +mkdir -p $NET_DIR/1/eth4/deps + +mkdir -p $NET_DIR/1/lag0/deps +ln -sf ../../eth4 $NET_DIR/1/lag0/deps/ + +mkdir -p $NET_DIR/1/br0/deps +ln -sf ../../eth1 $NET_DIR/1/br0/deps/ +ln -sf ../../eth2 $NET_DIR/1/br0/deps/ +ln -sf ../../eth3 $NET_DIR/1/br0/deps/ + +mkdir -p $NET_DIR/1/vlan1/deps +ln -sf ../../br0 $NET_DIR/1/vlan1/deps/ + +gensh $NET_DIR/0/eth3/ip-link.dn +gensh $NET_DIR/1/eth3/ip-link.up +echo 1 > $NET_DIR/next + +check "move eth3 from lag0 to br0" + +exit 0