mirror of
https://github.com/opencontainers/runc.git
synced 2025-10-11 18:30:54 +08:00

Remove veth interfaces on the host if an error occurs. Provide the host interface name, temporary peer interface name and the name of the peer once it is inside the container's namespace in the Network config. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
215 lines
5.5 KiB
Go
215 lines
5.5 KiB
Go
// +build linux
|
|
|
|
package libcontainer
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/docker/libcontainer/netlink"
|
|
"github.com/docker/libcontainer/utils"
|
|
)
|
|
|
|
var (
|
|
ErrNotValidStrategyType = errors.New("not a valid network strategy type")
|
|
)
|
|
|
|
var strategies = map[string]networkStrategy{
|
|
"veth": &veth{},
|
|
"loopback": &loopback{},
|
|
}
|
|
|
|
// networkStrategy represents a specific network configuration for
|
|
// a container's networking stack
|
|
type networkStrategy interface {
|
|
create(*network, int) error
|
|
initialize(*network) error
|
|
}
|
|
|
|
// getStrategy returns the specific network strategy for the
|
|
// provided type. If no strategy is registered for the type an
|
|
// ErrNotValidStrategyType is returned.
|
|
func getStrategy(tpe string) (networkStrategy, error) {
|
|
s, exists := strategies[tpe]
|
|
if !exists {
|
|
return nil, ErrNotValidStrategyType
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
|
|
func getNetworkInterfaceStats(interfaceName string) (*NetworkInterface, error) {
|
|
out := &NetworkInterface{Name: interfaceName}
|
|
// This can happen if the network runtime information is missing - possible if the
|
|
// container was created by an old version of libcontainer.
|
|
if interfaceName == "" {
|
|
return out, nil
|
|
}
|
|
type netStatsPair struct {
|
|
// Where to write the output.
|
|
Out *uint64
|
|
// The network stats file to read.
|
|
File string
|
|
}
|
|
// 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.
|
|
netStats := []netStatsPair{
|
|
{Out: &out.RxBytes, File: "tx_bytes"},
|
|
{Out: &out.RxPackets, File: "tx_packets"},
|
|
{Out: &out.RxErrors, File: "tx_errors"},
|
|
{Out: &out.RxDropped, File: "tx_dropped"},
|
|
|
|
{Out: &out.TxBytes, File: "rx_bytes"},
|
|
{Out: &out.TxPackets, File: "rx_packets"},
|
|
{Out: &out.TxErrors, File: "rx_errors"},
|
|
{Out: &out.TxDropped, File: "rx_dropped"},
|
|
}
|
|
for _, netStat := range netStats {
|
|
data, err := readSysfsNetworkStats(interfaceName, netStat.File)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
*(netStat.Out) = data
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics
|
|
func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
|
|
data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
|
|
}
|
|
|
|
// loopback is a network strategy that provides a basic loopback device
|
|
type loopback struct {
|
|
}
|
|
|
|
func (l *loopback) create(n *network, nspid int) error {
|
|
return nil
|
|
}
|
|
|
|
func (l *loopback) initialize(config *network) error {
|
|
iface, err := net.InterfaceByName("lo")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return netlink.NetworkLinkUp(iface)
|
|
}
|
|
|
|
// veth is a network strategy that uses a bridge and creates
|
|
// a veth pair, one that stays outside on the host and the other
|
|
// is placed inside the container's namespace
|
|
type veth struct {
|
|
}
|
|
|
|
func (v *veth) create(n *network, nspid int) (err error) {
|
|
tmpName, err := v.generateTempPeerName()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n.TempVethPeerName = tmpName
|
|
defer func() {
|
|
if err != nil {
|
|
netlink.NetworkLinkDel(n.HostInterfaceName)
|
|
netlink.NetworkLinkDel(n.TempVethPeerName)
|
|
}
|
|
}()
|
|
if n.Bridge == "" {
|
|
return fmt.Errorf("bridge is not specified")
|
|
}
|
|
bridge, err := net.InterfaceByName(n.Bridge)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkCreateVethPair(n.HostInterfaceName, n.TempVethPeerName, n.TxQueueLen); err != nil {
|
|
return err
|
|
}
|
|
host, err := net.InterfaceByName(n.HostInterfaceName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.AddToBridge(host, bridge); err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkSetMTU(host, n.Mtu); err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkLinkUp(host); err != nil {
|
|
return err
|
|
}
|
|
child, err := net.InterfaceByName(n.TempVethPeerName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return netlink.NetworkSetNsPid(child, nspid)
|
|
}
|
|
|
|
func (v *veth) generateTempPeerName() (string, error) {
|
|
return utils.GenerateRandomName("veth", 7)
|
|
}
|
|
|
|
func (v *veth) initialize(config *network) error {
|
|
peer := config.TempVethPeerName
|
|
if peer == "" {
|
|
return fmt.Errorf("peer is not specified")
|
|
}
|
|
child, err := net.InterfaceByName(peer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkLinkDown(child); err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkChangeName(child, config.Name); err != nil {
|
|
return err
|
|
}
|
|
// get the interface again after we changed the name as the index also changes.
|
|
if child, err = net.InterfaceByName(config.Name); err != nil {
|
|
return err
|
|
}
|
|
if config.MacAddress != "" {
|
|
if err := netlink.NetworkSetMacAddress(child, config.MacAddress); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
ip, ipNet, err := net.ParseCIDR(config.Address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkLinkAddIp(child, ip, ipNet); err != nil {
|
|
return err
|
|
}
|
|
if config.IPv6Address != "" {
|
|
if ip, ipNet, err = net.ParseCIDR(config.IPv6Address); err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkLinkAddIp(child, ip, ipNet); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := netlink.NetworkSetMTU(child, config.Mtu); err != nil {
|
|
return err
|
|
}
|
|
if err := netlink.NetworkLinkUp(child); err != nil {
|
|
return err
|
|
}
|
|
if config.Gateway != "" {
|
|
if err := netlink.AddDefaultGw(config.Gateway, config.Name); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if config.IPv6Gateway != "" {
|
|
if err := netlink.AddDefaultGw(config.IPv6Gateway, config.Name); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|