github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/libnetwork/drivers/solaris/bridge/port_mapping.go (about)

     1  // +build solaris
     2  
     3  package bridge
     4  
     5  import (
     6  	"bytes"
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  	"os"
    11  	"os/exec"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	"github.com/docker/libnetwork/types"
    15  )
    16  
    17  var (
    18  	defaultBindingIP = net.IPv4(0, 0, 0, 0)
    19  )
    20  
    21  const (
    22  	maxAllocatePortAttempts = 10
    23  )
    24  
    25  func addPFRules(epid, bindIntf string, bs []types.PortBinding) {
    26  	var id string
    27  
    28  	if len(epid) > 12 {
    29  		id = epid[:12]
    30  	} else {
    31  		id = epid
    32  	}
    33  
    34  	fname := "/var/lib/docker/network/files/pf." + id
    35  
    36  	f, err := os.OpenFile(fname,
    37  		os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
    38  	if err != nil {
    39  		logrus.Warnf("cannot open temp pf file")
    40  		return
    41  	}
    42  	for _, b := range bs {
    43  		r := fmt.Sprintf(
    44  			"pass in on %s proto %s from any to (%s) "+
    45  				"port %d rdr-to %s port %d\n", bindIntf,
    46  			b.Proto.String(), bindIntf, b.HostPort,
    47  			b.IP.String(), b.Port)
    48  		_, err = f.WriteString(r)
    49  		if err != nil {
    50  			logrus.Warnf("cannot write firewall rules to %s: %v", fname, err)
    51  		}
    52  	}
    53  	f.Close()
    54  
    55  	anchor := fmt.Sprintf("_auto/docker/ep%s", id)
    56  	err = exec.Command("/usr/sbin/pfctl", "-a", anchor, "-f", fname).Run()
    57  	if err != nil {
    58  		logrus.Warnf("failed to add firewall rules: %v", err)
    59  	}
    60  	os.Remove(fname)
    61  }
    62  
    63  func removePFRules(epid string) {
    64  	var id string
    65  
    66  	if len(epid) > 12 {
    67  		id = epid[:12]
    68  	} else {
    69  		id = epid
    70  	}
    71  
    72  	anchor := fmt.Sprintf("_auto/docker/ep%s", id)
    73  	err := exec.Command("/usr/sbin/pfctl", "-a", anchor, "-F", "all").Run()
    74  	if err != nil {
    75  		logrus.Warnf("failed to remove firewall rules: %v", err)
    76  	}
    77  }
    78  
    79  func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, bindIntf string, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
    80  	if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
    81  		return nil, nil
    82  	}
    83  
    84  	defHostIP := defaultBindingIP
    85  	if reqDefBindIP != nil {
    86  		defHostIP = reqDefBindIP
    87  	}
    88  
    89  	bs, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, bindIntf, ep.addr.IP, defHostIP, ulPxyEnabled)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	// Add PF rules for port bindings, if any
    95  	if len(bs) > 0 {
    96  		addPFRules(ep.id, bindIntf, bs)
    97  	}
    98  
    99  	return bs, err
   100  }
   101  
   102  func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, bindIntf string, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
   103  	bs := make([]types.PortBinding, 0, len(bindings))
   104  	for _, c := range bindings {
   105  		b := c.GetCopy()
   106  		if err := n.allocatePort(&b, containerIP, defHostIP); err != nil {
   107  			// On allocation failure,release previously
   108  			// allocated ports. On cleanup error, just log
   109  			// a warning message
   110  			if cuErr := n.releasePortsInternal(bs); cuErr != nil {
   111  				logrus.Warnf("Upon allocation failure "+
   112  					"for %v, failed to clear previously "+
   113  					"allocated port bindings: %v", b, cuErr)
   114  			}
   115  			return nil, err
   116  		}
   117  		bs = append(bs, b)
   118  	}
   119  	return bs, nil
   120  }
   121  
   122  func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP) error {
   123  	var (
   124  		host net.Addr
   125  		err  error
   126  	)
   127  
   128  	// Store the container interface address in the operational binding
   129  	bnd.IP = containerIP
   130  
   131  	// Adjust the host address in the operational binding
   132  	if len(bnd.HostIP) == 0 {
   133  		bnd.HostIP = defHostIP
   134  	}
   135  
   136  	// Adjust HostPortEnd if this is not a range.
   137  	if bnd.HostPortEnd == 0 {
   138  		bnd.HostPortEnd = bnd.HostPort
   139  	}
   140  
   141  	// Construct the container side transport address
   142  	container, err := bnd.ContainerAddr()
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	// Try up to maxAllocatePortAttempts times to get a port that's
   148  	// not already allocated.
   149  	for i := 0; i < maxAllocatePortAttempts; i++ {
   150  		if host, err = n.portMapper.MapRange(container, bnd.HostIP,
   151  			int(bnd.HostPort), int(bnd.HostPortEnd), false); err == nil {
   152  			break
   153  		}
   154  		// There is no point in immediately retrying to map an
   155  		// explicitly chosen port.
   156  		if bnd.HostPort != 0 {
   157  			logrus.Warnf(
   158  				"Failed to allocate and map port %d-%d: %s",
   159  				bnd.HostPort, bnd.HostPortEnd, err)
   160  			break
   161  		}
   162  		logrus.Warnf("Failed to allocate and map port: %s, retry: %d",
   163  			err, i+1)
   164  	}
   165  	if err != nil {
   166  		return err
   167  	}
   168  
   169  	// Save the host port (regardless it was or not specified in the
   170  	// binding)
   171  	switch netAddr := host.(type) {
   172  	case *net.TCPAddr:
   173  		bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
   174  		return nil
   175  	case *net.UDPAddr:
   176  		bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
   177  		return nil
   178  	default:
   179  		// For completeness
   180  		return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
   181  	}
   182  }
   183  
   184  func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
   185  	err := n.releasePortsInternal(ep.portMapping)
   186  	if err != nil {
   187  		return nil
   188  	}
   189  
   190  	// remove rules if there are any port mappings
   191  	if len(ep.portMapping) > 0 {
   192  		removePFRules(ep.id)
   193  	}
   194  
   195  	return nil
   196  
   197  }
   198  
   199  func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error {
   200  	var errorBuf bytes.Buffer
   201  
   202  	// Attempt to release all port bindings, do not stop on failure
   203  	for _, m := range bindings {
   204  		if err := n.releasePort(m); err != nil {
   205  			errorBuf.WriteString(
   206  				fmt.Sprintf(
   207  					"\ncould not release %v because of %v",
   208  					m, err))
   209  		}
   210  	}
   211  
   212  	if errorBuf.Len() != 0 {
   213  		return errors.New(errorBuf.String())
   214  	}
   215  	return nil
   216  }
   217  
   218  func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {
   219  	// Construct the host side transport address
   220  	host, err := bnd.HostAddr()
   221  	if err != nil {
   222  		return err
   223  	}
   224  	return n.portMapper.Unmap(host)
   225  }