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  }