From 66c5b2f72cb8d40d16f342324f2bb58e11d4b93c Mon Sep 17 00:00:00 2001 From: Song Gao Date: Mon, 20 Mar 2017 22:40:07 -0700 Subject: [PATCH] fake IFF_NO_PI on macOS (thanks @eliezedeck ) (#21) --- README.md | 4 +-- syscalls_darwin.go | 64 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 072ce71..d723413 100644 --- a/README.md +++ b/README.md @@ -147,8 +147,8 @@ $ ping 10.1.0.20 You'd see the ICMP packets printed out: ``` -2016/10/23 20:21:53 Interface Name: utun2 -2016/10/23 20:22:40 Packet Received: 00 00 00 02 45 00 00 54 4a 2e 00 00 40 01 1c 5c 0a 01 00 0a 0a 01 00 14 08 00 31 51 f0 f9 00 00 58 0d 7e 80 00 03 14 21 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 +2017/03/20 21:17:30 Interface Name: utun2 +2017/03/20 21:17:40 Packet Received: 45 00 00 54 e9 1d 00 00 40 01 7d 6c 0a 01 00 0a 0a 01 00 14 08 00 ee 04 21 15 00 00 58 d0 a9 64 00 08 fb a5 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 ``` ### TAP on Windows: diff --git a/syscalls_darwin.go b/syscalls_darwin.go index 4f53283..df2b285 100644 --- a/syscalls_darwin.go +++ b/syscalls_darwin.go @@ -5,7 +5,9 @@ package water import ( "errors" "fmt" + "io" "os" + "sync" "syscall" "unsafe" ) @@ -112,12 +114,68 @@ func newTUN(string) (ifce *Interface, err error) { } return &Interface{ - isTAP: false, - name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]), - ReadWriteCloser: os.NewFile(uintptr(fd), string(ifName.name[:])), + isTAP: false, + name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]), + ReadWriteCloser: &tunReadCloser{ + f: os.NewFile(uintptr(fd), string(ifName.name[:])), + }, }, nil } +// tunReadCloser is a hack to work around the first 4 bytes "packet +// information" because there doesn't seem to be an IFF_NO_PI for darwin. +type tunReadCloser struct { + f io.ReadWriteCloser + + rMu sync.Mutex + rBuf []byte + + wMu sync.Mutex + wBuf []byte +} + +var _ io.ReadWriteCloser = (*tunReadCloser)(nil) + +func (t *tunReadCloser) Read(to []byte) (int, error) { + t.rMu.Lock() + defer t.rMu.Unlock() + + if cap(t.rBuf) < len(to)+4 { + t.rBuf = make([]byte, len(to)+4) + } + t.rBuf = t.rBuf[:len(to)+4] + + n, err := t.f.Read(t.rBuf) + copy(to, t.rBuf[4:]) + return n - 4, err +} + +func (t *tunReadCloser) Write(from []byte) (int, error) { + t.wMu.Lock() + defer t.wMu.Unlock() + + if cap(t.wBuf) < len(from)+4 { + t.wBuf = make([]byte, len(from)+4) + } + t.wBuf = t.wBuf[:len(from)+4] + + t.wBuf[3] = 2 // Family: IP (2) + copy(t.wBuf[4:], from) + + n, err := t.f.Write(t.wBuf) + return n - 4, err +} + +func (t *tunReadCloser) Close() error { + // lock to make sure no read/write is in process. + t.rMu.Lock() + defer t.rMu.Unlock() + t.wMu.Lock() + defer t.wMu.Unlock() + + return t.f.Close() +} + func newTAP(ifName string) (ifce *Interface, err error) { return nil, errors.New("tap interface not implemented on this platform") }