mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement PayloadLoad operation (parsing, interpretation, evaluation, tests).
Tests payload load evaluation for all basic fields of IP, IPv6, & TCP headers. These are the main headers that are encountered for gVisor, so their implementation was prioritized. Loading for other packet headers should work as expected since raw/general payload loading works, but it has not been explicitly tested. These tests should be added later and a TODO has been left noting this. PiperOrigin-RevId: 668202252
This commit is contained in:
committed by
gVisor bot
parent
ac417d1200
commit
33dc9383dc
@@ -13,6 +13,7 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/stack",
|
||||
],
|
||||
)
|
||||
@@ -27,6 +28,8 @@ go_test(
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/buffer",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/stack",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
// returns the verdict issued by the ruleset and the packet modified by the
|
||||
// ruleset (if the verdict is not Drop).
|
||||
//
|
||||
// Inner Headers and Tunneling Headers are not supported.
|
||||
//
|
||||
// Finally, note that error checking for parameters/inputs is only guaranteed
|
||||
// for public functions. Most private functions are assumed to have
|
||||
// valid/prechecked inputs.
|
||||
@@ -43,6 +45,7 @@ import (
|
||||
"slices"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
@@ -563,6 +566,7 @@ type operation interface {
|
||||
var (
|
||||
_ operation = (*immediate)(nil)
|
||||
_ operation = (*comparison)(nil)
|
||||
_ operation = (*payloadLoad)(nil)
|
||||
)
|
||||
|
||||
// immediate is an operation that sets the data in a register.
|
||||
@@ -654,7 +658,7 @@ func (op comparison) evaluate(regs *registerSet, pkt *stack.PacketBuffer) {
|
||||
panic("comparison operation data is not BytesData")
|
||||
}
|
||||
// Gets the data from the source register.
|
||||
regBuf := bytesData.getRegisterBuffer(regs, op.sreg)
|
||||
regBuf := getRegisterBuffer(regs, op.sreg)
|
||||
|
||||
// Compares bytes from left to right for all bytes in the comparison data.
|
||||
dif := 0
|
||||
@@ -688,6 +692,104 @@ func (op comparison) evaluate(regs *registerSet, pkt *stack.PacketBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
// payloadLoad is an operation that loads data from the packet payload into a
|
||||
// register.
|
||||
// Note: payload operations are not supported for the verdict register.
|
||||
type payloadLoad struct {
|
||||
base payloadBase // Payload base to access data from.
|
||||
offset uint8 // Number of bytes to skip after the base.
|
||||
blen uint8 // Number of bytes to load.
|
||||
dreg uint8 // Number of the destination register.
|
||||
}
|
||||
|
||||
// payloadBase is the header that determines the location of the packet data.
|
||||
// Note: corresponds to enum nft_payload_bases from
|
||||
// include/uapi/linux/netfilter/nf_tables.h and uses the same constants.
|
||||
type payloadBase int
|
||||
|
||||
// String for NftPayloadBase returns the string representation of the payload
|
||||
// base.
|
||||
func (base payloadBase) String() string {
|
||||
switch base {
|
||||
case linux.NFT_PAYLOAD_LL_HEADER:
|
||||
return "Link Layer Header"
|
||||
case linux.NFT_PAYLOAD_NETWORK_HEADER:
|
||||
return "Network Header"
|
||||
case linux.NFT_PAYLOAD_TRANSPORT_HEADER:
|
||||
return "Transport Header"
|
||||
case linux.NFT_PAYLOAD_INNER_HEADER:
|
||||
panic("inner header not supported")
|
||||
case linux.NFT_PAYLOAD_TUN_HEADER:
|
||||
panic("tunneling header not supported")
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid payload base: %d", int(base)))
|
||||
}
|
||||
}
|
||||
|
||||
// validatePayloadBase ensures the payload base is valid.
|
||||
func validatePayloadBase(base payloadBase) error {
|
||||
switch base {
|
||||
case linux.NFT_PAYLOAD_LL_HEADER, linux.NFT_PAYLOAD_NETWORK_HEADER, linux.NFT_PAYLOAD_TRANSPORT_HEADER:
|
||||
return nil
|
||||
case linux.NFT_PAYLOAD_INNER_HEADER:
|
||||
return fmt.Errorf("inner header not supported")
|
||||
case linux.NFT_PAYLOAD_TUN_HEADER:
|
||||
return fmt.Errorf("tunneling header not supported")
|
||||
default:
|
||||
return fmt.Errorf("invalid payload base: %d", int(base))
|
||||
}
|
||||
}
|
||||
|
||||
// newPayloadLoad creates a new PayloadLoad operation.
|
||||
func newPayloadLoad(base payloadBase, offset, blen, dreg uint8) (*payloadLoad, error) {
|
||||
if dreg == linux.NFT_REG_VERDICT {
|
||||
return nil, fmt.Errorf("payload load operation cannot use verdict register as destination")
|
||||
}
|
||||
if blen > 16 || (blen > 4 && is4ByteRegister(dreg)) {
|
||||
return nil, fmt.Errorf("payload length %d is too long for destination register %d", blen, dreg)
|
||||
}
|
||||
if err := validatePayloadBase(base); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &payloadLoad{base: base, offset: offset, blen: blen, dreg: dreg}, nil
|
||||
}
|
||||
|
||||
// evaluate for PayloadLoad loads data from the packet payload into the
|
||||
// destination register.
|
||||
func (op payloadLoad) evaluate(regs *registerSet, pkt *stack.PacketBuffer) {
|
||||
// Gets the data from the packet payload.
|
||||
var payload []byte = nil
|
||||
switch op.base {
|
||||
case linux.NFT_PAYLOAD_LL_HEADER:
|
||||
// Note: Assumes Mac Header is present and valid for necessary use cases.
|
||||
// Also, doesn't check VLAN tag because VLAN isn't supported by gVisor.
|
||||
payload = pkt.LinkHeader().Slice()
|
||||
case linux.NFT_PAYLOAD_NETWORK_HEADER:
|
||||
// No checks done in linux kernel.
|
||||
payload = pkt.NetworkHeader().Slice()
|
||||
case linux.NFT_PAYLOAD_TRANSPORT_HEADER:
|
||||
// Note: Assumes L4 protocol is present and valid for necessary use cases.
|
||||
|
||||
// Errors if the packet is fragmented for IPv4 only.
|
||||
if net := pkt.NetworkHeader().Slice(); len(net) > 0 && pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
|
||||
if h := header.IPv4(net); h.More() || h.FragmentOffset() != 0 {
|
||||
break // packet is fragmented
|
||||
}
|
||||
}
|
||||
payload = pkt.TransportHeader().Slice()
|
||||
}
|
||||
|
||||
// Breaks if could not retrieve packet data.
|
||||
if payload == nil || len(payload) < int(op.offset+op.blen) {
|
||||
regs.verdict = Verdict{Code: VC(linux.NFT_BREAK)}
|
||||
return
|
||||
}
|
||||
|
||||
// Copies payload data into the specified register.
|
||||
data := newBytesData(payload[op.offset : op.offset+op.blen])
|
||||
data.storeData(regs, op.dreg)
|
||||
}
|
||||
|
||||
//
|
||||
// Register and Register-Related Implementations.
|
||||
// Note: Registers are represented by type uint8 for the register number.
|
||||
@@ -815,11 +917,12 @@ func (rd bytesData) validateRegister(reg uint8) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getRegisterBuffer is a helper function that gets the appropriate slice of
|
||||
// register data from the register set.
|
||||
// getRegisterBuffer is a helper function that gets the appropriate slice of the
|
||||
// register from the register set. The number of bytes returned is rounded up to
|
||||
// the nearest 4-byte multiple.
|
||||
// Note: does not support verdict data and assumes the register is valid for the
|
||||
// given data type.
|
||||
func (rd bytesData) getRegisterBuffer(regs *registerSet, reg uint8) []byte {
|
||||
func getRegisterBuffer(regs *registerSet, reg uint8) []byte {
|
||||
// Returns the entire 4-byte register
|
||||
if is4ByteRegister(reg) {
|
||||
start := (reg - linux.NFT_REG32_00) * linux.NFT_REG32_SIZE
|
||||
@@ -835,7 +938,7 @@ func (rd bytesData) storeData(regs *registerSet, reg uint8) {
|
||||
if err := rd.validateRegister(reg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
copy(rd.getRegisterBuffer(regs, reg), rd.data)
|
||||
copy(getRegisterBuffer(regs, reg), rd.data)
|
||||
}
|
||||
|
||||
// registerSet represents the set of registers supported by the kernel.
|
||||
@@ -846,7 +949,7 @@ type registerSet struct {
|
||||
data [registersByteSize]byte // 4 16-byte registers or 16 4-byte registers
|
||||
}
|
||||
|
||||
// newRegisterSet creates a new RegisterSet with the Continue Verdict and all
|
||||
// newRegisterSet creates a new registerSet with the Continue Verdict and all
|
||||
// registers set to 0.
|
||||
func newRegisterSet() registerSet {
|
||||
return registerSet{
|
||||
@@ -860,6 +963,10 @@ func (regs *registerSet) Verdict() Verdict {
|
||||
return regs.verdict
|
||||
}
|
||||
|
||||
func (regs *registerSet) String() string {
|
||||
return fmt.Sprintf("verdict: %v, data: %x", regs.verdict, regs.data)
|
||||
}
|
||||
|
||||
//
|
||||
// Verdict Implementation.
|
||||
// There are two types of verdicts:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,6 @@ package nftables
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -124,7 +123,7 @@ func InterpretRule(ruleString string) (*Rule, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// InterpretOperation creates a new Operation from the given operation string,
|
||||
// InterpretOperation creates a new operation from the given operation string,
|
||||
// assumed to be a single line of text surrounded in square brackets.
|
||||
// Note: the operation string should be generated as output from the official nft
|
||||
// binary (can be accomplished by using flag --debug=netlink).
|
||||
@@ -140,6 +139,8 @@ func InterpretOperation(line string, lnIdx int) (operation, error) {
|
||||
return InterpretImmediate(line, lnIdx)
|
||||
case "cmp":
|
||||
return InterpretComparison(line, lnIdx)
|
||||
case "payload":
|
||||
return InterpretPayloadLoad(line, lnIdx)
|
||||
default:
|
||||
return nil, &SyntaxError{lnIdx, 1, fmt.Sprintf("unrecognized operation type: %s", tokens[1])}
|
||||
}
|
||||
@@ -264,6 +265,102 @@ func InterpretComparison(line string, lnIdx int) (operation, error) {
|
||||
return cmp, nil
|
||||
}
|
||||
|
||||
// InterpretPayloadLoad creates a new PayloadLoad operation from the given
|
||||
// string.
|
||||
func InterpretPayloadLoad(line string, lnIdx int) (operation, error) {
|
||||
tokens := strings.Fields(line)
|
||||
|
||||
// Requires exactly 13 tokens:
|
||||
// "[", "payload", "load", len+"b", "@", payload base, "header", "+", offset, "=>", "reg", register index, "]".
|
||||
if len(tokens) != 13 {
|
||||
return nil, &SyntaxError{lnIdx, 0, fmt.Sprintf("incorrect number of tokens for payload load operation, should be exactly 13, got %d", len(tokens))}
|
||||
}
|
||||
|
||||
if err := checkOperationBrackets(tokens, lnIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tkIdx := 1
|
||||
|
||||
// First token should be "payload".
|
||||
if err := consumeToken("payload", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Second token should be "load".
|
||||
if err := consumeToken("load", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Third token should be the length (in bytes) of the payload followed by 'b'.
|
||||
len, err := parsePayloadLength(tokens[tkIdx], lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Fourth token should be "@".
|
||||
if err := consumeToken("@", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Fifth token should be the payload base header.
|
||||
base, err := parsePayloadBase(tokens[tkIdx], lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Sixth token should be "header".
|
||||
if err := consumeToken("header", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Seventh token should be "+".
|
||||
if err := consumeToken("+", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Eighth token should be the uint8 representing the offset.
|
||||
offset, err := parseUint8(tokens[tkIdx], "offset", lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Ninth token should be "=>".
|
||||
if err := consumeToken("=>", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Tenth token should be "reg".
|
||||
if err := consumeToken("reg", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Eleventh token should be the uint8 representing the register index.
|
||||
reg, err := parseRegister(tokens[tkIdx], lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Create the operation with the specified arguments.
|
||||
pdload, err := newPayloadLoad(base, offset, len, reg)
|
||||
if err != nil {
|
||||
return nil, &LogicError{lnIdx, tkIdx, err}
|
||||
}
|
||||
|
||||
return pdload, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Interpreter Helper Functions.
|
||||
//
|
||||
@@ -280,18 +377,25 @@ func checkOperationBrackets(tokens []string, lnIdx int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseUint8 parses the uint8 which should be supposed from the given string.
|
||||
func parseUint8(regString string, supposed string, lnIdx int, tkIdx int) (uint8, error) {
|
||||
v64, err := strconv.ParseUint(regString, 10, 8)
|
||||
if err != nil {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("could not parse uint8 %s: '%s'", supposed, regString)}
|
||||
}
|
||||
return uint8(v64), nil
|
||||
}
|
||||
|
||||
// parseRegister parses the register index from the given string.
|
||||
func parseRegister(regString string, lnIdx int, tkIdx int) (uint8, error) {
|
||||
reg64, err := strconv.ParseUint(regString, 10, 8)
|
||||
reg, err := parseUint8(regString, "register index", lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("could not parse uint8 register index: '%s'", regString)}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if reg64 > math.MaxUint8 || !isRegister(uint8(reg64)) {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid register index: %d", reg64)}
|
||||
if !isRegister(reg) {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid register index: %d", reg)}
|
||||
}
|
||||
|
||||
return uint8(reg64), nil
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
// parseRegisterData parses the register data from the given token and returns
|
||||
@@ -414,6 +518,39 @@ func parseCmpOp(copString string, lnIdx int, tkIdx int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// parsePayloadLength parses the payload length from the given string
|
||||
// expecting a unsigned 8-bit integer followed by 'b'.
|
||||
func parsePayloadLength(lenString string, lnIdx int, tkIdx int) (uint8, error) {
|
||||
lastChar := lenString[len(lenString)-1]
|
||||
if lastChar != 'b' {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("expected 'b' at the end of payload length, got '%c'", lastChar)}
|
||||
}
|
||||
numStr := lenString[:len(lenString)-1]
|
||||
len, err := strconv.ParseUint(numStr, 10, 8)
|
||||
if err != nil {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("could not parse uint8 payload length: '%s'", numStr)}
|
||||
}
|
||||
if len > 16 {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("payload length must be <= 16 bytes, got %d", len)}
|
||||
}
|
||||
return uint8(len), nil
|
||||
}
|
||||
|
||||
// parsePayloadBase parses the payload base header from the given string.
|
||||
func parsePayloadBase(baseString string, lnIdx int, tkIdx int) (payloadBase, error) {
|
||||
switch baseString {
|
||||
case "link":
|
||||
return linux.NFT_PAYLOAD_LL_HEADER, nil
|
||||
case "network":
|
||||
return linux.NFT_PAYLOAD_NETWORK_HEADER, nil
|
||||
case "transport":
|
||||
return linux.NFT_PAYLOAD_TRANSPORT_HEADER, nil
|
||||
// Inner and Tunnel Headers cannot be specified in payload load operation.
|
||||
default:
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid payload base: '%s'", baseString)}
|
||||
}
|
||||
}
|
||||
|
||||
// consumeToken is a helper function that checks if the token at the given index
|
||||
// matches the expected string, returning a SyntaxError if not.
|
||||
func consumeToken(expected string, tokens []string, lnIdx int, tkIdx int) error {
|
||||
|
||||
@@ -52,7 +52,7 @@ func checkOp(t *testing.T, test interpretOperationTestAction, checkFunc func(str
|
||||
}
|
||||
}
|
||||
|
||||
// TestInterpretImmediateOps tests the interpretation of immediate operations.
|
||||
// TestInterpretImmediateOps tests interpretation of immediate operations.
|
||||
func TestInterpretImmediateOps(t *testing.T) {
|
||||
for _, test := range []interpretOperationTestAction{
|
||||
{
|
||||
@@ -162,7 +162,7 @@ func checkImmediateOp(tname string, expected operation, actual operation) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestInterpretComparisonOps tests the interpretation of comparison operations.
|
||||
// TestInterpretComparisonOps tests interpretation of comparison operations.
|
||||
func TestInterpretComparisonOps(t *testing.T) {
|
||||
for _, test := range []interpretOperationTestAction{
|
||||
{
|
||||
@@ -375,6 +375,126 @@ func checkComparisonOp(tname string, expected operation, actual operation) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestInterpretPayloadLoadOps tests interpretation of payload load operations.
|
||||
// Most operations are direct output of nft binary commands. All stated commands
|
||||
// should be preceded by nft --debug=netlink to generate matching operations.
|
||||
func TestInterpretPayloadLoadOps(t *testing.T) {
|
||||
for _, test := range []interpretOperationTestAction{
|
||||
{
|
||||
tname: "load bytes into verdict register",
|
||||
opStr: "[ payload load 2b @ transport header + 0 => reg 0 ]",
|
||||
expected: nil,
|
||||
},
|
||||
// cmd: add rule ip6 ip tab ch tcp flags syn counter accept
|
||||
{
|
||||
tname: "load 1 byte into 4-byte register",
|
||||
opStr: "[ payload load 1b @ transport header + 13 => reg 9 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 13, 1, linux.NFT_REG32_01),
|
||||
},
|
||||
{
|
||||
tname: "load 1 byte into 16-byte register",
|
||||
opStr: "[ payload load 1b @ transport header + 13 => reg 1 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 13, 1, linux.NFT_REG_1),
|
||||
},
|
||||
// cmd: add rule ip tab ch tcp sport 80 counter accept
|
||||
{
|
||||
tname: "load 2 bytes into 4-byte register no offset",
|
||||
opStr: "[ payload load 2b @ transport header + 0 => reg 8 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 0, 2, linux.NFT_REG32_00),
|
||||
},
|
||||
{
|
||||
tname: "load 2 bytes into 16-byte register no offset",
|
||||
opStr: "[ payload load 2b @ transport header + 0 => reg 1 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 0, 2, linux.NFT_REG_1),
|
||||
},
|
||||
// cmd: add rule ip tab ch tcp dport 12345 counter accept
|
||||
{
|
||||
tname: "load 2 bytes into 4-byte register with offset",
|
||||
opStr: "[ payload load 2b @ transport header + 2 => reg 9 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, linux.NFT_REG32_01),
|
||||
},
|
||||
{
|
||||
tname: "load 2 bytes into 16-byte register with offset",
|
||||
opStr: "[ payload load 2b @ transport header + 2 => reg 1 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, linux.NFT_REG_1),
|
||||
},
|
||||
// cmd: add rule ip tab ch @th,24,24 0xabcdef counter accept
|
||||
{
|
||||
tname: "load 3 bytes into 4-byte register",
|
||||
opStr: "[ payload load 3b @ transport header + 3 => reg 10 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 3, 3, linux.NFT_REG32_02),
|
||||
},
|
||||
{
|
||||
tname: "load 3 bytes into 16-byte register",
|
||||
opStr: "[ payload load 3b @ transport header + 3 => reg 2 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_TRANSPORT_HEADER, 3, 3, linux.NFT_REG_2),
|
||||
},
|
||||
// cmd: add rule ip tab ch ip daddr 192.168.1.1 counter accept
|
||||
{
|
||||
tname: "load 4 bytes into 4-byte register",
|
||||
opStr: "[ payload load 4b @ network header + 16 => reg 12 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_NETWORK_HEADER, 16, 4, linux.NFT_REG32_04),
|
||||
},
|
||||
{
|
||||
tname: "load 4 bytes into 16-byte register",
|
||||
opStr: "[ payload load 4b @ network header + 16 => reg 1 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_NETWORK_HEADER, 16, 4, linux.NFT_REG_1),
|
||||
},
|
||||
// cmd: add rule ip tab ch ether saddr 01:23:45:67:89:ab counter drop
|
||||
{
|
||||
tname: "load 6 bytes into 4-byte register",
|
||||
opStr: "[ payload load 6b @ link header + 6 => reg 13 ]",
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
tname: "load 6 bytes into 16-byte register",
|
||||
opStr: "[ payload load 6b @ link header + 6 => reg 3 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_LL_HEADER, 6, 6, linux.NFT_REG_3),
|
||||
},
|
||||
// cmd: add rule ip6 tab ch ip6 saddr 2001:db8::2 counter accept
|
||||
{
|
||||
tname: "load 16 bytes into 4-byte register",
|
||||
opStr: "[ payload load 16b @ network header + 8 => reg 10 ]",
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
tname: "load 16 bytes into 16-byte register",
|
||||
opStr: "[ payload load 16b @ network header + 8 => reg 1 ]",
|
||||
expected: mustCreatePayloadLoad(t, linux.NFT_PAYLOAD_NETWORK_HEADER, 8, 16, linux.NFT_REG_1),
|
||||
},
|
||||
{
|
||||
tname: "load >16 bytes into 16-byte register",
|
||||
opStr: "[ payload load 20b @ network header + 16 => reg 1 ]",
|
||||
expected: nil,
|
||||
},
|
||||
} {
|
||||
t.Run(test.tname, func(t *testing.T) { checkOp(t, test, checkPayloadLoadOp) })
|
||||
}
|
||||
}
|
||||
|
||||
// checkPayloadLoadOp checks that the given operation is a payload load
|
||||
// operation and that it matches the expected payload load operation.
|
||||
func checkPayloadLoadOp(tname string, expected operation, actual operation) error {
|
||||
expectedPdLoad := expected.(*payloadLoad)
|
||||
pdload, ok := actual.(*payloadLoad)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected operation type to be PayloadLoad for %s, got %T", tname, actual)
|
||||
}
|
||||
if pdload.base != expectedPdLoad.base {
|
||||
return fmt.Errorf("expected payload base to be %v for %s, got %v", expectedPdLoad.base, tname, pdload.base)
|
||||
}
|
||||
if pdload.offset != expectedPdLoad.offset {
|
||||
return fmt.Errorf("expected offset to be %d for %s, got %d", expectedPdLoad.offset, tname, pdload.offset)
|
||||
}
|
||||
if pdload.blen != expectedPdLoad.blen {
|
||||
return fmt.Errorf("expected length to be %d for %s, got %d", expectedPdLoad.blen, tname, pdload.blen)
|
||||
}
|
||||
if pdload.dreg != expectedPdLoad.dreg {
|
||||
return fmt.Errorf("expected destination register to be %d for %s, got %d", expectedPdLoad.dreg, tname, pdload.dreg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestInterpretRule tests the interpretation of basic and general rules as a
|
||||
// list of operations.
|
||||
func TestInterpretRule(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user