summaryrefslogtreecommitdiffhomepage
path: root/tunnel
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2022-02-17 23:39:16 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2022-03-18 22:07:58 +0100
commited693a8e2f5f493655dc0810c281af2207537a96 (patch)
tree0c7e7b06631230864b608c7137353338e9270d4a /tunnel
parent75ce7ab5b8d2657822f28812826ecccfa08e88a1 (diff)
WIP transparent proxy
Diffstat (limited to 'tunnel')
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java20
-rw-r--r--tunnel/tools/libwg-go/api-android.go12
-rw-r--r--tunnel/tools/libwg-go/conntrack.go45
-rw-r--r--tunnel/tools/libwg-go/go.mod1
-rw-r--r--tunnel/tools/libwg-go/go.sum2
-rw-r--r--tunnel/tools/libwg-go/http-proxy.go278
-rw-r--r--tunnel/tools/libwg-go/nat-tun.go383
-rw-r--r--tunnel/tools/libwg-go/service.go17
-rw-r--r--tunnel/tools/libwg-go/transparent.go31
9 files changed, 545 insertions, 244 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
index cc22b548..5d31dc84 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
@@ -14,6 +14,7 @@ import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.system.OsConstants;
import android.util.Log;
@@ -342,19 +343,24 @@ public final class GoBackend implements Backend {
StreamObserver<ReverseResponse> responseObserver = new StreamObserver<ReverseResponse>() {
@Override
public void onNext(ReverseResponse resp) {
+ String pkg = "";
int uid = connectivityManager.getConnectionOwnerUid(resp.getUid().getProtocol(), toInetSocketAddress(resp.getUid().getLocal()), toInetSocketAddress(resp.getUid().getRemote()));
- PackageManager pm = context.getPackageManager();
- String pkg = pm.getNameForUid(uid);
- String[] pkgs = pm.getPackagesForUid(uid);
- Log.i(TAG, "reverse onNext uid:" + uid + " package:" + pkg);
- for (int i=0; i < pkgs.length; i++) {
- Log.i(TAG, "getPackagesForUid() = " + pkgs[i]);
+ if (uid != Process.INVALID_UID) {
+ PackageManager pm = context.getPackageManager();
+ pkg = pm.getNameForUid(uid);
+ String[] pkgs = pm.getPackagesForUid(uid);
+ Log.i(TAG, "reverse onNext uid:" + uid + " package:" + pkg);
+ for (int i=0; i < pkgs.length; i++) {
+ Log.i(TAG, "getPackagesForUid() = " + pkgs[i]);
+ }
+ } else {
+ Log.i(TAG, "Connection not found");
}
ReverseRequest req = ReverseRequest.newBuilder()
.setUid(GetConnectionOwnerUidResponse.newBuilder()
.setUid(uid)
- .setPackage(pkg)
+ .setPackage(pkg != null ? pkg: "")
.build())
.build();
diff --git a/tunnel/tools/libwg-go/api-android.go b/tunnel/tools/libwg-go/api-android.go
index 2ce9c49c..2dc5f53b 100644
--- a/tunnel/tools/libwg-go/api-android.go
+++ b/tunnel/tools/libwg-go/api-android.go
@@ -82,13 +82,23 @@ func wgTurnOn(interfaceName string, tunFd int32, settings string) int32 {
nativeTun, name, err := CreateUnmonitoredTUNFromFD(int(tunFd))
- tun, err := NewNatTun(nativeTun)
+ ct := NewConntrack()
+ service.http_proxy.SetConntrack(ct)
+
+ tun, err := NewNatTun(nativeTun, ct)
if err != nil {
unix.Close(int(tunFd))
logger.Errorf("CreateUnmonitoredTUNFromFD: %v", err)
return -1
}
+ if service != nil && service.http_proxy != nil {
+ tun.addTranslation(false, 80, int(service.http_proxy.addrPort.Port()))
+ tun.addTranslation(false, 443, int(service.http_proxy.tlsAddrPort.Port()))
+ tun.addTranslation(true, 80, int(service.http_proxy.addrPort.Port()))
+ tun.addTranslation(true, 443, int(service.http_proxy.tlsAddrPort.Port()))
+ }
+
logger.Verbosef("Attaching to interface %v", name)
device := device.NewDevice(tun, conn.NewStdNetBind(), logger)
diff --git a/tunnel/tools/libwg-go/conntrack.go b/tunnel/tools/libwg-go/conntrack.go
new file mode 100644
index 00000000..788b1b80
--- /dev/null
+++ b/tunnel/tools/libwg-go/conntrack.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "sync"
+
+ "golang.zx2c4.com/go118/netip"
+)
+
+type connection struct {
+ src netip.AddrPort
+ dst netip.AddrPort
+}
+
+func Connection(src, dst netip.AddrPort) connection {
+ return connection{
+ src: src,
+ dst: dst,
+ }
+}
+
+type Conntrack struct {
+ connections map[connection]connection
+ connectionsMutex sync.RWMutex
+}
+
+func NewConntrack() *Conntrack {
+ return &Conntrack{
+ connections: make(map[connection]connection),
+ connectionsMutex: sync.RWMutex{},
+ }
+}
+
+func (ct *Conntrack) addConnection(new, orig connection) {
+ ct.connectionsMutex.Lock()
+ ct.connections[new] = orig
+ ct.connectionsMutex.Unlock()
+}
+
+func (ct *Conntrack) lookupConnection(new connection) (connection, bool) {
+ ct.connectionsMutex.RLock()
+ c, ok := ct.connections[new]
+ ct.connectionsMutex.RUnlock()
+ return c, ok
+}
+
diff --git a/tunnel/tools/libwg-go/go.mod b/tunnel/tools/libwg-go/go.mod
index 00105ab0..7df5aa4d 100644
--- a/tunnel/tools/libwg-go/go.mod
+++ b/tunnel/tools/libwg-go/go.mod
@@ -13,6 +13,7 @@ require (
require (
github.com/golang/protobuf v1.5.2 // indirect
+ github.com/inconshreveable/go-vhost v0.0.0-20160627193104-06d84117953b // indirect
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
golang.org/x/net v0.0.0-20211111083644-e5c967477495 // indirect
golang.org/x/text v0.3.6 // indirect
diff --git a/tunnel/tools/libwg-go/go.sum b/tunnel/tools/libwg-go/go.sum
index 9039129d..b990a8c3 100644
--- a/tunnel/tools/libwg-go/go.sum
+++ b/tunnel/tools/libwg-go/go.sum
@@ -50,6 +50,8 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/inconshreveable/go-vhost v0.0.0-20160627193104-06d84117953b h1:IpLPmn6Re21F0MaV6Zsc5RdSE6KuoFpWmHiUSEs3PrE=
+github.com/inconshreveable/go-vhost v0.0.0-20160627193104-06d84117953b/go.mod h1:aA6DnFhALT3zH0y+A39we+zbrdMC2N0X/q21e6FI0LU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
diff --git a/tunnel/tools/libwg-go/http-proxy.go b/tunnel/tools/libwg-go/http-proxy.go
index 3c0bef47..54b56f79 100644
--- a/tunnel/tools/libwg-go/http-proxy.go
+++ b/tunnel/tools/libwg-go/http-proxy.go
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/elazarl/goproxy"
+ "github.com/inconshreveable/go-vhost"
"golang.zx2c4.com/go118/netip"
"golang.zx2c4.com/wireguard/device"
@@ -17,6 +18,10 @@ import (
)
const (
+ APIPA_PREFIX = "169.254.0.0/16"
+// ULA_PREFIX = "fdba:4b51:1606:a61d::/64"
+ ULA_PREFIX = "fdba:4b51:1606:0000::/64"
+
// Imported from Firefox' ProxyAutoConfig.cpp
// https://searchfox.org/mozilla-central/source/netwerk/base/ProxyAutoConfig.cpp
// This Source Code Form is subject to the terms of the Mozilla Public
@@ -257,23 +262,27 @@ const (
)
type HttpProxy struct {
+ conntrack *Conntrack
listener net.Listener
+ tlsListener net.Listener
logger *device.Logger
addrPort netip.AddrPort
- uidRequest chan netip.AddrPort
- uidResponse chan string
+ tlsAddrPort netip.AddrPort
pacFileUrl *url.URL
ctx *duktape.Context
+ defaultProxy *goproxy.ProxyHttpServer
+ uidRequest chan AddrPortPair
+ uidResponse chan string
}
-func NewHttpProxy(uidRequest chan netip.AddrPort, uidResponse chan string, logger *device.Logger, pacFileUrl *url.URL) *HttpProxy {
+func NewHttpProxy(uidRequest chan AddrPortPair, uidResponse chan string, logger *device.Logger, pacFileUrl *url.URL) *HttpProxy {
logger.Verbosef("NewHttpProxy")
return &HttpProxy{
listener: nil,
logger: logger,
+ pacFileUrl: pacFileUrl,
uidRequest: uidRequest,
uidResponse: uidResponse,
- pacFileUrl: pacFileUrl,
}
}
@@ -281,6 +290,10 @@ var (
ASCII_PAC_UTILS_NAMES = []string{"dnsDomainIs", "dnsDomainLevels", "isValidIpAddress", "convert_addr", "isInNet", "isPlainHostName", "isResolvable", "localHostOrDomainIs", "shExpMatch", "weekdayRange", "dateRange", "timeRange"}
)
+func (p *HttpProxy) SetConntrack(ct *Conntrack) {
+ p.conntrack = ct
+}
+
func (p *HttpProxy) GetAddrPort() netip.AddrPort {
return p.addrPort
}
@@ -292,6 +305,15 @@ func newGoProxy(proxyUrl string) *goproxy.ProxyHttpServer {
return url.Parse(proxyUrl)
}
proxy.ConnectDial = proxy.NewConnectDialToProxy(proxyUrl)
+ proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ if req.Host == "" {
+ fmt.Fprintln(w, "Cannot handle requests without Host header, e.g., HTTP 1.0")
+ return
+ }
+ req.URL.Scheme = "http"
+ req.URL.Host = req.Host
+ proxy.ServeHTTP(w, req)
+ })
return proxy
}
@@ -419,34 +441,104 @@ func (p *HttpProxy) Start() (listen_port uint16, err error) {
proxyMap["bbc.iplayer.android"] = newGoProxy("http://10.49.124.111:8888")
proxyMap["no.nrk.tv"] = newGoProxy("http://10.49.124.115:8888")
- defaultProxy := goproxy.NewProxyHttpServer()
- defaultProxy.Verbose = true
+ // TODO: Debugging NATed tls proxy
+ // defaultProxy := goproxy.NewProxyHttpServer()
+ // defaultProxy.Verbose = true
+ p.defaultProxy = newGoProxy("http://10.49.32.1:8888")
+ proxyMap[""] = p.defaultProxy
+
+ listen_port, err = p.startRegularProxy(pacFileBody, proxyMap)
+ if err != nil {
+ return
+ }
+
+ err = p.startTlsProxy(pacFileBody, proxyMap)
+ if err != nil {
+ p.Stop()
+ }
+ return
+}
-// listener, err := net.Listen("tcp", "localhost:")
- listener, err := net.Listen("tcp", "169.254.0.2:")
+func (p *HttpProxy) startRegularProxy(pacFileBody string, proxyMap map[string]*goproxy.ProxyHttpServer) (listen_port uint16, err error) {
+ p.listener, err = net.Listen("tcp", "[::]:")
if err != nil {
return
}
- p.addrPort, err = netip.ParseAddrPort(listener.Addr().String())
+ p.addrPort, err = netip.ParseAddrPort(p.listener.Addr().String())
if err != nil {
return
}
listen_port = p.addrPort.Port()
- handler := NewHttpHandler(defaultProxy, p.logger)
+ handler := NewHttpHandler(p, p.defaultProxy, pacFileBody, proxyMap, p.logger)
+
+ go http.Serve(NewUidListener(p, p.listener, handler, p.logger), handler)
+ return
+}
- go http.Serve(NewUidListener(listener, handler, p.uidRequest, p.uidResponse, pacFileBody, proxyMap, p.logger), handler)
+func (p *HttpProxy) startTlsProxy(pacFileBody string, proxyMap map[string]*goproxy.ProxyHttpServer) (err error) {
+ p.tlsListener, err = net.Listen("tcp", "[::]:")
+ if err != nil {
+ return
+ }
- return
+ p.tlsAddrPort, err = netip.ParseAddrPort(p.tlsListener.Addr().String())
+ if err != nil {
+ return
+ }
+
+ handler := NewHttpHandler(p, p.defaultProxy, pacFileBody, proxyMap, p.logger)
+
+ go func () {
+ for {
+ conn, err := p.tlsListener.Accept()
+
+ if err != nil {
+ p.logger.Verbosef("Error accepting new connection - %v", err)
+ return
+ }
+
+ go func(conn net.Conn) {
+ tlsConn, err := vhost.TLS(conn)
+ if err != nil {
+ p.logger.Verbosef("Error TLS connection - %v", err)
+ }
+ if tlsConn.Host() == "" {
+ p.logger.Verbosef("Cannot support non-SNI enabled clients")
+ return
+ }
+ handler.addConnToProxyMap(conn)
+ connectReq := &http.Request{
+ Method: "CONNECT",
+ URL: &url.URL{
+ Opaque: tlsConn.Host(),
+ Host: net.JoinHostPort(tlsConn.Host(), "443"),
+ },
+ Host: tlsConn.Host(),
+ Header: make(http.Header),
+ // TODO fetch remote addr from NAT map
+ RemoteAddr: conn.RemoteAddr().String(),
+ }
+ resp := dumbResponseWriter{tlsConn}
+ handler.ServeHTTP(resp, connectReq)
+ }(conn)
+ }
+ }()
+ return
}
func (p *HttpProxy) Stop() {
if p.listener != nil {
p.logger.Verbosef("Close: %v", p.listener)
p.listener.Close()
- p.listener = nil
+// p.listener = nil
+ }
+ if p.tlsListener != nil {
+ p.logger.Verbosef("Close: %v", p.tlsListener)
+ p.tlsListener.Close()
+// p.tlsListener = nil
}
if p.ctx != nil {
p.ctx.DestroyHeap()
@@ -455,19 +547,82 @@ func (p *HttpProxy) Stop() {
}
type HttpHandler struct {
+ p *HttpProxy
defaultProxy *goproxy.ProxyHttpServer
logger *device.Logger
remoteAddrPkgMap map[string]*goproxy.ProxyHttpServer
+ apipaPrefix netip.Prefix
+ ulaPrefix netip.Prefix
+ uidRequest chan AddrPortPair
+ uidResponse chan string
+ ctx *duktape.Context
+ proxyMap map[string]*goproxy.ProxyHttpServer
}
-func NewHttpHandler(defaultProxy *goproxy.ProxyHttpServer, logger *device.Logger) *HttpHandler{
- return &HttpHandler{
+func NewHttpHandler(p *HttpProxy, defaultProxy *goproxy.ProxyHttpServer, pacFileBody string, proxyMap map[string]*goproxy.ProxyHttpServer, logger *device.Logger) *HttpHandler{
+ apipa, _ := netip.ParsePrefix(APIPA_PREFIX)
+ ula, _ := netip.ParsePrefix(ULA_PREFIX)
+
+ h := &HttpHandler{
+ p: p,
defaultProxy: defaultProxy,
logger: logger,
remoteAddrPkgMap: make(map[string]*goproxy.ProxyHttpServer),
+ apipaPrefix: apipa,
+ ulaPrefix: ula,
+ uidRequest: p.uidRequest,
+ uidResponse: p.uidResponse,
+ proxyMap: proxyMap,
+ }
+
+ if pacFileBody != "" {
+ h.ctx = newPacFileCtx(pacFileBody, logger)
}
+
+ return h
}
+
+func (h *HttpHandler) addConnToProxyMap(c net.Conn) {
+ h.logger.Verbosef("Accept: %v -> %v", c.RemoteAddr().String(), c.LocalAddr().String())
+ local, err := netip.ParseAddrPort(c.RemoteAddr().String())
+ remote, err2 := netip.ParseAddrPort(c.LocalAddr().String())
+ if err != nil || err2 != nil{
+ // Skip
+ } else {
+ newConn := connection{src: local, dst: remote}
+ oldConn, ok := h.p.conntrack.lookupConnection(newConn)
+
+ if ok {
+ local = oldConn.src
+ remote = oldConn.dst
+ h.logger.Verbosef("Before NAT: %v -> %v", local, remote)
+ }
+
+ if !h.apipaPrefix.Contains(local.Addr()) && !h.ulaPrefix.Contains(local.Addr()) {
+ h.logger.Verbosef("uidRequest")
+ h.uidRequest <- AddrPortPair{local: local, remote: remote}
+
+ // TODO add timeout?
+ select {
+ case pkg := <-h.uidResponse:
+ h.logger.Verbosef("uidResponse: '%v'", pkg)
+
+ proxy, ok := h.proxyMap[pkg]
+ if !ok {
+ proxy = h.findProxyForPkg(pkg)
+ }
+
+ if proxy != nil {
+ h.remoteAddrPkgMap[c.RemoteAddr().String()] = proxy
+ }
+ }
+ } else {
+ h.logger.Verbosef("uidRequest skipped, %v -> %v", local, remote)
+ }
+ }
+}
+
func (h *HttpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
proxy, ok := h.remoteAddrPkgMap[req.RemoteAddr]
if ok && proxy != nil {
@@ -481,48 +636,21 @@ func (h *HttpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
proxy.ServeHTTP(rw, req)
} else {
- h.defaultProxy.ServeHTTP(rw, req)
+ h.defaultProxy.ServeHTTP(rw, req)
}
}
-// UidListener
-type UidListener struct {
- l net.Listener
- handler *HttpHandler
- logger *device.Logger
- uidRequest chan netip.AddrPort
- uidResponse chan string
- ctx *duktape.Context
- proxyMap map[string]*goproxy.ProxyHttpServer
-}
-
-func NewUidListener(listener net.Listener, handler *HttpHandler, uidRequest chan netip.AddrPort, uidResponse chan string, pacFileBody string, proxyMap map[string]*goproxy.ProxyHttpServer, logger *device.Logger) *UidListener{
- l := &UidListener{
- l: listener,
- handler: handler,
- logger: logger,
- uidRequest: uidRequest,
- uidResponse: uidResponse,
- proxyMap: proxyMap,
- }
-
- if pacFileBody != "" {
- l.ctx = newPacFileCtx(pacFileBody, logger)
- }
- return l
-}
-
-func (l *UidListener) findProxyForPkg(pkg string) *goproxy.ProxyHttpServer {
- if l.ctx == nil {
+func (h *HttpHandler) findProxyForPkg(pkg string) *goproxy.ProxyHttpServer {
+ if h.ctx == nil {
return nil
}
find := func() (res string, err error) {
- l.logger.Verbosef("Call FindProxyForPkg %v %v", pkg)
- res, err = FindProxyForPkg(l.ctx, pkg, l.logger)
- l.logger.Verbosef("FindProxyForPkg res %v %v", res, err)
+ h.logger.Verbosef("Call FindProxyForPkg %v %v", pkg)
+ res, err = FindProxyForPkg(h.ctx, pkg, h.logger)
+ h.logger.Verbosef("FindProxyForPkg res %v %v", res, err)
if err != nil {
- l.logger.Verbosef("FindProxyForPkg result is: %v stack:%v", res, l.ctx.GetTop())
+ h.logger.Verbosef("FindProxyForPkg result is: %v stack:%v", res, h.ctx.GetTop())
return "", err
} else {
values := strings.Split(strings.Trim(res, " "), ";")
@@ -542,38 +670,44 @@ func (l *UidListener) findProxyForPkg(pkg string) *goproxy.ProxyHttpServer {
}
proxy := newGoProxy("http://" + res)
- l.proxyMap[res] = proxy
+ h.proxyMap[res] = proxy
return proxy
}
-func (l *UidListener) Accept() (net.Conn, error) {
- c, err := l.l.Accept()
- if err != nil {
- return c, err
- }
+type AddrPortPair struct {
+ local netip.AddrPort
+ remote netip.AddrPort
+}
- l.logger.Verbosef("Accept: %v", c.RemoteAddr().String())
- addr_port, err := netip.ParseAddrPort(c.RemoteAddr().String())
- if err == nil {
- l.logger.Verbosef("uidRequest")
- l.uidRequest <- addr_port
+// UidListener
+type UidListener struct {
+ p *HttpProxy
+ l net.Listener
+ handler *HttpHandler
+ logger *device.Logger
+}
- // TODO add timeout?
- select {
- case pkg := <-l.uidResponse:
- l.logger.Verbosef("uidResponse: %v", pkg)
+func NewUidListener(httpProxy *HttpProxy, listener net.Listener, handler *HttpHandler, logger *device.Logger) *UidListener{
+ l := &UidListener{
+ p: httpProxy,
+ l: listener,
+ handler: handler,
+ logger: logger,
+ }
- proxy, ok := l.proxyMap[pkg]
- if !ok {
- proxy = l.findProxyForPkg(pkg)
- }
+ return l
+}
- if proxy != nil {
- l.handler.remoteAddrPkgMap[c.RemoteAddr().String()] = proxy
- }
- }
+func (l *UidListener) Accept() (net.Conn, error) {
+ c, err := l.l.Accept()
+ if err != nil {
+ l.logger.Verbosef("Accept failed: %v", err)
+ return c, err
}
+
+ l.handler.addConnToProxyMap(c)
+
return c, nil
}
diff --git a/tunnel/tools/libwg-go/nat-tun.go b/tunnel/tools/libwg-go/nat-tun.go
index abb3f9a5..529921d3 100644
--- a/tunnel/tools/libwg-go/nat-tun.go
+++ b/tunnel/tools/libwg-go/nat-tun.go
@@ -5,6 +5,8 @@
package main
+// TODO debug IPv6 NAT
+
import (
"encoding/binary"
"fmt"
@@ -15,85 +17,87 @@ import (
"golang.zx2c4.com/wireguard/tun"
)
-type connection struct {
- src netip.AddrPort
- dst netip.AddrPort
-}
-
-func Connection(src, dst netip.AddrPort) connection {
- return connection{
- src: src,
- dst: dst,
- }
-}
-
-type natTun struct {
- tun tun.Device
- intAddr netip.Addr
- extAddr netip.Addr
+type natEntry struct {
+ // intAddr netip.Addr
+ // extAddr netip.Addr
+ ipVersion int
+ loop bool
srcAddr netip.Addr
+ dstPort int
proxyAddr netip.Addr
proxyPort int
- connections map[connection]connection
}
-func NewNatTun(t tun.Device) (dev tun.Device, err error) {
- dev = nil
-
- extAddr, err := netip.ParseAddr("10.49.40.151")
- if err != nil {
- return
- }
-
- intAddr, err := netip.ParseAddr("10.49.40.101")
- if err != nil {
- return
- }
-
- proxyPort := 0
- var proxyAddr netip.Addr
- var srcAddr netip.Addr
- if service != nil && service.http_proxy != nil {
- srcAddr, err = netip.ParseAddr("169.254.0.1")
- if err != nil {
- return
- }
-
- proxyPort = int(service.http_proxy.addrPort.Port())
- proxyAddr, err = netip.ParseAddr("169.254.0.2")
- if err != nil {
- return
- }
- } else {
- proxyPort = 8888
- proxyAddr, err = netip.ParseAddr("10.49.124.115")
- if err != nil {
- return
- }
- }
+type natTun struct {
+ conntrack *Conntrack
+ tun tun.Device
+ translations []natEntry
+}
+func NewNatTun(t tun.Device, ct *Conntrack) (dev *natTun, err error) {
dev = &natTun{
+ conntrack: ct,
tun: t,
- intAddr: intAddr,
- extAddr: extAddr,
- srcAddr: srcAddr,
- proxyAddr: proxyAddr,
- proxyPort: proxyPort,
- connections: make(map[connection]connection),
+ translations: make([]natEntry, 4),
}
err = nil
return
}
-func (tun *natTun) addConnection(new, orig connection) {
- // TODO use mutex
- tun.connections[new] = orig
-}
+func (tun *natTun) addTranslation(ipv6 bool, dstPort int, proxyPort int) {
+ var ipVersion int
+ loop := true
+ var srcStr string
+ var proxyStr string
+ var srcAddr netip.Addr
+
+ if ipv6 {
+ ipVersion = IPV6_VERSION
+ //srcStr = "fe80::1"
+ //proxyStr = "fe80::2"
+ srcStr = "fdba:4b51:1606:a61d::1"
+ proxyStr = "fdba:4b51:1606:a61d::2"
+ // loop = false
+ // proxyStr = "2001:470:de6f:2fff::f780"
+ // proxyPort = 8888
+ } else {
+ ipVersion = IPV4_VERSION
+ srcStr = "169.254.0.1"
+ proxyStr = "169.254.0.2"
+ }
+
+ if srcStr != "" {
+ var err error
+ srcAddr, err = netip.ParseAddr(srcStr)
+ if err != nil {
+ return
+ }
+ }
+
+ proxyAddr, err := netip.ParseAddr(proxyStr)
+ if err != nil {
+ return
+ }
+
+ // proxyPort = 8888
+ // proxyAddr, err = netip.ParseAddr("10.49.124.115")
+ // if err != nil {
+ // return
+ // }
+
+ translation := natEntry{
+ // intAddr: intAddr,
+ // extAddr: extAddr,
+ ipVersion: ipVersion,
+ loop: loop,
+ srcAddr: srcAddr,
+ dstPort: dstPort,
+ proxyAddr: proxyAddr,
+ proxyPort: proxyPort,
+ }
-func (tun *natTun) lookupConnection(new connection) (connection, bool) {
- c, ok := tun.connections[new]
- return c, ok
+ tun.translations = append(tun.translations, translation)
}
func (tun *natTun) Name() (string, error) {
@@ -161,21 +165,41 @@ func putIPv4DstAddr(header []byte, addr netip.Addr) {
copy(header[IPV4_HEADER_DST_ADDR:IPV4_HEADER_DST_ADDR+4], addr.AsSlice())
}
+func getIPv6SrcAddr(header []byte) netip.Addr {
+ src, _ := netip.AddrFromSlice(header[IPV6_HEADER_SRC_ADDR:IPV6_HEADER_SRC_ADDR+16])
+ return src
+}
+
+func putIPv6SrcAddr(header []byte, addr netip.Addr) {
+ copy(header[IPV6_HEADER_SRC_ADDR:IPV6_HEADER_SRC_ADDR+16], addr.AsSlice())
+}
+
+func getIPv6DstAddr(header []byte) netip.Addr {
+ dst, _ := netip.AddrFromSlice(header[IPV6_HEADER_DST_ADDR:IPV6_HEADER_DST_ADDR+16])
+ return dst
+}
+
+func putIPv6DstAddr(header []byte, addr netip.Addr) {
+ copy(header[IPV6_HEADER_DST_ADDR:IPV6_HEADER_DST_ADDR+16], addr.AsSlice())
+}
+
func getSrcAddr(header []byte, version int) netip.Addr {
if version == IPV4_VERSION {
return getIPv4SrcAddr(header)
+ } else if version == IPV6_VERSION {
+ return getIPv6SrcAddr(header)
} else {
- // FIXME
- return netip.IPv6Unspecified()
- }
+ return netip.IPv6Unspecified()
+ }
}
func getDstAddr(header []byte, version int) netip.Addr {
if version == IPV4_VERSION {
return getIPv4DstAddr(header)
+ } else if version == IPV6_VERSION {
+ return getIPv6DstAddr(header)
} else {
- // FIXME
- return netip.IPv6Unspecified()
+ return netip.IPv6Unspecified()
}
}
@@ -226,52 +250,60 @@ func calculateChecksum(hc int, m, mPrim int) int {
}
func updateAddr(header []byte, version int, getFunc func (header []byte) netip.Addr, putFunc func (header []byte, addr netip.Addr), updateFunc func (netip.Addr) netip.Addr) {
- if version == IPV4_VERSION {
- addr := getFunc(header)
- newAddr := updateFunc(addr)
-
- if newAddr != addr {
- putFunc(header, newAddr)
- // TODO reduce code duplication see updateSrcAddr
- updateIPv4Checksum(header, func (checksum int) int {
- addr4 := addr.As4()
- newAddr4 := newAddr.As4()
- checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[0:2])), int(binary.BigEndian.Uint16(newAddr4[0:2])))
- checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[2:4])), int(binary.BigEndian.Uint16(newAddr4[2:4])))
- return checksum
- })
- updateTransportChecksum(header, version, func (checksum int) int {
- if version == IPV4_VERSION && checksum == 0 {
- return 0
- } else {
- addr4 := addr.As4()
- newAddr4 := newAddr.As4()
- checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[0:2])), int(binary.BigEndian.Uint16(newAddr4[0:2])))
- checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[2:4])), int(binary.BigEndian.Uint16(newAddr4[2:4])))
- return checksum
- }
- })
- }
- // } else if version == IPV6_VERSION {
- // addr := getIPv6SrcAddr(header)
- // putIPv6SrcAddr(header, updateFunc(addr))
+ addr := getFunc(header)
+ newAddr := updateFunc(addr)
+
+ if newAddr != addr {
+ putFunc(header, newAddr)
+ if version == IPV4_VERSION {
+ updateIPv4Checksum(header, func (checksum int) int {
+ addr4 := addr.As4()
+ newAddr4 := newAddr.As4()
+ checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[0:2])), int(binary.BigEndian.Uint16(newAddr4[0:2])))
+ checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[2:4])), int(binary.BigEndian.Uint16(newAddr4[2:4])))
+ return checksum
+ })
+ }
+ updateTransportChecksum(header, version, func (checksum int) int {
+ if checksum == 0 {
+ // if version == IPV6_VERSION {
+ // // Log error
+ // }
+ return 0
+ } else if version == IPV4_VERSION {
+ addr4 := addr.As4()
+ newAddr4 := newAddr.As4()
+ checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[0:2])), int(binary.BigEndian.Uint16(newAddr4[0:2])))
+ checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr4[2:4])), int(binary.BigEndian.Uint16(newAddr4[2:4])))
+ return checksum
+ } else if version == IPV6_VERSION {
+ addr6 := addr.As16()
+ newAddr6 := newAddr.As16()
+ for i := 0; i < 8; i++ {
+ checksum = calculateChecksum(checksum, int(binary.BigEndian.Uint16(addr6[i*2:(i+1)*2])), int(binary.BigEndian.Uint16(newAddr6[i*2:(i+1)*2])))
+ }
+ return checksum
+ } else {
+ return checksum
+ }
+ })
}
}
func updateSrcAddr(header []byte, version int, updateFunc func (netip.Addr) netip.Addr ) {
if version == IPV4_VERSION {
updateAddr(header, version, getIPv4SrcAddr, putIPv4SrcAddr, updateFunc)
- }
+ } else if version == IPV6_VERSION {
+ updateAddr(header, version, getIPv6SrcAddr, putIPv6SrcAddr, updateFunc)
+ }
}
func updateDstAddr(header []byte, version int, updateFunc func (netip.Addr) netip.Addr ) {
if version == IPV4_VERSION {
updateAddr(header, version, getIPv4DstAddr, putIPv4DstAddr, updateFunc)
+ } else if version == IPV6_VERSION {
+ updateAddr(header, version, getIPv6DstAddr, putIPv6DstAddr, updateFunc)
}
-
- // } else if version == IPV6_VERSION {
- // addr := getIPv6SrcAddr(header)
- // putIPv6SrcAddr(header, updateFunc(addr))
}
func updateDstPort(header []byte, version int, updateFunc func (int) int ) {
@@ -285,7 +317,10 @@ func updateDstPort(header []byte, version int, updateFunc func (int) int ) {
putUint16(transportPayload, TCP_HEADER_DST_PORT, newPort)
updateTransportChecksum(header, version, func (checksum int) int {
- if version == IPV4_VERSION && checksum == 0 {
+ if checksum == 0 {
+ // if version == IPV6_VERSION {
+ // // Log error
+ // }
return 0
} else {
checksum = calculateChecksum(checksum, int(port), int(newPort))
@@ -293,7 +328,6 @@ func updateDstPort(header []byte, version int, updateFunc func (int) int ) {
}
})
}
- // } else if version == IPV6_VERSION {
}
}
@@ -308,7 +342,10 @@ func updateSrcPort(header []byte, version int, updateFunc func (int) int ) {
putUint16(transportPayload, TCP_HEADER_SRC_PORT, newPort)
updateTransportChecksum(header, version, func (checksum int) int {
- if version == IPV4_VERSION && checksum == 0 {
+ if checksum == 0 {
+ // if version == IPV6_VERSION {
+ // // Log error
+ // }
return 0
} else {
checksum = calculateChecksum(checksum, int(port), int(newPort))
@@ -316,7 +353,6 @@ func updateSrcPort(header []byte, version int, updateFunc func (int) int ) {
}
})
}
- // } else if version == IPV6_VERSION {
}
}
@@ -398,60 +434,95 @@ func (tun *natTun) Read(buf []byte, offset int) (int, error) {
if err != nil {
// Ignore bad packet
- } else if version == IPV4_VERSION {
- isProxy := false
-
- srcPort := getSrcPort(header, version)
- origDstPort := 0
- newDstPort := 0
- var origSrcAddr netip.Addr
- var newSrcAddr netip.Addr
- var origDstAddr netip.Addr
- var newDstAddr netip.Addr
-
- updateDstPort(header, version, func(port int) int {
- // Transparent proxy HTTP and HTTPS
- if port == 80 || port == 443 {
- isProxy = true
- origDstPort = port
- newDstPort = tun.proxyPort
- return newDstPort
- } else {
- return port
- }
- })
- if tun.srcAddr.IsValid() {
- updateSrcAddr(header, version, func(addr netip.Addr) netip.Addr {
- if isProxy {
- origSrcAddr = addr
- newSrcAddr = tun.srcAddr
- return newSrcAddr
- } else {
- return addr
- }
- })
- }
- updateDstAddr(header, version, func(addr netip.Addr) netip.Addr {
- if isProxy {
- origDstAddr = addr
- newDstAddr = tun.proxyAddr
- return newDstAddr
- } else {
- return addr
- }
- })
-
- if isProxy {
- if !newSrcAddr.IsValid() {
+ } else if version == IPV4_VERSION || version == IPV6_VERSION {
+ origDstPort := getDstPort(header, version)
+
+ for _, entry := range(tun.translations) {
+ if version != entry.ipVersion {
+ continue
+ }
+
+ if origDstPort != entry.dstPort {
+ continue
+ }
+
+ srcPort := getSrcPort(header, version)
+ newDstPort := 0
+ var origSrcAddr netip.Addr
+ var origDstAddr netip.Addr
+ var newSrcAddr netip.Addr
+ var newDstAddr netip.Addr
+
+ updateDstPort(header, version, func(port int) int {
+ newDstPort = entry.proxyPort
+ return newDstPort
+ })
+
+ if entry.srcAddr.IsValid() {
+ updateSrcAddr(header, version, func(addr netip.Addr) netip.Addr {
+ origSrcAddr = addr
+ newSrcAddr = entry.srcAddr
+ return newSrcAddr
+ })
+ } else {
origSrcAddr = getSrcAddr(header, version)
newSrcAddr = origSrcAddr
- }
+ }
+
+ updateDstAddr(header, version, func(addr netip.Addr) netip.Addr {
+ origDstAddr = addr
+ newDstAddr = entry.proxyAddr
+ return newDstAddr
+ })
+
orig := Connection(netip.AddrPortFrom(origSrcAddr, uint16(srcPort)),
netip.AddrPortFrom(origDstAddr, uint16(origDstPort)))
new := Connection(netip.AddrPortFrom(newSrcAddr, uint16(srcPort)),
netip.AddrPortFrom(newDstAddr, uint16(newDstPort)))
- tun.addConnection(new, orig)
- }
+ tun.conntrack.addConnection(new, orig)
+
+ if !entry.loop {
+ return len, nil
+ }
+
+ // Write back to tunnel
+ writeBuf := buf[:offset+len]
+ written, err := tun.Write(writeBuf, offset)
+ if err != nil {
+ return 0, err
+ } else if written != len {
+ return 0, fmt.Errorf("NAT buffer partly written %v != %v", written, len)
+ } else {
+ return 0, nil
+ }
+ }
+
+ srcAddr := getSrcAddr(header, version)
+ srcPort := getSrcPort(header, version)
+ dstAddr := getDstAddr(header, version)
+ dstPort := getDstPort(header, version)
+
+ src := netip.AddrPortFrom(srcAddr, uint16(srcPort))
+ dst := netip.AddrPortFrom(dstAddr, uint16(dstPort))
+ new := Connection(dst, src)
+
+ orig, ok := tun.conntrack.lookupConnection(new)
+ if ok {
+ updateSrcAddr(header, version, func(netip.Addr) netip.Addr { return orig.dst.Addr() })
+ updateSrcPort(header, version, func(int) int { return int(orig.dst.Port()) })
+ updateDstAddr(header, version, func(netip.Addr) netip.Addr { return orig.src.Addr() })
+ updateDstPort(header, version, func(int) int { return int(orig.src.Port()) })
+ // Write back to tunnel
+ writeBuf := buf[:offset+len]
+ written, err := tun.Write(writeBuf, offset)
+ if err != nil {
+ return 0, err
+ } else if written != len {
+ return 0, fmt.Errorf("NAT rev buffer partly written %v != %v", written, len)
+ } else {
+ return 0, nil
+ }
+ }
// protocol := int(header[9])
// if protocol == PROTO_TCP && len >= (IPV4_HEADER_LEN + TCP_HEADER_LEN) {
@@ -491,7 +562,7 @@ func (tun *natTun) Write(buf []byte, offset int) (int, error) {
dst := netip.AddrPortFrom(dstAddr, uint16(dstPort))
new := Connection(dst, src)
- orig, ok := tun.lookupConnection(new)
+ orig, ok := tun.conntrack.lookupConnection(new)
if ok {
updateSrcAddr(header, version, func(netip.Addr) netip.Addr { return orig.dst.Addr() })
diff --git a/tunnel/tools/libwg-go/service.go b/tunnel/tools/libwg-go/service.go
index c5303876..57c128de 100644
--- a/tunnel/tools/libwg-go/service.go
+++ b/tunnel/tools/libwg-go/service.go
@@ -13,7 +13,6 @@ import (
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
- "golang.zx2c4.com/go118/netip"
gen "golang.zx2c4.com/wireguard/android/gen"
"golang.zx2c4.com/wireguard/device"
)
@@ -26,7 +25,7 @@ type LibwgServiceImpl struct {
gen.UnimplementedLibwgServer
logger *device.Logger
http_proxy *HttpProxy
- uidRequest chan netip.AddrPort
+ uidRequest chan AddrPortPair
uidResponse chan string
}
@@ -36,7 +35,7 @@ var server *grpc.Server
func NewLibwgService(logger *device.Logger) gen.LibwgServer {
return &LibwgServiceImpl{
logger: logger,
- uidRequest: make(chan netip.AddrPort),
+ uidRequest: make(chan AddrPortPair),
uidResponse: make(chan string),
}
}
@@ -167,22 +166,24 @@ func (e *LibwgServiceImpl) Reverse(stream gen.Libwg_ReverseServer) error {
}
select {
- case addr_port := <-e.uidRequest:
+ case addrPortPair := <-e.uidRequest:
+ local := addrPortPair.local
+ remote := addrPortPair.remote
r := &gen.ReverseResponse{
Request: &gen.ReverseResponse_Uid{
Uid: &gen.GetConnectionOwnerUidRequest{
Protocol: IPPROTO_TCP,
Local: &gen.InetSocketAddress{
Address: &gen.InetAddress{
- Address: addr_port.Addr().AsSlice(),
+ Address: local.Addr().AsSlice(),
},
- Port: uint32(addr_port.Port()),
+ Port: uint32(local.Port()),
},
Remote: &gen.InetSocketAddress{
Address: &gen.InetAddress{
- Address: e.http_proxy.GetAddrPort().Addr().AsSlice(),
+ Address: remote.Addr().AsSlice(),
},
- Port: uint32(e.http_proxy.GetAddrPort().Port()),
+ Port: uint32(remote.Port()),
},
},
},
diff --git a/tunnel/tools/libwg-go/transparent.go b/tunnel/tools/libwg-go/transparent.go
new file mode 100644
index 00000000..6dbe482c
--- /dev/null
+++ b/tunnel/tools/libwg-go/transparent.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "net"
+ "net/http"
+)
+
+type dumbResponseWriter struct {
+ net.Conn
+}
+
+func (dumb dumbResponseWriter) Header() http.Header {
+ panic("Header() should not be called on this ResponseWriter")
+}
+
+func (dumb dumbResponseWriter) Write(buf []byte) (int, error) {
+ if bytes.Equal(buf, []byte("HTTP/1.0 200 OK\r\n\r\n")) {
+ return len(buf), nil // throw away the HTTP OK response from the faux CONNECT request
+ }
+ return dumb.Conn.Write(buf)
+}
+
+func (dumb dumbResponseWriter) WriteHeader(code int) {
+ panic("WriteHeader() should not be called on this ResponseWriter")
+}
+
+func (dumb dumbResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ return dumb, bufio.NewReadWriter(bufio.NewReader(dumb), bufio.NewWriter(dumb)), nil
+}