feat: add packet modification features for TTL, IP ID, and TCP timestamp deletion

This commit is contained in:
SunBK201 2025-11-11 20:19:54 +08:00
parent db4888cd0c
commit 4cb071cd19
17 changed files with 538 additions and 47 deletions

View File

@ -190,6 +190,14 @@ function M.add_others_fields(section)
-- TTL Setting
local ttl = section:taboption("others", Flag, "set_ttl", translate("Set TTL"))
ttl.description = translate("Set the TTL 64 for packets")
-- TCP Timestamp Deletion
local tcpts = section:taboption("others", Flag, "del_tcpts", translate("Delete TCP Timestamps"))
tcpts.description = translate("Remove TCP Timestamp option")
-- IP ID Setting
local ipid = section:taboption("others", Flag, "set_ipid", translate("Set IP ID"))
ipid.description = translate("Set the IP ID to 0 for packets")
end
return M

View File

@ -137,8 +137,8 @@ detect_backend() {
if [ "$SERVER_MODE" = "TPROXY" ]; then
if opkg list-installed kmod-nft-tproxy | grep -q 'kmod-nft-tproxy'; then
if nft_available; then
FW_BACKEND="nft"
return 0
FW_BACKEND="nft"
return 0
fi
else
FW_BACKEND="ipt"
@ -147,8 +147,8 @@ detect_backend() {
elif [ "$SERVER_MODE" = "NFQUEUE" ]; then
if opkg list-installed kmod-nft-queue | grep -q 'kmod-nft-queue'; then
if nft_available; then
FW_BACKEND="nft"
return 0
FW_BACKEND="nft"
return 0
fi
else
FW_BACKEND="ipt"
@ -510,6 +510,7 @@ start_service() {
local port bind ua log_level ua_regex partial_replace set_ttl
local rewrite_mode rewrite_rules
local set_ttl set_ipid del_tcpts
config_get server_mode "main" "server_mode" "SOCKS5"
config_get port "main" "port" "1080"
config_get bind "main" "bind" "127.0.0.1"
@ -517,15 +518,21 @@ start_service() {
config_get ua_regex "main" "ua_regex" ""
config_get_bool partial_replace "main" "partial_replace" 0
config_get log_level "main" "log_level" "info"
config_get_bool set_ttl "main" "set_ttl" 0
config_get rewrite_mode "main" "rewrite_mode" "GLOBAL"
config_get rewrite_rules "main" "rewrite_rules" ""
config_get_bool set_ttl "main" "set_ttl" 0
config_get_bool set_ipid "main" "set_ipid" 0
config_get_bool del_tcpts "main" "del_tcpts" 0
# compose set_ttl set_ipid del_tcpts with others ",ttl,ipid,tcpts,"
local others=","
[ "$set_ipid" -eq "1" ] && others="${others}ipid,"
[ "$del_tcpts" -eq "1" ] && others="${others}tcpts,"
[ "$set_ttl" -eq "1" ] && others="${others}ttl,"
SERVER_MODE="$(echo "$server_mode" | tr '[:lower:]' '[:upper:]')"
SERVER_MODE="$server_mode"
SET_TTL="$set_ttl"
LOG "Server Mode: $SERVER_MODE"
LOG "Port: $port"
LOG "Bind: $bind"
@ -533,7 +540,6 @@ start_service() {
LOG "User-Agent Regex: $ua_regex"
LOG "Log level: $log_level"
LOG "Partial Replace: $partial_replace"
LOG "Set TTL: $SET_TTL"
set_ua3f_group
LOG "Run as GID: $UA3F_GID, Group: $UA3F_GROUP"
@ -553,6 +559,22 @@ start_service() {
fw_revert_ipt
fi
# dump all fw rules for debug
if [ "$log_level" = "debug" ]; then
if [ "$FW_BACKEND" = "nft" ]; then
LOG "nftables before rules:"
nft --handle list ruleset >>"$LOG_FILE" 2>&1
elif [ "$FW_BACKEND" = "ipt" ]; then
LOG "iptables before rules:"
LOG "mangle table:"
iptables -t mangle -L -v -n >>"$LOG_FILE" 2>&1
LOG "nat table:"
iptables -t nat -L -v -n >>"$LOG_FILE" 2>&1
LOG "filter table:"
iptables -t filter -L -v -n >>"$LOG_FILE" 2>&1
fi
fi
case "$SERVER_MODE" in
HTTP)
# No firewall interception
@ -607,20 +629,6 @@ start_service() {
;;
esac
if [ "$SET_TTL" = "1" ]; then
if [ "$FW_BACKEND" = "nft" ]; then
set_ttl_nft || {
LOG "set_ttl_nft setup failed"
}
LOG "Set TTL 64 via nftables"
else
set_ttl_ipt || {
LOG "set_ttl_ipt setup failed"
}
LOG "Set TTL 64 via iptables"
fi
fi
# dump all fw rules for debug
if [ "$log_level" = "debug" ]; then
if [ "$FW_BACKEND" = "nft" ]; then
@ -653,6 +661,7 @@ start_service() {
procd_append_param command -x "$rewrite_mode"
procd_append_param command -z "$rewrite_rules"
[ "$partial_replace" = "1" ] && procd_append_param command -s
procd_append_param command -o "$others"
procd_set_param respawn
procd_set_param stdout 1

View File

@ -253,3 +253,15 @@ msgstr "注意重写规则仅在规则判定模式下生效。NFQUEUE 模式
msgid "Rewrite Header"
msgstr "重写请求头"
msgid "Delete TCP Timestamps"
msgstr "删除 TCP 时间戳"
msgid "Remove TCP Timestamp option"
msgstr "移除 TCP 时间戳选项"
msgid "Set IP ID"
msgstr "固定 IP ID"
msgid "Set the IP ID to 0 for packets"
msgstr "将数据包的 IP ID 设置为 0不推荐开启除非你知道这是什么"

View File

@ -1,23 +1,26 @@
module github.com/sunbk201/ua3f
go 1.19
go 1.21
toolchain go1.24.6
require (
github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.4
github.com/florianl/go-nfqueue/v2 v2.0.2
github.com/google/gopacket v1.1.19
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/mdlayher/netlink v1.7.2
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42
github.com/sirupsen/logrus v1.9.3
golang.org/x/sys v0.30.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
sigs.k8s.io/knftables v0.0.19
)
require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
golang.org/x/net v0.25.0 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.7.0 // indirect
)

View File

@ -1,3 +1,5 @@
github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=
github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -11,12 +13,12 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg=
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
@ -31,8 +33,8 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
@ -51,3 +53,5 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/knftables v0.0.19 h1:0orK0+tYhY575F5X9uJGu80t+aVQ/hJj48I3fz3TBk8=
sigs.k8s.io/knftables v0.0.19/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk=

View File

@ -25,16 +25,19 @@ const (
)
type Config struct {
ServerMode ServerMode
BindAddr string
Port int
ListenAddr string
LogLevel string
RewriteMode RewriteMode
Rules string
PayloadUA string
UARegex string
PartialReplace bool
ServerMode ServerMode
BindAddr string
Port int
ListenAddr string
LogLevel string
RewriteMode RewriteMode
Rules string
PayloadUA string
UARegex string
PartialReplace bool
SetTTL bool
SetIPID bool
DelTCPTimestamp bool
}
func Parse() (*Config, bool) {
@ -48,6 +51,7 @@ func Parse() (*Config, bool) {
partial bool
rewriteMode string
rules string
others string
showVer bool
)
@ -60,6 +64,7 @@ func Parse() (*Config, bool) {
flag.BoolVar(&partial, "s", false, "Enable regex partial replace")
flag.StringVar(&rewriteMode, "x", string(RewriteModeGlobal), "Rewrite mode: GLOBAL, DIRECT, RULES")
flag.StringVar(&rules, "z", "", "Rules JSON string")
flag.StringVar(&others, "o", "", "Other options (tcpts, ttl, ipid)")
flag.BoolVar(&showVer, "v", false, "Show version")
flag.Parse()
@ -79,5 +84,19 @@ func Parse() (*Config, bool) {
cfg.BindAddr = "0.0.0.0"
cfg.ListenAddr = fmt.Sprintf("0.0.0.0:%d", port)
}
// Parse other options
opts := strings.Split(others, ",")
for _, opt := range opts {
switch strings.ToLower(strings.TrimSpace(opt)) {
case "tcpts":
cfg.DelTCPTimestamp = true
case "ttl":
cfg.SetTTL = true
case "ipid":
cfg.SetIPID = true
}
}
return cfg, showVer
}

View File

@ -78,6 +78,7 @@ func LogHeader(version string, cfg *config.Config) {
logrus.Infof("User-Agent Regex: '%s'", cfg.UARegex)
logrus.Infof("Partial Replace: %v", cfg.PartialReplace)
logrus.Infof("Log level: %s", cfg.LogLevel)
logrus.Infof("Packet Modifications - SetTTL: %v, SetIPID: %v, DelTCPTimestamp: %v", cfg.SetTTL, cfg.SetIPID, cfg.DelTCPTimestamp)
}
func LogDebugWithAddr(src string, dest string, msg string) {

View File

@ -107,7 +107,6 @@ func (s *NfqueueServer) Start() error {
s.wg.Wait()
return fmt.Errorf("failed to register nfqueue handler: %w", err)
}
logrus.Info("NFQUEUE handler registered, listening for packets")
// Wait until context is done
<-ctx.Done()

View File

@ -0,0 +1,72 @@
package netlink
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/sunbk201/ua3f/internal/config"
)
const (
NFT = "nft"
IPT = "ipt"
)
func (s *Server) setupFirewall() error {
s.cleanupFirewall()
backend := s.detectFirewallBackend()
switch backend {
case NFT:
return s.nftSetup()
case IPT:
return s.iptSetup()
default:
return fmt.Errorf("unsupported or no firewall backend: %s", backend)
}
}
func (s *Server) cleanupFirewall() error {
s.nftCleanup()
s.iptCleanup()
return nil
}
func (s *Server) detectFirewallBackend() string {
// Check if opkg is available (OpenWrt environment)
if isCommandAvailable("opkg") {
switch s.cfg.ServerMode {
case config.ServerModeTProxy:
// Check if kmod-nft-tproxy is installed
if isOpkgPackageInstalled("kmod-nft-tproxy") && isCommandAvailable("nft") {
logrus.Info("Detected nftables backend (kmod-nft-tproxy installed)")
return NFT
}
logrus.Info("Detected iptables backend (kmod-nft-tproxy not installed)")
return IPT
case config.ServerModeNFQueue:
// Check if kmod-nft-queue is installed
if isOpkgPackageInstalled("kmod-nft-queue") && isCommandAvailable("nft") {
logrus.Info("Detected nftables backend (kmod-nft-queue installed)")
return NFT
}
logrus.Info("Detected iptables backend (kmod-nft-queue not installed)")
return IPT
}
}
// Check if nft command is available
if isCommandAvailable("nft") {
logrus.Info("Detected nftables backend (nft command available)")
return NFT
}
// Check if iptables command is available
if isCommandAvailable("iptables") {
logrus.Info("Detected iptables backend (iptables command available)")
return IPT
}
// No backend detected
logrus.Warn("No firewall backend detected")
return ""
}

View File

@ -0,0 +1,96 @@
package netlink
import (
"github.com/coreos/go-iptables/iptables"
)
const (
table = "mangle"
chain = "POSTROUTING"
)
var RuleTTL = []string{
"-j", "TTL",
"--ttl-set", "64",
}
var RuleIP = []string{
"-j", "QUEUE",
"--queue-num", "10301",
"--queue-bypass",
}
var RuleDelTCPTS = []string{
"-p", "tcp",
"--tcp-flags", "SYN,RST,ACK,FIN",
"-j", "QUEUE",
"--queue-num", "10301",
"--queue-bypass",
}
func (s *Server) iptSetup() error {
_ = s.iptCleanup()
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
if err != nil {
return err
}
if s.cfg.SetTTL {
err = IptSetTTL(ipt)
if err != nil {
return err
}
}
if s.cfg.DelTCPTimestamp && !s.cfg.SetIPID {
err = IptDelTCPTS(ipt)
if err != nil {
return err
}
}
if s.cfg.SetIPID {
err = IptSetIP(ipt)
if err != nil {
return err
}
}
return nil
}
func (s *Server) iptCleanup() error {
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
if err != nil {
return err
}
_ = ipt.DeleteIfExists(table, chain, RuleTTL...)
_ = ipt.DeleteIfExists(table, chain, RuleIP...)
err = ipt.DeleteIfExists(table, chain, RuleDelTCPTS...)
if err != nil {
return err
}
return nil
}
// IptSetTTL creates a chain that sets TTL to 64 for IPv4 packets
func IptSetTTL(ipt *iptables.IPTables) error {
err := ipt.Append(table, chain, RuleTTL...)
if err != nil {
return err
}
return nil
}
func IptSetIP(ipt *iptables.IPTables) error {
err := ipt.Append(table, chain, RuleIP...)
if err != nil {
return err
}
return nil
}
func IptDelTCPTS(ipt *iptables.IPTables) error {
err := ipt.Append(table, chain, RuleDelTCPTS...)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,122 @@
package netlink
import (
nfq "github.com/florianl/go-nfqueue/v2"
"github.com/google/gopacket/layers"
"github.com/sirupsen/logrus"
"github.com/sunbk201/ua3f/internal/config"
"github.com/sunbk201/ua3f/internal/netfilter"
"sigs.k8s.io/knftables"
)
type Server struct {
cfg *config.Config
nfqServer *netfilter.NfqueueServer
nftable *knftables.Table
}
func New(cfg *config.Config) *Server {
s := &Server{
cfg: cfg,
nfqServer: &netfilter.NfqueueServer{
QueueNum: 10301,
},
nftable: &knftables.Table{
Name: "UA3F_HELPER",
Family: knftables.InetFamily,
},
}
s.nfqServer.HandlePacket = s.handlePacket
return s
}
func (s *Server) Setup() (err error) {
err = s.setupFirewall()
if err != nil {
return err
}
return nil
}
func (s *Server) Start() (err error) {
if s.cfg.SetTTL || s.cfg.DelTCPTimestamp || s.cfg.SetIPID {
logrus.Info("Packet modification features enabled")
return s.nfqServer.Start()
}
return nil
}
func (s *Server) Close() (err error) {
err = s.cleanupFirewall()
if err != nil {
return err
}
// err = s.nfqServer.Nf.Close()
return nil
}
// handlePacket processes a single NFQUEUE packet
func (s *Server) handlePacket(packet *netfilter.Packet) {
nf := s.nfqServer.Nf
modified := false
if s.cfg.DelTCPTimestamp && packet.TCP != nil {
modified = s.clearTCPTimestamp(packet.TCP) || modified
}
if s.cfg.SetIPID {
modified = s.zeroIPID(packet) || modified
}
if modified {
newPacket, err := packet.Serialize()
if err != nil {
logrus.Errorf("packet.Serialize: %v", err)
_ = nf.SetVerdict(*packet.A.PacketID, nfq.NfAccept)
return
}
if err := nf.SetVerdictWithOption(*packet.A.PacketID, nfq.NfAccept, nfq.WithAlteredPacket(newPacket)); err != nil {
logrus.Errorf("nf.SetVerdictWithOption: %v", err)
_ = nf.SetVerdict(*packet.A.PacketID, nfq.NfAccept)
}
} else {
_ = nf.SetVerdict(*packet.A.PacketID, nfq.NfAccept)
}
}
// clearTCPTimestamp removes the TCP timestamp option from the TCP layer
// Returns true if the timestamp option was found and removed
func (s *Server) clearTCPTimestamp(tcp *layers.TCP) bool {
if len(tcp.Options) == 0 {
return false
}
modified := false
newOptions := make([]layers.TCPOption, 0, len(tcp.Options))
for _, opt := range tcp.Options {
// TCP Timestamp option kind is 8
if opt.OptionType == 8 {
modified = true
continue
}
newOptions = append(newOptions, opt)
}
if modified {
tcp.Options = newOptions
}
return modified
}
// zeroIPID sets the IP ID field to zero for IPv4 packets
// Returns true if the packet was modified
func (s *Server) zeroIPID(packet *netfilter.Packet) bool {
if packet.IsIPv6 {
return false
}
ip4 := packet.NetworkLayer.(*layers.IPv4)
if ip4.Id == 0 {
return false
}
ip4.Id = 0
return true
}

View File

@ -0,0 +1,107 @@
package netlink
import (
"context"
"github.com/sirupsen/logrus"
"sigs.k8s.io/knftables"
)
func (s *Server) nftSetup() error {
// Clean up existing table if any
_ = s.nftCleanup()
nft, err := knftables.New(s.nftable.Family, s.nftable.Name)
if err != nil {
logrus.Errorf("Failed to create nftables table: %v", err)
return err
}
tx := nft.NewTransaction()
tx.Add(s.nftable)
if s.cfg.SetTTL {
NftSetTTL(tx, s.nftable)
}
if s.cfg.DelTCPTimestamp && !s.cfg.SetIPID {
NftDelTCPTS(tx, s.nftable)
}
if s.cfg.SetIPID {
NftSetIP(tx, s.nftable)
}
if err := nft.Run(context.TODO(), tx); err != nil {
logrus.Errorf("Failed to run nftables transaction: %v", err)
return err
}
logrus.Info("Nftables setup completed")
return nil
}
func (s *Server) nftCleanup() error {
nft, err := knftables.New(s.nftable.Family, s.nftable.Name)
if err != nil {
return err
}
tx := nft.NewTransaction()
tx.Delete(s.nftable)
if err := nft.Run(context.TODO(), tx); err != nil {
return err
}
return nil
}
// NftSetTTL creates a chain that sets TTL to 64 for IPv4 packets
func NftSetTTL(tx *knftables.Transaction, table *knftables.Table) {
chain := &knftables.Chain{
Name: "TTL64",
Type: knftables.PtrTo(knftables.FilterType),
Table: table.Name,
Hook: knftables.PtrTo(knftables.PostroutingHook),
Priority: knftables.PtrTo(knftables.ManglePriority),
}
rule := &knftables.Rule{
Chain: chain.Name,
Rule: knftables.Concat(
"ip ttl set 64",
),
}
tx.Add(chain)
tx.Add(rule)
}
func NftSetIP(tx *knftables.Transaction, table *knftables.Table) {
chain := &knftables.Chain{
Name: "HELPER_QUEUE",
Type: knftables.PtrTo(knftables.FilterType),
Table: table.Name,
Hook: knftables.PtrTo(knftables.PostroutingHook),
Priority: knftables.PtrTo(knftables.ManglePriority),
}
rule := &knftables.Rule{
Chain: chain.Name,
Rule: knftables.Concat(
"counter queue num 10301 bypass",
),
}
tx.Add(chain)
tx.Add(rule)
}
func NftDelTCPTS(tx *knftables.Transaction, table *knftables.Table) {
chain := &knftables.Chain{
Name: "HELPER_QUEUE",
Type: knftables.PtrTo(knftables.FilterType),
Table: table.Name,
Hook: knftables.PtrTo(knftables.PostroutingHook),
Priority: knftables.PtrTo(knftables.ManglePriority),
}
rule := &knftables.Rule{
Chain: chain.Name,
Rule: knftables.Concat(
"tcp flags syn counter queue num 10301 bypass",
),
}
tx.Add(chain)
tx.Add(rule)
}

View File

@ -0,0 +1,19 @@
package netlink
import "os/exec"
// isCommandAvailable checks if a command is available in the system
func isCommandAvailable(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}
// isOpkgPackageInstalled checks if a package is installed via opkg
func isOpkgPackageInstalled(pkg string) bool {
cmd := exec.Command("opkg", "list-installed", pkg)
output, err := cmd.Output()
if err != nil {
return false
}
return len(output) > 0
}

View File

@ -38,6 +38,8 @@ func (s *Server) Start() (err error) {
if client, err = s.listener.Accept(); err != nil {
if errors.Is(err, syscall.EMFILE) {
time.Sleep(time.Second)
} else if errors.Is(err, net.ErrClosed) {
return nil
}
logrus.Error("s.listener.Accept:", err)
continue

View File

@ -65,6 +65,8 @@ func (s *Server) Start() (err error) {
if client, err = s.listener.Accept(); err != nil {
if errors.Is(err, syscall.EMFILE) {
time.Sleep(time.Second)
} else if errors.Is(err, net.ErrClosed) {
return nil
}
logrus.Error("s.listener.Accept:", err)
continue

View File

@ -58,6 +58,8 @@ func (s *Server) Start() error {
if client, err = s.listener.Accept(); err != nil {
if errors.Is(err, syscall.EMFILE) {
time.Sleep(time.Second)
} else if errors.Is(err, net.ErrClosed) {
return nil
}
logrus.Error("s.listener.Accept:", err)
continue

View File

@ -11,6 +11,7 @@ import (
"github.com/sunbk201/ua3f/internal/log"
"github.com/sunbk201/ua3f/internal/rewrite"
"github.com/sunbk201/ua3f/internal/server"
"github.com/sunbk201/ua3f/internal/server/netlink"
"github.com/sunbk201/ua3f/internal/statistics"
)
@ -38,22 +39,35 @@ func main() {
log.LogHeader(version, cfg)
go statistics.StartRecorder()
helper := netlink.New(cfg)
err = helper.Setup()
if err != nil {
logrus.Fatal(err)
}
cleanup := make(chan os.Signal, 1)
signal.Notify(cleanup, syscall.SIGINT, syscall.SIGTERM)
go helper.Start()
go statistics.StartRecorder()
go func() {
<-cleanup
logrus.Info("Shutting down UA3F...")
if err := helper.Close(); err != nil {
logrus.Errorf("Error during helper close: %v", err)
}
if err := srv.Close(); err != nil {
logrus.Errorf("Error during UA3F close: %v", err)
}
logrus.Info("UA3F exited gracefully")
os.Exit(0)
}()
if err := srv.Start(); err != nil {
logrus.Fatal(err)
}
helper.Close()
srv.Close()
}