• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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