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:
parent
2ec6b585ea
commit
fde0b7aa0d
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue