Merge pull request #15584 from mavenugo/ln-vendor-in

Vendoring in libnetwork 22dc04d06067b40a9e7ef575aee6d1bb69d4dcc3
This commit is contained in:
Alexander Morozov 2015-08-14 11:56:47 -07:00
Родитель f39323c1e3 703e2264ba
Коммит d1fada4bf7
29 изменённых файлов: 656 добавлений и 439 удалений

Просмотреть файл

@ -392,19 +392,19 @@ func initNetworkController(config *Config) (libnetwork.NetworkController, error)
func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error {
option := options.Generic{
"EnableIPForwarding": config.Bridge.EnableIPForward}
"EnableIPForwarding": config.Bridge.EnableIPForward,
"EnableIPTables": config.Bridge.EnableIPTables,
"EnableUserlandProxy": config.Bridge.EnableUserlandProxy}
if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil {
return fmt.Errorf("Error initializing bridge driver: %v", err)
}
netOption := options.Generic{
"BridgeName": config.Bridge.Iface,
"Mtu": config.Mtu,
"EnableIPTables": config.Bridge.EnableIPTables,
"EnableIPMasquerade": config.Bridge.EnableIPMasq,
"EnableICC": config.Bridge.InterContainerCommunication,
"EnableUserlandProxy": config.Bridge.EnableUserlandProxy,
"BridgeName": config.Bridge.Iface,
"Mtu": config.Mtu,
"EnableIPMasquerade": config.Bridge.EnableIPMasq,
"EnableICC": config.Bridge.InterContainerCommunication,
}
if config.Bridge.IP != "" {

Просмотреть файл

@ -21,14 +21,14 @@ clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://gith
clone hg code.google.com/p/gosqlite 74691fb6f837
#get libnetwork packages
clone git github.com/docker/libnetwork bd3eecc96f3c05a4acef1bedcf74397bc6850d22
clone git github.com/docker/libnetwork 22dc04d06067b40a9e7ef575aee6d1bb69d4dcc3
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
clone git github.com/hashicorp/serf 7151adcef72687bf95f451a2e0ba15cb19412bf2
clone git github.com/docker/libkv 60c7c881345b3c67defc7f93a8297debf041d43c
clone git github.com/vishvananda/netns 493029407eeb434d0c2d44e02ea072ff2488d322
clone git github.com/vishvananda/netlink 20397a138846e4d6590e01783ed023ed7e1c38a6
clone git github.com/vishvananda/netlink 329b40d4e308deb2abe80dbb1f74078e5ab13164
clone git github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
clone git github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
clone git github.com/coreos/go-etcd v2.0.0

Просмотреть файл

@ -64,7 +64,7 @@ There are many networking solutions available to suit a broad range of use-cases
epInfo, err := ep.DriverInfo()
mapData, ok := epInfo[netlabel.PortMap]
if ok {
portMapping, ok := mapData.([]netutils.PortBinding)
portMapping, ok := mapData.([]types.PortBinding)
if ok {
fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping)
}

Просмотреть файл

@ -22,20 +22,24 @@ var (
const (
// Resource name regex
// Gorilla mux encloses the passed pattern with '^' and '$'. So we need to do some tricks
// to have mux eventually build a query regex which matches empty or word string (`^$|[\w]+`)
regex = "[a-zA-Z_0-9-]+"
qregx = "$|" + regex
// Router URL variable definition
nwName = "{" + urlNwName + ":" + regex + "}"
nwID = "{" + urlNwID + ":" + regex + "}"
nwPID = "{" + urlNwPID + ":" + regex + "}"
epName = "{" + urlEpName + ":" + regex + "}"
epID = "{" + urlEpID + ":" + regex + "}"
epPID = "{" + urlEpPID + ":" + regex + "}"
cnID = "{" + urlCnID + ":" + regex + "}"
nwName = "{" + urlNwName + ":" + regex + "}"
nwNameQr = "{" + urlNwName + ":" + qregx + "}"
nwID = "{" + urlNwID + ":" + regex + "}"
nwPIDQr = "{" + urlNwPID + ":" + qregx + "}"
epName = "{" + urlEpName + ":" + regex + "}"
epNameQr = "{" + urlEpName + ":" + qregx + "}"
epID = "{" + urlEpID + ":" + regex + "}"
epPIDQr = "{" + urlEpPID + ":" + qregx + "}"
cnID = "{" + urlCnID + ":" + regex + "}"
// Though this name can be anything, in order to support default network,
// we will keep it as name
urlNwName = "name"
// Internal URL variable name, they can be anything
// Internal URL variable name.They can be anything as
// long as they do not collide with query fields.
urlNwName = "network-name"
urlNwID = "network-id"
urlNwPID = "network-partial-id"
urlEpName = "endpoint-name"
@ -89,17 +93,17 @@ func (h *httpHandler) initRouter() {
}{
"GET": {
// Order matters
{"/networks", []string{"name", nwName}, procGetNetworks},
{"/networks", []string{"partial-id", nwPID}, procGetNetworks},
{"/networks", []string{"name", nwNameQr}, procGetNetworks},
{"/networks", []string{"partial-id", nwPIDQr}, procGetNetworks},
{"/networks", nil, procGetNetworks},
{"/networks/" + nwID, nil, procGetNetwork},
{"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints},
{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPID}, procGetEndpoints},
{"/networks/" + nwID + "/endpoints", []string{"name", epNameQr}, procGetEndpoints},
{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPIDQr}, procGetEndpoints},
{"/networks/" + nwID + "/endpoints", nil, procGetEndpoints},
{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
{"/services", []string{"network", nwName}, procGetServices},
{"/services", []string{"name", epName}, procGetServices},
{"/services", []string{"partial-id", epPID}, procGetServices},
{"/services", []string{"network", nwNameQr}, procGetServices},
{"/services", []string{"name", epNameQr}, procGetServices},
{"/services", []string{"partial-id", epPIDQr}, procGetServices},
{"/services", nil, procGetServices},
{"/services/" + epID, nil, procGetService},
{"/services/" + epID + "/backend", nil, procGetContainers},
@ -150,22 +154,7 @@ func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerF
}
}
mvars := mux.Vars(req)
rvars := req.URL.Query()
// workaround a mux issue which filters out valid queries with empty value
for k := range rvars {
if _, ok := mvars[k]; !ok {
if rvars.Get(k) == "" {
mvars[k] = ""
}
}
}
res, rsp := fct(ctrl, mvars, body)
if !rsp.isOK() {
http.Error(w, rsp.Status, rsp.StatusCode)
return
}
res, rsp := fct(ctrl, mux.Vars(req), body)
if res != nil {
writeJSON(w, rsp.StatusCode, res)
}

Просмотреть файл

@ -5,9 +5,11 @@ import (
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
@ -38,7 +40,9 @@ var (
// configuration info for the "bridge" driver.
type configuration struct {
EnableIPForwarding bool
EnableIPForwarding bool
EnableIPTables bool
EnableUserlandProxy bool
}
// networkConfiguration for network specific configuration
@ -48,7 +52,6 @@ type networkConfiguration struct {
FixedCIDR *net.IPNet
FixedCIDRv6 *net.IPNet
EnableIPv6 bool
EnableIPTables bool
EnableIPMasquerade bool
EnableICC bool
Mtu int
@ -56,7 +59,6 @@ type networkConfiguration struct {
DefaultGatewayIPv6 net.IP
DefaultBindingIP net.IP
AllowNonDefaultBridge bool
EnableUserlandProxy bool
}
// endpointConfiguration represents the user specified configuration for the sandbox endpoint
@ -89,13 +91,16 @@ type bridgeNetwork struct {
config *networkConfiguration
endpoints map[types.UUID]*bridgeEndpoint // key: endpoint id
portMapper *portmapper.PortMapper
driver *driver // The network's driver
sync.Mutex
}
type driver struct {
config *configuration
network *bridgeNetwork
networks map[types.UUID]*bridgeNetwork
config *configuration
network *bridgeNetwork
natChain *iptables.ChainInfo
filterChain *iptables.ChainInfo
networks map[types.UUID]*bridgeNetwork
sync.Mutex
}
@ -110,10 +115,13 @@ 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").CombinedOutput(); err != nil {
logrus.Warnf("Running modprobe bridge nf_nat br_netfilter failed with message: %s, error: %v", out, err)
if _, err := os.Stat("/proc/sys/net/bridge"); err != nil {
if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil {
logrus.Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err)
}
}
if out, err := exec.Command("modprobe", "-va", "nf_nat").CombinedOutput(); err != nil {
logrus.Warnf("Running modprobe nf_nat failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
}
if err := iptables.FirewalldInit(); err != nil {
logrus.Debugf("Fail to initialize firewalld: %v, using raw iptables instead", err)
@ -218,16 +226,6 @@ func (c *networkConfiguration) fromMap(data map[string]interface{}) error {
}
}
if i, ok := data["EnableIPTables"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.EnableIPTables, err = strconv.ParseBool(s); err != nil {
return types.BadRequestErrorf("failed to parse EnableIPTables value: %s", err.Error())
}
} else {
return types.BadRequestErrorf("invalid type for EnableIPTables value")
}
}
if i, ok := data["EnableIPMasquerade"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.EnableIPMasquerade, err = strconv.ParseBool(s); err != nil {
@ -329,6 +327,25 @@ func (c *networkConfiguration) fromMap(data map[string]interface{}) error {
return nil
}
func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, error) {
n.Lock()
defer n.Unlock()
if n.driver == nil {
return nil, nil, types.BadRequestErrorf("no driver found")
}
return n.driver.natChain, n.driver.filterChain, nil
}
func (n *bridgeNetwork) getNetworkBridgeName() string {
n.Lock()
config := n.config
n.Unlock()
return config.BridgeName
}
func (n *bridgeNetwork) getEndpoint(eid types.UUID) (*bridgeEndpoint, error) {
n.Lock()
defer n.Unlock()
@ -413,6 +430,7 @@ func (c *networkConfiguration) conflictsWithNetworks(id types.UUID, others []*br
func (d *driver) Config(option map[string]interface{}) error {
var config *configuration
var err error
d.Lock()
defer d.Unlock()
@ -439,10 +457,19 @@ func (d *driver) Config(option map[string]interface{}) error {
d.config = config
} else {
config = &configuration{}
d.config = config
}
if config.EnableIPForwarding {
return setupIPForwarding(config)
err = setupIPForwarding()
if err != nil {
return err
}
}
if config.EnableIPTables {
d.natChain, d.filterChain, err = setupIPChains(config)
return err
}
return nil
@ -475,7 +502,6 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error)
case map[string]interface{}:
config = &networkConfiguration{
EnableICC: true,
EnableIPTables: true,
EnableIPMasquerade: true,
}
err = config.fromMap(opt)
@ -573,6 +599,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
endpoints: make(map[types.UUID]*bridgeEndpoint),
config: config,
portMapper: portmapper.New(),
driver: d,
}
d.Lock()
@ -655,14 +682,14 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
{enableIPv6Forwarding, setupIPv6Forwarding},
// Setup Loopback Adresses Routing
{!config.EnableUserlandProxy, setupLoopbackAdressesRouting},
{!d.config.EnableUserlandProxy, setupLoopbackAdressesRouting},
// Setup IPTables.
{config.EnableIPTables, network.setupIPTables},
{d.config.EnableIPTables, network.setupIPTables},
//We want to track firewalld configuration so that
//if it is started/reloaded, the rules can be applied correctly
{config.EnableIPTables, network.setupFirewalld},
{d.config.EnableIPTables, network.setupFirewalld},
// Setup DefaultGatewayIPv4
{config.DefaultGatewayIPv4 != nil, setupGatewayIPv4},
@ -671,10 +698,10 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
{config.DefaultGatewayIPv6 != nil, setupGatewayIPv6},
// Add inter-network communication rules.
{config.EnableIPTables, setupNetworkIsolationRules},
{d.config.EnableIPTables, setupNetworkIsolationRules},
//Configure bridge networking filtering if ICC is off and IP tables are enabled
{!config.EnableICC && config.EnableIPTables, setupBridgeNetFiltering},
{!config.EnableICC && d.config.EnableIPTables, setupBridgeNetFiltering},
} {
if step.Condition {
bridgeSetup.queueStep(step.Fn)
@ -833,6 +860,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
// Get the network handler and make sure it exists
d.Lock()
n, ok := d.networks[nid]
dconfig := d.config
d.Unlock()
if !ok {
@ -945,7 +973,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err)
}
if !config.EnableUserlandProxy {
if !dconfig.EnableUserlandProxy {
err = setHairpinMode(host, true)
if err != nil {
return err
@ -1018,7 +1046,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}
// Program any required port mapping and store them in the endpoint
endpoint.portMapping, err = n.allocatePorts(epConfig, endpoint, config.DefaultBindingIP, config.EnableUserlandProxy)
endpoint.portMapping, err = n.allocatePorts(epConfig, endpoint, config.DefaultBindingIP, d.config.EnableUserlandProxy)
if err != nil {
return err
}
@ -1101,9 +1129,9 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
}
// Try removal of link. Discard error: link pair might have
// already been deleted by sandbox delete.
link, err := netlink.LinkByName(ep.srcName)
if err == nil {
// already been deleted by sandbox delete. Make sure defer
// does not see this error either.
if link, err := netlink.LinkByName(ep.srcName); err == nil {
netlink.LinkDel(link)
}
@ -1381,34 +1409,9 @@ func parseContainerOptions(cOptions map[string]interface{}) (*containerConfigura
}
}
// 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 generateMacAddr(ip)
return netutils.GenerateMACFromIP(ip)
}

Просмотреть файл

@ -115,17 +115,6 @@ func (eim ErrInvalidMtu) Error() string {
// BadRequest denotes the type of this error
func (eim ErrInvalidMtu) BadRequest() {}
// ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration
// not enabled.
type ErrIPFwdCfg struct{}
func (eipf *ErrIPFwdCfg) Error() string {
return "unexpected request to enable IP Forwarding"
}
// BadRequest denotes the type of this error
func (eipf *ErrIPFwdCfg) BadRequest() {}
// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid.
type ErrInvalidPort string

Просмотреть файл

@ -74,9 +74,9 @@ func linkContainers(action, parentIP, childIP string, ports []types.TransportPor
return InvalidLinkIPAddrError(childIP)
}
chain := iptables.Chain{Name: DockerChain, Bridge: bridge}
chain := iptables.ChainInfo{Name: DockerChain}
for _, port := range ports {
err := chain.Link(nfAction, ip1, ip2, int(port.Port), port.Proto.String())
err := chain.Link(nfAction, ip1, ip2, int(port.Port), port.Proto.String(), bridge)
if !ignoreErrors && err != nil {
return err
}

Просмотреть файл

@ -7,6 +7,8 @@ import (
"syscall"
"time"
"unsafe"
"github.com/docker/libnetwork/netutils"
)
const (
@ -74,16 +76,6 @@ func ioctlAddToBridge(iface, master *net.Interface) error {
return ifIoctBridge(iface, master, ioctlBrAddIf)
}
func randMacAddr() string {
hw := make(net.HardwareAddr, 6)
for i := 0; i < 6; i++ {
hw[i] = byte(rnd.Intn(255))
}
hw[0] &^= 0x1 // clear multicast bit
hw[0] |= 0x2 // set local assignment bit (IEEE802)
return hw.String()
}
func ioctlSetMacAddress(name, addr string) error {
if len(name) >= ifNameSize {
return fmt.Errorf("Interface name %s too long", name)
@ -133,7 +125,7 @@ func ioctlCreateBridge(name string, setMacAddr bool) error {
return err
}
if setMacAddr {
return ioctlSetMacAddress(name, randMacAddr())
return ioctlSetMacAddress(name, netutils.GenerateRandomMAC().String())
}
return nil
}

Просмотреть файл

@ -57,6 +57,11 @@ func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHos
bnd.HostIP = defHostIP
}
// Adjust HostPortEnd if this is not a range.
if bnd.HostPortEnd == 0 {
bnd.HostPortEnd = bnd.HostPort
}
// Construct the container side transport address
container, err := bnd.ContainerAddr()
if err != nil {
@ -65,12 +70,12 @@ func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHos
// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
for i := 0; i < maxAllocatePortAttempts; i++ {
if host, err = n.portMapper.Map(container, bnd.HostIP, int(bnd.HostPort), ulPxyEnabled); err == nil {
if host, err = n.portMapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
break
}
// There is no point in immediately retrying to map an explicitly chosen port.
if bnd.HostPort != 0 {
logrus.Warnf("Failed to allocate and map port %d: %s", bnd.HostPort, err)
logrus.Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
break
}
logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)

Просмотреть файл

@ -3,8 +3,13 @@ package bridge
import "github.com/docker/libnetwork/iptables"
func (n *bridgeNetwork) setupFirewalld(config *networkConfiguration, i *bridgeInterface) error {
d := n.driver
d.Lock()
driverConfig := d.config
d.Unlock()
// Sanity check.
if config.EnableIPTables == false {
if driverConfig.EnableIPTables == false {
return IPTableCfgError(config.BridgeName)
}

Просмотреть файл

@ -10,15 +10,19 @@ const (
ipv4ForwardConfPerm = 0644
)
func setupIPForwarding(config *configuration) error {
// Sanity Check
if config.EnableIPForwarding == false {
return &ErrIPFwdCfg{}
func setupIPForwarding() error {
// Get current IPv4 forward setup
ipv4ForwardData, err := ioutil.ReadFile(ipv4ForwardConf)
if err != nil {
return fmt.Errorf("Cannot read IP forwarding setup: %v", err)
}
// Enable IPv4 forwarding
if err := ioutil.WriteFile(ipv4ForwardConf, []byte{'1', '\n'}, ipv4ForwardConfPerm); err != nil {
return fmt.Errorf("Setup IP forwarding failed: %v", err)
// Enable IPv4 forwarding only if it is not already enabled
if ipv4ForwardData[0] != '1' {
// Enable IPv4 forwarding
if err := ioutil.WriteFile(ipv4ForwardConf, []byte{'1', '\n'}, ipv4ForwardConfPerm); err != nil {
return fmt.Errorf("Setup IP forwarding failed: %v", err)
}
}
return nil

Просмотреть файл

@ -4,6 +4,7 @@ import (
"fmt"
"net"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/iptables"
"github.com/docker/libnetwork/netutils"
)
@ -13,33 +14,77 @@ const (
DockerChain = "DOCKER"
)
func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInterface) error {
func setupIPChains(config *configuration) (*iptables.ChainInfo, *iptables.ChainInfo, error) {
// Sanity check.
if config.EnableIPTables == false {
return IPTableCfgError(config.BridgeName)
return nil, nil, fmt.Errorf("Cannot create new chains, EnableIPTable is disabled")
}
hairpinMode := !config.EnableUserlandProxy
natChain, err := iptables.NewChain(DockerChain, iptables.Nat, hairpinMode)
if err != nil {
return nil, nil, fmt.Errorf("Failed to create NAT chain: %s", err.Error())
}
defer func() {
if err != nil {
if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
logrus.Warnf("Failed on removing iptables NAT chain on cleanup: %v", err)
}
}
}()
filterChain, err := iptables.NewChain(DockerChain, iptables.Filter, hairpinMode)
if err != nil {
return nil, nil, fmt.Errorf("Failed to create FILTER chain: %s", err.Error())
}
return natChain, filterChain, nil
}
func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInterface) error {
d := n.driver
d.Lock()
driverConfig := d.config
d.Unlock()
// Sanity check.
if driverConfig.EnableIPTables == false {
return fmt.Errorf("Cannot program chains, EnableIPTable is disabled")
}
// Pickup this configuraton option from driver
hairpinMode := !driverConfig.EnableUserlandProxy
addrv4, _, err := netutils.GetIfaceAddr(config.BridgeName)
if err != nil {
return fmt.Errorf("Failed to setup IP tables, cannot acquire Interface address: %s", err.Error())
}
if err = setupIPTablesInternal(config.BridgeName, addrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
ipnet := addrv4.(*net.IPNet)
maskedAddrv4 := &net.IPNet{
IP: ipnet.IP.Mask(ipnet.Mask),
Mask: ipnet.Mask,
}
if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
}
_, err = iptables.NewChain(DockerChain, config.BridgeName, iptables.Nat, hairpinMode)
natChain, filterChain, err := n.getDriverChains()
if err != nil {
return fmt.Errorf("Failed to create NAT chain: %s", err.Error())
return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
}
chain, err := iptables.NewChain(DockerChain, config.BridgeName, iptables.Filter, hairpinMode)
err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode)
if err != nil {
return fmt.Errorf("Failed to create FILTER chain: %s", err.Error())
return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
}
n.portMapper.SetIptablesChain(chain)
err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode)
if err != nil {
return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
}
n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
return nil
}

Просмотреть файл

@ -131,10 +131,16 @@ func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error {
}
func setupLoopbackAdressesRouting(config *networkConfiguration, i *bridgeInterface) error {
// Enable loopback adresses routing
sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet")
if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil {
return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err)
ipv4LoRoutingData, err := ioutil.ReadFile(sysPath)
if err != nil {
return fmt.Errorf("Cannot read IPv4 local routing setup: %v", err)
}
// Enable loopback adresses routing only if it isn't already enabled
if ipv4LoRoutingData[0] != '1' {
if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil {
return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err)
}
}
return nil
}

Просмотреть файл

@ -12,8 +12,10 @@ import (
var bridgeIPv6 *net.IPNet
const (
bridgeIPv6Str = "fe80::1/64"
ipv6ForwardConfPerm = 0644
bridgeIPv6Str = "fe80::1/64"
ipv6ForwardConfPerm = 0644
ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding"
ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding"
)
func init() {
@ -27,10 +29,16 @@ 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'}, ipv6ForwardConfPerm); err != nil {
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
ipv6BridgeData, err := ioutil.ReadFile(procFile)
if err != nil {
return fmt.Errorf("Cannot read IPv6 setup for bridge %v: %v", config.BridgeName, err)
}
// Enable IPv6 on the bridge only if it isn't already enabled
if ipv6BridgeData[0] != '0' {
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil {
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
}
}
_, addrsv6, err := i.addresses()
@ -70,12 +78,29 @@ func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error {
}
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)
// Get current IPv6 default forwarding setup
ipv6ForwardDataDefault, err := ioutil.ReadFile(ipv6ForwardConfDefault)
if err != nil {
return fmt.Errorf("Cannot read IPv6 default forwarding setup: %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)
// Enable IPv6 default forwarding only if it is not already enabled
if ipv6ForwardDataDefault[0] != '1' {
if err := ioutil.WriteFile(ipv6ForwardConfDefault, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err)
}
}
// Get current IPv6 all forwarding setup
ipv6ForwardDataAll, err := ioutil.ReadFile(ipv6ForwardConfAll)
if err != nil {
return fmt.Errorf("Cannot read IPv6 all forwarding setup: %v", err)
}
// Enable IPv6 all forwarding only if it is not already enabled
if ipv6ForwardDataAll[0] != '1' {
if err := ioutil.WriteFile(ipv6ForwardConfAll, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err)
}
}
return nil
}

Просмотреть файл

@ -30,6 +30,9 @@ type network struct {
vxlanName string
driver *driver
joinCnt int
once *sync.Once
initEpoch int
initErr error
sync.Mutex
}
@ -42,6 +45,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
id: id,
driver: d,
endpoints: endpointTable{},
once: &sync.Once{},
}
n.gw = bridgeIP.IP
@ -77,10 +81,26 @@ func (n *network) joinSandbox() error {
n.Unlock()
return nil
}
n.joinCnt++
n.Unlock()
return n.initSandbox()
// If there is a race between two go routines here only one will win
// the other will wait.
n.once.Do(func() {
// save the error status of initSandbox in n.initErr so that
// all the racing go routines are able to know the status.
n.initErr = n.initSandbox()
})
// Increment joinCnt in all the goroutines only when the one time initSandbox
// was a success.
n.Lock()
if n.initErr == nil {
n.joinCnt++
}
err := n.initErr
n.Unlock()
return err
}
func (n *network) leaveSandbox() {
@ -90,6 +110,11 @@ func (n *network) leaveSandbox() {
n.Unlock()
return
}
// We are about to destroy sandbox since the container is leaving the network
// Reinitialize the once variable so that we will be able to trigger one time
// sandbox initialization(again) when another container joins subsequently.
n.once = &sync.Once{}
n.Unlock()
n.destroySandbox()
@ -111,7 +136,12 @@ func (n *network) destroySandbox() {
}
func (n *network) initSandbox() error {
sbox, err := sandbox.NewSandbox(sandbox.GenerateKey(string(n.id)), true)
n.Lock()
n.initEpoch++
n.Unlock()
sbox, err := sandbox.NewSandbox(
sandbox.GenerateKey(fmt.Sprintf("%d-", n.initEpoch)+string(n.id)), true)
if err != nil {
return fmt.Errorf("could not create network sandbox: %v", err)
}

Просмотреть файл

@ -6,6 +6,7 @@ import (
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl"
)
func validateID(nid, eid types.UUID) error {
@ -54,7 +55,7 @@ func createVxlan(vni uint32) (string, error) {
LinkAttrs: netlink.LinkAttrs{Name: name},
VxlanId: int(vni),
Learning: true,
Port: vxlanPort,
Port: int(nl.Swap16(vxlanPort)), //network endian order
Proxy: true,
L3miss: true,
L2miss: true,

Просмотреть файл

@ -0,0 +1,144 @@
/*
Package api represents all requests and responses suitable for conversation
with a remote driver.
*/
package api
import "net"
// Response is the basic response structure used in all responses.
type Response struct {
Err string
}
// GetError returns the error from the response, if any.
func (r *Response) GetError() string {
return r.Err
}
// CreateNetworkRequest requests a new network.
type CreateNetworkRequest struct {
// A network ID that remote plugins are expected to store for future
// reference.
NetworkID string
// A free form map->object interface for communication of options.
Options map[string]interface{}
}
// CreateNetworkResponse is the response to the CreateNetworkRequest.
type CreateNetworkResponse struct {
Response
}
// DeleteNetworkRequest is the request to delete an existing network.
type DeleteNetworkRequest struct {
// The ID of the network to delete.
NetworkID string
}
// DeleteNetworkResponse is the response to a request for deleting a network.
type DeleteNetworkResponse struct {
Response
}
// CreateEndpointRequest is the request to create an endpoint within a network.
type CreateEndpointRequest struct {
// Provided at create time, this will be the network id referenced.
NetworkID string
// The ID of the endpoint for later reference.
EndpointID string
Interfaces []*EndpointInterface
Options map[string]interface{}
}
// EndpointInterface represents an interface endpoint.
type EndpointInterface struct {
ID int
Address string
AddressIPv6 string
MacAddress string
}
// CreateEndpointResponse is the response to the CreateEndpoint action.
type CreateEndpointResponse struct {
Response
Interfaces []*EndpointInterface
}
// Interface is the representation of a linux interface.
type Interface struct {
ID int
Address *net.IPNet
AddressIPv6 *net.IPNet
MacAddress net.HardwareAddr
}
// DeleteEndpointRequest describes the API for deleting an endpoint.
type DeleteEndpointRequest struct {
NetworkID string
EndpointID string
}
// DeleteEndpointResponse is the response to the DeleteEndpoint action.
type DeleteEndpointResponse struct {
Response
}
// EndpointInfoRequest retrieves information about the endpoint from the network driver.
type EndpointInfoRequest struct {
NetworkID string
EndpointID string
}
// EndpointInfoResponse is the response to an EndpointInfoRequest.
type EndpointInfoResponse struct {
Response
Value map[string]interface{}
}
// JoinRequest describes the API for joining an endpoint to a sandbox.
type JoinRequest struct {
NetworkID string
EndpointID string
SandboxKey string
Options map[string]interface{}
}
// InterfaceName is the struct represetation of a pair of devices with source
// and destination, for the purposes of putting an endpoint into a container.
type InterfaceName struct {
SrcName string
DstName string
DstPrefix string
}
// StaticRoute is the plain JSON representation of a static route.
type StaticRoute struct {
Destination string
RouteType int
NextHop string
InterfaceID int
}
// JoinResponse is the response to a JoinRequest.
type JoinResponse struct {
Response
InterfaceNames []*InterfaceName
Gateway string
GatewayIPv6 string
HostsPath string
ResolvConfPath string
StaticRoutes []StaticRoute
}
// LeaveRequest describes the API for detaching an endpoint from a sandbox.
type LeaveRequest struct {
NetworkID string
EndpointID string
}
// LeaveResponse is the answer to LeaveRequest.
type LeaveResponse struct {
Response
}

Просмотреть файл

@ -7,6 +7,7 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/plugins"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/drivers/remote/api"
"github.com/docker/libnetwork/types"
)
@ -15,6 +16,10 @@ type driver struct {
networkType string
}
type maybeError interface {
GetError() string
}
func newDriver(name string, client *plugins.Client) driverapi.Driver {
return &driver{networkType: name, endpoint: client}
}
@ -46,23 +51,23 @@ func (d *driver) call(methodName string, arg interface{}, retVal maybeError) err
if err != nil {
return err
}
if e := retVal.getError(); e != "" {
if e := retVal.GetError(); e != "" {
return fmt.Errorf("remote: %s", e)
}
return nil
}
func (d *driver) CreateNetwork(id types.UUID, options map[string]interface{}) error {
create := &createNetworkRequest{
create := &api.CreateNetworkRequest{
NetworkID: string(id),
Options: options,
}
return d.call("CreateNetwork", create, &createNetworkResponse{})
return d.call("CreateNetwork", create, &api.CreateNetworkResponse{})
}
func (d *driver) DeleteNetwork(nid types.UUID) error {
delete := &deleteNetworkRequest{NetworkID: string(nid)}
return d.call("DeleteNetwork", delete, &deleteNetworkResponse{})
delete := &api.DeleteNetworkRequest{NetworkID: string(nid)}
return d.call("DeleteNetwork", delete, &api.DeleteNetworkResponse{})
}
func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
@ -70,29 +75,29 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
return fmt.Errorf("must not be called with nil EndpointInfo")
}
reqIfaces := make([]*endpointInterface, len(epInfo.Interfaces()))
reqIfaces := make([]*api.EndpointInterface, len(epInfo.Interfaces()))
for i, iface := range epInfo.Interfaces() {
addr4 := iface.Address()
addr6 := iface.AddressIPv6()
reqIfaces[i] = &endpointInterface{
reqIfaces[i] = &api.EndpointInterface{
ID: iface.ID(),
Address: addr4.String(),
AddressIPv6: addr6.String(),
MacAddress: iface.MacAddress().String(),
}
}
create := &createEndpointRequest{
create := &api.CreateEndpointRequest{
NetworkID: string(nid),
EndpointID: string(eid),
Interfaces: reqIfaces,
Options: epOptions,
}
var res createEndpointResponse
var res api.CreateEndpointResponse
if err := d.call("CreateEndpoint", create, &res); err != nil {
return err
}
ifaces, err := res.parseInterfaces()
ifaces, err := parseInterfaces(res)
if err != nil {
return err
}
@ -125,19 +130,19 @@ func errorWithRollback(msg string, err error) error {
}
func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
delete := &deleteEndpointRequest{
delete := &api.DeleteEndpointRequest{
NetworkID: string(nid),
EndpointID: string(eid),
}
return d.call("DeleteEndpoint", delete, &deleteEndpointResponse{})
return d.call("DeleteEndpoint", delete, &api.DeleteEndpointResponse{})
}
func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
info := &endpointInfoRequest{
info := &api.EndpointInfoRequest{
NetworkID: string(nid),
EndpointID: string(eid),
}
var res endpointInfoResponse
var res api.EndpointInfoResponse
if err := d.call("EndpointOperInfo", info, &res); err != nil {
return nil, err
}
@ -146,14 +151,14 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
// Join method is invoked when a Sandbox is attached to an endpoint.
func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
join := &joinRequest{
join := &api.JoinRequest{
NetworkID: string(nid),
EndpointID: string(eid),
SandboxKey: sboxKey,
Options: options,
}
var (
res joinResponse
res api.JoinResponse
err error
)
if err = d.call("Join", join, &res); err != nil {
@ -194,7 +199,7 @@ func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinI
}
}
if len(res.StaticRoutes) > 0 {
routes, err := res.parseStaticRoutes()
routes, err := parseStaticRoutes(res)
if err != nil {
return err
}
@ -215,13 +220,74 @@ func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinI
// Leave method is invoked when a Sandbox detaches from an endpoint.
func (d *driver) Leave(nid, eid types.UUID) error {
leave := &leaveRequest{
leave := &api.LeaveRequest{
NetworkID: string(nid),
EndpointID: string(eid),
}
return d.call("Leave", leave, &leaveResponse{})
return d.call("Leave", leave, &api.LeaveResponse{})
}
func (d *driver) Type() string {
return d.networkType
}
func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) {
var routes = make([]*types.StaticRoute, len(r.StaticRoutes))
for i, inRoute := range r.StaticRoutes {
var err error
outRoute := &types.StaticRoute{InterfaceID: inRoute.InterfaceID, RouteType: inRoute.RouteType}
if inRoute.Destination != "" {
if outRoute.Destination, err = toAddr(inRoute.Destination); err != nil {
return nil, err
}
}
if inRoute.NextHop != "" {
outRoute.NextHop = net.ParseIP(inRoute.NextHop)
if outRoute.NextHop == nil {
return nil, fmt.Errorf("failed to parse nexthop IP %s", inRoute.NextHop)
}
}
routes[i] = outRoute
}
return routes, nil
}
// parseInterfaces validates all the parameters of an Interface and returns them.
func parseInterfaces(r api.CreateEndpointResponse) ([]*api.Interface, error) {
var (
Interfaces = make([]*api.Interface, len(r.Interfaces))
)
for i, inIf := range r.Interfaces {
var err error
outIf := &api.Interface{ID: inIf.ID}
if inIf.Address != "" {
if outIf.Address, err = toAddr(inIf.Address); err != nil {
return nil, err
}
}
if inIf.AddressIPv6 != "" {
if outIf.AddressIPv6, err = toAddr(inIf.AddressIPv6); err != nil {
return nil, err
}
}
if inIf.MacAddress != "" {
if outIf.MacAddress, err = net.ParseMAC(inIf.MacAddress); err != nil {
return nil, err
}
}
Interfaces[i] = outIf
}
return Interfaces, nil
}
func toAddr(ipAddr string) (*net.IPNet, error) {
ip, ipnet, err := net.ParseCIDR(ipAddr)
if err != nil {
return nil, err
}
ipnet.IP = ip
return ipnet, nil
}

Просмотреть файл

@ -1,178 +0,0 @@
package remote
import (
"fmt"
"net"
"github.com/docker/libnetwork/types"
)
type response struct {
Err string
}
type maybeError interface {
getError() string
}
func (r *response) getError() string {
return r.Err
}
type createNetworkRequest struct {
NetworkID string
Options map[string]interface{}
}
type createNetworkResponse struct {
response
}
type deleteNetworkRequest struct {
NetworkID string
}
type deleteNetworkResponse struct {
response
}
type createEndpointRequest struct {
NetworkID string
EndpointID string
Interfaces []*endpointInterface
Options map[string]interface{}
}
type endpointInterface struct {
ID int
Address string
AddressIPv6 string
MacAddress string
}
type staticRoute struct {
Destination string
RouteType int
NextHop string
InterfaceID int
}
type createEndpointResponse struct {
response
Interfaces []*endpointInterface
}
func toAddr(ipAddr string) (*net.IPNet, error) {
ip, ipnet, err := net.ParseCIDR(ipAddr)
if err != nil {
return nil, err
}
ipnet.IP = ip
return ipnet, nil
}
type iface struct {
ID int
Address *net.IPNet
AddressIPv6 *net.IPNet
MacAddress net.HardwareAddr
}
func (r *createEndpointResponse) parseInterfaces() ([]*iface, error) {
var ifaces = make([]*iface, len(r.Interfaces))
for i, inIf := range r.Interfaces {
var err error
outIf := &iface{ID: inIf.ID}
if inIf.Address != "" {
if outIf.Address, err = toAddr(inIf.Address); err != nil {
return nil, err
}
}
if inIf.AddressIPv6 != "" {
if outIf.AddressIPv6, err = toAddr(inIf.AddressIPv6); err != nil {
return nil, err
}
}
if inIf.MacAddress != "" {
if outIf.MacAddress, err = net.ParseMAC(inIf.MacAddress); err != nil {
return nil, err
}
}
ifaces[i] = outIf
}
return ifaces, nil
}
func (r *joinResponse) parseStaticRoutes() ([]*types.StaticRoute, error) {
var routes = make([]*types.StaticRoute, len(r.StaticRoutes))
for i, inRoute := range r.StaticRoutes {
var err error
outRoute := &types.StaticRoute{InterfaceID: inRoute.InterfaceID, RouteType: inRoute.RouteType}
if inRoute.Destination != "" {
if outRoute.Destination, err = toAddr(inRoute.Destination); err != nil {
return nil, err
}
}
if inRoute.NextHop != "" {
outRoute.NextHop = net.ParseIP(inRoute.NextHop)
if outRoute.NextHop == nil {
return nil, fmt.Errorf("failed to parse nexthop IP %s", inRoute.NextHop)
}
}
routes[i] = outRoute
}
return routes, nil
}
type deleteEndpointRequest struct {
NetworkID string
EndpointID string
}
type deleteEndpointResponse struct {
response
}
type endpointInfoRequest struct {
NetworkID string
EndpointID string
}
type endpointInfoResponse struct {
response
Value map[string]interface{}
}
type joinRequest struct {
NetworkID string
EndpointID string
SandboxKey string
Options map[string]interface{}
}
type ifaceName struct {
SrcName string
DstPrefix string
}
type joinResponse struct {
response
InterfaceNames []*ifaceName
Gateway string
GatewayIPv6 string
StaticRoutes []*staticRoute
HostsPath string
ResolvConfPath string
}
type leaveRequest struct {
NetworkID string
EndpointID string
}
type leaveResponse struct {
response
}

Просмотреть файл

@ -297,12 +297,7 @@ func (ep *endpoint) processOptions(options ...EndpointOption) {
}
func createBasePath(dir string) error {
err := os.MkdirAll(dir, 0644)
if err != nil && !os.IsExist(err) {
return err
}
return nil
return os.MkdirAll(dir, 0644)
}
func createFile(path string) error {

Просмотреть файл

@ -42,10 +42,9 @@ var (
ErrIptablesNotFound = errors.New("Iptables not found")
)
// Chain defines the iptables chain.
type Chain struct {
// ChainInfo defines the iptables chain.
type ChainInfo struct {
Name string
Bridge string
Table Table
HairpinMode bool
}
@ -74,14 +73,12 @@ func initCheck() error {
}
// NewChain adds a new chain to ip table.
func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error) {
c := &Chain{
func NewChain(name string, table Table, hairpinMode bool) (*ChainInfo, error) {
c := &ChainInfo{
Name: name,
Bridge: bridge,
Table: table,
HairpinMode: hairpinMode,
}
if string(c.Table) == "" {
c.Table = Filter
}
@ -94,8 +91,16 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output)
}
}
return c, nil
}
switch table {
// ProgramChain is used to add rules to a chain
func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode bool) error {
if c.Name == "" {
return fmt.Errorf("Could not program chain, missing chain name.")
}
switch c.Table {
case Nat:
preroute := []string{
"-m", "addrtype",
@ -103,7 +108,7 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
"-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)
return fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
}
}
output := []string{
@ -115,28 +120,32 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
}
if !Exists(Nat, "OUTPUT", output...) {
if err := c.Output(Append, output...); err != nil {
return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
return fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
}
}
case Filter:
if bridgeName == "" {
return fmt.Errorf("Could not program chain %s/%s, missing bridge name.",
c.Table, c.Name)
}
link := []string{
"-o", c.Bridge,
"-o", bridgeName,
"-j", c.Name}
if !Exists(Filter, "FORWARD", link...) {
insert := append([]string{string(Insert), "FORWARD"}, link...)
if output, err := Raw(insert...); err != nil {
return nil, err
return err
} else if len(output) != 0 {
return nil, fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output)
return fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output)
}
}
}
return c, nil
return nil
}
// RemoveExistingChain removes existing chain from the table.
func RemoveExistingChain(name string, table Table) error {
c := &Chain{
c := &ChainInfo{
Name: name,
Table: table,
}
@ -147,7 +156,7 @@ func RemoveExistingChain(name string, table Table) error {
}
// Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table.
func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int, bridgeName string) error {
daddr := ip.String()
if ip.IsUnspecified() {
// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
@ -162,7 +171,7 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri
"-j", "DNAT",
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))}
if !c.HairpinMode {
args = append(args, "!", "-i", c.Bridge)
args = append(args, "!", "-i", bridgeName)
}
if output, err := Raw(args...); err != nil {
return err
@ -171,8 +180,8 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri
}
if output, err := Raw("-t", string(Filter), string(action), c.Name,
"!", "-i", c.Bridge,
"-o", c.Bridge,
"!", "-i", bridgeName,
"-o", bridgeName,
"-p", proto,
"-d", destAddr,
"--dport", strconv.Itoa(destPort),
@ -198,9 +207,9 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri
// Link adds reciprocal ACCEPT rule for two supplied IP addresses.
// Traffic is allowed from ip1 to ip2 and vice-versa
func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error {
func (c *ChainInfo) Link(action Action, ip1, ip2 net.IP, port int, proto string, bridgeName string) error {
if output, err := Raw("-t", string(Filter), string(action), c.Name,
"-i", c.Bridge, "-o", c.Bridge,
"-i", bridgeName, "-o", bridgeName,
"-p", proto,
"-s", ip1.String(),
"-d", ip2.String(),
@ -211,7 +220,7 @@ func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) err
return fmt.Errorf("Error iptables forward: %s", output)
}
if output, err := Raw("-t", string(Filter), string(action), c.Name,
"-i", c.Bridge, "-o", c.Bridge,
"-i", bridgeName, "-o", bridgeName,
"-p", proto,
"-s", ip2.String(),
"-d", ip1.String(),
@ -225,7 +234,7 @@ func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) err
}
// Prerouting adds linking rule to nat/PREROUTING chain.
func (c *Chain) Prerouting(action Action, args ...string) error {
func (c *ChainInfo) Prerouting(action Action, args ...string) error {
a := []string{"-t", string(Nat), string(action), "PREROUTING"}
if len(args) > 0 {
a = append(a, args...)
@ -239,7 +248,7 @@ func (c *Chain) Prerouting(action Action, args ...string) error {
}
// Output adds linking rule to an OUTPUT chain.
func (c *Chain) Output(action Action, args ...string) error {
func (c *ChainInfo) Output(action Action, args ...string) error {
a := []string{"-t", string(c.Table), string(action), "OUTPUT"}
if len(args) > 0 {
a = append(a, args...)
@ -253,7 +262,7 @@ func (c *Chain) Output(action Action, args ...string) error {
}
// Remove removes the chain.
func (c *Chain) Remove() error {
func (c *ChainInfo) 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", "-j", c.Name)

Просмотреть файл

@ -122,26 +122,36 @@ func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) {
return addrs4[0], addrs6, nil
}
// GenerateRandomMAC returns a new 6-byte(48-bit) hardware address (MAC)
func GenerateRandomMAC() net.HardwareAddr {
func genMAC(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
// Randomly generate the remaining 4 bytes (2^32)
_, err := rand.Read(hw[2:])
if err != nil {
return nil
// Fill the remaining 4 bytes based on the input
if ip == nil {
rand.Read(hw[2:])
} else {
copy(hw[2:], ip.To4())
}
return hw
}
// GenerateRandomMAC returns a new 6-byte(48-bit) hardware address (MAC)
func GenerateRandomMAC() net.HardwareAddr {
return genMAC(nil)
}
// GenerateMACFromIP returns a locally administered MAC address where the 4 least
// significant bytes are derived from the IPv4 address.
func GenerateMACFromIP(ip net.IP) net.HardwareAddr {
return genMAC(ip)
}
// GenerateRandomName returns a new name joined with a prefix. This size
// specified is used to truncate the randomly generated value
func GenerateRandomName(prefix string, size int) (string, error) {

Просмотреть файл

@ -70,10 +70,15 @@ type (
Begin int
End int
}
portRange struct {
begin int
end int
last int
}
portMap struct {
p map[int]struct{}
begin, end int
last int
p map[int]struct{}
defaultRange string
portRanges map[string]*portRange
}
protoMap map[string]*portMap
)
@ -123,8 +128,17 @@ func getDynamicPortRange() (start int, end int, err error) {
// RequestPort requests new port from global ports pool for specified ip and proto.
// If port is 0 it returns first free port. Otherwise it checks port availability
// in pool and return that port or error if port is already busy.
// in proto's pool and returns that port or error if port is already busy.
func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) {
return p.RequestPortInRange(ip, proto, port, port)
}
// RequestPortInRange requests new port from global ports pool for specified ip and proto.
// If portStart and portEnd are 0 it returns the first free port in the default ephemeral range.
// If portStart != portEnd it returns the first free port in the requested range.
// Otherwise (portStart == portEnd) it checks port availability in the requested proto's port-pool
// and returns that port or error if port is already busy.
func (p *PortAllocator) RequestPortInRange(ip net.IP, proto string, portStart, portEnd int) (int, error) {
p.mutex.Lock()
defer p.mutex.Unlock()
@ -146,15 +160,15 @@ func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, err
p.ipMap[ipstr] = protomap
}
mapping := protomap[proto]
if port > 0 {
if _, ok := mapping.p[port]; !ok {
mapping.p[port] = struct{}{}
return port, nil
if portStart > 0 && portStart == portEnd {
if _, ok := mapping.p[portStart]; !ok {
mapping.p[portStart] = struct{}{}
return portStart, nil
}
return 0, newErrPortAlreadyAllocated(ipstr, port)
return 0, newErrPortAlreadyAllocated(ipstr, portStart)
}
port, err := mapping.findPort()
port, err := mapping.findPort(portStart, portEnd)
if err != nil {
return 0, err
}
@ -178,12 +192,15 @@ func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
}
func (p *PortAllocator) newPortMap() *portMap {
return &portMap{
p: map[int]struct{}{},
begin: p.Begin,
end: p.End,
last: p.End,
defaultKey := getRangeKey(p.Begin, p.End)
pm := &portMap{
p: map[int]struct{}{},
defaultRange: defaultKey,
portRanges: map[string]*portRange{
defaultKey: newPortRange(p.Begin, p.End),
},
}
return pm
}
// ReleaseAll releases all ports for all ips.
@ -194,17 +211,58 @@ func (p *PortAllocator) ReleaseAll() error {
return nil
}
func (pm *portMap) findPort() (int, error) {
port := pm.last
for i := 0; i <= pm.end-pm.begin; i++ {
func getRangeKey(portStart, portEnd int) string {
return fmt.Sprintf("%d-%d", portStart, portEnd)
}
func newPortRange(portStart, portEnd int) *portRange {
return &portRange{
begin: portStart,
end: portEnd,
last: portEnd,
}
}
func (pm *portMap) getPortRange(portStart, portEnd int) (*portRange, error) {
var key string
if portStart == 0 && portEnd == 0 {
key = pm.defaultRange
} else {
key = getRangeKey(portStart, portEnd)
if portStart == portEnd ||
portStart == 0 || portEnd == 0 ||
portEnd < portStart {
return nil, fmt.Errorf("invalid port range: %s", key)
}
}
// Return existing port range, if already known.
if pr, exists := pm.portRanges[key]; exists {
return pr, nil
}
// Otherwise create a new port range.
pr := newPortRange(portStart, portEnd)
pm.portRanges[key] = pr
return pr, nil
}
func (pm *portMap) findPort(portStart, portEnd int) (int, error) {
pr, err := pm.getPortRange(portStart, portEnd)
if err != nil {
return 0, err
}
port := pr.last
for i := 0; i <= pr.end-pr.begin; i++ {
port++
if port > pm.end {
port = pm.begin
if port > pr.end {
port = pr.begin
}
if _, ok := pm.p[port]; !ok {
pm.p[port] = struct{}{}
pm.last = port
pr.last = port
return port, nil
}
}

Просмотреть файл

@ -31,7 +31,8 @@ var (
// PortMapper manages the network address translation
type PortMapper struct {
chain *iptables.Chain
chain *iptables.ChainInfo
bridgeName string
// udp:ip:port
currentMappings map[string]*mapping
@ -54,12 +55,18 @@ func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper {
}
// SetIptablesChain sets the specified chain into portmapper
func (pm *PortMapper) SetIptablesChain(c *iptables.Chain) {
func (pm *PortMapper) SetIptablesChain(c *iptables.ChainInfo, bridgeName string) {
pm.chain = c
pm.bridgeName = bridgeName
}
// Map maps the specified container transport address to the host's network address and transport port
func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) {
return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy)
}
// MapRange maps the specified container transport address to the host's network address and transport port range
func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) {
pm.lock.Lock()
defer pm.lock.Unlock()
@ -72,7 +79,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
switch container.(type) {
case *net.TCPAddr:
proto = "tcp"
if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil {
if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
return nil, err
}
@ -89,7 +96,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
}
case *net.UDPAddr:
proto = "udp"
if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil {
if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
return nil, err
}
@ -215,5 +222,5 @@ func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net
if pm.chain == nil {
return nil
}
return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName)
}

Просмотреть файл

@ -48,7 +48,7 @@ func init() {
func createBasePath() {
err := os.MkdirAll(prefix, 0644)
if err != nil && !os.IsExist(err) {
if err != nil {
panic("Could not create net namespace path directory")
}

Просмотреть файл

@ -24,11 +24,12 @@ func (t *TransportPort) GetCopy() TransportPort {
// PortBinding represent a port binding between the container and the host
type PortBinding struct {
Proto Protocol
IP net.IP
Port uint16
HostIP net.IP
HostPort uint16
Proto Protocol
IP net.IP
Port uint16
HostIP net.IP
HostPort uint16
HostPortEnd uint16
}
// HostAddr returns the host side transport address
@ -58,11 +59,12 @@ func (p PortBinding) ContainerAddr() (net.Addr, error) {
// GetCopy returns a copy of this PortBinding structure instance
func (p *PortBinding) GetCopy() PortBinding {
return PortBinding{
Proto: p.Proto,
IP: GetIPCopy(p.IP),
Port: p.Port,
HostIP: GetIPCopy(p.HostIP),
HostPort: p.HostPort,
Proto: p.Proto,
IP: GetIPCopy(p.IP),
Port: p.Port,
HostIP: GetIPCopy(p.HostIP),
HostPort: p.HostPort,
HostPortEnd: p.HostPortEnd,
}
}
@ -76,7 +78,8 @@ func (p *PortBinding) Equal(o *PortBinding) bool {
return false
}
if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort {
if p.Proto != o.Proto || p.Port != o.Port ||
p.HostPort != o.HostPort || p.HostPortEnd != o.HostPortEnd {
return false
}

Просмотреть файл

@ -39,8 +39,9 @@ func NativeEndian() binary.ByteOrder {
var x uint32 = 0x01020304
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
nativeEndian = binary.BigEndian
} else {
nativeEndian = binary.LittleEndian
}
nativeEndian = binary.LittleEndian
}
return nativeEndian
}

Просмотреть файл

@ -20,6 +20,15 @@ func NewRtMsg() *RtMsg {
}
}
func NewRtDelMsg() *RtMsg {
return &RtMsg{
RtMsg: syscall.RtMsg{
Table: syscall.RT_TABLE_MAIN,
Scope: syscall.RT_SCOPE_NOWHERE,
},
}
}
func (msg *RtMsg) Len() int {
return syscall.SizeofRtMsg
}

Просмотреть файл

@ -14,22 +14,21 @@ import (
// Equivalent to: `ip route add $route`
func RouteAdd(route *Route) error {
req := nl.NewNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
return routeHandle(route, req)
return routeHandle(route, req, nl.NewRtMsg())
}
// RouteAdd will delete a route from the system.
// Equivalent to: `ip route del $route`
func RouteDel(route *Route) error {
req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK)
return routeHandle(route, req)
return routeHandle(route, req, nl.NewRtDelMsg())
}
func routeHandle(route *Route, req *nl.NetlinkRequest) error {
func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil {
return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
}
msg := nl.NewRtMsg()
msg.Scope = uint8(route.Scope)
family := -1
var rtAttrs []*nl.RtAttr