123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package portmapper
- import (
- "errors"
- "fmt"
- "net"
- "sync"
- log "github.com/Sirupsen/logrus"
- "github.com/docker/docker/daemon/networkdriver/portallocator"
- "github.com/docker/docker/pkg/iptables"
- )
- type mapping struct {
- proto string
- userlandProxy UserlandProxy
- host net.Addr
- container net.Addr
- }
- var (
- chain *iptables.Chain
- lock sync.Mutex
- // udp:ip:port
- currentMappings = make(map[string]*mapping)
- NewProxy = NewProxyCommand
- )
- var (
- ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
- ErrPortMappedForIP = errors.New("port is already mapped to ip")
- ErrPortNotMapped = errors.New("port is not mapped")
- )
- func SetIptablesChain(c *iptables.Chain) {
- chain = c
- }
- func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) {
- lock.Lock()
- defer lock.Unlock()
- var (
- m *mapping
- proto string
- allocatedHostPort int
- proxy UserlandProxy
- )
- switch container.(type) {
- case *net.TCPAddr:
- proto = "tcp"
- if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
- return nil, err
- }
- m = &mapping{
- proto: proto,
- host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort},
- container: container,
- }
- proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
- case *net.UDPAddr:
- proto = "udp"
- if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
- return nil, err
- }
- m = &mapping{
- proto: proto,
- host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort},
- container: container,
- }
- proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port)
- default:
- return nil, ErrUnknownBackendAddressType
- }
- // release the allocated port on any further error during return.
- defer func() {
- if err != nil {
- portallocator.ReleasePort(hostIP, proto, allocatedHostPort)
- }
- }()
- key := getKey(m.host)
- if _, exists := currentMappings[key]; exists {
- return nil, ErrPortMappedForIP
- }
- containerIP, containerPort := getIPAndPort(m.container)
- if err := forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
- return nil, err
- }
- cleanup := func() error {
- // need to undo the iptables rules before we return
- proxy.Stop()
- forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
- if err := portallocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
- return err
- }
- return nil
- }
- if err := proxy.Start(); err != nil {
- if err := cleanup(); err != nil {
- return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
- }
- return nil, err
- }
- m.userlandProxy = proxy
- currentMappings[key] = m
- return m.host, nil
- }
- func Unmap(host net.Addr) error {
- lock.Lock()
- defer lock.Unlock()
- key := getKey(host)
- data, exists := currentMappings[key]
- if !exists {
- return ErrPortNotMapped
- }
- data.userlandProxy.Stop()
- delete(currentMappings, key)
- containerIP, containerPort := getIPAndPort(data.container)
- hostIP, hostPort := getIPAndPort(data.host)
- if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
- log.Errorf("Error on iptables delete: %s", err)
- }
- switch a := host.(type) {
- case *net.TCPAddr:
- return portallocator.ReleasePort(a.IP, "tcp", a.Port)
- case *net.UDPAddr:
- return portallocator.ReleasePort(a.IP, "udp", a.Port)
- }
- return nil
- }
- func getKey(a net.Addr) string {
- switch t := a.(type) {
- case *net.TCPAddr:
- return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
- case *net.UDPAddr:
- return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
- }
- return ""
- }
- func getIPAndPort(a net.Addr) (net.IP, int) {
- switch t := a.(type) {
- case *net.TCPAddr:
- return t.IP, t.Port
- case *net.UDPAddr:
- return t.IP, t.Port
- }
- return nil, 0
- }
- func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
- if chain == nil {
- return nil
- }
- return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
- }
|