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  }