github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/portmapper/proxy.go (about) 1 package portmapper 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "net" 8 "os" 9 "os/exec" 10 "time" 11 12 "github.com/ishidawataru/sctp" 13 ) 14 15 var userlandProxyCommandName = "docker-proxy" 16 17 type userlandProxy interface { 18 Start() error 19 Stop() error 20 } 21 22 // ipVersion refers to IP version - v4 or v6 23 type ipVersion string 24 25 const ( 26 // IPv4 is version 4 27 ipv4 ipVersion = "4" 28 // IPv4 is version 6 29 ipv6 ipVersion = "6" 30 ) 31 32 // proxyCommand wraps an exec.Cmd to run the userland TCP and UDP 33 // proxies as separate processes. 34 type proxyCommand struct { 35 cmd *exec.Cmd 36 } 37 38 func (p *proxyCommand) Start() error { 39 r, w, err := os.Pipe() 40 if err != nil { 41 return fmt.Errorf("proxy unable to open os.Pipe %s", err) 42 } 43 defer r.Close() 44 p.cmd.ExtraFiles = []*os.File{w} 45 if err := p.cmd.Start(); err != nil { 46 return err 47 } 48 w.Close() 49 50 errchan := make(chan error, 1) 51 go func() { 52 buf := make([]byte, 2) 53 r.Read(buf) 54 55 if string(buf) != "0\n" { 56 errStr, err := ioutil.ReadAll(r) 57 if err != nil { 58 errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err) 59 return 60 } 61 62 errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr) 63 return 64 } 65 errchan <- nil 66 }() 67 68 select { 69 case err := <-errchan: 70 return err 71 case <-time.After(16 * time.Second): 72 return fmt.Errorf("Timed out proxy starting the userland proxy") 73 } 74 } 75 76 func (p *proxyCommand) Stop() error { 77 if p.cmd.Process != nil { 78 if err := p.cmd.Process.Signal(os.Interrupt); err != nil { 79 return err 80 } 81 return p.cmd.Wait() 82 } 83 return nil 84 } 85 86 // dummyProxy just listen on some port, it is needed to prevent accidental 87 // port allocations on bound port, because without userland proxy we using 88 // iptables rules and not net.Listen 89 type dummyProxy struct { 90 listener io.Closer 91 addr net.Addr 92 ipVersion ipVersion 93 } 94 95 func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) { 96 // detect version of hostIP to bind only to correct version 97 version := ipv4 98 if hostIP.To4() == nil { 99 version = ipv6 100 } 101 switch proto { 102 case "tcp": 103 addr := &net.TCPAddr{IP: hostIP, Port: hostPort} 104 return &dummyProxy{addr: addr, ipVersion: version}, nil 105 case "udp": 106 addr := &net.UDPAddr{IP: hostIP, Port: hostPort} 107 return &dummyProxy{addr: addr, ipVersion: version}, nil 108 case "sctp": 109 addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort} 110 return &dummyProxy{addr: addr, ipVersion: version}, nil 111 default: 112 return nil, fmt.Errorf("Unknown addr type: %s", proto) 113 } 114 } 115 116 func (p *dummyProxy) Start() error { 117 switch addr := p.addr.(type) { 118 case *net.TCPAddr: 119 l, err := net.ListenTCP("tcp"+string(p.ipVersion), addr) 120 if err != nil { 121 return err 122 } 123 p.listener = l 124 case *net.UDPAddr: 125 l, err := net.ListenUDP("udp"+string(p.ipVersion), addr) 126 if err != nil { 127 return err 128 } 129 p.listener = l 130 case *sctp.SCTPAddr: 131 l, err := sctp.ListenSCTP("sctp"+string(p.ipVersion), addr) 132 if err != nil { 133 return err 134 } 135 p.listener = l 136 default: 137 return fmt.Errorf("Unknown addr type: %T", p.addr) 138 } 139 return nil 140 } 141 142 func (p *dummyProxy) Stop() error { 143 if p.listener != nil { 144 return p.listener.Close() 145 } 146 return nil 147 }