123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- 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/docker/libnetwork/sandbox"
- "github.com/docker/libnetwork/types"
- "github.com/vishvananda/netlink"
- )
- const (
- networkType = "bridge"
- vethPrefix = "veth"
- vethLen = 7
- containerVeth = "eth0"
- )
- var (
- ipAllocator *ipallocator.IPAllocator
- portMapper *portmapper.PortMapper
- )
- // Configuration info for the "bridge" 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
- Mtu int
- }
- // EndpointConfiguration represents the user specified configuration for the sandbox endpoint
- type EndpointConfiguration struct {
- MacAddress net.HardwareAddr
- }
- type bridgeEndpoint struct {
- id types.UUID
- port *sandbox.Interface
- config *EndpointConfiguration // User specified parameters
- }
- type bridgeNetwork struct {
- id types.UUID
- bridge *bridgeInterface // The bridge's L3 interface
- endpoints map[string]*bridgeEndpoint // key: sandbox id
- 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
- func New() (string, driverapi.Driver) {
- return networkType, &driver{}
- }
- func (n *bridgeNetwork) getEndpoint(eid types.UUID) (string, *bridgeEndpoint, error) {
- n.Lock()
- defer n.Unlock()
- if eid == "" {
- return "", nil, InvalidEndpointIDError(eid)
- }
- for sk, ep := range n.endpoints {
- if ep.id == eid {
- return sk, ep, nil
- }
- }
- return "", nil, nil
- }
- 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 bridge plugin
- func (d *driver) CreateNetwork(id types.UUID, option interface{}) error {
- var err error
- // Driver must be configured
- d.Lock()
- if d.config == nil {
- d.Unlock()
- return ErrInvalidConfig
- }
- config := d.config
- // Sanity checks
- if d.network != nil {
- d.Unlock()
- return ErrNetworkExists
- }
- // Create and set network handler in driver
- d.network = &bridgeNetwork{id: id, endpoints: make(map[string]*bridgeEndpoint)}
- d.Unlock()
- // On failure make sure to reset driver network handler to nil
- defer func() {
- if err != nil {
- d.Lock()
- d.network = nil
- d.Unlock()
- }
- }()
- // Create or retrieve the bridge L3 interface
- bridgeIface := newInterface(config)
- d.network.bridge = bridgeIface
- // Prepare the bridge setup configuration
- 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
- }
- return nil
- }
- func (d *driver) DeleteNetwork(nid types.UUID) error {
- var err error
- // Get network handler and remove it from driver
- d.Lock()
- n := d.network
- d.network = nil
- d.Unlock()
- // On failure set network handler back in driver, but
- // only if is not already taken over by some other thread
- defer func() {
- if err != nil {
- d.Lock()
- if d.network == nil {
- d.network = n
- }
- d.Unlock()
- }
- }()
- // Sanity check
- if n == nil {
- err = driverapi.ErrNoNetwork
- return err
- }
- // Cannot remove network if endpoints are still present
- if len(n.endpoints) != 0 {
- err = ActiveEndpointsError(n.id)
- return err
- }
- // Programming
- err = netlink.LinkDel(n.bridge.Link)
- return err
- }
- func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions interface{}) (*sandbox.Info, error) {
- var (
- ipv6Addr *net.IPNet
- err error
- )
- // Get the network handler and make sure it exists
- d.Lock()
- n := d.network
- config := d.config
- d.Unlock()
- if n == nil {
- return nil, driverapi.ErrNoNetwork
- }
- // Sanity check
- n.Lock()
- if n.id != nid {
- n.Unlock()
- return nil, InvalidNetworkIDError(nid)
- }
- n.Unlock()
- // Check if endpoint id is good and retrieve correspondent endpoint
- _, ep, err := n.getEndpoint(eid)
- if err != nil {
- return nil, err
- }
- // Endpoint with that id exists either on desired or other sandbox
- if ep != nil {
- return nil, driverapi.ErrEndpointExists
- }
- // Check if valid sandbox key
- if sboxKey == "" {
- return nil, InvalidSandboxIDError(sboxKey)
- }
- // Check if endpoint already exists for this sandbox
- n.Lock()
- if _, ok := n.endpoints[sboxKey]; ok {
- n.Unlock()
- return nil, driverapi.ErrEndpointExists
- }
- // Try to convert the options to endpoint configuration
- epConfig, err := parseEndpointOptions(epOptions)
- if err != nil {
- return nil, err
- }
- // Create and add the endpoint
- endpoint := &bridgeEndpoint{id: eid, config: epConfig}
- n.endpoints[sboxKey] = endpoint
- n.Unlock()
- // On failure make sure to remove the endpoint
- defer func() {
- if err != nil {
- n.Lock()
- delete(n.endpoints, sboxKey)
- n.Unlock()
- }
- }()
- // Generate a name for what will be the host side pipe interface
- name1, err := generateIfaceName()
- if err != nil {
- return nil, err
- }
- // Generate a name for what will be the sandbox side pipe interface
- name2, err := generateIfaceName()
- if err != nil {
- return nil, err
- }
- // Generate and add the interface pipe host <-> sandbox
- veth := &netlink.Veth{
- LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
- PeerName: name2}
- if err = netlink.LinkAdd(veth); err != nil {
- return nil, err
- }
- // Get the host side pipe interface handler
- host, err := netlink.LinkByName(name1)
- if err != nil {
- return nil, err
- }
- defer func() {
- if err != nil {
- netlink.LinkDel(host)
- }
- }()
- // Get the sandbox side pipe interface handler
- sbox, err := netlink.LinkByName(name2)
- if err != nil {
- return nil, err
- }
- defer func() {
- if err != nil {
- netlink.LinkDel(sbox)
- }
- }()
- // Add user specified attributes
- if epConfig != nil && epConfig.MacAddress != nil {
- err = netlink.LinkSetHardwareAddr(sbox, epConfig.MacAddress)
- if err != nil {
- return nil, err
- }
- }
- // Add bridge inherited attributes to pipe interfaces
- if config.Mtu != 0 {
- err = netlink.LinkSetMTU(host, config.Mtu)
- if err != nil {
- return nil, err
- }
- err = netlink.LinkSetMTU(sbox, config.Mtu)
- if err != nil {
- return nil, err
- }
- }
- // Attach host side pipe interface into the bridge
- if err = netlink.LinkSetMaster(host,
- &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
- return nil, err
- }
- // v4 address for the sandbox side pipe interface
- ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
- if err != nil {
- return nil, err
- }
- ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
- // v6 address for the sandbox side pipe interface
- 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}
- }
- // Store the sandbox side pipe interface
- // This is needed for cleanup on DeleteEndpoint()
- intf := &sandbox.Interface{}
- intf.SrcName = name2
- intf.DstName = containerVeth
- intf.Address = ipv4Addr
- // Update endpoint with the sandbox interface info
- endpoint.port = intf
- // Generate the sandbox info to return
- sinfo := &sandbox.Info{Interfaces: []*sandbox.Interface{intf}}
- sinfo.Gateway = n.bridge.bridgeIPv4.IP
- if config.EnableIPv6 {
- intf.AddressIPv6 = ipv6Addr
- sinfo.GatewayIPv6 = n.bridge.bridgeIPv6.IP
- }
- return sinfo, nil
- }
- func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
- var err error
- // Get the network handler and make sure it exists
- d.Lock()
- n := d.network
- config := d.config
- d.Unlock()
- if n == nil {
- return driverapi.ErrNoNetwork
- }
- // Sanity Check
- n.Lock()
- if n.id != nid {
- n.Unlock()
- return InvalidNetworkIDError(nid)
- }
- n.Unlock()
- // Check endpoint id and if an endpoint is actually there
- sboxKey, ep, err := n.getEndpoint(eid)
- if err != nil {
- return err
- }
- if ep == nil {
- return EndpointNotFoundError(eid)
- }
- // Remove it
- n.Lock()
- delete(n.endpoints, sboxKey)
- n.Unlock()
- // On failure make sure to set back ep in n.endpoints, but only
- // if it hasn't been taken over already by some other thread.
- defer func() {
- if err != nil {
- n.Lock()
- if _, ok := n.endpoints[sboxKey]; !ok {
- n.endpoints[sboxKey] = ep
- }
- n.Unlock()
- }
- }()
- // Release the v4 address allocated to this endpoint's sandbox interface
- err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.port.Address.IP)
- if err != nil {
- return err
- }
- // Release the v6 address allocated to this endpoint's sandbox interface
- if config.EnableIPv6 {
- err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, ep.port.AddressIPv6.IP)
- if err != nil {
- return err
- }
- }
- // Try removal of link. Discard error: link pair might have
- // already been deleted by sandbox delete.
- link, err := netlink.LinkByName(ep.port.SrcName)
- if err == nil {
- netlink.LinkDel(link)
- }
- return nil
- }
- func parseEndpointOptions(epOptions interface{}) (*EndpointConfiguration, error) {
- if epOptions == nil {
- return nil, nil
- }
- switch opt := epOptions.(type) {
- case options.Generic:
- opaqueConfig, err := options.GenerateFromModel(opt, &EndpointConfiguration{})
- if err != nil {
- return nil, err
- }
- return opaqueConfig.(*EndpointConfiguration), nil
- case *EndpointConfiguration:
- return opt, nil
- default:
- return nil, ErrInvalidEndpointConfig
- }
- }
- // 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
- }
|