@@ -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"
@@ -88,12 +89,11 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
88
89
return syserr .ErrInvalidArgument
89
90
}
90
91
91
- // Nftables functions error check the address family value.
92
- family := stack .AddressFamily (nfGenMsg .Family )
92
+ family := nftables .AFtoNetlinkAF (nfGenMsg .Family )
93
93
// TODO: b/421437663 - Match the message type and call the appropriate Nftables function.
94
94
switch msgType {
95
95
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 {
97
97
log .Debugf ("Nftables new table error: %s" , err )
98
98
return err .GetError ()
99
99
}
@@ -104,49 +104,110 @@ func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *n
104
104
return err .GetError ()
105
105
}
106
106
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
107
113
default :
108
114
log .Debugf ("Unsupported message type: %d" , msgType )
109
115
return syserr .ErrNotSupported
110
116
}
111
117
}
112
118
113
119
// 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
+
115
125
// TODO: b/421437663 - Handle the case where the table name is set to empty string.
116
126
// The table name is required.
117
127
tabNameBytes , ok := attrs [linux .NFTA_TABLE_NAME ]
118
128
if ! ok {
119
129
return syserr .NewAnnotatedError (syserr .ErrInvalidArgument , fmt .Sprintf ("Nftables: Table name attribute is malformed or not found" ))
120
130
}
121
131
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 ))
129
133
if err != nil && err .GetError () != syserr .ErrNoFileOrDir {
130
134
return err
131
135
}
132
136
133
137
// If a table already exists, only update its dormant flags if NLM_F_EXCL and NLM_F_REPLACE
134
138
// are not set. From net/netfilter/nf_tables_api.c:nf_tables_newtable:nf_tables_updtable
135
139
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 ()))
138
142
}
139
143
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 ()))
142
146
}
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 {
146
206
return err
147
207
}
148
208
}
149
209
210
+ dormant := (attrFlags & uint32 (linux .NFT_TABLE_F_DORMANT )) != 0
150
211
tab .SetDormant (dormant )
151
212
return nil
152
213
}
@@ -159,12 +220,16 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
159
220
return syserr .NewAnnotatedError (syserr .ErrInvalidArgument , fmt .Sprintf ("Nftables: Table name attribute is malformed or not found" ))
160
221
}
161
222
162
- tab , err := nft .GetTable (family , tabNameBytes .String ())
223
+ tab , err := nft .GetTable (family , tabNameBytes .String (), uint32 ( ms . PortID ) )
163
224
if err != nil {
164
225
return err
165
226
}
166
227
167
228
tabName := tab .GetName ()
229
+ userFlags , err := tab .GetLinuxUserFlagSet ()
230
+ if err != nil {
231
+ return err
232
+ }
168
233
m := ms .AddMessage (linux.NetlinkMessageHeader {
169
234
Type : uint16 (linux .NFNL_SUBSYS_NFTABLES )<< 8 | uint16 (linux .NFT_MSG_GETTABLE ),
170
235
})
@@ -176,13 +241,73 @@ func (p *Protocol) getTable(nft *nftables.NFTables, attrs map[uint16]nlmsg.Bytes
176
241
ResourceID : uint16 (0 ),
177
242
})
178
243
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
+
179
256
return nil
180
257
}
181
258
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.
182
301
func netLinkMessagePayloadSize (h * linux.NetlinkMessageHeader ) int {
183
302
return int (h .Length ) - linux .NetlinkMessageHeaderSize
184
303
}
185
304
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
+
186
311
// init registers the NETLINK_NETFILTER provider.
187
312
func init () {
188
313
netlink .RegisterProvider (linux .NETLINK_NETFILTER , NewProtocol )
0 commit comments