github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/libnetwork/osl/namespace_linux.go (about) 1 package osl 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "runtime" 11 "strconv" 12 "strings" 13 "sync" 14 "syscall" 15 "time" 16 17 log "github.com/Sirupsen/logrus" 18 "github.com/docker/docker/pkg/reexec" 19 "github.com/docker/libnetwork/ns" 20 "github.com/docker/libnetwork/types" 21 "github.com/vishvananda/netlink" 22 "github.com/vishvananda/netns" 23 ) 24 25 const defaultPrefix = "/var/run/docker" 26 27 var ( 28 once sync.Once 29 garbagePathMap = make(map[string]bool) 30 gpmLock sync.Mutex 31 gpmWg sync.WaitGroup 32 gpmCleanupPeriod = 60 * time.Second 33 gpmChan = make(chan chan struct{}) 34 prefix = defaultPrefix 35 ) 36 37 // The networkNamespace type is the linux implementation of the Sandbox 38 // interface. It represents a linux network namespace, and moves an interface 39 // into it when called on method AddInterface or sets the gateway etc. 40 type networkNamespace struct { 41 path string 42 iFaces []*nwIface 43 gw net.IP 44 gwv6 net.IP 45 staticRoutes []*types.StaticRoute 46 neighbors []*neigh 47 nextIfIndex int 48 isDefault bool 49 nlHandle *netlink.Handle 50 sync.Mutex 51 } 52 53 // SetBasePath sets the base url prefix for the ns path 54 func SetBasePath(path string) { 55 prefix = path 56 } 57 58 func init() { 59 reexec.Register("netns-create", reexecCreateNamespace) 60 } 61 62 func basePath() string { 63 return filepath.Join(prefix, "netns") 64 } 65 66 func createBasePath() { 67 err := os.MkdirAll(basePath(), 0755) 68 if err != nil { 69 panic("Could not create net namespace path directory") 70 } 71 72 // Start the garbage collection go routine 73 go removeUnusedPaths() 74 } 75 76 func removeUnusedPaths() { 77 gpmLock.Lock() 78 period := gpmCleanupPeriod 79 gpmLock.Unlock() 80 81 ticker := time.NewTicker(period) 82 for { 83 var ( 84 gc chan struct{} 85 gcOk bool 86 ) 87 88 select { 89 case <-ticker.C: 90 case gc, gcOk = <-gpmChan: 91 } 92 93 gpmLock.Lock() 94 pathList := make([]string, 0, len(garbagePathMap)) 95 for path := range garbagePathMap { 96 pathList = append(pathList, path) 97 } 98 garbagePathMap = make(map[string]bool) 99 gpmWg.Add(1) 100 gpmLock.Unlock() 101 102 for _, path := range pathList { 103 os.Remove(path) 104 } 105 106 gpmWg.Done() 107 if gcOk { 108 close(gc) 109 } 110 } 111 } 112 113 func addToGarbagePaths(path string) { 114 gpmLock.Lock() 115 garbagePathMap[path] = true 116 gpmLock.Unlock() 117 } 118 119 func removeFromGarbagePaths(path string) { 120 gpmLock.Lock() 121 delete(garbagePathMap, path) 122 gpmLock.Unlock() 123 } 124 125 // GC triggers garbage collection of namespace path right away 126 // and waits for it. 127 func GC() { 128 gpmLock.Lock() 129 if len(garbagePathMap) == 0 { 130 // No need for GC if map is empty 131 gpmLock.Unlock() 132 return 133 } 134 gpmLock.Unlock() 135 136 // if content exists in the garbage paths 137 // we can trigger GC to run, providing a 138 // channel to be notified on completion 139 waitGC := make(chan struct{}) 140 gpmChan <- waitGC 141 // wait for GC completion 142 <-waitGC 143 } 144 145 // GenerateKey generates a sandbox key based on the passed 146 // container id. 147 func GenerateKey(containerID string) string { 148 maxLen := 12 149 // Read sandbox key from host for overlay 150 if strings.HasPrefix(containerID, "-") { 151 var ( 152 index int 153 indexStr string 154 tmpkey string 155 ) 156 dir, err := ioutil.ReadDir(basePath()) 157 if err != nil { 158 return "" 159 } 160 161 for _, v := range dir { 162 id := v.Name() 163 if strings.HasSuffix(id, containerID[:maxLen-1]) { 164 indexStr = strings.TrimSuffix(id, containerID[:maxLen-1]) 165 tmpindex, err := strconv.Atoi(indexStr) 166 if err != nil { 167 return "" 168 } 169 if tmpindex > index { 170 index = tmpindex 171 tmpkey = id 172 } 173 174 } 175 } 176 containerID = tmpkey 177 if containerID == "" { 178 return "" 179 } 180 } 181 182 if len(containerID) < maxLen { 183 maxLen = len(containerID) 184 } 185 186 return basePath() + "/" + containerID[:maxLen] 187 } 188 189 // NewSandbox provides a new sandbox instance created in an os specific way 190 // provided a key which uniquely identifies the sandbox 191 func NewSandbox(key string, osCreate, isRestore bool) (Sandbox, error) { 192 if !isRestore { 193 err := createNetworkNamespace(key, osCreate) 194 if err != nil { 195 return nil, err 196 } 197 } else { 198 once.Do(createBasePath) 199 } 200 201 n := &networkNamespace{path: key, isDefault: !osCreate} 202 203 sboxNs, err := netns.GetFromPath(n.path) 204 if err != nil { 205 return nil, fmt.Errorf("failed get network namespace %q: %v", n.path, err) 206 } 207 defer sboxNs.Close() 208 209 n.nlHandle, err = netlink.NewHandleAt(sboxNs, syscall.NETLINK_ROUTE) 210 if err != nil { 211 return nil, fmt.Errorf("failed to create a netlink handle: %v", err) 212 } 213 214 if err = n.loopbackUp(); err != nil { 215 n.nlHandle.Delete() 216 return nil, err 217 } 218 219 return n, nil 220 } 221 222 func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter { 223 return n 224 } 225 226 func (n *networkNamespace) NeighborOptions() NeighborOptionSetter { 227 return n 228 } 229 230 func mountNetworkNamespace(basePath string, lnPath string) error { 231 return syscall.Mount(basePath, lnPath, "bind", syscall.MS_BIND, "") 232 } 233 234 // GetSandboxForExternalKey returns sandbox object for the supplied path 235 func GetSandboxForExternalKey(basePath string, key string) (Sandbox, error) { 236 if err := createNamespaceFile(key); err != nil { 237 return nil, err 238 } 239 240 if err := mountNetworkNamespace(basePath, key); err != nil { 241 return nil, err 242 } 243 n := &networkNamespace{path: key} 244 245 sboxNs, err := netns.GetFromPath(n.path) 246 if err != nil { 247 return nil, fmt.Errorf("failed get network namespace %q: %v", n.path, err) 248 } 249 defer sboxNs.Close() 250 251 n.nlHandle, err = netlink.NewHandleAt(sboxNs, syscall.NETLINK_ROUTE) 252 if err != nil { 253 return nil, fmt.Errorf("failed to create a netlink handle: %v", err) 254 } 255 256 if err = n.loopbackUp(); err != nil { 257 n.nlHandle.Delete() 258 return nil, err 259 } 260 261 return n, nil 262 } 263 264 func reexecCreateNamespace() { 265 if len(os.Args) < 2 { 266 log.Fatal("no namespace path provided") 267 } 268 if err := mountNetworkNamespace("/proc/self/ns/net", os.Args[1]); err != nil { 269 log.Fatal(err) 270 } 271 } 272 273 func createNetworkNamespace(path string, osCreate bool) error { 274 if err := createNamespaceFile(path); err != nil { 275 return err 276 } 277 278 cmd := &exec.Cmd{ 279 Path: reexec.Self(), 280 Args: append([]string{"netns-create"}, path), 281 Stdout: os.Stdout, 282 Stderr: os.Stderr, 283 } 284 if osCreate { 285 cmd.SysProcAttr = &syscall.SysProcAttr{} 286 cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET 287 } 288 if err := cmd.Run(); err != nil { 289 return fmt.Errorf("namespace creation reexec command failed: %v", err) 290 } 291 292 return nil 293 } 294 295 func unmountNamespaceFile(path string) { 296 if _, err := os.Stat(path); err == nil { 297 syscall.Unmount(path, syscall.MNT_DETACH) 298 } 299 } 300 301 func createNamespaceFile(path string) (err error) { 302 var f *os.File 303 304 once.Do(createBasePath) 305 // Remove it from garbage collection list if present 306 removeFromGarbagePaths(path) 307 308 // If the path is there unmount it first 309 unmountNamespaceFile(path) 310 311 // wait for garbage collection to complete if it is in progress 312 // before trying to create the file. 313 gpmWg.Wait() 314 315 if f, err = os.Create(path); err == nil { 316 f.Close() 317 } 318 319 return err 320 } 321 322 func (n *networkNamespace) loopbackUp() error { 323 iface, err := n.nlHandle.LinkByName("lo") 324 if err != nil { 325 return err 326 } 327 return n.nlHandle.LinkSetUp(iface) 328 } 329 330 func (n *networkNamespace) InvokeFunc(f func()) error { 331 return nsInvoke(n.nsPath(), func(nsFD int) error { return nil }, func(callerFD int) error { 332 f() 333 return nil 334 }) 335 } 336 337 // InitOSContext initializes OS context while configuring network resources 338 func InitOSContext() func() { 339 runtime.LockOSThread() 340 if err := ns.SetNamespace(); err != nil { 341 log.Error(err) 342 } 343 return runtime.UnlockOSThread 344 } 345 346 func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error { 347 defer InitOSContext()() 348 349 newNs, err := netns.GetFromPath(path) 350 if err != nil { 351 return fmt.Errorf("failed get network namespace %q: %v", path, err) 352 } 353 defer newNs.Close() 354 355 // Invoked before the namespace switch happens but after the namespace file 356 // handle is obtained. 357 if err := prefunc(int(newNs)); err != nil { 358 return fmt.Errorf("failed in prefunc: %v", err) 359 } 360 361 if err = netns.Set(newNs); err != nil { 362 return err 363 } 364 defer ns.SetNamespace() 365 366 // Invoked after the namespace switch. 367 return postfunc(ns.ParseHandlerInt()) 368 } 369 370 func (n *networkNamespace) nsPath() string { 371 n.Lock() 372 defer n.Unlock() 373 374 return n.path 375 } 376 377 func (n *networkNamespace) Info() Info { 378 return n 379 } 380 381 func (n *networkNamespace) Key() string { 382 return n.path 383 } 384 385 func (n *networkNamespace) Destroy() error { 386 if n.nlHandle != nil { 387 n.nlHandle.Delete() 388 } 389 // Assuming no running process is executing in this network namespace, 390 // unmounting is sufficient to destroy it. 391 if err := syscall.Unmount(n.path, syscall.MNT_DETACH); err != nil { 392 return err 393 } 394 395 // Stash it into the garbage collection list 396 addToGarbagePaths(n.path) 397 return nil 398 } 399 400 // Restore restore the network namespace 401 func (n *networkNamespace) Restore(ifsopt map[string][]IfaceOption, routes []*types.StaticRoute, gw net.IP, gw6 net.IP) error { 402 // restore interfaces 403 for name, opts := range ifsopt { 404 if !strings.Contains(name, "+") { 405 return fmt.Errorf("wrong iface name in restore osl sandbox interface: %s", name) 406 } 407 seps := strings.Split(name, "+") 408 srcName := seps[0] 409 dstPrefix := seps[1] 410 i := &nwIface{srcName: srcName, dstName: dstPrefix, ns: n} 411 i.processInterfaceOptions(opts...) 412 if i.master != "" { 413 i.dstMaster = n.findDst(i.master, true) 414 if i.dstMaster == "" { 415 return fmt.Errorf("could not find an appropriate master %q for %q", 416 i.master, i.srcName) 417 } 418 } 419 if n.isDefault { 420 i.dstName = i.srcName 421 } else { 422 links, err := n.nlHandle.LinkList() 423 if err != nil { 424 return fmt.Errorf("failed to retrieve list of links in network namespace %q during restore", n.path) 425 } 426 // due to the docker network connect/disconnect, so the dstName should 427 // restore from the namespace 428 for _, link := range links { 429 addrs, err := n.nlHandle.AddrList(link, netlink.FAMILY_V4) 430 if err != nil { 431 return err 432 } 433 ifaceName := link.Attrs().Name 434 if strings.HasPrefix(ifaceName, "vxlan") { 435 if i.dstName == "vxlan" { 436 i.dstName = ifaceName 437 break 438 } 439 } 440 // find the interface name by ip 441 if i.address != nil { 442 for _, addr := range addrs { 443 if addr.IPNet.String() == i.address.String() { 444 i.dstName = ifaceName 445 break 446 } 447 continue 448 } 449 if i.dstName == ifaceName { 450 break 451 } 452 } 453 // This is to find the interface name of the pair in overlay sandbox 454 if strings.HasPrefix(ifaceName, "veth") { 455 if i.master != "" && i.dstName == "veth" { 456 i.dstName = ifaceName 457 } 458 } 459 } 460 461 var index int 462 indexStr := strings.TrimPrefix(i.dstName, dstPrefix) 463 if indexStr != "" { 464 index, err = strconv.Atoi(indexStr) 465 if err != nil { 466 return err 467 } 468 } 469 index++ 470 n.Lock() 471 if index > n.nextIfIndex { 472 n.nextIfIndex = index 473 } 474 n.iFaces = append(n.iFaces, i) 475 n.Unlock() 476 } 477 } 478 479 // restore routes 480 for _, r := range routes { 481 n.Lock() 482 n.staticRoutes = append(n.staticRoutes, r) 483 n.Unlock() 484 } 485 486 // restore gateway 487 if len(gw) > 0 { 488 n.Lock() 489 n.gw = gw 490 n.Unlock() 491 } 492 493 if len(gw6) > 0 { 494 n.Lock() 495 n.gwv6 = gw6 496 n.Unlock() 497 } 498 499 return nil 500 }