Skip to content

Commit 9b8dd94

Browse files
kerumetogvisor-bot
authored andcommitted
Netlink_Netfilter process message implementation for adding and getting tables.
Added an Nftables field to the stack and functionality and error-checking for creating a new NFTable (NFT_MSG_NEWTABLE) and retrieving that table (NFT_MSG_GETTABLE). Removed hostinet tests, as nftables is only supported for netstack. PiperOrigin-RevId: 767789742
1 parent 14c05c1 commit 9b8dd94

21 files changed

+690
-47
lines changed

pkg/abi/linux/netlink_netfilter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const (
3535
)
3636

3737
// NetFilterGenMsg describes the netlink netfilter genmsg message, from uapi/linux/netfilter/nfnetlink.h.
38+
//
39+
// +marshal
3840
type NetFilterGenMsg struct {
3941
Family uint8
4042
Version uint8

pkg/abi/linux/nf_tables.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,29 @@ const (
124124
NFT_MSG_MAX
125125
)
126126

127+
// NfTableFlags represents table flags that can be set for a table, namely dormant.
128+
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
129+
const (
130+
NFT_TABLE_F_DORMANT = 0x1
131+
)
132+
133+
// NfTableAttributes represents the netfilter table attributes.
134+
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
135+
const (
136+
NFTA_TABLE_UNSPEC uint16 = iota
137+
NFTA_TABLE_NAME
138+
NFTA_TABLE_FLAGS
139+
NFTA_TABLE_USE
140+
NFTA_TABLE_HANDLE
141+
NFTA_TABLE_PAD
142+
NFTA_TABLE_USERDATA
143+
NFTA_TABLE_OWNER
144+
__NFTA_TABLE_MAX
145+
)
146+
147+
// NFTA_TABLE_MAX is the maximum netfilter table attribute.
148+
const NFTA_TABLE_MAX = __NFTA_TABLE_MAX - 1
149+
127150
// Nf table relational operators.
128151
// Used by the nft comparison operation to compare values in registers.
129152
// These correspond to enum values in include/uapi/linux/netfilter/nf_tables.h.

pkg/sentry/socket/netlink/netfilter/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ go_library(
1212
deps = [
1313
"//pkg/abi/linux",
1414
"//pkg/context",
15+
"//pkg/log",
16+
"//pkg/sentry/inet",
1517
"//pkg/sentry/kernel",
1618
"//pkg/sentry/socket/netlink",
1719
"//pkg/sentry/socket/netlink/nlmsg",
20+
"//pkg/sentry/socket/netstack",
1821
"//pkg/syserr",
1922
"//pkg/tcpip/nftables",
23+
"//pkg/tcpip/stack",
2024
],
2125
)

pkg/sentry/socket/netlink/netfilter/protocol.go

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ package netfilter
1818
import (
1919
"gvisor.dev/gvisor/pkg/abi/linux"
2020
"gvisor.dev/gvisor/pkg/context"
21+
"gvisor.dev/gvisor/pkg/log"
22+
"gvisor.dev/gvisor/pkg/sentry/inet"
2123
"gvisor.dev/gvisor/pkg/sentry/kernel"
2224
"gvisor.dev/gvisor/pkg/sentry/socket/netlink"
2325
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
26+
"gvisor.dev/gvisor/pkg/sentry/socket/netstack"
2427
"gvisor.dev/gvisor/pkg/syserr"
2528
"gvisor.dev/gvisor/pkg/tcpip/nftables"
29+
"gvisor.dev/gvisor/pkg/tcpip/stack"
2630
)
2731

2832
// Protocol implements netlink.Protocol.
@@ -60,22 +64,122 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
6064
// Netlink message payloads must be of at least the size of the genmsg. Return early if it is not,
6165
// from linux/net/netfilter/nfnetlink.c.
6266
if netLinkMessagePayloadSize(&hdr) < linux.SizeOfNetfilterGenMsg {
67+
log.Debugf("Netlink message payload is too small: %d < %d", netLinkMessagePayloadSize(&hdr), linux.SizeOfNetfilterGenMsg)
6368
return nil
6469
}
6570

6671
msgType := hdr.NetFilterMsgType()
72+
st := inet.StackFromContext(ctx).(*netstack.Stack).Stack
73+
nft := (st.NFTables()).(*nftables.NFTables)
74+
var nfGenMsg linux.NetFilterGenMsg
75+
76+
// The payload of a message is its attributes.
77+
atr, ok := msg.GetData(&nfGenMsg)
78+
if !ok {
79+
log.Debugf("Failed to get message data")
80+
return syserr.ErrInvalidArgument
81+
}
82+
83+
attrs, ok := atr.Parse()
84+
if !ok {
85+
log.Debugf("Failed to parse message attributes")
86+
return syserr.ErrInvalidArgument
87+
}
88+
89+
// Nftables functions error check the address family value.
90+
family := stack.AddressFamily(nfGenMsg.Family)
6791
// TODO: b/421437663 - Match the message type and call the appropriate Nftables function.
6892
switch msgType {
93+
case linux.NFT_MSG_NEWTABLE:
94+
return p.newTable(nft, attrs, family, hdr.Flags)
95+
case linux.NFT_MSG_GETTABLE:
96+
return p.getTable(nft, attrs, family, hdr.Flags, ms)
6997
default:
98+
log.Debugf("Unsupported message type: %d", msgType)
7099
return syserr.ErrInvalidArgument
71100
}
72101
}
73102

74-
// init registers the NETLINK_NETFILTER provider.
75-
func init() {
76-
netlink.RegisterProvider(linux.NETLINK_NETFILTER, NewProtocol)
103+
// newTable creates a new table for the given family.
104+
func (p *Protocol) newTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, flags uint16) *syserr.Error {
105+
// TODO: b/421437663 - Handle the case where the table name is set to empty string.
106+
// The table name is required.
107+
tabNameBytes, ok := attrs[linux.NFTA_TABLE_NAME]
108+
if !ok {
109+
log.Debugf("Nftables: Table name attribute is malformed or not found")
110+
return syserr.ErrInvalidArgument
111+
}
112+
113+
var dormant bool
114+
if dbytes, ok := attrs[linux.NFTA_TABLE_FLAGS]; ok {
115+
dflag, _ := dbytes.Uint32()
116+
dormant = (dflag & linux.NFT_TABLE_F_DORMANT) == linux.NFT_TABLE_F_DORMANT
117+
}
118+
119+
tab, err := nft.GetTable(family, tabNameBytes.String())
120+
121+
// If a table already exists, only update its dormant flags if NLM_F_EXCL and NLM_F_REPLACE
122+
// are not set. From net/netfilter/nf_tables_api.c:nf_tables_newtable:nf_tables_updtable
123+
if tab != nil && err == nil {
124+
if flags&linux.NLM_F_EXCL == linux.NLM_F_EXCL {
125+
log.Debugf("Nftables: Table with name: %s already exists", tabNameBytes.String())
126+
return syserr.ErrExists
127+
}
128+
129+
if flags&linux.NLM_F_REPLACE == linux.NLM_F_REPLACE {
130+
log.Debugf("Nftables: Table with name: %s already exists and NLM_F_REPLACE is not supported", tabNameBytes.String())
131+
return syserr.ErrNotSupported
132+
}
133+
} else {
134+
// There does not seem to be a way to add comments to a table using the nft binary.
135+
tab, err = nft.CreateTable(family, tabNameBytes.String())
136+
if err != nil {
137+
log.Debugf("Nftables: Failed to create table with name: %s. Error: %s", tabNameBytes.String(), err.Error())
138+
// If there is an error, it is not a duplicate error (checked above).
139+
return syserr.ErrInvalidArgument
140+
}
141+
}
142+
143+
tab.SetDormant(dormant)
144+
return nil
145+
}
146+
147+
// getTable returns a table for the given family. Returns nil on success and
148+
// a sys.error on failure.
149+
func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, flags uint16, ms *nlmsg.MessageSet) *syserr.Error {
150+
// The table name is required.
151+
tabNameBytes, ok := attrs[linux.NFTA_TABLE_NAME]
152+
if !ok {
153+
log.Debugf("Nftables: Table name attribute is malformed or not found")
154+
return syserr.ErrInvalidArgument
155+
}
156+
157+
tab, err := nft.GetTable(family, tabNameBytes.String())
158+
if err != nil {
159+
log.Debugf("Nftables: ENOENT for table with name: %s", tabNameBytes.String())
160+
return syserr.ErrNoFileOrDir
161+
}
162+
163+
tabName := tab.GetName()
164+
m := ms.AddMessage(linux.NetlinkMessageHeader{
165+
Type: uint16(linux.NFNL_SUBSYS_NFTABLES)<<8 | uint16(linux.NFT_MSG_GETTABLE),
166+
})
167+
168+
m.Put(&linux.NetFilterGenMsg{
169+
Family: uint8(family),
170+
Version: uint8(linux.NFNETLINK_V0),
171+
// Unused, set to 0.
172+
ResourceID: uint16(0),
173+
})
174+
m.PutAttrString(linux.NFTA_TABLE_NAME, tabName)
175+
return nil
77176
}
78177

79178
func netLinkMessagePayloadSize(h *linux.NetlinkMessageHeader) int {
80179
return int(h.Length) - linux.NetlinkMessageHeaderSize
81180
}
181+
182+
// init registers the NETLINK_NETFILTER provider.
183+
func init() {
184+
netlink.RegisterProvider(linux.NETLINK_NETFILTER, NewProtocol)
185+
}

pkg/tcpip/nftables/nftables.go

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
"gvisor.dev/gvisor/pkg/tcpip/stack"
2525
)
2626

27+
// TODO: b/421437663 - Refactor functions to return a POSIX syserr
28+
2729
//
2830
// Interface-Related Methods
2931
//
@@ -291,7 +293,7 @@ func (nf *NFTables) GetTable(family stack.AddressFamily, tableName string) (*Tab
291293
// Note: if the table already exists, the existing table is returned without any
292294
// modifications.
293295
// Note: Table initialized as not dormant.
294-
func (nf *NFTables) AddTable(family stack.AddressFamily, name string, comment string,
296+
func (nf *NFTables) AddTable(family stack.AddressFamily, name string,
295297
errorOnDuplicate bool) (*Table, error) {
296298
// Ensures address family is valid.
297299
if err := validateAddressFamily(family); err != nil {
@@ -325,7 +327,6 @@ func (nf *NFTables) AddTable(family stack.AddressFamily, name string, comment st
325327
name: name,
326328
afFilter: nf.filters[family],
327329
chains: make(map[string]*Chain),
328-
comment: comment,
329330
flagSet: make(map[TableFlag]struct{}),
330331
}
331332
tableMap[name] = t
@@ -337,8 +338,8 @@ func (nf *NFTables) AddTable(family stack.AddressFamily, name string, comment st
337338
// but also returns an error if a table by the same name already exists.
338339
// Note: this interface mirrors the difference between the create and add
339340
// commands within the nft binary.
340-
func (nf *NFTables) CreateTable(family stack.AddressFamily, name string, comment string) (*Table, error) {
341-
return nf.AddTable(family, name, comment, true)
341+
func (nf *NFTables) CreateTable(family stack.AddressFamily, name string) (*Table, error) {
342+
return nf.AddTable(family, name, true)
342343
}
343344

344345
// DeleteTable deletes the specified table from the NFTables object returning
@@ -436,16 +437,6 @@ func (t *Table) GetAddressFamily() stack.AddressFamily {
436437
return t.afFilter.family
437438
}
438439

439-
// GetComment returns the comment of the table.
440-
func (t *Table) GetComment() string {
441-
return t.comment
442-
}
443-
444-
// SetComment sets the comment of the table.
445-
func (t *Table) SetComment(comment string) {
446-
t.comment = comment
447-
}
448-
449440
// IsDormant returns whether the table is dormant.
450441
func (t *Table) IsDormant() bool {
451442
_, dormant := t.flagSet[TableFlagDormant]

pkg/tcpip/nftables/nftables_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ func TestEvaluateImmediateVerdict(t *testing.T) {
528528
// Sets up an NFTables object with a base chain (for 2 rules) and another
529529
// target chain (for 1 rule).
530530
nf := newNFTablesStd()
531-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
531+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
532532
if err != nil {
533533
t.Fatalf("unexpected error for AddTable: %v", err)
534534
}
@@ -592,7 +592,7 @@ func TestEvaluateImmediateBytesData(t *testing.T) {
592592
t.Run(tname, func(t *testing.T) {
593593
// Sets up an NFTables object with a base chain with policy accept.
594594
nf := newNFTablesStd()
595-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
595+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
596596
if err != nil {
597597
t.Fatalf("unexpected error for AddTable: %v", err)
598598
}
@@ -1078,7 +1078,7 @@ func TestEvaluateComparison(t *testing.T) {
10781078
t.Run(test.tname, func(t *testing.T) {
10791079
// Sets up an NFTables object with a single table, chain, and rule.
10801080
nf := newNFTablesStd()
1081-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
1081+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
10821082
if err != nil {
10831083
t.Fatalf("unexpected error for AddTable: %v", err)
10841084
}
@@ -1315,7 +1315,7 @@ func TestEvaluateRanged(t *testing.T) {
13151315
t.Run(test.tname, func(t *testing.T) {
13161316
// Sets up an NFTables object with a single table, chain, and rule.
13171317
nf := newNFTablesStd()
1318-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
1318+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
13191319
if err != nil {
13201320
t.Fatalf("unexpected error for AddTable: %v", err)
13211321
}
@@ -1549,7 +1549,7 @@ func TestEvaluatePayloadLoad(t *testing.T) {
15491549
t.Run(test.tname, func(t *testing.T) {
15501550
// Sets up an NFTables object with a single table, chain, and rule.
15511551
nf := newNFTablesStd()
1552-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
1552+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
15531553
if err != nil {
15541554
t.Fatalf("unexpected error for AddTable: %v", err)
15551555
}
@@ -2053,7 +2053,7 @@ func TestEvaluatePayloadSet(t *testing.T) {
20532053
t.Run(test.tname, func(t *testing.T) {
20542054
// Sets up an NFTables object with a single table, chain, and rule.
20552055
nf := newNFTablesStd()
2056-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
2056+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
20572057
if err != nil {
20582058
t.Fatalf("unexpected error for AddTable: %v", err)
20592059
}
@@ -2238,7 +2238,7 @@ func TestEvaluateBitwise(t *testing.T) {
22382238
t.Run(test.tname, func(t *testing.T) {
22392239
// Sets up an NFTables object with a single table, chain, and rule.
22402240
nf := newNFTablesStd()
2241-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
2241+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
22422242
if err != nil {
22432243
t.Fatalf("unexpected error for AddTable: %v", err)
22442244
}
@@ -2304,7 +2304,7 @@ func TestEvaluateCounter(t *testing.T) {
23042304
t.Run("counter increment tests", func(t *testing.T) {
23052305
// Sets up an NFTables object with a base chain with policy accept.
23062306
nf := newNFTablesStd()
2307-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
2307+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
23082308
if err != nil {
23092309
t.Fatalf("unexpected error for AddTable: %v", err)
23102310
}
@@ -2372,7 +2372,7 @@ func TestEvaluateLast(t *testing.T) {
23722372
fakeClock := faketime.NewManualClock()
23732373
fixedRNG := rand.RNGFrom(&fixedReader{})
23742374
nf := NewNFTables(fakeClock, fixedRNG)
2375-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
2375+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
23762376
if err != nil {
23772377
t.Fatalf("unexpected error for AddTable: %v", err)
23782378
}
@@ -2499,7 +2499,7 @@ func TestEvaluateRoute(t *testing.T) {
24992499
t.Run(test.tname, func(t *testing.T) {
25002500
// Sets up an NFTables object with a single table, chain, and rule.
25012501
nf := newNFTablesStd()
2502-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
2502+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
25032503
if err != nil {
25042504
t.Fatalf("unexpected error for AddTable: %v", err)
25052505
}
@@ -2740,7 +2740,7 @@ func TestEvaluateByteorder(t *testing.T) {
27402740
t.Run(test.tname, func(t *testing.T) {
27412741
// Sets up an NFTables object with a single table, chain, and rule.
27422742
nf := newNFTablesStd()
2743-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
2743+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
27442744
if err != nil {
27452745
t.Fatalf("unexpected error for AddTable: %v", err)
27462746
}
@@ -2922,7 +2922,7 @@ func TestEvaluateMetaLoad(t *testing.T) {
29222922
// Using Manual Clock sets time.Now to Unix Epoch which fixes rng seed!
29232923
nf := NewNFTables(fakeClock, rand.RNGFrom(&fixedReader{}))
29242924

2925-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
2925+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
29262926
if err != nil {
29272927
t.Fatalf("unexpected error for AddTable: %v", err)
29282928
}
@@ -3012,7 +3012,7 @@ func TestEvaluateMetaSet(t *testing.T) {
30123012
t.Run(test.tname, func(t *testing.T) {
30133013
// Sets up an NFTables object with a single table, chain, and rule.
30143014
nf := newNFTablesStd()
3015-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
3015+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
30163016
if err != nil {
30173017
t.Fatalf("unexpected error for AddTable: %v", err)
30183018
}
@@ -3470,7 +3470,7 @@ func TestLoopCheckOnRegisterAndUnregister(t *testing.T) {
34703470
t.Run(test.tname, func(t *testing.T) {
34713471
// Sets up an NFTables object based on test struct.
34723472
nf := newNFTablesStd()
3473-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
3473+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
34743474
if err != nil {
34753475
t.Fatalf("unexpected error for AddTable: %v", err)
34763476
}
@@ -3581,7 +3581,7 @@ func TestMaxNestedJumps(t *testing.T) {
35813581
t.Run(test.tname, func(t *testing.T) {
35823582
// Sets up chains of nested jumps or gotos.
35833583
nf := newNFTablesStd()
3584-
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
3584+
tab, err := nf.AddTable(arbitraryFamily, "test", false)
35853585
if err != nil {
35863586
t.Fatalf("unexpected error for AddTable: %v", err)
35873587
}

pkg/tcpip/nftables/nftables_types.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,6 @@ type Table struct {
178178
// flags is the set of optional flags for the table.
179179
// Note: currently nftables only has the single Dormant flag.
180180
flagSet map[TableFlag]struct{}
181-
182-
// comment is the optional comment for the table.
183-
comment string
184181
}
185182

186183
// hookFunctionStack represents the list of base chains for a specific hook.

0 commit comments

Comments
 (0)