Skip to content

Commit 708c511

Browse files
parth-opensrcgvisor-bot
authored andcommitted
nftables: Added support for cmp op init.
PiperOrigin-RevId: 841668653
1 parent 9a46748 commit 708c511

File tree

8 files changed

+365
-134
lines changed

8 files changed

+365
-134
lines changed

pkg/abi/linux/nf_tables.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,17 @@ const (
364364
NFT_CMP_GTE // greater than or equal to
365365
)
366366

367+
// Nf table cmp expression netlink attributes.
368+
// These correspond to enum values in include/uapi/linux/netfilter/nf_tables.h.
369+
const (
370+
NFTA_CMP_UNSPEC uint16 = iota
371+
NFTA_CMP_SREG
372+
NFTA_CMP_OP
373+
NFTA_CMP_DATA
374+
__NFTA_CMP_MAX
375+
NFTA_CMP_MAX = __NFTA_CMP_MAX - 1
376+
)
377+
367378
// Nf table range operators.
368379
// Used by the nft range operation to compare values in registers.
369380
// These correspond to enum values in include/uapi/linux/netfilter/nf_tables.h.
@@ -479,6 +490,17 @@ const (
479490
NFT_META_BRI_BROUTE // Packet br_netfilter_broute bit
480491
)
481492

493+
// Nf table meta expression netlink attributes
494+
// These correspond to enum values in include/uapi/linux/netfilter/nf_tables.h.
495+
const (
496+
NFTA_META_UNSPEC = iota
497+
NFTA_META_DREG
498+
NFTA_META_KEY
499+
NFTA_META_SREG
500+
__NFTA_META_MAX
501+
NFTA_META_MAX = __NFTA_META_MAX - 1
502+
)
503+
482504
// Nftables Generation Attributes
483505
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
484506
const (

pkg/tcpip/nftables/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ go_library(
2222
"nft_counter.go",
2323
"nft_immediate.go",
2424
"nft_last.go",
25+
"nft_meta.go",
2526
"nft_metaload.go",
2627
"nft_metaset.go",
2728
"nft_payload.go",

pkg/tcpip/nftables/nft_comparison.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020

2121
"gvisor.dev/gvisor/pkg/abi/linux"
22+
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
2223
"gvisor.dev/gvisor/pkg/syserr"
2324
"gvisor.dev/gvisor/pkg/tcpip/stack"
2425
)
@@ -115,3 +116,37 @@ func (op comparison) evaluate(regs *registerSet, pkt *stack.PacketBuffer, rule *
115116
regs.verdict = stack.NFVerdict{Code: VC(linux.NFT_BREAK)}
116117
}
117118
}
119+
120+
var cmpAttrPolicy = []NlaPolicy{
121+
linux.NFTA_CMP_SREG: NlaPolicy{nlaType: linux.NLA_U32},
122+
linux.NFTA_CMP_OP: NlaPolicy{nlaType: linux.NLA_U32},
123+
linux.NFTA_CMP_DATA: NlaPolicy{nlaType: linux.NLA_NESTED},
124+
}
125+
126+
func initComparison(tab *Table, exprInfo ExprInfo) (*comparison, *syserr.AnnotatedError) {
127+
attrs, ok := NfParseWithPolicy(exprInfo.ExprData, cmpAttrPolicy)
128+
if !ok {
129+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse comparison expression data")
130+
}
131+
sreg, ok := AttrNetToHostU32(linux.NFTA_CMP_SREG, attrs)
132+
if !ok {
133+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse NFTA_CMP_SREG attribute")
134+
}
135+
op, ok := AttrNetToHostU32(linux.NFTA_CMP_OP, attrs)
136+
if !ok {
137+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse NFTA_CMP_OP attribute")
138+
}
139+
dataAttrBytes, ok := attrs[linux.NFTA_CMP_DATA]
140+
if !ok {
141+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: NFTA_CMP_DATA attribute is not found")
142+
}
143+
dataAttrs, ok := NfParse(nlmsg.AttrsView(dataAttrBytes))
144+
if !ok {
145+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse NFTA_CMP_DATA attribute")
146+
}
147+
valueBytes, ok := dataAttrs[linux.NFTA_DATA_VALUE]
148+
if !ok {
149+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: NFTA_DATA_VALUE attribute is not found")
150+
}
151+
return newComparison(uint8(sreg), int(op), valueBytes)
152+
}

pkg/tcpip/nftables/nft_meta.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2025 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package nftables
16+
17+
import (
18+
"fmt"
19+
20+
"gvisor.dev/gvisor/pkg/abi/linux"
21+
"gvisor.dev/gvisor/pkg/syserr"
22+
)
23+
24+
// metaKey is the key that determines the specific meta data to retrieve.
25+
// Note: corresponds to enum nft_meta_keys from
26+
// include/uapi/linux/netfilter/nf_tables.h and uses the same constants.
27+
type metaKey int
28+
29+
// metaKeyStrings is a map of meta key to its string representation.
30+
var metaKeyStrings = map[metaKey]string{
31+
linux.NFT_META_LEN: "NFT_META_LEN",
32+
linux.NFT_META_PROTOCOL: "NFT_META_PROTOCOL",
33+
linux.NFT_META_PRIORITY: "NFT_META_PRIORITY",
34+
linux.NFT_META_MARK: "NFT_META_MARK",
35+
linux.NFT_META_IIF: "NFT_META_IIF",
36+
linux.NFT_META_OIF: "NFT_META_OIF",
37+
linux.NFT_META_IIFNAME: "NFT_META_IIFNAME",
38+
linux.NFT_META_OIFNAME: "NFT_META_OIFNAME",
39+
linux.NFT_META_IIFTYPE: "NFT_META_IIFTYPE",
40+
linux.NFT_META_OIFTYPE: "NFT_META_OIFTYPE",
41+
linux.NFT_META_SKUID: "NFT_META_SKUID",
42+
linux.NFT_META_SKGID: "NFT_META_SKGID",
43+
linux.NFT_META_NFTRACE: "NFT_META_NFTRACE",
44+
linux.NFT_META_RTCLASSID: "NFT_META_RTCLASSID",
45+
linux.NFT_META_SECMARK: "NFT_META_SECMARK",
46+
linux.NFT_META_NFPROTO: "NFT_META_NFPROTO",
47+
linux.NFT_META_L4PROTO: "NFT_META_L4PROTO",
48+
linux.NFT_META_BRI_IIFNAME: "NFT_META_BRI_IIFNAME",
49+
linux.NFT_META_BRI_OIFNAME: "NFT_META_BRI_OIFNAME",
50+
linux.NFT_META_PKTTYPE: "NFT_META_PKTTYPE",
51+
linux.NFT_META_CPU: "NFT_META_CPU",
52+
linux.NFT_META_IIFGROUP: "NFT_META_IIFGROUP",
53+
linux.NFT_META_OIFGROUP: "NFT_META_OIFGROUP",
54+
linux.NFT_META_CGROUP: "NFT_META_CGROUP",
55+
linux.NFT_META_PRANDOM: "NFT_META_PRANDOM",
56+
linux.NFT_META_SECPATH: "NFT_META_SECPATH",
57+
linux.NFT_META_IIFKIND: "NFT_META_IIFKIND",
58+
linux.NFT_META_OIFKIND: "NFT_META_OIFKIND",
59+
linux.NFT_META_BRI_IIFPVID: "NFT_META_BRI_IIFPVID",
60+
linux.NFT_META_BRI_IIFVPROTO: "NFT_META_BRI_IIFVPROTO",
61+
linux.NFT_META_TIME_NS: "NFT_META_TIME_NS",
62+
linux.NFT_META_TIME_DAY: "NFT_META_TIME_DAY",
63+
linux.NFT_META_TIME_HOUR: "NFT_META_TIME_HOUR",
64+
linux.NFT_META_SDIF: "NFT_META_SDIF",
65+
linux.NFT_META_SDIFNAME: "NFT_META_SDIFNAME",
66+
linux.NFT_META_BRI_BROUTE: "NFT_META_BRI_BROUTE",
67+
}
68+
69+
// String for metaKey returns the string representation of the meta key. This
70+
// supports strings for supported and unsupported meta keys.
71+
func (key metaKey) String() string {
72+
if keyStr, ok := metaKeyStrings[key]; ok {
73+
return keyStr
74+
}
75+
panic(fmt.Sprintf("invalid meta key: %d", int(key)))
76+
}
77+
78+
// metaDataLengths holds the length in bytes for each supported meta key.
79+
var metaDataLengths = map[metaKey]int{
80+
linux.NFT_META_LEN: 4,
81+
linux.NFT_META_PROTOCOL: 2,
82+
linux.NFT_META_NFPROTO: 1,
83+
linux.NFT_META_L4PROTO: 1,
84+
linux.NFT_META_SKUID: 4,
85+
linux.NFT_META_SKGID: 4,
86+
linux.NFT_META_RTCLASSID: 4,
87+
linux.NFT_META_PKTTYPE: 1,
88+
linux.NFT_META_PRANDOM: 4,
89+
linux.NFT_META_TIME_NS: 8,
90+
linux.NFT_META_TIME_DAY: 1,
91+
linux.NFT_META_TIME_HOUR: 4,
92+
}
93+
94+
// validateMetaKey ensures the meta key is valid.
95+
func validateMetaKey(key metaKey) *syserr.AnnotatedError {
96+
switch key {
97+
case linux.NFT_META_LEN, linux.NFT_META_PROTOCOL, linux.NFT_META_NFPROTO,
98+
linux.NFT_META_L4PROTO, linux.NFT_META_SKUID, linux.NFT_META_SKGID,
99+
linux.NFT_META_RTCLASSID, linux.NFT_META_PKTTYPE, linux.NFT_META_PRANDOM,
100+
linux.NFT_META_TIME_NS, linux.NFT_META_TIME_DAY, linux.NFT_META_TIME_HOUR:
101+
102+
return nil
103+
default:
104+
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("meta key %v is not supported", key))
105+
}
106+
}
107+
108+
var metaAttrPolicy = []NlaPolicy{
109+
linux.NFTA_META_DREG: NlaPolicy{nlaType: linux.NLA_U32},
110+
linux.NFTA_META_KEY: NlaPolicy{nlaType: linux.NLA_BE32, validator: AttrMaxValidator[uint32](255)},
111+
linux.NFTA_META_SREG: NlaPolicy{nlaType: linux.NLA_U32},
112+
}
113+
114+
func initMeta(tab *Table, exprInfo ExprInfo) (operation, *syserr.AnnotatedError) {
115+
attrs, ok := NfParseWithPolicy(exprInfo.ExprData, metaAttrPolicy)
116+
if !ok {
117+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse meta expression data")
118+
}
119+
if _, ok := attrs[linux.NFTA_META_SREG]; ok {
120+
if _, ok := attrs[linux.NFTA_META_DREG]; ok {
121+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Only one of NFTA_PAYLOAD_SREG and NFTA_PAYLOAD_DREG should be set")
122+
}
123+
return initMetaSet(attrs)
124+
}
125+
if _, ok := attrs[linux.NFTA_META_DREG]; ok {
126+
return initMetaLoad(attrs)
127+
}
128+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: NFTA_PAYLOAD_SREG or NFTA_PAYLOAD_DREG attribute is not found")
129+
}

pkg/tcpip/nftables/nft_metaload.go

Lines changed: 17 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020

2121
"gvisor.dev/gvisor/pkg/abi/linux"
22+
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
2223
"gvisor.dev/gvisor/pkg/syserr"
2324
"gvisor.dev/gvisor/pkg/tcpip/header"
2425
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -41,90 +42,6 @@ type metaLoad struct {
4142
// endian for the latter.
4243
}
4344

44-
// metaKey is the key that determines the specific meta data to retrieve.
45-
// Note: corresponds to enum nft_meta_keys from
46-
// include/uapi/linux/netfilter/nf_tables.h and uses the same constants.
47-
type metaKey int
48-
49-
// metaKeyStrings is a map of meta key to its string representation.
50-
var metaKeyStrings = map[metaKey]string{
51-
linux.NFT_META_LEN: "NFT_META_LEN",
52-
linux.NFT_META_PROTOCOL: "NFT_META_PROTOCOL",
53-
linux.NFT_META_PRIORITY: "NFT_META_PRIORITY",
54-
linux.NFT_META_MARK: "NFT_META_MARK",
55-
linux.NFT_META_IIF: "NFT_META_IIF",
56-
linux.NFT_META_OIF: "NFT_META_OIF",
57-
linux.NFT_META_IIFNAME: "NFT_META_IIFNAME",
58-
linux.NFT_META_OIFNAME: "NFT_META_OIFNAME",
59-
linux.NFT_META_IIFTYPE: "NFT_META_IIFTYPE",
60-
linux.NFT_META_OIFTYPE: "NFT_META_OIFTYPE",
61-
linux.NFT_META_SKUID: "NFT_META_SKUID",
62-
linux.NFT_META_SKGID: "NFT_META_SKGID",
63-
linux.NFT_META_NFTRACE: "NFT_META_NFTRACE",
64-
linux.NFT_META_RTCLASSID: "NFT_META_RTCLASSID",
65-
linux.NFT_META_SECMARK: "NFT_META_SECMARK",
66-
linux.NFT_META_NFPROTO: "NFT_META_NFPROTO",
67-
linux.NFT_META_L4PROTO: "NFT_META_L4PROTO",
68-
linux.NFT_META_BRI_IIFNAME: "NFT_META_BRI_IIFNAME",
69-
linux.NFT_META_BRI_OIFNAME: "NFT_META_BRI_OIFNAME",
70-
linux.NFT_META_PKTTYPE: "NFT_META_PKTTYPE",
71-
linux.NFT_META_CPU: "NFT_META_CPU",
72-
linux.NFT_META_IIFGROUP: "NFT_META_IIFGROUP",
73-
linux.NFT_META_OIFGROUP: "NFT_META_OIFGROUP",
74-
linux.NFT_META_CGROUP: "NFT_META_CGROUP",
75-
linux.NFT_META_PRANDOM: "NFT_META_PRANDOM",
76-
linux.NFT_META_SECPATH: "NFT_META_SECPATH",
77-
linux.NFT_META_IIFKIND: "NFT_META_IIFKIND",
78-
linux.NFT_META_OIFKIND: "NFT_META_OIFKIND",
79-
linux.NFT_META_BRI_IIFPVID: "NFT_META_BRI_IIFPVID",
80-
linux.NFT_META_BRI_IIFVPROTO: "NFT_META_BRI_IIFVPROTO",
81-
linux.NFT_META_TIME_NS: "NFT_META_TIME_NS",
82-
linux.NFT_META_TIME_DAY: "NFT_META_TIME_DAY",
83-
linux.NFT_META_TIME_HOUR: "NFT_META_TIME_HOUR",
84-
linux.NFT_META_SDIF: "NFT_META_SDIF",
85-
linux.NFT_META_SDIFNAME: "NFT_META_SDIFNAME",
86-
linux.NFT_META_BRI_BROUTE: "NFT_META_BRI_BROUTE",
87-
}
88-
89-
// String for metaKey returns the string representation of the meta key. This
90-
// supports strings for supported and unsupported meta keys.
91-
func (key metaKey) String() string {
92-
if keyStr, ok := metaKeyStrings[key]; ok {
93-
return keyStr
94-
}
95-
panic(fmt.Sprintf("invalid meta key: %d", int(key)))
96-
}
97-
98-
// metaDataLengths holds the length in bytes for each supported meta key.
99-
var metaDataLengths = map[metaKey]int{
100-
linux.NFT_META_LEN: 4,
101-
linux.NFT_META_PROTOCOL: 2,
102-
linux.NFT_META_NFPROTO: 1,
103-
linux.NFT_META_L4PROTO: 1,
104-
linux.NFT_META_SKUID: 4,
105-
linux.NFT_META_SKGID: 4,
106-
linux.NFT_META_RTCLASSID: 4,
107-
linux.NFT_META_PKTTYPE: 1,
108-
linux.NFT_META_PRANDOM: 4,
109-
linux.NFT_META_TIME_NS: 8,
110-
linux.NFT_META_TIME_DAY: 1,
111-
linux.NFT_META_TIME_HOUR: 4,
112-
}
113-
114-
// validateMetaKey ensures the meta key is valid.
115-
func validateMetaKey(key metaKey) *syserr.AnnotatedError {
116-
switch key {
117-
case linux.NFT_META_LEN, linux.NFT_META_PROTOCOL, linux.NFT_META_NFPROTO,
118-
linux.NFT_META_L4PROTO, linux.NFT_META_SKUID, linux.NFT_META_SKGID,
119-
linux.NFT_META_RTCLASSID, linux.NFT_META_PKTTYPE, linux.NFT_META_PRANDOM,
120-
linux.NFT_META_TIME_NS, linux.NFT_META_TIME_DAY, linux.NFT_META_TIME_HOUR:
121-
122-
return nil
123-
default:
124-
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("meta key %v is not supported", key))
125-
}
126-
}
127-
12845
// newMetaLoad creates a new metaLoad operation.
12946
func newMetaLoad(key metaKey, dreg uint8) (*metaLoad, *syserr.AnnotatedError) {
13047
if isVerdictRegister(dreg) {
@@ -241,3 +158,19 @@ func (op metaLoad) evaluate(regs *registerSet, pkt *stack.PacketBuffer, rule *Ru
241158
// Copies target data into the destination register.
242159
copy(dst, target)
243160
}
161+
162+
func initMetaLoad(attrs map[uint16]nlmsg.BytesView) (*metaLoad, *syserr.AnnotatedError) {
163+
key, ok := AttrNetToHostU32(linux.NFTA_META_KEY, attrs)
164+
if !ok {
165+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse NFTA_META_KEY attribute")
166+
}
167+
reg, ok := AttrNetToHostU32(linux.NFTA_META_DREG, attrs)
168+
if !ok {
169+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse NFTA_META_DREG attribute")
170+
}
171+
dreg, err := nftMatchReg(reg)
172+
if err != nil {
173+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Invalid source register: %d", reg))
174+
}
175+
return newMetaLoad(metaKey(key), uint8(dreg))
176+
}

0 commit comments

Comments
 (0)