mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
![Chris Telfer](/assets/img/avatar_default.png)
The previous code used string slices to limit the length of certain fields like endpoint or sandbox IDs. This assumes that these strings are at least as long as the slice length. Unfortunately, some sandbox IDs can be smaller than 7 characters. This fix addresses this issue by systematically converting format string calls that were taking fixed-slice arguments to use a precision specifier in the string format itself. From the golang fmt package documentation: For strings, byte slices and byte arrays, however, precision limits the length of the input to be formatted (not the size of the output), truncating if necessary. Normally it is measured in runes, but for these types when formatted with the %x or %X format it is measured in bytes. This nicely fits the desired behavior: it will limit the number of runes considered for string interpolation to the precision value. Signed-off-by: Chris Telfer <ctelfer@docker.com>
867 lines
22 KiB
Go
867 lines
22 KiB
Go
// +build windows
|
|
|
|
// Shim for the Host Network Service (HNS) to manage networking for
|
|
// Windows Server containers and Hyper-V containers. This module
|
|
// is a basic libnetwork driver that passes all the calls to HNS
|
|
// It implements the 4 networking modes supported by HNS L2Bridge,
|
|
// L2Tunnel, NAT and Transparent(DHCP)
|
|
//
|
|
// The network are stored in memory and docker daemon ensures discovering
|
|
// and loading these networks on startup
|
|
|
|
package windows
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/Microsoft/hcsshim"
|
|
"github.com/docker/docker/pkg/system"
|
|
"github.com/docker/libnetwork/datastore"
|
|
"github.com/docker/libnetwork/discoverapi"
|
|
"github.com/docker/libnetwork/driverapi"
|
|
"github.com/docker/libnetwork/netlabel"
|
|
"github.com/docker/libnetwork/types"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// networkConfiguration for network specific configuration
|
|
type networkConfiguration struct {
|
|
ID string
|
|
Type string
|
|
Name string
|
|
HnsID string
|
|
RDID string
|
|
VLAN uint
|
|
VSID uint
|
|
DNSServers string
|
|
MacPools []hcsshim.MacPool
|
|
DNSSuffix string
|
|
SourceMac string
|
|
NetworkAdapterName string
|
|
dbIndex uint64
|
|
dbExists bool
|
|
DisableGatewayDNS bool
|
|
EnableOutboundNat bool
|
|
OutboundNatExceptions []string
|
|
}
|
|
|
|
// endpointConfiguration represents the user specified configuration for the sandbox endpoint
|
|
type endpointOption struct {
|
|
MacAddress net.HardwareAddr
|
|
QosPolicies []types.QosPolicy
|
|
DNSServers []string
|
|
DisableDNS bool
|
|
DisableICC bool
|
|
}
|
|
|
|
// EndpointConnectivity stores the port bindings and exposed ports that the user has specified in epOptions.
|
|
type EndpointConnectivity struct {
|
|
PortBindings []types.PortBinding
|
|
ExposedPorts []types.TransportPort
|
|
}
|
|
|
|
type hnsEndpoint struct {
|
|
id string
|
|
nid string
|
|
profileID string
|
|
Type string
|
|
//Note: Currently, the sandboxID is the same as the containerID since windows does
|
|
//not expose the sandboxID.
|
|
//In the future, windows will support a proper sandboxID that is different
|
|
//than the containerID.
|
|
//Therefore, we are using sandboxID now, so that we won't have to change this code
|
|
//when windows properly supports a sandboxID.
|
|
sandboxID string
|
|
macAddress net.HardwareAddr
|
|
epOption *endpointOption // User specified parameters
|
|
epConnectivity *EndpointConnectivity // User specified parameters
|
|
portMapping []types.PortBinding // Operation port bindings
|
|
addr *net.IPNet
|
|
gateway net.IP
|
|
dbIndex uint64
|
|
dbExists bool
|
|
}
|
|
|
|
type hnsNetwork struct {
|
|
id string
|
|
created bool
|
|
config *networkConfiguration
|
|
endpoints map[string]*hnsEndpoint // key: endpoint id
|
|
driver *driver // The network's driver
|
|
sync.Mutex
|
|
}
|
|
|
|
type driver struct {
|
|
name string
|
|
networks map[string]*hnsNetwork
|
|
store datastore.DataStore
|
|
sync.Mutex
|
|
}
|
|
|
|
const (
|
|
errNotFound = "HNS failed with error : The object identifier does not represent a valid object. "
|
|
)
|
|
|
|
// IsBuiltinLocalDriver validates if network-type is a builtin local-scoped driver
|
|
func IsBuiltinLocalDriver(networkType string) bool {
|
|
if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "ics" == networkType || "transparent" == networkType {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// New constructs a new bridge driver
|
|
func newDriver(networkType string) *driver {
|
|
return &driver{name: networkType, networks: map[string]*hnsNetwork{}}
|
|
}
|
|
|
|
// GetInit returns an initializer for the given network type
|
|
func GetInit(networkType string) func(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|
return func(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|
if !IsBuiltinLocalDriver(networkType) {
|
|
return types.BadRequestErrorf("Network type not supported: %s", networkType)
|
|
}
|
|
|
|
d := newDriver(networkType)
|
|
|
|
err := d.initStore(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return dc.RegisterDriver(networkType, d, driverapi.Capability{
|
|
DataScope: datastore.LocalScope,
|
|
ConnectivityScope: datastore.LocalScope,
|
|
})
|
|
}
|
|
}
|
|
|
|
func (d *driver) getNetwork(id string) (*hnsNetwork, error) {
|
|
d.Lock()
|
|
defer d.Unlock()
|
|
|
|
if nw, ok := d.networks[id]; ok {
|
|
return nw, nil
|
|
}
|
|
|
|
return nil, types.NotFoundErrorf("network not found: %s", id)
|
|
}
|
|
|
|
func (n *hnsNetwork) getEndpoint(eid string) (*hnsEndpoint, error) {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
|
|
if ep, ok := n.endpoints[eid]; ok {
|
|
return ep, nil
|
|
}
|
|
|
|
return nil, types.NotFoundErrorf("Endpoint not found: %s", eid)
|
|
}
|
|
|
|
func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string) (*networkConfiguration, error) {
|
|
config := &networkConfiguration{Type: d.name}
|
|
|
|
for label, value := range genericOptions {
|
|
switch label {
|
|
case NetworkName:
|
|
config.Name = value
|
|
case HNSID:
|
|
config.HnsID = value
|
|
case RoutingDomain:
|
|
config.RDID = value
|
|
case Interface:
|
|
config.NetworkAdapterName = value
|
|
case DNSSuffix:
|
|
config.DNSSuffix = value
|
|
case DNSServers:
|
|
config.DNSServers = value
|
|
case DisableGatewayDNS:
|
|
b, err := strconv.ParseBool(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config.DisableGatewayDNS = b
|
|
case MacPool:
|
|
config.MacPools = make([]hcsshim.MacPool, 0)
|
|
s := strings.Split(value, ",")
|
|
if len(s)%2 != 0 {
|
|
return nil, types.BadRequestErrorf("Invalid mac pool. You must specify both a start range and an end range")
|
|
}
|
|
for i := 0; i < len(s)-1; i += 2 {
|
|
config.MacPools = append(config.MacPools, hcsshim.MacPool{
|
|
StartMacAddress: s[i],
|
|
EndMacAddress: s[i+1],
|
|
})
|
|
}
|
|
case VLAN:
|
|
vlan, err := strconv.ParseUint(value, 10, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config.VLAN = uint(vlan)
|
|
case VSID:
|
|
vsid, err := strconv.ParseUint(value, 10, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config.VSID = uint(vsid)
|
|
case EnableOutboundNat:
|
|
if system.GetOSVersion().Build <= 16236 {
|
|
return nil, fmt.Errorf("Invalid network option. OutboundNat is not supported on this OS version")
|
|
}
|
|
b, err := strconv.ParseBool(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config.EnableOutboundNat = b
|
|
case OutboundNatExceptions:
|
|
s := strings.Split(value, ",")
|
|
config.OutboundNatExceptions = s
|
|
}
|
|
}
|
|
|
|
config.ID = id
|
|
config.Type = d.name
|
|
return config, nil
|
|
}
|
|
|
|
func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
|
|
if len(ipamV6Data) > 0 {
|
|
return types.ForbiddenErrorf("windowsshim driver doesn't support v6 subnets")
|
|
}
|
|
|
|
if len(ipamV4Data) == 0 {
|
|
return types.BadRequestErrorf("network %s requires ipv4 configuration", id)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
|
}
|
|
|
|
func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
|
|
return "", nil
|
|
}
|
|
|
|
func (d *driver) createNetwork(config *networkConfiguration) error {
|
|
network := &hnsNetwork{
|
|
id: config.ID,
|
|
endpoints: make(map[string]*hnsEndpoint),
|
|
config: config,
|
|
driver: d,
|
|
}
|
|
|
|
d.Lock()
|
|
d.networks[config.ID] = network
|
|
d.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
// Create a new network
|
|
func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
|
if _, err := d.getNetwork(id); err == nil {
|
|
return types.ForbiddenErrorf("network %s exists", id)
|
|
}
|
|
|
|
genData, ok := option[netlabel.GenericData].(map[string]string)
|
|
if !ok {
|
|
return fmt.Errorf("Unknown generic data option")
|
|
}
|
|
|
|
// Parse and validate the config. It should not conflict with existing networks' config
|
|
config, err := d.parseNetworkOptions(id, genData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = config.processIPAM(id, ipV4Data, ipV6Data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = d.createNetwork(config)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// A non blank hnsid indicates that the network was discovered
|
|
// from HNS. No need to call HNS if this network was discovered
|
|
// from HNS
|
|
if config.HnsID == "" {
|
|
subnets := []hcsshim.Subnet{}
|
|
|
|
for _, ipData := range ipV4Data {
|
|
subnet := hcsshim.Subnet{
|
|
AddressPrefix: ipData.Pool.String(),
|
|
}
|
|
|
|
if ipData.Gateway != nil {
|
|
subnet.GatewayAddress = ipData.Gateway.IP.String()
|
|
}
|
|
|
|
subnets = append(subnets, subnet)
|
|
}
|
|
|
|
network := &hcsshim.HNSNetwork{
|
|
Name: config.Name,
|
|
Type: d.name,
|
|
Subnets: subnets,
|
|
DNSServerList: config.DNSServers,
|
|
DNSSuffix: config.DNSSuffix,
|
|
MacPools: config.MacPools,
|
|
SourceMac: config.SourceMac,
|
|
NetworkAdapterName: config.NetworkAdapterName,
|
|
}
|
|
|
|
if config.VLAN != 0 {
|
|
vlanPolicy, err := json.Marshal(hcsshim.VlanPolicy{
|
|
Type: "VLAN",
|
|
VLAN: config.VLAN,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
network.Policies = append(network.Policies, vlanPolicy)
|
|
}
|
|
|
|
if config.VSID != 0 {
|
|
vsidPolicy, err := json.Marshal(hcsshim.VsidPolicy{
|
|
Type: "VSID",
|
|
VSID: config.VSID,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
network.Policies = append(network.Policies, vsidPolicy)
|
|
}
|
|
|
|
if network.Name == "" {
|
|
network.Name = id
|
|
}
|
|
|
|
configurationb, err := json.Marshal(network)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
configuration := string(configurationb)
|
|
logrus.Debugf("HNSNetwork Request =%v Address Space=%v", configuration, subnets)
|
|
|
|
hnsresponse, err := hcsshim.HNSNetworkRequest("POST", "", configuration)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
config.HnsID = hnsresponse.Id
|
|
genData[HNSID] = config.HnsID
|
|
|
|
} else {
|
|
// Delete any stale HNS endpoints for this network.
|
|
if endpoints, err := hcsshim.HNSListEndpointRequest(); err == nil {
|
|
for _, ep := range endpoints {
|
|
if ep.VirtualNetwork == config.HnsID {
|
|
logrus.Infof("Removing stale HNS endpoint %s", ep.Id)
|
|
_, err = hcsshim.HNSEndpointRequest("DELETE", ep.Id, "")
|
|
if err != nil {
|
|
logrus.Warnf("Error removing HNS endpoint %s", ep.Id)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
logrus.Warnf("Error listing HNS endpoints for network %s", config.HnsID)
|
|
}
|
|
}
|
|
|
|
n, err := d.getNetwork(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n.created = true
|
|
return d.storeUpdate(config)
|
|
}
|
|
|
|
func (d *driver) DeleteNetwork(nid string) error {
|
|
n, err := d.getNetwork(nid)
|
|
if err != nil {
|
|
return types.InternalMaskableErrorf("%s", err)
|
|
}
|
|
|
|
n.Lock()
|
|
config := n.config
|
|
n.Unlock()
|
|
|
|
if n.created {
|
|
_, err = hcsshim.HNSNetworkRequest("DELETE", config.HnsID, "")
|
|
if err != nil && err.Error() != errNotFound {
|
|
return types.ForbiddenErrorf(err.Error())
|
|
}
|
|
}
|
|
|
|
d.Lock()
|
|
delete(d.networks, nid)
|
|
d.Unlock()
|
|
|
|
// delele endpoints belong to this network
|
|
for _, ep := range n.endpoints {
|
|
if err := d.storeDelete(ep); err != nil {
|
|
logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err)
|
|
}
|
|
}
|
|
|
|
return d.storeDelete(config)
|
|
}
|
|
|
|
func convertQosPolicies(qosPolicies []types.QosPolicy) ([]json.RawMessage, error) {
|
|
var qps []json.RawMessage
|
|
|
|
// Enumerate through the qos policies specified by the user and convert
|
|
// them into the internal structure matching the JSON blob that can be
|
|
// understood by the HCS.
|
|
for _, elem := range qosPolicies {
|
|
encodedPolicy, err := json.Marshal(hcsshim.QosPolicy{
|
|
Type: "QOS",
|
|
MaximumOutgoingBandwidthInBytes: elem.MaxEgressBandwidth,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
qps = append(qps, encodedPolicy)
|
|
}
|
|
return qps, nil
|
|
}
|
|
|
|
// ConvertPortBindings converts PortBindings to JSON for HNS request
|
|
func ConvertPortBindings(portBindings []types.PortBinding) ([]json.RawMessage, error) {
|
|
var pbs []json.RawMessage
|
|
|
|
// Enumerate through the port bindings specified by the user and convert
|
|
// them into the internal structure matching the JSON blob that can be
|
|
// understood by the HCS.
|
|
for _, elem := range portBindings {
|
|
proto := strings.ToUpper(elem.Proto.String())
|
|
if proto != "TCP" && proto != "UDP" {
|
|
return nil, fmt.Errorf("invalid protocol %s", elem.Proto.String())
|
|
}
|
|
|
|
if elem.HostPort != elem.HostPortEnd {
|
|
return nil, fmt.Errorf("Windows does not support more than one host port in NAT settings")
|
|
}
|
|
|
|
if len(elem.HostIP) != 0 {
|
|
return nil, fmt.Errorf("Windows does not support host IP addresses in NAT settings")
|
|
}
|
|
|
|
encodedPolicy, err := json.Marshal(hcsshim.NatPolicy{
|
|
Type: "NAT",
|
|
ExternalPort: elem.HostPort,
|
|
InternalPort: elem.Port,
|
|
Protocol: elem.Proto.String(),
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pbs = append(pbs, encodedPolicy)
|
|
}
|
|
return pbs, nil
|
|
}
|
|
|
|
// ParsePortBindingPolicies parses HNS endpoint response message to PortBindings
|
|
func ParsePortBindingPolicies(policies []json.RawMessage) ([]types.PortBinding, error) {
|
|
var bindings []types.PortBinding
|
|
hcsPolicy := &hcsshim.NatPolicy{}
|
|
|
|
for _, elem := range policies {
|
|
|
|
if err := json.Unmarshal([]byte(elem), &hcsPolicy); err != nil || hcsPolicy.Type != "NAT" {
|
|
continue
|
|
}
|
|
|
|
binding := types.PortBinding{
|
|
HostPort: hcsPolicy.ExternalPort,
|
|
HostPortEnd: hcsPolicy.ExternalPort,
|
|
Port: hcsPolicy.InternalPort,
|
|
Proto: types.ParseProtocol(hcsPolicy.Protocol),
|
|
HostIP: net.IPv4(0, 0, 0, 0),
|
|
}
|
|
|
|
bindings = append(bindings, binding)
|
|
}
|
|
|
|
return bindings, nil
|
|
}
|
|
|
|
func parseEndpointOptions(epOptions map[string]interface{}) (*endpointOption, error) {
|
|
if epOptions == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
ec := &endpointOption{}
|
|
|
|
if opt, ok := epOptions[netlabel.MacAddress]; ok {
|
|
if mac, ok := opt.(net.HardwareAddr); ok {
|
|
ec.MacAddress = mac
|
|
} else {
|
|
return nil, fmt.Errorf("Invalid endpoint configuration")
|
|
}
|
|
}
|
|
|
|
if opt, ok := epOptions[QosPolicies]; ok {
|
|
if policies, ok := opt.([]types.QosPolicy); ok {
|
|
ec.QosPolicies = policies
|
|
} else {
|
|
return nil, fmt.Errorf("Invalid endpoint configuration")
|
|
}
|
|
}
|
|
|
|
if opt, ok := epOptions[netlabel.DNSServers]; ok {
|
|
if dns, ok := opt.([]string); ok {
|
|
ec.DNSServers = dns
|
|
} else {
|
|
return nil, fmt.Errorf("Invalid endpoint configuration")
|
|
}
|
|
}
|
|
|
|
if opt, ok := epOptions[DisableICC]; ok {
|
|
if disableICC, ok := opt.(bool); ok {
|
|
ec.DisableICC = disableICC
|
|
} else {
|
|
return nil, fmt.Errorf("Invalid endpoint configuration")
|
|
}
|
|
}
|
|
|
|
if opt, ok := epOptions[DisableDNS]; ok {
|
|
if disableDNS, ok := opt.(bool); ok {
|
|
ec.DisableDNS = disableDNS
|
|
} else {
|
|
return nil, fmt.Errorf("Invalid endpoint configuration")
|
|
}
|
|
}
|
|
|
|
return ec, nil
|
|
}
|
|
|
|
// ParseEndpointConnectivity parses options passed to CreateEndpoint, specifically port bindings, and store in a endpointConnectivity object.
|
|
func ParseEndpointConnectivity(epOptions map[string]interface{}) (*EndpointConnectivity, error) {
|
|
if epOptions == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
ec := &EndpointConnectivity{}
|
|
|
|
if opt, ok := epOptions[netlabel.PortMap]; ok {
|
|
if bs, ok := opt.([]types.PortBinding); ok {
|
|
ec.PortBindings = bs
|
|
} else {
|
|
return nil, fmt.Errorf("Invalid endpoint configuration")
|
|
}
|
|
}
|
|
|
|
if opt, ok := epOptions[netlabel.ExposedPorts]; ok {
|
|
if ports, ok := opt.([]types.TransportPort); ok {
|
|
ec.ExposedPorts = ports
|
|
} else {
|
|
return nil, fmt.Errorf("Invalid endpoint configuration")
|
|
}
|
|
}
|
|
return ec, nil
|
|
}
|
|
|
|
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
|
n, err := d.getNetwork(nid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check if endpoint id is good and retrieve corresponding endpoint
|
|
ep, err := n.getEndpoint(eid)
|
|
if err == nil && ep != nil {
|
|
return driverapi.ErrEndpointExists(eid)
|
|
}
|
|
|
|
endpointStruct := &hcsshim.HNSEndpoint{
|
|
VirtualNetwork: n.config.HnsID,
|
|
}
|
|
|
|
epOption, err := parseEndpointOptions(epOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
epConnectivity, err := ParseEndpointConnectivity(epOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
macAddress := ifInfo.MacAddress()
|
|
// Use the macaddress if it was provided
|
|
if macAddress != nil {
|
|
endpointStruct.MacAddress = strings.Replace(macAddress.String(), ":", "-", -1)
|
|
}
|
|
|
|
endpointStruct.Policies, err = ConvertPortBindings(epConnectivity.PortBindings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
qosPolicies, err := convertQosPolicies(epOption.QosPolicies)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
endpointStruct.Policies = append(endpointStruct.Policies, qosPolicies...)
|
|
|
|
if ifInfo.Address() != nil {
|
|
endpointStruct.IPAddress = ifInfo.Address().IP
|
|
}
|
|
|
|
endpointStruct.DNSServerList = strings.Join(epOption.DNSServers, ",")
|
|
|
|
// overwrite the ep DisableDNS option if DisableGatewayDNS was set to true during the network creation option
|
|
if n.config.DisableGatewayDNS {
|
|
logrus.Debugf("n.config.DisableGatewayDNS[%v] overwrites epOption.DisableDNS[%v]", n.config.DisableGatewayDNS, epOption.DisableDNS)
|
|
epOption.DisableDNS = n.config.DisableGatewayDNS
|
|
}
|
|
|
|
if n.driver.name == "nat" && !epOption.DisableDNS {
|
|
logrus.Debugf("endpointStruct.EnableInternalDNS =[%v]", endpointStruct.EnableInternalDNS)
|
|
endpointStruct.EnableInternalDNS = true
|
|
}
|
|
|
|
endpointStruct.DisableICC = epOption.DisableICC
|
|
|
|
// Inherit OutboundNat policy from the network
|
|
if n.config.EnableOutboundNat {
|
|
outboundNatPolicy, err := json.Marshal(hcsshim.OutboundNatPolicy{
|
|
Policy: hcsshim.Policy{Type: hcsshim.OutboundNat},
|
|
Exceptions: n.config.OutboundNatExceptions,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
endpointStruct.Policies = append(endpointStruct.Policies, outboundNatPolicy)
|
|
}
|
|
|
|
configurationb, err := json.Marshal(endpointStruct)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hnsresponse, err := hcsshim.HNSEndpointRequest("POST", "", string(configurationb))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mac, err := net.ParseMAC(hnsresponse.MacAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO For now the ip mask is not in the info generated by HNS
|
|
endpoint := &hnsEndpoint{
|
|
id: eid,
|
|
nid: n.id,
|
|
Type: d.name,
|
|
addr: &net.IPNet{IP: hnsresponse.IPAddress, Mask: hnsresponse.IPAddress.DefaultMask()},
|
|
macAddress: mac,
|
|
}
|
|
|
|
if hnsresponse.GatewayAddress != "" {
|
|
endpoint.gateway = net.ParseIP(hnsresponse.GatewayAddress)
|
|
}
|
|
|
|
endpoint.profileID = hnsresponse.Id
|
|
endpoint.epConnectivity = epConnectivity
|
|
endpoint.epOption = epOption
|
|
endpoint.portMapping, err = ParsePortBindingPolicies(hnsresponse.Policies)
|
|
|
|
if err != nil {
|
|
hcsshim.HNSEndpointRequest("DELETE", hnsresponse.Id, "")
|
|
return err
|
|
}
|
|
|
|
n.Lock()
|
|
n.endpoints[eid] = endpoint
|
|
n.Unlock()
|
|
|
|
if ifInfo.Address() == nil {
|
|
ifInfo.SetIPAddress(endpoint.addr)
|
|
}
|
|
|
|
if macAddress == nil {
|
|
ifInfo.SetMacAddress(endpoint.macAddress)
|
|
}
|
|
|
|
if err = d.storeUpdate(endpoint); err != nil {
|
|
logrus.Errorf("Failed to save endpoint %.7s to store: %v", endpoint.id, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) DeleteEndpoint(nid, eid string) error {
|
|
n, err := d.getNetwork(nid)
|
|
if err != nil {
|
|
return types.InternalMaskableErrorf("%s", err)
|
|
}
|
|
|
|
ep, err := n.getEndpoint(eid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
n.Lock()
|
|
delete(n.endpoints, eid)
|
|
n.Unlock()
|
|
|
|
_, err = hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "")
|
|
if err != nil && err.Error() != errNotFound {
|
|
return err
|
|
}
|
|
|
|
if err := d.storeDelete(ep); err != nil {
|
|
logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
|
network, err := d.getNetwork(nid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ep, err := network.getEndpoint(eid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data := make(map[string]interface{}, 1)
|
|
if network.driver.name == "nat" {
|
|
data["AllowUnqualifiedDNSQuery"] = true
|
|
}
|
|
|
|
data["hnsid"] = ep.profileID
|
|
if ep.epConnectivity.ExposedPorts != nil {
|
|
// Return a copy of the config data
|
|
epc := make([]types.TransportPort, 0, len(ep.epConnectivity.ExposedPorts))
|
|
for _, tp := range ep.epConnectivity.ExposedPorts {
|
|
epc = append(epc, tp.GetCopy())
|
|
}
|
|
data[netlabel.ExposedPorts] = epc
|
|
}
|
|
|
|
if ep.portMapping != nil {
|
|
// Return a copy of the operational data
|
|
pmc := make([]types.PortBinding, 0, len(ep.portMapping))
|
|
for _, pm := range ep.portMapping {
|
|
pmc = append(pmc, pm.GetCopy())
|
|
}
|
|
data[netlabel.PortMap] = pmc
|
|
}
|
|
|
|
if len(ep.macAddress) != 0 {
|
|
data[netlabel.MacAddress] = ep.macAddress
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// Join method is invoked when a Sandbox is attached to an endpoint.
|
|
func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
|
network, err := d.getNetwork(nid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Ensure that the endpoint exists
|
|
endpoint, err := network.getEndpoint(eid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = jinfo.SetGateway(endpoint.gateway)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
endpoint.sandboxID = sboxKey
|
|
|
|
err = hcsshim.HotAttachEndpoint(endpoint.sandboxID, endpoint.profileID)
|
|
if err != nil {
|
|
// If container doesn't exists in hcs, do not throw error for hot add/remove
|
|
if err != hcsshim.ErrComputeSystemDoesNotExist {
|
|
return err
|
|
}
|
|
}
|
|
|
|
jinfo.DisableGatewayService()
|
|
return nil
|
|
}
|
|
|
|
// Leave method is invoked when a Sandbox detaches from an endpoint.
|
|
func (d *driver) Leave(nid, eid string) error {
|
|
network, err := d.getNetwork(nid)
|
|
if err != nil {
|
|
return types.InternalMaskableErrorf("%s", err)
|
|
}
|
|
|
|
// Ensure that the endpoint exists
|
|
endpoint, err := network.getEndpoint(eid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = hcsshim.HotDetachEndpoint(endpoint.sandboxID, endpoint.profileID)
|
|
if err != nil {
|
|
// If container doesn't exists in hcs, do not throw error for hot add/remove
|
|
if err != hcsshim.ErrComputeSystemDoesNotExist {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
|
return nil
|
|
}
|
|
|
|
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) Type() string {
|
|
return d.name
|
|
}
|
|
|
|
func (d *driver) IsBuiltIn() bool {
|
|
return true
|
|
}
|
|
|
|
// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
|
|
func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
|
|
func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
|
return nil
|
|
}
|