Skip to content

Commit df7a6b6

Browse files
kerumetogvisor-bot
authored andcommitted
Update netfilter protocol address family checks.
Netfilter messages return different error values when validating address families depending on the message type being received. Updated functionality to reflect that. PiperOrigin-RevId: 781215895
1 parent 7323f1d commit df7a6b6

File tree

15 files changed

+1315
-330
lines changed

15 files changed

+1315
-330
lines changed

pkg/abi/linux/netlink.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,35 @@ type NetlinkMessageHeader struct {
6666
// NetlinkMessageHeaderSize is the size of NetlinkMessageHeader.
6767
const NetlinkMessageHeaderSize = 16
6868

69-
// Netlink message header flags, from uapi/linux/netlink.h.
69+
// Netlink message header flag values, from uapi/linux/netlink.h.
7070
const (
7171
NLM_F_REQUEST = 0x1
7272
NLM_F_MULTI = 0x2
7373
NLM_F_ACK = 0x4
7474
NLM_F_ECHO = 0x8
7575
NLM_F_DUMP_INTR = 0x10
76-
NLM_F_ROOT = 0x100
77-
NLM_F_MATCH = 0x200
78-
NLM_F_ATOMIC = 0x400
79-
NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
80-
NLM_F_REPLACE = 0x100
81-
NLM_F_EXCL = 0x200
82-
NLM_F_CREATE = 0x400
83-
NLM_F_APPEND = 0x800
76+
)
77+
78+
// Netlink message header flags for GET requests, from uapi/linux/netlink.h.
79+
const (
80+
NLM_F_ROOT = 0x100
81+
NLM_F_MATCH = 0x200
82+
NLM_F_ATOMIC = 0x400
83+
NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
84+
)
85+
86+
// Netlink message header flags for NEW requests, from uapi/linux/netlink.h.
87+
const (
88+
NLM_F_REPLACE = 0x100
89+
NLM_F_EXCL = 0x200
90+
NLM_F_CREATE = 0x400
91+
NLM_F_APPEND = 0x800
92+
)
93+
94+
// Netlink message header flags for DELETE requests, from uapi/linux/netlink.h.
95+
const (
96+
NLM_F_NONREC = 0x100
97+
NLM_F_BULK = 0x200
8498
)
8599

86100
// Standard netlink message types, from uapi/linux/netlink.h.

pkg/abi/linux/nf_tables.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ package linux
1616

1717
// This file contains constants required to support nf_tables.
1818

19+
// Name length constants for nf_table structures. These correspond to values in
20+
// include/uapi/linux/netfilter/nf_tables.h.
21+
const (
22+
NFT_NAME_MAXLEN = 256
23+
NFT_TABLE_MAXNAMELEN = NFT_NAME_MAXLEN
24+
NFT_CHAIN_MAXNAMELEN = NFT_NAME_MAXLEN
25+
NFT_SET_MAXNAMELEN = NFT_NAME_MAXLEN
26+
NFT_OBJ_MAXNAMELEN = NFT_NAME_MAXLEN
27+
NFT_USERDATA_MAXLEN = 256
28+
NFT_OSF_MAXGENRELEN = 16
29+
)
30+
1931
// 16-byte Registers that can be used to maintain state for rules.
2032
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
2133
const (
@@ -127,7 +139,10 @@ const (
127139
// NfTableFlags represents table flags that can be set for a table, namely dormant.
128140
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
129141
const (
130-
NFT_TABLE_F_DORMANT = 0x1
142+
NFT_TABLE_F_DORMANT uint32 = 0x1
143+
NFT_TABLE_F_OWNER = 0x2
144+
NFT_TABLE_F_PERSIST = 0x4
145+
NFT_TABLE_F_MASK = NFT_TABLE_F_DORMANT | NFT_TABLE_F_OWNER | NFT_TABLE_F_PERSIST
131146
)
132147

133148
// NfTableAttributes represents the netfilter table attributes.

pkg/sentry/socket/netlink/netfilter/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ go_library(
1313
"//pkg/abi/linux",
1414
"//pkg/context",
1515
"//pkg/log",
16+
"//pkg/marshal/primitive",
1617
"//pkg/sentry/inet",
1718
"//pkg/sentry/kernel",
1819
"//pkg/sentry/socket/netlink",

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

Lines changed: 144 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"gvisor.dev/gvisor/pkg/abi/linux"
2222
"gvisor.dev/gvisor/pkg/context"
2323
"gvisor.dev/gvisor/pkg/log"
24+
"gvisor.dev/gvisor/pkg/marshal/primitive"
2425
"gvisor.dev/gvisor/pkg/sentry/inet"
2526
"gvisor.dev/gvisor/pkg/sentry/kernel"
2627
"gvisor.dev/gvisor/pkg/sentry/socket/netlink"
@@ -88,12 +89,11 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
8889
return syserr.ErrInvalidArgument
8990
}
9091

91-
// Nftables functions error check the address family value.
92-
family := stack.AddressFamily(nfGenMsg.Family)
92+
family := nftables.AFtoNetlinkAF(nfGenMsg.Family)
9393
// TODO: b/421437663 - Match the message type and call the appropriate Nftables function.
9494
switch msgType {
9595
case linux.NFT_MSG_NEWTABLE:
96-
if err := p.newTable(nft, attrs, family, hdr.Flags); err != nil {
96+
if err := p.newTable(nft, attrs, family, hdr.Flags, ms); err != nil {
9797
log.Debugf("Nftables new table error: %s", err)
9898
return err.GetError()
9999
}
@@ -104,49 +104,110 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
104104
return err.GetError()
105105
}
106106
return nil
107+
case linux.NFT_MSG_DELTABLE, linux.NFT_MSG_DESTROYTABLE:
108+
if err := p.deleteTable(nft, attrs, family, hdr, msgType, ms); err != nil {
109+
log.Debugf("Nftables delete table error: %s", err)
110+
return err.GetError()
111+
}
112+
return nil
107113
default:
108114
log.Debugf("Unsupported message type: %d", msgType)
109115
return syserr.ErrNotSupported
110116
}
111117
}
112118

113119
// newTable creates a new table for the given family.
114-
func (p *Protocol) newTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, flags uint16) *syserr.AnnotatedError {
120+
func (p *Protocol) newTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, flags uint16, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
121+
if family == stack.NumAFs {
122+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Address family is not supported"))
123+
}
124+
115125
// TODO: b/421437663 - Handle the case where the table name is set to empty string.
116126
// The table name is required.
117127
tabNameBytes, ok := attrs[linux.NFTA_TABLE_NAME]
118128
if !ok {
119129
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Table name attribute is malformed or not found"))
120130
}
121131

122-
var dormant bool
123-
if dbytes, ok := attrs[linux.NFTA_TABLE_FLAGS]; ok {
124-
dflag, _ := dbytes.Uint32()
125-
dormant = (dflag & linux.NFT_TABLE_F_DORMANT) == linux.NFT_TABLE_F_DORMANT
126-
}
127-
128-
tab, err := nft.GetTable(family, tabNameBytes.String())
132+
tab, err := nft.GetTable(family, tabNameBytes.String(), uint32(ms.PortID))
129133
if err != nil && err.GetError() != syserr.ErrNoFileOrDir {
130134
return err
131135
}
132136

133137
// If a table already exists, only update its dormant flags if NLM_F_EXCL and NLM_F_REPLACE
134138
// are not set. From net/netfilter/nf_tables_api.c:nf_tables_newtable:nf_tables_updtable
135139
if tab != nil {
136-
if flags&linux.NLM_F_EXCL == linux.NLM_F_EXCL {
137-
return syserr.NewAnnotatedError(syserr.ErrExists, fmt.Sprintf("Nftables: Table with name: %s already exists", tabNameBytes.String()))
140+
if flags&linux.NLM_F_EXCL != 0 {
141+
return syserr.NewAnnotatedError(syserr.ErrExists, fmt.Sprintf("Nftables: Table with name: %s already exists", tab.GetName()))
138142
}
139143

140-
if flags&linux.NLM_F_REPLACE == linux.NLM_F_REPLACE {
141-
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table with name: %s already exists and NLM_F_REPLACE is not supported", tabNameBytes.String()))
144+
if flags&linux.NLM_F_REPLACE != 0 {
145+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table with name: %s already exists and NLM_F_REPLACE is not supported", tab.GetName()))
142146
}
143-
} else {
144-
tab, err = nft.CreateTable(family, tabNameBytes.String())
145-
if err != nil {
147+
148+
return p.updateTable(nft, tab, attrs, family, ms)
149+
}
150+
151+
// TODO: b/421437663 - Support additional user-specified table flags.
152+
var attrFlags uint32 = 0
153+
if uflags, ok := attrs[linux.NFTA_TABLE_FLAGS]; ok {
154+
attrFlags, _ = uflags.Uint32()
155+
// Flags sent through the NFTA_TABLE_FLAGS attribute are of type uint32
156+
// but should only have user flags set. This check needs to be done before table creation.
157+
if attrFlags & ^uint32(linux.NFT_TABLE_F_MASK) != 0 {
158+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table flags set are not supported"))
159+
}
160+
}
161+
162+
tab, err = nft.CreateTable(family, tabNameBytes.String())
163+
if err != nil {
164+
return err
165+
}
166+
167+
if udata, ok := attrs[linux.NFTA_TABLE_USERDATA]; ok {
168+
tab.SetUserData(udata)
169+
}
170+
171+
// Flags should only be assigned after we have successfully created the table.
172+
dormant := (attrFlags & uint32(linux.NFT_TABLE_F_DORMANT)) != 0
173+
tab.SetDormant(dormant)
174+
175+
owner := (attrFlags & uint32(linux.NFT_TABLE_F_OWNER)) != 0
176+
if owner {
177+
if err := tab.SetOwner(uint32(ms.PortID)); err != nil {
178+
return err
179+
}
180+
}
181+
182+
return nil
183+
}
184+
185+
// updateTable updates an existing table.
186+
func (p *Protocol) updateTable(nft *nftables.NFTables, tab *nftables.Table, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
187+
var attrFlags uint32
188+
if uflags, ok := attrs[linux.NFTA_TABLE_FLAGS]; ok {
189+
attrFlags, _ = uflags.Uint32()
190+
// This check needs to be done before table update.
191+
if attrFlags & ^uint32(linux.NFT_TABLE_F_MASK) > 0 {
192+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table flags set are not supported"))
193+
}
194+
}
195+
196+
// When updating the table, if the table has an owner but the owner flag isn't set,
197+
// the table should not be updated.
198+
// From net/netfilter/nf_tables_api.c:nf_tables_updtable.
199+
if tab.HasOwner() && (attrFlags&uint32(linux.NFT_TABLE_F_OWNER)) == 0 {
200+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table with name: %s already has an owner but NFT_TABLE_F_OWNER was not set when updating the table", tab.GetName()))
201+
}
202+
203+
// The owner is only updated if the table has no previous owner.
204+
if !tab.HasOwner() && attrFlags&uint32(linux.NFT_TABLE_F_OWNER) != 0 {
205+
if err := tab.SetOwner(uint32(ms.PortID)); err != nil {
146206
return err
147207
}
148208
}
149209

210+
dormant := (attrFlags & uint32(linux.NFT_TABLE_F_DORMANT)) != 0
150211
tab.SetDormant(dormant)
151212
return nil
152213
}
@@ -159,12 +220,16 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
159220
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Table name attribute is malformed or not found"))
160221
}
161222

162-
tab, err := nft.GetTable(family, tabNameBytes.String())
223+
tab, err := nft.GetTable(family, tabNameBytes.String(), uint32(ms.PortID))
163224
if err != nil {
164225
return err
165226
}
166227

167228
tabName := tab.GetName()
229+
userFlags, err := tab.GetLinuxUserFlagSet()
230+
if err != nil {
231+
return err
232+
}
168233
m := ms.AddMessage(linux.NetlinkMessageHeader{
169234
Type: uint16(linux.NFNL_SUBSYS_NFTABLES)<<8 | uint16(linux.NFT_MSG_GETTABLE),
170235
})
@@ -176,13 +241,73 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
176241
ResourceID: uint16(0),
177242
})
178243
m.PutAttrString(linux.NFTA_TABLE_NAME, tabName)
244+
m.PutAttr(linux.NFTA_TABLE_USE, primitive.AllocateUint32(uint32(tab.ChainCount())))
245+
m.PutAttr(linux.NFTA_TABLE_HANDLE, primitive.AllocateUint64(tab.GetHandle()))
246+
m.PutAttr(linux.NFTA_TABLE_FLAGS, primitive.AllocateUint8(userFlags))
247+
248+
if tab.HasOwner() {
249+
m.PutAttr(linux.NFTA_TABLE_OWNER, primitive.AllocateUint32(tab.GetOwner()))
250+
}
251+
252+
if tab.HasUserData() {
253+
m.PutAttr(linux.NFTA_TABLE_USERDATA, primitive.AsByteSlice(tab.GetUserData()))
254+
}
255+
179256
return nil
180257
}
181258

259+
// deleteTable deletes a table for the given family.
260+
func (p *Protocol) deleteTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, hdr linux.NetlinkMessageHeader, msgType linux.NfTableMsgType, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
261+
if family == stack.Unspec || (!hasAttr(linux.NFTA_TABLE_NAME, attrs) && !hasAttr(linux.NFTA_TABLE_HANDLE, attrs)) {
262+
nft.Flush(attrs, family, uint32(ms.PortID))
263+
return nil
264+
}
265+
266+
var tab *nftables.Table
267+
var err *syserr.AnnotatedError
268+
if tabHandleBytes, ok := attrs[linux.NFTA_TABLE_HANDLE]; ok {
269+
tabHandle, ok := tabHandleBytes.Uint64()
270+
if !ok {
271+
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Table handle attribute is malformed or not found"))
272+
}
273+
274+
tab, err = nft.GetTableByHandle(family, uint64(tabHandle), uint32(ms.PortID))
275+
} else {
276+
tabNameBytes, ok := attrs[linux.NFTA_TABLE_NAME]
277+
if !ok {
278+
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Table name attribute is malformed or not found"))
279+
}
280+
tab, err = nft.GetTable(family, tabNameBytes.String(), uint32(ms.PortID))
281+
}
282+
283+
if err != nil {
284+
// Ignore ENOENT if DESTROY_TABLE is set
285+
if err.GetError() == syserr.ErrNoFileOrDir && msgType == linux.NFT_MSG_DESTROYTABLE {
286+
return nil
287+
}
288+
return err
289+
}
290+
291+
// Don't delete the table if it is not empty and NLM_F_NONREC is set.
292+
if hdr.Flags&linux.NLM_F_NONREC == linux.NLM_F_NONREC && tab.ChainCount() > 0 {
293+
return syserr.NewAnnotatedError(syserr.ErrBusy, fmt.Sprintf("Nftables: Table with family: %d and name: %s already exists", int(family), tab.GetName()))
294+
}
295+
296+
_, err = nft.DeleteTable(family, tab.GetName())
297+
return err
298+
}
299+
300+
// netLinkMessagePayloadSize returns the size of the netlink message payload.
182301
func netLinkMessagePayloadSize(h *linux.NetlinkMessageHeader) int {
183302
return int(h.Length) - linux.NetlinkMessageHeaderSize
184303
}
185304

305+
// hasAttr returns whether the given attribute key is present in the attribute map.
306+
func hasAttr(attrName uint16, attrs map[uint16]nlmsg.BytesView) bool {
307+
_, ok := attrs[attrName]
308+
return ok
309+
}
310+
186311
// init registers the NETLINK_NETFILTER provider.
187312
func init() {
188313
netlink.RegisterProvider(linux.NETLINK_NETFILTER, NewProtocol)

pkg/sentry/socket/netlink/nlmsg/BUILD

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ go_library(
1010
srcs = [
1111
"message.go",
1212
],
13-
visibility = ["//pkg/sentry:internal"],
13+
visibility = [
14+
"//pkg/sentry:internal",
15+
"//pkg/tcpip/nftables:__subpackages__",
16+
],
1417
deps = [
1518
"//pkg/abi/linux",
1619
"//pkg/bits",
1720
"//pkg/hostarch",
21+
"//pkg/log",
1822
"//pkg/marshal",
1923
"//pkg/marshal/primitive",
2024
],

pkg/sentry/socket/netlink/nlmsg/message.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"gvisor.dev/gvisor/pkg/abi/linux"
2323
"gvisor.dev/gvisor/pkg/bits"
2424
"gvisor.dev/gvisor/pkg/hostarch"
25+
"gvisor.dev/gvisor/pkg/log"
2526
"gvisor.dev/gvisor/pkg/marshal"
2627
"gvisor.dev/gvisor/pkg/marshal/primitive"
2728
)
@@ -252,17 +253,20 @@ func (v AttrsView) ParseFirst() (hdr linux.NetlinkAttrHeader, value []byte, rest
252253

253254
hdrBytes, ok := b.Extract(linux.NetlinkAttrHeaderSize)
254255
if !ok {
256+
log.Debugf("Failed to parse netlink attributes at header stage")
255257
return
256258
}
257259
hdr.UnmarshalUnsafe(hdrBytes)
258260

259261
value, ok = b.Extract(int(hdr.Length) - linux.NetlinkAttrHeaderSize)
260262
if !ok {
263+
log.Debugf("Failed to parse %d bytes after %d header bytes", int(hdr.Length)-linux.NetlinkAttrHeaderSize, linux.NetlinkAttrHeaderSize)
261264
return
262265
}
263266

264267
_, ok = b.Extract(alignPad(int(hdr.Length), linux.NLA_ALIGNTO))
265268
if !ok {
269+
log.Debugf("Failed to parse netlink attributes at aligning stage")
266270
return
267271
}
268272

@@ -323,6 +327,17 @@ func (v *BytesView) Uint32() (uint32, bool) {
323327
return uint32(val), true
324328
}
325329

330+
// Uint64 converts the raw attribute value to uint64.
331+
func (v *BytesView) Uint64() (uint64, bool) {
332+
attr := []byte(*v)
333+
val := primitive.Uint64(0)
334+
if len(attr) != val.SizeBytes() {
335+
return 0, false
336+
}
337+
val.UnmarshalBytes(attr)
338+
return uint64(val), true
339+
}
340+
326341
// Int32 converts the raw attribute value to int32.
327342
func (v *BytesView) Int32() (int32, bool) {
328343
attr := []byte(*v)

pkg/tcpip/nftables/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ go_library(
3131
"//pkg/abi/linux",
3232
"//pkg/atomicbitops",
3333
"//pkg/rand",
34+
"//pkg/sentry/socket/netlink/nlmsg",
3435
"//pkg/syserr",
3536
"//pkg/tcpip",
3637
"//pkg/tcpip/checksum",

0 commit comments

Comments
 (0)