2018-05-24 23:47:16 -05:00
|
|
|
// 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_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2018-06-04 15:48:32 -05:00
|
|
|
"net"
|
2018-05-24 23:47:16 -05:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/google/nftables"
|
2018-06-04 15:48:32 -05:00
|
|
|
"github.com/google/nftables/binaryutil"
|
2018-05-24 23:47:16 -05:00
|
|
|
"github.com/google/nftables/expr"
|
|
|
|
"github.com/mdlayher/netlink"
|
2018-06-04 15:48:32 -05:00
|
|
|
"golang.org/x/sys/unix"
|
2018-05-24 23:47:16 -05:00
|
|
|
)
|
|
|
|
|
2018-06-04 15:48:32 -05:00
|
|
|
func ifname(n string) []byte {
|
|
|
|
b := make([]byte, 16)
|
|
|
|
copy(b, []byte(n+"\x00"))
|
|
|
|
return b
|
|
|
|
}
|
2018-05-24 23:47:16 -05:00
|
|
|
|
2018-06-04 15:48:32 -05:00
|
|
|
func TestConfigureNAT(t *testing.T) {
|
2018-05-24 23:47:16 -05:00
|
|
|
// The want byte sequences come from stracing nft(8), e.g.:
|
|
|
|
// strace -f -v -x -s 2048 -eraw=sendto nft add table ip nat
|
|
|
|
//
|
|
|
|
// The nft(8) command sequence was taken from:
|
|
|
|
// https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)
|
|
|
|
want := [][]byte{
|
|
|
|
// batch begin
|
|
|
|
[]byte("\x00\x00\x0a\x00"),
|
2018-06-14 01:26:52 -05:00
|
|
|
// nft flush ruleset
|
|
|
|
[]byte("\x00\x00\x00\x00"),
|
2018-05-24 23:47:16 -05:00
|
|
|
// nft add table ip nat
|
|
|
|
[]byte("\x02\x00\x00\x00\x08\x00\x01\x00\x6e\x61\x74\x00\x08\x00\x02\x00\x00\x00\x00\x00"),
|
|
|
|
// nft add chain nat prerouting '{' type nat hook prerouting priority 0 \; '}'
|
|
|
|
[]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"),
|
2018-10-03 11:23:26 -05:00
|
|
|
// nft add chain nat postrouting '{' type nat hook postrouting priority 100 \; '}'
|
2018-05-24 23:47:16 -05:00
|
|
|
[]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"),
|
2018-06-04 15:48:32 -05:00
|
|
|
// 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\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"),
|
2018-06-14 15:26:38 -05:00
|
|
|
// nft add rule nat prerouting iifname uplink0 udp dport 4070-4090 dnat 192.168.23.2:4070-4090
|
|
|
|
[]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\xf8\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\x11\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\x05\x0c\x00\x03\x80\x06\x00\x01\x00\x0f\xe6\x00\x00\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\x03\x0c\x00\x03\x80\x06\x00\x01\x00\x0f\xfa\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\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\x03\x0c\x00\x02\x80\x06\x00\x01\x00\x0f\xfa\x00\x00\x38\x00\x01\x80\x08\x00\x01\x00\x6e\x61\x74\x00\x2c\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\x08\x00\x06\x00\x00\x00\x00\x03"),
|
2018-05-24 23:47:16 -05:00
|
|
|
// batch end
|
|
|
|
[]byte("\x00\x00\x0a\x00"),
|
|
|
|
}
|
|
|
|
|
|
|
|
c := &nftables.Conn{
|
|
|
|
TestDial: func(req []netlink.Message) ([]netlink.Message, error) {
|
|
|
|
for idx, msg := range req {
|
|
|
|
b, err := msg.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(b) < 16 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
b = b[16:]
|
|
|
|
if len(want) == 0 {
|
|
|
|
t.Errorf("no want entry for message %d: %x", idx, b)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if got, want := b, want[0]; !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("message %d: got %x, want %x", idx, got, want)
|
|
|
|
}
|
|
|
|
want = want[1:]
|
|
|
|
}
|
|
|
|
return req, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-06-14 01:26:52 -05:00
|
|
|
c.FlushRuleset()
|
|
|
|
|
2018-05-24 23:47:16 -05:00
|
|
|
nat := c.AddTable(&nftables.Table{
|
|
|
|
Family: nftables.TableFamilyIPv4,
|
|
|
|
Name: "nat",
|
|
|
|
})
|
|
|
|
|
2018-06-04 15:48:32 -05:00
|
|
|
prerouting := c.AddChain(&nftables.Chain{
|
2018-05-24 23:47:16 -05:00
|
|
|
Name: "prerouting",
|
|
|
|
Hooknum: nftables.ChainHookPrerouting,
|
|
|
|
Priority: nftables.ChainPriorityFilter,
|
|
|
|
Table: nat,
|
|
|
|
Type: nftables.ChainTypeNAT,
|
|
|
|
})
|
|
|
|
|
|
|
|
postrouting := c.AddChain(&nftables.Chain{
|
|
|
|
Name: "postrouting",
|
|
|
|
Hooknum: nftables.ChainHookPostrouting,
|
|
|
|
Priority: nftables.ChainPriorityNATSource,
|
|
|
|
Table: nat,
|
|
|
|
Type: nftables.ChainTypeNAT,
|
|
|
|
})
|
|
|
|
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
Table: nat,
|
|
|
|
Chain: postrouting,
|
|
|
|
Exprs: []expr.Any{
|
2018-06-04 15:48:32 -05:00
|
|
|
// meta load oifname => reg 1
|
|
|
|
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
|
|
|
// cmp eq reg 1 0x696c7075 0x00306b6e 0x00000000 0x00000000
|
2018-05-24 23:47:16 -05:00
|
|
|
&expr.Cmp{
|
|
|
|
Op: expr.CmpOpEq,
|
|
|
|
Register: 1,
|
2018-06-04 15:48:32 -05:00
|
|
|
Data: ifname("uplink0"),
|
2018-05-24 23:47:16 -05:00
|
|
|
},
|
|
|
|
// masq
|
|
|
|
&expr.Masq{},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2018-06-04 15:48:32 -05:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2018-06-14 15:26:38 -05:00
|
|
|
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_UDP},
|
|
|
|
},
|
|
|
|
|
|
|
|
// [ payload load 2b @ transport header + 2 => reg 1 ]
|
|
|
|
&expr.Payload{
|
|
|
|
DestRegister: 1,
|
|
|
|
Base: expr.PayloadBaseTransportHeader,
|
|
|
|
Offset: 2, // TODO
|
|
|
|
Len: 2, // TODO
|
|
|
|
},
|
|
|
|
// [ cmp gte reg 1 0x0000e60f ]
|
|
|
|
&expr.Cmp{
|
|
|
|
Op: expr.CmpOpGte,
|
|
|
|
Register: 1,
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(4070),
|
|
|
|
},
|
|
|
|
// [ cmp lte reg 1 0x0000fa0f ]
|
|
|
|
&expr.Cmp{
|
|
|
|
Op: expr.CmpOpLte,
|
|
|
|
Register: 1,
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(4090),
|
|
|
|
},
|
|
|
|
|
|
|
|
// [ 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(4070),
|
|
|
|
},
|
|
|
|
// [ immediate reg 3 0x0000fa0f ]
|
|
|
|
&expr.Immediate{
|
|
|
|
Register: 3,
|
|
|
|
Data: binaryutil.BigEndian.PutUint16(4090),
|
|
|
|
},
|
|
|
|
// [ nat dnat ip addr_min reg 1 addr_max reg 0 proto_min reg 2 proto_max reg 3 ]
|
|
|
|
&expr.NAT{
|
|
|
|
Type: expr.NATTypeDestNAT,
|
|
|
|
Family: unix.NFPROTO_IPV4,
|
|
|
|
RegAddrMin: 1,
|
|
|
|
RegProtoMin: 2,
|
|
|
|
RegProtoMax: 3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2018-05-24 23:47:16 -05:00
|
|
|
if err := c.Flush(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2018-06-23 14:12:14 -05:00
|
|
|
|
|
|
|
func TestGetRule(t *testing.T) {
|
|
|
|
// The want byte sequences come from stracing nft(8), e.g.:
|
|
|
|
// strace -f -v -x -s 2048 -eraw=sendto nft list chain ip filter forward
|
|
|
|
|
|
|
|
want := [][]byte{
|
|
|
|
[]byte{0x2, 0x0, 0x0, 0x0, 0xb, 0x0, 0x1, 0x0, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0, 0x0, 0xa, 0x0, 0x2, 0x0, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x0, 0x0, 0x0},
|
|
|
|
}
|
|
|
|
|
|
|
|
// The reply messages come from adding log.Printf("msgs: %#v", msgs) to
|
|
|
|
// (*github.com/mdlayher/netlink/Conn).receive
|
|
|
|
reply := [][]netlink.Message{
|
|
|
|
nil,
|
|
|
|
[]netlink.Message{netlink.Message{Header: netlink.Header{Length: 0x68, Type: 0xa06, Flags: 0x802, Sequence: 0x9acb0443, PID: 0xba38ef3c}, Data: []uint8{0x2, 0x0, 0x0, 0xc, 0xb, 0x0, 0x1, 0x0, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x0, 0xc, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x30, 0x0, 0x4, 0x0, 0x2c, 0x0, 0x1, 0x0, 0xc, 0x0, 0x1, 0x0, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x0, 0x1c, 0x0, 0x2, 0x0, 0xc, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6d, 0x92, 0x20, 0x20, 0xc, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x48, 0xd9}}},
|
|
|
|
[]netlink.Message{netlink.Message{Header: netlink.Header{Length: 0x14, Type: 0x3, Flags: 0x2, Sequence: 0x9acb0443, PID: 0xba38ef3c}, Data: []uint8{0x0, 0x0, 0x0, 0x0}}},
|
|
|
|
}
|
|
|
|
|
|
|
|
c := &nftables.Conn{
|
|
|
|
TestDial: func(req []netlink.Message) ([]netlink.Message, error) {
|
|
|
|
for idx, msg := range req {
|
|
|
|
b, err := msg.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(b) < 16 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
b = b[16:]
|
|
|
|
if len(want) == 0 {
|
|
|
|
t.Errorf("no want entry for message %d: %x", idx, b)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if got, want := b, want[0]; !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("message %d: got %#v, want %#v", idx, got, want)
|
|
|
|
}
|
|
|
|
want = want[1:]
|
|
|
|
}
|
|
|
|
rep := reply[0]
|
|
|
|
reply = reply[1:]
|
|
|
|
return rep, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
rules, err := c.GetRule(
|
|
|
|
&nftables.Table{
|
|
|
|
Family: nftables.TableFamilyIPv4,
|
|
|
|
Name: "filter",
|
|
|
|
},
|
|
|
|
&nftables.Chain{
|
|
|
|
Name: "input",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := len(rules), 1; got != want {
|
|
|
|
t.Fatalf("unexpected number of rules: got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
rule := rules[0]
|
|
|
|
if got, want := len(rule.Exprs), 1; got != want {
|
|
|
|
t.Fatalf("unexpected number of exprs: got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
ce, ok := rule.Exprs[0].(*expr.Counter)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected expression type: got %T, want *expr.Counter", rule.Exprs[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := ce.Packets, uint64(674009); got != want {
|
|
|
|
t.Errorf("unexpected number of packets: got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := ce.Bytes, uint64(1838293024); got != want {
|
|
|
|
t.Errorf("unexpected number of bytes: got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
}
|
2018-08-08 16:09:16 -05:00
|
|
|
|
|
|
|
func TestAddCounter(t *testing.T) {
|
|
|
|
// The want byte sequences come from stracing nft(8), e.g.:
|
|
|
|
// strace -f -v -x -s 2048 -eraw=sendto nft add table ip nat
|
|
|
|
//
|
|
|
|
// The nft(8) command sequence was taken from:
|
|
|
|
// https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)
|
|
|
|
want := [][]byte{
|
|
|
|
// batch begin
|
|
|
|
[]byte("\x00\x00\x0a\x00"),
|
|
|
|
// nft add counter ip filter fwded
|
|
|
|
[]byte("\x02\x00\x00\x00\x0b\x00\x01\x00\x66\x69\x6c\x74\x65\x72\x00\x00\x0a\x00\x02\x00\x66\x77\x64\x65\x64\x00\x00\x00\x08\x00\x03\x00\x00\x00\x00\x01\x1c\x00\x04\x80\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
|
|
|
|
// nft add rule ip filter forward counter name fwded
|
|
|
|
[]byte("\x02\x00\x00\x00\x0b\x00\x01\x00\x66\x69\x6c\x74\x65\x72\x00\x00\x0c\x00\x02\x00\x66\x6f\x72\x77\x61\x72\x64\x00\x2c\x00\x04\x80\x28\x00\x01\x80\x0b\x00\x01\x00\x6f\x62\x6a\x72\x65\x66\x00\x00\x18\x00\x02\x80\x08\x00\x01\x00\x00\x00\x00\x01\x09\x00\x02\x00\x66\x77\x64\x65\x64\x00\x00\x00"),
|
|
|
|
// batch end
|
|
|
|
[]byte("\x00\x00\x0a\x00"),
|
|
|
|
}
|
|
|
|
|
|
|
|
c := &nftables.Conn{
|
|
|
|
TestDial: func(req []netlink.Message) ([]netlink.Message, error) {
|
|
|
|
for idx, msg := range req {
|
|
|
|
b, err := msg.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(b) < 16 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
b = b[16:]
|
|
|
|
if len(want) == 0 {
|
|
|
|
t.Errorf("no want entry for message %d: %x", idx, b)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if got, want := b, want[0]; !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("message %d: got %x, want %x", idx, got, want)
|
|
|
|
}
|
|
|
|
want = want[1:]
|
|
|
|
}
|
|
|
|
return req, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
c.AddObj(&nftables.CounterObj{
|
|
|
|
Table: &nftables.Table{Name: "filter", Family: nftables.TableFamilyIPv4},
|
|
|
|
Name: "fwded",
|
|
|
|
Bytes: 0,
|
|
|
|
Packets: 0,
|
|
|
|
})
|
|
|
|
|
|
|
|
c.AddRule(&nftables.Rule{
|
|
|
|
Table: &nftables.Table{Name: "filter", Family: nftables.TableFamilyIPv4},
|
|
|
|
Chain: &nftables.Chain{Name: "forward", Type: nftables.ChainTypeFilter},
|
|
|
|
Exprs: []expr.Any{
|
|
|
|
&expr.Objref{
|
|
|
|
Type: 1,
|
|
|
|
Name: "fwded",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
if err := c.Flush(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetObjReset(t *testing.T) {
|
|
|
|
// The want byte sequences come from stracing nft(8), e.g.:
|
|
|
|
// strace -f -v -x -s 2048 -eraw=sendto nft list chain ip filter forward
|
|
|
|
|
|
|
|
want := [][]byte{
|
|
|
|
[]byte{0x2, 0x0, 0x0, 0x0, 0xb, 0x0, 0x1, 0x0, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0, 0x0, 0xa, 0x0, 0x2, 0x0, 0x66, 0x77, 0x64, 0x65, 0x64, 0x0, 0x0, 0x0, 0x8, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x1},
|
|
|
|
}
|
|
|
|
|
|
|
|
// The reply messages come from adding log.Printf("msgs: %#v", msgs) to
|
|
|
|
// (*github.com/mdlayher/netlink/Conn).receive
|
|
|
|
reply := [][]netlink.Message{
|
|
|
|
nil,
|
|
|
|
[]netlink.Message{netlink.Message{Header: netlink.Header{Length: 0x64, Type: 0xa12, Flags: 0x802, Sequence: 0x9acb0443, PID: 0xde9}, Data: []uint8{0x2, 0x0, 0x0, 0x10, 0xb, 0x0, 0x1, 0x0, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0, 0x0, 0xa, 0x0, 0x2, 0x0, 0x66, 0x77, 0x64, 0x65, 0x64, 0x0, 0x0, 0x0, 0x8, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1c, 0x0, 0x4, 0x0, 0xc, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x61, 0xc, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xc, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}}},
|
|
|
|
[]netlink.Message{netlink.Message{Header: netlink.Header{Length: 0x14, Type: 0x3, Flags: 0x2, Sequence: 0x9acb0443, PID: 0xde9}, Data: []uint8{0x0, 0x0, 0x0, 0x0}}},
|
|
|
|
}
|
|
|
|
|
|
|
|
c := &nftables.Conn{
|
|
|
|
TestDial: func(req []netlink.Message) ([]netlink.Message, error) {
|
|
|
|
for idx, msg := range req {
|
|
|
|
b, err := msg.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(b) < 16 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
b = b[16:]
|
|
|
|
if len(want) == 0 {
|
|
|
|
t.Errorf("no want entry for message %d: %x", idx, b)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if got, want := b, want[0]; !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("message %d: got %#v, want %#v", idx, got, want)
|
|
|
|
}
|
|
|
|
want = want[1:]
|
|
|
|
}
|
|
|
|
rep := reply[0]
|
|
|
|
reply = reply[1:]
|
|
|
|
return rep, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-10-03 11:23:26 -05:00
|
|
|
filter := &nftables.Table{Name: "filter", Family: nftables.TableFamilyIPv4}
|
2018-08-08 16:09:16 -05:00
|
|
|
objs, err := c.GetObjReset(&nftables.CounterObj{
|
2018-10-03 11:23:26 -05:00
|
|
|
Table: filter,
|
2018-08-08 16:09:16 -05:00
|
|
|
Name: "fwded",
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := len(objs), 1; got != want {
|
|
|
|
t.Fatalf("unexpected number of rules: got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
obj := objs[0]
|
|
|
|
co, ok := obj.(*nftables.CounterObj)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected type: got %T, want *nftables.CounterObj", obj)
|
|
|
|
}
|
2018-10-03 11:23:26 -05:00
|
|
|
if got, want := co.Table.Name, filter.Name; got != want {
|
|
|
|
t.Errorf("unexpected table name: got %q, want %q", got, want)
|
|
|
|
}
|
|
|
|
if got, want := co.Table.Family, filter.Family; got != want {
|
|
|
|
t.Errorf("unexpected table family: got %d, want %d", got, want)
|
|
|
|
}
|
2018-08-08 16:09:16 -05:00
|
|
|
if got, want := co.Packets, uint64(9); got != want {
|
|
|
|
t.Errorf("unexpected number of packets: got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := co.Bytes, uint64(1121); got != want {
|
|
|
|
t.Errorf("unexpected number of bytes: got %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
}
|