176 lines
4.8 KiB
Go
Raw Normal View History

2024-09-06 14:24:55 +03:00
package netfilterHelper
import (
"fmt"
2024-10-22 23:23:07 +03:00
"net"
2024-09-06 14:24:55 +03:00
"strconv"
2024-10-22 23:23:07 +03:00
"github.com/coreos/go-iptables/iptables"
"github.com/vishvananda/netlink"
2024-09-06 14:24:55 +03:00
)
type PortRemap struct {
IPTables *iptables.IPTables
ChainName string
2024-10-22 23:23:07 +03:00
Addresses []netlink.Addr
2024-09-06 14:24:55 +03:00
From uint16
To uint16
2025-02-11 15:53:58 +03:00
enabled bool
2024-09-06 14:24:55 +03:00
}
2025-02-11 15:53:58 +03:00
func (r *PortRemap) insertIPTablesRules(table string) error {
if table == "" || table == "nat" {
2025-02-14 20:55:37 +03:00
preroutingChain := r.ChainName + "_PRR"
err := r.IPTables.NewChain("nat", preroutingChain)
2024-09-06 16:50:56 +03:00
if err != nil {
2025-02-11 15:53:58 +03:00
// If not "AlreadyExists"
if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) {
return fmt.Errorf("failed to create chain: %w", err)
}
2024-09-06 16:50:56 +03:00
}
2024-09-06 14:24:55 +03:00
2024-10-22 23:23:07 +03:00
for _, addr := range r.Addresses {
2025-02-11 15:53:58 +03:00
if !((r.IPTables.Proto() == iptables.ProtocolIPv4 && len(addr.IP) == net.IPv4len) || (r.IPTables.Proto() == iptables.ProtocolIPv6 && len(addr.IP) == net.IPv6len)) {
2024-10-22 23:23:07 +03:00
continue
}
2025-02-14 20:55:37 +03:00
if r.IPTables.Proto() != iptables.ProtocolIPv6 {
for _, iptablesArgs := range [][]string{
{"-p", "tcp", "-d", addr.IP.String(), "--dport", fmt.Sprintf("%d", r.From), "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", r.To)},
{"-p", "udp", "-d", addr.IP.String(), "--dport", fmt.Sprintf("%d", r.From), "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", r.To)},
} {
err = r.IPTables.AppendUnique("nat", preroutingChain, iptablesArgs...)
if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
}
} else {
for _, iptablesArgs := range [][]string{
{"-p", "tcp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
{"-p", "udp", "-d", addr.IP.String(), "--dport", strconv.Itoa(int(r.From)), "-j", "DNAT", "--to-destination", fmt.Sprintf(":%d", r.To)},
} {
err = r.IPTables.AppendUnique("nat", preroutingChain, iptablesArgs...)
if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
2025-02-11 15:53:58 +03:00
}
}
2024-09-06 16:50:56 +03:00
}
2025-02-14 20:55:37 +03:00
err = r.IPTables.InsertUnique("nat", "PREROUTING", 1, "-j", preroutingChain)
if err != nil {
return fmt.Errorf("failed to linking chain: %w", err)
}
postroutingChain := r.ChainName + "_POR"
err = r.IPTables.NewChain("nat", postroutingChain)
if err != nil {
// If not "AlreadyExists"
if eerr, eok := err.(*iptables.Error); !(eok && eerr.ExitStatus() == 1) {
return fmt.Errorf("failed to create chain: %w", err)
}
}
for _, addr := range r.Addresses {
if !((r.IPTables.Proto() == iptables.ProtocolIPv4 && len(addr.IP) == net.IPv4len) || (r.IPTables.Proto() == iptables.ProtocolIPv6 && len(addr.IP) == net.IPv6len)) {
continue
}
if r.IPTables.Proto() == iptables.ProtocolIPv4 {
for _, iptablesArgs := range [][]string{
{"-p", "tcp", "-d", addr.IP.String(), "--sport", strconv.Itoa(int(r.To)), "-j", "SNAT", "--to-source", addr.IP.String()},
{"-p", "udp", "-d", addr.IP.String(), "--sport", strconv.Itoa(int(r.To)), "-j", "SNAT", "--to-source", addr.IP.String()},
} {
err = r.IPTables.AppendUnique("nat", postroutingChain, iptablesArgs...)
if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
}
}
}
err = r.IPTables.InsertUnique("nat", "POSTROUTING", 1, "-j", postroutingChain)
2024-09-06 16:50:56 +03:00
if err != nil {
return fmt.Errorf("failed to linking chain: %w", err)
}
2024-09-06 14:24:55 +03:00
}
2024-09-06 16:50:56 +03:00
return nil
}
2025-02-11 15:53:58 +03:00
func (r *PortRemap) deleteIPTablesRules() []error {
2024-09-06 14:24:55 +03:00
var errs []error
2025-02-14 20:55:37 +03:00
preroutingChain := r.ChainName + "_PRR"
err := r.IPTables.DeleteIfExists("nat", "PREROUTING", "-j", preroutingChain)
if err != nil {
errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err))
}
postroutingChain := r.ChainName + "_POR"
err = r.IPTables.DeleteIfExists("nat", "POSTROUTING", "-j", postroutingChain)
2024-09-06 14:24:55 +03:00
if err != nil {
errs = append(errs, fmt.Errorf("failed to unlinking chain: %w", err))
}
err = r.IPTables.ClearAndDeleteChain("nat", r.ChainName)
if err != nil {
errs = append(errs, fmt.Errorf("failed to delete chain: %w", err))
}
return errs
}
2025-02-11 15:53:58 +03:00
func (r *PortRemap) enable() error {
err := r.insertIPTablesRules("")
if err != nil {
return err
}
r.enabled = true
return nil
}
2024-09-06 14:24:55 +03:00
func (r *PortRemap) Enable() error {
2025-02-11 15:53:58 +03:00
if r.enabled {
2024-09-06 14:24:55 +03:00
return nil
}
2025-02-11 15:53:58 +03:00
err := r.IPTables.ClearChain("nat", r.ChainName)
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
}
err = r.enable()
2024-09-06 14:24:55 +03:00
if err != nil {
r.Disable()
return err
}
return nil
}
2025-02-11 15:53:58 +03:00
func (r *PortRemap) Disable() []error {
errs := r.deleteIPTablesRules()
r.enabled = false
return errs
}
2025-02-11 22:42:08 +03:00
func (r *PortRemap) NetfilterDHook(table string) error {
2025-02-11 15:53:58 +03:00
if !r.enabled {
return nil
}
return r.insertIPTablesRules(table)
}
2024-10-22 23:23:07 +03:00
func (nh *NetfilterHelper) PortRemap(name string, from, to uint16, addr []netlink.Addr) *PortRemap {
2024-09-06 14:24:55 +03:00
return &PortRemap{
IPTables: nh.IPTables,
ChainName: name,
2024-10-22 23:23:07 +03:00
Addresses: addr,
2024-09-06 14:24:55 +03:00
From: from,
To: to,
}
}