moby/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go
Sebastiaan van Stijn 1b3fef5333
Windows: require Windows Server RS5 / ltsc2019 (build 17763) as minimum
Windows Server 2016 (RS1) reached end of support, and Docker Desktop requires
Windows 10 V19H2 (version 1909, build 18363) as a minimum.

This patch makes Windows Server RS5 /  ltsc2019 (build 17763) the minimum version
to run the daemon, and removes some hacks for older versions of Windows.

There is one check remaining that checks for Windows RS3 for a workaround
on older versions, but recent changes in Windows seemed to have regressed
on the same issue, so I kept that code for now to check if we may need that
workaround (again);

085c6a98d5/daemon/graphdriver/windows/windows.go (L319-L341)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-02-18 22:58:28 +01:00

294 lines
6 KiB
Go

package overlay
import (
"encoding/json"
"fmt"
"net"
"sync"
"github.com/Microsoft/hcsshim"
"github.com/Microsoft/hcsshim/osversion"
"github.com/docker/docker/libnetwork/driverapi"
"github.com/docker/docker/libnetwork/drivers/windows"
"github.com/docker/docker/libnetwork/netlabel"
"github.com/docker/docker/libnetwork/types"
"github.com/sirupsen/logrus"
)
type endpointTable map[string]*endpoint
const overlayEndpointPrefix = "overlay/endpoint"
type endpoint struct {
id string
nid string
profileID string
remote bool
mac net.HardwareAddr
addr *net.IPNet
disablegateway bool
portMapping []types.PortBinding // Operation port bindings
}
var (
//Server 2016 (RS1) does not support concurrent add/delete of endpoints. Therefore, we need
//to use this mutex and serialize the add/delete of endpoints on RS1.
endpointMu sync.Mutex
windowsBuild = osversion.Build()
)
func validateID(nid, eid string) error {
if nid == "" {
return fmt.Errorf("invalid network id")
}
if eid == "" {
return fmt.Errorf("invalid endpoint id")
}
return nil
}
func (n *network) endpoint(eid string) *endpoint {
n.Lock()
defer n.Unlock()
return n.endpoints[eid]
}
func (n *network) addEndpoint(ep *endpoint) {
n.Lock()
n.endpoints[ep.id] = ep
n.Unlock()
}
func (n *network) deleteEndpoint(eid string) {
n.Lock()
delete(n.endpoints, eid)
n.Unlock()
}
func (n *network) removeEndpointWithAddress(addr *net.IPNet) {
var networkEndpoint *endpoint
n.Lock()
for _, ep := range n.endpoints {
if ep.addr.IP.Equal(addr.IP) {
networkEndpoint = ep
break
}
}
if networkEndpoint != nil {
delete(n.endpoints, networkEndpoint.id)
}
n.Unlock()
if networkEndpoint != nil {
logrus.Debugf("Removing stale endpoint from HNS")
_, err := endpointRequest("DELETE", networkEndpoint.profileID, "")
if err != nil {
logrus.Debugf("Failed to delete stale overlay endpoint (%.7s) from hns", networkEndpoint.id)
}
}
}
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
epOptions map[string]interface{}) error {
var err error
if err = validateID(nid, eid); err != nil {
return err
}
n := d.network(nid)
if n == nil {
return fmt.Errorf("network id %q not found", nid)
}
ep := n.endpoint(eid)
if ep != nil {
logrus.Debugf("Deleting stale endpoint %s", eid)
n.deleteEndpoint(eid)
_, err := endpointRequest("DELETE", ep.profileID, "")
if err != nil {
return err
}
}
ep = &endpoint{
id: eid,
nid: n.id,
addr: ifInfo.Address(),
mac: ifInfo.MacAddress(),
}
if ep.addr == nil {
return fmt.Errorf("create endpoint was not passed interface IP address")
}
s := n.getSubnetforIP(ep.addr)
if s == nil {
return fmt.Errorf("no matching subnet for IP %q in network %q", ep.addr, nid)
}
// Todo: Add port bindings and qos policies here
hnsEndpoint := &hcsshim.HNSEndpoint{
Name: eid,
VirtualNetwork: n.hnsID,
IPAddress: ep.addr.IP,
EnableInternalDNS: true,
GatewayAddress: s.gwIP.String(),
}
if ep.mac != nil {
hnsEndpoint.MacAddress = ep.mac.String()
}
paPolicy, err := json.Marshal(hcsshim.PaPolicy{
Type: "PA",
PA: n.providerAddress,
})
if err != nil {
return err
}
hnsEndpoint.Policies = append(hnsEndpoint.Policies, paPolicy)
natPolicy, err := json.Marshal(hcsshim.PaPolicy{
Type: "OutBoundNAT",
})
if err != nil {
return err
}
hnsEndpoint.Policies = append(hnsEndpoint.Policies, natPolicy)
epConnectivity, err := windows.ParseEndpointConnectivity(epOptions)
if err != nil {
return err
}
ep.portMapping = epConnectivity.PortBindings
ep.portMapping, err = windows.AllocatePorts(n.portMapper, ep.portMapping, ep.addr.IP)
if err != nil {
return err
}
defer func() {
if err != nil {
windows.ReleasePorts(n.portMapper, ep.portMapping)
}
}()
pbPolicy, err := windows.ConvertPortBindings(ep.portMapping)
if err != nil {
return err
}
hnsEndpoint.Policies = append(hnsEndpoint.Policies, pbPolicy...)
ep.disablegateway = true
configurationb, err := json.Marshal(hnsEndpoint)
if err != nil {
return err
}
hnsresponse, err := endpointRequest("POST", "", string(configurationb))
if err != nil {
return err
}
ep.profileID = hnsresponse.Id
if ep.mac == nil {
ep.mac, err = net.ParseMAC(hnsresponse.MacAddress)
if err != nil {
return err
}
if err := ifInfo.SetMacAddress(ep.mac); err != nil {
return err
}
}
ep.portMapping, err = windows.ParsePortBindingPolicies(hnsresponse.Policies)
if err != nil {
endpointRequest("DELETE", hnsresponse.Id, "")
return err
}
n.addEndpoint(ep)
return nil
}
func (d *driver) DeleteEndpoint(nid, eid string) error {
if err := validateID(nid, eid); err != nil {
return err
}
n := d.network(nid)
if n == nil {
return fmt.Errorf("network id %q not found", nid)
}
ep := n.endpoint(eid)
if ep == nil {
return fmt.Errorf("endpoint id %q not found", eid)
}
windows.ReleasePorts(n.portMapper, ep.portMapping)
n.deleteEndpoint(eid)
_, err := endpointRequest("DELETE", ep.profileID, "")
if err != nil {
return err
}
return nil
}
func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
if err := validateID(nid, eid); err != nil {
return nil, err
}
n := d.network(nid)
if n == nil {
return nil, fmt.Errorf("network id %q not found", nid)
}
ep := n.endpoint(eid)
if ep == nil {
return nil, fmt.Errorf("endpoint id %q not found", eid)
}
data := make(map[string]interface{}, 1)
data["hnsid"] = ep.profileID
data["AllowUnqualifiedDNSQuery"] = true
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
}
return data, nil
}
func endpointRequest(method, path, request string) (*hcsshim.HNSEndpoint, error) {
if windowsBuild == 14393 {
endpointMu.Lock()
}
hnsresponse, err := hcsshim.HNSEndpointRequest(method, path, request)
if windowsBuild == 14393 {
endpointMu.Unlock()
}
return hnsresponse, err
}