mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-29 16:06:36 +08:00
147 lines
3.8 KiB
Go
147 lines
3.8 KiB
Go
package nftables
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
func RenderTable(plan NodePlan) string {
|
|
var b strings.Builder
|
|
b.WriteString("table inet flvx {\n")
|
|
b.WriteString(" chain prerouting {\n")
|
|
b.WriteString(" type nat hook prerouting priority dstnat; policy accept;\n")
|
|
for _, rule := range sortedRules(plan.Rules) {
|
|
family := nftAddressFamily(rule.TargetHost)
|
|
dnatFamily := ""
|
|
if family != "" {
|
|
dnatFamily = family + " "
|
|
}
|
|
for _, protocol := range normalizedProtocols(rule.Protocols) {
|
|
b.WriteString(fmt.Sprintf(" %s dport %d counter dnat %sto %s comment %q\n",
|
|
protocol,
|
|
rule.InPort,
|
|
dnatFamily,
|
|
formatDNATTarget(rule.TargetHost, rule.TargetPort),
|
|
counterComment(rule.ForwardID, CounterDirectionDNAT, protocol),
|
|
))
|
|
}
|
|
}
|
|
b.WriteString(" }\n\n")
|
|
b.WriteString(" chain postrouting {\n")
|
|
b.WriteString(" type nat hook postrouting priority srcnat; policy accept;\n")
|
|
if len(plan.Rules) > 0 {
|
|
b.WriteString(" masquerade comment \"flvx masquerade\"\n")
|
|
}
|
|
b.WriteString(" }\n\n")
|
|
b.WriteString(" chain forward {\n")
|
|
b.WriteString(" type filter hook forward priority filter; policy accept;\n")
|
|
for _, rule := range sortedRules(plan.Rules) {
|
|
family := nftAddressFamily(rule.TargetHost)
|
|
if family == "" {
|
|
continue
|
|
}
|
|
targetHost := strings.Trim(strings.TrimSpace(rule.TargetHost), "[]")
|
|
for _, protocol := range normalizedProtocols(rule.Protocols) {
|
|
b.WriteString(fmt.Sprintf(" ct original proto-dst %d %s daddr %s %s dport %d counter comment %q\n",
|
|
rule.InPort,
|
|
family,
|
|
targetHost,
|
|
protocol,
|
|
rule.TargetPort,
|
|
counterComment(rule.ForwardID, CounterDirectionToTarget, protocol),
|
|
))
|
|
b.WriteString(fmt.Sprintf(" ct original proto-dst %d %s saddr %s %s sport %d counter comment %q\n",
|
|
rule.InPort,
|
|
family,
|
|
targetHost,
|
|
protocol,
|
|
rule.TargetPort,
|
|
counterComment(rule.ForwardID, CounterDirectionFromTarget, protocol),
|
|
))
|
|
}
|
|
}
|
|
b.WriteString(" }\n")
|
|
b.WriteString("}\n")
|
|
return b.String()
|
|
}
|
|
|
|
func counterComment(forwardID int64, direction, protocol string) string {
|
|
return fmt.Sprintf("flvx forward:%d %s %s", forwardID, direction, protocol)
|
|
}
|
|
|
|
func RuleHash(rule Rule) string {
|
|
protocols := normalizedProtocols(rule.Protocols)
|
|
sum := sha256.Sum256([]byte(fmt.Sprintf("%d|%d|%s|%d|%s",
|
|
rule.ForwardID,
|
|
rule.InPort,
|
|
strings.TrimSpace(rule.TargetHost),
|
|
rule.TargetPort,
|
|
strings.Join(protocols, ","),
|
|
)))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func PlanHashes(plan NodePlan) map[int64]string {
|
|
hashes := make(map[int64]string, len(plan.Rules))
|
|
for _, rule := range plan.Rules {
|
|
hashes[rule.ForwardID] = RuleHash(rule)
|
|
}
|
|
return hashes
|
|
}
|
|
|
|
func sortedRules(rules []Rule) []Rule {
|
|
out := append([]Rule(nil), rules...)
|
|
sort.SliceStable(out, func(i, j int) bool {
|
|
if out[i].InPort == out[j].InPort {
|
|
return out[i].ForwardID < out[j].ForwardID
|
|
}
|
|
return out[i].InPort < out[j].InPort
|
|
})
|
|
return out
|
|
}
|
|
|
|
func normalizedProtocols(protocols []string) []string {
|
|
seen := map[string]struct{}{}
|
|
out := make([]string, 0, 2)
|
|
for _, protocol := range protocols {
|
|
p := strings.ToLower(strings.TrimSpace(protocol))
|
|
if p != "tcp" && p != "udp" {
|
|
continue
|
|
}
|
|
if _, ok := seen[p]; ok {
|
|
continue
|
|
}
|
|
seen[p] = struct{}{}
|
|
out = append(out, p)
|
|
}
|
|
if len(out) == 0 {
|
|
return []string{"tcp", "udp"}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
func formatDNATTarget(host string, port int) string {
|
|
trimmed := strings.Trim(strings.TrimSpace(host), "[]")
|
|
if ip := net.ParseIP(trimmed); ip != nil && ip.To4() == nil {
|
|
return fmt.Sprintf("[%s]:%d", trimmed, port)
|
|
}
|
|
return fmt.Sprintf("%s:%d", trimmed, port)
|
|
}
|
|
|
|
func nftAddressFamily(host string) string {
|
|
trimmed := strings.Trim(strings.TrimSpace(host), "[]")
|
|
ip := net.ParseIP(trimmed)
|
|
if ip == nil {
|
|
return ""
|
|
}
|
|
if ip.To4() == nil {
|
|
return "ip6"
|
|
}
|
|
return "ip"
|
|
}
|