src/net: fixes after spec. audit by wkz

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2023-04-08 10:53:44 +02:00
parent caa07e8b20
commit 82c8423cea
5 changed files with 278 additions and 108 deletions
+4
View File
@@ -1,8 +1,12 @@
LDLIBS = -lite
EXEC = net
net: main.o
$(CC) -o $@ $^ $(LDLIBS)
check: $(EXEC)
./test.sh
clean:
$(RM) net
+80 -37
View File
@@ -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/<NUM+1>/
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/<GEN>/
|-- 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/<GEN+1>/
|-- 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.
-33
View File
@@ -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
+93 -38
View File
@@ -1,11 +1,12 @@
#include <err.h>
#include <libgen.h>
#include <stdlib.h>
#include <sysexits.h>
#include <net/if.h>
#include <libite/lite.h>
#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;
}
+101
View File
@@ -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