libcontainer support for arbitrary route table entries

Docker-DCO-1.1-Signed-off-by: William Thurston <me@williamthurston.com> (github: jhspaybar)
This commit is contained in:
William Thurston 2014-05-17 07:06:29 +00:00
parent ed1c71fa15
commit d931738466
6 changed files with 66 additions and 12 deletions

View File

@ -46,6 +46,9 @@ type Container struct {
// Networks specifies the container's network setup to be created
Networks []*Network `json:"networks,omitempty"`
// Routes can be specified to create entries in the route table as the container is started
Routes []*Route `json:"routes,omitempty"`
// Cgroups specifies specific cgroup settings for the various subsystems that the container is
// placed into to limit the resources the container has available
Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"`
@ -91,3 +94,24 @@ type Network struct {
// container's interfaces if a pair is created, specifically in the case of type veth
Mtu int `json:"mtu,omitempty"`
}
// Routes can be specified to create entries in the route table as the container is started
//
// All of destination, source, and gateway should be either IPv4 or IPv6.
// One of the three options must be present, and ommitted entries will use their
// IP family default for the route table. For IPv4 for example, setting the
// gateway to 1.2.3.4 and the interface to eth0 will set up a standard
// destination of 0.0.0.0(or *) when viewed in the route table.
type Route struct {
// Sets the destination and mask, should be a CIDR. Accepts IPv4 and IPv6
Destination string `json:"destination,omitempty"`
// Sets the source and mask, should be a CIDR. Accepts IPv4 and IPv6
Source string `json:"source,omitempty"`
// Sets the gateway. Accepts IPv4 and IPv6
Gateway string `json:"gateway,omitempty"`
// The device to set this route up for, for example: eth0
InterfaceName string `json:"interface_name,omitempty"`
}

View File

@ -24,6 +24,16 @@
"mtu": 1500
}
],
"routes": [
{
"gateway": "172.17.42.1",
"interface_name": "eth0"
},
{
"destination": "192.168.0.0/24",
"interface_name": "eth0"
}
],
"capabilities": [
"MKNOD"
],

View File

@ -39,6 +39,11 @@ func TestContainerJsonFormat(t *testing.T) {
t.Fail()
}
if len(container.Routes) != 2 {
t.Log("should have found 2 routes")
t.Fail()
}
if !container.Namespaces["NEWNET"] {
t.Log("namespaces should contain NEWNET")
t.Fail()

View File

@ -53,8 +53,8 @@ func SetInterfaceMaster(name, master string) error {
return netlink.AddToBridge(iface, masterIface)
}
func SetDefaultGateway(ip string) error {
return netlink.AddDefaultGw(net.ParseIP(ip))
func SetDefaultGateway(ip, ifaceName string) error {
return netlink.AddDefaultGw(ip, ifaceName)
}
func SetInterfaceIp(name string, rawIp string) error {

View File

@ -12,6 +12,8 @@ import (
type Veth struct {
}
const defaultDevice = "eth0"
func (v *Veth) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error {
var (
bridge string
@ -56,21 +58,21 @@ func (v *Veth) Initialize(config *libcontainer.Network, context libcontainer.Con
if err := InterfaceDown(vethChild); err != nil {
return fmt.Errorf("interface down %s %s", vethChild, err)
}
if err := ChangeInterfaceName(vethChild, "eth0"); err != nil {
return fmt.Errorf("change %s to eth0 %s", vethChild, err)
if err := ChangeInterfaceName(vethChild, defaultDevice); err != nil {
return fmt.Errorf("change %s to %s %s", vethChild, defaultDevice, err)
}
if err := SetInterfaceIp("eth0", config.Address); err != nil {
return fmt.Errorf("set eth0 ip %s", err)
if err := SetInterfaceIp(defaultDevice, config.Address); err != nil {
return fmt.Errorf("set %s ip %s", defaultDevice, err)
}
if err := SetMtu("eth0", config.Mtu); err != nil {
return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
if err := SetMtu(defaultDevice, config.Mtu); err != nil {
return fmt.Errorf("set %s mtu to %d %s", defaultDevice, config.Mtu, err)
}
if err := InterfaceUp("eth0"); err != nil {
return fmt.Errorf("eth0 up %s", err)
if err := InterfaceUp(defaultDevice); err != nil {
return fmt.Errorf("%s up %s", defaultDevice, err)
}
if config.Gateway != "" {
if err := SetDefaultGateway(config.Gateway); err != nil {
return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
if err := SetDefaultGateway(config.Gateway, defaultDevice); err != nil {
return fmt.Errorf("set gateway to %s on device %s failed with %s", config.Gateway, defaultDevice, err)
}
}
return nil

View File

@ -18,6 +18,7 @@ import (
"github.com/dotcloud/docker/pkg/libcontainer/security/capabilities"
"github.com/dotcloud/docker/pkg/libcontainer/security/restrict"
"github.com/dotcloud/docker/pkg/libcontainer/utils"
"github.com/dotcloud/docker/pkg/netlink"
"github.com/dotcloud/docker/pkg/system"
"github.com/dotcloud/docker/pkg/user"
)
@ -60,6 +61,9 @@ func Init(container *libcontainer.Container, uncleanRootfs, consolePath string,
if err := setupNetwork(container, context); err != nil {
return fmt.Errorf("setup networking %s", err)
}
if err := setupRoute(container); err != nil {
return fmt.Errorf("setup route %s", err)
}
label.Init()
@ -168,6 +172,15 @@ func setupNetwork(container *libcontainer.Container, context libcontainer.Contex
return nil
}
func setupRoute(container *libcontainer.Container) error {
for _, config := range container.Routes {
if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil {
return err
}
}
return nil
}
// FinalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaky file descriptors
// before execing the command inside the namespace