mirror of
https://github.com/SunBK201/UA3F.git
synced 2025-12-16 08:44:29 +00:00
fix: prevent flow offload hijack packet
This commit is contained in:
parent
973a33432a
commit
8abb282547
@ -9,7 +9,9 @@ import (
|
||||
"net"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/gonetx/ipset"
|
||||
"github.com/sunbk201/ua3f/internal/config"
|
||||
"sigs.k8s.io/knftables"
|
||||
@ -255,8 +257,8 @@ func (f *Firewall) DeleteTproxyRoute(fwmark, routeTable string) error {
|
||||
}
|
||||
|
||||
func detectFirewallBackend(cfg *config.Config) string {
|
||||
nftAvailable := isCommandAvailable("nft")
|
||||
iptAvailable := isCommandAvailable("iptables")
|
||||
nftAvailable := IsCommandAvailable("nft")
|
||||
iptAvailable := IsCommandAvailable("iptables")
|
||||
nftTproxyAvailable := isOpkgPackageInstalled("kmod-nft-tproxy") && nftAvailable
|
||||
nftNfqueueAvailable := isOpkgPackageInstalled("kmod-nft-queue") && nftAvailable
|
||||
tproxyNeeded := cfg.ServerMode == config.ServerModeTProxy
|
||||
@ -334,7 +336,7 @@ func getLocalIPv4CIDRs() ([]string, error) {
|
||||
return cidrs, nil
|
||||
}
|
||||
|
||||
func isCommandAvailable(cmd string) bool {
|
||||
func IsCommandAvailable(cmd string) bool {
|
||||
_, err := exec.LookPath(cmd)
|
||||
return err == nil
|
||||
}
|
||||
@ -430,3 +432,67 @@ func initLanCidrs() {
|
||||
}
|
||||
LAN_CIDRS = append(LAN_CIDRS, localCIDRs...)
|
||||
}
|
||||
|
||||
func GetLanDevice() (string, error) {
|
||||
out, err := exec.Command("ubus", "call", "network.interface.lan", "status").Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var lanInterface struct {
|
||||
Device string `json:"device"`
|
||||
}
|
||||
if err := json.Unmarshal(out, &lanInterface); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if lanInterface.Device == "" {
|
||||
return "", errors.New("no device found for lan interface")
|
||||
}
|
||||
// get real device if it's a bridge
|
||||
out, err = exec.Command("ubus", "call", "network.device", "status").Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var devices map[string]struct {
|
||||
Type string `json:"type"`
|
||||
Bridges []string `json:"bridge-members"`
|
||||
}
|
||||
if err := json.Unmarshal(out, &devices); err != nil {
|
||||
return "", err
|
||||
}
|
||||
dev, ok := devices[lanInterface.Device]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("device %s not found", lanInterface.Device)
|
||||
}
|
||||
if dev.Type != "bridge" {
|
||||
return lanInterface.Device, nil
|
||||
}
|
||||
if len(dev.Bridges) == 0 {
|
||||
return "", fmt.Errorf("bridge %s has no members", lanInterface.Device)
|
||||
}
|
||||
return dev.Bridges[0], nil
|
||||
}
|
||||
|
||||
func FlowOffloadEnabled() bool {
|
||||
cmd := exec.Command("nft", "list", "chain", string(knftables.InetFamily), "fw4", "forward")
|
||||
if output, err := cmd.CombinedOutput(); err == nil {
|
||||
if strings.Contains(string(output), "flow add") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
rules, err := ipt.List("filter", "forward")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if strings.Contains(rule, "FLOWOFFLOAD") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -3,10 +3,13 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/sunbk201/ua3f/internal/netfilter"
|
||||
"sigs.k8s.io/knftables"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -51,6 +54,12 @@ func (s *Server) iptSetup() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if netfilter.FlowOffloadEnabled() {
|
||||
err = s.IptSetTTLIngress(ipt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.cfg.DelTCPTimestamp && !s.cfg.SetIPID {
|
||||
err = s.IptDelTCPTS(ipt)
|
||||
@ -75,6 +84,9 @@ func (s *Server) iptCleanup() error {
|
||||
_ = ipt.DeleteIfExists(table, chain, RuleTTL...)
|
||||
_ = ipt.DeleteIfExists(table, chain, RuleIP...)
|
||||
_ = ipt.DeleteIfExists(table, chain, RuleDelTCPTS...)
|
||||
if s.cfg.SetTTL {
|
||||
_ = s.NftCleanup()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -101,3 +113,26 @@ func (s *Server) IptSetIP(ipt *iptables.IPTables) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) IptSetTTLIngress(ipt *iptables.IPTables) error {
|
||||
if !netfilter.IsCommandAvailable("nft") {
|
||||
return errors.New("nft command not available")
|
||||
}
|
||||
|
||||
nft, err := knftables.New(s.Nftable.Family, s.Nftable.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx := nft.NewTransaction()
|
||||
tx.Add(s.Nftable)
|
||||
if err := nft.Run(context.TODO(), tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lanDev, err := netfilter.GetLanDevice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.NftSetTTLIngress(nft, s.Nftable, lanDev)
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/sunbk201/ua3f/internal/netfilter"
|
||||
"sigs.k8s.io/knftables"
|
||||
)
|
||||
|
||||
@ -37,6 +38,18 @@ func (s *Server) nftSetup() error {
|
||||
if err := nft.Run(context.TODO(), tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.cfg.SetTTL && netfilter.FlowOffloadEnabled() {
|
||||
lanDev, err := netfilter.GetLanDevice()
|
||||
if err != nil {
|
||||
slog.Warn("nftSetup netfilter.GetLanDevice", slog.Any("error", err))
|
||||
} else {
|
||||
err = s.NftSetTTLIngress(nft, s.Nftable, lanDev)
|
||||
if err != nil {
|
||||
slog.Warn("NftSetTTLIngress", slog.Any("error", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -73,6 +86,32 @@ func (s *Server) NftSetTTL(tx *knftables.Transaction, table *knftables.Table) {
|
||||
tx.Add(rule)
|
||||
}
|
||||
|
||||
func (s *Server) NftSetTTLIngress(nft knftables.Interface, table *knftables.Table, device string) error {
|
||||
tx := nft.NewTransaction()
|
||||
|
||||
chain := &knftables.Chain{
|
||||
Name: "TTL64_INGRESS",
|
||||
Table: table.Name,
|
||||
Type: knftables.PtrTo(knftables.FilterType),
|
||||
Hook: knftables.PtrTo(knftables.IngressHook),
|
||||
Priority: knftables.PtrTo(knftables.ManglePriority),
|
||||
Device: knftables.PtrTo(device),
|
||||
}
|
||||
rule := &knftables.Rule{
|
||||
Chain: chain.Name,
|
||||
Rule: knftables.Concat(
|
||||
"ip ttl set 65",
|
||||
),
|
||||
}
|
||||
tx.Add(chain)
|
||||
tx.Add(rule)
|
||||
|
||||
if err := nft.Run(context.TODO(), tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) NftDelTCPTS(tx *knftables.Transaction, table *knftables.Table) {
|
||||
chain := &knftables.Chain{
|
||||
Name: "DEL_TCPTS",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user