github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/network_linux.go (about)

     1  // +build linux
     2  
     3  package libcontainer
     4  
     5  import (
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/opencontainers/runc/libcontainer/configs"
    14  	"github.com/opencontainers/runc/libcontainer/utils"
    15  	"github.com/vishvananda/netlink"
    16  )
    17  
    18  var strategies = map[string]networkStrategy{
    19  	"veth":     &veth{},
    20  	"loopback": &loopback{},
    21  }
    22  
    23  // networkStrategy represents a specific network configuration for
    24  // a container's networking stack
    25  type networkStrategy interface {
    26  	create(*network, int) error
    27  	initialize(*network) error
    28  	detach(*configs.Network) error
    29  	attach(*configs.Network) error
    30  }
    31  
    32  // getStrategy returns the specific network strategy for the
    33  // provided type.
    34  func getStrategy(tpe string) (networkStrategy, error) {
    35  	s, exists := strategies[tpe]
    36  	if !exists {
    37  		return nil, fmt.Errorf("unknown strategy type %q", tpe)
    38  	}
    39  	return s, nil
    40  }
    41  
    42  // Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
    43  func getNetworkInterfaceStats(interfaceName string) (*NetworkInterface, error) {
    44  	out := &NetworkInterface{Name: interfaceName}
    45  	// This can happen if the network runtime information is missing - possible if the
    46  	// container was created by an old version of libcontainer.
    47  	if interfaceName == "" {
    48  		return out, nil
    49  	}
    50  	type netStatsPair struct {
    51  		// Where to write the output.
    52  		Out *uint64
    53  		// The network stats file to read.
    54  		File string
    55  	}
    56  	// Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
    57  	netStats := []netStatsPair{
    58  		{Out: &out.RxBytes, File: "tx_bytes"},
    59  		{Out: &out.RxPackets, File: "tx_packets"},
    60  		{Out: &out.RxErrors, File: "tx_errors"},
    61  		{Out: &out.RxDropped, File: "tx_dropped"},
    62  
    63  		{Out: &out.TxBytes, File: "rx_bytes"},
    64  		{Out: &out.TxPackets, File: "rx_packets"},
    65  		{Out: &out.TxErrors, File: "rx_errors"},
    66  		{Out: &out.TxDropped, File: "rx_dropped"},
    67  	}
    68  	for _, netStat := range netStats {
    69  		data, err := readSysfsNetworkStats(interfaceName, netStat.File)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		*(netStat.Out) = data
    74  	}
    75  	return out, nil
    76  }
    77  
    78  // Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics
    79  func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
    80  	data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile))
    81  	if err != nil {
    82  		return 0, err
    83  	}
    84  	return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
    85  }
    86  
    87  // loopback is a network strategy that provides a basic loopback device
    88  type loopback struct {
    89  }
    90  
    91  func (l *loopback) create(n *network, nspid int) error {
    92  	return nil
    93  }
    94  
    95  func (l *loopback) initialize(config *network) error {
    96  	return netlink.LinkSetUp(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: "lo"}})
    97  }
    98  
    99  func (l *loopback) attach(n *configs.Network) (err error) {
   100  	return nil
   101  }
   102  
   103  func (l *loopback) detach(n *configs.Network) (err error) {
   104  	return nil
   105  }
   106  
   107  // veth is a network strategy that uses a bridge and creates
   108  // a veth pair, one that is attached to the bridge on the host and the other
   109  // is placed inside the container's namespace
   110  type veth struct {
   111  }
   112  
   113  func (v *veth) detach(n *configs.Network) (err error) {
   114  	return netlink.LinkSetMaster(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: n.HostInterfaceName}}, nil)
   115  }
   116  
   117  // attach a container network interface to an external network
   118  func (v *veth) attach(n *configs.Network) (err error) {
   119  	brl, err := netlink.LinkByName(n.Bridge)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	br, ok := brl.(*netlink.Bridge)
   124  	if !ok {
   125  		return fmt.Errorf("Wrong device type %T", brl)
   126  	}
   127  	host, err := netlink.LinkByName(n.HostInterfaceName)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	if err := netlink.LinkSetMaster(host, br); err != nil {
   133  		return err
   134  	}
   135  	if err := netlink.LinkSetMTU(host, n.Mtu); err != nil {
   136  		return err
   137  	}
   138  	if n.HairpinMode {
   139  		if err := netlink.LinkSetHairpin(host, true); err != nil {
   140  			return err
   141  		}
   142  	}
   143  	if err := netlink.LinkSetUp(host); err != nil {
   144  		return err
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func (v *veth) create(n *network, nspid int) (err error) {
   151  	tmpName, err := v.generateTempPeerName()
   152  	if err != nil {
   153  		return err
   154  	}
   155  	n.TempVethPeerName = tmpName
   156  	if n.Bridge == "" {
   157  		return fmt.Errorf("bridge is not specified")
   158  	}
   159  	veth := &netlink.Veth{
   160  		LinkAttrs: netlink.LinkAttrs{
   161  			Name:   n.HostInterfaceName,
   162  			TxQLen: n.TxQueueLen,
   163  		},
   164  		PeerName: n.TempVethPeerName,
   165  	}
   166  	if err := netlink.LinkAdd(veth); err != nil {
   167  		return err
   168  	}
   169  	defer func() {
   170  		if err != nil {
   171  			netlink.LinkDel(veth)
   172  		}
   173  	}()
   174  	if err := v.attach(&n.Network); err != nil {
   175  		return err
   176  	}
   177  	child, err := netlink.LinkByName(n.TempVethPeerName)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	return netlink.LinkSetNsPid(child, nspid)
   182  }
   183  
   184  func (v *veth) generateTempPeerName() (string, error) {
   185  	return utils.GenerateRandomName("veth", 7)
   186  }
   187  
   188  func (v *veth) initialize(config *network) error {
   189  	peer := config.TempVethPeerName
   190  	if peer == "" {
   191  		return fmt.Errorf("peer is not specified")
   192  	}
   193  	child, err := netlink.LinkByName(peer)
   194  	if err != nil {
   195  		return err
   196  	}
   197  	if err := netlink.LinkSetDown(child); err != nil {
   198  		return err
   199  	}
   200  	if err := netlink.LinkSetName(child, config.Name); err != nil {
   201  		return err
   202  	}
   203  	// get the interface again after we changed the name as the index also changes.
   204  	if child, err = netlink.LinkByName(config.Name); err != nil {
   205  		return err
   206  	}
   207  	if config.MacAddress != "" {
   208  		mac, err := net.ParseMAC(config.MacAddress)
   209  		if err != nil {
   210  			return err
   211  		}
   212  		if err := netlink.LinkSetHardwareAddr(child, mac); err != nil {
   213  			return err
   214  		}
   215  	}
   216  	ip, err := netlink.ParseAddr(config.Address)
   217  	if err != nil {
   218  		return err
   219  	}
   220  	if err := netlink.AddrAdd(child, ip); err != nil {
   221  		return err
   222  	}
   223  	if config.IPv6Address != "" {
   224  		ip6, err := netlink.ParseAddr(config.IPv6Address)
   225  		if err != nil {
   226  			return err
   227  		}
   228  		if err := netlink.AddrAdd(child, ip6); err != nil {
   229  			return err
   230  		}
   231  	}
   232  	if err := netlink.LinkSetMTU(child, config.Mtu); err != nil {
   233  		return err
   234  	}
   235  	if err := netlink.LinkSetUp(child); err != nil {
   236  		return err
   237  	}
   238  	if config.Gateway != "" {
   239  		gw := net.ParseIP(config.Gateway)
   240  		if err := netlink.RouteAdd(&netlink.Route{
   241  			Scope:     netlink.SCOPE_UNIVERSE,
   242  			LinkIndex: child.Attrs().Index,
   243  			Gw:        gw,
   244  		}); err != nil {
   245  			return err
   246  		}
   247  	}
   248  	if config.IPv6Gateway != "" {
   249  		gw := net.ParseIP(config.IPv6Gateway)
   250  		if err := netlink.RouteAdd(&netlink.Route{
   251  			Scope:     netlink.SCOPE_UNIVERSE,
   252  			LinkIndex: child.Attrs().Index,
   253  			Gw:        gw,
   254  		}); err != nil {
   255  			return err
   256  		}
   257  	}
   258  	return nil
   259  }