1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #18775 from mrjana/mh

libnetwork vendoring
This commit is contained in:
Sebastiaan van Stijn 2015-12-26 11:42:55 +01:00
commit d4270c1764
23 changed files with 486 additions and 81 deletions

View file

@ -16,7 +16,6 @@ network. Docker Engine supports multi-host networking out-of-the-box through the
`overlay` network driver. Unlike `bridge` networks, overlay networks require `overlay` network driver. Unlike `bridge` networks, overlay networks require
some pre-existing conditions before you can create one. These conditions are: some pre-existing conditions before you can create one. These conditions are:
* A host with a 3.16 kernel version or higher.
* Access to a key-value store. Docker supports Consul, Etcd, and ZooKeeper (Distributed store) key-value stores. * Access to a key-value store. Docker supports Consul, Etcd, and ZooKeeper (Distributed store) key-value stores.
* A cluster of hosts with connectivity to the key-value store. * A cluster of hosts with connectivity to the key-value store.
* A properly configured Engine `daemon` on each host in the cluster. * A properly configured Engine `daemon` on each host in the cluster.

View file

@ -24,7 +24,7 @@ clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
clone git github.com/docker/go-connections 4e42727957c146776e5de9cec8c39e4059ed9f20 clone git github.com/docker/go-connections 4e42727957c146776e5de9cec8c39e4059ed9f20
#get libnetwork packages #get libnetwork packages
clone git github.com/docker/libnetwork bbd6e6d8ca1e7c9b42f6f53277b0bde72847ff90 clone git github.com/docker/libnetwork 9f0563ea8f430d8828553aac97161cbff4056436
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4

View file

@ -121,7 +121,8 @@ type driverData struct {
} }
type ipamData struct { type ipamData struct {
driver ipamapi.Ipam driver ipamapi.Ipam
capability *ipamapi.Capability
// default address spaces are provided by ipam driver at registration time // default address spaces are provided by ipam driver at registration time
defaultLocalAddressSpace, defaultGlobalAddressSpace string defaultLocalAddressSpace, defaultGlobalAddressSpace string
} }
@ -306,7 +307,7 @@ func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver,
return nil return nil
} }
func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error { func (c *controller) registerIpamDriver(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
if !config.IsValidName(name) { if !config.IsValidName(name) {
return ErrInvalidName(name) return ErrInvalidName(name)
} }
@ -322,7 +323,7 @@ func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error
return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err) return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err)
} }
c.Lock() c.Lock()
c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS} c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS, capability: caps}
c.Unlock() c.Unlock()
log.Debugf("Registering ipam driver: %q", name) log.Debugf("Registering ipam driver: %q", name)
@ -330,6 +331,14 @@ func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error
return nil return nil
} }
func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error {
return c.registerIpamDriver(name, driver, &ipamapi.Capability{})
}
func (c *controller) RegisterIpamDriverWithCapabilities(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
return c.registerIpamDriver(name, driver, caps)
}
// NewNetwork creates a new network of the specified network type. The options // NewNetwork creates a new network of the specified network type. The options
// are network specific and modeled in a generic way. // are network specific and modeled in a generic way.
func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) { func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) {

View file

@ -103,6 +103,9 @@ func (sb *sandbox) needDefaultGW() bool {
if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" { if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" {
continue continue
} }
if ep.getNetwork().Internal() {
return false
}
if ep.joinInfo.disableGatewayService { if ep.joinInfo.disableGatewayService {
return false return false
} }

View file

@ -490,6 +490,12 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati
config.EnableIPv6 = val.(bool) config.EnableIPv6 = val.(bool)
} }
if val, ok := option[netlabel.Internal]; ok {
if internal, ok := val.(bool); ok && internal {
return nil, &driverapi.ErrNotImplemented{}
}
}
// Finally validate the configuration // Finally validate the configuration
if err = config.Validate(); err != nil { if err = config.Validate(); err != nil {
return nil, err return nil, err

View file

@ -157,6 +157,6 @@ func isPacketForwardingEnabled(ipVer ipVersion, iface string) (bool, error) {
} }
func isRunningInContainer() bool { func isRunningInContainer() bool {
_, err := os.Stat("/.dockerinit") _, err := os.Stat("/.dockerenv")
return !os.IsNotExist(err) return !os.IsNotExist(err)
} }

View file

@ -62,6 +62,13 @@ func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error {
return nil return nil
} }
// Store and program user specified bridge network and network gateway
i.bridgeIPv6 = config.AddressIPv6
i.gatewayIPv6 = config.AddressIPv6.IP
if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: i.bridgeIPv6}); err != nil {
return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err}
}
// Setting route to global IPv6 subnet // Setting route to global IPv6 subnet
logrus.Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) logrus.Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName)
err = netlink.RouteAdd(&netlink.Route{ err = netlink.RouteAdd(&netlink.Route{

View file

@ -0,0 +1,131 @@
package overlay
import (
"fmt"
"sync"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/iptables"
)
const globalChain = "DOCKER-OVERLAY"
var filterOnce sync.Once
func rawIPTables(args ...string) error {
if output, err := iptables.Raw(args...); err != nil {
return fmt.Errorf("unable to add overlay filter: %v", err)
} else if len(output) != 0 {
return fmt.Errorf("unable to add overlay filter: %s", string(output))
}
return nil
}
func chainExists(cname string) bool {
if _, err := iptables.Raw("-L", cname); err != nil {
return false
}
return true
}
func setupGlobalChain() {
if err := rawIPTables("-N", globalChain); err != nil {
logrus.Debugf("could not create global overlay chain: %v", err)
}
if err := rawIPTables("-A", globalChain, "-j", "RETURN"); err != nil {
logrus.Debugf("could not install default return chain in the overlay global chain: %v", err)
}
}
func setNetworkChain(cname string, remove bool) error {
// Initialize the onetime global overlay chain
filterOnce.Do(setupGlobalChain)
exists := chainExists(cname)
opt := "-N"
// In case of remove, make sure to flush the rules in the chain
if remove && exists {
if err := rawIPTables("-F", cname); err != nil {
return fmt.Errorf("failed to flush overlay network chain %s rules: %v", cname, err)
}
opt = "-X"
}
if (!remove && !exists) || (remove && exists) {
if err := rawIPTables(opt, cname); err != nil {
return fmt.Errorf("failed network chain operation %q for chain %s: %v", opt, cname, err)
}
}
if !remove {
if !iptables.Exists(iptables.Filter, cname, "-j", "DROP") {
if err := rawIPTables("-A", cname, "-j", "DROP"); err != nil {
return fmt.Errorf("failed adding default drop rule to overlay network chain %s: %v", cname, err)
}
}
}
return nil
}
func addNetworkChain(cname string) error {
return setNetworkChain(cname, false)
}
func removeNetworkChain(cname string) error {
return setNetworkChain(cname, true)
}
func setFilters(cname, brName string, remove bool) error {
opt := "-I"
if remove {
opt = "-D"
}
// Everytime we set filters for a new subnet make sure to move the global overlay hook to the top of the both the OUTPUT and forward chains
if !remove {
for _, chain := range []string{"OUTPUT", "FORWARD"} {
exists := iptables.Exists(iptables.Filter, chain, "-j", globalChain)
if exists {
if err := rawIPTables("-D", chain, "-j", globalChain); err != nil {
return fmt.Errorf("failed to delete overlay hook in chain %s while moving the hook: %v", chain, err)
}
}
if err := rawIPTables("-I", chain, "-j", globalChain); err != nil {
return fmt.Errorf("failed to insert overlay hook in chain %s: %v", chain, err)
}
}
}
// Insert/Delete the rule to jump to per-bridge chain
exists := iptables.Exists(iptables.Filter, globalChain, "-o", brName, "-j", cname)
if (!remove && !exists) || (remove && exists) {
if err := rawIPTables(opt, globalChain, "-o", brName, "-j", cname); err != nil {
return fmt.Errorf("failed to add per-bridge filter rule for bridge %s, network chain %s: %v", brName, cname, err)
}
}
exists = iptables.Exists(iptables.Filter, cname, "-i", brName, "-j", "ACCEPT")
if (!remove && exists) || (remove && !exists) {
return nil
}
if err := rawIPTables(opt, cname, "-i", brName, "-j", "ACCEPT"); err != nil {
return fmt.Errorf("failed to add overlay filter rile for network chain %s, bridge %s: %v", cname, brName, err)
}
return nil
}
func addFilters(cname, brName string) error {
return setFilters(cname, brName, false)
}
func removeFilters(cname, brName string) error {
return setFilters(cname, brName, true)
}

View file

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
"os"
"sync" "sync"
"syscall" "syscall"
@ -12,11 +13,17 @@ import (
"github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/osl"
"github.com/docker/libnetwork/resolvconf"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
) )
var (
hostMode bool
hostModeOnce sync.Once
)
type networkTable map[string]*network type networkTable map[string]*network
type subnet struct { type subnet struct {
@ -87,22 +94,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat
return nil return nil
} }
/* func (d *driver) createNetworkfromStore(nid string) (*network, error) {
n := &network{
id: nid,
driver: d,
endpoints: endpointTable{},
once: &sync.Once{},
subnets: []*subnet{},
}
err := d.store.GetObject(datastore.Key(n.Key()...), n)
if err != nil {
return nil, fmt.Errorf("unable to get network %q from data store, %v", nid, err)
}
return n, nil
}*/
func (d *driver) DeleteNetwork(nid string) error { func (d *driver) DeleteNetwork(nid string) error {
if nid == "" { if nid == "" {
return fmt.Errorf("invalid network id") return fmt.Errorf("invalid network id")
@ -171,24 +162,110 @@ func (n *network) destroySandbox() {
} }
for _, s := range n.subnets { for _, s := range n.subnets {
if hostMode {
if err := removeFilters(n.id[:12], s.brName); err != nil {
logrus.Warnf("Could not remove overlay filters: %v", err)
}
}
if s.vxlanName != "" { if s.vxlanName != "" {
err := deleteVxlan(s.vxlanName) err := deleteInterface(s.vxlanName)
if err != nil { if err != nil {
logrus.Warnf("could not cleanup sandbox properly: %v", err) logrus.Warnf("could not cleanup sandbox properly: %v", err)
} }
} }
} }
if hostMode {
if err := removeNetworkChain(n.id[:12]); err != nil {
logrus.Warnf("could not remove network chain: %v", err)
}
}
sbox.Destroy() sbox.Destroy()
n.setSandbox(nil) n.setSandbox(nil)
} }
} }
func (n *network) initSubnetSandbox(s *subnet) error { func setHostMode() {
// create a bridge and vxlan device for this subnet and move it to the sandbox if os.Getenv("_OVERLAY_HOST_MODE") != "" {
brName, err := netutils.GenerateIfaceName("bridge", 7) hostMode = true
if err != nil { return
return err
} }
err := createVxlan("testvxlan", 1)
if err != nil {
logrus.Errorf("Failed to create testvxlan interface: %v", err)
return
}
defer deleteInterface("testvxlan")
path := "/proc/self/ns/net"
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
logrus.Errorf("Failed to open path %s for network namespace for setting host mode: %v", path, err)
return
}
defer f.Close()
nsFD := f.Fd()
iface, err := netlink.LinkByName("testvxlan")
if err != nil {
logrus.Errorf("Failed to get link testvxlan: %v", err)
return
}
// If we are not able to move the vxlan interface to a namespace
// then fallback to host mode
if err := netlink.LinkSetNsFd(iface, int(nsFD)); err != nil {
hostMode = true
}
}
func (n *network) generateVxlanName(s *subnet) string {
return "vx-" + fmt.Sprintf("%06x", n.vxlanID(s)) + "-" + n.id[:5]
}
func (n *network) generateBridgeName(s *subnet) string {
return "ov-" + fmt.Sprintf("%06x", n.vxlanID(s)) + "-" + n.id[:5]
}
func isOverlap(nw *net.IPNet) bool {
var nameservers []string
if rc, err := resolvconf.Get(); err == nil {
nameservers = resolvconf.GetNameserversAsCIDR(rc.Content)
}
if err := netutils.CheckNameserverOverlaps(nameservers, nw); err != nil {
return true
}
if err := netutils.CheckRouteOverlaps(nw); err != nil {
return true
}
return false
}
func (n *network) initSubnetSandbox(s *subnet) error {
brName := n.generateBridgeName(s)
vxlanName := n.generateVxlanName(s)
if hostMode {
// Try to delete stale bridge interface if it exists
deleteInterface(brName)
// Try to delete the vxlan interface by vni if already present
deleteVxlanByVNI(n.vxlanID(s))
if isOverlap(s.subnetIP) {
return fmt.Errorf("overlay subnet %s has conflicts in the host while running in host mode", s.subnetIP.String())
}
}
// create a bridge and vxlan device for this subnet and move it to the sandbox
sbox := n.sandbox() sbox := n.sandbox()
if err := sbox.AddInterface(brName, "br", if err := sbox.AddInterface(brName, "br",
@ -197,7 +274,7 @@ func (n *network) initSubnetSandbox(s *subnet) error {
return fmt.Errorf("bridge creation in sandbox failed for subnet %q: %v", s.subnetIP.String(), err) return fmt.Errorf("bridge creation in sandbox failed for subnet %q: %v", s.subnetIP.String(), err)
} }
vxlanName, err := createVxlan(n.vxlanID(s)) err := createVxlan(vxlanName, n.vxlanID(s))
if err != nil { if err != nil {
return err return err
} }
@ -207,6 +284,12 @@ func (n *network) initSubnetSandbox(s *subnet) error {
return fmt.Errorf("vxlan interface creation failed for subnet %q: %v", s.subnetIP.String(), err) return fmt.Errorf("vxlan interface creation failed for subnet %q: %v", s.subnetIP.String(), err)
} }
if hostMode {
if err := addFilters(n.id[:12], brName); err != nil {
return err
}
}
n.Lock() n.Lock()
s.vxlanName = vxlanName s.vxlanName = vxlanName
s.brName = brName s.brName = brName
@ -220,8 +303,16 @@ func (n *network) initSandbox() error {
n.initEpoch++ n.initEpoch++
n.Unlock() n.Unlock()
hostModeOnce.Do(setHostMode)
if hostMode {
if err := addNetworkChain(n.id[:12]); err != nil {
return err
}
}
sbox, err := osl.NewSandbox( sbox, err := osl.NewSandbox(
osl.GenerateKey(fmt.Sprintf("%d-", n.initEpoch)+n.id), true) osl.GenerateKey(fmt.Sprintf("%d-", n.initEpoch)+n.id), !hostMode)
if err != nil { if err != nil {
return fmt.Errorf("could not create network sandbox: %v", err) return fmt.Errorf("could not create network sandbox: %v", err)
} }

View file

@ -47,14 +47,9 @@ func createVethPair() (string, string, error) {
return name1, name2, nil return name1, name2, nil
} }
func createVxlan(vni uint32) (string, error) { func createVxlan(name string, vni uint32) error {
defer osl.InitOSContext()() defer osl.InitOSContext()()
name, err := netutils.GenerateIfaceName("vxlan", 7)
if err != nil {
return "", fmt.Errorf("error generating vxlan name: %v", err)
}
vxlan := &netlink.Vxlan{ vxlan := &netlink.Vxlan{
LinkAttrs: netlink.LinkAttrs{Name: name}, LinkAttrs: netlink.LinkAttrs{Name: name},
VxlanId: int(vni), VxlanId: int(vni),
@ -66,23 +61,45 @@ func createVxlan(vni uint32) (string, error) {
} }
if err := netlink.LinkAdd(vxlan); err != nil { if err := netlink.LinkAdd(vxlan); err != nil {
return "", fmt.Errorf("error creating vxlan interface: %v", err) return fmt.Errorf("error creating vxlan interface: %v", err)
}
return name, nil
}
func deleteVxlan(name string) error {
defer osl.InitOSContext()()
link, err := netlink.LinkByName(name)
if err != nil {
return fmt.Errorf("failed to find vxlan interface with name %s: %v", name, err)
}
if err := netlink.LinkDel(link); err != nil {
return fmt.Errorf("error deleting vxlan interface: %v", err)
} }
return nil return nil
} }
func deleteInterface(name string) error {
defer osl.InitOSContext()()
link, err := netlink.LinkByName(name)
if err != nil {
return fmt.Errorf("failed to find interface with name %s: %v", name, err)
}
if err := netlink.LinkDel(link); err != nil {
return fmt.Errorf("error deleting interface with name %s: %v", name, err)
}
return nil
}
func deleteVxlanByVNI(vni uint32) error {
defer osl.InitOSContext()()
links, err := netlink.LinkList()
if err != nil {
return fmt.Errorf("failed to list interfaces while deleting vxlan interface by vni: %v", err)
}
for _, l := range links {
if l.Type() == "vxlan" && l.(*netlink.Vxlan).VxlanId == int(vni) {
err = netlink.LinkDel(l)
if err != nil {
return fmt.Errorf("error deleting vxlan interface with id %d: %v", vni, err)
}
return nil
}
}
return fmt.Errorf("could not find a vxlan interface to delete with id %d", vni)
}

View file

@ -51,7 +51,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
d := &driver{ d := &driver{
networks: networkTable{}, networks: networkTable{},
peerDb: peerNetworkMap{ peerDb: peerNetworkMap{
mp: map[string]peerMap{}, mp: map[string]*peerMap{},
}, },
config: config, config: config,
} }

View file

@ -26,7 +26,7 @@ type peerMap struct {
} }
type peerNetworkMap struct { type peerNetworkMap struct {
mp map[string]peerMap mp map[string]*peerMap
sync.Mutex sync.Mutex
} }
@ -138,7 +138,7 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask
d.peerDb.Lock() d.peerDb.Lock()
pMap, ok := d.peerDb.mp[nid] pMap, ok := d.peerDb.mp[nid]
if !ok { if !ok {
d.peerDb.mp[nid] = peerMap{ d.peerDb.mp[nid] = &peerMap{
mp: make(map[string]peerEntry), mp: make(map[string]peerEntry),
} }

View file

@ -748,11 +748,8 @@ func (ep *endpoint) DataScope() string {
return ep.getNetwork().DataScope() return ep.getNetwork().DataScope()
} }
func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error { func (ep *endpoint) assignAddress(ipam ipamapi.Ipam, assignIPv4, assignIPv6 bool) error {
var ( var err error
ipam ipamapi.Ipam
err error
)
n := ep.getNetwork() n := ep.getNetwork()
if n.Type() == "host" || n.Type() == "null" { if n.Type() == "host" || n.Type() == "null" {
@ -761,11 +758,6 @@ func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
log.Debugf("Assigning addresses for endpoint %s's interface on network %s", ep.Name(), n.Name()) log.Debugf("Assigning addresses for endpoint %s's interface on network %s", ep.Name(), n.Name())
ipam, err = n.getController().getIpamDriver(n.ipamType)
if err != nil {
return err
}
if assignIPv4 { if assignIPv4 {
if err = ep.assignAddressVersion(4, ipam); err != nil { if err = ep.assignAddressVersion(4, ipam); err != nil {
return err return err

View file

@ -486,21 +486,28 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres
// DumpDatabase dumps the internal info // DumpDatabase dumps the internal info
func (a *Allocator) DumpDatabase() string { func (a *Allocator) DumpDatabase() string {
a.Lock() a.Lock()
defer a.Unlock() aspaces := make(map[string]*addrSpace, len(a.addrSpaces))
for as, aSpace := range a.addrSpaces {
aspaces[as] = aSpace
}
a.Unlock()
var s string var s string
for as, aSpace := range a.addrSpaces { for as, aSpace := range aspaces {
s = fmt.Sprintf("\n\n%s Config", as) s = fmt.Sprintf("\n\n%s Config", as)
aSpace.Lock() aSpace.Lock()
for k, config := range aSpace.subnets { for k, config := range aSpace.subnets {
s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config)) s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config))
if config.Range == nil {
a.retrieveBitmask(k, config.Pool)
}
} }
aSpace.Unlock() aSpace.Unlock()
} }
s = fmt.Sprintf("%s\n\nBitmasks", s) s = fmt.Sprintf("%s\n\nBitmasks", s)
for k, bm := range a.addresses { for k, bm := range a.addresses {
s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n\t%s: %s\n\t%d", k, bm, bm.Unselected())) s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%s: %s", k, bm))
} }
return s return s

View file

@ -22,8 +22,10 @@ const (
// Callback provides a Callback interface for registering an IPAM instance into LibNetwork // Callback provides a Callback interface for registering an IPAM instance into LibNetwork
type Callback interface { type Callback interface {
// RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a ipam instance // RegisterIpamDriver provides a way for Remote drivers to dynamically register with libnetwork
RegisterIpamDriver(name string, driver Ipam) error RegisterIpamDriver(name string, driver Ipam) error
// RegisterIpamDriverWithCapabilities provides a way for Remote drivers to dynamically register with libnetwork and specify cpaabilities
RegisterIpamDriverWithCapabilities(name string, driver Ipam, capability *Capability) error
} }
/************** /**************
@ -70,3 +72,8 @@ type Ipam interface {
// Release the address from the specified pool ID // Release the address from the specified pool ID
ReleaseAddress(string, net.IP) error ReleaseAddress(string, net.IP) error
} }
// Capability represents the requirements and capabilities of the IPAM driver
type Capability struct {
RequiresMACAddress bool
}

View file

@ -2,6 +2,8 @@
// messages between libnetwork and the remote ipam plugin // messages between libnetwork and the remote ipam plugin
package api package api
import "github.com/docker/libnetwork/ipamapi"
// Response is the basic response structure used in all responses // Response is the basic response structure used in all responses
type Response struct { type Response struct {
Error string Error string
@ -17,6 +19,17 @@ func (r *Response) GetError() string {
return r.Error return r.Error
} }
// GetCapabilityResponse is the response of GetCapability request
type GetCapabilityResponse struct {
Response
RequiresMACAddress bool
}
// ToCapability converts the capability response into the internal ipam driver capaility structure
func (capRes GetCapabilityResponse) ToCapability() *ipamapi.Capability {
return &ipamapi.Capability{RequiresMACAddress: capRes.RequiresMACAddress}
}
// GetAddressSpacesResponse is the response to the ``get default address spaces`` request message // GetAddressSpacesResponse is the response to the ``get default address spaces`` request message
type GetAddressSpacesResponse struct { type GetAddressSpacesResponse struct {
Response Response

View file

@ -30,8 +30,17 @@ func newAllocator(name string, client *plugins.Client) ipamapi.Ipam {
// Init registers a remote ipam when its plugin is activated // Init registers a remote ipam when its plugin is activated
func Init(cb ipamapi.Callback, l, g interface{}) error { func Init(cb ipamapi.Callback, l, g interface{}) error {
plugins.Handle(ipamapi.PluginEndpointType, func(name string, client *plugins.Client) { plugins.Handle(ipamapi.PluginEndpointType, func(name string, client *plugins.Client) {
if err := cb.RegisterIpamDriver(name, newAllocator(name, client)); err != nil { a := newAllocator(name, client)
log.Errorf("error registering remote ipam %s due to %v", name, err) if cps, err := a.(*allocator).getCapabilities(); err == nil {
if err := cb.RegisterIpamDriverWithCapabilities(name, a, cps); err != nil {
log.Errorf("error registering remote ipam driver %s due to %v", name, err)
}
} else {
log.Infof("remote ipam driver %s does not support capabilities", name)
log.Debug(err)
if err := cb.RegisterIpamDriver(name, a); err != nil {
log.Errorf("error registering remote ipam driver %s due to %v", name, err)
}
} }
}) })
return nil return nil
@ -49,6 +58,14 @@ func (a *allocator) call(methodName string, arg interface{}, retVal PluginRespon
return nil return nil
} }
func (a *allocator) getCapabilities() (*ipamapi.Capability, error) {
var res api.GetCapabilityResponse
if err := a.call("GetCapabilities", nil, &res); err != nil {
return nil, err
}
return res.ToCapability(), nil
}
// GetDefaultAddressSpaces returns the local and global default address spaces // GetDefaultAddressSpaces returns the local and global default address spaces
func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
res := &api.GetAddressSpacesResponse{} res := &api.GetAddressSpacesResponse{}

View file

@ -0,0 +1,22 @@
// Package ipamutils provides utililty functions for ipam management
package ipamutils
import (
"net"
"github.com/docker/libnetwork/types"
)
// ElectInterfaceAddresses looks for an interface on the OS with the specified name
// and returns its IPv4 and IPv6 addresses in CIDR form. If the interface does not exist,
// it chooses from a predifined list the first IPv4 address which does not conflict
// with other interfaces on the system.
func ElectInterfaceAddresses(name string) (*net.IPNet, []*net.IPNet, error) {
return nil, nil, types.NotImplementedErrorf("not supported on freebsd")
}
// FindAvailableNetwork returns a network from the passed list which does not
// overlap with existing interfaces in the system
func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) {
return nil, types.NotImplementedErrorf("not supported on freebsd")
}

View file

@ -41,6 +41,9 @@ const (
// Gateway represents the gateway for the network // Gateway represents the gateway for the network
Gateway = Prefix + ".gateway" Gateway = Prefix + ".gateway"
// Internal constant represents that the network is internal which disables default gateway service
Internal = Prefix + ".internal"
) )
var ( var (

View file

@ -16,6 +16,7 @@ import (
"github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/etchosts"
"github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/options" "github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
) )
@ -58,6 +59,7 @@ type Network interface {
// NetworkInfo returns some configuration and operational information about the network // NetworkInfo returns some configuration and operational information about the network
type NetworkInfo interface { type NetworkInfo interface {
IpamConfig() (string, []*IpamConf, []*IpamConf) IpamConfig() (string, []*IpamConf, []*IpamConf)
IpamInfo() ([]*IpamInfo, []*IpamInfo)
DriverOptions() map[string]string DriverOptions() map[string]string
Scope() string Scope() string
} }
@ -161,6 +163,7 @@ type network struct {
persist bool persist bool
stopWatchCh chan struct{} stopWatchCh chan struct{}
drvOnce *sync.Once drvOnce *sync.Once
internal bool
sync.Mutex sync.Mutex
} }
@ -303,6 +306,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
dstN.dbIndex = n.dbIndex dstN.dbIndex = n.dbIndex
dstN.dbExists = n.dbExists dstN.dbExists = n.dbExists
dstN.drvOnce = n.drvOnce dstN.drvOnce = n.drvOnce
dstN.internal = n.internal
for _, v4conf := range n.ipamV4Config { for _, v4conf := range n.ipamV4Config {
dstV4Conf := &IpamConf{} dstV4Conf := &IpamConf{}
@ -389,6 +393,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
} }
netMap["ipamV6Info"] = string(iis) netMap["ipamV6Info"] = string(iis)
} }
netMap["internal"] = n.internal
return json.Marshal(netMap) return json.Marshal(netMap)
} }
@ -452,6 +457,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
return err return err
} }
} }
if v, ok := netMap["internal"]; ok {
n.internal = v.(bool)
}
return nil return nil
} }
@ -478,6 +486,18 @@ func NetworkOptionPersist(persist bool) NetworkOption {
} }
} }
// NetworkOptionInternalNetwork returns an option setter to config the network
// to be internal which disables default gateway service
func NetworkOptionInternalNetwork() NetworkOption {
return func(n *network) {
n.internal = true
if n.generic == nil {
n.generic = make(map[string]interface{})
}
n.generic[netlabel.Internal] = true
}
}
// NetworkOptionIpam function returns an option setter for the ipam configuration for this network // NetworkOptionIpam function returns an option setter for the ipam configuration for this network
func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf) NetworkOption { func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf) NetworkOption {
return func(n *network) { return func(n *network) {
@ -678,7 +698,22 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
} }
} }
if err = ep.assignAddress(true, !n.postIPv6); err != nil { ipam, err := n.getController().getIPAM(n.ipamType)
if err != nil {
return nil, err
}
if ipam.capability.RequiresMACAddress {
if ep.iface.mac == nil {
ep.iface.mac = netutils.GenerateRandomMAC()
}
if ep.ipamOptions == nil {
ep.ipamOptions = make(map[string]string)
}
ep.ipamOptions[netlabel.MacAddress] = ep.iface.mac.String()
}
if err = ep.assignAddress(ipam.driver, true, !n.postIPv6); err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {
@ -698,7 +733,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
} }
}() }()
if err = ep.assignAddress(false, n.postIPv6); err != nil { if err = ep.assignAddress(ipam.driver, false, n.postIPv6); err != nil {
return nil, err return nil, err
} }
@ -1148,3 +1183,32 @@ func (n *network) IpamConfig() (string, []*IpamConf, []*IpamConf) {
return n.ipamType, v4L, v6L return n.ipamType, v4L, v6L
} }
func (n *network) IpamInfo() ([]*IpamInfo, []*IpamInfo) {
n.Lock()
defer n.Unlock()
v4Info := make([]*IpamInfo, len(n.ipamV4Info))
v6Info := make([]*IpamInfo, len(n.ipamV6Info))
for i, info := range n.ipamV4Info {
ic := &IpamInfo{}
info.CopyTo(ic)
v4Info[i] = ic
}
for i, info := range n.ipamV6Info {
ic := &IpamInfo{}
info.CopyTo(ic)
v6Info[i] = ic
}
return v4Info, v6Info
}
func (n *network) Internal() bool {
n.Lock()
defer n.Unlock()
return n.internal
}

View file

@ -109,6 +109,7 @@ func (i *nwIface) Remove() error {
n.Lock() n.Lock()
path := n.path path := n.path
isDefault := n.isDefault
n.Unlock() n.Unlock()
return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error { return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
@ -134,7 +135,7 @@ func (i *nwIface) Remove() error {
if err := netlink.LinkDel(iface); err != nil { if err := netlink.LinkDel(iface); err != nil {
return fmt.Errorf("failed deleting bridge %q: %v", i.SrcName(), err) return fmt.Errorf("failed deleting bridge %q: %v", i.SrcName(), err)
} }
} else { } else if !isDefault {
// Move the network interface to caller namespace. // Move the network interface to caller namespace.
if err := netlink.LinkSetNsFd(iface, callerFD); err != nil { if err := netlink.LinkSetNsFd(iface, callerFD); err != nil {
fmt.Println("LinkSetNsPid failed: ", err) fmt.Println("LinkSetNsPid failed: ", err)
@ -213,9 +214,15 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
} }
n.Lock() n.Lock()
i.dstName = fmt.Sprintf("%s%d", i.dstName, n.nextIfIndex) if n.isDefault {
n.nextIfIndex++ i.dstName = i.srcName
} else {
i.dstName = fmt.Sprintf("%s%d", i.dstName, n.nextIfIndex)
n.nextIfIndex++
}
path := n.path path := n.path
isDefault := n.isDefault
n.Unlock() n.Unlock()
return nsInvoke(path, func(nsFD int) error { return nsInvoke(path, func(nsFD int) error {
@ -231,9 +238,13 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err) return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err)
} }
// Move the network interface to the destination namespace. // Move the network interface to the destination
if err := netlink.LinkSetNsFd(iface, nsFD); err != nil { // namespace only if the namespace is not a default
return fmt.Errorf("failed to set namespace on link %q: %v", i.srcName, err) // type
if !isDefault {
if err := netlink.LinkSetNsFd(iface, nsFD); err != nil {
return fmt.Errorf("failed to set namespace on link %q: %v", i.srcName, err)
}
} }
return nil return nil

View file

@ -41,6 +41,7 @@ type networkNamespace struct {
staticRoutes []*types.StaticRoute staticRoutes []*types.StaticRoute
neighbors []*neigh neighbors []*neigh
nextIfIndex int nextIfIndex int
isDefault bool
sync.Mutex sync.Mutex
} }
@ -146,7 +147,7 @@ func NewSandbox(key string, osCreate bool) (Sandbox, error) {
return nil, err return nil, err
} }
return &networkNamespace{path: key}, nil return &networkNamespace{path: key, isDefault: !osCreate}, nil
} }
func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter { func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter {

View file

@ -19,6 +19,11 @@ func NewSandbox(key string, osCreate bool) (Sandbox, error) {
return nil, nil return nil, nil
} }
// GetSandboxForExternalKey returns sandbox object for the supplied path
func GetSandboxForExternalKey(path string, key string) (Sandbox, error) {
return nil, nil
}
// GC triggers garbage collection of namespace path right away // GC triggers garbage collection of namespace path right away
// and waits for it. // and waits for it.
func GC() { func GC() {