123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // +build solaris
- package bridge
- import (
- "bytes"
- "errors"
- "fmt"
- "net"
- "os"
- "os/exec"
- "github.com/Sirupsen/logrus"
- "github.com/docker/libnetwork/types"
- )
- var (
- defaultBindingIP = net.IPv4(0, 0, 0, 0)
- )
- const (
- maxAllocatePortAttempts = 10
- )
- func addPFRules(epid, bindIntf string, bs []types.PortBinding) {
- var id string
- if len(epid) > 12 {
- id = epid[:12]
- } else {
- id = epid
- }
- fname := "/var/lib/docker/network/files/pf." + id
- f, err := os.OpenFile(fname,
- os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
- if err != nil {
- logrus.Warn("cannot open temp pf file")
- return
- }
- for _, b := range bs {
- r := fmt.Sprintf(
- "pass in on %s proto %s from any to (%s) "+
- "port %d rdr-to %s port %d\n", bindIntf,
- b.Proto.String(), bindIntf, b.HostPort,
- b.IP.String(), b.Port)
- _, err = f.WriteString(r)
- if err != nil {
- logrus.Warnf("cannot write firewall rules to %s: %v", fname, err)
- }
- }
- f.Close()
- anchor := fmt.Sprintf("_auto/docker/ep%s", id)
- err = exec.Command("/usr/sbin/pfctl", "-a", anchor, "-f", fname).Run()
- if err != nil {
- logrus.Warnf("failed to add firewall rules: %v", err)
- }
- os.Remove(fname)
- }
- func removePFRules(epid string) {
- var id string
- if len(epid) > 12 {
- id = epid[:12]
- } else {
- id = epid
- }
- anchor := fmt.Sprintf("_auto/docker/ep%s", id)
- err := exec.Command("/usr/sbin/pfctl", "-a", anchor, "-F", "all").Run()
- if err != nil {
- logrus.Warnf("failed to remove firewall rules: %v", err)
- }
- }
- func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, bindIntf string, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
- if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
- return nil, nil
- }
- defHostIP := defaultBindingIP
- if reqDefBindIP != nil {
- defHostIP = reqDefBindIP
- }
- bs, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, bindIntf, ep.addr.IP, defHostIP, ulPxyEnabled)
- if err != nil {
- return nil, err
- }
- // Add PF rules for port bindings, if any
- if len(bs) > 0 {
- addPFRules(ep.id, bindIntf, bs)
- }
- return bs, err
- }
- func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, bindIntf string, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
- bs := make([]types.PortBinding, 0, len(bindings))
- for _, c := range bindings {
- b := c.GetCopy()
- if err := n.allocatePort(&b, containerIP, defHostIP); err != nil {
- // On allocation failure,release previously
- // allocated ports. On cleanup error, just log
- // a warning message
- if cuErr := n.releasePortsInternal(bs); cuErr != nil {
- logrus.Warnf("Upon allocation failure "+
- "for %v, failed to clear previously "+
- "allocated port bindings: %v", b, cuErr)
- }
- return nil, err
- }
- bs = append(bs, b)
- }
- return bs, nil
- }
- func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP) error {
- var (
- host net.Addr
- err error
- )
- // Store the container interface address in the operational binding
- bnd.IP = containerIP
- // Adjust the host address in the operational binding
- if len(bnd.HostIP) == 0 {
- bnd.HostIP = defHostIP
- }
- // Adjust HostPortEnd if this is not a range.
- if bnd.HostPortEnd == 0 {
- bnd.HostPortEnd = bnd.HostPort
- }
- // Construct the container side transport address
- container, err := bnd.ContainerAddr()
- if err != nil {
- return err
- }
- // Try up to maxAllocatePortAttempts times to get a port that's
- // not already allocated.
- for i := 0; i < maxAllocatePortAttempts; i++ {
- if host, err = n.portMapper.MapRange(container, bnd.HostIP,
- int(bnd.HostPort), int(bnd.HostPortEnd), false); err == nil {
- break
- }
- // There is no point in immediately retrying to map an
- // explicitly chosen port.
- if bnd.HostPort != 0 {
- logrus.Warnf(
- "Failed to allocate and map port %d-%d: %s",
- bnd.HostPort, bnd.HostPortEnd, err)
- break
- }
- logrus.Warnf("Failed to allocate and map port: %s, retry: %d",
- err, i+1)
- }
- if err != nil {
- return err
- }
- // Save the host port (regardless it was or not specified in the
- // binding)
- switch netAddr := host.(type) {
- case *net.TCPAddr:
- bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
- return nil
- case *net.UDPAddr:
- bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
- return nil
- default:
- // For completeness
- return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
- }
- }
- func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
- err := n.releasePortsInternal(ep.portMapping)
- if err != nil {
- return nil
- }
- // remove rules if there are any port mappings
- if len(ep.portMapping) > 0 {
- removePFRules(ep.id)
- }
- return nil
- }
- func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error {
- var errorBuf bytes.Buffer
- // Attempt to release all port bindings, do not stop on failure
- for _, m := range bindings {
- if err := n.releasePort(m); err != nil {
- errorBuf.WriteString(
- fmt.Sprintf(
- "\ncould not release %v because of %v",
- m, err))
- }
- }
- if errorBuf.Len() != 0 {
- return errors.New(errorBuf.String())
- }
- return nil
- }
- func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {
- // Construct the host side transport address
- host, err := bnd.HostAddr()
- if err != nil {
- return err
- }
- return n.portMapper.Unmap(host)
- }
|