Files
runc/linux_network.go
Michael Crosby fde0b7aa0d Refactor network and veth creation
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>
2015-02-11 13:33:58 -08:00

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
}