• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
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 errno
22import os
23import socket
24import sys
25
26import net_test
27import sock_diag
28import tcp_test
29
30# //include/linux/fs.h
31MNT_FORCE       = 1         # Attempt to forcibily umount
32MNT_DETACH      = 2         # Just detach from the tree
33MNT_EXPIRE      = 4         # Mark for expiry
34UMOUNT_NOFOLLOW = 8         # Don't follow symlink on umount
35
36# //include/uapi/linux/fs.h
37MS_RDONLY       = 1         # Mount read-only
38MS_NOSUID       = 2         # Ignore suid and sgid bits
39MS_NODEV        = 4         # Disallow access to device special files
40MS_NOEXEC       = 8         # Disallow program execution
41MS_SYNCHRONOUS  = 16        # Writes are synced at once
42MS_REMOUNT      = 32        # Alter flags of a mounted FS
43MS_MANDLOCK     = 64        # Allow mandatory locks on an FS
44MS_DIRSYNC      = 128       # Directory modifications are synchronous
45MS_NOATIME      = 1024      # Do not update access times.
46MS_NODIRATIME   = 2048      # Do not update directory access times
47MS_BIND         = 4096      #
48MS_MOVE         = 8192      #
49MS_REC          = 16384     #
50MS_SILENT       = 32768     #
51MS_POSIXACL     = (1<<16)   # VFS does not apply the umask
52MS_UNBINDABLE   = (1<<17)   # change to unbindable
53MS_PRIVATE      = (1<<18)   # change to private
54MS_SLAVE        = (1<<19)   # change to slave
55MS_SHARED       = (1<<20)   # change to shared
56MS_RELATIME     = (1<<21)   # Update atime relative to mtime/ctime.
57MS_STRICTATIME  = (1<<24)   # Always perform atime updates
58MS_LAZYTIME     = (1<<25)   # Update the on-disk [acm]times lazily
59
60# //include/uapi/linux/sched.h
61CLONE_NEWNS     = 0x00020000   # New mount namespace group
62CLONE_NEWCGROUP = 0x02000000   # New cgroup namespace
63CLONE_NEWUTS    = 0x04000000   # New utsname namespace
64CLONE_NEWIPC    = 0x08000000   # New ipc namespace
65CLONE_NEWUSER   = 0x10000000   # New user namespace
66CLONE_NEWPID    = 0x20000000   # New pid namespace
67CLONE_NEWNET    = 0x40000000   # New network namespace
68
69libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
70
71# See the relevant system call's man pages and:
72#   https://docs.python.org/3/library/ctypes.html#fundamental-data-types
73libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p,
74                       ctypes.c_ulong, ctypes.c_void_p)
75libc.sethostname.argtypes = (ctypes.c_char_p, ctypes.c_size_t)
76libc.umount2.argtypes = (ctypes.c_char_p, ctypes.c_int)
77libc.unshare.argtypes = (ctypes.c_int,)
78
79
80def Mount(src, tgt, fs, flags=MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RELATIME):
81  ret = libc.mount(src.encode(), tgt.encode(), fs.encode() if fs else None,
82                   flags, None)
83  if ret < 0:
84    errno = ctypes.get_errno()
85    raise OSError(errno, '%s mounting %s on %s (fs=%s flags=0x%x)'
86                  % (os.strerror(errno), src, tgt, fs, flags))
87
88
89def ReMountProc():
90  libc.umount2(b'/proc', MNT_DETACH)  # Ignore failure: might not be mounted
91  Mount('proc', '/proc', 'proc')
92
93
94def ReMountSys():
95  libc.umount2(b'/sys/fs/cgroup', MNT_DETACH)  # Ignore failure: might not be mounted
96  libc.umount2(b'/sys/fs/bpf', MNT_DETACH)  # Ignore failure: might not be mounted
97  libc.umount2(b'/sys', MNT_DETACH)  # Ignore failure: might not be mounted
98  Mount('sysfs', '/sys', 'sysfs')
99  Mount('bpf', '/sys/fs/bpf', 'bpf')
100  Mount('cgroup2', '/sys/fs/cgroup', 'cgroup2')
101
102
103def SetFileContents(f, s):
104  with open(f, 'w') as set_file:
105    set_file.write(s)
106
107
108def SetHostname(s):
109  hostname = s.encode()
110  ret = libc.sethostname(hostname, len(hostname))
111  if ret < 0:
112    errno = ctypes.get_errno()
113    raise OSError(errno, '%s while sethostname(%s)' % (os.strerror(errno), s))
114
115
116def UnShare(flags):
117  ret = libc.unshare(flags)
118  if ret < 0:
119    errno = ctypes.get_errno()
120    raise OSError(errno, '%s while unshare(0x%x)' % (os.strerror(errno), flags))
121
122
123def DumpMounts(hdr):
124  print('')
125  print(hdr)
126  with open('/proc/mounts', 'r') as mounts:
127    sys.stdout.write(mounts.read())
128  print('---')
129
130
131# Requires at least kernel configuration options:
132#   CONFIG_NAMESPACES=y
133#   CONFIG_NET_NS=y
134#   CONFIG_UTS_NS=y
135def EnterNewNetworkNamespace():
136  """Instantiate and transition into a fresh new network namespace."""
137
138  sys.stdout.write('Creating clean namespace... ')
139
140  # sysctl only present on 4.14 and earlier Android kernels
141  if net_test.LINUX_VERSION < (4, 15, 0):
142    TCP_DEFAULT_INIT_RWND = "/proc/sys/net/ipv4/tcp_default_init_rwnd"
143    # In root netns this will succeed
144    init_rwnd_sysctl = open(TCP_DEFAULT_INIT_RWND, "w")
145
146  try:
147    UnShare(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET)
148  except OSError as err:
149    print('failed: %s (likely: no privs or lack of kernel support).' % err)
150    raise
151
152  try:
153    # DumpMounts('Before:')
154    Mount('none', '/', None, MS_REC|MS_PRIVATE)
155    ReMountProc()
156    ReMountSys()
157    # DumpMounts('After:')
158    SetHostname('netns')
159    SetFileContents('/proc/sys/net/ipv4/ping_group_range', '0 2147483647')
160    net_test.SetInterfaceUp('lo')
161  except:
162    print('failed.')
163    # We've already transitioned into the new netns -- it's too late to recover.
164    raise
165
166  if net_test.LINUX_VERSION < (4, 15, 0):
167    # In non-root netns this open might fail due to non-namespace-ified sysctl
168    # ie. lack of kernel commit:
169    #    https://android-review.googlesource.com/c/kernel/common/+/1312623
170    #    ANDROID: namespace'ify tcp_default_init_rwnd implementation
171    try:
172      init_rwnd_sysctl = open(TCP_DEFAULT_INIT_RWND, "w")
173    except IOError as e:
174      if e.errno != errno.ENOENT:
175        raise
176      # Note! if the netns open above succeeded (and thus we don't reach here)
177      # then we don't need to actually update the sysctl, since we'll be able to do
178      # that in the sock_diag_test.py TcpRcvWindowTest test case setUp() call instead.
179      #
180      # As such this write here is *still* to the root netns sysctl
181      # (because we obtained a file descriptor *prior* to unshare/etc...)
182      # and handles the case where the sysctl is not namespace aware and thus
183      # affects the entire system.
184      init_rwnd_sysctl.write("60");
185
186  print('succeeded.')
187
188
189def HasEstablishedTcpSessionOnPort(port):
190  sd = sock_diag.SockDiag()
191
192  sock_id = sd._EmptyInetDiagSockId()
193  sock_id.sport = port
194
195  states = 1 << tcp_test.TCP_ESTABLISHED
196
197  matches = sd.DumpAllInetSockets(socket.IPPROTO_TCP, b"",
198                                  sock_id=sock_id, states=states)
199
200  return len(matches) > 0
201