This patch introduces helper tools for creating and working
with namespaces (ltp/testcases/kernel/containers/share) and
also includes documentation (ltp/doc/namespaces-helper-tools.txt):

* ns_create:
    Creates a child process in the new specified namespace(s),
    child is then daemonized and is running in the background.
    PID of the daemonized child process is printed on the stdout.
    As the new namespace(s) is(are) maintained by the daemonized
    child process it(they) can be removed by killing this process.
* ns_exec:
    Enters the namespace(s) of a process specified by a PID and
    then executes the indicated program inside that namespace(s).
* ns_ifmove:
    Moves a network interface to the namespace of a process
    specified by a PID.

Example usage:
==============
$ myns=$(ns_create net ipc)

$ ip link add veth0 type veth peer name veth1
$ ns_exec $myns ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

$ ns_ifmove veth1 $myns
$ ns_exec $myns ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth1: <BROADCAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 6a:0a:45:ed:6e:d0 brd ff:ff:ff:ff:ff:ff

$ ip link del veth0
$ kill -9 $myns
==============

The only requirement from kernel side is the support of "setns"
syscall. There is no need to have util-linux (unshare, nsenter)
installed. This way test cases utilizing namespaces can be
executed even on older kernels which do not provide required
tools for working with namespaces.

Signed-off-by: Matus Marhefka <mmarh...@redhat.com>
---
 doc/namespaces-helper-tools.txt                 |  58 ++++++++++
 include/lapi/namespaces_constants.h             |  38 +++++++
 testcases/kernel/containers/libclone/libclone.h |  21 +---
 testcases/kernel/containers/share/.gitignore    |   3 +
 testcases/kernel/containers/share/Makefile      |  22 ++++
 testcases/kernel/containers/share/ns_create.c   | 135 ++++++++++++++++++++++
 testcases/kernel/containers/share/ns_exec.c     | 143 ++++++++++++++++++++++++
 testcases/kernel/containers/share/ns_ifmove.c   | 121 ++++++++++++++++++++
 8 files changed, 521 insertions(+), 20 deletions(-)
 create mode 100644 doc/namespaces-helper-tools.txt
 create mode 100644 include/lapi/namespaces_constants.h
 create mode 100644 testcases/kernel/containers/share/.gitignore
 create mode 100644 testcases/kernel/containers/share/Makefile
 create mode 100644 testcases/kernel/containers/share/ns_create.c
 create mode 100644 testcases/kernel/containers/share/ns_exec.c
 create mode 100644 testcases/kernel/containers/share/ns_ifmove.c

diff --git a/doc/namespaces-helper-tools.txt b/doc/namespaces-helper-tools.txt
new file mode 100644
index 0000000..3dbe617
--- /dev/null
+++ b/doc/namespaces-helper-tools.txt
@@ -0,0 +1,58 @@
+LTP namespaces helper tools
+===========================
+
+
+1. Introduction
+---------------
+
+LTP provides helper tools for creating and working with namespaces. These are
+located in ltp/testcases/kernel/containers/share directory and include:
+
+* ns_create
+** creates a child process in the new specified namespace(s)
+** child is then daemonized and is running in the background
+** PID of the daemonized child process is printed on the stdout
+** the new namespace(s) is(are) maintained by the daemonized child process
+** namespace(s) can be removed by killing the daemonized process
+* ns_exec
+** enters the namespace(s) of a process specified by a PID
+** then executes the indicated program inside that namespace(s)
+* ns_ifmove
+** moves a network interface to the namespace of a process specified by a PID
+
+Purpose of these helper tools is the ability to execute test cases utilizing
+namespaces even on older kernels which do not provide tooling (i.e. unshare(1)
+or nsenter(1) from util-linux) required for working with namespaces. The only
+requirement from kernel side is the support of "setns" syscall.
+
+2. Example usage
+----------------
+
+The following code shows how test cases can use the namespaces helper tools:
+
+[source,sh]
+-------------------------------------------------------------------------------
+# Creates a new network and ipc namespace and stores the PID of the daemonized
+# process inside that namespace into variable myns
+myns=$(ns_create net ipc)
+
+ip link add veth0 type veth peer name veth1
+
+# Executes command 'ip a' inside the namespace specified by PID in myns 
variable
+ns_exec $myns ip a
+1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
+    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+
+# Moves interface veth1 into the namespace specified by PID in myns variable
+ns_ifmove veth1 $myns
+ns_exec $myns ip a
+1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
+    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+6: veth1: <BROADCAST> mtu 1500 qdisc noop state DOWN qlen 1000
+    link/ether 6a:0a:45:ed:6e:d0 brd ff:ff:ff:ff:ff:ff
+
+# cleanup
+ip link del veth0
+# By killing the daemonized process we also delete the namespace
+kill -9 $myns
+-------------------------------------------------------------------------------
diff --git a/include/lapi/namespaces_constants.h 
b/include/lapi/namespaces_constants.h
new file mode 100644
index 0000000..52ee383
--- /dev/null
+++ b/include/lapi/namespaces_constants.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***********************************************************************/
+
+#ifndef __NAMESPACES_CONSTANTS_H__
+#define __NAMESPACES_CONSTANTS_H__
+
+#ifndef CLONE_NEWIPC
+#  define CLONE_NEWIPC 0x08000000
+#endif
+#ifndef CLONE_NEWNS
+#  define CLONE_NEWNS  0x00020000
+#endif
+#ifndef CLONE_NEWNET
+#  define CLONE_NEWNET 0x40000000
+#endif
+#ifndef CLONE_NEWPID
+#  define CLONE_NEWPID 0x20000000
+#endif
+#ifndef CLONE_NEWUSER
+#  define CLONE_NEWUSER        0x10000000
+#endif
+#ifndef CLONE_NEWUTS
+#  define CLONE_NEWUTS 0x04000000
+#endif
+
+#endif /* __NAMESPACES_CONSTANTS_H__ */
diff --git a/testcases/kernel/containers/libclone/libclone.h 
b/testcases/kernel/containers/libclone/libclone.h
index 3a38bc9..70d52e0 100644
--- a/testcases/kernel/containers/libclone/libclone.h
+++ b/testcases/kernel/containers/libclone/libclone.h
@@ -28,6 +28,7 @@
 #include <signal.h>
 #include "linux_syscall_numbers.h"
 #include "test.h"
+#include "lapi/namespaces_constants.h"
 
 #define T_UNSHARE 0
 #define T_CLONE 1
@@ -55,26 +56,6 @@
 #define __NR_unshare SYS_unshare
 #endif
 
-#ifndef CLONE_NEWUTS
-#define CLONE_NEWUTS           0x04000000
-#endif
-
-#ifndef CLONE_NEWIPC
-#define CLONE_NEWIPC           0x08000000
-#endif
-
-#ifndef CLONE_NEWUSER
-#define CLONE_NEWUSER          0x10000000
-#endif
-
-#ifndef CLONE_NEWPID
-#define CLONE_NEWPID            0x20000000
-#endif
-
-#ifndef CLONE_NEWNET
-#define CLONE_NEWNET           0x40000000
-#endif
-
 /*
  * Run fn1 in a unshared environmnent, and fn2 in the original context
  * Fn2 may be NULL.
diff --git a/testcases/kernel/containers/share/.gitignore 
b/testcases/kernel/containers/share/.gitignore
new file mode 100644
index 0000000..0d5ecf0
--- /dev/null
+++ b/testcases/kernel/containers/share/.gitignore
@@ -0,0 +1,3 @@
+/ns_ifmove
+/ns_create
+/ns_exec
diff --git a/testcases/kernel/containers/share/Makefile 
b/testcases/kernel/containers/share/Makefile
new file mode 100644
index 0000000..962d688
--- /dev/null
+++ b/testcases/kernel/containers/share/Makefile
@@ -0,0 +1,22 @@
+# Copyright (c) 2015 Red Hat, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of version 2 the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##############################################################################
+top_srcdir              ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+include $(abs_srcdir)/../Makefile.inc
+
+LDLIBS                  := -lltp
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/containers/share/ns_create.c 
b/testcases/kernel/containers/share/ns_create.c
new file mode 100644
index 0000000..ceac7e6
--- /dev/null
+++ b/testcases/kernel/containers/share/ns_create.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by Matus Marhefka <mmarh...@redhat.com>
+ *
+ ***********************************************************************
+ * Creates a child process in the new specified namespace(s), child is then
+ * daemonized and is running in the background. PID of the daemonized child
+ * process is printed on the stdout. As the new namespace(s) is(are) maintained
+ * by the daemonized child process it(they) can be removed by killing this
+ * process.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <strings.h>
+#include <errno.h>
+#include "test.h"
+#include "lapi/namespaces_constants.h"
+
+char *TCID = "ns_create";
+
+struct param {
+       const char *name;
+       int flag;
+};
+
+struct param params[] = {
+       {"ipc",  CLONE_NEWIPC},
+       {"mnt",  CLONE_NEWNS},
+       {"net",  CLONE_NEWNET},
+       {"pid",  CLONE_NEWPID},
+       {"user", CLONE_NEWUSER},
+       {"uts",  CLONE_NEWUTS},
+       {NULL,   0}
+};
+
+
+struct param *get_param(const char *name)
+{
+       int i;
+
+       for (i = 0; params[i].name; i++) {
+               if (!strcasecmp(params[i].name, name))
+                       return params + i;
+       }
+
+       return NULL;
+}
+
+void print_help(void)
+{
+       int i;
+
+       printf("usage: ns_create <%s", params[0].name);
+
+       for (i = 1; params[i].name; i++)
+               printf("|%s", params[i].name);
+       printf(">\n");
+}
+
+
+static int child_fn(void *arg)
+{
+       int i;
+
+       if (setsid() == -1) {
+               tst_resm(TINFO | TERRNO, "setsid");
+               exit(1);
+       }
+
+       if (chdir("/") == -1) {
+               tst_resm(TINFO | TERRNO, "chdir");
+               exit(1);
+       }
+
+       /* close all inherrited file descriptors */
+       for (i = 0; i < sysconf(_SC_OPEN_MAX); i++)
+               close(i);
+
+       pause();
+       return 0;
+}
+
+/*
+ * ./ns_create <ipc|mnt|net|pid|user|uts>
+ * where all possible namespaces are: ipc, mnt, net, pid, user, uts.
+ */
+int main(int argc, char *argv[])
+{
+       int pid, flags, i;
+
+       if (argc < 2) {
+               print_help();
+               return 1;
+       }
+
+       flags = 0;
+       for (i = 1; argv[i]; i++) {
+               struct param *p = get_param(argv[i]);
+
+               if (!p) {
+                       tst_resm(TINFO, "Unknown parameter: %s", argv[i]);
+                       print_help();
+                       return 1;
+               }
+
+               flags |= p->flag;
+       }
+
+       pid = ltp_clone_quick(flags | SIGCHLD, (void *)child_fn, NULL);
+       if (pid == -1) {
+               tst_resm(TINFO | TERRNO, "ltp_clone_quick");
+               return 1;
+       }
+
+       printf("%d", pid);
+       return 0;
+}
diff --git a/testcases/kernel/containers/share/ns_exec.c 
b/testcases/kernel/containers/share/ns_exec.c
new file mode 100644
index 0000000..6e9dd98
--- /dev/null
+++ b/testcases/kernel/containers/share/ns_exec.c
@@ -0,0 +1,143 @@
+/* Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by Matus Marhefka <mmarh...@redhat.com>
+ *
+ ***********************************************************************
+ * Enters the namespace(s) of a process specified by a PID and then executes
+ * the indicated program inside that namespace(s).
+ *
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "test.h"
+#include "linux_syscall_numbers.h"
+#include "lapi/namespaces_constants.h"
+
+#define PROC_PATH "/proc"
+#define NS_TOTAL 6
+
+char *TCID = "ns_exec";
+int ns_fd[NS_TOTAL];
+int ns_fd_index;
+
+
+static int open_ns_fd(const char *pid, const char *ns)
+{
+       int fd;
+       char file_buf[30];
+
+       sprintf(file_buf, "%s/%s/ns/%s", PROC_PATH, pid, ns);
+
+       fd = open(file_buf, O_RDONLY);
+       if (fd > 0) {
+               ns_fd[ns_fd_index] = fd;
+               ++ns_fd_index;
+               return 0;
+       } else if (fd == -1 && errno != ENOENT) {
+               tst_resm(TINFO | TERRNO, "open");
+               return -1;
+       }
+
+       return 0;
+}
+
+static void close_ns_fd(void)
+{
+       int i;
+
+       for (i = 0; i < ns_fd_index; i++)
+               close(ns_fd[i]);
+}
+
+static int child_fn(void *arg)
+{
+       char **args = (char **)arg;
+
+       execvp(args[2], args+2);
+       tst_resm(TINFO | TERRNO, "execvp");
+       return 1;
+}
+
+/*
+ * ./ns_exec <NS_PID> <PROGRAM> [ARGS]
+ */
+int main(int argc, char *argv[])
+{
+       int i, rv, pid;
+
+       rv = syscall(__NR_setns, -1, 0);
+       if (rv == -1 && errno == ENOSYS) {
+               tst_resm(TINFO, "setns is not supported in the kernel");
+               return 1;
+       }
+
+       if (argc < 3) {
+               tst_resm(TINFO, "%s <NS_PID> <PROGRAM> [ARGS]\n", argv[0]);
+               return 1;
+       }
+
+       rv = 0;
+       memset(ns_fd, 0, sizeof(ns_fd));
+       rv |= open_ns_fd(argv[1], "ipc");
+       rv |= open_ns_fd(argv[1], "mnt");
+       rv |= open_ns_fd(argv[1], "net");
+       rv |= open_ns_fd(argv[1], "pid");
+       rv |= open_ns_fd(argv[1], "user");
+       rv |= open_ns_fd(argv[1], "uts");
+       if (rv != 0)
+               return 1;
+
+       if (ns_fd_index == 0) {
+               tst_resm(TINFO, "no namespace entries in /proc/%s/ns/",
+                        argv[1]);
+               close_ns_fd();
+               return 1;
+       }
+
+       for (i = 0; i < ns_fd_index; i++) {
+               if (syscall(__NR_setns, ns_fd[i], 0) == -1) {
+                       tst_resm(TINFO | TERRNO, "setns");
+                       close_ns_fd();
+                       return 1;
+               }
+       }
+
+       pid = ltp_clone_quick(SIGCHLD, (void *)child_fn, (void *)argv);
+       if (pid == -1) {
+               tst_resm(TINFO | TERRNO, "ltp_clone_quick");
+               close_ns_fd();
+               return 1;
+       }
+
+       if (waitpid(pid, &rv, 0) == -1) {
+               tst_resm(TINFO | TERRNO, "waitpid");
+               return 1;
+       }
+
+       close_ns_fd();
+
+       if (WIFEXITED(rv))
+               return WEXITSTATUS(rv);
+
+       return 0;
+}
diff --git a/testcases/kernel/containers/share/ns_ifmove.c 
b/testcases/kernel/containers/share/ns_ifmove.c
new file mode 100644
index 0000000..6d47545
--- /dev/null
+++ b/testcases/kernel/containers/share/ns_ifmove.c
@@ -0,0 +1,121 @@
+/* Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by Matus Marhefka <mmarh...@redhat.com>
+ *
+ ***********************************************************************
+ * Moves a network interface to the namespace of a process specified by a PID.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+#include "test.h"
+
+char *TCID = "ns_linkset";
+
+struct {
+       struct nlmsghdr nh;
+       struct ifinfomsg ifi;
+       char attrbuf[512];
+} req;
+
+
+int get_intf_index_from_name(const char *intf_name)
+{
+       struct ifreq ifr;
+       int sock_fd;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, intf_name, sizeof(ifr.ifr_name) - 1);
+       ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
+
+       sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (sock_fd == -1) {
+               tst_resm(TINFO | TERRNO, "socket");
+               return -1;
+       }
+
+       /* gets interface index */
+       if (ioctl(sock_fd, SIOCGIFINDEX, &ifr) == -1) {
+               tst_resm(TINFO | TERRNO, "ioctl");
+               close(sock_fd);
+               return -1;
+       }
+
+       close(sock_fd);
+       return ifr.ifr_ifindex;
+}
+
+/*
+ * ./ns_ifmove <INTERFACE_NAME> <NAMESPACE_PID>
+ */
+int main(int argc, char **argv)
+{
+       struct rtattr *rta;
+       int intf_index, pid, rtnetlink_socket;
+
+       if (argc != 3) {
+               tst_resm(TINFO, "%s <INTERFACE_NAME> <NAMESPACE_PID>\n",
+                        argv[0]);
+               return 1;
+       }
+
+       intf_index = get_intf_index_from_name(argv[1]);
+       if (intf_index == -1) {
+               tst_resm(TINFO , "unable to get interface index");
+               return 1;
+       }
+
+       pid = atoi(argv[2]);
+
+       rtnetlink_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       if (rtnetlink_socket == -1) {
+               tst_resm(TINFO | TERRNO, "socket");
+               return 1;
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+       req.nh.nlmsg_flags = NLM_F_REQUEST;
+       req.nh.nlmsg_type = RTM_NEWLINK;
+       req.ifi.ifi_family = AF_UNSPEC;
+       req.ifi.ifi_index = intf_index;
+       req.ifi.ifi_change = 0xffffffff;
+       rta = (struct rtattr *)(((char *) &req) +
+               NLMSG_ALIGN(req.nh.nlmsg_len));
+       rta->rta_type = IFLA_NET_NS_PID;
+       rta->rta_len = RTA_LENGTH(sizeof(int));
+       req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) +
+               RTA_LENGTH(sizeof(pid));
+       memcpy(RTA_DATA(rta), &pid, sizeof(pid));
+
+       if (send(rtnetlink_socket, &req, req.nh.nlmsg_len, 0) == -1) {
+               tst_resm(TINFO | TERRNO, "send");
+               return 1;
+       }
+
+       close(rtnetlink_socket);
+       return 0;
+}
-- 
1.8.3.1


------------------------------------------------------------------------------
_______________________________________________
Ltp-list mailing list
Ltp-list@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ltp-list

Reply via email to