mirror of
https://github.com/clearlinux/libnetwork.git
synced 2026-06-16 10:35:54 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b116b5c0d2 | |||
| 78fef19fc6 | |||
| 6c07f504c3 | |||
| c32ef60c4b | |||
| e578e95aa1 | |||
| 9548cbe674 | |||
| 90638ec9cf | |||
| 22f8e6bbab | |||
| 8b3374153e | |||
| 9c6e929b63 | |||
| ace6b57623 | |||
| 68d42b860c | |||
| c624d72ce9 | |||
| 6532b64a07 | |||
| f72ad20491 | |||
| 2133cdc219 | |||
| 005bc475ee | |||
| d1d67dca84 | |||
| 4ded6fe364 | |||
| effb423db7 | |||
| a5ac79f562 | |||
| 694754890c | |||
| 7c2a2c8a87 | |||
| 1ab46c7c4b | |||
| 2da2dc055d | |||
| 83799f7458 | |||
| 60da50e183 | |||
| b028371e0a | |||
| fc2dfe5201 | |||
| 051f4ccdad | |||
| a4d595b6e7 | |||
| 05462c27b2 |
Generated
+1
-1
@@ -79,7 +79,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vishvananda/netns",
|
||||
"Rev": "008d17ae001344769b031375bdb38a86219154c6"
|
||||
"Rev": "5478c060110032f972e86a1f844fdb9a2f008f2c"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// NsHandle is a handle to a network namespace. It can be cast directly
|
||||
// to an int and used as a file descriptor.
|
||||
type NsHandle int
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux
|
||||
|
||||
package netns
|
||||
|
||||
import (
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux,386
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
|
||||
Generated
Vendored
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux,amd64
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux,arm
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// +build linux,ppc64le
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 350
|
||||
)
|
||||
+6
-6
@@ -10,26 +10,26 @@ var (
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
)
|
||||
|
||||
func Set(ns Namespace) (err error) {
|
||||
func Set(ns NsHandle) (err error) {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func New() (ns Namespace, err error) {
|
||||
func New() (ns NsHandle, err error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func Get() (Namespace, error) {
|
||||
func Get() (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func GetFromName(name string) (Namespace, error) {
|
||||
func GetFromName(name string) (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func GetFromPid(pid int) (Namespace, error) {
|
||||
func GetFromPid(pid int) (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
func GetFromDocker(id string) (Namespace, error) {
|
||||
func GetFromDocker(id string) (NsHandle, error) {
|
||||
return -1, ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@ type NetworkController interface {
|
||||
|
||||
// NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned.
|
||||
NetworkByID(id string) (Network, error)
|
||||
|
||||
// GC triggers immediate garbage collection of resources which are garbage collected.
|
||||
GC()
|
||||
}
|
||||
|
||||
// NetworkWalker is a client provided function which will be used to walk the Networks.
|
||||
@@ -299,3 +302,7 @@ func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (c *controller) GC() {
|
||||
sandbox.GC()
|
||||
}
|
||||
|
||||
+71
-10
@@ -3,11 +3,14 @@ package bridge
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipallocator"
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/options"
|
||||
@@ -102,6 +105,15 @@ func newDriver() driverapi.Driver {
|
||||
|
||||
// Init registers a new instance of bridge driver
|
||||
func Init(dc driverapi.DriverCallback) error {
|
||||
// try to modprobe bridge first
|
||||
// see gh#12177
|
||||
if out, err := exec.Command("modprobe", "-va", "bridge", "nf_nat", "br_netfilter").Output(); err != nil {
|
||||
logrus.Warnf("Running modprobe bridge nf_nat failed with message: %s, error: %v", out, err)
|
||||
}
|
||||
if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
|
||||
logrus.Warnf("Failed to remove existing iptables entries in %s : %v", DockerChain, err)
|
||||
}
|
||||
|
||||
return dc.RegisterDriver(networkType, newDriver())
|
||||
}
|
||||
|
||||
@@ -287,6 +299,11 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
|
||||
// Even if a bridge exists try to setup IPv4.
|
||||
bridgeSetup.queueStep(setupBridgeIPv4)
|
||||
|
||||
enableIPv6Forwarding := false
|
||||
if d.config != nil && d.config.EnableIPForwarding && config.FixedCIDRv6 != nil {
|
||||
enableIPv6Forwarding = true
|
||||
}
|
||||
|
||||
// Conditionally queue setup steps depending on configuration values.
|
||||
for _, step := range []struct {
|
||||
Condition bool
|
||||
@@ -310,6 +327,9 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
|
||||
// specified subnet.
|
||||
{config.FixedCIDRv6 != nil, setupFixedCIDRv6},
|
||||
|
||||
// Enable IPv6 Forwarding
|
||||
{enableIPv6Forwarding, setupIPv6Forwarding},
|
||||
|
||||
// Setup Loopback Adresses Routing
|
||||
{!config.EnableUserlandProxy, setupLoopbackAdressesRouting},
|
||||
|
||||
@@ -482,14 +502,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
}
|
||||
}()
|
||||
|
||||
// Set the sbox's MAC. If specified, use the one configured by user, otherwise use a random one
|
||||
mac := electMacAddress(epConfig)
|
||||
err = netlink.LinkSetHardwareAddr(sbox, mac)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint.macAddress = mac
|
||||
|
||||
// Add bridge inherited attributes to pipe interfaces
|
||||
if config.Mtu != 0 {
|
||||
err = netlink.LinkSetMTU(host, config.Mtu)
|
||||
@@ -508,6 +520,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
return err
|
||||
}
|
||||
|
||||
if !config.EnableUserlandProxy {
|
||||
err = netlink.LinkSetHairpin(host, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// v4 address for the sandbox side pipe interface
|
||||
ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
|
||||
if err != nil {
|
||||
@@ -515,6 +534,14 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
}
|
||||
ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
|
||||
|
||||
// Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP.
|
||||
mac := electMacAddress(epConfig, ip4)
|
||||
err = netlink.LinkSetHardwareAddr(sbox, mac)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint.macAddress = mac
|
||||
|
||||
// v6 address for the sandbox side pipe interface
|
||||
ipv6Addr = &net.IPNet{}
|
||||
if config.EnableIPv6 {
|
||||
@@ -670,6 +697,15 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
|
||||
|
||||
m := make(map[string]interface{})
|
||||
|
||||
if ep.config.ExposedPorts != nil {
|
||||
// Return a copy of the config data
|
||||
epc := make([]types.TransportPort, 0, len(ep.config.ExposedPorts))
|
||||
for _, tp := range ep.config.ExposedPorts {
|
||||
epc = append(epc, tp.GetCopy())
|
||||
}
|
||||
m[netlabel.ExposedPorts] = epc
|
||||
}
|
||||
|
||||
if ep.portMapping != nil {
|
||||
// Return a copy of the operational data
|
||||
pmc := make([]types.PortBinding, 0, len(ep.portMapping))
|
||||
@@ -901,11 +937,36 @@ func parseContainerOptions(cOptions map[string]interface{}) (*ContainerConfigura
|
||||
}
|
||||
}
|
||||
|
||||
func electMacAddress(epConfig *EndpointConfiguration) net.HardwareAddr {
|
||||
// Generate a IEEE802 compliant MAC address from the given IP address.
|
||||
//
|
||||
// The generator is guaranteed to be consistent: the same IP will always yield the same
|
||||
// MAC address. This is to avoid ARP cache issues.
|
||||
func generateMacAddr(ip net.IP) net.HardwareAddr {
|
||||
hw := make(net.HardwareAddr, 6)
|
||||
|
||||
// The first byte of the MAC address has to comply with these rules:
|
||||
// 1. Unicast: Set the least-significant bit to 0.
|
||||
// 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1.
|
||||
// 3. As "small" as possible: The veth address has to be "smaller" than the bridge address.
|
||||
hw[0] = 0x02
|
||||
|
||||
// The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI).
|
||||
// Since this address is locally administered, we can do whatever we want as long as
|
||||
// it doesn't conflict with other addresses.
|
||||
hw[1] = 0x42
|
||||
|
||||
// Insert the IP address into the last 32 bits of the MAC address.
|
||||
// This is a simple way to guarantee the address will be consistent and unique.
|
||||
copy(hw[2:], ip.To4())
|
||||
|
||||
return hw
|
||||
}
|
||||
|
||||
func electMacAddress(epConfig *EndpointConfiguration, ip net.IP) net.HardwareAddr {
|
||||
if epConfig != nil && epConfig.MacAddress != nil {
|
||||
return epConfig.MacAddress
|
||||
}
|
||||
return netutils.GenerateRandomMAC()
|
||||
return generateMacAddr(ip)
|
||||
}
|
||||
|
||||
// Generates a name to be used for a virtual ethernet
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
@@ -10,5 +13,15 @@ func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err}
|
||||
}
|
||||
|
||||
// Setting route to global IPv6 subnet
|
||||
log.Debugf("Adding route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
|
||||
err := netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: i.Link.Attrs().Index,
|
||||
Dst: config.FixedCIDRv6,
|
||||
})
|
||||
if err != nil && !os.IsExist(err) {
|
||||
log.Errorf("Could not add route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
var bridgeIPv6 *net.IPNet
|
||||
|
||||
const bridgeIPv6Str = "fe80::1/64"
|
||||
const (
|
||||
bridgeIPv6Str = "fe80::1/64"
|
||||
ipv6ForwardConfPerm = 0644
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We allow ourselves to panic in this special case because we indicate a
|
||||
@@ -25,7 +29,7 @@ func init() {
|
||||
func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
// Enable IPv6 on the bridge
|
||||
procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6"
|
||||
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
|
||||
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil {
|
||||
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
||||
}
|
||||
|
||||
@@ -64,3 +68,14 @@ func setupGatewayIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupIPv6Forwarding(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
// Enable IPv6 forwarding
|
||||
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
||||
logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
||||
logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+23
-15
@@ -44,9 +44,10 @@ var (
|
||||
|
||||
// Chain defines the iptables chain.
|
||||
type Chain struct {
|
||||
Name string
|
||||
Bridge string
|
||||
Table Table
|
||||
Name string
|
||||
Bridge string
|
||||
Table Table
|
||||
HairpinMode bool
|
||||
}
|
||||
|
||||
// ChainError is returned to represent errors during ip table operation.
|
||||
@@ -75,9 +76,10 @@ func initCheck() error {
|
||||
// NewChain adds a new chain to ip table.
|
||||
func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error) {
|
||||
c := &Chain{
|
||||
Name: name,
|
||||
Bridge: bridge,
|
||||
Table: table,
|
||||
Name: name,
|
||||
Bridge: bridge,
|
||||
Table: table,
|
||||
HairpinMode: hairpinMode,
|
||||
}
|
||||
|
||||
if string(c.Table) == "" {
|
||||
@@ -97,7 +99,8 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
|
||||
case Nat:
|
||||
preroute := []string{
|
||||
"-m", "addrtype",
|
||||
"--dst-type", "LOCAL"}
|
||||
"--dst-type", "LOCAL",
|
||||
"-j", c.Name}
|
||||
if !Exists(Nat, "PREROUTING", preroute...) {
|
||||
if err := c.Prerouting(Append, preroute...); err != nil {
|
||||
return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
|
||||
@@ -105,7 +108,8 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
|
||||
}
|
||||
output := []string{
|
||||
"-m", "addrtype",
|
||||
"--dst-type", "LOCAL"}
|
||||
"--dst-type", "LOCAL",
|
||||
"-j", c.Name}
|
||||
if !hairpinMode {
|
||||
output = append(output, "!", "--dst", "127.0.0.0/8")
|
||||
}
|
||||
@@ -151,12 +155,16 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri
|
||||
// value" by both iptables and ip6tables.
|
||||
daddr = "0/0"
|
||||
}
|
||||
if output, err := Raw("-t", string(Nat), string(action), c.Name,
|
||||
args := []string{"-t", string(Nat), string(action), c.Name,
|
||||
"-p", proto,
|
||||
"-d", daddr,
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "DNAT",
|
||||
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
|
||||
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))}
|
||||
if !c.HairpinMode {
|
||||
args = append(args, "!", "-i", c.Bridge)
|
||||
}
|
||||
if output, err := Raw(args...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "FORWARD", Output: output}
|
||||
@@ -222,7 +230,7 @@ func (c *Chain) Prerouting(action Action, args ...string) error {
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
|
||||
if output, err := Raw(a...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "PREROUTING", Output: output}
|
||||
@@ -236,7 +244,7 @@ func (c *Chain) Output(action Action, args ...string) error {
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
|
||||
if output, err := Raw(a...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "OUTPUT", Output: output}
|
||||
@@ -248,9 +256,9 @@ func (c *Chain) Output(action Action, args ...string) error {
|
||||
func (c *Chain) Remove() error {
|
||||
// Ignore errors - This could mean the chains were never set up
|
||||
if c.Table == Nat {
|
||||
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
|
||||
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name)
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", c.Name)
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) // Created in versions <= 0.1.6
|
||||
|
||||
c.Prerouting(Delete)
|
||||
c.Output(Delete)
|
||||
|
||||
@@ -48,6 +48,7 @@ func TestForward(t *testing.T) {
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "DNAT",
|
||||
"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
|
||||
"!", "-i", natChain.Bridge,
|
||||
}
|
||||
|
||||
if !Exists(natChain.Table, natChain.Name, dnatRule...) {
|
||||
@@ -130,16 +131,11 @@ func TestPrerouting(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule := []string{
|
||||
"-j", natChain.Name}
|
||||
|
||||
rule = append(rule, args...)
|
||||
|
||||
if !Exists(natChain.Table, "PREROUTING", rule...) {
|
||||
if !Exists(natChain.Table, "PREROUTING", args...) {
|
||||
t.Fatalf("rule does not exist")
|
||||
}
|
||||
|
||||
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...)
|
||||
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, args...)
|
||||
if _, err = Raw(delRule...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -155,17 +151,12 @@ func TestOutput(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule := []string{
|
||||
"-j", natChain.Name}
|
||||
|
||||
rule = append(rule, args...)
|
||||
|
||||
if !Exists(natChain.Table, "OUTPUT", rule...) {
|
||||
if !Exists(natChain.Table, "OUTPUT", args...) {
|
||||
t.Fatalf("rule does not exist")
|
||||
}
|
||||
|
||||
delRule := append([]string{"-D", "OUTPUT", "-t",
|
||||
string(natChain.Table)}, rule...)
|
||||
string(natChain.Table)}, args...)
|
||||
if _, err = Raw(delRule...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -60,14 +60,23 @@ type network struct {
|
||||
}
|
||||
|
||||
func (n *network) Name() string {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return n.name
|
||||
}
|
||||
|
||||
func (n *network) ID() string {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return string(n.id)
|
||||
}
|
||||
|
||||
func (n *network) Type() string {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if n.driver == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -94,7 +92,6 @@ func Get() *PortAllocator {
|
||||
func newInstance() *PortAllocator {
|
||||
start, end, err := getDynamicPortRange()
|
||||
if err != nil {
|
||||
logrus.Warn(err)
|
||||
start, end = DefaultPortRangeStart, DefaultPortRangeEnd
|
||||
}
|
||||
return &PortAllocator{
|
||||
|
||||
+129
-23
@@ -4,17 +4,28 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
const prefix = "/var/run/docker/netns"
|
||||
|
||||
var once sync.Once
|
||||
var (
|
||||
once sync.Once
|
||||
garbagePathMap = make(map[string]bool)
|
||||
gpmLock sync.Mutex
|
||||
gpmWg sync.WaitGroup
|
||||
gpmCleanupPeriod = 60 * time.Second
|
||||
gpmChan = make(chan chan struct{})
|
||||
)
|
||||
|
||||
// The networkNamespace type is the linux implementation of the Sandbox
|
||||
// interface. It represents a linux network namespace, and moves an interface
|
||||
@@ -26,11 +37,87 @@ type networkNamespace struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
reexec.Register("netns-create", reexecCreateNamespace)
|
||||
}
|
||||
|
||||
func createBasePath() {
|
||||
err := os.MkdirAll(prefix, 0644)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
panic("Could not create net namespace path directory")
|
||||
}
|
||||
|
||||
// Start the garbage collection go routine
|
||||
go removeUnusedPaths()
|
||||
}
|
||||
|
||||
func removeUnusedPaths() {
|
||||
gpmLock.Lock()
|
||||
period := gpmCleanupPeriod
|
||||
gpmLock.Unlock()
|
||||
|
||||
ticker := time.NewTicker(period)
|
||||
for {
|
||||
var (
|
||||
gc chan struct{}
|
||||
gcOk bool
|
||||
)
|
||||
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case gc, gcOk = <-gpmChan:
|
||||
}
|
||||
|
||||
gpmLock.Lock()
|
||||
pathList := make([]string, 0, len(garbagePathMap))
|
||||
for path := range garbagePathMap {
|
||||
pathList = append(pathList, path)
|
||||
}
|
||||
garbagePathMap = make(map[string]bool)
|
||||
gpmWg.Add(1)
|
||||
gpmLock.Unlock()
|
||||
|
||||
for _, path := range pathList {
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
gpmWg.Done()
|
||||
if gcOk {
|
||||
close(gc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addToGarbagePaths(path string) {
|
||||
gpmLock.Lock()
|
||||
garbagePathMap[path] = true
|
||||
gpmLock.Unlock()
|
||||
}
|
||||
|
||||
func removeFromGarbagePaths(path string) {
|
||||
gpmLock.Lock()
|
||||
delete(garbagePathMap, path)
|
||||
gpmLock.Unlock()
|
||||
}
|
||||
|
||||
// GC triggers garbage collection of namespace path right away
|
||||
// and waits for it.
|
||||
func GC() {
|
||||
gpmLock.Lock()
|
||||
if len(garbagePathMap) == 0 {
|
||||
// No need for GC if map is empty
|
||||
gpmLock.Unlock()
|
||||
return
|
||||
}
|
||||
gpmLock.Unlock()
|
||||
|
||||
// if content exists in the garbage paths
|
||||
// we can trigger GC to run, providing a
|
||||
// channel to be notified on completion
|
||||
waitGC := make(chan struct{})
|
||||
gpmChan <- waitGC
|
||||
// wait for GC completion
|
||||
<-waitGC
|
||||
}
|
||||
|
||||
// GenerateKey generates a sandbox key based on the passed
|
||||
@@ -55,6 +142,20 @@ func NewSandbox(key string, osCreate bool) (Sandbox, error) {
|
||||
return &networkNamespace{path: key, sinfo: info}, nil
|
||||
}
|
||||
|
||||
func reexecCreateNamespace() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal("no namespace path provided")
|
||||
}
|
||||
|
||||
if err := syscall.Mount("/proc/self/ns/net", os.Args[1], "bind", syscall.MS_BIND, ""); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := loopbackUp(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
@@ -69,23 +170,18 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if osCreate {
|
||||
defer netns.Set(origns)
|
||||
newns, err := netns.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer newns.Close()
|
||||
|
||||
if err := loopbackUp(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := &exec.Cmd{
|
||||
Path: reexec.Self(),
|
||||
Args: append([]string{"netns-create"}, path),
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
|
||||
procNet := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid())
|
||||
|
||||
if err := syscall.Mount(procNet, path, "bind", syscall.MS_BIND, ""); err != nil {
|
||||
return nil, err
|
||||
if osCreate {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("namespace creation reexec command failed: %v", err)
|
||||
}
|
||||
|
||||
interfaces := []*Interface{}
|
||||
@@ -93,10 +189,9 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func cleanupNamespaceFile(path string) {
|
||||
func unmountNamespaceFile(path string) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
n := &networkNamespace{path: path}
|
||||
n.Destroy()
|
||||
syscall.Unmount(path, syscall.MNT_DETACH)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +199,20 @@ func createNamespaceFile(path string) (err error) {
|
||||
var f *os.File
|
||||
|
||||
once.Do(createBasePath)
|
||||
// cleanup namespace file if it already exists because of a previous ungraceful exit.
|
||||
cleanupNamespaceFile(path)
|
||||
// Remove it from garbage collection list if present
|
||||
removeFromGarbagePaths(path)
|
||||
|
||||
// If the path is there unmount it first
|
||||
unmountNamespaceFile(path)
|
||||
|
||||
// wait for garbage collection to complete if it is in progress
|
||||
// before trying to create the file.
|
||||
gpmWg.Wait()
|
||||
|
||||
if f, err = os.Create(path); err == nil {
|
||||
f.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -269,5 +373,7 @@ func (n *networkNamespace) Destroy() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(n.path)
|
||||
// Stash it into the garbage collection list
|
||||
addToGarbagePaths(n.path)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/vishvananda/netlink"
|
||||
@@ -31,6 +32,11 @@ func newKey(t *testing.T) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Set the rpmCleanupPeriod to be low to make the test run quicker
|
||||
gpmLock.Lock()
|
||||
gpmCleanupPeriod = 2 * time.Second
|
||||
gpmLock.Unlock()
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
@@ -137,3 +143,17 @@ func verifySandbox(t *testing.T, s Sandbox) {
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyCleanup(t *testing.T, s Sandbox, wait bool) {
|
||||
if wait {
|
||||
time.Sleep(time.Duration(gpmCleanupPeriod * 2))
|
||||
}
|
||||
|
||||
if _, err := os.Stat(s.Key()); err == nil {
|
||||
if wait {
|
||||
t.Fatalf("The sandbox path %s is not getting cleaned up even after twice the cleanup period", s.Key())
|
||||
} else {
|
||||
t.Fatalf("The sandbox path %s is not cleaned up after running gc", s.Key())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,19 @@ package sandbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestSandboxCreate(t *testing.T) {
|
||||
key, err := newKey(t)
|
||||
if err != nil {
|
||||
@@ -44,6 +54,7 @@ func TestSandboxCreate(t *testing.T) {
|
||||
|
||||
verifySandbox(t, s)
|
||||
s.Destroy()
|
||||
verifyCleanup(t, s, true)
|
||||
}
|
||||
|
||||
func TestSandboxCreateTwice(t *testing.T) {
|
||||
@@ -66,6 +77,23 @@ func TestSandboxCreateTwice(t *testing.T) {
|
||||
s.Destroy()
|
||||
}
|
||||
|
||||
func TestSandboxGC(t *testing.T) {
|
||||
key, err := newKey(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to obtain a key: %v", err)
|
||||
}
|
||||
|
||||
s, err := NewSandbox(key, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a new sandbox: %v", err)
|
||||
}
|
||||
|
||||
s.Destroy()
|
||||
|
||||
GC()
|
||||
verifyCleanup(t, s, false)
|
||||
}
|
||||
|
||||
func TestInterfaceEqual(t *testing.T) {
|
||||
list := getInterfaceList()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user