1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/libnetwork/drivers/bridge/bridge.go
Jana Radhakrishnan e91ed59cb2 Changed all the naked error returns in bridge driver to proper error
types, except the naked error returns which were just prefixing
strings to previously returned error strings.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-04-17 02:50:22 +00:00

400 lines
8.2 KiB
Go

package bridge
import (
"net"
"strings"
"sync"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipallocator"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/pkg/options"
"github.com/docker/libnetwork/portmapper"
"github.com/vishvananda/netlink"
)
const (
networkType = "simplebridge"
vethPrefix = "veth"
vethLen = 7
containerVeth = "eth0"
)
var (
ipAllocator *ipallocator.IPAllocator
portMapper *portmapper.PortMapper
)
// Configuration info for the "simplebridge" driver.
type Configuration struct {
BridgeName string
AddressIPv4 *net.IPNet
FixedCIDR *net.IPNet
FixedCIDRv6 *net.IPNet
EnableIPv6 bool
EnableIPTables bool
EnableIPMasquerade bool
EnableICC bool
EnableIPForwarding bool
AllowNonDefaultBridge bool
}
type bridgeEndpoint struct {
id driverapi.UUID
addressIPv4 net.IP
addressIPv6 net.IP
}
type bridgeNetwork struct {
id driverapi.UUID
// bridge interface points to the linux bridge and it's configuration
bridge *bridgeInterface
endpoint *bridgeEndpoint
sync.Mutex
}
type driver struct {
config *Configuration
network *bridgeNetwork
sync.Mutex
}
func init() {
ipAllocator = ipallocator.New()
portMapper = portmapper.New()
}
// New provides a new instance of bridge driver instance
func New() (string, driverapi.Driver) {
return networkType, &driver{}
}
func (d *driver) Config(option interface{}) error {
var config *Configuration
d.Lock()
defer d.Unlock()
if d.config != nil {
return ErrConfigExists
}
switch opt := option.(type) {
case options.Generic:
opaqueConfig, err := options.GenerateFromModel(opt, &Configuration{})
if err != nil {
return err
}
config = opaqueConfig.(*Configuration)
case *Configuration:
config = opt
}
d.config = config
return nil
}
// Create a new network using simplebridge plugin
func (d *driver) CreateNetwork(id driverapi.UUID, option interface{}) error {
var (
err error
)
d.Lock()
if d.config == nil {
d.Unlock()
return ErrInvalidConfig
}
config := d.config
if d.network != nil {
d.Unlock()
return ErrNetworkExists
}
d.network = &bridgeNetwork{id: id}
d.Unlock()
defer func() {
// On failure make sure to reset d.network to nil
if err != nil {
d.Lock()
d.network = nil
d.Unlock()
}
}()
bridgeIface := newInterface(config)
bridgeSetup := newBridgeSetup(config, bridgeIface)
// 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 := bridgeIface.exists()
if !bridgeAlreadyExists {
bridgeSetup.queueStep(setupDevice)
bridgeSetup.queueStep(setupBridgeIPv4)
}
// Conditionnally queue setup steps depending on configuration values.
for _, step := range []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, setupVerifyAndReconcile},
// Setup the bridge to allocate containers IPv4 addresses in the
// specified subnet.
{config.FixedCIDR != nil, setupFixedCIDRv4},
// Setup the bridge to allocate containers global IPv6 addresses in the
// specified subnet.
{config.FixedCIDRv6 != nil, setupFixedCIDRv6},
// Setup IPTables.
{config.EnableIPTables, setupIPTables},
// Setup IP forwarding.
{config.EnableIPForwarding, setupIPForwarding},
} {
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 err
}
d.network.bridge = bridgeIface
return nil
}
func (d *driver) DeleteNetwork(nid driverapi.UUID) error {
var err error
d.Lock()
n := d.network
d.network = nil
d.Unlock()
defer func() {
if err != nil {
// On failure set d.network back to n
// but only if is not already take over
// by some other thread
d.Lock()
if d.network == nil {
d.network = n
}
d.Unlock()
}
}()
if n == nil {
err = driverapi.ErrNoNetwork
return err
}
if n.endpoint != nil {
err = &ActiveEndpointsError{nid: string(n.id), eid: string(n.endpoint.id)}
return err
}
err = netlink.LinkDel(n.bridge.Link)
return err
}
func (d *driver) CreateEndpoint(nid, eid driverapi.UUID, sboxKey string, epOption interface{}) (*driverapi.SandboxInfo, error) {
var (
ipv6Addr net.IPNet
err error
)
d.Lock()
n := d.network
config := d.config
d.Unlock()
if n == nil {
return nil, driverapi.ErrNoNetwork
}
n.Lock()
if n.id != nid {
n.Unlock()
return nil, InvalidNetworkIDError(nid)
}
if n.endpoint != nil {
n.Unlock()
return nil, driverapi.ErrEndpointExists
}
n.endpoint = &bridgeEndpoint{id: eid}
n.Unlock()
defer func() {
// On failye make sure to reset n.endpoint to nil
if err != nil {
n.Lock()
n.endpoint = nil
n.Unlock()
}
}()
name1, err := generateIfaceName()
if err != nil {
return nil, err
}
name2, err := generateIfaceName()
if err != nil {
return nil, err
}
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
PeerName: name2}
if err = netlink.LinkAdd(veth); err != nil {
return nil, err
}
host, err := netlink.LinkByName(name1)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
netlink.LinkDel(host)
}
}()
container, err := netlink.LinkByName(name2)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
netlink.LinkDel(container)
}
}()
if err = netlink.LinkSetMaster(host,
&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
return nil, err
}
ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
if err != nil {
return nil, err
}
ipv4Addr := net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
if config.EnableIPv6 {
ip6, err := ipAllocator.RequestIP(n.bridge.bridgeIPv6, nil)
if err != nil {
return nil, err
}
ipv6Addr = net.IPNet{IP: ip6, Mask: n.bridge.bridgeIPv6.Mask}
}
var interfaces []*driverapi.Interface
sinfo := &driverapi.SandboxInfo{}
intf := &driverapi.Interface{}
intf.SrcName = name2
intf.DstName = containerVeth
intf.Address = ipv4Addr
sinfo.Gateway = n.bridge.bridgeIPv4.IP
if config.EnableIPv6 {
intf.AddressIPv6 = ipv6Addr
sinfo.GatewayIPv6 = n.bridge.bridgeIPv6.IP
}
n.endpoint.addressIPv4 = ip4
n.endpoint.addressIPv6 = ipv6Addr.IP
interfaces = append(interfaces, intf)
sinfo.Interfaces = interfaces
return sinfo, nil
}
func (d *driver) DeleteEndpoint(nid, eid driverapi.UUID) error {
var err error
d.Lock()
n := d.network
config := d.config
d.Unlock()
if n == nil {
return driverapi.ErrNoNetwork
}
n.Lock()
if n.id != nid {
n.Unlock()
return InvalidNetworkIDError(nid)
}
if n.endpoint == nil {
n.Unlock()
return driverapi.ErrNoEndpoint
}
ep := n.endpoint
if ep.id != eid {
n.Unlock()
return InvalidEndpointIDError(eid)
}
n.endpoint = nil
n.Unlock()
defer func() {
if err != nil {
// On failure make to set back n.endpoint with ep
// but only if it hasn't been taken over
// already by some other thread.
n.Lock()
if n.endpoint == nil {
n.endpoint = ep
}
n.Unlock()
}
}()
err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.addressIPv4)
if err != nil {
return err
}
if config.EnableIPv6 {
err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, ep.addressIPv6)
if err != nil {
return err
}
}
return nil
}
// Generates a name to be used for a virtual ethernet
// interface. The name is constructed by 'veth' appended
// by a randomly generated hex value. (example: veth0f60e2c)
func generateIfaceName() (string, error) {
for i := 0; i < 3; i++ {
name, err := netutils.GenerateRandomName(vethPrefix, vethLen)
if err != nil {
continue
}
if _, err := net.InterfaceByName(name); err != nil {
if strings.Contains(err.Error(), "no such") {
return name, nil
}
return "", err
}
}
return "", ErrIfaceName
}