dotcloud/docker

View on GitHub
libnetwork/drivers/windows/overlay/ov_network_windows.go

Summary

Maintainability
C
7 hrs
Test Coverage
package overlay

import (
    "context"
    "encoding/json"
    "fmt"
    "net"
    "strconv"
    "strings"
    "sync"

    "github.com/Microsoft/hcsshim"
    "github.com/containerd/log"
    "github.com/docker/docker/libnetwork/driverapi"
    "github.com/docker/docker/libnetwork/netlabel"
    "github.com/docker/docker/libnetwork/portmapper"
    "github.com/docker/docker/libnetwork/types"
)

var (
    hostMode  bool
    networkMu sync.Mutex
)

type networkTable map[string]*network

type subnet struct {
    vni      uint32
    subnetIP *net.IPNet
    gwIP     *net.IP
}

type subnetJSON struct {
    SubnetIP string
    GwIP     string
    Vni      uint32
}

type network struct {
    id              string
    name            string
    hnsID           string
    providerAddress string
    interfaceName   string
    endpoints       endpointTable
    driver          *driver
    initEpoch       int
    initErr         error
    subnets         []*subnet
    secure          bool
    portMapper      *portmapper.PortMapper
    sync.Mutex
}

func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
    return nil, types.NotImplementedErrorf("not implemented")
}

func (d *driver) NetworkFree(id string) error {
    return types.NotImplementedErrorf("not implemented")
}

func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
    var (
        networkName   string
        interfaceName string
        staleNetworks []string
    )

    if id == "" {
        return fmt.Errorf("invalid network id")
    }

    if nInfo == nil {
        return fmt.Errorf("invalid network info structure")
    }

    if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
        return types.InvalidParameterErrorf("ipv4 pool is empty")
    }

    staleNetworks = make([]string, 0)
    vnis := make([]uint32, 0, len(ipV4Data))

    existingNetwork := d.network(id)
    if existingNetwork != nil {
        log.G(context.TODO()).Debugf("Network preexists. Deleting %s", id)
        err := d.DeleteNetwork(id)
        if err != nil {
            log.G(context.TODO()).Errorf("Error deleting stale network %s", err.Error())
        }
    }

    n := &network{
        id:         id,
        driver:     d,
        endpoints:  endpointTable{},
        subnets:    []*subnet{},
        portMapper: portmapper.New(),
    }

    genData, ok := option[netlabel.GenericData].(map[string]string)

    if !ok {
        return fmt.Errorf("Unknown generic data option")
    }

    for label, value := range genData {
        switch label {
        case "com.docker.network.windowsshim.networkname":
            networkName = value
        case "com.docker.network.windowsshim.interface":
            interfaceName = value
        case "com.docker.network.windowsshim.hnsid":
            n.hnsID = value
        case netlabel.OverlayVxlanIDList:
            vniStrings := strings.Split(value, ",")
            for _, vniStr := range vniStrings {
                vni, err := strconv.Atoi(vniStr)
                if err != nil {
                    return fmt.Errorf("invalid vxlan id value %q passed", vniStr)
                }

                vnis = append(vnis, uint32(vni))
            }
        }
    }

    // If we are getting vnis from libnetwork, either we get for
    // all subnets or none.
    if len(vnis) < len(ipV4Data) {
        return fmt.Errorf("insufficient vnis(%d) passed to overlay. Windows driver requires VNIs to be prepopulated", len(vnis))
    }

    for i, ipd := range ipV4Data {
        s := &subnet{
            subnetIP: ipd.Pool,
            gwIP:     &ipd.Gateway.IP,
        }

        if len(vnis) != 0 {
            s.vni = vnis[i]
        }

        d.Lock()
        for _, network := range d.networks {
            found := false
            for _, sub := range network.subnets {
                if sub.vni == s.vni {
                    staleNetworks = append(staleNetworks, network.id)
                    found = true
                    break
                }
            }
            if found {
                break
            }
        }
        d.Unlock()

        n.subnets = append(n.subnets, s)
    }

    for _, staleNetwork := range staleNetworks {
        d.DeleteNetwork(staleNetwork)
    }

    n.name = networkName
    if n.name == "" {
        n.name = id
    }

    n.interfaceName = interfaceName

    if nInfo != nil {
        if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil {
            return err
        }
    }

    d.addNetwork(n)

    err := d.createHnsNetwork(n)

    if err != nil {
        d.deleteNetwork(id)
    } else {
        genData["com.docker.network.windowsshim.hnsid"] = n.hnsID
    }

    return err
}

func (d *driver) DeleteNetwork(nid string) error {
    if nid == "" {
        return fmt.Errorf("invalid network id")
    }

    n := d.network(nid)
    if n == nil {
        return types.ForbiddenErrorf("could not find network with id %s", nid)
    }

    _, err := hcsshim.HNSNetworkRequest("DELETE", n.hnsID, "")
    if err != nil {
        return types.ForbiddenErrorf(err.Error())
    }

    d.deleteNetwork(nid)

    return nil
}

func (d *driver) ProgramExternalConnectivity(_ context.Context, nid, eid string, options map[string]interface{}) error {
    return nil
}

func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
    return nil
}

func (d *driver) addNetwork(n *network) {
    d.Lock()
    d.networks[n.id] = n
    d.Unlock()
}

func (d *driver) deleteNetwork(nid string) {
    d.Lock()
    delete(d.networks, nid)
    d.Unlock()
}

func (d *driver) network(nid string) *network {
    d.Lock()
    defer d.Unlock()
    return d.networks[nid]
}

// func (n *network) restoreNetworkEndpoints() error {
//     log.G(ctx).Infof("Restoring endpoints for overlay network: %s", n.id)

//     hnsresponse, err := hcsshim.HNSListEndpointRequest("GET", "", "")
//     if err != nil {
//         return err
//     }

//     for _, endpoint := range hnsresponse {
//         if endpoint.VirtualNetwork != n.hnsID {
//             continue
//         }

//         ep := n.convertToOverlayEndpoint(&endpoint)

//         if ep != nil {
//             log.G(ctx).Debugf("Restored endpoint:%s Remote:%t", ep.id, ep.remote)
//             n.addEndpoint(ep)
//         }
//     }

//     return nil
// }

func (n *network) convertToOverlayEndpoint(v *hcsshim.HNSEndpoint) *endpoint {
    ep := &endpoint{
        id:        v.Name,
        profileID: v.Id,
        nid:       n.id,
        remote:    v.IsRemoteEndpoint,
    }

    mac, err := net.ParseMAC(v.MacAddress)
    if err != nil {
        return nil
    }

    ep.mac = mac
    ep.addr = &net.IPNet{
        IP:   v.IPAddress,
        Mask: net.CIDRMask(32, 32),
    }

    return ep
}

func (d *driver) createHnsNetwork(n *network) error {
    subnets := []hcsshim.Subnet{}

    for _, s := range n.subnets {
        subnet := hcsshim.Subnet{
            AddressPrefix: s.subnetIP.String(),
        }

        if s.gwIP != nil {
            subnet.GatewayAddress = s.gwIP.String()
        }

        vsidPolicy, err := json.Marshal(hcsshim.VsidPolicy{
            Type: "VSID",
            VSID: uint(s.vni),
        })
        if err != nil {
            return err
        }

        subnet.Policies = append(subnet.Policies, vsidPolicy)
        subnets = append(subnets, subnet)
    }

    network := &hcsshim.HNSNetwork{
        Name:               n.name,
        Type:               d.Type(),
        Subnets:            subnets,
        NetworkAdapterName: n.interfaceName,
        AutomaticDNS:       true,
    }

    configurationb, err := json.Marshal(network)
    if err != nil {
        return err
    }

    configuration := string(configurationb)
    log.G(context.TODO()).Infof("HNSNetwork Request =%v", configuration)

    hnsresponse, err := hcsshim.HNSNetworkRequest("POST", "", configuration)
    if err != nil {
        return err
    }

    n.hnsID = hnsresponse.Id
    n.providerAddress = hnsresponse.ManagementIP

    return nil
}

// contains return true if the passed ip belongs to one the network's
// subnets
func (n *network) contains(ip net.IP) bool {
    for _, s := range n.subnets {
        if s.subnetIP.Contains(ip) {
            return true
        }
    }

    return false
}

// getSubnetforIP returns the subnet to which the given IP belongs
func (n *network) getSubnetforIP(ip *net.IPNet) *subnet {
    for _, s := range n.subnets {
        // first check if the mask lengths are the same
        i, _ := s.subnetIP.Mask.Size()
        j, _ := ip.Mask.Size()
        if i != j {
            continue
        }
        if s.subnetIP.Contains(ip.IP) {
            return s
        }
    }
    return nil
}

// getMatchingSubnet return the network's subnet that matches the input
func (n *network) getMatchingSubnet(ip *net.IPNet) *subnet {
    if ip == nil {
        return nil
    }
    for _, s := range n.subnets {
        // first check if the mask lengths are the same
        i, _ := s.subnetIP.Mask.Size()
        j, _ := ip.Mask.Size()
        if i != j {
            continue
        }
        if s.subnetIP.IP.Equal(ip.IP) {
            return s
        }
    }
    return nil
}