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 string `json:"type,omitempty"`
// Name of the network interface
Name string `json:"name,omitempty"`
// The bridge to use.
Bridge string `json:"bridge,omitempty"`
@ -36,11 +39,9 @@ type Network struct {
// Note: This does not apply to loopback interfaces.
TxQueueLen int `json:"txqueuelen,omitempty"`
// The name of the veth interface on the Host.
VethHost string `json:"veth_host,omitempty"`
// The name of the veth interface created inside the container for the child.
VethChild string `json:"veth_child,omitempty"`
// HostInterfaceName is a unique name of a veth pair that resides on in the host interface of the
// container.
HostInterfaceName string `json:"host_interface_name,omitempty"`
}
// 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" {
return
}
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
factory, err := libcontainer.New("", nil)
if err != nil {
log.Fatalf("unable to initialize for container: %s", err)
}
factory.StartInitialization(3)
os.Exit(1)
}

View File

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

View File

@ -29,13 +29,23 @@ type pid struct {
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()
type initConfig struct {
Args []string `json:"args"`
Env []string `json:"env"`
Cwd string `json:"cwd"`
User string `json:"user"`
Config *configs.Config `json:"config"`
Args []string `json:"args"`
Env []string `json:"env"`
Cwd string `json:"cwd"`
User string `json:"user"`
Config *configs.Config `json:"config"`
Networks []*network `json:"network"`
}
type initer interface {
@ -184,18 +194,15 @@ func setupUser(config *initConfig) error {
return nil
}
// setupVethNetwork uses the Network config if it is not nil to initialize
// the new veth interface inside the container for use by changing the name to eth0
// setting the MTU and IP address along with the default gateway
func setupNetwork(config *configs.Config) error {
// setupNetwork sets up and initializes any network interface inside the container.
func setupNetwork(config *initConfig) error {
for _, config := range config.Networks {
strategy, err := getStrategy(config.Type)
if err != nil {
return err
}
err1 := strategy.Initialize(config)
if err1 != nil {
return err1
if err := strategy.initialize(config); err != nil {
return err
}
}
return nil

View File

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

View File

@ -239,9 +239,13 @@ func (p *initProcess) createNetworkInterfaces() error {
if err != nil {
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
}
p.config.Networks = append(p.config.Networks, n)
}
return nil
}

View File

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

View File

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

View File

@ -18,7 +18,7 @@ type linuxUsernsSideCar struct {
}
func (l *linuxUsernsSideCar) Init() error {
if err := setupNetwork(l.config.Config); err != nil {
if err := setupNetwork(l.config); err != nil {
return err
}
if err := setupRoute(l.config.Config); err != nil {
@ -26,7 +26,7 @@ func (l *linuxUsernsSideCar) Init() error {
}
label.Init()
// 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 {
return err
}

View File

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