summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasatake YAMATO2017-11-24 11:31:03 +0100
committerKarel Zak2017-11-27 15:54:49 +0100
commit4195756e032eb9c9be5ed8ab961ef4d72da7294c (patch)
treed4315d108ee9594858ef74c60c64a63d78a097c0
parentrtcwake: fix sys vs. rtc time delta usage (diff)
downloadkernel-qcow2-util-linux-4195756e032eb9c9be5ed8ab961ef4d72da7294c.tar.gz
kernel-qcow2-util-linux-4195756e032eb9c9be5ed8ab961ef4d72da7294c.tar.xz
kernel-qcow2-util-linux-4195756e032eb9c9be5ed8ab961ef4d72da7294c.zip
lsns: add netnsid column
Linux network subsystem assigns an unique integer to a network namespace. term0# ip netns add UTIL-LINUX-LSNS-TEST-NS term0# ip netns list UTIL-LINUX-LSNS-TEST-NS term0# ip link add name lsns-vetha type veth peer name lsns-vethb term0 # ip link set lsns-vethb netns UTIL-LINUX-LSNS-TEST-NS term0# ip netns list UTIL-LINUX-LSNS-TEST-NS (id: 0) term0# ip link show dev lsns-vetha 230: lsns-vetha@if229: <BROADCAST,MULTICAST> mtu 1500 qdisc noop ... link/ether 3e:27:68:ba:b3:95 brd ff:ff:ff:ff:ff:ff link-netnsid 0 In this example 0 is assigned to UTIL-LINUX-LSNS-TEST-NS net namespace. The name, UTIL-LINUX-LSNS-TEST-NS, and it semantics is given and defined by iproute2 in userland; and nothing to do with util-linux. However, the id, 0, is managed in linux kernel. If lsns can show the ids, it helps users understand the state of network namespaces. This commit adds NETNSID column to the output. Here is an example of session: term0# ip netns exec UTIL-LINUX-LSNS-TEST-NS cat (Open another terminal) term1# ./lsns --type net NS TYPE NPROCS PID USER NETNSID COMMAND 4026531993 net 383 1 root unassigned /usr/lib/systemd/... 4026532433 net 1 1219 rtkit unassigned /usr/libexec/rtkit-daemon 4026532562 net 1 18201 root 0 cat 0 is shown as NETNSID for the cat process. For the initial name space, "unassigned" is printed. For the namespaces other type than net, "n/a" is printed. If an error occurred during getting the id, "n/a" is printed. Changes in V2: * Remove wrongly used & operators. * Make netnsid field empty if value for the column is not available. Suggested by Karel Zak. * Remove redundant condtion for checking the avaiablebility of netlink socket. Suggested by Karel Zak. Signed-off-by: Masatake YAMATO <yamato@redhat.com>
-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;
}