Skip to content

Commit cbde1c3

Browse files
kerumetogvisor-bot
authored andcommitted
Drop unnecessary size checks and corrections for Netlink attributes.
Some Netlink attributes have a fixed size. The map attempted to make adding attributes safer and easier by having default sizes for certain attributes. However, attributes are identified via their enums, which often overlap for different types of requests, making these default sizes not hold true for all attributes that map to it. PiperOrigin-RevId: 778632075
1 parent 8f111be commit cbde1c3

File tree

15 files changed

+1346
-317
lines changed

15 files changed

+1346
-317
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: 148 additions & 18 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"
@@ -89,11 +90,19 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
8990
}
9091

9192
// Nftables functions error check the address family value.
92-
family := stack.AddressFamily(nfGenMsg.Family)
93+
family, err := nftables.AFtoNetlinkAF(nfGenMsg.Family)
9394
// TODO: b/421437663 - Match the message type and call the appropriate Nftables function.
9495
switch msgType {
9596
case linux.NFT_MSG_NEWTABLE:
96-
if err := p.newTable(nft, attrs, family, hdr.Flags); err != nil {
97+
// We only check the error value in the case of NFT_MSG_NEWTABLE as linux returns
98+
// an EOPNOTSUPP error only in that case. Otherwise the other operations will return
99+
// errors specific to their function.
100+
if err != nil {
101+
log.Debugf("Nftables: Unsupported address family: %d", int(nfGenMsg.Family))
102+
return err
103+
}
104+
105+
if err := p.newTable(nft, attrs, family, hdr.Flags, ms); err != nil {
97106
log.Debugf("Nftables new table error: %s", err)
98107
return err.GetError()
99108
}
@@ -104,49 +113,106 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
104113
return err.GetError()
105114
}
106115
return nil
116+
case linux.NFT_MSG_DELTABLE, linux.NFT_MSG_DESTROYTABLE:
117+
if err := p.deleteTable(nft, attrs, family, hdr, msgType, ms); err != nil {
118+
log.Debugf("Nftables delete table error: %s", err)
119+
return err.GetError()
120+
}
121+
return nil
107122
default:
108123
log.Debugf("Unsupported message type: %d", msgType)
109124
return syserr.ErrNotSupported
110125
}
111126
}
112127

113128
// 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 {
129+
func (p *Protocol) newTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, flags uint16, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
115130
// TODO: b/421437663 - Handle the case where the table name is set to empty string.
116131
// The table name is required.
117132
tabNameBytes, ok := attrs[linux.NFTA_TABLE_NAME]
118133
if !ok {
119134
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Table name attribute is malformed or not found"))
120135
}
121136

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())
137+
tab, err := nft.GetTable(family, tabNameBytes.String(), uint32(ms.PortID))
129138
if err != nil && err.GetError() != syserr.ErrNoFileOrDir {
130139
return err
131140
}
132141

133142
// If a table already exists, only update its dormant flags if NLM_F_EXCL and NLM_F_REPLACE
134143
// are not set. From net/netfilter/nf_tables_api.c:nf_tables_newtable:nf_tables_updtable
135144
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()))
145+
if flags&linux.NLM_F_EXCL != 0 {
146+
return syserr.NewAnnotatedError(syserr.ErrExists, fmt.Sprintf("Nftables: Table with name: %s already exists", tab.GetName()))
138147
}
139148

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()))
149+
if flags&linux.NLM_F_REPLACE != 0 {
150+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table with name: %s already exists and NLM_F_REPLACE is not supported", tab.GetName()))
142151
}
143-
} else {
144-
tab, err = nft.CreateTable(family, tabNameBytes.String())
145-
if err != nil {
152+
153+
return p.updateTable(nft, tab, attrs, family, ms)
154+
}
155+
156+
// TODO: b/421437663 - Support additional user-specified table flags.
157+
var attrFlags uint32 = 0
158+
if uflags, ok := attrs[linux.NFTA_TABLE_FLAGS]; ok {
159+
attrFlags, _ = uflags.Uint32()
160+
// Flags sent through the NFTA_TABLE_FLAGS attribute are of type uint32
161+
// but should only have user flags set. This check needs to be done before table creation.
162+
if attrFlags & ^uint32(linux.NFT_TABLE_F_MASK) != 0 {
163+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table flags set are not supported"))
164+
}
165+
}
166+
167+
tab, err = nft.CreateTable(family, tabNameBytes.String())
168+
if err != nil {
169+
return err
170+
}
171+
172+
if udata, ok := attrs[linux.NFTA_TABLE_USERDATA]; ok {
173+
tab.SetUserData(udata)
174+
}
175+
176+
// Flags should only be assigned after we have successfully created the table.
177+
dormant := (attrFlags & uint32(linux.NFT_TABLE_F_DORMANT)) != 0
178+
tab.SetDormant(dormant)
179+
180+
owner := (attrFlags & uint32(linux.NFT_TABLE_F_OWNER)) != 0
181+
if owner {
182+
if err := tab.SetOwner(uint32(ms.PortID)); err != nil {
146183
return err
147184
}
148185
}
149186

187+
return nil
188+
}
189+
190+
// updateTable updates an existing table.
191+
func (p *Protocol) updateTable(nft *nftables.NFTables, tab *nftables.Table, attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
192+
var attrFlags uint32
193+
if uflags, ok := attrs[linux.NFTA_TABLE_FLAGS]; ok {
194+
attrFlags, _ = uflags.Uint32()
195+
// This check needs to be done before table update.
196+
if attrFlags & ^uint32(linux.NFT_TABLE_F_MASK) > 0 {
197+
return syserr.NewAnnotatedError(syserr.ErrNotSupported, fmt.Sprintf("Nftables: Table flags set are not supported"))
198+
}
199+
}
200+
201+
// When updating the table, if the table has an owner but the owner flag isn't set,
202+
// the table should not be updated.
203+
// From net/netfilter/nf_tables_api.c:nf_tables_updtable.
204+
if tab.HasOwner() && (attrFlags&uint32(linux.NFT_TABLE_F_OWNER)) == 0 {
205+
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()))
206+
}
207+
208+
// The owner is only updated if the table has no previous owner.
209+
if !tab.HasOwner() && attrFlags&uint32(linux.NFT_TABLE_F_OWNER) != 0 {
210+
if err := tab.SetOwner(uint32(ms.PortID)); err != nil {
211+
return err
212+
}
213+
}
214+
215+
dormant := (attrFlags & uint32(linux.NFT_TABLE_F_DORMANT)) != 0
150216
tab.SetDormant(dormant)
151217
return nil
152218
}
@@ -159,12 +225,16 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
159225
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Table name attribute is malformed or not found"))
160226
}
161227

162-
tab, err := nft.GetTable(family, tabNameBytes.String())
228+
tab, err := nft.GetTable(family, tabNameBytes.String(), uint32(ms.PortID))
163229
if err != nil {
164230
return err
165231
}
166232

167233
tabName := tab.GetName()
234+
userFlags, err := tab.GetLinuxUserFlagSet()
235+
if err != nil {
236+
return err
237+
}
168238
m := ms.AddMessage(linux.NetlinkMessageHeader{
169239
Type: uint16(linux.NFNL_SUBSYS_NFTABLES)<<8 | uint16(linux.NFT_MSG_GETTABLE),
170240
})
@@ -176,13 +246,73 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
176246
ResourceID: uint16(0),
177247
})
178248
m.PutAttrString(linux.NFTA_TABLE_NAME, tabName)
249+
m.PutAttr(linux.NFTA_TABLE_USE, primitive.AllocateUint32(uint32(tab.ChainCount())))
250+
m.PutAttr(linux.NFTA_TABLE_HANDLE, primitive.AllocateUint64(tab.GetHandle()))
251+
m.PutAttr(linux.NFTA_TABLE_FLAGS, primitive.AllocateUint8(userFlags))
252+
253+
if tab.HasOwner() {
254+
m.PutAttr(linux.NFTA_TABLE_OWNER, primitive.AllocateUint32(tab.GetOwner()))
255+
}
256+
257+
if tab.HasUserData() {
258+
m.PutAttr(linux.NFTA_TABLE_USERDATA, primitive.AsByteSlice(tab.GetUserData()))
259+
}
260+
179261
return nil
180262
}
181263

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

310+
// hasAttr returns whether the given attribute key is present in the attribute map.
311+
func hasAttr(attrName uint16, attrs map[uint16]nlmsg.BytesView) bool {
312+
_, ok := attrs[attrName]
313+
return ok
314+
}
315+
186316
// init registers the NETLINK_NETFILTER provider.
187317
func init() {
188318
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)