@@ -21,6 +21,7 @@ import (
21
21
"gvisor.dev/gvisor/pkg/abi/linux"
22
22
"gvisor.dev/gvisor/pkg/context"
23
23
"gvisor.dev/gvisor/pkg/log"
24
+ "gvisor.dev/gvisor/pkg/marshal/primitive"
24
25
"gvisor.dev/gvisor/pkg/sentry/inet"
25
26
"gvisor.dev/gvisor/pkg/sentry/kernel"
26
27
"gvisor.dev/gvisor/pkg/sentry/socket/netlink"
@@ -89,11 +90,19 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
89
90
}
90
91
91
92
// Nftables functions error check the address family value.
92
- family := stack . AddressFamily (nfGenMsg .Family )
93
+ family , err := nftables . AFtoNetlinkAF (nfGenMsg .Family )
93
94
// TODO: b/421437663 - Match the message type and call the appropriate Nftables function.
94
95
switch msgType {
95
96
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 {
97
106
log .Debugf ("Nftables new table error: %s" , err )
98
107
return err .GetError ()
99
108
}
@@ -104,49 +113,106 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
104
113
return err .GetError ()
105
114
}
106
115
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
107
122
default :
108
123
log .Debugf ("Unsupported message type: %d" , msgType )
109
124
return syserr .ErrNotSupported
110
125
}
111
126
}
112
127
113
128
// 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 {
115
130
// TODO: b/421437663 - Handle the case where the table name is set to empty string.
116
131
// The table name is required.
117
132
tabNameBytes , ok := attrs [linux .NFTA_TABLE_NAME ]
118
133
if ! ok {
119
134
return syserr .NewAnnotatedError (syserr .ErrInvalidArgument , fmt .Sprintf ("Nftables: Table name attribute is malformed or not found" ))
120
135
}
121
136
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 ))
129
138
if err != nil && err .GetError () != syserr .ErrNoFileOrDir {
130
139
return err
131
140
}
132
141
133
142
// If a table already exists, only update its dormant flags if NLM_F_EXCL and NLM_F_REPLACE
134
143
// are not set. From net/netfilter/nf_tables_api.c:nf_tables_newtable:nf_tables_updtable
135
144
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 ()))
138
147
}
139
148
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 ()))
142
151
}
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 {
146
183
return err
147
184
}
148
185
}
149
186
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
150
216
tab .SetDormant (dormant )
151
217
return nil
152
218
}
@@ -159,12 +225,16 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
159
225
return syserr .NewAnnotatedError (syserr .ErrInvalidArgument , fmt .Sprintf ("Nftables: Table name attribute is malformed or not found" ))
160
226
}
161
227
162
- tab , err := nft .GetTable (family , tabNameBytes .String ())
228
+ tab , err := nft .GetTable (family , tabNameBytes .String (), uint32 ( ms . PortID ) )
163
229
if err != nil {
164
230
return err
165
231
}
166
232
167
233
tabName := tab .GetName ()
234
+ userFlags , err := tab .GetLinuxUserFlagSet ()
235
+ if err != nil {
236
+ return err
237
+ }
168
238
m := ms .AddMessage (linux.NetlinkMessageHeader {
169
239
Type : uint16 (linux .NFNL_SUBSYS_NFTABLES )<< 8 | uint16 (linux .NFT_MSG_GETTABLE ),
170
240
})
@@ -176,13 +246,73 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
176
246
ResourceID : uint16 (0 ),
177
247
})
178
248
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
+
179
261
return nil
180
262
}
181
263
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.
182
306
func netLinkMessagePayloadSize (h * linux.NetlinkMessageHeader ) int {
183
307
return int (h .Length ) - linux .NetlinkMessageHeaderSize
184
308
}
185
309
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
+
186
316
// init registers the NETLINK_NETFILTER provider.
187
317
func init () {
188
318
netlink .RegisterProvider (linux .NETLINK_NETFILTER , NewProtocol )
0 commit comments