2015-02-20 01:21:42 +00:00
|
|
|
package bridge
|
|
|
|
|
2015-02-20 06:21:05 +00:00
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"github.com/docker/libnetwork"
|
|
|
|
)
|
2015-02-20 01:21:42 +00:00
|
|
|
|
2015-02-23 01:24:22 +00:00
|
|
|
const (
|
|
|
|
NetworkType = "simplebridge"
|
|
|
|
VethPrefix = "veth"
|
|
|
|
)
|
2015-02-20 01:21:42 +00:00
|
|
|
|
2015-02-23 01:24:22 +00:00
|
|
|
type Configuration struct {
|
|
|
|
BridgeName string
|
|
|
|
AddressIPv4 *net.IPNet
|
|
|
|
AddressIPv6 *net.IPNet
|
|
|
|
FixedCIDR string
|
|
|
|
FixedCIDRv6 string
|
|
|
|
EnableIPv6 bool
|
|
|
|
EnableIPTables bool
|
|
|
|
EnableIPForwarding bool
|
2015-02-20 01:21:42 +00:00
|
|
|
}
|
|
|
|
|
2015-02-20 06:21:05 +00:00
|
|
|
func init() {
|
2015-02-23 01:24:22 +00:00
|
|
|
libnetwork.RegisterNetworkType(NetworkType, Create, &Configuration{})
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
func Create()
|
|
|
|
|
|
|
|
- NewBridgeInterface(*Configuration) (*BridgeInterface, error)
|
|
|
|
. Issues LinkByName on config.BridgeName
|
|
|
|
- Create BridgeSetup instance with sequence of steps
|
|
|
|
- if !bridgeInterface.Exists()
|
|
|
|
. Add DeviceCreation (error if non-default name)
|
|
|
|
. Add AddressIPv4: set IPv4 (with automatic election if necessary)
|
|
|
|
- General case
|
|
|
|
. Add option EnableIPv6 if no IPv6 on bridge (disable_ipv6=0 + set IPv6)
|
|
|
|
. Verify configured addresses (with v4 and v6 updated in config)
|
|
|
|
. Add FixedCIDR v4: register subnet on IP Allocator
|
|
|
|
. Add FixedCIDR v6: register subnet on IP Allocator, route
|
|
|
|
- Add IPTables setup
|
|
|
|
- Add IPForward setup (depends on FixedCIDRv6)
|
|
|
|
- err := bridgeSetup.Apply()
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
func Create(config *Configuration) (libnetwork.Network, error) {
|
|
|
|
bridgeIntfc := NewInterface(config)
|
|
|
|
bridgeSetup := NewBridgeSetup(bridgeIntfc)
|
|
|
|
|
|
|
|
// If the bridge interface doesn't exist, we need to start the setup steps
|
|
|
|
// by creating a new device and assigning it an IPv4 address.
|
|
|
|
bridgeAlreadyExists := bridgeIntfc.Exists()
|
|
|
|
if !bridgeAlreadyExists {
|
|
|
|
bridgeSetup.QueueStep(SetupDevice)
|
|
|
|
bridgeSetup.QueueStep(SetupBridgeIPv4)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conditionnally queue setup steps depending on configuration values.
|
|
|
|
optSteps := []struct {
|
|
|
|
Condition bool
|
|
|
|
Fn SetupStep
|
|
|
|
}{
|
|
|
|
// Enable IPv6 on the bridge if required. We do this even for a
|
|
|
|
// previously existing bridge, as it may be here from a previous
|
|
|
|
// installation where IPv6 wasn't supported yet and needs to be
|
|
|
|
// assigned an IPv6 link-local address.
|
|
|
|
{config.EnableIPv6, SetupBridgeIPv6},
|
|
|
|
|
|
|
|
// We ensure that the bridge has the expectedIPv4 and IPv6 addresses in
|
|
|
|
// the case of a previously existing device.
|
|
|
|
{bridgeAlreadyExists, SetupVerifyConfiguredAddresses},
|
|
|
|
|
|
|
|
// Setup the bridge to allocate containers IPv4 addresses in the
|
|
|
|
// specified subnet.
|
|
|
|
{config.FixedCIDR != "", SetupFixedCIDRv4},
|
|
|
|
|
|
|
|
// Setup the bridge to allocate containers global IPv6 addresses in the
|
|
|
|
// specified subnet.
|
|
|
|
{config.FixedCIDRv6 != "", SetupFixedCIDRv6},
|
|
|
|
|
|
|
|
// Setup IPTables.
|
|
|
|
{config.EnableIPTables, SetupIPTables},
|
|
|
|
|
|
|
|
// Setup IP forwarding.
|
|
|
|
{config.EnableIPForwarding, SetupIPForwarding},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, step := range optSteps {
|
|
|
|
if step.Condition {
|
|
|
|
bridgeSetup.QueueStep(step.Fn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the prepared list of steps, and abort at the first error.
|
|
|
|
bridgeSetup.QueueStep(SetupDeviceUp)
|
|
|
|
if err := bridgeSetup.Apply(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &bridgeNetwork{*config}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
func Create(config *Configuration) (libnetwork.Network, error) {
|
|
|
|
var (
|
|
|
|
addrv4 netlink.Addr
|
|
|
|
addrsv6 []netlink.Addr
|
|
|
|
)
|
|
|
|
|
|
|
|
b := &bridgeNetwork{Config: *config}
|
|
|
|
if b.Config.BridgeName == "" {
|
|
|
|
b.Config.BridgeName = DefaultBridge
|
|
|
|
}
|
|
|
|
|
|
|
|
link, err := netlink.LinkByName(b.Config.BridgeName)
|
|
|
|
if err != nil {
|
|
|
|
// The bridge interface doesn't exist, but we only attempt to create it
|
|
|
|
// if using the default name.
|
|
|
|
if b.Config.BridgeName != DefaultBridge {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the bridge interface.
|
|
|
|
if addrv4, addrsv6, err = createBridge(&b.Config); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// The bridge interface exists: start by getting its configured
|
|
|
|
// addresses and verify if it matches the requested configuration.
|
|
|
|
addrv4, addrsv6, err = getInterfaceAddr(link)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.Config.AddressIPv4 != "" {
|
|
|
|
bridgeIP, _, err := net.ParseCIDR(b.Config.AddressIPv4)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !addrv4.IP.Equal(bridgeIP) {
|
|
|
|
return nil, fmt.Errorf("Bridge IP %s does not match requested configuration %s", addrv4.IP, bridgeIP)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A bridge might exist but not have any IPv6 addr associated with it
|
|
|
|
// yet (for example, an existing Docker installation that has only been
|
|
|
|
// used with IPv4 and docker0 already is set up). In that case, we can
|
|
|
|
// perform the bridge init for IPv6 here, else we will error out below
|
|
|
|
// if --ipv6=true.
|
|
|
|
if len(addrsv6) == 0 && config.EnableIPv6 {
|
|
|
|
if err := setupIPv6Bridge(iface, config); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return b, nil
|
2015-02-20 01:21:42 +00:00
|
|
|
}
|
2015-02-23 01:24:22 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
func createBridge(config *Configuration) (netlink.Addr, []netlink.Addr, error) {
|
|
|
|
// Formats an error return with default values.
|
|
|
|
fmtError := func(format string, params ...interface{}) (netlink.Addr, []netlink.Addr, error) {
|
|
|
|
return netlink.Addr{}, nil, fmt.Errorf(format, params...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Elect a subnet for the bridge interface.
|
|
|
|
bridgeIPNet, err := electBridgeNetwork(config)
|
|
|
|
if err != nil {
|
|
|
|
return fmtError("Failed to elect bridge network: %v", err)
|
|
|
|
}
|
|
|
|
log.Debugf("Creating bridge interface %q with network %s", config.BridgeName, bridgeIPNet)
|
|
|
|
|
|
|
|
// We attempt to create the bridge, and ignore the returned error if it is
|
|
|
|
// already existing.
|
|
|
|
iface, err := createBridgeInterface(config.BridgeName)
|
|
|
|
if err != nil && !os.IsExist(err) {
|
|
|
|
return netlink.Addr{}, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Configure bridge IPv4.
|
|
|
|
if err := netlink.AddrAdd(iface, &netlink.Addr{bridgeIPNet, ""}); err != nil {
|
|
|
|
return fmtError("Failed to add address %s to bridge: %v", bridgeIPNet, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Configure bridge IPv6.
|
|
|
|
if config.EnableIPv6 {
|
|
|
|
if err := setupIPv6Bridge(iface, config); err != nil {
|
|
|
|
return fmtError("Failed to setup bridge IPv6: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Up the bridge interface.
|
|
|
|
if err := netlink.LinkSetUp(iface); err != nil {
|
|
|
|
return fmtError("Failed to up network bridge: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.FixedCIDRv6 != "" {
|
|
|
|
dest, network, err := net.ParseCIDR(config.FixedCIDRv6)
|
|
|
|
if err != nil {
|
|
|
|
return fmtError("Invalid bridge fixed CIDR IPv6 %q: %v", config.FixedCIDRv6, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set route to global IPv6 subnet
|
|
|
|
log.Infof("Adding route to IPv6 network %q via device %q", dest, iface)
|
|
|
|
if err := netlink.RouteAdd(&netlink.Route{Dst: network, LinkIndex: iface.Attrs().Index}); err != nil {
|
|
|
|
return fmtError("Could not add route to IPv6 network %q via device %q", config.FixedCIDRv6, iface)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return getInterfaceAddrByName(config.BridgeName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkBridgeConfig(iface netlink.Link, config *Configuration) (netlink.Addr, []netlink.Addr, error) {
|
|
|
|
addrv4, addrsv6, err := getInterfaceAddr(iface)
|
|
|
|
if err != nil {
|
|
|
|
return netlink.Addr{}, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If config dictates a specific IP for the bridge, we have to check if it
|
|
|
|
// corresponds to reality.
|
|
|
|
if config.AddressIPv4 != "" {
|
|
|
|
bridgeIP, _, err := net.ParseCIDR(config.AddressIPv4)
|
|
|
|
if err != nil {
|
|
|
|
return netlink.Addr{}, nil, err
|
|
|
|
}
|
|
|
|
if !addrv4.IP.Equal(bridgeIP) {
|
|
|
|
return netlink.Addr{}, nil, fmt.Errorf("bridge ip (%s) does not match existing configuration %s", addrv4.IP, bridgeIP)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return addrv4, addrsv6, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func setupIPv6Bridge(iface netlink.Link, config *Configuration) error {
|
|
|
|
procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6"
|
|
|
|
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
|
|
|
|
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ip, net, err := net.ParseCIDR(config.AddressIPv6)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Invalid bridge IPv6 address %q: %v", config.AddressIPv6, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
net.IP = ip
|
|
|
|
if err := netlink.AddrAdd(iface, &netlink.Addr{net, ""}); err != nil {
|
|
|
|
return fmt.Errorf("Failed to add address %s to bridge: %v", net, err)
|
|
|
|
}
|
2015-02-20 01:21:42 +00:00
|
|
|
|
2015-02-23 01:24:22 +00:00
|
|
|
return nil
|
2015-02-20 01:21:42 +00:00
|
|
|
}
|
2015-02-23 01:24:22 +00:00
|
|
|
*/
|
2015-02-20 01:21:42 +00:00
|
|
|
|
2015-02-20 06:21:05 +00:00
|
|
|
type bridgeNetwork struct {
|
2015-02-23 01:24:22 +00:00
|
|
|
Config Configuration
|
2015-02-20 01:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *bridgeNetwork) Type() string {
|
2015-02-23 01:24:22 +00:00
|
|
|
return NetworkType
|
2015-02-20 01:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|