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 <han...@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

diff --git a/include/namespace.h b/include/namespace.h
index 51324b21ba0cd5..acecc8c1f0d2b8 100644
--- a/include/namespace.h
+++ b/include/namespace.h
@@ -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 */
diff --git a/include/utils.h b/include/utils.h
index 22369e0b4e0374..59fdd76b502b3c 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -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);
diff --git a/ip/Makefile b/ip/Makefile
index 4276a34b529e3f..4da6f33968ffe1 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -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
 
diff --git a/ip/ip.c b/ip/ip.c
index 07050b07592ac1..6aa8aaab4c03f9 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -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 },
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 5a39623aa21d9f..1f59db40038ef2 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -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);
diff --git a/ip/ipafnetns.c b/ip/ipafnetns.c
new file mode 100644
index 00000000000000..5b7a7e59bc947a
--- /dev/null
+++ b/ip/ipafnetns.c
@@ -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;
+}
diff --git a/lib/utils.c b/lib/utils.c
index 6d5642f4f1f3fa..d80618f7c485a4 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -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);
+}
-- 
2.9.3

Reply via email to