@@ -7,6 +7,7 @@
#include <sys/syscall.h>
#include <errno.h>
+#define AFNETNS_RUN_DIR "/var/run/afnetns"
#define NETNS_RUN_DIR "/var/run/netns"
#define NETNS_ETC_DIR "/etc/netns"
@@ -14,6 +15,10 @@
#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */
#endif
+#ifndef CLONE_NEWAFNET
+#define CLONE_NEWAFNET 0x00001000 /* Clone new afnet context */
+#endif
+
#ifndef MNT_DETACH
#define MNT_DETACH 0x00000002 /* Just detach from the tree */
#endif /* MNT_DETACH */
@@ -256,6 +256,7 @@ int do_each_netns(int (*func)(char *nsname, void *arg), void *arg,
char *int_to_str(int val, char *buf);
int get_guid(__u64 *guid, const char *arg);
int get_real_family(int rtm_type, int rtm_family);
+int cmd_exec(const char *cmd, char **argv, bool do_fork);
int cmd_exec(const char *cmd, char **argv, bool do_fork);
int make_path(const char *path, mode_t mode);
@@ -8,7 +8,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \
iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
- ipvrf.o iplink_xstats.o
+ ipvrf.o iplink_xstats.o ipafnetns.o
RTMONOBJ=rtmon.o
@@ -51,8 +51,8 @@ static void usage(void)
" ip [ -force ] -batch filename\n"
"where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n"
" tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n"
-" netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n"
-" vrf }\n"
+" netns | afnetns | l2tp | fou | macsec | tcp_metrics | token |\n"
+" netconf | ila | vrf }\n"
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
" -h[uman-readable] | -iec |\n"
" -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |\n"
@@ -99,6 +99,7 @@ static const struct cmd {
{ "mroute", do_multiroute },
{ "mrule", do_multirule },
{ "netns", do_netns },
+ { "afnetns", do_afnetns },
{ "netconf", do_ipnetconf },
{ "vrf", do_ipvrf},
{ "help", do_help },
@@ -50,6 +50,7 @@ int do_multiaddr(int argc, char **argv);
int do_multiroute(int argc, char **argv);
int do_multirule(int argc, char **argv);
int do_netns(int argc, char **argv);
+int do_afnetns(int argc, char **argv);
int do_xfrm(int argc, char **argv);
int do_ipl2tp(int argc, char **argv);
int do_ipfou(int argc, char **argv);
new file mode 100644
@@ -0,0 +1,227 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "utils.h"
+#include "ip_common.h"
+#include "namespace.h"
+
+static void usage(void)
+{
+ static const char *help =
+ "Usage: ip afnetns list\n"
+ " ip afnetns add NAME\n"
+ " ip afnetns del NAME\n"
+ " ip afnetns exec NAME cmd ...\n";
+ fputs(help, stderr);
+}
+
+static int afnetns_list(void)
+{
+ struct dirent *entry;
+ DIR *dir;
+
+ dir = opendir(AFNETNS_RUN_DIR);
+ if (!dir)
+ return 0;
+
+ while ((entry = readdir(dir))) {
+ if (!strcmp(entry->d_name, ".") ||
+ !strcmp(entry->d_name, ".."))
+ continue;
+ printf("%s\n", entry->d_name);
+ }
+ closedir(dir);
+
+ return 0;
+}
+
+static int create_afnetns_dir(void)
+{
+ int err;
+ const mode_t mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+
+ err = mkdir(AFNETNS_RUN_DIR, mode);
+ if (!err || errno == EEXIST)
+ return 0;
+
+ fprintf(stderr, "Could not create afnet run dir \"%s\": %s\n",
+ AFNETNS_RUN_DIR, strerror(errno));
+ return err;
+}
+
+static int afnetns_delete(int argc, char **argv)
+{
+ const char *name;
+ char *path;
+ int err;
+
+ if (argc < 1) {
+ fputs("No afnetns name specified\n", stderr);
+ return -1;
+ }
+
+ name = argv[0];
+ err = asprintf(&path, "%s/%s", AFNETNS_RUN_DIR, name);
+ if (err < 0) {
+ perror("asprintf");
+ return err;
+ }
+
+ err = umount2(path, MNT_DETACH);
+ if (err)
+ fprintf(stderr, "Cannot umount afnet namespace file \"%s\": %s\n",
+ path, strerror(errno));
+
+ err = unlink(path);
+ if (err) {
+ fprintf(stderr, "Cannot remove afnet namespace file \"%s\": %s\n",
+ path, strerror(errno));
+ goto out;
+ }
+
+out:
+ free(path);
+ return err;
+}
+
+static int afnetns_add(int argc, char **argv)
+{
+ const char *name;
+ int err, fd;
+ char *path;
+
+ if (argc < 1) {
+ fputs("No afnetns name specified\n", stderr);
+ return -1;
+ }
+
+ err = create_afnetns_dir();
+ if (err)
+ return err;
+
+ name = argv[0];
+ err = asprintf(&path, "%s/%s", AFNETNS_RUN_DIR, name);
+ if (err < 0) {
+ perror("asprintf");
+ return err;
+ }
+
+ fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
+ if (fd < 0) {
+ err = fd;
+ fprintf(stderr, "Cannot create afnetns file \"%s\": %s\n",
+ path, strerror(errno));
+ goto out;
+ }
+ err = close(fd);
+ if (err) {
+ perror("close");
+ goto out;
+ }
+
+ err = unshare(CLONE_NEWAFNET);
+ if (err < 0) {
+ fprintf(stderr, "Failed to create a new afnet namesapce \"%s\": %s\n",
+ name, strerror(errno));
+ goto out;
+ }
+
+ err = mount("/proc/self/ns/afnet", path, "none", MS_BIND, NULL);
+ if (err < 0) {
+ fprintf(stderr, "Bind /proc/self/ns/afnet -> %s failed: %s\n",
+ path, strerror(errno));
+ goto out_delete;
+ }
+
+ err = 0;
+out:
+ free(path);
+ return err;
+out_delete:
+ afnetns_delete(argc, argv);
+ goto out;
+}
+
+static int afnetns_switch(const char *name)
+{
+ int err, ns;
+ char *path;
+
+ err = asprintf(&path, "%s/%s", AFNETNS_RUN_DIR, name);
+ if (err < 0) {
+ perror("asprintf");
+ return err;
+ };
+
+ ns = open(path, O_RDONLY | O_CLOEXEC);
+ if (ns < 0) {
+ fprintf(stderr, "Cannot open afnet namespace \"%s\": %s\n",
+ name, strerror(errno));
+ err = ns;
+ goto out;
+ }
+
+ err = setns(ns, CLONE_NEWAFNET);
+ if (err) {
+ fprintf(stderr, "setting the afnet namespace \"%s\" failed: %s\n",
+ name, strerror(errno));
+ goto out;
+ }
+ err = close(ns);
+ if (err) {
+ perror("close");
+ goto out;
+ }
+
+out:
+ free(path);
+ return err;
+}
+
+static int afnetns_exec(int argc, char **argv)
+{
+ const char *cmd;
+ int err;
+
+ if (argc < 2) {
+ fputs("No netns name and or commands specified\n", stderr);
+ return -1;
+ }
+
+ err = afnetns_switch(argv[0]);
+ if (err)
+ return err;
+
+ cmd = argv[1];
+ return -cmd_exec(cmd, argv + 1, !!batch_mode);
+}
+
+int do_afnetns(int argc, char **argv)
+{
+ if (argc < 1)
+ return afnetns_list();
+
+ if (!matches(*argv, "help")) {
+ usage();
+ return 0;
+ }
+
+ if (!matches(*argv, "list") || !matches(*argv, "show") ||
+ !matches(*argv, "lst"))
+ return afnetns_list();
+
+ if (!matches(*argv, "add"))
+ return afnetns_add(argc-1, argv+1);
+
+ if (!matches(*argv, "delete"))
+ return afnetns_delete(argc-1, argv+1);
+
+ if (!matches(*argv, "exec"))
+ return afnetns_exec(argc-1, argv+1);
+
+ fprintf(stderr, "Command \"%s\" is unkown, try \"ip afnetns help\".\n", *argv);
+ return -1;
+}
@@ -17,7 +17,9 @@
#include <syslog.h>
#include <fcntl.h>
#include <limits.h>
+#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/wait.h>
#include <netinet/in.h>
#include <string.h>
#include <netdb.h>
@@ -1214,3 +1216,37 @@ int get_real_family(int rtm_type, int rtm_family)
return rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6;
}
+
+int cmd_exec(const char *cmd, char **argv, bool do_fork)
+{
+ fflush(stdout);
+ if (do_fork) {
+ int status;
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ exit(1);
+ }
+
+ if (pid != 0) {
+ /* Parent */
+ if (waitpid(pid, &status, 0) < 0) {
+ perror("waitpid");
+ exit(1);
+ }
+
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ }
+
+ exit(1);
+ }
+ }
+
+ if (execvp(cmd, argv) < 0)
+ fprintf(stderr, "exec of \"%s\" failed: %s\n",
+ cmd, strerror(errno));
+ _exit(1);
+}
Like ip netns, ip afnetns ... provides the basic utility features to create, delete afnet namespaces and execute commands inside afnetns. Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org> --- include/namespace.h | 5 ++ include/utils.h | 1 + ip/Makefile | 2 +- ip/ip.c | 5 +- ip/ip_common.h | 1 + ip/ipafnetns.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/utils.c | 36 +++++++++ 7 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 ip/ipafnetns.c