mirror of
https://github.com/SunBK201/UA3F.git
synced 2025-12-16 16:57:08 +00:00
190 lines
4.4 KiB
Go
190 lines
4.4 KiB
Go
//go:build linux
|
|
|
|
package netlink
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"github.com/sunbk201/ua3f/internal/netfilter"
|
|
"sigs.k8s.io/knftables"
|
|
)
|
|
|
|
func (s *Server) nftSetup() error {
|
|
if !s.cfg.SetTTL && !s.cfg.DelTCPTimestamp && !s.cfg.SetIPID {
|
|
slog.Info("No packet modification features enabled, skipping nftables setup")
|
|
return nil
|
|
}
|
|
|
|
nft, err := knftables.New(s.Nftable.Family, s.Nftable.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tx := nft.NewTransaction()
|
|
tx.Add(s.Nftable)
|
|
|
|
if s.cfg.SetTTL {
|
|
s.NftSetTTL(tx, s.Nftable)
|
|
}
|
|
if (s.cfg.DelTCPTimestamp || s.cfg.SetTCPInitialWindow) && !s.cfg.SetIPID {
|
|
s.NftHookTCPSyn(tx, s.Nftable)
|
|
}
|
|
if s.cfg.SetIPID {
|
|
s.NftSetIP(tx, s.Nftable)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (s *Server) NftSetTTL(tx *knftables.Transaction, table *knftables.Table) {
|
|
chain := &knftables.Chain{
|
|
Name: "TTL64",
|
|
Table: table.Name,
|
|
Type: knftables.PtrTo(knftables.FilterType),
|
|
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 (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) NftHookTCPSyn(tx *knftables.Transaction, table *knftables.Table) {
|
|
chain := &knftables.Chain{
|
|
Name: "HOOK_TCP_SYN",
|
|
Table: table.Name,
|
|
Type: knftables.PtrTo(knftables.FilterType),
|
|
Hook: knftables.PtrTo(knftables.PostroutingHook),
|
|
Priority: knftables.PtrTo(knftables.ManglePriority),
|
|
}
|
|
tx.Add(chain)
|
|
var rule *knftables.Rule
|
|
rule = &knftables.Rule{
|
|
Chain: chain.Name,
|
|
Rule: knftables.Concat(
|
|
"tcp flags syn",
|
|
fmt.Sprintf("counter queue num %d bypass", s.nfqServer.QueueNum),
|
|
),
|
|
}
|
|
tx.Add(rule)
|
|
}
|
|
|
|
func (s *Server) NftSetIP(tx *knftables.Transaction, table *knftables.Table) {
|
|
chain := &knftables.Chain{
|
|
Name: "HELPER_QUEUE",
|
|
Table: table.Name,
|
|
Type: knftables.PtrTo(knftables.FilterType),
|
|
Hook: knftables.PtrTo(knftables.PostroutingHook),
|
|
Priority: knftables.PtrTo(knftables.ManglePriority),
|
|
}
|
|
rule := &knftables.Rule{
|
|
Chain: chain.Name,
|
|
Rule: knftables.Concat(
|
|
"meta l4proto tcp",
|
|
fmt.Sprintf("counter queue num %d bypass", s.nfqServer.QueueNum),
|
|
),
|
|
}
|
|
tx.Add(chain)
|
|
tx.Add(rule)
|
|
}
|
|
|
|
// unused currently
|
|
func ResetOptionAvailable() bool {
|
|
const TestName = "UA3F_TEST_RESET"
|
|
table := &knftables.Table{
|
|
Name: TestName,
|
|
Family: knftables.InetFamily,
|
|
}
|
|
nft, err := knftables.New(table.Family, table.Name)
|
|
if err != nil {
|
|
slog.Error("ResetOptionAvailable knftables.New", slog.Any("error", err))
|
|
return false
|
|
}
|
|
tx := nft.NewTransaction()
|
|
chain := &knftables.Chain{
|
|
Name: TestName,
|
|
Table: table.Name,
|
|
Type: knftables.PtrTo(knftables.FilterType),
|
|
Hook: knftables.PtrTo(knftables.PostroutingHook),
|
|
Priority: knftables.PtrTo(knftables.ManglePriority),
|
|
}
|
|
rule := &knftables.Rule{
|
|
Chain: chain.Name,
|
|
Rule: knftables.Concat(
|
|
"tcp option timestamp exists",
|
|
"counter reset tcp option timestamp",
|
|
),
|
|
}
|
|
tx.Add(table)
|
|
tx.Add(chain)
|
|
tx.Add(rule)
|
|
err = nft.Check(context.TODO(), tx)
|
|
if err != nil {
|
|
slog.Info("tcp option reset is not available")
|
|
}
|
|
return err == nil
|
|
}
|