ip netns list
will only list the networks namespaces which were configured via the ip-netns(8)
utility.
The lsns(1)
program from the util-linux
package is also highly deficient: it will list only those namespaces which are accessible via the /proc/<pid>/ns/*
files, omitting all the per-thread namespaces and those kept alive by a bind mount or an open file descriptor.
The following demo script tries to do better: it will look for bind mounts through the /proc/<pid>/task/<tid>/mountinfo
files, and for open fds through /proc/<pid>/task/<tid>/fd
files.
For each namespace, it will print a path through which it is accessible:
# perl ./lsnsx.pl
...
mnt 3
4026531840 /proc/1/ns/mnt
4026531860 /proc/30/ns/mnt
4026532374 /proc/3119/ns/mnt
net 6
4026531992 /proc/1/ns/net
4026532376 /proc/25781/fd/9
4026532465 /proc/28373/fd/7
...
You can then use that path with nsenter(1)
, eg.
nsenter --net=/proc/28373/fd/7 ip link
The script could be easily changed to do that itself, or show other info, like the whole list of processes which use a namespace.
If the path is not accessible, it will follow it by the parent/mount ids and the /proc/<pid>/mountinfo
file where it was found. Escaped newlines, tabs and spaces will be left as they are:
net 9
...
4026532732 /v/net\040ns /proc/3119/mountinfo 60 41
Since it has to read all those /proc/*/task/*
files, this may get slow on any machine where heavily threaded programs are used; unfortunately I wasn't able to find any quick way to check if two threads/tasks share the same namespace(s): kcmp(2)
will only tell if they're sharing the same address space, file descriptor table, etc; not anything namespace-related.
lsnsx.pl
:
#! /usr/bin/perl
use strict;
my %t2n = (
# the CLONE_NEW* from sched.h
0x02000000 => "cgroup", 0x04000000 => "uts", 0x08000000 => "ipc",
0x10000000 => "user", 0x20000000 => "pid", 0x40000000 => "net",
0x00020000 => "mnt" # CLONE_NEWNS
);
my (%ns);
my $nsfs_dev = (stat "/proc/self/ns/mnt")[0];
my $type = shift || qr/\w+/;
sub unescape { $_[0] =~ s/\\([0-7]{3})/chr oct $1/ger }
# NS_GET_NSTYPE = 0xb7 << 8 | 3
sub nstype { my $h; open $h, $_[0] and ioctl $h, 0xb7 << 8 | 3, 0 }
for(</proc/[0-9]*/task/[0-9]*/{ns/*,fd/*,mountinfo}>){
s{/([0-9]*)/task/\1/}{/$1/};
if(my ($procpid) = m{(.*)/mountinfo$}){
open my $h, $_ or next;
LOOP: while(<$h>){
next unless (my @s = split)[2] eq "0:$nsfs_dev";
if(my($t, $i) = $s[3] =~ /^($type):\[(\d+)\]$/){
next if exists $ns{$t}{$i};
for ("", "$procpid/root"){
my ($d, $i1) =
(stat $_.unescape $s[4])[0, 1];
$ns{$t}{$i} = $_.$s[4], next LOOP
if $d == $nsfs_dev && $i == $i1;
}
$ns{$t}{$i} = "@s[4, 0, 1] $procpid/mountinfo"
}
}
}elsif(m{/ns/}){
$ns{$1}{$2} //= $_ if readlink =~ /^($type):\[(\d+)\]$/;
}else{
next unless my ($dev, $ino) = stat $_;
next unless $dev == $nsfs_dev;
next unless my $t = nstype $_;
next if ($t = $t2n{$t}) and $t !~ $type;
$ns{$t // '???'}{$ino} //= $_;
}
}
for my $type (sort keys %ns){
my $h = $ns{$type}; my @i = sort {$a<=>$b} keys %$h;
printf "%-8s %d\n", $type, scalar @i;
printf " %-11d %s\n", $_, $$h{$_} for @i;
}
ip a
gives me all interfaces including virtual ones used fordocker
containers. In one specific instance I've got 39 entries listed, and most of these do not have an IP address configured for the host.@if16
or@if18
appended (not the name reversed). More info about this with my answer there. Anyway I also provided an answer to this question, trying to explain how it works. Using shell isn't optimal for any automatization.