1
0
mirror of https://github.com/rtr7/router7.git synced 2024-05-06 15:54:52 +00:00

netconfig: enable NAT hairpinning for port forwardings

fixes https://github.com/rtr7/router7/issues/53
This commit is contained in:
Michael Stapelberg
2022-03-08 09:32:09 +01:00
parent 6d41b077a9
commit 8dc93c66c4
2 changed files with 126 additions and 16 deletions

View File

@ -137,19 +137,20 @@ func goldenNftablesRules(additionalForwarding bool) string {
add := ""
if additionalForwarding {
add = `
iifname "uplink0" tcp dport 8045 dnat to 192.168.42.22:8045`
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 tcp dport 8045 dnat to 192.168.42.22:8045`
}
return `table ip nat {
chain prerouting {
type nat hook prerouting priority 0; policy accept;
iifname "uplink0" tcp dport 8080 dnat to 192.168.42.23:9999` + add + `
iifname "uplink0" tcp dport 8040-8060 dnat to 192.168.42.99:8040-8060
iifname "uplink0" udp dport 53 dnat to 192.168.42.99:53
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 tcp dport 8080 dnat to 192.168.42.23:9999` + add + `
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 tcp dport 8040-8060 dnat to 192.168.42.99:8040-8060
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 udp dport 53 dnat to 192.168.42.99:53
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname "uplink0" masquerade
iifname "lan0" oifname "lan0" ct status 0x20 masquerade
}
}
table ip filter {

View File

@ -409,6 +409,73 @@ func nfifname(n string) []byte {
return b
}
// matchUplinkIP is conceptually equivalent to "ip daddr <uplink0-ip>", but
// without actually using the IP address of the uplink0 interface (which would
// mean that rules need to change when the IP address changes).
//
// Instead, it uses “fib daddr type local” to match all locally-configured IP
// addresses and then excludes the loopback and LAN IP addresses.
func matchUplinkIP() []expr.Any {
return []expr.Any{
// [ payload load 4b @ network header + 16 => reg 1 ]
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: 16, // TODO
Len: 4, // TODO
},
// [ bitwise reg 1 = (reg=1 & 0x000000ff ) ^ 0x00000000 ]
&expr.Bitwise{
DestRegister: 1,
SourceRegister: 1,
Len: 4,
Mask: []byte{0xff, 0x00, 0x00, 0x00}, // 255.0.0.0, i.e. /8
Xor: []byte{0x00, 0x00, 0x00, 0x00},
},
// [ cmp neq reg 1 0x0000007f ]
&expr.Cmp{
Op: expr.CmpOpNeq,
Register: 1,
Data: []byte{0x7f, 0x00, 0x00, 0x00},
},
// [ payload load 4b @ network header + 16 => reg 1 ]
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: 16, // TODO
Len: 4, // TODO
},
// [ bitwise reg 1 = (reg=1 & 0x00ffffff ) ^ 0x00000000 ]
&expr.Bitwise{
DestRegister: 1,
SourceRegister: 1,
Len: 4,
Mask: []byte{0xff, 0xff, 0xff, 0x00}, // 255.255.255.0, i.e. /24
Xor: []byte{0x00, 0x00, 0x00, 0x00},
},
// [ cmp neq reg 1 0x0000000a ]
&expr.Cmp{
Op: expr.CmpOpNeq,
Register: 1,
Data: []byte{0x0a, 0x00, 0x00, 0x00},
},
// [ fib daddr type => reg 1 ]
&expr.Fib{
Register: 1,
FlagDADDR: true,
ResultADDRTYPE: true,
},
// [ cmp eq reg 1 0x00000002 ]
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte{0x02, 0x00, 0x00, 0x00},
},
}
}
func portForwardExpr(ifname string, proto uint8, portMin, portMax uint16, dest net.IP, dportMin, dportMax uint16) []expr.Any {
var cmp []expr.Any
if portMin == portMax {
@ -436,16 +503,7 @@ func portForwardExpr(ifname string, proto uint8, portMin, portMax uint16, dest n
},
}
}
ex := []expr.Any{
// [ meta load iifname => reg 1 ]
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
// [ cmp eq reg 1 0x696c7075 0x00306b6e 0x00000000 0x00000000 ]
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: nfifname(ifname),
},
ex := append(matchUplinkIP(),
// [ meta load l4proto => reg 1 ]
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
// [ cmp eq reg 1 0x00000006 ]
@ -461,8 +519,7 @@ func portForwardExpr(ifname string, proto uint8, portMin, portMax uint16, dest n
Base: expr.PayloadBaseTransportHeader,
Offset: 2, // TODO
Len: 2, // TODO
},
}
})
ex = append(ex, cmp...)
ex = append(ex,
// [ immediate reg 1 0x0217a8c0 ]
@ -627,6 +684,52 @@ func getCounterObj(c *nftables.Conn, o *nftables.CounterObj) *nftables.CounterOb
return o
}
func hairpinDNAT() []expr.Any {
return []expr.Any{
// [ meta load oifname => reg 1 ]
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
// [ cmp eq reg 1 0x306e616c 0x00000000 0x00000000 0x00000000 ]
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: nfifname("lan0"),
},
// [ meta load oifname => reg 1 ]
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
// [ cmp eq reg 1 0x306e616c 0x00000000 0x00000000 0x00000000 ]
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: nfifname("lan0"),
},
// [ ct load status => reg 1 ]
&expr.Ct{
Register: 1,
SourceRegister: false,
Key: expr.CtKeySTATUS,
},
// [ bitwise reg 1 = (reg=1 & 0x00000020 ) ^ 0x00000000 ]
&expr.Bitwise{
DestRegister: 1,
SourceRegister: 1,
Len: 4,
Mask: []byte{0x20, 0x00, 0x00, 0x00},
Xor: []byte{0x00, 0x00, 0x00, 0x00},
},
// [ cmp neq reg 1 0x00000000 ]
&expr.Cmp{
Op: expr.CmpOpNeq,
Register: 1,
Data: []byte{0x00, 0x00, 0x00, 0x00},
},
// [ masq ]
&expr.Masq{},
}
}
func applyFirewall(dir, ifname string) error {
c := &nftables.Conn{}
@ -670,6 +773,12 @@ func applyFirewall(dir, ifname string) error {
},
})
c.AddRule(&nftables.Rule{
Table: nat,
Chain: postrouting,
Exprs: hairpinDNAT(),
})
if err := applyPortForwardings(dir, ifname, c, nat, prerouting); err != nil {
return err
}