tools: add filtering by mount namespace

In previous patches, I added the option --cgroupmap to filter events
belonging to a set of cgroup-v2. Although this approach works fine with
systemd services and containers when cgroup-v2 is enabled, it does not
work with containers when only cgroup-v1 is enabled because
bpf_get_current_cgroup_id() only works with cgroup-v2. It also requires
Linux 4.18 to get this bpf helper function.

This patch adds an additional way to filter by containers, using mount
namespaces.

Note that this does not help with systemd services since they normally
don't create a new mount namespace (unless you set some options like
'ReadOnlyPaths=', see "man 5 systemd.exec").

My goal with this patch is to filter Kubernetes pods, even on
distributions with an older kernel (<4.18) or without cgroup-v2 enabled.

- This is only implemented for tools that already support filtering by
  cgroup id (bindsnoop, capable, execsnoop, profile, tcpaccept, tcpconnect,
  tcptop and tcptracer).

- I picked the mount namespace because the other namespaces could be
  disabled in Kubernetes (e.g. HostNetwork, HostPID, HostIPC).

It can be tested by following the example in docs/special_filtering added
in this commit, to avoid compiling locally the following command can be used

```
sudo bpftool map create /sys/fs/bpf/mnt_ns_set type hash key 8 value 4 \
  entries 128 name mnt_ns_set flags 0
docker run -ti --rm --privileged \
  -v /usr/src:/usr/src -v /lib/modules:/lib/modules \
  -v /sys/fs/bpf:/sys/fs/bpf --pid=host kinvolk/bcc:alban-containers-filters \
  /usr/share/bcc/tools/execsnoop --mntnsmap /sys/fs/bpf/mnt_ns_set

```

Co-authored-by: Alban Crequy <alban@kinvolk.io>
Co-authored-by: Mauricio Vásquez <mauricio@kinvolk.io>
diff --git a/tools/bindsnoop.py b/tools/bindsnoop.py
index 4d3133f..de569c2 100755
--- a/tools/bindsnoop.py
+++ b/tools/bindsnoop.py
@@ -6,7 +6,7 @@
 # based on tcpconnect utility from Brendan Gregg's suite.
 #
 # USAGE: bindsnoop [-h] [-t] [-E] [-p PID] [-P PORT[,PORT ...]] [-w]
-#             [--count] [--cgroupmap mappath]
+#             [--count] [--cgroupmap mappath] [--mntnsmap mappath]
 #
 # bindsnoop reports socket options set before the bind call
 # that would impact this system call behavior:
@@ -28,6 +28,7 @@
 
 from __future__ import print_function, absolute_import, unicode_literals
 from bcc import BPF, DEBUG_SOURCE
+from bcc.containers import filter_by_containers
 from bcc.utils import printb
 import argparse
 import re
@@ -51,6 +52,7 @@
     ./bindsnoop -E        # report bind errors
     ./bindsnoop --count   # count bind per src ip
     ./bindsnoop --cgroupmap mappath  # only trace cgroups in this BPF map
+    ./bindsnoop --mntnsmap  mappath  # only trace mount namespaces in the map
 
 it is reporting socket options set before the bins call
 impacting system call behavior:
@@ -84,6 +86,8 @@
     help="count binds per src ip and port")
 parser.add_argument("--cgroupmap",
     help="trace cgroups in this BPF map only")
+parser.add_argument("--mntnsmap",
+    help="trace mount namespaces in this BPF map only")
 parser.add_argument("--ebpf", action="store_true",
     help=argparse.SUPPRESS)
 parser.add_argument("--debug-source", action="store_true",
@@ -148,8 +152,6 @@
 };
 BPF_HASH(ipv6_count, struct ipv6_flow_key_t);
 
-CGROUP_MAP
-
 // bind options for event reporting
 union bind_options {
     u8 data;
@@ -174,7 +176,9 @@
 
     FILTER_UID
 
-    FILTER_CGROUP
+    if (container_should_be_filtered()) {
+        return 0;
+    }
 
     // stash the sock ptr for lookup on return
     currsock.update(&tid, &socket);
@@ -323,11 +327,6 @@
                bpf_get_current_comm(&data6.task, sizeof(data6.task));
                ipv6_bind_events.perf_submit(ctx, &data6, sizeof(data6));"""
     },
-    'filter_cgroup': """
-    u64 cgroupid = bpf_get_current_cgroup_id();
-    if (cgroupset.lookup(&cgroupid) == NULL) {
-      return 0;
-    }""",
 }
 
 # code substitutions
@@ -351,22 +350,11 @@
         'if (uid != %s) { return 0; }' % args.uid)
 if args.errors:
     bpf_text = bpf_text.replace('FILTER_ERRORS', 'ignore_errors = 0;')
-if args.cgroupmap:
-    bpf_text = bpf_text.replace('FILTER_CGROUP', struct_init['filter_cgroup'])
-    bpf_text = bpf_text.replace(
-        'CGROUP_MAP',
-        (
-            'BPF_TABLE_PINNED("hash", u64, u64, cgroupset, 1024, "%s");' %
-            args.cgroupmap
-        )
-    )
-
+bpf_text = filter_by_containers(args) + bpf_text
 bpf_text = bpf_text.replace('FILTER_PID', '')
 bpf_text = bpf_text.replace('FILTER_PORT', '')
 bpf_text = bpf_text.replace('FILTER_UID', '')
 bpf_text = bpf_text.replace('FILTER_ERRORS', '')
-bpf_text = bpf_text.replace('FILTER_CGROUP', '')
-bpf_text = bpf_text.replace('CGROUP_MAP', '')
 
 # selecting output format - 80 characters or wide, fitting IPv6 addresses
 header_fmt = "%8s %-12.12s %-4s %-15s %-5s %5s %2s"