1#!/usr/bin/python 2# 3# Copyright 2020 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Namespace related support code.""" 18 19import ctypes 20import ctypes.util 21import os 22import socket 23 24import net_test 25import sock_diag 26import tcp_test 27 28# //include/linux/fs.h 29MNT_FORCE = 1 # Attempt to forcibily umount 30MNT_DETACH = 2 # Just detach from the tree 31MNT_EXPIRE = 4 # Mark for expiry 32UMOUNT_NOFOLLOW = 8 # Don't follow symlink on umount 33 34# //include/uapi/linux/fs.h 35MS_RDONLY = 1 # Mount read-only 36MS_NOSUID = 2 # Ignore suid and sgid bits 37MS_NODEV = 4 # Disallow access to device special files 38MS_NOEXEC = 8 # Disallow program execution 39MS_SYNCHRONOUS = 16 # Writes are synced at once 40MS_REMOUNT = 32 # Alter flags of a mounted FS 41MS_MANDLOCK = 64 # Allow mandatory locks on an FS 42MS_DIRSYNC = 128 # Directory modifications are synchronous 43MS_NOATIME = 1024 # Do not update access times. 44MS_NODIRATIME = 2048 # Do not update directory access times 45MS_BIND = 4096 # 46MS_MOVE = 8192 # 47MS_REC = 16384 # 48MS_SILENT = 32768 # 49MS_POSIXACL = (1<<16) # VFS does not apply the umask 50MS_UNBINDABLE = (1<<17) # change to unbindable 51MS_PRIVATE = (1<<18) # change to private 52MS_SLAVE = (1<<19) # change to slave 53MS_SHARED = (1<<20) # change to shared 54MS_RELATIME = (1<<21) # Update atime relative to mtime/ctime. 55MS_STRICTATIME = (1<<24) # Always perform atime updates 56MS_LAZYTIME = (1<<25) # Update the on-disk [acm]times lazily 57 58# //include/uapi/linux/sched.h 59CLONE_NEWNS = 0x00020000 # New mount namespace group 60CLONE_NEWCGROUP = 0x02000000 # New cgroup namespace 61CLONE_NEWUTS = 0x04000000 # New utsname namespace 62CLONE_NEWIPC = 0x08000000 # New ipc namespace 63CLONE_NEWUSER = 0x10000000 # New user namespace 64CLONE_NEWPID = 0x20000000 # New pid namespace 65CLONE_NEWNET = 0x40000000 # New network namespace 66 67libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) 68 69# See the relevant system call's man pages and: 70# https://docs.python.org/3/library/ctypes.html#fundamental-data-types 71libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, 72 ctypes.c_ulong, ctypes.c_void_p) 73libc.sethostname.argtype = (ctypes.c_char_p, ctypes.c_size_t) 74libc.umount2.argtypes = (ctypes.c_char_p, ctypes.c_int) 75libc.unshare.argtypes = (ctypes.c_int,) 76 77 78def Mount(src, tgt, fs, flags=MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RELATIME): 79 ret = libc.mount(src, tgt, fs, flags, None) 80 if ret < 0: 81 errno = ctypes.get_errno() 82 raise OSError(errno, '%s mounting %s on %s (fs=%s flags=0x%x)' 83 % (os.strerror(errno), src, tgt, fs, flags)) 84 85 86def ReMountProc(): 87 libc.umount2('/proc', MNT_DETACH) # Ignore failure: might not be mounted 88 Mount('proc', '/proc', 'proc') 89 90 91def ReMountSys(): 92 libc.umount2('/sys', MNT_DETACH) # Ignore failure: might not be mounted 93 Mount('sysfs', '/sys', 'sysfs') 94 95 96def SetFileContents(f, s): 97 open(f, 'w').write(s) 98 99 100def SetHostname(s): 101 ret = libc.sethostname(s, len(s)) 102 if ret < 0: 103 errno = ctypes.get_errno() 104 raise OSError(errno, '%s while sethostname(%s)' % (os.strerror(errno), s)) 105 106 107def UnShare(flags): 108 ret = libc.unshare(flags) 109 if ret < 0: 110 errno = ctypes.get_errno() 111 raise OSError(errno, '%s while unshare(0x%x)' % (os.strerror(errno), flags)) 112 113 114def DumpMounts(hdr): 115 print 116 print hdr 117 print open('/proc/mounts', 'r').read(), 118 print '---' 119 120 121# Requires at least kernel configuration options: 122# CONFIG_NAMESPACES=y 123# CONFIG_NET_NS=y 124# CONFIG_UTS_NS=y 125def IfPossibleEnterNewNetworkNamespace(): 126 """Instantiate and transition into a fresh new network namespace if possible.""" 127 128 print 'Creating clean namespace...', 129 130 try: 131 UnShare(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET) 132 except OSError as err: 133 print 'failed: %s (likely: no privs or lack of kernel support).' % err 134 return False 135 136 try: 137 # DumpMounts('Before:') 138 Mount('none', '/', None, MS_REC|MS_PRIVATE) 139 ReMountProc() 140 ReMountSys() 141 # DumpMounts('After:') 142 SetHostname('netns') 143 SetFileContents('/proc/sys/net/ipv4/ping_group_range', '0 2147483647') 144 net_test.SetInterfaceUp('lo') 145 except: 146 print 'failed.' 147 # We've already transitioned into the new netns -- it's too late to recover. 148 raise 149 150 print 'succeeded.' 151 return True 152 153 154def HasEstablishedTcpSessionOnPort(port): 155 sd = sock_diag.SockDiag() 156 157 sock_id = sd._EmptyInetDiagSockId() 158 sock_id.sport = port 159 160 states = 1 << tcp_test.TCP_ESTABLISHED 161 162 matches = sd.DumpAllInetSockets(socket.IPPROTO_TCP, "", 163 sock_id=sock_id, states=states) 164 165 return len(matches) > 0 166