Merge pull request #5868 from jhspaybar/5749-libcontainerroutes
libcontainer support for arbitrary route table entries
This commit is contained in:
commit
744a7c030c
8 changed files with 165 additions and 31 deletions
|
@ -46,6 +46,9 @@ type Container struct {
|
||||||
// Networks specifies the container's network setup to be created
|
// Networks specifies the container's network setup to be created
|
||||||
Networks []*Network `json:"networks,omitempty"`
|
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
|
// Cgroups specifies specific cgroup settings for the various subsystems that the container is
|
||||||
// placed into to limit the resources the container has available
|
// placed into to limit the resources the container has available
|
||||||
Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"`
|
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
|
// container's interfaces if a pair is created, specifically in the case of type veth
|
||||||
Mtu int `json:"mtu,omitempty"`
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,16 @@
|
||||||
"mtu": 1500
|
"mtu": 1500
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"gateway": "172.17.42.1",
|
||||||
|
"interface_name": "eth0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "192.168.0.0/24",
|
||||||
|
"interface_name": "eth0"
|
||||||
|
}
|
||||||
|
],
|
||||||
"capabilities": [
|
"capabilities": [
|
||||||
"MKNOD"
|
"MKNOD"
|
||||||
],
|
],
|
||||||
|
|
|
@ -39,6 +39,11 @@ func TestContainerJsonFormat(t *testing.T) {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(container.Routes) != 2 {
|
||||||
|
t.Log("should have found 2 routes")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
if !container.Namespaces["NEWNET"] {
|
if !container.Namespaces["NEWNET"] {
|
||||||
t.Log("namespaces should contain NEWNET")
|
t.Log("namespaces should contain NEWNET")
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
|
|
@ -53,8 +53,8 @@ func SetInterfaceMaster(name, master string) error {
|
||||||
return netlink.AddToBridge(iface, masterIface)
|
return netlink.AddToBridge(iface, masterIface)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetDefaultGateway(ip string) error {
|
func SetDefaultGateway(ip, ifaceName string) error {
|
||||||
return netlink.AddDefaultGw(net.ParseIP(ip))
|
return netlink.AddDefaultGw(ip, ifaceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetInterfaceIp(name string, rawIp string) error {
|
func SetInterfaceIp(name string, rawIp string) error {
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
type Veth struct {
|
type Veth struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultDevice = "eth0"
|
||||||
|
|
||||||
func (v *Veth) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error {
|
func (v *Veth) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error {
|
||||||
var (
|
var (
|
||||||
bridge string
|
bridge string
|
||||||
|
@ -56,21 +58,21 @@ func (v *Veth) Initialize(config *libcontainer.Network, context libcontainer.Con
|
||||||
if err := InterfaceDown(vethChild); err != nil {
|
if err := InterfaceDown(vethChild); err != nil {
|
||||||
return fmt.Errorf("interface down %s %s", vethChild, err)
|
return fmt.Errorf("interface down %s %s", vethChild, err)
|
||||||
}
|
}
|
||||||
if err := ChangeInterfaceName(vethChild, "eth0"); err != nil {
|
if err := ChangeInterfaceName(vethChild, defaultDevice); err != nil {
|
||||||
return fmt.Errorf("change %s to eth0 %s", vethChild, err)
|
return fmt.Errorf("change %s to %s %s", vethChild, defaultDevice, err)
|
||||||
}
|
}
|
||||||
if err := SetInterfaceIp("eth0", config.Address); err != nil {
|
if err := SetInterfaceIp(defaultDevice, config.Address); err != nil {
|
||||||
return fmt.Errorf("set eth0 ip %s", err)
|
return fmt.Errorf("set %s ip %s", defaultDevice, err)
|
||||||
}
|
}
|
||||||
if err := SetMtu("eth0", config.Mtu); err != nil {
|
if err := SetMtu(defaultDevice, config.Mtu); err != nil {
|
||||||
return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
|
return fmt.Errorf("set %s mtu to %d %s", defaultDevice, config.Mtu, err)
|
||||||
}
|
}
|
||||||
if err := InterfaceUp("eth0"); err != nil {
|
if err := InterfaceUp(defaultDevice); err != nil {
|
||||||
return fmt.Errorf("eth0 up %s", err)
|
return fmt.Errorf("%s up %s", defaultDevice, err)
|
||||||
}
|
}
|
||||||
if config.Gateway != "" {
|
if config.Gateway != "" {
|
||||||
if err := SetDefaultGateway(config.Gateway); err != nil {
|
if err := SetDefaultGateway(config.Gateway, defaultDevice); err != nil {
|
||||||
return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
|
return fmt.Errorf("set gateway to %s on device %s failed with %s", config.Gateway, defaultDevice, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/security/capabilities"
|
"github.com/dotcloud/docker/pkg/libcontainer/security/capabilities"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/security/restrict"
|
"github.com/dotcloud/docker/pkg/libcontainer/security/restrict"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
||||||
|
"github.com/dotcloud/docker/pkg/netlink"
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
"github.com/dotcloud/docker/pkg/user"
|
"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 {
|
if err := setupNetwork(container, context); err != nil {
|
||||||
return fmt.Errorf("setup networking %s", err)
|
return fmt.Errorf("setup networking %s", err)
|
||||||
}
|
}
|
||||||
|
if err := setupRoute(container); err != nil {
|
||||||
|
return fmt.Errorf("setup route %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
label.Init()
|
label.Init()
|
||||||
|
|
||||||
|
@ -168,6 +172,15 @@ func setupNetwork(container *libcontainer.Container, context libcontainer.Contex
|
||||||
return nil
|
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
|
// FinalizeNamespace drops the caps, sets the correct user
|
||||||
// and working dir, and closes any leaky file descriptors
|
// and working dir, and closes any leaky file descriptors
|
||||||
// before execing the command inside the namespace
|
// before execing the command inside the namespace
|
||||||
|
|
|
@ -131,10 +131,9 @@ type RtMsg struct {
|
||||||
syscall.RtMsg
|
syscall.RtMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRtMsg(family int) *RtMsg {
|
func newRtMsg() *RtMsg {
|
||||||
return &RtMsg{
|
return &RtMsg{
|
||||||
RtMsg: syscall.RtMsg{
|
RtMsg: syscall.RtMsg{
|
||||||
Family: uint8(family),
|
|
||||||
Table: syscall.RT_TABLE_MAIN,
|
Table: syscall.RT_TABLE_MAIN,
|
||||||
Scope: syscall.RT_SCOPE_UNIVERSE,
|
Scope: syscall.RT_SCOPE_UNIVERSE,
|
||||||
Protocol: syscall.RTPROT_BOOT,
|
Protocol: syscall.RTPROT_BOOT,
|
||||||
|
@ -367,40 +366,118 @@ done:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new default gateway. Identical to:
|
// Add a new route table entry.
|
||||||
// ip route add default via $ip
|
func AddRoute(destination, source, gateway, device string) error {
|
||||||
func AddDefaultGw(ip net.IP) error {
|
if destination == "" && source == "" && gateway == "" {
|
||||||
|
return fmt.Errorf("one of destination, source or gateway must not be blank")
|
||||||
|
}
|
||||||
|
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
family := getIpFamily(ip)
|
|
||||||
|
|
||||||
wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||||
|
msg := newRtMsg()
|
||||||
|
currentFamily := -1
|
||||||
|
var rtAttrs []*RtAttr
|
||||||
|
|
||||||
msg := newRtMsg(family)
|
if destination != "" {
|
||||||
wb.AddData(msg)
|
destIP, destNet, err := net.ParseCIDR(destination)
|
||||||
|
if err != nil {
|
||||||
var ipData []byte
|
return fmt.Errorf("destination CIDR %s couldn't be parsed", destination)
|
||||||
if family == syscall.AF_INET {
|
}
|
||||||
ipData = ip.To4()
|
destFamily := getIpFamily(destIP)
|
||||||
} else {
|
currentFamily = destFamily
|
||||||
ipData = ip.To16()
|
destLen, bits := destNet.Mask.Size()
|
||||||
|
if destLen == 0 && bits == 0 {
|
||||||
|
return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination)
|
||||||
|
}
|
||||||
|
msg.Family = uint8(destFamily)
|
||||||
|
msg.Dst_len = uint8(destLen)
|
||||||
|
var destData []byte
|
||||||
|
if destFamily == syscall.AF_INET {
|
||||||
|
destData = destIP.To4()
|
||||||
|
} else {
|
||||||
|
destData = destIP.To16()
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData))
|
||||||
}
|
}
|
||||||
|
|
||||||
gateway := newRtAttr(syscall.RTA_GATEWAY, ipData)
|
if source != "" {
|
||||||
|
srcIP, srcNet, err := net.ParseCIDR(source)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("source CIDR %s couldn't be parsed", source)
|
||||||
|
}
|
||||||
|
srcFamily := getIpFamily(srcIP)
|
||||||
|
if currentFamily != -1 && currentFamily != srcFamily {
|
||||||
|
return fmt.Errorf("source and destination ip were not the same IP family")
|
||||||
|
}
|
||||||
|
currentFamily = srcFamily
|
||||||
|
srcLen, bits := srcNet.Mask.Size()
|
||||||
|
if srcLen == 0 && bits == 0 {
|
||||||
|
return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source)
|
||||||
|
}
|
||||||
|
msg.Family = uint8(srcFamily)
|
||||||
|
msg.Src_len = uint8(srcLen)
|
||||||
|
var srcData []byte
|
||||||
|
if srcFamily == syscall.AF_INET {
|
||||||
|
srcData = srcIP.To4()
|
||||||
|
} else {
|
||||||
|
srcData = srcIP.To16()
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData))
|
||||||
|
}
|
||||||
|
|
||||||
wb.AddData(gateway)
|
if gateway != "" {
|
||||||
|
gwIP := net.ParseIP(gateway)
|
||||||
|
if gwIP == nil {
|
||||||
|
return fmt.Errorf("gateway IP %s couldn't be parsed", gateway)
|
||||||
|
}
|
||||||
|
gwFamily := getIpFamily(gwIP)
|
||||||
|
if currentFamily != -1 && currentFamily != gwFamily {
|
||||||
|
return fmt.Errorf("gateway, source, and destination ip were not the same IP family")
|
||||||
|
}
|
||||||
|
msg.Family = uint8(gwFamily)
|
||||||
|
var gwData []byte
|
||||||
|
if gwFamily == syscall.AF_INET {
|
||||||
|
gwData = gwIP.To4()
|
||||||
|
} else {
|
||||||
|
gwData = gwIP.To16()
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData))
|
||||||
|
}
|
||||||
|
|
||||||
|
wb.AddData(msg)
|
||||||
|
for _, attr := range rtAttrs {
|
||||||
|
wb.AddData(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
native = nativeEndian()
|
||||||
|
b = make([]byte, 4)
|
||||||
|
)
|
||||||
|
iface, err := net.InterfaceByName(device)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
native.PutUint32(b, uint32(iface.Index))
|
||||||
|
|
||||||
|
wb.AddData(newRtAttr(syscall.RTA_OIF, b))
|
||||||
|
|
||||||
if err := s.Send(wb); err != nil {
|
if err := s.Send(wb); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.HandleAck(wb.Seq)
|
return s.HandleAck(wb.Seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a new default gateway. Identical to:
|
||||||
|
// ip route add default via $ip
|
||||||
|
func AddDefaultGw(ip, device string) error {
|
||||||
|
return AddRoute("", "", ip, device)
|
||||||
|
}
|
||||||
|
|
||||||
// Bring up a particular network interface
|
// Bring up a particular network interface
|
||||||
func NetworkLinkUp(iface *net.Interface) error {
|
func NetworkLinkUp(iface *net.Interface) error {
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
|
|
|
@ -27,9 +27,12 @@ func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddDefaultGw(ip net.IP) error {
|
func AddRoute(destination, source, gateway, device string) error {
|
||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDefaultGw(ip, device string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
||||||
|
|
Loading…
Reference in a new issue