Skip to content

Commit 522dafc

Browse files
parth-opensrcgvisor-bot
authored andcommitted
nftables: Added support for meta set and get/load init.
Reference from net/netfilter/nft_meta.c PiperOrigin-RevId: 836743636
1 parent 9a46748 commit 522dafc

File tree

7 files changed

+277
-134
lines changed

7 files changed

+277
-134
lines changed

pkg/abi/linux/nf_tables.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,17 @@ const (
479479
NFT_META_BRI_BROUTE // Packet br_netfilter_broute bit
480480
)
481481

482+
// Nf table meta expression netlink attributes
483+
// These correspond to enum values in include/uapi/linux/netfilter/nf_tables.h.
484+
const (
485+
NFTA_META_UNSPEC = iota
486+
NFTA_META_DREG
487+
NFTA_META_KEY
488+
NFTA_META_SREG
489+
__NFTA_META_MAX
490+
NFTA_META_MAX = __NFTA_META_MAX - 1
491+
)
492+
482493
// Nftables Generation Attributes
483494
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
484495
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_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+
}

pkg/tcpip/nftables/nft_metaset.go

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"fmt"
1919

2020
"gvisor.dev/gvisor/pkg/abi/linux"
21+
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
2122
"gvisor.dev/gvisor/pkg/syserr"
2223
"gvisor.dev/gvisor/pkg/tcpip"
2324
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -49,6 +50,21 @@ func checkMetaKeySetCompatible(key metaKey) *syserr.AnnotatedError {
4950
}
5051
}
5152

53+
// evaluate for metaSet sets specific meta data to the value in the source
54+
// register.
55+
func (op metaSet) evaluate(regs *registerSet, pkt *stack.PacketBuffer, rule *Rule) {
56+
// Gets the data from the source register.
57+
src := getRegisterBuffer(regs, op.sreg)[:metaDataLengths[op.key]]
58+
59+
// Sets the meta data of the appropriate field.
60+
switch op.key {
61+
// Only Packet Type is supported for now.
62+
case linux.NFT_META_PKTTYPE:
63+
pkt.PktType = tcpip.PacketType(src[0])
64+
return
65+
}
66+
}
67+
5268
// newMetaSet creates a new metaSet operation.
5369
func newMetaSet(key metaKey, sreg uint8) (*metaSet, *syserr.AnnotatedError) {
5470
if isVerdictRegister(sreg) {
@@ -63,25 +79,21 @@ func newMetaSet(key metaKey, sreg uint8) (*metaSet, *syserr.AnnotatedError) {
6379
if metaDataLengths[key] > 4 && !is16ByteRegister(sreg) {
6480
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("meta load operation cannot use 4-byte register as destination for key %s", key))
6581
}
66-
6782
return &metaSet{key: key, sreg: sreg}, nil
6883
}
6984

70-
// evaluate for metaSet sets specific meta data to the value in the source
71-
// register.
72-
func (op metaSet) evaluate(regs *registerSet, pkt *stack.PacketBuffer, rule *Rule) {
73-
// Gets the data from the source register.
74-
src := getRegisterBuffer(regs, op.sreg)[:metaDataLengths[op.key]]
75-
76-
// Sets the meta data of the appropriate field.
77-
switch op.key {
78-
// Only Packet Type is supported for now.
79-
case linux.NFT_META_PKTTYPE:
80-
pkt.PktType = tcpip.PacketType(src[0])
81-
return
85+
func initMetaSet(attrs map[uint16]nlmsg.BytesView) (*metaSet, *syserr.AnnotatedError) {
86+
key, ok := AttrNetToHostU32(linux.NFTA_META_KEY, attrs)
87+
if !ok {
88+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse NFTA_META_KEY attribute")
8289
}
83-
84-
// Breaks if could not set the meta data.
85-
regs.verdict = stack.NFVerdict{Code: VC(linux.NFT_BREAK)}
86-
return
90+
reg, ok := AttrNetToHostU32(linux.NFTA_META_SREG, attrs)
91+
if !ok {
92+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Nftables: Failed to parse NFTA_META_SREG attribute")
93+
}
94+
sreg, err := nftMatchReg(reg)
95+
if err != nil {
96+
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, fmt.Sprintf("Nftables: Invalid source register: %d", reg))
97+
}
98+
return newMetaSet(metaKey(key), uint8(sreg))
8799
}

pkg/tcpip/nftables/nftables.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,11 @@ func (r *Rule) AddOpFromExprInfo(tab *Table, exprInfo ExprInfo) *syserr.Annotate
11101110
if op, err = initPayload(tab, exprInfo); err != nil {
11111111
return err
11121112
}
1113+
case "meta":
1114+
if op, err = initMeta(tab, exprInfo); err != nil {
1115+
return err
1116+
}
1117+
11131118
default:
11141119
return syserr.NewAnnotatedError(syserr.ErrNoFileOrDir, fmt.Sprintf("Nftables: Unknown expression type not found: %s", exprInfo.ExprName))
11151120
}

0 commit comments

Comments
 (0)