|
@@ -6,26 +6,263 @@ import (
|
|
|
"github.com/docker/libnetwork"
|
|
|
)
|
|
|
|
|
|
-const networkType = "bridgednetwork"
|
|
|
+const (
|
|
|
+ NetworkType = "simplebridge"
|
|
|
+ VethPrefix = "veth"
|
|
|
+)
|
|
|
|
|
|
-type bridgeConfiguration struct {
|
|
|
- Subnet net.IPNet
|
|
|
+type Configuration struct {
|
|
|
+ BridgeName string
|
|
|
+ AddressIPv4 *net.IPNet
|
|
|
+ AddressIPv6 *net.IPNet
|
|
|
+ FixedCIDR string
|
|
|
+ FixedCIDRv6 string
|
|
|
+ EnableIPv6 bool
|
|
|
+ EnableIPTables bool
|
|
|
+ EnableIPForwarding bool
|
|
|
}
|
|
|
|
|
|
func init() {
|
|
|
- libnetwork.RegisterNetworkType(networkType, Create, &bridgeConfiguration{})
|
|
|
+ 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
|
|
|
}
|
|
|
+*/
|
|
|
+
|
|
|
+/*
|
|
|
+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)
|
|
|
+ }
|
|
|
|
|
|
-func Create(config *bridgeConfiguration) (libnetwork.Network, error) {
|
|
|
- return &bridgeNetwork{Config: *config}, nil
|
|
|
+ return nil
|
|
|
}
|
|
|
+*/
|
|
|
|
|
|
type bridgeNetwork struct {
|
|
|
- Config bridgeConfiguration
|
|
|
+ Config Configuration
|
|
|
}
|
|
|
|
|
|
func (b *bridgeNetwork) Type() string {
|
|
|
- return networkType
|
|
|
+ return NetworkType
|
|
|
}
|
|
|
|
|
|
func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
|