summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--sys-utils/lsns.c186
2 files changed, 185 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 28fd7760c..79a1fd3b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -234,6 +234,7 @@ AC_CHECK_HEADERS([ \
linux/tiocl.h \
linux/version.h \
linux/securebits.h \
+ linux/net_namespace.h \
locale.h \
mntent.h \
net/if.h \
diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c
index 1fb0b87f3..7ccc733a4 100644
--- a/sys-utils/lsns.c
+++ b/sys-utils/lsns.c
@@ -29,6 +29,14 @@
#include <wchar.h>
#include <libsmartcols.h>
+#ifdef HAVE_LINUX_NET_NAMESPACE_H
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/net_namespace.h>
+#endif
+
#include "pathnames.h"
#include "nls.h"
#include "xalloc.h"
@@ -52,6 +60,8 @@ UL_DEBUG_DEFINE_MASKNAMES(lsns) = UL_DEBUG_EMPTY_MASKNAMES;
#define LSNS_DEBUG_NS (1 << 3)
#define LSNS_DEBUG_ALL 0xFFFF
+#define LSNS_NETNS_UNUSABLE -2
+
#define DBG(m, x) __UL_DBG(lsns, LSNS_DEBUG_, m, x)
#define ON_DBG(m, x) __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x)
@@ -67,7 +77,8 @@ enum {
COL_PPID,
COL_COMMAND,
COL_UID,
- COL_USER
+ COL_USER,
+ COL_NETNSID
};
/* column names */
@@ -88,7 +99,8 @@ static const struct colinfo infos[] = {
[COL_PPID] = { "PPID", 5, SCOLS_FL_RIGHT, N_("PPID of the PID") },
[COL_COMMAND] = { "COMMAND", 0, SCOLS_FL_TRUNC, N_("command line of the PID")},
[COL_UID] = { "UID", 0, SCOLS_FL_RIGHT, N_("UID of the PID")},
- [COL_USER] = { "USER", 0, 0, N_("username of the PID")}
+ [COL_USER] = { "USER", 0, 0, N_("username of the PID")},
+ [COL_NETNSID] = { "NETNSID", 0, SCOLS_FL_RIGHT, N_("Net namespace ID")}
};
static int columns[ARRAY_SIZE(infos) * 2];
@@ -118,6 +130,7 @@ struct lsns_namespace {
ino_t id;
int type; /* LSNS_* */
int nprocs;
+ int netnsid;
struct lsns_process *proc;
@@ -139,6 +152,8 @@ struct lsns_process {
struct libscols_line *outline;
struct lsns_process *parent;
+
+ int netnsid;
};
struct lsns {
@@ -158,6 +173,16 @@ struct lsns {
no_headings: 1;
};
+struct netnsid_cache {
+ ino_t ino;
+ int id;
+ struct list_head netnsids;
+};
+
+static struct list_head netnsids_cache;
+
+static int netlink_fd = -1;
+
static void lsns_init_debug(void)
{
__UL_INIT_DEBUG(lsns, LSNS_DEBUG_, 0, LSNS_DEBUG);
@@ -242,6 +267,137 @@ error:
return rc;
}
+#ifdef HAVE_LINUX_NET_NAMESPACE_H
+static bool netnsid_cache_find(ino_t netino, int *netnsid)
+{
+ struct list_head *p;
+
+ list_for_each(p, &netnsids_cache) {
+ struct netnsid_cache *e = list_entry(p,
+ struct netnsid_cache,
+ netnsids);
+ if (e->ino == netino) {
+ *netnsid = e->id;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void netnsid_cache_add(ino_t netino, int netnsid)
+{
+ struct netnsid_cache *e;
+
+ e = xcalloc(1, sizeof(*e));
+ e->ino = netino;
+ e->id = netnsid;
+ INIT_LIST_HEAD(&e->netnsids);
+ list_add(&e->netnsids, &netnsids_cache);
+}
+
+static int get_netnsid_via_netlink_send_request(int target_fd)
+{
+ unsigned char req[NLMSG_SPACE(sizeof(struct rtgenmsg))
+ + RTA_SPACE(sizeof(int32_t))];
+
+ struct nlmsghdr *nlh = (struct nlmsghdr *)req;
+ struct rtgenmsg *rt = NLMSG_DATA(req);
+ struct rtattr *rta = (struct rtattr *)
+ (req + NLMSG_SPACE(sizeof(struct rtgenmsg)));
+ int32_t *fd = RTA_DATA(rta);
+
+ nlh->nlmsg_len = sizeof(req);
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ nlh->nlmsg_type = RTM_GETNSID;
+ rt->rtgen_family = AF_UNSPEC;
+ rta->rta_type = NETNSA_FD;
+ rta->rta_len = RTA_SPACE(sizeof(int32_t));
+ *fd = target_fd;
+
+ if (send(netlink_fd, req, sizeof(req), 0) < 0)
+ return -1;
+ return 0;
+}
+
+static int get_netnsid_via_netlink_recv_response(int *netnsid)
+{
+ unsigned char res[NLMSG_SPACE(sizeof(struct rtgenmsg))
+ + ((RTA_SPACE(sizeof(int32_t))
+ < RTA_SPACE(sizeof(struct nlmsgerr)))
+ ? RTA_SPACE(sizeof(struct nlmsgerr))
+ : RTA_SPACE(sizeof(int32_t)))];
+ int reslen, rtalen;
+
+ struct nlmsghdr *nlh;
+ struct rtattr *rta;
+
+ reslen = recv(netlink_fd, res, sizeof(res), 0);
+ if (reslen < 0)
+ return -1;
+
+ nlh = (struct nlmsghdr *)res;
+ if (!(NLMSG_OK(nlh, reslen)
+ && nlh->nlmsg_type == RTM_NEWNSID))
+ return -1;
+
+ rtalen = NLMSG_PAYLOAD(nlh, sizeof(struct rtgenmsg));
+ rta = (struct rtattr *)(res + NLMSG_SPACE(sizeof(struct rtgenmsg)));
+ if (!(RTA_OK(rta, rtalen)
+ && rta->rta_type == NETNSA_NSID))
+ return -1;
+
+ *netnsid = *(int *)RTA_DATA(rta);
+
+ return 0;
+}
+
+static int get_netnsid_via_netlink(int dir, const char *path)
+{
+ int netnsid;
+ int target_fd;
+
+ if (netlink_fd < 0)
+ return LSNS_NETNS_UNUSABLE;
+
+ target_fd = openat(dir, path, O_RDONLY);
+ if (target_fd < 0)
+ return LSNS_NETNS_UNUSABLE;
+
+ if (get_netnsid_via_netlink_send_request(target_fd) < 0) {
+ netnsid = LSNS_NETNS_UNUSABLE;
+ goto out;
+ }
+
+ if (get_netnsid_via_netlink_recv_response(&netnsid) < 0) {
+ netnsid = LSNS_NETNS_UNUSABLE;
+ goto out;
+ }
+
+ out:
+ close(target_fd);
+ return netnsid;
+}
+
+static int get_netnsid(int dir, ino_t netino)
+{
+ int netnsid;
+
+ if (!netnsid_cache_find(netino, &netnsid)) {
+ netnsid = get_netnsid_via_netlink(dir, "ns/net");
+ netnsid_cache_add(netino, netnsid);
+ }
+
+ return netnsid;
+}
+#else
+static int get_netnsid(int dir __attribute__((__unused__)),
+ ino_t netino __attribute__((__unused__)))
+{
+ return LSNS_NETNS_UNUSABLE;
+}
+#endif /* HAVE_LINUX_NET_NAMESPACE_H */
+
static int read_process(struct lsns *ls, pid_t pid)
{
struct lsns_process *p = NULL;
@@ -264,6 +420,7 @@ static int read_process(struct lsns *ls, pid_t pid)
rc = -ENOMEM;
goto done;
}
+ p->netnsid = LSNS_NETNS_UNUSABLE;
if (fstat(dirfd(dir), &st) == 0) {
p->uid = st.st_uid;
@@ -293,6 +450,8 @@ static int read_process(struct lsns *ls, pid_t pid)
rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]);
if (rc && rc != -EACCES && rc != -ENOENT)
goto done;
+ if (i == LSNS_ID_NET)
+ p->netnsid = get_netnsid(dirfd(dir), p->ns_ids[i]);
rc = 0;
}
@@ -413,6 +572,18 @@ static int cmp_namespaces(struct list_head *a, struct list_head *b,
return cmp_numbers(xa->id, xb->id);
}
+static int netnsid_xasputs(char **str, int netnsid)
+{
+ if (netnsid >= 0)
+ return xasprintf(str, "%d", netnsid);
+#ifdef NETNSA_NSID_NOT_ASSIGNED
+ else if (netnsid == NETNSA_NSID_NOT_ASSIGNED)
+ return xasprintf(str, "%s", "unassigned");
+#endif
+ else
+ return 0;
+}
+
static int read_namespaces(struct lsns *ls)
{
struct list_head *p;
@@ -490,6 +661,10 @@ static void add_scols_line(struct lsns *ls, struct libscols_table *table,
case COL_USER:
xasprintf(&str, "%s", get_id(uid_cache, proc->uid)->name);
break;
+ case COL_NETNSID:
+ if (ns->type == LSNS_ID_NET)
+ netnsid_xasputs(&str, proc->netnsid);
+ break;
default:
break;
}
@@ -675,6 +850,7 @@ int main(int argc, char *argv[])
INIT_LIST_HEAD(&ls.processes);
INIT_LIST_HEAD(&ls.namespaces);
+ INIT_LIST_HEAD(&netnsids_cache);
while ((c = getopt_long(argc, argv,
"Jlp:o:nruhVt:", long_opts, NULL)) != -1) {
@@ -748,6 +924,7 @@ int main(int argc, char *argv[])
columns[ncolumns++] = COL_NPROCS;
columns[ncolumns++] = COL_PID;
columns[ncolumns++] = COL_USER;
+ columns[ncolumns++] = COL_NETNSID;
columns[ncolumns++] = COL_COMMAND;
}
@@ -761,6 +938,9 @@ int main(int argc, char *argv[])
if (!uid_cache)
err(EXIT_FAILURE, _("failed to allocate UID cache"));
+#ifdef HAVE_LINUX_NET_NAMESPACE_H
+ netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+#endif
r = read_processes(&ls);
if (!r)
r = read_namespaces(&ls);
@@ -775,6 +955,8 @@ int main(int argc, char *argv[])
r = show_namespaces(&ls);
}
+ if (netlink_fd >= 0)
+ close(netlink_fd);
free_idcache(uid_cache);
return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}