Files
HY2XS_flamy/apps/service/forward.go
T

235 lines
6.2 KiB
Go

package service
import (
"errors"
"fmt"
"hy2xs-admin/dao"
"hy2xs-admin/model/constant"
"hy2xs-admin/util"
"strings"
)
var (
netManager string
ingressInterface string
Add = "add"
Delete = "delete"
Table = "hui_porthopping"
Comment = "hui_hysteria_porthopping"
)
func InitForward() {
if nft, err := util.Exec("command -v nft"); err == nil && strings.TrimSpace(nft) != "" {
netManager = "nft"
} else if iptables, err := util.Exec("command -v iptables"); err == nil && strings.TrimSpace(iptables) != "" {
netManager = "iptables"
}
if ii, err := util.Exec("ls /sys/class/net | grep -E '^en|^eth'"); err == nil && strings.TrimSpace(ii) != "" {
iiList := strings.Split(ii, "\n")
ingressInterface = strings.TrimSpace(iiList[0])
}
}
func InitTableAndChain() error {
if netManager == "nft" {
_, err := util.Exec(fmt.Sprintf("nft add table inet %s", Table))
if err != nil {
return err
}
_, err = util.Exec(fmt.Sprintf("nft add chain inet %s prerouting { type nat hook prerouting priority dstnat\\; policy accept\\; }", Table))
if err != nil {
return err
}
}
return nil
}
func InitPortHopping() error {
if err := RemoveByComment(); err != nil {
return err
}
hysteria2Config, err := GetHysteria2Config()
if err != nil {
return err
}
// set port forward
hysteria2ConfigPortHopping, err := dao.GetConfig("key = ?", constant.Hysteria2ConfigPortHopping)
if err != nil {
return err
}
if *hysteria2ConfigPortHopping.Value != "" {
listen := strings.Split(*hysteria2Config.Listen, ":")
if len(listen) == 2 {
portHoppings := strings.Split(*hysteria2ConfigPortHopping.Value, ",")
for _, item := range portHoppings {
if err := portForward(item, listen[1], Add); err != nil {
return err
}
}
}
}
return nil
}
func portForward(rules string, target string, option string) error {
switch netManager {
case "nft":
switch option {
case Add, Delete:
return nftForward(rules, target, option)
default:
return errors.New("unsupported command option")
}
case "iptables":
switch option {
case Add:
return iptablesForward(rules, target, "-A")
case Delete:
return iptablesForward(rules, target, "-D")
default:
return errors.New("unsupported command option")
}
default:
return errors.New("port hopping not supported on this system")
}
}
func RemoveByComment() error {
switch netManager {
case "nft":
return ntfRemoveByComment(Comment)
case "iptables":
return iptablesRemoveByComment(Comment)
default:
return errors.New("port hopping not supported on this system")
}
}
func nftForward(rules string, target string, option string) error {
if ingressInterface == "" {
return fmt.Errorf("no network interface detected")
}
// nft list ruleset
// 创建表:nft add table inet hui_hysteria_porthopping
// 创建链:nft add chain inet hui_hysteria_porthopping prerouting { type nat hook prerouting priority dstnat\; policy accept\; }
// 添加规则:nft add rule inet hui_hysteria_porthopping prerouting iifname enp1s0 udp dport {30000-40000} counter redirect to :444 comment hui_hysteria_porthopping
_, err := util.Exec(fmt.Sprintf("nft %s rule inet %s prerouting iifname %s udp dport {%s} counter redirect to :%s comment %s", option, Table, ingressInterface, rules, target, Comment))
if err != nil {
return err
}
return nil
}
func ntfRemoveByComment(comment string) error {
rules, err := nftRules()
if err != nil {
return err
}
for _, rule := range rules {
if strings.Contains(rule, comment) {
parts := strings.Fields(rule)
handle := parts[len(parts)-1]
_, err := util.Exec(fmt.Sprintf("nft delete rule inet %s prerouting handle %s", Table, strings.TrimSpace(handle)))
if err != nil {
return err
}
}
}
return nil
}
func nftRules() ([]string, error) {
listOutput, err := util.Exec(fmt.Sprintf("nft list ruleset | grep -q %s && echo 'found' || echo 'not found'", Comment))
if err != nil {
return nil, err
}
if strings.TrimSpace(listOutput) == "not found" {
return []string{}, nil
}
output, err := util.Exec(fmt.Sprintf("nft --handle list chain inet %s prerouting", Table))
if err != nil {
return nil, err
}
rules := strings.Split(output, "\n")
return rules, nil
}
func iptablesForward(rules string, target string, option string) error {
if ingressInterface == "" {
return fmt.Errorf("no network interface detected")
}
rulePairs := strings.Split(rules, ",")
for _, pair := range rulePairs {
ports := ""
portRange := strings.Split(pair, "-")
if len(portRange) == 1 {
ports = strings.TrimSpace(portRange[0])
} else if len(portRange) == 2 {
startPort := strings.TrimSpace(portRange[0])
endPort := strings.TrimSpace(portRange[1])
ports = startPort + ":" + endPort
} else {
return fmt.Errorf("invalid port range format: %s", pair)
}
if len(ports) != 0 {
if err := iptablesAddRule(option, ports, target); err != nil {
return err
}
}
}
return nil
}
func iptablesAddRule(option, ports, target string) error {
protocols := [2]string{"iptables", "ip6tables"}
for _, protocol := range protocols {
// iptables -t nat -A PREROUTING -i enp1s0 -p udp --dport 30000:40000 -j REDIRECT --to-port 444 -m comment --comment hui_hysteria_porthopping
_, err := util.Exec(fmt.Sprintf("%s -t nat %s PREROUTING -i %s -p udp --dport %s -j REDIRECT --to-port %s -m comment --comment %s", protocol, option, ingressInterface, ports, target, Comment))
if err != nil {
return err
}
}
return nil
}
func iptablesRemoveByComment(comment string) error {
protocols := [2]string{"iptables", "ip6tables"}
for _, protocol := range protocols {
rules, err := iptablesRules(protocol)
if err != nil {
return err
}
for _, rule := range rules {
if strings.Contains(rule, comment) {
parts := strings.Fields(rule)
handle := parts[0]
_, err := util.Exec(fmt.Sprintf("%s -t nat -D PREROUTING %s", protocol, strings.TrimSpace(handle)))
if err != nil {
return err
}
}
}
}
return nil
}
func iptablesRules(protocol string) ([]string, error) {
// iptables -t nat -L PREROUTING -v --line-numbers
output, err := util.Exec(fmt.Sprintf("%s -t nat -L PREROUTING -v --line-numbers", protocol))
if err != nil {
return nil, err
}
rules := strings.Split(output, "\n")
return rules, nil
}