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>
This commit is contained in:
Michael Crosby 2015-02-10 11:51:45 -08:00
parent 2ec6b585ea
commit fde0b7aa0d
10 changed files with 76 additions and 51 deletions

View File

@ -8,6 +8,9 @@ type Network struct {
// Type sets the networks type, commonly veth and loopback // Type sets the networks type, commonly veth and loopback
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
// Name of the network interface
Name string `json:"name,omitempty"`
// The bridge to use. // The bridge to use.
Bridge string `json:"bridge,omitempty"` Bridge string `json:"bridge,omitempty"`
@ -36,11 +39,9 @@ type Network struct {
// Note: This does not apply to loopback interfaces. // Note: This does not apply to loopback interfaces.
TxQueueLen int `json:"txqueuelen,omitempty"` TxQueueLen int `json:"txqueuelen,omitempty"`
// The name of the veth interface on the Host. // HostInterfaceName is a unique name of a veth pair that resides on in the host interface of the
VethHost string `json:"veth_host,omitempty"` // container.
HostInterfaceName string `json:"host_interface_name,omitempty"`
// The name of the veth interface created inside the container for the child.
VethChild string `json:"veth_child,omitempty"`
} }
// Routes can be specified to create entries in the route table as the container is started // Routes can be specified to create entries in the route table as the container is started

View File

@ -15,14 +15,12 @@ func init() {
if len(os.Args) < 2 || os.Args[1] != "init" { if len(os.Args) < 2 || os.Args[1] != "init" {
return return
} }
runtime.GOMAXPROCS(1)
runtime.LockOSThread() runtime.LockOSThread()
factory, err := libcontainer.New("", nil) factory, err := libcontainer.New("", nil)
if err != nil { if err != nil {
log.Fatalf("unable to initialize for container: %s", err) log.Fatalf("unable to initialize for container: %s", err)
} }
factory.StartInitialization(3) factory.StartInitialization(3)
os.Exit(1) os.Exit(1)
} }

View File

@ -71,7 +71,7 @@ func (c *linuxContainer) Stats() (*Stats, error) {
for _, iface := range c.config.Networks { for _, iface := range c.config.Networks {
switch iface.Type { switch iface.Type {
case "veth": case "veth":
istats, err := getNetworkInterfaceStats(iface.VethHost) istats, err := getNetworkInterfaceStats(iface.HostInterfaceName)
if err != nil { if err != nil {
return stats, newGenericError(err, SystemError) return stats, newGenericError(err, SystemError)
} }
@ -134,6 +134,8 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
} }
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *initProcess { func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *initProcess {
t := "_LIBCONTAINER_INITTYPE=standard"
cloneFlags := c.config.Namespaces.CloneFlags() cloneFlags := c.config.Namespaces.CloneFlags()
if cloneFlags&syscall.CLONE_NEWUSER != 0 { if cloneFlags&syscall.CLONE_NEWUSER != 0 {
c.addUidGidMappings(cmd.SysProcAttr) c.addUidGidMappings(cmd.SysProcAttr)
@ -141,9 +143,10 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
if cmd.SysProcAttr.Credential == nil { if cmd.SysProcAttr.Credential == nil {
cmd.SysProcAttr.Credential = &syscall.Credential{} cmd.SysProcAttr.Credential = &syscall.Credential{}
} }
t = "_LIBCONTAINER_INITTYPE=userns"
} }
cmd.Env = append(cmd.Env, t)
cmd.SysProcAttr.Cloneflags = cloneFlags cmd.SysProcAttr.Cloneflags = cloneFlags
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE=standard")
return &initProcess{ return &initProcess{
cmd: cmd, cmd: cmd,
childPipe: childPipe, childPipe: childPipe,

View File

@ -29,13 +29,23 @@ type pid struct {
Pid int `json:"pid"` Pid int `json:"pid"`
} }
// network is an internal struct used to setup container networks.
type network struct {
configs.Network
// TempVethPeerName is a unique tempory veth peer name that was placed into
// the container's namespace.
TempVethPeerName string `json:"temp_veth_peer_name"`
}
// Process is used for transferring parameters from Exec() to Init() // Process is used for transferring parameters from Exec() to Init()
type initConfig struct { type initConfig struct {
Args []string `json:"args"` Args []string `json:"args"`
Env []string `json:"env"` Env []string `json:"env"`
Cwd string `json:"cwd"` Cwd string `json:"cwd"`
User string `json:"user"` User string `json:"user"`
Config *configs.Config `json:"config"` Config *configs.Config `json:"config"`
Networks []*network `json:"network"`
} }
type initer interface { type initer interface {
@ -184,18 +194,15 @@ func setupUser(config *initConfig) error {
return nil return nil
} }
// setupVethNetwork uses the Network config if it is not nil to initialize // setupNetwork sets up and initializes any network interface inside the container.
// the new veth interface inside the container for use by changing the name to eth0 func setupNetwork(config *initConfig) error {
// setting the MTU and IP address along with the default gateway
func setupNetwork(config *configs.Config) error {
for _, config := range config.Networks { for _, config := range config.Networks {
strategy, err := getStrategy(config.Type) strategy, err := getStrategy(config.Type)
if err != nil { if err != nil {
return err return err
} }
err1 := strategy.Initialize(config) if err := strategy.initialize(config); err != nil {
if err1 != nil { return err
return err1
} }
} }
return nil return nil

View File

@ -11,12 +11,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/netlink" "github.com/docker/libcontainer/netlink"
"github.com/docker/libcontainer/utils"
) )
const defaultVethInterfaceName = "eth0"
var ( var (
ErrNotValidStrategyType = errors.New("not a valid network strategy type") ErrNotValidStrategyType = errors.New("not a valid network strategy type")
) )
@ -29,8 +27,8 @@ var strategies = map[string]networkStrategy{
// networkStrategy represents a specific network configuration for // networkStrategy represents a specific network configuration for
// a container's networking stack // a container's networking stack
type networkStrategy interface { type networkStrategy interface {
Create(*configs.Network, int) error create(*network, int) error
Initialize(*configs.Network) error initialize(*network) error
} }
// getStrategy returns the specific network strategy for the // getStrategy returns the specific network strategy for the
@ -93,11 +91,11 @@ func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
type loopback struct { type loopback struct {
} }
func (l *loopback) Create(n *configs.Network, nspid int) error { func (l *loopback) create(n *network, nspid int) error {
return nil return nil
} }
func (l *loopback) Initialize(config *configs.Network) error { func (l *loopback) initialize(config *network) error {
iface, err := net.InterfaceByName("lo") iface, err := net.InterfaceByName("lo")
if err != nil { if err != nil {
return err return err
@ -111,7 +109,18 @@ func (l *loopback) Initialize(config *configs.Network) error {
type veth struct { type veth struct {
} }
func (v *veth) Create(n *configs.Network, nspid int) error { 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 == "" { if n.Bridge == "" {
return fmt.Errorf("bridge is not specified") return fmt.Errorf("bridge is not specified")
} }
@ -119,10 +128,10 @@ func (v *veth) Create(n *configs.Network, nspid int) error {
if err != nil { if err != nil {
return err return err
} }
if err := netlink.NetworkCreateVethPair(n.VethHost, n.VethChild, n.TxQueueLen); err != nil { if err := netlink.NetworkCreateVethPair(n.HostInterfaceName, n.TempVethPeerName, n.TxQueueLen); err != nil {
return err return err
} }
host, err := net.InterfaceByName(n.VethHost) host, err := net.InterfaceByName(n.HostInterfaceName)
if err != nil { if err != nil {
return err return err
} }
@ -135,30 +144,34 @@ func (v *veth) Create(n *configs.Network, nspid int) error {
if err := netlink.NetworkLinkUp(host); err != nil { if err := netlink.NetworkLinkUp(host); err != nil {
return err return err
} }
child, err := net.InterfaceByName(n.VethChild) child, err := net.InterfaceByName(n.TempVethPeerName)
if err != nil { if err != nil {
return err return err
} }
return netlink.NetworkSetNsPid(child, nspid) return netlink.NetworkSetNsPid(child, nspid)
} }
func (v *veth) Initialize(config *configs.Network) error { func (v *veth) generateTempPeerName() (string, error) {
vethChild := config.VethChild return utils.GenerateRandomName("veth", 7)
if vethChild == "" { }
return fmt.Errorf("vethChild is not specified")
func (v *veth) initialize(config *network) error {
peer := config.TempVethPeerName
if peer == "" {
return fmt.Errorf("peer is not specified")
} }
child, err := net.InterfaceByName(vethChild) child, err := net.InterfaceByName(peer)
if err != nil { if err != nil {
return err return err
} }
if err := netlink.NetworkLinkDown(child); err != nil { if err := netlink.NetworkLinkDown(child); err != nil {
return err return err
} }
if err := netlink.NetworkChangeName(child, defaultVethInterfaceName); err != nil { if err := netlink.NetworkChangeName(child, config.Name); err != nil {
return err return err
} }
// get the interface again after we changed the name as the index also changes. // get the interface again after we changed the name as the index also changes.
if child, err = net.InterfaceByName(defaultVethInterfaceName); err != nil { if child, err = net.InterfaceByName(config.Name); err != nil {
return err return err
} }
if config.MacAddress != "" { if config.MacAddress != "" {
@ -188,12 +201,12 @@ func (v *veth) Initialize(config *configs.Network) error {
return err return err
} }
if config.Gateway != "" { if config.Gateway != "" {
if err := netlink.AddDefaultGw(config.Gateway, defaultVethInterfaceName); err != nil { if err := netlink.AddDefaultGw(config.Gateway, config.Name); err != nil {
return err return err
} }
} }
if config.IPv6Gateway != "" { if config.IPv6Gateway != "" {
if err := netlink.AddDefaultGw(config.IPv6Gateway, defaultVethInterfaceName); err != nil { if err := netlink.AddDefaultGw(config.IPv6Gateway, config.Name); err != nil {
return err return err
} }
} }

View File

@ -239,9 +239,13 @@ func (p *initProcess) createNetworkInterfaces() error {
if err != nil { if err != nil {
return err return err
} }
if err := strategy.Create(config, p.pid()); err != nil { n := &network{
Network: *config,
}
if err := strategy.create(n, p.pid()); err != nil {
return err return err
} }
p.config.Networks = append(p.config.Networks, n)
} }
return nil return nil
} }

View File

@ -46,15 +46,11 @@ func setupRootfs(config *configs.Config) (err error) {
if err := setupPtmx(config); err != nil { if err := setupPtmx(config); err != nil {
return err return err
} }
uid, err := config.HostUID()
if err != nil {
return err
}
// stdin, stdout and stderr could be pointing to /dev/null from parent namespace. // stdin, stdout and stderr could be pointing to /dev/null from parent namespace.
// Re-open them inside this namespace. // Re-open them inside this namespace.
// FIXME: Need to fix this for user namespaces. // FIXME: Need to fix this for user namespaces.
if !config.Namespaces.Contains(configs.NEWUSER) { if !config.Namespaces.Contains(configs.NEWUSER) {
if err := reOpenDevNull(config.RootFs); err != nil { if err := reOpenDevNull(config.Rootfs); err != nil {
return err return err
} }
} }

View File

@ -35,7 +35,7 @@ func (l *linuxStandardInit) Init() error {
return err return err
} }
} }
if err := setupNetwork(l.config.Config); err != nil { if err := setupNetwork(l.config); err != nil {
return err return err
} }
if err := setupRoute(l.config.Config); err != nil { if err := setupRoute(l.config.Config); err != nil {

View File

@ -18,7 +18,7 @@ type linuxUsernsSideCar struct {
} }
func (l *linuxUsernsSideCar) Init() error { func (l *linuxUsernsSideCar) Init() error {
if err := setupNetwork(l.config.Config); err != nil { if err := setupNetwork(l.config); err != nil {
return err return err
} }
if err := setupRoute(l.config.Config); err != nil { if err := setupRoute(l.config.Config); err != nil {
@ -26,7 +26,7 @@ func (l *linuxUsernsSideCar) Init() error {
} }
label.Init() label.Init()
// InitializeMountNamespace() can be executed only for a new mount namespace // InitializeMountNamespace() can be executed only for a new mount namespace
if l.config.Config.Namespaces.Contains(configs.NEWNET) { if l.config.Config.Namespaces.Contains(configs.NEWNS) {
if err := setupRootfs(l.config.Config); err != nil { if err := setupRootfs(l.config.Config); err != nil {
return err return err
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"log" "log"
"runtime"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/libcontainer" "github.com/docker/libcontainer"
@ -15,6 +16,8 @@ var initCommand = cli.Command{
cli.IntFlag{Name: "fd", Value: 0, Usage: "internal pipe fd"}, cli.IntFlag{Name: "fd", Value: 0, Usage: "internal pipe fd"},
}, },
Action: func(context *cli.Context) { Action: func(context *cli.Context) {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
factory, err := libcontainer.New("", nil) factory, err := libcontainer.New("", nil)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)