2019-01-28 22:56:40 -06:00
|
|
|
// Copyright 2018 Google LLC. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// 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 (
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2025-03-03 05:55:40 -06:00
|
|
|
"math"
|
2020-01-22 15:37:16 -06:00
|
|
|
"strings"
|
2020-02-10 04:14:20 -06:00
|
|
|
"time"
|
2019-01-28 22:56:40 -06:00
|
|
|
|
2024-09-02 11:48:06 -05:00
|
|
|
"github.com/google/nftables/binaryutil"
|
2019-08-28 01:51:13 -05:00
|
|
|
"github.com/google/nftables/expr"
|
2023-05-20 11:23:52 -05:00
|
|
|
"github.com/google/nftables/internal/parseexprfunc"
|
2024-09-02 11:48:06 -05:00
|
|
|
"github.com/google/nftables/userdata"
|
2019-01-28 22:56:40 -06:00
|
|
|
"github.com/mdlayher/netlink"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
)
|
|
|
|
|
2019-12-17 17:02:00 -06:00
|
|
|
// SetConcatTypeBits defines concatination bits, originally defined in
|
|
|
|
// https://git.netfilter.org/iptables/tree/iptables/nft.c?id=26753888720d8e7eb422ae4311348347f5a05cb4#n1002
|
2020-01-22 15:37:16 -06:00
|
|
|
const (
|
|
|
|
SetConcatTypeBits = 6
|
|
|
|
SetConcatTypeMask = (1 << SetConcatTypeBits) - 1
|
2022-04-22 10:12:20 -05:00
|
|
|
// below consts added because not found in go unix package
|
|
|
|
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n306
|
|
|
|
NFT_SET_CONCAT = 0x80
|
|
|
|
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n330
|
|
|
|
NFTA_SET_DESC_CONCAT = 2
|
|
|
|
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n428
|
|
|
|
NFTA_SET_ELEM_KEY_END = 10
|
2022-10-29 01:33:22 -05:00
|
|
|
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n429
|
|
|
|
NFTA_SET_ELEM_EXPRESSIONS = 0x11
|
2020-01-22 15:37:16 -06:00
|
|
|
)
|
2019-12-17 17:02:00 -06:00
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
// SetDatatype represents a datatype declared by nft.
|
|
|
|
type SetDatatype struct {
|
|
|
|
Name string
|
|
|
|
Bytes uint32
|
|
|
|
|
|
|
|
// nftMagic represents the magic value that nft uses for
|
|
|
|
// certain types (ie: IP addresses). We populate SET_KEY_TYPE
|
|
|
|
// identically, so `nft list ...` commands produce correct output.
|
|
|
|
nftMagic uint32
|
|
|
|
}
|
|
|
|
|
2019-12-01 03:10:42 -06:00
|
|
|
// GetNFTMagic returns a custom datatype based on user's parameters
|
|
|
|
func (s *SetDatatype) GetNFTMagic() uint32 {
|
|
|
|
return s.nftMagic
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetNFTMagic returns a custom datatype based on user's parameters
|
|
|
|
func (s *SetDatatype) SetNFTMagic(nftMagic uint32) {
|
|
|
|
s.nftMagic = nftMagic
|
|
|
|
}
|
|
|
|
|
2021-09-08 13:50:07 -05:00
|
|
|
// NFT datatypes. See: https://git.netfilter.org/nftables/tree/include/datatype.h
|
2019-01-28 22:56:40 -06:00
|
|
|
var (
|
2019-12-01 03:10:42 -06:00
|
|
|
TypeInvalid = SetDatatype{Name: "invalid", nftMagic: 0}
|
2019-08-28 01:51:13 -05:00
|
|
|
TypeVerdict = SetDatatype{Name: "verdict", Bytes: 0, nftMagic: 1}
|
2021-09-08 13:50:07 -05:00
|
|
|
TypeNFProto = SetDatatype{Name: "nf_proto", Bytes: 1, nftMagic: 2}
|
|
|
|
TypeBitmask = SetDatatype{Name: "bitmask", Bytes: 0, nftMagic: 3}
|
2019-10-19 01:53:01 -05:00
|
|
|
TypeInteger = SetDatatype{Name: "integer", Bytes: 4, nftMagic: 4}
|
2021-09-08 13:50:07 -05:00
|
|
|
TypeString = SetDatatype{Name: "string", Bytes: 0, nftMagic: 5}
|
|
|
|
TypeLLAddr = SetDatatype{Name: "ll_addr", Bytes: 0, nftMagic: 6}
|
2019-01-28 22:56:40 -06:00
|
|
|
TypeIPAddr = SetDatatype{Name: "ipv4_addr", Bytes: 4, nftMagic: 7}
|
|
|
|
TypeIP6Addr = SetDatatype{Name: "ipv6_addr", Bytes: 16, nftMagic: 8}
|
|
|
|
TypeEtherAddr = SetDatatype{Name: "ether_addr", Bytes: 6, nftMagic: 9}
|
2021-09-08 13:50:07 -05:00
|
|
|
TypeEtherType = SetDatatype{Name: "ether_type", Bytes: 2, nftMagic: 10}
|
|
|
|
TypeARPOp = SetDatatype{Name: "arp_op", Bytes: 2, nftMagic: 11}
|
2019-01-28 22:56:40 -06:00
|
|
|
TypeInetProto = SetDatatype{Name: "inet_proto", Bytes: 1, nftMagic: 12}
|
|
|
|
TypeInetService = SetDatatype{Name: "inet_service", Bytes: 2, nftMagic: 13}
|
2021-09-08 13:50:07 -05:00
|
|
|
TypeICMPType = SetDatatype{Name: "icmp_type", Bytes: 1, nftMagic: 14}
|
|
|
|
TypeTCPFlag = SetDatatype{Name: "tcp_flag", Bytes: 1, nftMagic: 15}
|
|
|
|
TypeDCCPPktType = SetDatatype{Name: "dccp_pkttype", Bytes: 1, nftMagic: 16}
|
|
|
|
TypeMHType = SetDatatype{Name: "mh_type", Bytes: 1, nftMagic: 17}
|
|
|
|
TypeTime = SetDatatype{Name: "time", Bytes: 8, nftMagic: 18}
|
2019-10-19 01:53:01 -05:00
|
|
|
TypeMark = SetDatatype{Name: "mark", Bytes: 4, nftMagic: 19}
|
2021-09-08 13:50:07 -05:00
|
|
|
TypeIFIndex = SetDatatype{Name: "iface_index", Bytes: 4, nftMagic: 20}
|
|
|
|
TypeARPHRD = SetDatatype{Name: "iface_type", Bytes: 2, nftMagic: 21}
|
|
|
|
TypeRealm = SetDatatype{Name: "realm", Bytes: 4, nftMagic: 22}
|
|
|
|
TypeClassID = SetDatatype{Name: "classid", Bytes: 4, nftMagic: 23}
|
|
|
|
TypeUID = SetDatatype{Name: "uid", Bytes: sizeOfUIDT, nftMagic: 24}
|
|
|
|
TypeGID = SetDatatype{Name: "gid", Bytes: sizeOfGIDT, nftMagic: 25}
|
|
|
|
TypeCTState = SetDatatype{Name: "ct_state", Bytes: 4, nftMagic: 26}
|
|
|
|
TypeCTDir = SetDatatype{Name: "ct_dir", Bytes: 1, nftMagic: 27}
|
|
|
|
TypeCTStatus = SetDatatype{Name: "ct_status", Bytes: 4, nftMagic: 28}
|
|
|
|
TypeICMP6Type = SetDatatype{Name: "icmpv6_type", Bytes: 1, nftMagic: 29}
|
|
|
|
TypeCTLabel = SetDatatype{Name: "ct_label", Bytes: ctLabelBitSize / 8, nftMagic: 30}
|
|
|
|
TypePktType = SetDatatype{Name: "pkt_type", Bytes: 1, nftMagic: 31}
|
|
|
|
TypeICMPCode = SetDatatype{Name: "icmp_code", Bytes: 1, nftMagic: 32}
|
|
|
|
TypeICMPV6Code = SetDatatype{Name: "icmpv6_code", Bytes: 1, nftMagic: 33}
|
|
|
|
TypeICMPXCode = SetDatatype{Name: "icmpx_code", Bytes: 1, nftMagic: 34}
|
|
|
|
TypeDevGroup = SetDatatype{Name: "devgroup", Bytes: 4, nftMagic: 35}
|
|
|
|
TypeDSCP = SetDatatype{Name: "dscp", Bytes: 1, nftMagic: 36}
|
|
|
|
TypeECN = SetDatatype{Name: "ecn", Bytes: 1, nftMagic: 37}
|
|
|
|
TypeFIBAddr = SetDatatype{Name: "fib_addrtype", Bytes: 4, nftMagic: 38}
|
|
|
|
TypeBoolean = SetDatatype{Name: "boolean", Bytes: 1, nftMagic: 39}
|
|
|
|
TypeCTEventBit = SetDatatype{Name: "ct_event", Bytes: 4, nftMagic: 40}
|
|
|
|
TypeIFName = SetDatatype{Name: "ifname", Bytes: ifNameSize, nftMagic: 41}
|
|
|
|
TypeIGMPType = SetDatatype{Name: "igmp_type", Bytes: 1, nftMagic: 42}
|
|
|
|
TypeTimeDate = SetDatatype{Name: "time", Bytes: 8, nftMagic: 43}
|
2022-11-01 15:27:48 -05:00
|
|
|
TypeTimeHour = SetDatatype{Name: "hour", Bytes: 4, nftMagic: 44}
|
2021-09-08 13:50:07 -05:00
|
|
|
TypeTimeDay = SetDatatype{Name: "day", Bytes: 1, nftMagic: 45}
|
|
|
|
TypeCGroupV2 = SetDatatype{Name: "cgroupsv2", Bytes: 8, nftMagic: 46}
|
2019-01-28 22:56:40 -06:00
|
|
|
|
2023-04-02 03:11:12 -05:00
|
|
|
nftDatatypes = []SetDatatype{
|
|
|
|
TypeVerdict,
|
|
|
|
TypeNFProto,
|
|
|
|
TypeBitmask,
|
|
|
|
TypeInteger,
|
|
|
|
TypeString,
|
|
|
|
TypeLLAddr,
|
|
|
|
TypeIPAddr,
|
|
|
|
TypeIP6Addr,
|
|
|
|
TypeEtherAddr,
|
|
|
|
TypeEtherType,
|
|
|
|
TypeARPOp,
|
|
|
|
TypeInetProto,
|
|
|
|
TypeInetService,
|
|
|
|
TypeICMPType,
|
|
|
|
TypeTCPFlag,
|
|
|
|
TypeDCCPPktType,
|
|
|
|
TypeMHType,
|
|
|
|
TypeTime,
|
|
|
|
TypeMark,
|
|
|
|
TypeIFIndex,
|
|
|
|
TypeARPHRD,
|
|
|
|
TypeRealm,
|
|
|
|
TypeClassID,
|
|
|
|
TypeUID,
|
|
|
|
TypeGID,
|
|
|
|
TypeCTState,
|
|
|
|
TypeCTDir,
|
|
|
|
TypeCTStatus,
|
|
|
|
TypeICMP6Type,
|
|
|
|
TypeCTLabel,
|
|
|
|
TypePktType,
|
|
|
|
TypeICMPCode,
|
|
|
|
TypeICMPV6Code,
|
|
|
|
TypeICMPXCode,
|
|
|
|
TypeDevGroup,
|
|
|
|
TypeDSCP,
|
|
|
|
TypeECN,
|
|
|
|
TypeFIBAddr,
|
|
|
|
TypeBoolean,
|
|
|
|
TypeCTEventBit,
|
|
|
|
TypeIFName,
|
|
|
|
TypeIGMPType,
|
|
|
|
TypeTimeDate,
|
|
|
|
TypeTimeHour,
|
|
|
|
TypeTimeDay,
|
|
|
|
TypeCGroupV2,
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2025-02-26 05:27:31 -06:00
|
|
|
)
|
2021-09-08 13:50:07 -05:00
|
|
|
|
2025-02-26 05:27:31 -06:00
|
|
|
const (
|
2021-09-08 13:50:07 -05:00
|
|
|
// ctLabelBitSize is defined in https://git.netfilter.org/nftables/tree/src/ct.c.
|
|
|
|
ctLabelBitSize uint32 = 128
|
|
|
|
|
|
|
|
// ifNameSize is called IFNAMSIZ in linux/if.h.
|
|
|
|
ifNameSize uint32 = 16
|
|
|
|
|
|
|
|
// bits/typesizes.h
|
|
|
|
sizeOfUIDT uint32 = 4
|
|
|
|
sizeOfGIDT uint32 = 4
|
2019-01-28 22:56:40 -06:00
|
|
|
)
|
|
|
|
|
2023-04-02 03:11:12 -05:00
|
|
|
var nftDatatypesByName map[string]SetDatatype
|
|
|
|
var nftDatatypesByMagic map[uint32]SetDatatype
|
|
|
|
|
|
|
|
// Create maps for efficient datatype lookup.
|
|
|
|
func init() {
|
|
|
|
nftDatatypesByName = make(map[string]SetDatatype, len(nftDatatypes))
|
|
|
|
nftDatatypesByMagic = make(map[uint32]SetDatatype, len(nftDatatypes))
|
|
|
|
for _, dt := range nftDatatypes {
|
|
|
|
nftDatatypesByName[dt.Name] = dt
|
|
|
|
nftDatatypesByMagic[dt.nftMagic] = dt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 15:37:16 -06:00
|
|
|
// ErrTooManyTypes is the error returned by ConcatSetType, if nftMagic would overflow.
|
|
|
|
var ErrTooManyTypes = errors.New("too many types to concat")
|
|
|
|
|
|
|
|
// MustConcatSetType does the same as ConcatSetType, but panics instead of an
|
|
|
|
// error. It simplifies safe initialization of global variables.
|
|
|
|
func MustConcatSetType(types ...SetDatatype) SetDatatype {
|
|
|
|
t, err := ConcatSetType(types...)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConcatSetType constructs a new SetDatatype which consists of a concatenation
|
|
|
|
// of the passed types. It returns ErrTooManyTypes, if nftMagic would overflow
|
|
|
|
// (more than 5 types).
|
|
|
|
func ConcatSetType(types ...SetDatatype) (SetDatatype, error) {
|
|
|
|
if len(types) > 32/SetConcatTypeBits {
|
|
|
|
return SetDatatype{}, ErrTooManyTypes
|
|
|
|
}
|
|
|
|
|
|
|
|
var magic, bytes uint32
|
|
|
|
names := make([]string, len(types))
|
|
|
|
for i, t := range types {
|
|
|
|
bytes += t.Bytes
|
|
|
|
// concatenated types pad the length to multiples of the register size (4 bytes)
|
|
|
|
// see https://git.netfilter.org/nftables/tree/src/datatype.c?id=488356b895024d0944b20feb1f930558726e0877#n1162
|
|
|
|
if t.Bytes%4 != 0 {
|
|
|
|
bytes += 4 - (t.Bytes % 4)
|
|
|
|
}
|
|
|
|
names[i] = t.Name
|
|
|
|
|
|
|
|
magic <<= SetConcatTypeBits
|
|
|
|
magic |= t.nftMagic & SetConcatTypeMask
|
|
|
|
}
|
|
|
|
return SetDatatype{Name: strings.Join(names, " . "), Bytes: bytes, nftMagic: magic}, nil
|
|
|
|
}
|
|
|
|
|
2022-04-22 10:12:20 -05:00
|
|
|
// ConcatSetTypeElements uses the ConcatSetType name to calculate and return
|
|
|
|
// a list of base types which were used to construct the concatenated type
|
|
|
|
func ConcatSetTypeElements(t SetDatatype) []SetDatatype {
|
|
|
|
names := strings.Split(t.Name, " . ")
|
|
|
|
types := make([]SetDatatype, len(names))
|
|
|
|
for i, n := range names {
|
2023-04-02 03:11:12 -05:00
|
|
|
types[i] = nftDatatypesByName[n]
|
2022-04-22 10:12:20 -05:00
|
|
|
}
|
|
|
|
return types
|
|
|
|
}
|
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
// Set represents an nftables set. Anonymous sets are only valid within the
|
|
|
|
// context of a single batch.
|
|
|
|
type Set struct {
|
2020-02-05 04:33:52 -06:00
|
|
|
Table *Table
|
|
|
|
ID uint32
|
|
|
|
Name string
|
|
|
|
Anonymous bool
|
|
|
|
Constant bool
|
|
|
|
Interval bool
|
2024-09-02 11:48:06 -05:00
|
|
|
AutoMerge bool
|
2020-02-05 04:33:52 -06:00
|
|
|
IsMap bool
|
|
|
|
HasTimeout bool
|
2022-10-29 01:33:22 -05:00
|
|
|
Counter bool
|
2022-07-29 11:32:59 -05:00
|
|
|
// Can be updated per evaluation path, per `nft list ruleset`
|
|
|
|
// indicates that set contains "flags dynamic"
|
|
|
|
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nf_tables.h?id=84d12cfacf8ddd857a09435f3d982ab6250d250c#n298
|
|
|
|
Dynamic bool
|
2022-04-22 10:12:20 -05:00
|
|
|
// Indicates that the set contains a concatenation
|
|
|
|
// https://git.netfilter.org/nftables/tree/include/linux/netfilter/nf_tables.h?id=d1289bff58e1878c3162f574c603da993e29b113#n306
|
|
|
|
Concatenation bool
|
|
|
|
Timeout time.Duration
|
|
|
|
KeyType SetDatatype
|
|
|
|
DataType SetDatatype
|
2023-05-18 11:05:49 -05:00
|
|
|
// Either host (binaryutil.NativeEndian) or big (binaryutil.BigEndian) endian as per
|
|
|
|
// https://git.netfilter.org/nftables/tree/include/datatype.h?id=d486c9e626405e829221b82d7355558005b26d8a#n109
|
|
|
|
KeyByteOrder binaryutil.ByteOrder
|
2024-12-19 03:24:56 -06:00
|
|
|
Comment string
|
2024-10-18 11:21:51 -05:00
|
|
|
// Indicates that the set has "size" specifier
|
|
|
|
Size uint32
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetElement represents a data point within a set.
|
|
|
|
type SetElement struct {
|
2022-04-22 10:12:20 -05:00
|
|
|
Key []byte
|
|
|
|
Val []byte
|
|
|
|
// Field used for definition of ending interval value in concatenated types
|
|
|
|
// https://git.netfilter.org/libnftnl/tree/include/set_elem.h?id=e2514c0eff4da7e8e0aabd410f7b7d0b7564c880#n11
|
|
|
|
KeyEnd []byte
|
2019-08-09 12:36:23 -05:00
|
|
|
IntervalEnd bool
|
2019-08-28 01:51:13 -05:00
|
|
|
// To support vmap, a caller must be able to pass Verdict type of data.
|
|
|
|
// If IsMap is true and VerdictData is not nil, then Val of SetElement will be ignored
|
|
|
|
// and VerdictData will be wrapped into Attribute data.
|
|
|
|
VerdictData *expr.Verdict
|
2020-02-05 04:33:52 -06:00
|
|
|
// To support aging of set elements
|
2020-02-10 04:14:20 -06:00
|
|
|
Timeout time.Duration
|
2023-04-21 01:26:50 -05:00
|
|
|
|
|
|
|
// Life left of the "timeout" elements
|
|
|
|
Expires time.Duration
|
2023-05-20 11:23:52 -05:00
|
|
|
|
|
|
|
Counter *expr.Counter
|
2025-01-15 02:36:42 -06:00
|
|
|
Comment string
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
|
2023-05-20 11:23:52 -05:00
|
|
|
func (s *SetElement) decode(fam byte) func(b []byte) error {
|
2019-08-13 15:19:49 -05:00
|
|
|
return func(b []byte) error {
|
|
|
|
ad, err := netlink.NewAttributeDecoder(b)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create nested attribute decoder: %v", err)
|
|
|
|
}
|
|
|
|
ad.ByteOrder = binary.BigEndian
|
|
|
|
|
|
|
|
for ad.Next() {
|
|
|
|
switch ad.Type() {
|
|
|
|
case unix.NFTA_SET_ELEM_KEY:
|
|
|
|
s.Key, err = decodeElement(ad.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-04-22 10:12:20 -05:00
|
|
|
case NFTA_SET_ELEM_KEY_END:
|
|
|
|
s.KeyEnd, err = decodeElement(ad.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-13 15:19:49 -05:00
|
|
|
case unix.NFTA_SET_ELEM_DATA:
|
|
|
|
s.Val, err = decodeElement(ad.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case unix.NFTA_SET_ELEM_FLAGS:
|
|
|
|
flags := ad.Uint32()
|
|
|
|
s.IntervalEnd = (flags & unix.NFT_SET_ELEM_INTERVAL_END) != 0
|
2020-02-05 04:33:52 -06:00
|
|
|
case unix.NFTA_SET_ELEM_TIMEOUT:
|
2023-04-21 01:26:50 -05:00
|
|
|
s.Timeout = time.Millisecond * time.Duration(ad.Uint64())
|
|
|
|
case unix.NFTA_SET_ELEM_EXPIRATION:
|
|
|
|
s.Expires = time.Millisecond * time.Duration(ad.Uint64())
|
2025-01-15 02:36:42 -06:00
|
|
|
case unix.NFTA_SET_ELEM_USERDATA:
|
|
|
|
userData := ad.Bytes()
|
|
|
|
// Try to extract comment from userdata if present
|
|
|
|
if comment, ok := userdata.GetString(userData, userdata.NFTNL_UDATA_SET_ELEM_COMMENT); ok {
|
|
|
|
s.Comment = comment
|
|
|
|
}
|
2023-05-20 11:23:52 -05:00
|
|
|
case unix.NFTA_SET_ELEM_EXPR:
|
2024-07-24 01:26:13 -05:00
|
|
|
elems, err := parseexprfunc.ParseExprBytesFunc(fam, ad)
|
2023-05-20 11:23:52 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, elem := range elems {
|
|
|
|
switch item := elem.(type) {
|
|
|
|
case *expr.Counter:
|
|
|
|
s.Counter = item
|
|
|
|
}
|
|
|
|
}
|
2019-08-13 15:19:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ad.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeElement(d []byte) ([]byte, error) {
|
|
|
|
ad, err := netlink.NewAttributeDecoder(d)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to create nested attribute decoder: %v", err)
|
|
|
|
}
|
|
|
|
ad.ByteOrder = binary.BigEndian
|
|
|
|
var b []byte
|
|
|
|
for ad.Next() {
|
|
|
|
switch ad.Type() {
|
|
|
|
case unix.NFTA_SET_ELEM_KEY:
|
|
|
|
fallthrough
|
|
|
|
case unix.NFTA_SET_ELEM_DATA:
|
|
|
|
b = ad.Bytes()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := ad.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
// SetAddElements applies data points to an nftables set.
|
|
|
|
func (cc *Conn) SetAddElements(s *Set, vals []SetElement) error {
|
2022-05-09 06:25:29 -05:00
|
|
|
cc.mu.Lock()
|
|
|
|
defer cc.mu.Unlock()
|
2019-01-28 22:56:40 -06:00
|
|
|
if s.Anonymous {
|
|
|
|
return errors.New("anonymous sets cannot be updated")
|
|
|
|
}
|
2025-03-03 05:55:40 -06:00
|
|
|
return cc.appendElemList(s, vals, unix.NFT_MSG_NEWSETELEM)
|
|
|
|
}
|
2019-01-28 22:56:40 -06:00
|
|
|
|
2025-03-03 05:55:40 -06:00
|
|
|
// SetDeleteElements deletes data points from an nftables set.
|
|
|
|
func (cc *Conn) SetDeleteElements(s *Set, vals []SetElement) error {
|
|
|
|
cc.mu.Lock()
|
|
|
|
defer cc.mu.Unlock()
|
|
|
|
if s.Anonymous {
|
|
|
|
return errors.New("anonymous sets cannot be updated")
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2025-03-03 05:55:40 -06:00
|
|
|
return cc.appendElemList(s, vals, unix.NFT_MSG_DELSETELEM)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
|
2025-03-03 05:55:40 -06:00
|
|
|
// maxElemBatchSize is the maximum size in bytes of encoded set elements which
|
|
|
|
// are sent in one netlink message. The size field of a netlink attribute is a
|
|
|
|
// uint16, and 1024 bytes is more than enough for the per-message headers.
|
|
|
|
const maxElemBatchSize = math.MaxUint16 - 1024
|
|
|
|
|
|
|
|
func (cc *Conn) appendElemList(s *Set, vals []SetElement, hdrType uint16) error {
|
|
|
|
if len(vals) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2019-01-28 22:56:40 -06:00
|
|
|
var elements []netlink.Attribute
|
2025-03-03 05:55:40 -06:00
|
|
|
batchSize := 0
|
|
|
|
var batches [][]netlink.Attribute
|
2019-08-09 12:36:23 -05:00
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
for i, v := range vals {
|
2019-08-09 12:36:23 -05:00
|
|
|
item := make([]netlink.Attribute, 0)
|
|
|
|
var flags uint32
|
|
|
|
if v.IntervalEnd {
|
|
|
|
flags |= unix.NFT_SET_ELEM_INTERVAL_END
|
2019-08-13 15:19:49 -05:00
|
|
|
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_FLAGS | unix.NLA_F_NESTED, Data: binaryutil.BigEndian.PutUint32(flags)})
|
2019-08-09 12:36:23 -05:00
|
|
|
}
|
|
|
|
|
2019-08-27 10:52:21 -05:00
|
|
|
encodedKey, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NFTA_DATA_VALUE, Data: v.Key}})
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
2025-03-03 05:55:40 -06:00
|
|
|
return fmt.Errorf("marshal key %d: %v", i, err)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2022-04-22 10:12:20 -05:00
|
|
|
|
2019-08-09 12:36:23 -05:00
|
|
|
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_KEY | unix.NLA_F_NESTED, Data: encodedKey})
|
2022-04-22 10:12:20 -05:00
|
|
|
if len(v.KeyEnd) > 0 {
|
|
|
|
encodedKeyEnd, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NFTA_DATA_VALUE, Data: v.KeyEnd}})
|
|
|
|
if err != nil {
|
2025-03-03 05:55:40 -06:00
|
|
|
return fmt.Errorf("marshal key end %d: %v", i, err)
|
2022-04-22 10:12:20 -05:00
|
|
|
}
|
|
|
|
item = append(item, netlink.Attribute{Type: NFTA_SET_ELEM_KEY_END | unix.NLA_F_NESTED, Data: encodedKeyEnd})
|
|
|
|
}
|
2020-02-05 04:33:52 -06:00
|
|
|
if s.HasTimeout && v.Timeout != 0 {
|
|
|
|
// Set has Timeout flag set, which means an individual element can specify its own timeout.
|
2020-02-10 04:14:20 -06:00
|
|
|
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_TIMEOUT, Data: binaryutil.BigEndian.PutUint64(uint64(v.Timeout.Milliseconds()))})
|
2020-02-05 04:33:52 -06:00
|
|
|
}
|
2019-08-28 01:51:13 -05:00
|
|
|
// The following switch statement deal with 3 different types of elements.
|
|
|
|
// 1. v is an element of vmap
|
|
|
|
// 2. v is an element of a regular map
|
|
|
|
// 3. v is an element of a regular set (default)
|
|
|
|
switch {
|
|
|
|
case v.VerdictData != nil:
|
|
|
|
// Since VerdictData is not nil, v is vmap element, need to add to the attributes
|
|
|
|
encodedVal := []byte{}
|
|
|
|
encodedKind, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_DATA_VALUE, Data: binaryutil.BigEndian.PutUint32(uint32(v.VerdictData.Kind))},
|
|
|
|
})
|
|
|
|
if err != nil {
|
2025-03-03 05:55:40 -06:00
|
|
|
return fmt.Errorf("marshal item %d: %v", i, err)
|
2019-08-28 01:51:13 -05:00
|
|
|
}
|
|
|
|
encodedVal = append(encodedVal, encodedKind...)
|
|
|
|
if len(v.VerdictData.Chain) != 0 {
|
|
|
|
encodedChain, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_ELEM_DATA, Data: []byte(v.VerdictData.Chain + "\x00")},
|
|
|
|
})
|
|
|
|
if err != nil {
|
2025-03-03 05:55:40 -06:00
|
|
|
return fmt.Errorf("marshal item %d: %v", i, err)
|
2019-08-28 01:51:13 -05:00
|
|
|
}
|
|
|
|
encodedVal = append(encodedVal, encodedChain...)
|
|
|
|
}
|
|
|
|
encodedVerdict, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_ELEM_DATA | unix.NLA_F_NESTED, Data: encodedVal}})
|
|
|
|
if err != nil {
|
2025-03-03 05:55:40 -06:00
|
|
|
return fmt.Errorf("marshal item %d: %v", i, err)
|
2019-08-28 01:51:13 -05:00
|
|
|
}
|
|
|
|
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_DATA | unix.NLA_F_NESTED, Data: encodedVerdict})
|
|
|
|
case len(v.Val) > 0:
|
|
|
|
// Since v.Val's length is not 0 then, v is a regular map element, need to add to the attributes
|
2019-08-27 10:52:21 -05:00
|
|
|
encodedVal, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NFTA_DATA_VALUE, Data: v.Val}})
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
2025-03-03 05:55:40 -06:00
|
|
|
return fmt.Errorf("marshal item %d: %v", i, err)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2019-08-28 01:51:13 -05:00
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_DATA | unix.NLA_F_NESTED, Data: encodedVal})
|
2019-08-28 01:51:13 -05:00
|
|
|
default:
|
|
|
|
// If niether of previous cases matche, it means 'e' is an element of a regular Set, no need to add to the attributes
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
|
2025-01-15 02:36:42 -06:00
|
|
|
// Add comment to userdata if present
|
|
|
|
if len(v.Comment) > 0 {
|
|
|
|
userData := userdata.AppendString(nil, userdata.NFTNL_UDATA_SET_ELEM_COMMENT, v.Comment)
|
|
|
|
item = append(item, netlink.Attribute{Type: unix.NFTA_SET_ELEM_USERDATA, Data: userData})
|
|
|
|
}
|
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
encodedItem, err := netlink.MarshalAttributes(item)
|
|
|
|
if err != nil {
|
2025-03-03 05:55:40 -06:00
|
|
|
return fmt.Errorf("marshal item %d: %v", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
itemSize := unix.NLA_HDRLEN + len(encodedItem)
|
|
|
|
if batchSize+itemSize > maxElemBatchSize {
|
|
|
|
batches = append(batches, elements)
|
|
|
|
elements = nil
|
|
|
|
batchSize = 0
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
elements = append(elements, netlink.Attribute{Type: uint16(i+1) | unix.NLA_F_NESTED, Data: encodedItem})
|
2025-03-03 05:55:40 -06:00
|
|
|
batchSize += itemSize
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2025-03-03 05:55:40 -06:00
|
|
|
batches = append(batches, elements)
|
2019-01-28 22:56:40 -06:00
|
|
|
|
2025-03-03 05:55:40 -06:00
|
|
|
for _, batch := range batches {
|
|
|
|
encodedElem, err := netlink.MarshalAttributes(batch)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("marshal elements: %v", err)
|
|
|
|
}
|
2019-01-28 22:56:40 -06:00
|
|
|
|
2025-03-03 05:55:40 -06:00
|
|
|
message := []netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_ELEM_LIST_SET, Data: []byte(s.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_SET_ELEM_LIST_SET_ID, Data: binaryutil.BigEndian.PutUint32(s.ID)},
|
|
|
|
{Type: unix.NFTA_SET_ELEM_LIST_TABLE, Data: []byte(s.Table.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_SET_ELEM_LIST_ELEMENTS | unix.NLA_F_NESTED, Data: encodedElem},
|
|
|
|
}
|
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
cc.messages = append(cc.messages, netlinkMessage{
|
2025-03-03 05:55:40 -06:00
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | hdrType),
|
|
|
|
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
|
|
|
|
},
|
|
|
|
Data: append(extraHeader(uint8(s.Table.Family), 0), cc.marshalAttr(message)...),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return nil
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddSet adds the specified Set.
|
|
|
|
func (cc *Conn) AddSet(s *Set, vals []SetElement) error {
|
2022-05-09 06:25:29 -05:00
|
|
|
cc.mu.Lock()
|
|
|
|
defer cc.mu.Unlock()
|
2019-01-28 22:56:40 -06:00
|
|
|
// Based on nft implementation & linux source.
|
|
|
|
// Link: https://github.com/torvalds/linux/blob/49a57857aeea06ca831043acbb0fa5e0f50602fd/net/netfilter/nf_tables_api.c#L3395
|
|
|
|
// Another reference: https://git.netfilter.org/nftables/tree/src
|
|
|
|
|
|
|
|
if s.Anonymous && !s.Constant {
|
|
|
|
return errors.New("anonymous structs must be constant")
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.ID == 0 {
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
s.ID = cc.allocateTransactionID()
|
2019-01-28 22:56:40 -06:00
|
|
|
if s.Anonymous {
|
|
|
|
s.Name = "__set%d"
|
2019-08-27 10:52:21 -05:00
|
|
|
if s.IsMap {
|
|
|
|
s.Name = "__map%d"
|
|
|
|
}
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var flags uint32
|
|
|
|
if s.Anonymous {
|
|
|
|
flags |= unix.NFT_SET_ANONYMOUS
|
|
|
|
}
|
|
|
|
if s.Constant {
|
|
|
|
flags |= unix.NFT_SET_CONSTANT
|
|
|
|
}
|
2019-08-08 00:45:20 -05:00
|
|
|
if s.Interval {
|
|
|
|
flags |= unix.NFT_SET_INTERVAL
|
|
|
|
}
|
2019-08-27 10:52:21 -05:00
|
|
|
if s.IsMap {
|
2019-01-28 22:56:40 -06:00
|
|
|
flags |= unix.NFT_SET_MAP
|
|
|
|
}
|
2020-02-05 04:33:52 -06:00
|
|
|
if s.HasTimeout {
|
|
|
|
flags |= unix.NFT_SET_TIMEOUT
|
|
|
|
}
|
2022-07-29 11:32:59 -05:00
|
|
|
if s.Dynamic {
|
|
|
|
flags |= unix.NFT_SET_EVAL
|
|
|
|
}
|
2022-04-22 10:12:20 -05:00
|
|
|
if s.Concatenation {
|
|
|
|
flags |= NFT_SET_CONCAT
|
|
|
|
}
|
2019-01-28 22:56:40 -06:00
|
|
|
tableInfo := []netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
|
2019-08-09 12:36:23 -05:00
|
|
|
{Type: unix.NFTA_SET_FLAGS, Data: binaryutil.BigEndian.PutUint32(flags)},
|
2019-01-28 22:56:40 -06:00
|
|
|
{Type: unix.NFTA_SET_KEY_TYPE, Data: binaryutil.BigEndian.PutUint32(s.KeyType.nftMagic)},
|
|
|
|
{Type: unix.NFTA_SET_KEY_LEN, Data: binaryutil.BigEndian.PutUint32(s.KeyType.Bytes)},
|
|
|
|
{Type: unix.NFTA_SET_ID, Data: binaryutil.BigEndian.PutUint32(s.ID)},
|
|
|
|
}
|
2019-08-27 10:52:21 -05:00
|
|
|
if s.IsMap {
|
2019-08-28 01:51:13 -05:00
|
|
|
// Check if it is vmap case
|
|
|
|
if s.DataType.nftMagic == 1 {
|
|
|
|
// For Verdict data type, the expected magic is 0xfffff0
|
|
|
|
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_DATA_TYPE, Data: binaryutil.BigEndian.PutUint32(uint32(unix.NFT_DATA_VERDICT))},
|
|
|
|
netlink.Attribute{Type: unix.NFTA_SET_DATA_LEN, Data: binaryutil.BigEndian.PutUint32(s.DataType.Bytes)})
|
|
|
|
} else {
|
|
|
|
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_DATA_TYPE, Data: binaryutil.BigEndian.PutUint32(s.DataType.nftMagic)},
|
|
|
|
netlink.Attribute{Type: unix.NFTA_SET_DATA_LEN, Data: binaryutil.BigEndian.PutUint32(s.DataType.Bytes)})
|
|
|
|
}
|
2019-08-27 10:52:21 -05:00
|
|
|
}
|
2020-02-05 04:33:52 -06:00
|
|
|
if s.HasTimeout && s.Timeout != 0 {
|
|
|
|
// If Set's global timeout is specified, add it to set's attributes
|
2020-02-10 04:14:20 -06:00
|
|
|
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_TIMEOUT, Data: binaryutil.BigEndian.PutUint64(uint64(s.Timeout.Milliseconds()))})
|
2020-02-05 04:33:52 -06:00
|
|
|
}
|
2019-08-27 10:52:21 -05:00
|
|
|
if s.Constant {
|
|
|
|
// nft cli tool adds the number of elements to set/map's descriptor
|
|
|
|
// It make sense to do only if a set or map are constant, otherwise skip NFTA_SET_DESC attribute
|
|
|
|
numberOfElements, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_DATA_VALUE, Data: binaryutil.BigEndian.PutUint32(uint32(len(vals)))},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to marshal number of elements %d: %v", len(vals), err)
|
|
|
|
}
|
|
|
|
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_SET_DESC, Data: numberOfElements})
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2024-10-18 11:21:51 -05:00
|
|
|
|
|
|
|
var descBytes []byte
|
|
|
|
|
|
|
|
if s.Size > 0 {
|
|
|
|
// Marshal set size description
|
|
|
|
descSizeBytes, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_DESC_SIZE, Data: binaryutil.BigEndian.PutUint32(s.Size)},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to marshal set size description: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
descBytes = append(descBytes, descSizeBytes...)
|
|
|
|
}
|
|
|
|
|
2022-04-22 10:12:20 -05:00
|
|
|
if s.Concatenation {
|
|
|
|
// Length of concatenated types is a must, otherwise segfaults when executing nft list ruleset
|
|
|
|
var concatDefinition []byte
|
|
|
|
elements := ConcatSetTypeElements(s.KeyType)
|
|
|
|
for i, v := range elements {
|
|
|
|
// Marshal base type size value
|
|
|
|
valData, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_DATA_VALUE, Data: binaryutil.BigEndian.PutUint32(v.Bytes)},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to marshal element key size %d: %v", i, err)
|
|
|
|
}
|
|
|
|
// Marshal base type size description
|
|
|
|
descSize, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_DESC_SIZE, Data: valData},
|
|
|
|
})
|
2023-09-16 01:51:22 -05:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to marshal base type size description: %w", err)
|
|
|
|
}
|
2022-04-22 10:12:20 -05:00
|
|
|
concatDefinition = append(concatDefinition, descSize...)
|
|
|
|
}
|
|
|
|
// Marshal all base type descriptions into concatenation size description
|
|
|
|
concatBytes, err := netlink.MarshalAttributes([]netlink.Attribute{{Type: unix.NLA_F_NESTED | NFTA_SET_DESC_CONCAT, Data: concatDefinition}})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to marshal concat definition %v", err)
|
|
|
|
}
|
2024-10-18 11:21:51 -05:00
|
|
|
|
|
|
|
descBytes = append(descBytes, concatBytes...)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(descBytes) > 0 {
|
|
|
|
// Marshal set description
|
|
|
|
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NLA_F_NESTED | unix.NFTA_SET_DESC, Data: descBytes})
|
2022-04-22 10:12:20 -05:00
|
|
|
}
|
2024-09-02 11:48:06 -05:00
|
|
|
|
|
|
|
// https://git.netfilter.org/libnftnl/tree/include/udata.h#n17
|
|
|
|
var userData []byte
|
|
|
|
|
2023-05-18 11:05:49 -05:00
|
|
|
if s.Anonymous || s.Constant || s.Interval || s.KeyByteOrder == binaryutil.BigEndian {
|
2024-09-02 11:48:06 -05:00
|
|
|
// Semantically useless - kept for binary compatability with nft
|
|
|
|
userData = userdata.AppendUint32(userData, userdata.NFTNL_UDATA_SET_KEYBYTEORDER, 2)
|
2023-05-18 11:05:49 -05:00
|
|
|
} else if s.KeyByteOrder == binaryutil.NativeEndian {
|
2022-08-30 15:16:08 -05:00
|
|
|
// Per https://git.netfilter.org/nftables/tree/src/mnl.c?id=187c6d01d35722618c2711bbc49262c286472c8f#n1165
|
2024-09-02 11:48:06 -05:00
|
|
|
userData = userdata.AppendUint32(userData, userdata.NFTNL_UDATA_SET_KEYBYTEORDER, 1)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2024-09-02 11:48:06 -05:00
|
|
|
|
|
|
|
if s.Interval && s.AutoMerge {
|
|
|
|
// https://git.netfilter.org/nftables/tree/src/mnl.c?id=187c6d01d35722618c2711bbc49262c286472c8f#n1174
|
|
|
|
userData = userdata.AppendUint32(userData, userdata.NFTNL_UDATA_SET_MERGE_ELEMENTS, 1)
|
|
|
|
}
|
|
|
|
|
2024-12-19 03:24:56 -06:00
|
|
|
if len(s.Comment) != 0 {
|
|
|
|
userData = userdata.AppendString(userData, userdata.NFTNL_UDATA_SET_COMMENT, s.Comment)
|
|
|
|
}
|
|
|
|
|
2024-09-02 11:48:06 -05:00
|
|
|
if len(userData) > 0 {
|
|
|
|
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NFTA_SET_USERDATA, Data: userData})
|
|
|
|
}
|
|
|
|
|
2022-10-29 01:33:22 -05:00
|
|
|
if s.Counter {
|
|
|
|
data, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_LIST_ELEM, Data: []byte("counter\x00")},
|
|
|
|
{Type: unix.NFTA_SET_ELEM_PAD | unix.NFTA_SET_ELEM_DATA, Data: []byte{}},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tableInfo = append(tableInfo, netlink.Attribute{Type: unix.NLA_F_NESTED | NFTA_SET_ELEM_EXPRESSIONS, Data: data})
|
|
|
|
}
|
2019-01-28 22:56:40 -06:00
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
cc.messages = append(cc.messages, netlinkMessage{
|
2019-01-28 22:56:40 -06:00
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSET),
|
2019-03-02 10:04:15 -06:00
|
|
|
Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
|
2019-01-28 22:56:40 -06:00
|
|
|
},
|
2019-07-22 22:12:57 -05:00
|
|
|
Data: append(extraHeader(uint8(s.Table.Family), 0), cc.marshalAttr(tableInfo)...),
|
2019-01-28 22:56:40 -06:00
|
|
|
})
|
|
|
|
|
|
|
|
// Set the values of the set if initial values were provided.
|
2025-03-03 05:55:40 -06:00
|
|
|
return cc.appendElemList(s, vals, unix.NFT_MSG_NEWSETELEM)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// DelSet deletes a specific set, along with all elements it contains.
|
|
|
|
func (cc *Conn) DelSet(s *Set) {
|
2022-05-09 06:25:29 -05:00
|
|
|
cc.mu.Lock()
|
|
|
|
defer cc.mu.Unlock()
|
2019-01-28 22:56:40 -06:00
|
|
|
data := cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
|
|
|
|
})
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
cc.messages = append(cc.messages, netlinkMessage{
|
2019-01-28 22:56:40 -06:00
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSET),
|
2019-03-02 10:04:15 -06:00
|
|
|
Flags: netlink.Request | netlink.Acknowledge,
|
2019-01-28 22:56:40 -06:00
|
|
|
},
|
2019-07-22 22:12:57 -05:00
|
|
|
Data: append(extraHeader(uint8(s.Table.Family), 0), data...),
|
2019-01-28 22:56:40 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-10-23 08:10:52 -05:00
|
|
|
// FlushSet deletes all data points from an nftables set.
|
|
|
|
func (cc *Conn) FlushSet(s *Set) {
|
2022-05-09 06:25:29 -05:00
|
|
|
cc.mu.Lock()
|
|
|
|
defer cc.mu.Unlock()
|
2019-10-23 08:10:52 -05:00
|
|
|
data := cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_TABLE, Data: []byte(s.Table.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_SET_NAME, Data: []byte(s.Name + "\x00")},
|
|
|
|
})
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
cc.messages = append(cc.messages, netlinkMessage{
|
2019-10-23 08:10:52 -05:00
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSETELEM),
|
|
|
|
Flags: netlink.Request | netlink.Acknowledge,
|
|
|
|
},
|
|
|
|
Data: append(extraHeader(uint8(s.Table.Family), 0), data...),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2025-02-26 05:27:31 -06:00
|
|
|
const (
|
2023-12-13 01:23:07 -06:00
|
|
|
newSetHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSET)
|
|
|
|
delSetHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSET)
|
|
|
|
)
|
2019-01-28 22:56:40 -06:00
|
|
|
|
|
|
|
func setsFromMsg(msg netlink.Message) (*Set, error) {
|
2023-12-13 01:23:07 -06:00
|
|
|
if got, want1, want2 := msg.Header.Type, newSetHeaderType, delSetHeaderType; got != want1 && got != want2 {
|
|
|
|
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ad.ByteOrder = binary.BigEndian
|
|
|
|
|
|
|
|
var set Set
|
|
|
|
for ad.Next() {
|
|
|
|
switch ad.Type() {
|
|
|
|
case unix.NFTA_SET_NAME:
|
|
|
|
set.Name = ad.String()
|
|
|
|
case unix.NFTA_SET_ID:
|
|
|
|
set.ID = binary.BigEndian.Uint32(ad.Bytes())
|
2020-02-05 04:33:52 -06:00
|
|
|
case unix.NFTA_SET_TIMEOUT:
|
2020-02-10 04:14:20 -06:00
|
|
|
set.Timeout = time.Duration(time.Millisecond * time.Duration(binary.BigEndian.Uint64(ad.Bytes())))
|
2020-02-05 04:33:52 -06:00
|
|
|
set.HasTimeout = true
|
2019-01-28 22:56:40 -06:00
|
|
|
case unix.NFTA_SET_FLAGS:
|
|
|
|
flags := ad.Uint32()
|
|
|
|
set.Constant = (flags & unix.NFT_SET_CONSTANT) != 0
|
|
|
|
set.Anonymous = (flags & unix.NFT_SET_ANONYMOUS) != 0
|
2019-08-09 12:36:23 -05:00
|
|
|
set.Interval = (flags & unix.NFT_SET_INTERVAL) != 0
|
2021-09-08 13:53:45 -05:00
|
|
|
set.IsMap = (flags & unix.NFT_SET_MAP) != 0
|
|
|
|
set.HasTimeout = (flags & unix.NFT_SET_TIMEOUT) != 0
|
2025-01-11 14:52:27 -06:00
|
|
|
set.Dynamic = (flags & unix.NFT_SET_EVAL) != 0
|
2022-04-22 10:12:20 -05:00
|
|
|
set.Concatenation = (flags & NFT_SET_CONCAT) != 0
|
2019-01-28 22:56:40 -06:00
|
|
|
case unix.NFTA_SET_KEY_TYPE:
|
|
|
|
nftMagic := ad.Uint32()
|
2023-04-02 03:11:12 -05:00
|
|
|
dt, err := parseSetDatatype(nftMagic)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not determine data type: %w", err)
|
2021-09-08 13:54:47 -05:00
|
|
|
}
|
2023-04-02 03:11:12 -05:00
|
|
|
set.KeyType = dt
|
2023-06-14 02:57:49 -05:00
|
|
|
case unix.NFTA_SET_KEY_LEN:
|
|
|
|
set.KeyType.Bytes = binary.BigEndian.Uint32(ad.Bytes())
|
2019-08-27 10:52:21 -05:00
|
|
|
case unix.NFTA_SET_DATA_TYPE:
|
|
|
|
nftMagic := ad.Uint32()
|
2019-08-31 02:36:43 -05:00
|
|
|
// Special case for the data type verdict, in the message it is stored as 0xffffff00 but it is defined as 1
|
|
|
|
if nftMagic == 0xffffff00 {
|
|
|
|
set.KeyType = TypeVerdict
|
|
|
|
break
|
|
|
|
}
|
2023-04-02 03:11:12 -05:00
|
|
|
dt, err := parseSetDatatype(nftMagic)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not determine data type: %w", err)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2023-04-02 03:11:12 -05:00
|
|
|
set.DataType = dt
|
2023-06-14 02:57:49 -05:00
|
|
|
case unix.NFTA_SET_DATA_LEN:
|
|
|
|
set.DataType.Bytes = binary.BigEndian.Uint32(ad.Bytes())
|
2024-09-02 11:48:06 -05:00
|
|
|
case unix.NFTA_SET_USERDATA:
|
|
|
|
data := ad.Bytes()
|
|
|
|
value, ok := userdata.GetUint32(data, userdata.NFTNL_UDATA_SET_MERGE_ELEMENTS)
|
|
|
|
set.AutoMerge = ok && value == 1
|
2024-10-18 11:21:51 -05:00
|
|
|
case unix.NFTA_SET_DESC:
|
|
|
|
nestedAD, err := netlink.NewAttributeDecoder(ad.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("nested NewAttributeDecoder() failed: %w", err)
|
|
|
|
}
|
|
|
|
for nestedAD.Next() {
|
|
|
|
switch nestedAD.Type() {
|
|
|
|
case unix.NFTA_SET_DESC_SIZE:
|
|
|
|
set.Size = binary.BigEndian.Uint32(nestedAD.Bytes())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nestedAD.Err() != nil {
|
|
|
|
return nil, fmt.Errorf("decoding set description: %w", nestedAD.Err())
|
|
|
|
}
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return &set, nil
|
|
|
|
}
|
|
|
|
|
2023-04-02 03:11:12 -05:00
|
|
|
func parseSetDatatype(magic uint32) (SetDatatype, error) {
|
|
|
|
types := make([]SetDatatype, 0, 32/SetConcatTypeBits)
|
|
|
|
for magic != 0 {
|
|
|
|
t := magic & SetConcatTypeMask
|
|
|
|
magic = magic >> SetConcatTypeBits
|
|
|
|
dt, ok := nftDatatypesByMagic[t]
|
|
|
|
if !ok {
|
|
|
|
return TypeInvalid, fmt.Errorf("could not determine data type %+v", dt)
|
2019-12-17 17:02:00 -06:00
|
|
|
}
|
2023-04-02 03:11:12 -05:00
|
|
|
// Because we start with the last type, we insert the later types at the front.
|
|
|
|
types = append([]SetDatatype{dt}, types...)
|
|
|
|
}
|
|
|
|
|
|
|
|
dt, err := ConcatSetType(types...)
|
|
|
|
if err != nil {
|
|
|
|
return TypeInvalid, fmt.Errorf("could not create data type: %w", err)
|
2019-12-17 17:02:00 -06:00
|
|
|
}
|
2023-04-02 03:11:12 -05:00
|
|
|
return dt, nil
|
2019-12-17 17:02:00 -06:00
|
|
|
}
|
|
|
|
|
2025-02-26 05:27:31 -06:00
|
|
|
const (
|
2023-12-13 01:23:07 -06:00
|
|
|
newElemHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSETELEM)
|
|
|
|
delElemHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSETELEM)
|
|
|
|
)
|
2019-01-28 22:56:40 -06:00
|
|
|
|
2023-05-20 11:23:52 -05:00
|
|
|
func elementsFromMsg(fam byte, msg netlink.Message) ([]SetElement, error) {
|
2023-12-13 01:23:07 -06:00
|
|
|
if got, want1, want2 := msg.Header.Type, newElemHeaderType, delElemHeaderType; got != want1 && got != want2 {
|
|
|
|
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ad.ByteOrder = binary.BigEndian
|
|
|
|
|
|
|
|
var elements []SetElement
|
|
|
|
for ad.Next() {
|
|
|
|
b := ad.Bytes()
|
2019-08-13 15:19:49 -05:00
|
|
|
if ad.Type() == unix.NFTA_SET_ELEM_LIST_ELEMENTS {
|
|
|
|
ad, err := netlink.NewAttributeDecoder(b)
|
2025-01-15 02:36:42 -06:00
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ad.ByteOrder = binary.BigEndian
|
|
|
|
|
|
|
|
for ad.Next() {
|
2019-08-13 15:19:49 -05:00
|
|
|
var elem SetElement
|
2019-01-28 22:56:40 -06:00
|
|
|
switch ad.Type() {
|
2019-08-13 15:19:49 -05:00
|
|
|
case unix.NFTA_LIST_ELEM:
|
2023-05-20 11:23:52 -05:00
|
|
|
ad.Do(elem.decode(fam))
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
2025-01-15 02:36:42 -06:00
|
|
|
|
2019-08-13 15:19:49 -05:00
|
|
|
elements = append(elements, elem)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return elements, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSets returns the sets in the specified table.
|
|
|
|
func (cc *Conn) GetSets(t *Table) ([]*Set, error) {
|
2022-05-09 06:25:29 -05:00
|
|
|
conn, closer, err := cc.netlinkConn()
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-09 06:25:29 -05:00
|
|
|
defer func() { _ = closer() }()
|
2019-01-28 22:56:40 -06:00
|
|
|
|
|
|
|
data, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_TABLE, Data: []byte(t.Name + "\x00")},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
message := netlink.Message{
|
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETSET),
|
2019-03-02 10:04:15 -06:00
|
|
|
Flags: netlink.Request | netlink.Acknowledge | netlink.Dump,
|
2019-01-28 22:56:40 -06:00
|
|
|
},
|
|
|
|
Data: append(extraHeader(uint8(t.Family), 0), data...),
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
|
|
|
|
return nil, fmt.Errorf("SendMessages: %v", err)
|
|
|
|
}
|
|
|
|
|
2022-10-02 09:01:48 -05:00
|
|
|
reply, err := receiveAckAware(conn, message.Header.Flags)
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
2024-08-11 03:00:48 -05:00
|
|
|
return nil, fmt.Errorf("receiveAckAware: %v", err)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
var sets []*Set
|
|
|
|
for _, msg := range reply {
|
|
|
|
s, err := setsFromMsg(msg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-14 04:06:46 -06:00
|
|
|
s.Table = &Table{Name: t.Name, Use: t.Use, Flags: t.Flags, Family: t.Family}
|
2019-01-28 22:56:40 -06:00
|
|
|
sets = append(sets, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
return sets, nil
|
|
|
|
}
|
|
|
|
|
2020-01-21 01:39:40 -06:00
|
|
|
// GetSetByName returns the set in the specified table if matching name is found.
|
|
|
|
func (cc *Conn) GetSetByName(t *Table, name string) (*Set, error) {
|
2022-05-09 06:25:29 -05:00
|
|
|
conn, closer, err := cc.netlinkConn()
|
2020-01-21 01:39:40 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-09 06:25:29 -05:00
|
|
|
defer func() { _ = closer() }()
|
2020-01-21 01:39:40 -06:00
|
|
|
|
|
|
|
data, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_SET_TABLE, Data: []byte(t.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_SET_NAME, Data: []byte(name + "\x00")},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
message := netlink.Message{
|
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETSET),
|
|
|
|
Flags: netlink.Request | netlink.Acknowledge,
|
|
|
|
},
|
|
|
|
Data: append(extraHeader(uint8(t.Family), 0), data...),
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
|
|
|
|
return nil, fmt.Errorf("SendMessages: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-10-02 09:01:48 -05:00
|
|
|
reply, err := receiveAckAware(conn, message.Header.Flags)
|
2020-01-21 01:39:40 -06:00
|
|
|
if err != nil {
|
2024-08-11 03:00:48 -05:00
|
|
|
return nil, fmt.Errorf("receiveAckAware: %w", err)
|
2020-01-21 01:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply) != 1 {
|
2024-08-11 03:00:48 -05:00
|
|
|
return nil, fmt.Errorf("receiveAckAware: expected to receive 1 message but got %d", len(reply))
|
2020-01-21 01:39:40 -06:00
|
|
|
}
|
|
|
|
rs, err := setsFromMsg(reply[0])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rs.Table = &Table{Name: t.Name, Use: t.Use, Flags: t.Flags, Family: t.Family}
|
|
|
|
|
|
|
|
return rs, nil
|
|
|
|
}
|
|
|
|
|
2019-01-28 22:56:40 -06:00
|
|
|
// GetSetElements returns the elements in the specified set.
|
|
|
|
func (cc *Conn) GetSetElements(s *Set) ([]SetElement, error) {
|
2022-05-09 06:25:29 -05:00
|
|
|
conn, closer, err := cc.netlinkConn()
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-09 06:25:29 -05:00
|
|
|
defer func() { _ = closer() }()
|
2019-01-28 22:56:40 -06:00
|
|
|
|
|
|
|
data, err := netlink.MarshalAttributes([]netlink.Attribute{
|
2025-03-03 05:55:40 -06:00
|
|
|
{Type: unix.NFTA_SET_ELEM_LIST_TABLE, Data: []byte(s.Table.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_SET_ELEM_LIST_SET, Data: []byte(s.Name + "\x00")},
|
2019-01-28 22:56:40 -06:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
message := netlink.Message{
|
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETSETELEM),
|
2019-03-02 10:04:15 -06:00
|
|
|
Flags: netlink.Request | netlink.Acknowledge | netlink.Dump,
|
2019-01-28 22:56:40 -06:00
|
|
|
},
|
|
|
|
Data: append(extraHeader(uint8(s.Table.Family), 0), data...),
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
|
|
|
|
return nil, fmt.Errorf("SendMessages: %v", err)
|
|
|
|
}
|
|
|
|
|
2022-10-02 09:01:48 -05:00
|
|
|
reply, err := receiveAckAware(conn, message.Header.Flags)
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
2024-08-11 03:00:48 -05:00
|
|
|
return nil, fmt.Errorf("receiveAckAware: %v", err)
|
2019-01-28 22:56:40 -06:00
|
|
|
}
|
|
|
|
var elems []SetElement
|
|
|
|
for _, msg := range reply {
|
2023-05-20 11:23:52 -05:00
|
|
|
s, err := elementsFromMsg(uint8(s.Table.Family), msg)
|
2019-01-28 22:56:40 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
elems = append(elems, s...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return elems, nil
|
|
|
|
}
|