Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - IPv6 Container Support #6923

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Andreas Tiefenthaler <at@an-ti.eu>
Andrew Duckworth <grillopress@gmail.com>
Andrew Macgregor <andrew.macgregor@agworld.com.au>
Andrew Munsell <andrew@wizardapps.net>
Andrew Williams <williams.andrew@gmail.com>
Andrews Medina <andrewsmedina@gmail.com>
Andy Chambers <anchambers@paypal.com>
andy diller <dillera@gmail.com>
Expand Down Expand Up @@ -46,6 +47,7 @@ Briehan Lombaard <briehan.lombaard@gmail.com>
Bruno Bigras <bigras.bruno@gmail.com>
Caleb Spare <cespare@gmail.com>
Calen Pennington <cale@edx.org>
Calum Lacroix <c@cypher.cc>
Carl X. Su <bcbcarl@gmail.com>
Charles Hooper <charles.hooper@dotcloud.com>
Charles Lindsay <chaz@chazomatic.us>
Expand Down
4 changes: 4 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ type DaemonConfig struct {
EnableIptables bool
EnableIpForward bool
DefaultIp net.IP
DefaultIp6 net.IP
BridgeIface string
BridgeIP string
BridgeIP6 string
InterContainerCommunication bool
GraphDriver string
Mtu int
Expand All @@ -39,8 +41,10 @@ func DaemonConfigFromJob(job *engine.Job) *DaemonConfig {
EnableIptables: job.GetenvBool("EnableIptables"),
EnableIpForward: job.GetenvBool("EnableIpForward"),
BridgeIP: job.Getenv("BridgeIP"),
BridgeIP6: job.Getenv("BridgeIP6"),
BridgeIface: job.Getenv("BridgeIface"),
DefaultIp: net.ParseIP(job.Getenv("DefaultIp")),
DefaultIp6: net.ParseIP(job.Getenv("DefaultIp6")),
InterContainerCommunication: job.GetenvBool("InterContainerCommunication"),
GraphDriver: job.Getenv("GraphDriver"),
}
Expand Down
50 changes: 35 additions & 15 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,15 @@ type Container struct {
type PortMapping map[string]string // Deprecated

type NetworkSettings struct {
IPAddress string
IPPrefixLen int
Gateway string
Bridge string
PortMapping map[string]PortMapping // Deprecated
Ports nat.PortMap
IPAddress string
IPAddress6 string
IPPrefixLen int
IPPrefixLen6 int
Gateway string
Gateway6 string
Bridge string
PortMapping map[string]PortMapping // Deprecated
Ports nat.PortMap
}

func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
Expand Down Expand Up @@ -416,11 +419,14 @@ func populateCommand(c *Container) {
if !c.Config.NetworkDisabled {
network := c.NetworkSettings
en = &execdriver.Network{
Gateway: network.Gateway,
Bridge: network.Bridge,
IPAddress: network.IPAddress,
IPPrefixLen: network.IPPrefixLen,
Mtu: c.runtime.config.Mtu,
Gateway: network.Gateway,
Gateway6: network.Gateway6,
IPAddress: network.IPAddress,
IPAddress6: network.IPAddress6,
IPPrefixLen: network.IPPrefixLen,
IPPrefixLen6: network.IPPrefixLen6,
Bridge: network.Bridge,
Mtu: c.runtime.config.Mtu,
}
}

Expand Down Expand Up @@ -471,12 +477,12 @@ func (container *Container) Start() (err error) {

if container.runtime.config.DisableNetwork {
container.Config.NetworkDisabled = true
container.buildHostnameAndHostsFiles("127.0.1.1")
container.buildHostnameAndHostsFiles("127.0.1.1", "::2")
} else {
if err := container.allocateNetwork(); err != nil {
return err
}
container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress, container.NetworkSettings.IPAddress6)
}

// Make sure the config is compatible with the current kernel
Expand All @@ -493,6 +499,11 @@ func (container *Container) Start() (err error) {
log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
}

//TODO(ajw) Review this
if container.runtime.sysInfo.IPv6ForwardingDisabled {
log.Printf("WARNING: IPv6 forwarding is disabled. IPv6 networking will not work")
}

if err := prepareVolumesForContainer(container); err != nil {
return err
}
Expand Down Expand Up @@ -536,6 +547,8 @@ func (container *Container) Start() (err error) {
link, err := links.NewLink(
container.NetworkSettings.IPAddress,
child.NetworkSettings.IPAddress,
container.NetworkSettings.IPAddress6,
child.NetworkSettings.IPAddress6,
linkAlias,
child.Config.Env,
child.Config.ExposedPorts,
Expand Down Expand Up @@ -680,7 +693,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
return utils.NewBufReader(reader), nil
}

func (container *Container) buildHostnameAndHostsFiles(IP string) {
func (container *Container) buildHostnameAndHostsFiles(IP, IP6 string) {
container.HostnamePath = path.Join(container.root, "hostname")
ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)

Expand All @@ -697,8 +710,10 @@ ff02::2 ip6-allrouters

if container.Config.Domainname != "" {
hostsContent = append([]byte(fmt.Sprintf("%s\t%s.%s %s\n", IP, container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
hostsContent = append([]byte(fmt.Sprintf("%s\t%s.%s %s\n", IP6, container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
} else if !container.Config.NetworkDisabled {
hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...)
hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP6, container.Config.Hostname)), hostsContent...)
}

ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
Expand All @@ -719,11 +734,13 @@ func (container *Container) allocateNetwork() error {
if container.runtime.config.DisableNetwork {
env = &engine.Env{}
} else {
currentIP := container.NetworkSettings.IPAddress
currentIP := container.NetworkSettings.IPAddress
currentIP6 := container.NetworkSettings.IPAddress6

job := eng.Job("allocate_interface", container.ID)
if currentIP != "" {
job.Setenv("RequestIP", currentIP)
job.Setenv("RequestIP6", currentIP6)
}

env, err = job.Stdout.AddEnv()
Expand Down Expand Up @@ -816,8 +833,11 @@ func (container *Container) allocateNetwork() error {

container.NetworkSettings.Bridge = env.Get("Bridge")
container.NetworkSettings.IPAddress = env.Get("IP")
container.NetworkSettings.IPAddress6 = env.Get("IP6")
container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen")
container.NetworkSettings.IPPrefixLen6 = env.GetInt("IPPrefixLen6")
container.NetworkSettings.Gateway = env.Get("Gateway")
container.NetworkSettings.Gateway6 = env.Get("Gateway6")

return nil
}
Expand Down
127 changes: 127 additions & 0 deletions container_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,133 @@ func TestParseNetworkOptsUdp(t *testing.T) {
}
}


func TestParseNetworkOptsPrivateOnly6(t *testing.T) {
ports, bindings, err := nat.ParsePortSpecs([]string{"[2001:db8::100]::80"})
if err != nil {
t.Fatal(err)
}
if len(ports) != 1 {
t.Logf("Expected 1 got %d", len(ports))
t.FailNow()
}
if len(bindings) != 1 {
t.Logf("Expected 1 got %d", len(bindings))
t.FailNow()
}
for k := range ports {
if k.Proto() != "tcp" {
t.Logf("Expected tcp got %s", k.Proto())
t.Fail()
}
if k.Port() != "80" {
t.Logf("Expected 80 got %s", k.Port())
t.Fail()
}
b, exists := bindings[k]
if !exists {
t.Log("Binding does not exist")
t.FailNow()
}
if len(b) != 1 {
t.Logf("Expected 1 got %d", len(b))
t.FailNow()
}
s := b[0]
if s.HostPort != "" {
t.Logf("Expected \"\" got %s", s.HostPort)
t.Fail()
}
if s.HostIp != "2001:db8::100" {
t.Fail()
}
}
}

func TestParseNetworkOptsPublic6(t *testing.T) {
ports, bindings, err := nat.ParsePortSpecs([]string{"[2001:db8::100]:8080:80"})
if err != nil {
t.Fatal(err)
}
if len(ports) != 1 {
t.Logf("Expected 1 got %d", len(ports))
t.FailNow()
}
if len(bindings) != 1 {
t.Logf("Expected 1 got %d", len(bindings))
t.FailNow()
}
for k := range ports {
if k.Proto() != "tcp" {
t.Logf("Expected tcp got %s", k.Proto())
t.Fail()
}
if k.Port() != "80" {
t.Logf("Expected 80 got %s", k.Port())
t.Fail()
}
b, exists := bindings[k]
if !exists {
t.Log("Binding does not exist")
t.FailNow()
}
if len(b) != 1 {
t.Logf("Expected 1 got %d", len(b))
t.FailNow()
}
s := b[0]
if s.HostPort != "8080" {
t.Logf("Expected 8080 got %s", s.HostPort)
t.Fail()
}
if s.HostIp != "2001:db8::100" {
t.Fail()
}
}
}

func TestParseNetworkOptsUdp6(t *testing.T) {
ports, bindings, err := nat.ParsePortSpecs([]string{"[2001:db8::100]::6000/udp"})
if err != nil {
t.Fatal(err)
}
if len(ports) != 1 {
t.Logf("Expected 1 got %d", len(ports))
t.FailNow()
}
if len(bindings) != 1 {
t.Logf("Expected 1 got %d", len(bindings))
t.FailNow()
}
for k := range ports {
if k.Proto() != "udp" {
t.Logf("Expected udp got %s", k.Proto())
t.Fail()
}
if k.Port() != "6000" {
t.Logf("Expected 6000 got %s", k.Port())
t.Fail()
}
b, exists := bindings[k]
if !exists {
t.Log("Binding does not exist")
t.FailNow()
}
if len(b) != 1 {
t.Logf("Expected 1 got %d", len(b))
t.FailNow()
}
s := b[0]
if s.HostPort != "" {
t.Logf("Expected \"\" got %s", s.HostPort)
t.Fail()
}
if s.HostIp != "2001:db8::100" {
t.Fail()
}
}
}

func TestGetFullName(t *testing.T) {
name, err := getFullName("testing")
if err != nil {
Expand Down
11 changes: 9 additions & 2 deletions docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ func main() {
flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
flAutoRestart = flag.Bool([]string{"r", "-restart"}, true, "Restart previously running containers")
bridgeName = flag.String([]string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge; use 'none' to disable container networking")
bridgeIp = flag.String([]string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
bridgeIp = flag.String([]string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IPv4 address, not compatible with -b")
bridgeIp6 = flag.String([]string{"#bip6", "-bip6"}, "", "Use this CIDR notation address for the network bridge's IPv6 address, not compatible with -b")
pidfile = flag.String([]string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file")
flRoot = flag.String([]string{"g", "-graph"}, "/var/lib/docker", "Path to use as the root of the docker runtime")
flEnableCors = flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"}, false, "Enable CORS headers in the remote API")
flDns = opts.NewListOpts(opts.ValidateIp4Address)
flEnableIptables = flag.Bool([]string{"#iptables", "-iptables"}, true, "Disable docker's addition of iptables rules")
flEnableIpForward = flag.Bool([]string{"#ip-forward", "-ip-forward"}, true, "Disable enabling of net.ipv4.ip_forward")
flDefaultIp = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")
flDefaultIp = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container IPv4 ports")
flDefaultIp6 = flag.String([]string{"#ip6", "-ip6"}, "::", "Default IP address to use when binding container IPv6 ports")
flInterContainerComm = flag.Bool([]string{"#icc", "-icc"}, true, "Enable inter-container communication")
flGraphDriver = flag.String([]string{"s", "-storage-driver"}, "", "Force the docker runtime to use a specific storage driver")
flHosts = opts.NewListOpts(api.ValidateHost)
Expand Down Expand Up @@ -67,6 +69,9 @@ func main() {
if *bridgeName != "" && *bridgeIp != "" {
log.Fatal("You specified -b & --bip, mutually exclusive options. Please specify only one.")
}
if *bridgeName != "" && *bridgeIp6 != "" {
log.Fatal("You specified -b & --bip6, mutually exclusive options. Please specify only one.")
}

if *flDebug {
os.Setenv("DEBUG", "1")
Expand Down Expand Up @@ -95,7 +100,9 @@ func main() {
job.SetenvBool("EnableIpForward", *flEnableIpForward)
job.Setenv("BridgeIface", *bridgeName)
job.Setenv("BridgeIP", *bridgeIp)
job.Setenv("BridgeIP6", *bridgeIp6)
job.Setenv("DefaultIp", *flDefaultIp)
job.Setenv("DefaultIp6", *flDefaultIp6)
job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
job.Setenv("GraphDriver", *flGraphDriver)
job.SetenvInt("Mtu", *flMtu)
Expand Down
15 changes: 10 additions & 5 deletions execdriver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ func GetInitFunc(name string) (InitFunc, error) {
type InitArgs struct {
User string
Gateway string
Gateway6 string
Ip string
Ip6 string
WorkDir string
Privileged bool
Env []string
Expand All @@ -68,11 +70,14 @@ type Driver interface {

// Network settings of the container
type Network struct {
Gateway string `json:"gateway"`
IPAddress string `json:"ip"`
Bridge string `json:"bridge"`
IPPrefixLen int `json:"ip_prefix_len"`
Mtu int `json:"mtu"`
Gateway string `json:"gateway"`
Gateway6 string `json:"gateway6"`
IPAddress string `json:"ip"`
IPAddress6 string `json:"ip6"`
IPPrefixLen int `json:"ip_prefix_len"`
IPPrefixLen6 int `json:"ip_prefix_len6"`
Bridge string `json:"bridge"`
Mtu int `json:"mtu"`
}

type Resources struct {
Expand Down
2 changes: 2 additions & 0 deletions execdriver/lxc/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ func (d *driver) Run(c *execdriver.Command, startCallback execdriver.StartCallba
if c.Network != nil {
params = append(params,
"-g", c.Network.Gateway,
"-g6", c.Network.Gateway6,
"-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
"-i6", fmt.Sprintf("%s/%d", c.Network.IPAddress6, c.Network.IPPrefixLen6),
"-mtu", strconv.Itoa(c.Network.Mtu),
)
}
Expand Down