Browse code

Emulate DNAT in userland for loopback-to-loopback connections. This makes container ports available from localhost.

Solomon Hykes authored on 2013/04/20 11:35:44
Showing 1 changed files
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"encoding/binary"
5 5
 	"errors"
6 6
 	"fmt"
7
+	"io"
7 8
 	"log"
8 9
 	"net"
9 10
 	"os/exec"
... ...
@@ -221,10 +222,55 @@ func (mapper *PortMapper) Map(port int, dest net.TCPAddr) error {
221 221
 	if err := mapper.iptablesForward("-A", port, dest); err != nil {
222 222
 		return err
223 223
 	}
224
+
224 225
 	mapper.mapping[port] = dest
226
+	listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
227
+	if err != nil {
228
+		mapper.Unmap(port)
229
+		return err
230
+	}
231
+	// FIXME: store the listener so we can close it at Unmap
232
+	go proxy(listener, "tcp", dest.String())
225 233
 	return nil
226 234
 }
227 235
 
236
+// proxy listens for socket connections on `listener`, and forwards them unmodified
237
+// to `proto:address`
238
+func proxy(listener net.Listener, proto, address string) error {
239
+	Debugf("proxying to %s:%s", proto, address)
240
+	defer Debugf("Done proxying to %s:%s", proto, address)
241
+	for {
242
+		Debugf("Listening on %s", listener)
243
+		src, err := listener.Accept()
244
+		if err != nil {
245
+			return err
246
+		}
247
+		Debugf("Connecting to %s:%s", proto, address)
248
+		dst, err := net.Dial(proto, address)
249
+		if err != nil {
250
+			log.Printf("Error connecting to %s:%s: %s", proto, address, err)
251
+			src.Close()
252
+			continue
253
+		}
254
+		Debugf("Connected to backend, splicing")
255
+		splice(src, dst)
256
+	}
257
+	return nil
258
+}
259
+
260
+func halfSplice(dst, src net.Conn) error {
261
+	_, err := io.Copy(dst, src)
262
+	// FIXME: on EOF from a tcp connection, pass WriteClose()
263
+	dst.Close()
264
+	src.Close()
265
+	return err
266
+}
267
+
268
+func splice(a, b net.Conn) {
269
+	go halfSplice(a, b)
270
+	go halfSplice(b, a)
271
+}
272
+
228 273
 func (mapper *PortMapper) Unmap(port int) error {
229 274
 	dest, ok := mapper.mapping[port]
230 275
 	if !ok {