Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.25.0: add src ip in the egress nftable rules #914

Open
wants to merge 2 commits into
base: release-v0.25.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 91 additions & 10 deletions firewall/nftables_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,20 @@ func (n *nftablesManager) ForwardRule() error {
&expr.Verdict{Kind: expr.VerdictAccept},
},
})
n.conn.InsertRule(&nftables.Rule{
Table: natTable,
Chain: &nftables.Chain{Name: nattablePRTChain, Table: natTable},
Exprs: []expr.Any{
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte(ncutils.GetInterfaceName() + "\x00"),
},
&expr.Counter{},
&expr.Masq{},
},
})
return n.conn.Flush()
}

Expand Down Expand Up @@ -302,26 +316,93 @@ func (n *nftablesManager) InsertEgressRoutingRules(server string, egressInfo mod
}
for _, egressGwRange := range egressInfo.EgressGWCfg.Ranges {
if egressInfo.EgressGWCfg.NatEnabled == "yes" {

source := egressInfo.Network.String()
if !isAddrIpv4(egressGwRange) {
source = egressInfo.Network6.String()
}
if egressRangeIface, err := getInterfaceName(config.ToIPNet(egressGwRange)); err != nil {
logger.Log(0, "failed to get interface name: ", egressRangeIface, err.Error())
} else {
ruleSpec := []string{"-s", egressInfo.Network.String(), "-o", egressRangeIface, "-j", "MASQUERADE"}
// to avoid duplicate iface route rule,delete if exists
n.deleteRule(defaultNatTable, nattablePRTChain, genRuleKey(ruleSpec...))
rule = &nftables.Rule{
Table: natTable,
Chain: &nftables.Chain{Name: nattablePRTChain, Table: natTable},
UserData: []byte(genRuleKey(ruleSpec...)),
Exprs: []expr.Any{
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
var exp []expr.Any
if isAddrIpv4(source) {
exp = []expr.Any{
// Match source IP address
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: 12, // Source address offset in IP header
Len: 4,
},
&expr.Bitwise{
SourceRegister: 1,
DestRegister: 1,
Len: 4,
Mask: egressInfo.Network.Mask, // /16 mask for 100.64.0.0/16
Xor: []byte{0, 0, 0, 0},
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: egressInfo.Network.IP.To4(), // 100.64.0.0/16
},
// Match outgoing interface by index
&expr.Meta{
Key: expr.MetaKeyOIFNAME,
Register: 1,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte(egressRangeIface + "\x00"),
Data: []byte(egressRangeIface), // Interface name with null terminator
},
&expr.Counter{},
// Perform masquerade
&expr.Masq{},
},
}
} else {
exp = []expr.Any{
// Match source IPv6 address (2001:db8::/64)
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: 8, // Source address offset in IPv6 header
Len: 16, // Length of IPv6 address
},
&expr.Bitwise{
SourceRegister: 1,
DestRegister: 1,
Len: 16,
Mask: egressInfo.Network.Mask, // /64 mask
Xor: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: egressInfo.Network.IP.To16(), // 2001:db8::/64
},
// Match outgoing interface by name
&expr.Meta{
Key: expr.MetaKeyOIFNAME,
Register: 1,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte(egressRangeIface), // Interface name with null terminator
},
// Perform masquerade
&expr.Masq{},
}
}

n.deleteRule(defaultNatTable, nattablePRTChain, genRuleKey(ruleSpec...))
rule = &nftables.Rule{
Table: natTable,
Chain: &nftables.Chain{Name: nattablePRTChain, Table: natTable},
UserData: []byte(genRuleKey(ruleSpec...)),
Exprs: exp,
}
n.conn.InsertRule(rule)
if err := n.conn.Flush(); err != nil {
Expand Down
14 changes: 8 additions & 6 deletions functions/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,7 @@ func Daemon() {
reset := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
signal.Notify(reset, syscall.SIGHUP)
// initialize firewall manager
var err error
config.FwClose, err = firewall.Init()
if err != nil {
logger.Log(0, "failed to intialize firewall: ", err.Error())
}

cancel := startGoRoutines(&wg)

for {
Expand All @@ -86,6 +81,7 @@ func Daemon() {
return
case <-reset:
slog.Info("received reset")
config.FwClose()
//check if it needs to restore the default gateway
checkAndRestoreDefaultGateway()
closeRoutines([]context.CancelFunc{
Expand Down Expand Up @@ -147,6 +143,12 @@ func startGoRoutines(wg *sync.WaitGroup) context.CancelFunc {
if err := config.ReadServerConf(); err != nil {
slog.Warn("error reading server map from disk", "error", err)
}
// initialize firewall manager
var err error
config.FwClose, err = firewall.Init()
if err != nil {
logger.Log(0, "failed to intialize firewall: ", err.Error())
}
updateConfig := false

if !config.Netclient().IsStaticPort {
Expand Down
Loading