github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/libnetwork/portmapper/mapper.go (about)

     1  package portmapper
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"sync"
     8  
     9  	"github.com/Sirupsen/logrus"
    10  	"github.com/docker/libnetwork/iptables"
    11  	"github.com/docker/libnetwork/portallocator"
    12  )
    13  
    14  type mapping struct {
    15  	proto         string
    16  	userlandProxy userlandProxy
    17  	host          net.Addr
    18  	container     net.Addr
    19  }
    20  
    21  var newProxy = newProxyCommand
    22  
    23  var (
    24  	// ErrUnknownBackendAddressType refers to an unknown container or unsupported address type
    25  	ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
    26  	// ErrPortMappedForIP refers to a port already mapped to an ip address
    27  	ErrPortMappedForIP = errors.New("port is already mapped to ip")
    28  	// ErrPortNotMapped refers to an unmapped port
    29  	ErrPortNotMapped = errors.New("port is not mapped")
    30  )
    31  
    32  // PortMapper manages the network address translation
    33  type PortMapper struct {
    34  	chain      *iptables.ChainInfo
    35  	bridgeName string
    36  
    37  	// udp:ip:port
    38  	currentMappings map[string]*mapping
    39  	lock            sync.Mutex
    40  
    41  	proxyPath string
    42  
    43  	Allocator *portallocator.PortAllocator
    44  }
    45  
    46  // New returns a new instance of PortMapper
    47  func New(proxyPath string) *PortMapper {
    48  	return NewWithPortAllocator(portallocator.Get(), proxyPath)
    49  }
    50  
    51  // NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator
    52  func NewWithPortAllocator(allocator *portallocator.PortAllocator, proxyPath string) *PortMapper {
    53  	return &PortMapper{
    54  		currentMappings: make(map[string]*mapping),
    55  		Allocator:       allocator,
    56  		proxyPath:       proxyPath,
    57  	}
    58  }
    59  
    60  // SetIptablesChain sets the specified chain into portmapper
    61  func (pm *PortMapper) SetIptablesChain(c *iptables.ChainInfo, bridgeName string) {
    62  	pm.chain = c
    63  	pm.bridgeName = bridgeName
    64  }
    65  
    66  // Map maps the specified container transport address to the host's network address and transport port
    67  func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) {
    68  	return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy)
    69  }
    70  
    71  // MapRange maps the specified container transport address to the host's network address and transport port range
    72  func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) {
    73  	pm.lock.Lock()
    74  	defer pm.lock.Unlock()
    75  
    76  	var (
    77  		m                 *mapping
    78  		proto             string
    79  		allocatedHostPort int
    80  	)
    81  
    82  	switch container.(type) {
    83  	case *net.TCPAddr:
    84  		proto = "tcp"
    85  		if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
    86  			return nil, err
    87  		}
    88  
    89  		m = &mapping{
    90  			proto:     proto,
    91  			host:      &net.TCPAddr{IP: hostIP, Port: allocatedHostPort},
    92  			container: container,
    93  		}
    94  
    95  		if useProxy {
    96  			m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port, pm.proxyPath)
    97  			if err != nil {
    98  				return nil, err
    99  			}
   100  		} else {
   101  			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
   102  		}
   103  	case *net.UDPAddr:
   104  		proto = "udp"
   105  		if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
   106  			return nil, err
   107  		}
   108  
   109  		m = &mapping{
   110  			proto:     proto,
   111  			host:      &net.UDPAddr{IP: hostIP, Port: allocatedHostPort},
   112  			container: container,
   113  		}
   114  
   115  		if useProxy {
   116  			m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port, pm.proxyPath)
   117  			if err != nil {
   118  				return nil, err
   119  			}
   120  		} else {
   121  			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
   122  		}
   123  	default:
   124  		return nil, ErrUnknownBackendAddressType
   125  	}
   126  
   127  	// release the allocated port on any further error during return.
   128  	defer func() {
   129  		if err != nil {
   130  			pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort)
   131  		}
   132  	}()
   133  
   134  	key := getKey(m.host)
   135  	if _, exists := pm.currentMappings[key]; exists {
   136  		return nil, ErrPortMappedForIP
   137  	}
   138  
   139  	containerIP, containerPort := getIPAndPort(m.container)
   140  	if hostIP.To4() != nil {
   141  		if err := pm.forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
   142  			return nil, err
   143  		}
   144  	}
   145  
   146  	cleanup := func() error {
   147  		// need to undo the iptables rules before we return
   148  		m.userlandProxy.Stop()
   149  		if hostIP.To4() != nil {
   150  			pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
   151  			if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
   152  				return err
   153  			}
   154  		}
   155  
   156  		return nil
   157  	}
   158  
   159  	if err := m.userlandProxy.Start(); err != nil {
   160  		if err := cleanup(); err != nil {
   161  			return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
   162  		}
   163  		return nil, err
   164  	}
   165  
   166  	pm.currentMappings[key] = m
   167  	return m.host, nil
   168  }
   169  
   170  // Unmap removes stored mapping for the specified host transport address
   171  func (pm *PortMapper) Unmap(host net.Addr) error {
   172  	pm.lock.Lock()
   173  	defer pm.lock.Unlock()
   174  
   175  	key := getKey(host)
   176  	data, exists := pm.currentMappings[key]
   177  	if !exists {
   178  		return ErrPortNotMapped
   179  	}
   180  
   181  	if data.userlandProxy != nil {
   182  		data.userlandProxy.Stop()
   183  	}
   184  
   185  	delete(pm.currentMappings, key)
   186  
   187  	containerIP, containerPort := getIPAndPort(data.container)
   188  	hostIP, hostPort := getIPAndPort(data.host)
   189  	if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
   190  		logrus.Errorf("Error on iptables delete: %s", err)
   191  	}
   192  
   193  	switch a := host.(type) {
   194  	case *net.TCPAddr:
   195  		return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port)
   196  	case *net.UDPAddr:
   197  		return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
   198  	}
   199  	return nil
   200  }
   201  
   202  //ReMapAll will re-apply all port mappings
   203  func (pm *PortMapper) ReMapAll() {
   204  	pm.lock.Lock()
   205  	defer pm.lock.Unlock()
   206  	logrus.Debugln("Re-applying all port mappings.")
   207  	for _, data := range pm.currentMappings {
   208  		containerIP, containerPort := getIPAndPort(data.container)
   209  		hostIP, hostPort := getIPAndPort(data.host)
   210  		if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
   211  			logrus.Errorf("Error on iptables add: %s", err)
   212  		}
   213  	}
   214  }
   215  
   216  func getKey(a net.Addr) string {
   217  	switch t := a.(type) {
   218  	case *net.TCPAddr:
   219  		return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
   220  	case *net.UDPAddr:
   221  		return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
   222  	}
   223  	return ""
   224  }
   225  
   226  func getIPAndPort(a net.Addr) (net.IP, int) {
   227  	switch t := a.(type) {
   228  	case *net.TCPAddr:
   229  		return t.IP, t.Port
   230  	case *net.UDPAddr:
   231  		return t.IP, t.Port
   232  	}
   233  	return nil, 0
   234  }
   235  
   236  func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
   237  	if pm.chain == nil {
   238  		return nil
   239  	}
   240  	return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName)
   241  }