Skip to content

Commit c595580

Browse files
Added SO_MARK setting for redirect auto configuration
1 parent f5b4972 commit c595580

File tree

5 files changed

+106
-37
lines changed

5 files changed

+106
-37
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ Options:
159159
Address of HTTP proxy server (default "127.0.0.1:8080")
160160
-logfile string
161161
Log file path (Default: stdout)
162+
-mark uint
163+
Set the mark for each packet sent through transparent proxy
162164
-nocolor
163165
Disable colored output for logs (no effect if -j flag specified)
164166
-s string
@@ -387,6 +389,12 @@ sudo env PATH=$PATH gohpts -d -T 8888 -M redirect -auto
387389
388390
Please note, automatic configuration requires `sudo` and is very generic, which might not be suitable for your needs.
389391
392+
You can optionally specify `-mark <value>` to prevent possible proxy loops
393+
394+
```shell
395+
sudo env PATH=$PATH gohpts -d -T 8888 -M redirect -auto -mark 100
396+
```
397+
390398
## `tproxy` (via _MANGLE_ and _IP_TRANSPARENT_)
391399
392400
[[Back]](#table-of-contents)

cmd/gohpts/cli.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func root(args []string) error {
5555
return nil
5656
})
5757
flags.BoolVar(&conf.Auto, "auto", false, "Automatically setup iptables for transparent proxy (requires elevated privileges)")
58+
flags.UintVar(&conf.Mark, "mark", 0, "Set the mark for each packet sent through transparent proxy")
5859
}
5960
flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout)")
6061
flags.BoolVar(&conf.Debug, "d", false, "Show logs in DEBUG mode")
@@ -110,6 +111,11 @@ func root(args []string) error {
110111
return fmt.Errorf("-auto is available only for -M redirect")
111112
}
112113
}
114+
if seen["mark"] {
115+
if !seen["auto"] {
116+
return fmt.Errorf("-mark requires -auto flag")
117+
}
118+
}
113119
if seen["f"] {
114120
for _, da := range []string{"s", "u", "U", "c", "k", "l"} {
115121
if seen[da] {

gohpts.go

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ type Config struct {
8787
TProxyOnly string
8888
TProxyMode string
8989
Auto bool
90+
Mark uint
9091
LogFilePath string
9192
Debug bool
9293
Json bool
@@ -109,6 +110,7 @@ type proxyapp struct {
109110
tproxyAddr string
110111
tproxyMode string
111112
auto bool
113+
mark uint
112114
user string
113115
pass string
114116
proxychain chain
@@ -544,7 +546,7 @@ func (p *proxyapp) updateSocksList() {
544546
p.mu.Lock()
545547
defer p.mu.Unlock()
546548
p.availProxyList = p.availProxyList[:0]
547-
var base proxy.Dialer = &net.Dialer{Timeout: timeout}
549+
var base proxy.Dialer = getBaseSockDialer(timeout, p.mark)
548550
var dialer proxy.Dialer
549551
var err error
550552
failed := 0
@@ -683,7 +685,7 @@ func (p *proxyapp) getSocks() (proxy.Dialer, *http.Client, error) {
683685
p.logger.Error().Msgf("%s Not all SOCKS5 Proxy available", ctl)
684686
return nil, nil, fmt.Errorf("not all socks5 proxy available")
685687
}
686-
var dialer proxy.Dialer = &net.Dialer{Timeout: timeout}
688+
var dialer proxy.Dialer = getBaseSockDialer(timeout, p.mark)
687689
var err error
688690
for _, pr := range copyProxyList {
689691
auth := proxy.Auth{
@@ -1166,48 +1168,68 @@ func (p *proxyapp) applyRedirectRules() string {
11661168
if err := cmd0.Run(); err != nil {
11671169
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
11681170
}
1169-
_, tproxyPort, _ := net.SplitHostPort(p.tproxyAddr)
1170-
cmd1 := exec.Command("bash", "-c", fmt.Sprintf(`
1171+
cmd1 := exec.Command("bash", "-c", `
11711172
set -ex
11721173
iptables -t nat -N GOHPTS 2>/dev/null
11731174
iptables -t nat -F GOHPTS
11741175
11751176
iptables -t nat -A GOHPTS -d 127.0.0.0/8 -j RETURN
1176-
iptables -t nat -A GOHPTS -p tcp --dport %s -j RETURN
11771177
iptables -t nat -A GOHPTS -p tcp --dport 22 -j RETURN
1178-
`, tproxyPort))
1178+
`)
11791179
cmd1.Stdout = os.Stdout
11801180
cmd1.Stderr = os.Stderr
11811181
if err := cmd1.Run(); err != nil {
11821182
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
11831183
}
1184-
if len(p.proxylist) > 0 {
1185-
for _, pr := range p.proxylist {
1186-
_, port, _ := net.SplitHostPort(pr.Address)
1187-
cmd2 := exec.Command("bash", "-c", fmt.Sprintf(`
1184+
_, tproxyPort, _ := net.SplitHostPort(p.tproxyAddr)
1185+
if p.mark > 0 {
1186+
cmd2 := exec.Command("bash", "-c", fmt.Sprintf(`
11881187
set -ex
1189-
iptables -t nat -A GOHPTS -p tcp --dport %s -j RETURN
1190-
`, port))
1191-
cmd2.Stdout = os.Stdout
1192-
cmd2.Stderr = os.Stderr
1193-
if err := cmd2.Run(); err != nil {
1194-
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
1195-
}
1188+
iptables -t nat -A GOHPTS -p tcp -m mark --mark %d -j RETURN
1189+
`, p.mark))
1190+
cmd2.Stdout = os.Stdout
1191+
cmd2.Stderr = os.Stderr
1192+
if err := cmd2.Run(); err != nil {
1193+
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
11961194
}
1197-
}
1198-
if p.httpServerAddr != "" {
1199-
_, httpPort, _ := net.SplitHostPort(p.httpServerAddr)
1200-
cmd3 := exec.Command("bash", "-c", fmt.Sprintf(`
1195+
} else {
1196+
cmd2 := exec.Command("bash", "-c", fmt.Sprintf(`
12011197
set -ex
12021198
iptables -t nat -A GOHPTS -p tcp --dport %s -j RETURN
1203-
`, httpPort))
1204-
cmd3.Stdout = os.Stdout
1205-
cmd3.Stderr = os.Stderr
1206-
if err := cmd3.Run(); err != nil {
1199+
`, tproxyPort))
1200+
cmd2.Stdout = os.Stdout
1201+
cmd2.Stderr = os.Stderr
1202+
if err := cmd2.Run(); err != nil {
12071203
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
12081204
}
1205+
if len(p.proxylist) > 0 {
1206+
for _, pr := range p.proxylist {
1207+
_, port, _ := net.SplitHostPort(pr.Address)
1208+
cmd3 := exec.Command("bash", "-c", fmt.Sprintf(`
1209+
set -ex
1210+
iptables -t nat -A GOHPTS -p tcp --dport %s -j RETURN
1211+
`, port))
1212+
cmd3.Stdout = os.Stdout
1213+
cmd3.Stderr = os.Stderr
1214+
if err := cmd3.Run(); err != nil {
1215+
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
1216+
}
1217+
}
1218+
}
1219+
if p.httpServerAddr != "" {
1220+
_, httpPort, _ := net.SplitHostPort(p.httpServerAddr)
1221+
cmd4 := exec.Command("bash", "-c", fmt.Sprintf(`
1222+
set -ex
1223+
iptables -t nat -A GOHPTS -p tcp --dport %s -j RETURN
1224+
`, httpPort))
1225+
cmd4.Stdout = os.Stdout
1226+
cmd4.Stderr = os.Stderr
1227+
if err := cmd4.Run(); err != nil {
1228+
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
1229+
}
1230+
}
12091231
}
1210-
cmd4 := exec.Command("bash", "-c", fmt.Sprintf(`
1232+
cmd5 := exec.Command("bash", "-c", fmt.Sprintf(`
12111233
set -ex
12121234
if command -v docker >/dev/null 2>&1
12131235
then
@@ -1224,25 +1246,25 @@ func (p *proxyapp) applyRedirectRules() string {
12241246
iptables -t nat -C OUTPUT -p tcp -j GOHPTS 2>/dev/null || \
12251247
iptables -t nat -A OUTPUT -p tcp -j GOHPTS
12261248
`, tproxyPort))
1227-
cmd4.Stdout = os.Stdout
1228-
cmd4.Stderr = os.Stderr
1229-
if err := cmd4.Run(); err != nil {
1249+
cmd5.Stdout = os.Stdout
1250+
cmd5.Stderr = os.Stderr
1251+
if err := cmd5.Run(); err != nil {
12301252
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
12311253
}
1232-
cmd5 := exec.Command("bash", "-c", `
1254+
cmd6 := exec.Command("bash", "-c", `
12331255
cat /proc/sys/net/ipv4/ip_forward
12341256
`)
1235-
output, err := cmd5.CombinedOutput()
1257+
output, err := cmd6.CombinedOutput()
12361258
if err != nil {
12371259
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
12381260
}
1239-
cmd6 := exec.Command("bash", "-c", `
1261+
cmd7 := exec.Command("bash", "-c", `
12401262
set -ex
12411263
sysctl -w net.ipv4.ip_forward=1
12421264
`)
1243-
cmd6.Stdout = os.Stdout
1244-
cmd6.Stderr = os.Stderr
1245-
_ = cmd6.Run()
1265+
cmd7.Stdout = os.Stdout
1266+
cmd7.Stderr = os.Stderr
1267+
_ = cmd7.Run()
12461268
return string(output)
12471269
}
12481270

@@ -1572,9 +1594,19 @@ func New(conf *Config) *proxyapp {
15721594
}
15731595
}
15741596
p.auto = conf.Auto
1575-
if p.auto && p.tproxyMode != "" && p.tproxyMode != "redirect" {
1597+
if p.auto && runtime.GOOS != "linux" {
1598+
p.logger.Fatal().Msg("Auto setup is available only for linux system")
1599+
}
1600+
if p.auto && p.tproxyMode != "redirect" {
15761601
p.logger.Fatal().Msg("Auto setup is available only for redirect mode")
15771602
}
1603+
p.mark = conf.Mark
1604+
if p.mark > 0 && runtime.GOOS != "linux" {
1605+
p.logger.Fatal().Msg("SO_MARK is available only for linux system")
1606+
}
1607+
if p.mark > 0xFFFFFFFF {
1608+
p.logger.Fatal().Msg("SO_MARK is out of range")
1609+
}
15781610
var addrHTTP, addrSOCKS, certFile, keyFile string
15791611
if conf.ServerConfPath != "" {
15801612
var sconf serverConfig
@@ -1645,7 +1677,7 @@ func New(conf *Config) *proxyapp {
16451677
User: conf.User,
16461678
Password: conf.Pass,
16471679
}
1648-
dialer, err := proxy.SOCKS5("tcp", addrSOCKS, &auth, &net.Dialer{Timeout: timeout})
1680+
dialer, err := proxy.SOCKS5("tcp", addrSOCKS, &auth, getBaseSockDialer(timeout, p.mark))
16491681
if err != nil {
16501682
p.logger.Fatal().Err(err).Msg("Unable to create SOCKS5 dialer")
16511683
}

tproxy_linux.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,20 @@ func (ts *tproxyServer) Shutdown() {
222222
return
223223
}
224224
}
225+
226+
func getBaseSockDialer(timeout time.Duration, mark uint) *net.Dialer {
227+
var dialer *net.Dialer
228+
if mark > 0 {
229+
dialer = &net.Dialer{
230+
Timeout: timeout,
231+
Control: func(_, _ string, c syscall.RawConn) error {
232+
return c.Control(func(fd uintptr) {
233+
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, int(mark))
234+
})
235+
},
236+
}
237+
} else {
238+
dialer = &net.Dialer{Timeout: timeout}
239+
}
240+
return dialer
241+
}

tproxy_nonlinux.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"sync"
99
"syscall"
10+
"time"
1011
)
1112

1213
type tproxyServer struct {
@@ -40,3 +41,8 @@ func (ts *tproxyServer) handleConnection(srcConn net.Conn) {
4041
}
4142

4243
func (ts *tproxyServer) Shutdown() {}
44+
45+
func getBaseSockDialer(timeout time.Duration, mark uint) *net.Dialer {
46+
_ = mark
47+
return &net.Dialer{Timeout: timeout}
48+
}

0 commit comments

Comments
 (0)