mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Nftables: Added support for set creation.
PiperOrigin-RevId: 936156719
This commit is contained in:
@@ -28,6 +28,7 @@ const (
|
||||
NFT_OBJ_MAXNAMELEN = NFT_NAME_MAXLEN
|
||||
NFT_USERDATA_MAXLEN = 256
|
||||
NFT_OSF_MAXGENRELEN = 16
|
||||
NFT_SET_EXPR_MAX = 2
|
||||
)
|
||||
|
||||
// 16-byte Registers that can be used to maintain state for rules.
|
||||
@@ -537,3 +538,125 @@ const (
|
||||
__NFTA_NAT_MAX
|
||||
NFTA_NAT_MAX = __NFTA_NAT_MAX - 1
|
||||
)
|
||||
|
||||
// NfTableSetFlags represents the netfilter set flags.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFT_SET_ANONYMOUS = uint16(0x1)
|
||||
NFT_SET_CONSTANT = uint16(0x2)
|
||||
NFT_SET_INTERVAL = uint16(0x4)
|
||||
NFT_SET_MAP = uint16(0x8)
|
||||
NFT_SET_TIMEOUT = uint16(0x10)
|
||||
NFT_SET_EVAL = uint16(0x20)
|
||||
NFT_SET_OBJECT = uint16(0x40)
|
||||
NFT_SET_CONCAT = uint16(0x80)
|
||||
NFT_SET_EXPR = uint16(0x100)
|
||||
)
|
||||
|
||||
// NfTableSetAttributes represents the netfilter set attributes.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFTA_SET_UNSPEC uint16 = iota
|
||||
NFTA_SET_TABLE
|
||||
NFTA_SET_NAME
|
||||
NFTA_SET_FLAGS
|
||||
NFTA_SET_KEY_TYPE
|
||||
NFTA_SET_KEY_LEN
|
||||
NFTA_SET_DATA_TYPE
|
||||
NFTA_SET_DATA_LEN
|
||||
NFTA_SET_POLICY
|
||||
NFTA_SET_DESC
|
||||
NFTA_SET_ID
|
||||
NFTA_SET_TIMEOUT
|
||||
NFTA_SET_GC_INTERVAL
|
||||
NFTA_SET_USERDATA
|
||||
NFTA_SET_PAD
|
||||
NFTA_SET_OBJ_TYPE
|
||||
NFTA_SET_HANDLE
|
||||
NFTA_SET_EXPR
|
||||
NFTA_SET_EXPRESSIONS
|
||||
__NFTA_SET_MAX
|
||||
NFTA_SET_MAX = __NFTA_SET_MAX - 1
|
||||
)
|
||||
|
||||
// NfTableSetPolicies represents the netfilter set policies.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFT_SET_POL_PERFORMANCE uint32 = iota // prefer high performance over low memory use
|
||||
NFT_SET_POL_MEMORY // prefer low memory use over high performance
|
||||
)
|
||||
|
||||
// NfTableSetDescAttributes represents the netfilter set description attributes.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFTA_SET_DESC_UNSPEC uint16 = iota
|
||||
NFTA_SET_DESC_SIZE
|
||||
NFTA_SET_DESC_CONCAT
|
||||
__NFTA_SET_DESC_MAX
|
||||
NFTA_SET_DESC_MAX = __NFTA_SET_DESC_MAX - 1
|
||||
)
|
||||
|
||||
// NfTableSetFieldAttributes represents the netfilter set field attributes.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFTA_SET_FIELD_UNSPEC uint16 = iota
|
||||
NFTA_SET_FIELD_LEN
|
||||
__NFTA_SET_FIELD_MAX
|
||||
NFTA_SET_FIELD_MAX = __NFTA_SET_FIELD_MAX - 1
|
||||
)
|
||||
|
||||
// NfTableObjectAttributes represents the netfilter object attributes.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFT_OBJECT_UNSPEC uint32 = iota
|
||||
NFT_OBJECT_COUNTER
|
||||
NFT_OBJECT_QUOTA
|
||||
NFT_OBJECT_CT_HELPER
|
||||
NFT_OBJECT_LIMIT
|
||||
NFT_OBJECT_CONNLIMIT
|
||||
NFT_OBJECT_TUNNEL
|
||||
NFT_OBJECT_CT_TIMEOUT
|
||||
NFT_OBJECT_SECMARK
|
||||
NFT_OBJECT_CT_EXPECT
|
||||
NFT_OBJECT_SYNPROXY
|
||||
__NFT_OBJECT_MAX
|
||||
NFT_OBJECT_MAX = __NFT_OBJECT_MAX - 1
|
||||
)
|
||||
|
||||
// NfTableSetElemListAttributes represents the netfilter set element list attributes.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFTA_SET_ELEM_LIST_UNSPEC uint16 = iota
|
||||
NFTA_SET_ELEM_LIST_TABLE
|
||||
NFTA_SET_ELEM_LIST_SET
|
||||
NFTA_SET_ELEM_LIST_ELEMENTS
|
||||
NFTA_SET_ELEM_LIST_SET_ID
|
||||
__NFTA_SET_ELEM_LIST_MAX
|
||||
NFTA_SET_ELEM_LIST_MAX = __NFTA_SET_ELEM_LIST_MAX - 1
|
||||
)
|
||||
|
||||
// NfTableSetElemAttributes represents the netfilter set element attributes.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFTA_SET_ELEM_UNSPEC uint16 = iota
|
||||
NFTA_SET_ELEM_KEY
|
||||
NFTA_SET_ELEM_DATA
|
||||
NFTA_SET_ELEM_FLAGS
|
||||
NFTA_SET_ELEM_TIMEOUT
|
||||
NFTA_SET_ELEM_EXPIRATION
|
||||
NFTA_SET_ELEM_USERDATA
|
||||
NFTA_SET_ELEM_EXPR
|
||||
NFTA_SET_ELEM_PAD
|
||||
NFTA_SET_ELEM_OBJREF
|
||||
NFTA_SET_ELEM_KEY_END
|
||||
NFTA_SET_ELEM_EXPRESSIONS
|
||||
__NFTA_SET_ELEM_MAX
|
||||
NFTA_SET_ELEM_MAX = __NFTA_SET_ELEM_MAX - 1
|
||||
)
|
||||
|
||||
// NfTableSetElemFlags represents the netfilter set element flags.
|
||||
// These correspond to values in include/uapi/linux/netfilter/nf_tables.h.
|
||||
const (
|
||||
NFT_SET_ELEM_INTERVAL_END = uint16(0x1)
|
||||
NFT_SET_ELEM_CATCHALL = uint16(0x2)
|
||||
)
|
||||
|
||||
@@ -1224,7 +1224,9 @@ func (p *Protocol) processBatchMessage(ctx context.Context, buf []byte, ms *nlms
|
||||
// **********************************************************************
|
||||
// TODO: b/436922484 - Add a transaction system to avoid deep copying the
|
||||
// entire NFTables structure.
|
||||
// Change logic to just replace atomic ptrs.
|
||||
// **********************************************************************
|
||||
|
||||
nft.Mu.Lock()
|
||||
defer nft.Mu.Unlock()
|
||||
|
||||
@@ -1327,7 +1329,9 @@ func (p *Protocol) processBatchMessage(ctx context.Context, buf []byte, ms *nlms
|
||||
subErr = p.deleteChain(nftCopy, attrs, family, hdr.Flags, hdr.NetFilterMsgType(), ms)
|
||||
case linux.NFT_MSG_NEWRULE:
|
||||
subErr = p.newRule(nftCopy, st, attrs, family, hdr.Flags, ms)
|
||||
case linux.NFT_MSG_DELRULE, linux.NFT_MSG_DESTROYRULE, linux.NFT_MSG_NEWSET,
|
||||
case linux.NFT_MSG_NEWSET:
|
||||
subErr = nftCopy.NewSet(attrs, family, hdr.Flags, ms)
|
||||
case linux.NFT_MSG_DELRULE, linux.NFT_MSG_DESTROYRULE,
|
||||
linux.NFT_MSG_DELSET, linux.NFT_MSG_DESTROYSET, linux.NFT_MSG_NEWSETELEM,
|
||||
linux.NFT_MSG_DELSETELEM, linux.NFT_MSG_DESTROYSETELEM,
|
||||
linux.NFT_MSG_NEWOBJ, linux.NFT_MSG_DELOBJ, linux.NFT_MSG_DESTROYOBJ,
|
||||
|
||||
@@ -34,6 +34,7 @@ go_library(
|
||||
"nftables.go",
|
||||
"nftables_attrs.go",
|
||||
"nftables_mutex.go",
|
||||
"nftables_set.go",
|
||||
"nftables_types.go",
|
||||
"nftinterp.go",
|
||||
],
|
||||
|
||||
@@ -597,6 +597,8 @@ func (nf *NFTables) AddTable(family stack.AddressFamily, name string,
|
||||
flagSet: make(map[TableFlag]struct{}),
|
||||
handle: nf.getNewTableHandle(),
|
||||
handleCounter: atomicbitops.Uint64{},
|
||||
sets: make(map[string]*nftSet),
|
||||
setHandles: make(map[uint64]*nftSet),
|
||||
}
|
||||
tableMap[name] = t
|
||||
tableHandleMap[t.handle] = t
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
// Copyright 2026 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package nftables
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// isSame checks if the set is the same as the given set description.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nft_set_is_same
|
||||
func (s *nftSet) isSame(other *nftSet) bool {
|
||||
if s.keyType != other.keyType ||
|
||||
s.dataType != other.dataType ||
|
||||
s.flags != other.flags ||
|
||||
s.keyLen != other.keyLen ||
|
||||
s.dataLen != other.dataLen || s.fieldCount != other.fieldCount ||
|
||||
s.timeout != other.timeout ||
|
||||
s.gcInterval != other.gcInterval ||
|
||||
s.policy != other.policy ||
|
||||
len(s.exprInfos) != len(other.exprInfos) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := uint8(0); i < s.fieldCount; i++ {
|
||||
if s.fieldLen[i] != other.fieldLen[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: b/505409691 - Compare exprs.
|
||||
return true
|
||||
}
|
||||
|
||||
// AddSetToTable adds a new set to the corresponding table, returning an error if
|
||||
// the table doesn't exist or a set by the same name already exists.
|
||||
func (nf *NFTables) addSetToTable(tab *Table, set *nftSet) *syserr.AnnotatedError {
|
||||
handle := tab.getNewHandle()
|
||||
set.handle = handle
|
||||
tab.sets[set.name] = set
|
||||
tab.setHandles[handle] = set
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSetFlags validates the set flags.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nf_tables_newset
|
||||
func validateSetFlags(flags uint16) *syserr.AnnotatedError {
|
||||
if flags&^(linux.NFT_SET_ANONYMOUS|linux.NFT_SET_CONSTANT|
|
||||
linux.NFT_SET_INTERVAL|linux.NFT_SET_TIMEOUT|
|
||||
linux.NFT_SET_MAP|linux.NFT_SET_EVAL|
|
||||
linux.NFT_SET_OBJECT|linux.NFT_SET_CONCAT|linux.NFT_SET_EXPR) != 0 {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Unsupported set flags")
|
||||
}
|
||||
if (flags & (linux.NFT_SET_MAP | linux.NFT_SET_OBJECT)) ==
|
||||
(linux.NFT_SET_MAP | linux.NFT_SET_OBJECT) {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Only one of NFT_SET_MAP or NFT_SET_OBJECT is supported")
|
||||
}
|
||||
if (flags & (linux.NFT_SET_EVAL | linux.NFT_SET_OBJECT)) ==
|
||||
(linux.NFT_SET_EVAL | linux.NFT_SET_OBJECT) {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Only one of NFT_SET_EVAL or NFT_SET_OBJECT is supported")
|
||||
}
|
||||
if (flags & (linux.NFT_SET_ANONYMOUS | linux.NFT_SET_TIMEOUT | linux.NFT_SET_EVAL)) ==
|
||||
(linux.NFT_SET_ANONYMOUS | linux.NFT_SET_TIMEOUT) {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Only one of NFT_SET_ANONYMOUS or NFT_SET_TIMEOUT is supported")
|
||||
}
|
||||
if (flags & (linux.NFT_SET_CONSTANT | linux.NFT_SET_TIMEOUT)) ==
|
||||
(linux.NFT_SET_CONSTANT | linux.NFT_SET_TIMEOUT) {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Only one of NFT_SET_CONSTANT or NFT_SET_TIMEOUT is supported")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseSetDataAttr parses the set data type and length attributes.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nf_tables_newset
|
||||
func parseSetDataAttr(attrs map[uint16]nlmsg.BytesView, setFlags uint16) (dataType uint32, dataLen uint32, err *syserr.AnnotatedError) {
|
||||
dataType, ok := AttrNetToHost[uint32](linux.NFTA_SET_DATA_TYPE, attrs)
|
||||
if !ok {
|
||||
if setFlags&linux.NFT_SET_MAP != 0 {
|
||||
return 0, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set data type attribute is missing")
|
||||
}
|
||||
// If the set is not a map, then the data type attribute is not required.
|
||||
return 0, 0, nil
|
||||
}
|
||||
if setFlags&linux.NFT_SET_MAP == 0 {
|
||||
return 0, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set data type attribute should not be present for non-map set")
|
||||
}
|
||||
if (dataType&linux.NFT_DATA_RESERVED_MASK) == linux.NFT_DATA_RESERVED_MASK &&
|
||||
dataType != linux.NFT_DATA_VERDICT {
|
||||
return 0, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Unsupported set data type")
|
||||
}
|
||||
if dataType == linux.NFT_DATA_VERDICT {
|
||||
return dataType, 16, nil
|
||||
}
|
||||
dataLen, ok = AttrNetToHost[uint32](linux.NFTA_SET_DATA_LEN, attrs)
|
||||
if !ok {
|
||||
return 0, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set data length attribute is missing")
|
||||
}
|
||||
if dataLen == 0 || dataLen > linux.NFT_DATA_VALUE_MAXLEN {
|
||||
return 0, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Invalid set data length")
|
||||
}
|
||||
return dataType, dataLen, nil
|
||||
}
|
||||
|
||||
// parseSetObjectAttr parses the set object type attribute.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nf_tables_newset
|
||||
func parseSetObjectAttr(attrs map[uint16]nlmsg.BytesView, setFlags uint16) (objType uint32, err *syserr.AnnotatedError) {
|
||||
objType, ok := AttrNetToHost[uint32](linux.NFTA_SET_OBJ_TYPE, attrs)
|
||||
if ok && (setFlags&linux.NFT_SET_OBJECT == 0) {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set object flag is not set but object type attribute is present")
|
||||
}
|
||||
if !ok && (setFlags&linux.NFT_SET_OBJECT != 0) {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set object flag is set but object type attribute is missing")
|
||||
}
|
||||
if !ok {
|
||||
// The object type attribute is not required if the set is not an object set.
|
||||
return linux.NFT_OBJECT_UNSPEC, nil
|
||||
}
|
||||
if objType == linux.NFT_OBJECT_UNSPEC || objType > linux.NFT_OBJECT_MAX {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrNotSupported, "Invalid set object type")
|
||||
}
|
||||
return objType, nil
|
||||
}
|
||||
|
||||
// parseSetTimeoutAttr parses the set timeout attribute.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nf_tables_newset
|
||||
func parseSetTimeoutAttr(attrs map[uint16]nlmsg.BytesView, setFlags uint16) (timeMiliSec uint64, err *syserr.AnnotatedError) {
|
||||
timeMS, ok := AttrNetToHost[uint64](linux.NFTA_SET_TIMEOUT, attrs)
|
||||
if ok && (setFlags&linux.NFT_SET_TIMEOUT == 0) {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set timeout flag is not set but timeout attribute is present")
|
||||
}
|
||||
if !ok {
|
||||
// The timeout attribute is not a required attribute.
|
||||
return 0, nil
|
||||
}
|
||||
if setFlags&linux.NFT_SET_ANONYMOUS != 0 {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrNotSupported, "Set timeout flag is not supported for anonymous sets")
|
||||
}
|
||||
return timeMS, nil
|
||||
}
|
||||
|
||||
// parseSetGCIntervalAttr parses the set gc interval attribute.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nf_tables_newset
|
||||
// TODO: b/505409691 - Add support for timeout and gc.
|
||||
func parseSetGCIntervalAttr(attrs map[uint16]nlmsg.BytesView, setFlags uint16) (gcIntMiliSec uint32, err *syserr.AnnotatedError) {
|
||||
gc, ok := AttrNetToHost[uint32](linux.NFTA_SET_GC_INTERVAL, attrs)
|
||||
if !ok {
|
||||
// The gc interval attribute is not a required attribute.
|
||||
return 0, nil
|
||||
}
|
||||
if setFlags&linux.NFT_SET_TIMEOUT == 0 {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set gc flag is not set but gc attribute is present")
|
||||
}
|
||||
if setFlags&linux.NFT_SET_ANONYMOUS != 0 {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrNotSupported, "Set gc flag is not supported for anonymous sets")
|
||||
}
|
||||
return gc, nil
|
||||
}
|
||||
|
||||
// parseSetPolicyAttr parses the set policy attribute.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nf_tables_newset
|
||||
// TODO: b/505409691 - Add support for policy.
|
||||
func parseSetPolicyAttr(attrs map[uint16]nlmsg.BytesView, setFlags uint16) (policy uint32, err *syserr.AnnotatedError) {
|
||||
policy, ok := AttrNetToHost[uint32](linux.NFTA_SET_POLICY, attrs)
|
||||
if !ok {
|
||||
// The policy attribute is not a required attribute;
|
||||
// default to performance optimized.
|
||||
return linux.NFT_SET_POL_PERFORMANCE, nil
|
||||
}
|
||||
if policy != linux.NFT_SET_POL_PERFORMANCE && policy != linux.NFT_SET_POL_MEMORY {
|
||||
return 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Invalid set policy")
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// setConcatPolicy is the policy for parsing the set description concat attributes.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nft_concat_policy
|
||||
var setConcatPolicy = []NlaPolicy{
|
||||
linux.NFTA_SET_FIELD_LEN: {nlaType: linux.NLA_U32},
|
||||
}
|
||||
|
||||
// parseSetConcatAttr parses the set description concat attributes.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nft_set_desc_concat_parse
|
||||
func parseSetConcatAttr(descConcatAttrs nlmsg.BytesView, keyLen uint32) (concat []uint8, err *syserr.AnnotatedError) {
|
||||
roundUpToNearestMultipleOfFour := func(num uint32) uint32 {
|
||||
return (num + (3)) & ^uint32(3)
|
||||
}
|
||||
|
||||
concatAttrsList := nlmsg.AttrsView(descConcatAttrs)
|
||||
var concatList [linux.NFT_REG32_COUNT]uint8
|
||||
i := 0
|
||||
totalLen := uint32(0)
|
||||
for !concatAttrsList.Empty() {
|
||||
if i >= len(concatList) {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrFileTooBig, "Too many set description concat elements")
|
||||
}
|
||||
hdr, value, rest, ok := concatAttrsList.ParseFirst()
|
||||
if !ok {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Failed to parse set description concat element")
|
||||
}
|
||||
concatAttrsList = rest
|
||||
if hdr.Type != linux.NFTA_LIST_ELEM {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set description concat element is not of type NFTA_LIST_ELEM")
|
||||
}
|
||||
concatElem, ok := NfParseWithOpts(nlmsg.AttrsView(value), &NfParseOpts{
|
||||
Policy: setConcatPolicy,
|
||||
})
|
||||
if !ok {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Failed to parse set description concat element attributes")
|
||||
}
|
||||
fieldLen, ok := AttrNetToHost[uint32](linux.NFTA_SET_FIELD_LEN, concatElem)
|
||||
if !ok {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Failed to parse set description field length")
|
||||
}
|
||||
if fieldLen == 0 || fieldLen > math.MaxUint8 {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Invalid set field length")
|
||||
}
|
||||
|
||||
concatList[i] = uint8(fieldLen)
|
||||
i++
|
||||
totalLen += roundUpToNearestMultipleOfFour(fieldLen)
|
||||
}
|
||||
if totalLen != keyLen {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Total set description field length does not match key length")
|
||||
}
|
||||
if keyLen > linux.NFT_REG32_COUNT {
|
||||
return nil, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Key length is larger than maximum supported register size")
|
||||
}
|
||||
return concatList[:i], nil
|
||||
}
|
||||
|
||||
// setDescPolicy is the policy for parsing the set description attributes.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nft_set_desc_policy
|
||||
var setDescPolicy = []NlaPolicy{
|
||||
linux.NFTA_SET_DESC_SIZE: {nlaType: linux.NLA_U32},
|
||||
linux.NFTA_SET_DESC_CONCAT: {nlaType: linux.NLA_NESTED, validator: AttrArrayValidator(setConcatPolicy)},
|
||||
}
|
||||
|
||||
type setDesc struct {
|
||||
size uint32
|
||||
concat []uint8
|
||||
}
|
||||
|
||||
// parseSetDescAttr parses the set description attributes.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nft_set_desc_concat_parse
|
||||
func parseSetDescAttr(attrs map[uint16]nlmsg.BytesView, keyLen uint32, setFlags uint16) (concat []uint8, descSize uint32, err *syserr.AnnotatedError) {
|
||||
descAttrs, ok := attrs[linux.NFTA_SET_DESC]
|
||||
if !ok {
|
||||
if setFlags&linux.NFT_SET_CONCAT != 0 {
|
||||
return nil, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set concat flag present but set description attribute is missing")
|
||||
}
|
||||
// The set description attribute is not a required attribute.
|
||||
// If the descSize is 0, then the set has no bounds on the number of elements.
|
||||
return nil, 0, nil
|
||||
}
|
||||
subAttrs, ok := NfParseWithOpts(nlmsg.AttrsView(descAttrs), &NfParseOpts{
|
||||
Policy: setDescPolicy,
|
||||
})
|
||||
if !ok {
|
||||
return nil, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Failed to parse set description attributes")
|
||||
}
|
||||
descSize, ok = AttrNetToHost[uint32](linux.NFTA_SET_DESC_SIZE, subAttrs)
|
||||
if !ok {
|
||||
descSize = 0
|
||||
}
|
||||
if concatAttrs, ok := subAttrs[linux.NFTA_SET_DESC_CONCAT]; ok {
|
||||
concat, err = parseSetConcatAttr(concatAttrs, keyLen)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
if len(concat) > 1 {
|
||||
if setFlags&linux.NFT_SET_CONCAT == 0 {
|
||||
return nil, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set concat flag missing but multiple concat elements found")
|
||||
}
|
||||
} else if setFlags&linux.NFT_SET_CONCAT != 0 {
|
||||
return nil, 0, syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set concat flag present but single/no concat elements found")
|
||||
}
|
||||
return concat, descSize, nil
|
||||
}
|
||||
|
||||
// NewSet creates a set in the given table.
|
||||
// Ref: net/netfilter/nf_tables_api.c:nf_tables_newset
|
||||
func (nf *NFTables) NewSet(attrs map[uint16]nlmsg.BytesView, family stack.AddressFamily, msgFlags uint16, ms *nlmsg.MessageSet) *syserr.AnnotatedError {
|
||||
tabNameBytes, tabNameOk := attrs[linux.NFTA_SET_TABLE]
|
||||
if !tabNameOk {
|
||||
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set table name attribute is malformed or not found")
|
||||
}
|
||||
|
||||
setNameBytes, nameOk := attrs[linux.NFTA_SET_NAME]
|
||||
if !nameOk {
|
||||
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set name attribute is malformed or not found")
|
||||
}
|
||||
|
||||
if _, idOk := attrs[linux.NFTA_SET_ID]; !idOk {
|
||||
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set id attribute is missing")
|
||||
}
|
||||
|
||||
keyType, keyTypeOk := AttrNetToHost[uint32](linux.NFTA_SET_KEY_TYPE, attrs)
|
||||
if !keyTypeOk {
|
||||
keyType = uint32(0)
|
||||
}
|
||||
|
||||
keyLen, keyLenOk := AttrNetToHost[uint32](linux.NFTA_SET_KEY_LEN, attrs)
|
||||
if !keyLenOk {
|
||||
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set key length attribute is missing")
|
||||
}
|
||||
if keyLen == 0 || keyLen > linux.NFT_DATA_VALUE_MAXLEN {
|
||||
return syserr.NewAnnotatedError(syserr.ErrInvalidArgument, "Set key length attribute is invalid")
|
||||
}
|
||||
|
||||
setFlagsU32, ok := AttrNetToHost[uint32](linux.NFTA_SET_FLAGS, attrs)
|
||||
setFlags := uint16(setFlagsU32)
|
||||
if ok {
|
||||
if err := validateSetFlags(setFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dataType, dataLen, err := parseSetDataAttr(attrs, setFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objType, err := parseSetObjectAttr(attrs, setFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeout, err := parseSetTimeoutAttr(attrs, setFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gcInterval, err := parseSetGCIntervalAttr(attrs, setFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policy, err := parseSetPolicyAttr(attrs, setFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
descConcat, descSize, err := parseSetDescAttr(attrs, keyLen, setFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tab *Table
|
||||
if tabNameOk {
|
||||
tabName := tabNameBytes.String()
|
||||
tab, err = nf.GetTable(family, tabName, uint32(ms.PortID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
udataAttr, udataExists := attrs[linux.NFTA_SET_USERDATA]
|
||||
var udata []byte
|
||||
if udataExists {
|
||||
udata = []byte(udataAttr)
|
||||
}
|
||||
|
||||
if uint32(msgFlags)&linux.NLM_F_CREATE == 0 {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "NLM_F_CREATE is required for set creation")
|
||||
}
|
||||
|
||||
var exprInfos []ExprInfo
|
||||
if singleExpr, ok := attrs[linux.NFTA_SET_EXPR]; ok {
|
||||
exprInfo, err := nf.ParseExpr(nlmsg.AttrsView(singleExpr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exprInfos = []ExprInfo{*exprInfo}
|
||||
} else if exprBytes, ok := attrs[linux.NFTA_SET_EXPRESSIONS]; ok {
|
||||
exprInfos, err = nf.ParseNestedExprs(nlmsg.AttrsView(exprBytes), linux.NFT_SET_EXPR_MAX)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
setName := setNameBytes.String()
|
||||
newSet := &nftSet{
|
||||
name: setName,
|
||||
keyType: keyType,
|
||||
dataType: dataType,
|
||||
objType: objType,
|
||||
descSize: descSize,
|
||||
fieldLen: descConcat,
|
||||
fieldCount: uint8(len(descConcat)),
|
||||
timeout: timeout,
|
||||
gcInterval: gcInterval,
|
||||
policy: policy,
|
||||
udata: udata,
|
||||
exprInfos: exprInfos,
|
||||
flags: setFlags,
|
||||
dead: 0,
|
||||
keyLen: uint8(keyLen),
|
||||
dataLen: uint8(dataLen),
|
||||
}
|
||||
|
||||
if set, exists := tab.sets[setName]; exists {
|
||||
if msgFlags&linux.NLM_F_EXCL != 0 {
|
||||
return syserr.NewAnnotatedError(syserr.ErrExists, "Set already exists")
|
||||
}
|
||||
if msgFlags&linux.NLM_F_REPLACE != 0 {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Replace flag is not supported")
|
||||
}
|
||||
if set.flags&linux.NFT_SET_ANONYMOUS != 0 {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "Anonymous set already exists")
|
||||
}
|
||||
if set.isSame(newSet) {
|
||||
return nil
|
||||
}
|
||||
return syserr.NewAnnotatedError(syserr.ErrExists, "Set already exists with differing attributes")
|
||||
}
|
||||
|
||||
if msgFlags&linux.NLM_F_CREATE == 0 {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "NLM_F_CREATE is required for set creation")
|
||||
}
|
||||
|
||||
// TODO: b/505409691 - Support non-map sets.
|
||||
if setFlags&linux.NFT_SET_MAP == 0 {
|
||||
return syserr.NewAnnotatedError(syserr.ErrNotSupported, "only map sets are supported yet")
|
||||
}
|
||||
|
||||
return nf.addSetToTable(tab, newSet)
|
||||
}
|
||||
@@ -307,6 +307,10 @@ type Table struct {
|
||||
// Note: currently nftables only has the single Dormant flag.
|
||||
flagSet map[TableFlag]struct{}
|
||||
|
||||
// sets is a map of sets for each table.
|
||||
sets map[string]*nftSet
|
||||
setHandles map[uint64]*nftSet
|
||||
|
||||
// handleCounter is the counter for chain and rule handles.
|
||||
handleCounter atomicbitops.Uint64
|
||||
|
||||
@@ -908,6 +912,61 @@ var netlinkAFToStackAF = map[uint8]stack.AddressFamily{
|
||||
linux.NFPROTO_IPV6: stack.IP6,
|
||||
}
|
||||
|
||||
// Set represents an nftables set.
|
||||
// Ref: include/net/netfilter/nf_tables.h:nft_set
|
||||
type nftSet struct {
|
||||
// name represents the unique name for this set.
|
||||
name string
|
||||
// keyType represents the type of key; i.e. IPv4, IPv6, port, etc.
|
||||
keyType uint32
|
||||
// dataType represents the type of data; i.e. VERDICT or DATA.
|
||||
dataType uint32
|
||||
// objType represents the type of object that the set is storing.
|
||||
objType uint32
|
||||
// descSize represents the max number of elements.
|
||||
// If 0, then the set has no bounds on the number of elements.
|
||||
descSize uint32
|
||||
// fieldLen represents the length of each sub-key.
|
||||
fieldLen []uint8
|
||||
// fieldCount represents the number of sub-keys.
|
||||
fieldCount uint8
|
||||
// timeout represents the timeout for the elements in the set.
|
||||
// TODO: b/505409691 - Add support for set timeout.
|
||||
timeout uint64
|
||||
// gcInterval represents the interval for garbage collection.
|
||||
// GC gets rid of expired elements in the set.
|
||||
// TODO: b/505409691 - Add support for gc interval.
|
||||
gcInterval uint32
|
||||
// policy passed by the user hints at
|
||||
// whether the set should be memory or performance optimized.
|
||||
// TODO: b/505409691 - Add support for set policy.
|
||||
policy uint32
|
||||
// udata represents the user data of the set.
|
||||
udata []byte
|
||||
// exprInfos represents the stateful nft expressions/ops;
|
||||
// counter, limit, etc.
|
||||
// that are directly associated with the individual set elements.
|
||||
// TODO: b/505409691 - Add support for expressions other than the counters.
|
||||
exprInfos []ExprInfo
|
||||
// flags determines the set's configuration.
|
||||
flags uint16
|
||||
// dead marks the set as deleted.
|
||||
dead uint8
|
||||
// genmask links to nftables generation.
|
||||
// We don't need to support this as we make a full copy of
|
||||
// the set on each transaction; so each NFTables instance
|
||||
// is it's own "generation".
|
||||
genmask uint16
|
||||
// keyLen is the length of the key,
|
||||
// or the combined length of all the sub-keys.
|
||||
keyLen uint8
|
||||
// dataLen is the length of the data;
|
||||
// incase of a verdict set, this is not required.
|
||||
dataLen uint8
|
||||
// handle is the NFTables unique identifier for this set.
|
||||
handle uint64
|
||||
}
|
||||
|
||||
// AFtoNetlinkAF converts a generic address family to a netfilter address family.
|
||||
// On error, we simply cast it to be a stack.AddressFamily and return an error to allow netfilter
|
||||
// sockets to handle it accordingly if needed.
|
||||
@@ -1122,6 +1181,32 @@ func deepCopyChain(chain *Chain, tableCopy *Table) *Chain {
|
||||
return chainCopy
|
||||
}
|
||||
|
||||
// deepCopySet returns a deep copy of the Set struct.
|
||||
func deepCopySet(set *nftSet) *nftSet {
|
||||
setCopy := &nftSet{
|
||||
name: set.name,
|
||||
keyType: set.keyType,
|
||||
dataType: set.dataType,
|
||||
objType: set.objType,
|
||||
descSize: set.descSize,
|
||||
fieldLen: slices.Clone(set.fieldLen),
|
||||
fieldCount: set.fieldCount,
|
||||
timeout: set.timeout,
|
||||
gcInterval: set.gcInterval,
|
||||
policy: set.policy,
|
||||
udata: slices.Clone(set.udata),
|
||||
exprInfos: slices.Clone(set.exprInfos),
|
||||
flags: set.flags,
|
||||
dead: set.dead,
|
||||
genmask: set.genmask,
|
||||
keyLen: set.keyLen,
|
||||
dataLen: set.dataLen,
|
||||
handle: set.handle,
|
||||
}
|
||||
|
||||
return setCopy
|
||||
}
|
||||
|
||||
// deepCopyTable returns a deep copy of the Table struct.
|
||||
func deepCopyTable(table *Table, afFilter *addressFamilyFilter) *Table {
|
||||
tableCopy := &Table{
|
||||
@@ -1131,6 +1216,8 @@ func deepCopyTable(table *Table, afFilter *addressFamilyFilter) *Table {
|
||||
chainHandles: make(map[uint64]*Chain),
|
||||
flagSet: make(map[TableFlag]struct{}),
|
||||
handle: table.handle,
|
||||
sets: make(map[string]*nftSet),
|
||||
setHandles: make(map[uint64]*nftSet),
|
||||
owner: table.owner,
|
||||
userData: slices.Clone(table.userData),
|
||||
}
|
||||
@@ -1140,6 +1227,12 @@ func deepCopyTable(table *Table, afFilter *addressFamilyFilter) *Table {
|
||||
tableCopy.flagSet[flag] = struct{}{}
|
||||
}
|
||||
|
||||
for setName, set := range table.sets {
|
||||
setCopy := deepCopySet(set)
|
||||
tableCopy.sets[setName] = setCopy
|
||||
tableCopy.setHandles[setCopy.handle] = setCopy
|
||||
}
|
||||
|
||||
for chainName, chain := range table.chains {
|
||||
chainCopy := deepCopyChain(chain, tableCopy)
|
||||
tableCopy.chains[chainName] = chainCopy
|
||||
|
||||
Reference in New Issue
Block a user