Skip to content

Commit 59602db

Browse files
S-MockingMocking
and
Mocking
authored
Add "tproxy" option (#1189)
* Add "tproxy" option Added the function of "MacOS" FreeBSD firewall traffic forwarding and resolving destination address example: "inbounds": [ { "listen": "127.0.0.1", "port": 1122, "protocol": "dokodemo-door", "tag": "dokodemo", "settings": { "network": "tcp", "followRedirect": true, "userLevel": 0 }, "streamSettings": { "sockopt": { "tproxy": "pf" } } } ] * Add "tproxy" option Added the function of "MacOS" FreeBSD firewall traffic forwarding and resolving destination address example: "inbounds": [ { "listen": "127.0.0.1", "port": 1122, "protocol": "dokodemo-door", "tag": "dokodemo", "settings": { "network": "tcp", "followRedirect": true, "userLevel": 0 }, "streamSettings": { "sockopt": { "tproxy": "pf" } } } ] * Add "tproxy" option Added the function of "MacOS" FreeBSD firewall traffic forwarding and resolving destination address example: "inbounds": [ { "listen": "127.0.0.1", "port": 1122, "protocol": "dokodemo-door", "tag": "dokodemo", "settings": { "network": "tcp", "followRedirect": true, "userLevel": 0 }, "streamSettings": { "sockopt": { "tproxy": "pf" } } } ] Co-authored-by: Mocking <[email protected]>
1 parent 76638d7 commit 59602db

File tree

6 files changed

+138
-21
lines changed

6 files changed

+138
-21
lines changed

app/proxyman/inbound/worker.go

+7
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ func (w *tcpWorker) callback(conn stat.Connection) {
7272
}
7373
case internet.SocketConfig_TProxy:
7474
dest = net.DestinationFromAddr(conn.LocalAddr())
75+
case internet.SocketConfig_PF:
76+
d, err := net.OriginalDst(conn)
77+
if err != nil {
78+
newError("failed to get original destination").Base(err).WriteToLog(session.ExportIDToError(ctx))
79+
} else {
80+
dest = d
81+
}
7582
}
7683
if dest.IsValid() {
7784
ctx = session.ContextWithOutbound(ctx, &session.Outbound{

common/net/destination-darwin.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//go:build darwin
2+
3+
package net
4+
5+
import (
6+
"net"
7+
"os"
8+
"syscall"
9+
"unsafe"
10+
)
11+
12+
const (
13+
PfOut = 2
14+
IOCOut = 0x40000000
15+
IOCIn = 0x80000000
16+
IOCInOut = IOCIn | IOCOut
17+
IOCPARMMask = 0x1FFF
18+
LEN = 4*16 + 4*4 + 4*1
19+
// #define _IOC(inout,group,num,len) (inout | ((len & IOCPARMMask) << 16) | ((group) << 8) | (num))
20+
// #define _IOWR(g,n,t) _IOC(IOCInOut, (g), (n), sizeof(t))
21+
// #define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook)
22+
DIOCNATLOOK = IOCInOut | ((LEN & IOCPARMMask) << 16) | ('D' << 8) | 23
23+
)
24+
25+
// OriginalDst uses ioctl to read original destination from /dev/pf
26+
func OriginalDst(conn Conn) (Destination, error) {
27+
f, err := os.Open("/dev/pf")
28+
if err != nil {
29+
return Destination{}, newError("failed to open device /dev/pf").Base(err)
30+
}
31+
defer f.Close()
32+
33+
fd := f.Fd()
34+
nl := struct { // struct pfioc_natlook
35+
saddr, daddr, rsaddr, rdaddr [16]byte
36+
sxport, dxport, rsxport, rdxport [4]byte
37+
af, proto, protoVariant, direction uint8
38+
}{
39+
af: syscall.AF_INET,
40+
proto: syscall.IPPROTO_TCP,
41+
direction: PfOut,
42+
}
43+
var raIP, laIP net.IP
44+
var raPort, laPort int
45+
la := conn.LocalAddr()
46+
ra := conn.RemoteAddr()
47+
switch la.(type) {
48+
case *net.TCPAddr:
49+
raIP = ra.(*net.TCPAddr).IP
50+
laIP = la.(*net.TCPAddr).IP
51+
raPort = ra.(*net.TCPAddr).Port
52+
laPort = la.(*net.TCPAddr).Port
53+
case *net.UDPAddr:
54+
raIP = ra.(*net.UDPAddr).IP
55+
laIP = la.(*net.UDPAddr).IP
56+
raPort = ra.(*net.UDPAddr).Port
57+
laPort = la.(*net.UDPAddr).Port
58+
}
59+
if raIP.To4() != nil {
60+
if laIP.IsUnspecified() {
61+
laIP = net.ParseIP("127.0.0.1")
62+
}
63+
copy(nl.saddr[:net.IPv4len], raIP.To4())
64+
copy(nl.daddr[:net.IPv4len], laIP.To4())
65+
}
66+
if raIP.To16() != nil && raIP.To4() == nil {
67+
if laIP.IsUnspecified() {
68+
laIP = net.ParseIP("::1")
69+
}
70+
copy(nl.saddr[:], raIP)
71+
copy(nl.daddr[:], laIP)
72+
}
73+
nl.sxport[0], nl.sxport[1] = byte(raPort>>8), byte(raPort)
74+
nl.dxport[0], nl.dxport[1] = byte(laPort>>8), byte(laPort)
75+
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, DIOCNATLOOK, uintptr(unsafe.Pointer(&nl))); errno != 0 {
76+
return Destination{}, os.NewSyscallError("ioctl", err)
77+
}
78+
79+
odPort := nl.rdxport
80+
var odIP net.IP
81+
switch nl.af {
82+
case syscall.AF_INET:
83+
odIP = make(net.IP, net.IPv4len)
84+
copy(odIP, nl.rdaddr[:net.IPv4len])
85+
case syscall.AF_INET6:
86+
odIP = make(net.IP, net.IPv6len)
87+
copy(odIP, nl.rdaddr[:])
88+
}
89+
return Destination{
90+
Address: IPAddress(odIP),
91+
Port: PortFromBytes(odPort[:2]),
92+
Network: Network_TCP,
93+
}, nil
94+
}

common/net/destination-other.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:build !darwin
2+
3+
package net
4+
5+
// OriginalDst uses ioctl to read original destination from /dev/pf
6+
func OriginalDst(conn Conn) (Destination, error) {
7+
return Destination{}, newError("This platform is not supported")
8+
}

infra/conf/transport_internet.go

+2
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
558558
tproxy = internet.SocketConfig_TProxy
559559
case "redirect":
560560
tproxy = internet.SocketConfig_Redirect
561+
case "pf":
562+
tproxy = internet.SocketConfig_PF
561563
default:
562564
tproxy = internet.SocketConfig_Off
563565
}

transport/internet/config.pb.go

+25-21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

transport/internet/config.proto

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ message SocketConfig {
7474
TProxy = 1;
7575
// Redirect mode.
7676
Redirect = 2;
77+
// PF mode.
78+
PF = 3;
7779
}
7880

7981
// TProxy is for enabling TProxy socket option.

0 commit comments

Comments
 (0)