add enough expressions to express port forwardings

This commit is contained in:
Michael Stapelberg 2018-06-04 22:48:32 +02:00
parent afbe6ed893
commit dab6002a09
8 changed files with 243 additions and 35 deletions

View File

@ -16,7 +16,7 @@
package expr package expr
import ( import (
"github.com/google/nftables/internal/binaryutil" "github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink" "github.com/mdlayher/netlink"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -116,12 +116,12 @@ const (
type Cmp struct { type Cmp struct {
Op CmpOp Op CmpOp
Register uint32 Register uint32
Data uint32 Data []byte
} }
func (e *Cmp) marshal() ([]byte, error) { func (e *Cmp) marshal() ([]byte, error) {
cmpData, err := netlink.MarshalAttributes([]netlink.Attribute{ cmpData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: binaryutil.NativeEndian.PutUint32(e.Data)}, {Type: unix.NFTA_DATA_VALUE, Data: e.Data},
}) })
if err != nil { if err != nil {
return nil, err return nil, err

47
expr/immediate.go Normal file
View File

@ -0,0 +1,47 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type Immediate struct {
Register uint32
Data []byte
}
func (e *Immediate) marshal() ([]byte, error) {
immData, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_DATA_VALUE, Data: e.Data},
})
if err != nil {
return nil, err
}
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_IMMEDIATE_DREG, Data: binaryutil.BigEndian.PutUint32(e.Register)},
{Type: unix.NLA_F_NESTED | unix.NFTA_IMMEDIATE_DATA, Data: immData},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("immediate\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}

65
expr/nat.go Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type NATType uint32
// Possible NATType values.
const (
NATTypeSourceNAT NATType = unix.NFT_NAT_SNAT
NATTypeDestNAT NATType = unix.NFT_NAT_DNAT
)
type NAT struct {
Type NATType
Family uint32 // TODO: typed const
RegAddrMin uint32
RegProtoMin uint32
}
// |00048|N-|00001| |len |flags| type|
// |00008|--|00001| |len |flags| type|
// | 6e 61 74 00 | | data | n a t
// |00036|N-|00002| |len |flags| type|
// |00008|--|00001| |len |flags| type| NFTA_NAT_TYPE
// | 00 00 00 01 | | data | NFT_NAT_DNAT
// |00008|--|00002| |len |flags| type| NFTA_NAT_FAMILY
// | 00 00 00 02 | | data | NFPROTO_IPV4
// |00008|--|00003| |len |flags| type| NFTA_NAT_REG_ADDR_MIN
// | 00 00 00 01 | | data | reg 1
// |00008|--|00005| |len |flags| type| NFTA_NAT_REG_PROTO_MIN
// | 00 00 00 02 | | data | reg 2
func (e *NAT) marshal() ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_NAT_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(e.Type))},
{Type: unix.NFTA_NAT_FAMILY, Data: binaryutil.BigEndian.PutUint32(e.Family)},
{Type: unix.NFTA_NAT_REG_ADDR_MIN, Data: binaryutil.BigEndian.PutUint32(e.RegAddrMin)},
{Type: unix.NFTA_NAT_REG_PROTO_MIN, Data: binaryutil.BigEndian.PutUint32(e.RegProtoMin)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("nat\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}

53
expr/payload.go Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expr
import (
"github.com/google/nftables/binaryutil"
"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)
type PayloadBase uint32
// Possible PayloadBase values.
const (
PayloadBaseLLHeader PayloadBase = unix.NFT_PAYLOAD_LL_HEADER
PayloadBaseNetworkHeader PayloadBase = unix.NFT_PAYLOAD_NETWORK_HEADER
PayloadBaseTransportHeader PayloadBase = unix.NFT_PAYLOAD_TRANSPORT_HEADER
)
type Payload struct {
DestRegister uint32
Base PayloadBase
Offset uint32
Len uint32
}
func (e *Payload) marshal() ([]byte, error) {
data, err := netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_PAYLOAD_DREG, Data: binaryutil.BigEndian.PutUint32(e.DestRegister)},
{Type: unix.NFTA_PAYLOAD_BASE, Data: binaryutil.BigEndian.PutUint32(uint32(e.Base))},
{Type: unix.NFTA_PAYLOAD_OFFSET, Data: binaryutil.BigEndian.PutUint32(e.Offset)},
{Type: unix.NFTA_PAYLOAD_LEN, Data: binaryutil.BigEndian.PutUint32(e.Len)},
})
if err != nil {
return nil, err
}
return netlink.MarshalAttributes([]netlink.Attribute{
{Type: unix.NFTA_EXPR_NAME, Data: []byte("payload\x00")},
{Type: unix.NLA_F_NESTED | unix.NFTA_EXPR_DATA, Data: data},
})
}

View File

@ -1,21 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nftables
import "github.com/google/nftables/internal/binaryutil"
// NativeEndian is either little endian or big endian, depending on the
// native endian-ness.
var NativeEndian binaryutil.ByteOrder = binaryutil.NativeEndian

View File

@ -19,8 +19,8 @@ import (
"fmt" "fmt"
"math" "math"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/expr" "github.com/google/nftables/expr"
"github.com/google/nftables/internal/binaryutil"
"github.com/mdlayher/netlink" "github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nltest" "github.com/mdlayher/netlink/nltest"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"

View File

@ -16,17 +16,23 @@ package nftables_test
import ( import (
"bytes" "bytes"
"net"
"testing" "testing"
"github.com/google/nftables" "github.com/google/nftables"
"github.com/google/nftables/binaryutil"
"github.com/google/nftables/expr" "github.com/google/nftables/expr"
"github.com/mdlayher/netlink" "github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
) )
func TestConfigureNAT(t *testing.T) { func ifname(n string) []byte {
// TODO: skip this test as non-root b := make([]byte, 16)
// TODO: run this in its own network namespace to not mess with the host copy(b, []byte(n+"\x00"))
return b
}
func TestConfigureNAT(t *testing.T) {
// The want byte sequences come from stracing nft(8), e.g.: // The want byte sequences come from stracing nft(8), e.g.:
// strace -f -v -x -s 2048 -eraw=sendto nft add table ip nat // strace -f -v -x -s 2048 -eraw=sendto nft add table ip nat
// //
@ -41,8 +47,10 @@ func TestConfigureNAT(t *testing.T) {
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x0f\x00\x03\x00\x70\x72\x65\x72\x6f\x75\x74\x69\x6e\x67\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00\x08\x00\x07\x00\x6e\x61\x74\x00"), []byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x0f\x00\x03\x00\x70\x72\x65\x72\x6f\x75\x74\x69\x6e\x67\x00\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00\x08\x00\x07\x00\x6e\x61\x74\x00"),
// nft add chain nat postrouting { type nat hook postrouting priority 100 \; } // nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x10\x00\x03\x00\x70\x6f\x73\x74\x72\x6f\x75\x74\x69\x6e\x67\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x04\x08\x00\x02\x00\x00\x00\x00\x64\x08\x00\x07\x00\x6e\x61\x74\x00"), []byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x10\x00\x03\x00\x70\x6f\x73\x74\x72\x6f\x75\x74\x69\x6e\x67\x00\x14\x00\x04\x80\x08\x00\x01\x00\x00\x00\x00\x04\x08\x00\x02\x00\x00\x00\x00\x64\x08\x00\x07\x00\x6e\x61\x74\x00"),
// nft add rule nat postrouting oif wlp4s0 masquerade // nft add rule nat postrouting oifname uplink0 masquerade
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x10\x00\x02\x00\x70\x6f\x73\x74\x72\x6f\x75\x74\x69\x6e\x67\x00\x68\x00\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x05\x08\x00\x01\x00\x00\x00\x00\x01\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x08\x00\x01\x00\x03\x00\x00\x00\x14\x00\x01\x80\x09\x00\x01\x00\x6d\x61\x73\x71\x00\x00\x00\x00\x04\x00\x02\x80"), []byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x10\x00\x02\x00\x70\x6f\x73\x74\x72\x6f\x75\x74\x69\x6e\x67\x00\x74\x00\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x07\x08\x00\x01\x00\x00\x00\x00\x01\x38\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x18\x00\x03\x80\x14\x00\x01\x00\x75\x70\x6c\x69\x6e\x6b\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x01\x80\x09\x00\x01\x00\x6d\x61\x73\x71\x00\x00\x00\x00\x04\x00\x02\x80"),
// nft add rule nat prerouting iif uplink0 tcp dport 4070 dnat 192.168.23.2:4080
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x0f\x00\x02\x00\x70\x72\x65\x72\x6f\x75\x74\x69\x6e\x67\x00\x00\x98\x01\x04\x80\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x06\x08\x00\x01\x00\x00\x00\x00\x01\x38\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x2c\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x18\x00\x03\x80\x14\x00\x01\x00\x75\x70\x6c\x69\x6e\x6b\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x01\x80\x09\x00\x01\x00\x6d\x65\x74\x61\x00\x00\x00\x00\x14\x00\x02\x80\x08\x00\x02\x00\x00\x00\x00\x10\x08\x00\x01\x00\x00\x00\x00\x01\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x05\x00\x01\x00\x06\x00\x00\x00\x34\x00\x01\x80\x0c\x00\x01\x00\x70\x61\x79\x6c\x6f\x61\x64\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x02\x08\x00\x03\x00\x00\x00\x00\x02\x08\x00\x04\x00\x00\x00\x00\x02\x2c\x00\x01\x80\x08\x00\x01\x00\x63\x6d\x70\x00\x20\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x00\x0c\x00\x03\x80\x06\x00\x01\x00\x0f\xe6\x00\x00\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x0c\x00\x02\x80\x08\x00\x01\x00\xc0\xa8\x17\x02\x2c\x00\x01\x80\x0e\x00\x01\x00\x69\x6d\x6d\x65\x64\x69\x61\x74\x65\x00\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x02\x0c\x00\x02\x80\x06\x00\x01\x00\x0f\xf0\x00\x00\x30\x00\x01\x80\x08\x00\x01\x00\x6e\x61\x74\x00\x24\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x08\x00\x02\x00\x00\x00\x00\x02\x08\x00\x03\x00\x00\x00\x00\x01\x08\x00\x05\x00\x00\x00\x00\x02"),
// batch end // batch end
[]byte("\x00\x00\x0a\x00"), []byte("\x00\x00\x0a\x00"),
} }
@ -76,7 +84,7 @@ func TestConfigureNAT(t *testing.T) {
Name: "nat", Name: "nat",
}) })
c.AddChain(&nftables.Chain{ prerouting := c.AddChain(&nftables.Chain{
Name: "prerouting", Name: "prerouting",
Hooknum: nftables.ChainHookPrerouting, Hooknum: nftables.ChainHookPrerouting,
Priority: nftables.ChainPriorityFilter, Priority: nftables.ChainPriorityFilter,
@ -96,19 +104,75 @@ func TestConfigureNAT(t *testing.T) {
Table: nat, Table: nat,
Chain: postrouting, Chain: postrouting,
Exprs: []expr.Any{ Exprs: []expr.Any{
// meta load oif => reg 1 // meta load oifname => reg 1
&expr.Meta{Key: expr.MetaKeyOIF, Register: 1}, &expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
// cmp eq reg 1 0x00000003 // cmp eq reg 1 0x696c7075 0x00306b6e 0x00000000 0x00000000
&expr.Cmp{ &expr.Cmp{
Op: expr.CmpOpEq, Op: expr.CmpOpEq,
Register: 1, Register: 1,
Data: 3, /* wlp4s0 */ Data: ifname("uplink0"),
}, },
// masq // masq
&expr.Masq{}, &expr.Masq{},
}, },
}) })
c.AddRule(&nftables.Rule{
Table: nat,
Chain: prerouting,
Exprs: []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: ifname("uplink0"),
},
// [ meta load l4proto => reg 1 ]
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
// [ cmp eq reg 1 0x00000006 ]
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte{unix.IPPROTO_TCP},
},
// [ payload load 2b @ transport header + 2 => reg 1 ]
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 2, // TODO
Len: 2, // TODO
},
// [ cmp eq reg 1 0x0000e60f ]
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: binaryutil.BigEndian.PutUint16(4070),
},
// [ immediate reg 1 0x0217a8c0 ]
&expr.Immediate{
Register: 1,
Data: net.ParseIP("192.168.23.2").To4(),
},
// [ immediate reg 2 0x0000f00f ]
&expr.Immediate{
Register: 2,
Data: binaryutil.BigEndian.PutUint16(4080),
},
// [ nat dnat ip addr_min reg 1 addr_max reg 0 proto_min reg 2 proto_max reg 0 ]
&expr.NAT{
Type: expr.NATTypeDestNAT,
Family: unix.NFPROTO_IPV4,
RegAddrMin: 1,
RegProtoMin: 2,
},
},
})
if err := c.Flush(); err != nil { if err := c.Flush(); err != nil {
t.Fatal(err) t.Fatal(err)
} }