#!/usr/bin/python # # Copyright 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Namespace related support code.""" import ctypes import ctypes.util import os import socket import sys import net_test import sock_diag import tcp_test # //include/linux/fs.h MNT_FORCE = 1 # Attempt to forcibily umount MNT_DETACH = 2 # Just detach from the tree MNT_EXPIRE = 4 # Mark for expiry UMOUNT_NOFOLLOW = 8 # Don't follow symlink on umount # //include/uapi/linux/fs.h MS_RDONLY = 1 # Mount read-only MS_NOSUID = 2 # Ignore suid and sgid bits MS_NODEV = 4 # Disallow access to device special files MS_NOEXEC = 8 # Disallow program execution MS_SYNCHRONOUS = 16 # Writes are synced at once MS_REMOUNT = 32 # Alter flags of a mounted FS MS_MANDLOCK = 64 # Allow mandatory locks on an FS MS_DIRSYNC = 128 # Directory modifications are synchronous MS_NOATIME = 1024 # Do not update access times. MS_NODIRATIME = 2048 # Do not update directory access times MS_BIND = 4096 # MS_MOVE = 8192 # MS_REC = 16384 # MS_SILENT = 32768 # MS_POSIXACL = (1<<16) # VFS does not apply the umask MS_UNBINDABLE = (1<<17) # change to unbindable MS_PRIVATE = (1<<18) # change to private MS_SLAVE = (1<<19) # change to slave MS_SHARED = (1<<20) # change to shared MS_RELATIME = (1<<21) # Update atime relative to mtime/ctime. MS_STRICTATIME = (1<<24) # Always perform atime updates MS_LAZYTIME = (1<<25) # Update the on-disk [acm]times lazily # //include/uapi/linux/sched.h CLONE_NEWNS = 0x00020000 # New mount namespace group CLONE_NEWCGROUP = 0x02000000 # New cgroup namespace CLONE_NEWUTS = 0x04000000 # New utsname namespace CLONE_NEWIPC = 0x08000000 # New ipc namespace CLONE_NEWUSER = 0x10000000 # New user namespace CLONE_NEWPID = 0x20000000 # New pid namespace CLONE_NEWNET = 0x40000000 # New network namespace libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) # See the relevant system call's man pages and: # https://docs.python.org/3/library/ctypes.html#fundamental-data-types libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_ulong, ctypes.c_void_p) libc.sethostname.argtype = (ctypes.c_char_p, ctypes.c_size_t) libc.umount2.argtypes = (ctypes.c_char_p, ctypes.c_int) libc.unshare.argtypes = (ctypes.c_int,) def Mount(src, tgt, fs, flags=MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RELATIME): ret = libc.mount(src, tgt, fs, flags, None) if ret < 0: errno = ctypes.get_errno() raise OSError(errno, '%s mounting %s on %s (fs=%s flags=0x%x)' % (os.strerror(errno), src, tgt, fs, flags)) def ReMountProc(): libc.umount2('/proc', MNT_DETACH) # Ignore failure: might not be mounted Mount('proc', '/proc', 'proc') def ReMountSys(): libc.umount2('/sys', MNT_DETACH) # Ignore failure: might not be mounted Mount('sysfs', '/sys', 'sysfs') def SetFileContents(f, s): open(f, 'w').write(s) def SetHostname(s): ret = libc.sethostname(s, len(s)) if ret < 0: errno = ctypes.get_errno() raise OSError(errno, '%s while sethostname(%s)' % (os.strerror(errno), s)) def UnShare(flags): ret = libc.unshare(flags) if ret < 0: errno = ctypes.get_errno() raise OSError(errno, '%s while unshare(0x%x)' % (os.strerror(errno), flags)) def DumpMounts(hdr): print('') print(hdr) sys.stdout.write(open('/proc/mounts', 'r').read()) print('---') # Requires at least kernel configuration options: # CONFIG_NAMESPACES=y # CONFIG_NET_NS=y # CONFIG_UTS_NS=y def IfPossibleEnterNewNetworkNamespace(): """Instantiate and transition into a fresh new network namespace if possible.""" sys.stdout.write('Creating clean namespace... ') try: UnShare(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET) except OSError as err: print('failed: %s (likely: no privs or lack of kernel support).' % err) return False try: # DumpMounts('Before:') Mount('none', '/', None, MS_REC|MS_PRIVATE) ReMountProc() ReMountSys() # DumpMounts('After:') SetHostname('netns') SetFileContents('/proc/sys/net/ipv4/ping_group_range', '0 2147483647') net_test.SetInterfaceUp('lo') except: print('failed.') # We've already transitioned into the new netns -- it's too late to recover. raise print('succeeded.') return True def HasEstablishedTcpSessionOnPort(port): sd = sock_diag.SockDiag() sock_id = sd._EmptyInetDiagSockId() sock_id.sport = port states = 1 << tcp_test.TCP_ESTABLISHED matches = sd.DumpAllInetSockets(socket.IPPROTO_TCP, "", sock_id=sock_id, states=states) return len(matches) > 0