gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/pkg/katautils/network.go (about) 1 // Copyright (c) 2018 Intel Corporation 2 // Copyright (c) 2018 HyperHQ Inc. 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package katautils 8 9 import ( 10 "bufio" 11 "fmt" 12 "os" 13 "path/filepath" 14 goruntime "runtime" 15 "strings" 16 17 "github.com/containernetworking/plugins/pkg/ns" 18 "github.com/containernetworking/plugins/pkg/testutils" 19 vc "github.com/kata-containers/runtime/virtcontainers" 20 "github.com/kata-containers/runtime/virtcontainers/pkg/rootless" 21 "golang.org/x/sys/unix" 22 ) 23 24 const procMountInfoFile = "/proc/self/mountinfo" 25 26 // EnterNetNS is free from any call to a go routine, and it calls 27 // into runtime.LockOSThread(), meaning it won't be executed in a 28 // different thread than the one expected by the caller. 29 func EnterNetNS(netNSPath string, cb func() error) error { 30 if netNSPath == "" { 31 return cb() 32 } 33 34 goruntime.LockOSThread() 35 defer goruntime.UnlockOSThread() 36 37 currentNS, err := ns.GetCurrentNS() 38 if err != nil { 39 return err 40 } 41 defer currentNS.Close() 42 43 targetNS, err := ns.GetNS(netNSPath) 44 if err != nil { 45 return err 46 } 47 48 if err := targetNS.Set(); err != nil { 49 return err 50 } 51 defer currentNS.Set() 52 53 return cb() 54 } 55 56 // SetupNetworkNamespace create a network namespace 57 func SetupNetworkNamespace(config *vc.NetworkConfig) error { 58 if config.DisableNewNetNs { 59 kataUtilsLogger.Info("DisableNewNetNs is on, shim and hypervisor are running in the host netns") 60 return nil 61 } 62 63 var err error 64 var n ns.NetNS 65 66 if config.NetNSPath == "" { 67 if rootless.IsRootless() { 68 n, err = rootless.NewNS() 69 if err != nil { 70 return err 71 } 72 } else { 73 n, err = testutils.NewNS() 74 if err != nil { 75 return err 76 } 77 } 78 79 config.NetNSPath = n.Path() 80 config.NetNsCreated = true 81 kataUtilsLogger.WithField("netns", n.Path()).Info("create netns") 82 83 return nil 84 } 85 86 isHostNs, err := hostNetworkingRequested(config.NetNSPath) 87 if err != nil { 88 return err 89 } 90 if isHostNs { 91 return fmt.Errorf("Host networking requested, not supported by runtime") 92 } 93 94 return nil 95 } 96 97 // getNetNsFromBindMount returns the network namespace for the bind-mounted path 98 func getNetNsFromBindMount(nsPath string, procMountFile string) (string, error) { 99 netNsMountType := "nsfs" 100 101 // Resolve all symlinks in the path as the mountinfo file contains 102 // resolved paths. 103 nsPath, err := filepath.EvalSymlinks(nsPath) 104 if err != nil { 105 return "", err 106 } 107 108 f, err := os.Open(procMountFile) 109 if err != nil { 110 return "", err 111 } 112 defer f.Close() 113 114 scanner := bufio.NewScanner(f) 115 for scanner.Scan() { 116 text := scanner.Text() 117 118 // Scan the mountinfo file to search for the network namespace path 119 // This file contains mounts in the eg format: 120 // "711 26 0:3 net:[4026532009] /run/docker/netns/default rw shared:535 - nsfs nsfs rw" 121 // 122 // Reference: https://www.kernel.org/doc/Documentation/filesystems/proc.txt 123 124 // We are interested in the first 9 fields of this file, 125 // to check for the correct mount type. 126 fields := strings.Split(text, " ") 127 if len(fields) < 9 { 128 continue 129 } 130 131 // We check here if the mount type is a network namespace mount type, namely "nsfs" 132 mountTypeFieldIdx := 8 133 if fields[mountTypeFieldIdx] != netNsMountType { 134 continue 135 } 136 137 // This is the mount point/destination for the mount 138 mntDestIdx := 4 139 if fields[mntDestIdx] != nsPath { 140 continue 141 } 142 143 // This is the root/source of the mount 144 return fields[3], nil 145 } 146 147 return "", nil 148 } 149 150 // hostNetworkingRequested checks if the network namespace requested is the 151 // same as the current process. 152 func hostNetworkingRequested(configNetNs string) (bool, error) { 153 var evalNS, nsPath, currentNsPath string 154 var err error 155 156 // Net namespace provided as "/proc/pid/ns/net" or "/proc/<pid>/task/<tid>/ns/net" 157 if strings.HasPrefix(configNetNs, "/proc") && strings.HasSuffix(configNetNs, "/ns/net") { 158 if _, err := os.Stat(configNetNs); err != nil { 159 return false, err 160 } 161 162 // Here we are trying to resolve the path but it fails because 163 // namespaces links don't really exist. For this reason, the 164 // call to EvalSymlinks will fail when it will try to stat the 165 // resolved path found. As we only care about the path, we can 166 // retrieve it from the PathError structure. 167 if _, err = filepath.EvalSymlinks(configNetNs); err != nil { 168 nsPath = err.(*os.PathError).Path 169 } else { 170 return false, fmt.Errorf("Net namespace path %s is not a symlink", configNetNs) 171 } 172 173 _, evalNS = filepath.Split(nsPath) 174 175 } else { 176 // Bind-mounted path provided 177 evalNS, _ = getNetNsFromBindMount(configNetNs, procMountInfoFile) 178 } 179 180 currentNS := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) 181 if _, err = filepath.EvalSymlinks(currentNS); err != nil { 182 currentNsPath = err.(*os.PathError).Path 183 } else { 184 return false, fmt.Errorf("Unexpected: Current network namespace path is not a symlink") 185 } 186 187 _, evalCurrentNS := filepath.Split(currentNsPath) 188 189 if evalNS == evalCurrentNS { 190 return true, nil 191 } 192 193 return false, nil 194 } 195 196 // cleanupNetNS cleanup netns created by kata, trigger only create sandbox fails 197 func cleanupNetNS(netNSPath string) error { 198 n, err := ns.GetNS(netNSPath) 199 if err != nil { 200 return fmt.Errorf("failed to get netns %s: %v", netNSPath, err) 201 } 202 203 err = n.Close() 204 if err != nil { 205 return fmt.Errorf("failed to close netns %s: %v", netNSPath, err) 206 } 207 208 if err = unix.Unmount(netNSPath, unix.MNT_DETACH); err != nil { 209 return fmt.Errorf("failed to unmount namespace %s: %v", netNSPath, err) 210 } 211 if err := os.RemoveAll(netNSPath); err != nil { 212 return fmt.Errorf("failed to clean up namespace %s: %v", netNSPath, err) 213 } 214 215 return nil 216 }