mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement Byteorder operation (parsing, interpretation, evaluation, tests).
Uses binary.NativeEndian & binary.BigEndian for all byteorder operations. Also removes all magic number instances of 4 and 16 as register sizes. PiperOrigin-RevId: 674065001
This commit is contained in:
committed by
gVisor bot
parent
2da6847b5d
commit
548733574e
@@ -55,6 +55,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// TODO(b/345684870): Break this file up into multiple files by operation type.
|
||||
// Each operation should get its own file.
|
||||
// TODO(b/345684870): Make the nftables package thread-safe! Must be done before
|
||||
// the package is used in production.
|
||||
|
||||
@@ -593,6 +595,7 @@ var (
|
||||
_ operation = (*counter)(nil)
|
||||
_ operation = (*last)(nil)
|
||||
_ operation = (*route)(nil)
|
||||
_ operation = (*byteorder)(nil)
|
||||
)
|
||||
|
||||
// immediate is an operation that sets the data in a register.
|
||||
@@ -877,7 +880,7 @@ func newPayloadLoad(base payloadBase, offset, blen, dreg uint8) (*payloadLoad, e
|
||||
if isVerdictRegister(dreg) {
|
||||
return nil, fmt.Errorf("payload load operation cannot use verdict register as destination")
|
||||
}
|
||||
if blen > 16 || (blen > 4 && is4ByteRegister(dreg)) {
|
||||
if blen > linux.NFT_REG_SIZE || (blen > linux.NFT_REG32_SIZE && is4ByteRegister(dreg)) {
|
||||
return nil, fmt.Errorf("payload length %d is too long for destination register %d", blen, dreg)
|
||||
}
|
||||
if err := validatePayloadBase(base); err != nil {
|
||||
@@ -950,7 +953,7 @@ func newPayloadSet(base payloadBase, offset, blen, sreg, csumType, csumOffset, c
|
||||
if isVerdictRegister(sreg) {
|
||||
return nil, fmt.Errorf("payload set operation cannot use verdict register as destination")
|
||||
}
|
||||
if blen > 16 || (blen > 4 && is4ByteRegister(sreg)) {
|
||||
if blen > linux.NFT_REG_SIZE || (blen > linux.NFT_REG32_SIZE && is4ByteRegister(sreg)) {
|
||||
return nil, fmt.Errorf("payload length %d is too long for destination register %d", blen, sreg)
|
||||
}
|
||||
if err := validatePayloadBase(base); err != nil {
|
||||
@@ -1110,7 +1113,7 @@ func newBitwiseBool(sreg, dreg uint8, mask, xor []byte) (*bitwise, error) {
|
||||
if blen != len(xor) {
|
||||
return nil, fmt.Errorf("bitwise boolean operation mask and xor must be the same length")
|
||||
}
|
||||
if blen > 16 || (blen > 4 && (is4ByteRegister(sreg) || is4ByteRegister(dreg))) {
|
||||
if blen > linux.NFT_REG_SIZE || (blen > linux.NFT_REG32_SIZE && (is4ByteRegister(sreg) || is4ByteRegister(dreg))) {
|
||||
return nil, fmt.Errorf("bitwise operation length %d is too long for source register %d, destination register %d", blen, sreg, dreg)
|
||||
}
|
||||
return &bitwise{sreg: sreg, dreg: dreg, bop: linux.NFT_BITWISE_BOOL, blen: uint8(blen), mask: newBytesData(mask), xor: newBytesData(xor)}, nil
|
||||
@@ -1121,7 +1124,7 @@ func newBitwiseShift(sreg, dreg, blen uint8, shift uint32, right bool) (*bitwise
|
||||
if isVerdictRegister(sreg) || isVerdictRegister(dreg) {
|
||||
return nil, fmt.Errorf("bitwise operation cannot use verdict register as source or destination")
|
||||
}
|
||||
if blen > 16 || (blen > 4 && (is4ByteRegister(sreg) || is4ByteRegister(dreg))) {
|
||||
if blen > linux.NFT_REG_SIZE || (blen > linux.NFT_REG32_SIZE && (is4ByteRegister(sreg) || is4ByteRegister(dreg))) {
|
||||
return nil, fmt.Errorf("bitwise operation length %d is too long for source register %d, destination register %d", blen, sreg, dreg)
|
||||
}
|
||||
if shift >= bitshiftLimit {
|
||||
@@ -1389,6 +1392,128 @@ func (op route) evaluate(regs *registerSet, pkt *stack.PacketBuffer, rule *Rule)
|
||||
data.storeData(regs, op.dreg)
|
||||
}
|
||||
|
||||
// byteorder is an operation that performs byte order operations on a register.
|
||||
// Note: byteorder operations are not supported for the verdict register.
|
||||
type byteorder struct {
|
||||
sreg uint8 // Number of the source register.
|
||||
dreg uint8 // Number of the destination register.
|
||||
bop byteorderOp // Byte order operation to perform.
|
||||
blen uint8 // Number of total bytes to operate on.
|
||||
size uint8 // Granular size in bytes to operate on.
|
||||
}
|
||||
|
||||
// byteorderOp is the byte order operator for a byteorder operation.
|
||||
// Note: corresponds to enum nft_byteorder_ops from
|
||||
// include/uapi/linux/netfilter/nf_tables.h and uses the same constants.
|
||||
type byteorderOp int
|
||||
|
||||
// String for byteorderOp returns the string representation of the byteorder
|
||||
// operator.
|
||||
func (bop byteorderOp) String() string {
|
||||
switch bop {
|
||||
case linux.NFT_BYTEORDER_NTOH:
|
||||
return "network to host"
|
||||
case linux.NFT_BYTEORDER_HTON:
|
||||
return "host to network"
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown supported byteorder operator: %d", int(bop)))
|
||||
}
|
||||
}
|
||||
|
||||
// validateByteorderOp ensures the byteorder operator is valid.
|
||||
func validateByteorderOp(bop byteorderOp) error {
|
||||
switch bop {
|
||||
// Supported operators.
|
||||
case linux.NFT_BYTEORDER_NTOH, linux.NFT_BYTEORDER_HTON:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid byteorder operator: %d", int(bop))
|
||||
}
|
||||
}
|
||||
|
||||
// newByteorder creates a new byteorder operation.
|
||||
func newByteorder(sreg, dreg uint8, bop byteorderOp, blen, size uint8) (*byteorder, error) {
|
||||
if isVerdictRegister(sreg) || isVerdictRegister(dreg) {
|
||||
return nil, fmt.Errorf("byteorder operation cannot use verdict register")
|
||||
}
|
||||
if err := validateByteorderOp(bop); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blen > linux.NFT_REG_SIZE {
|
||||
return nil, fmt.Errorf("byteorder operation cannot have length greater than the max register size of %d bytes", linux.NFT_REG_SIZE)
|
||||
}
|
||||
if (is4ByteRegister(sreg) || is4ByteRegister(dreg)) && blen > linux.NFT_REG32_SIZE {
|
||||
return nil, fmt.Errorf("byteorder operation cannot have length greater than the max register size of %d bytes", linux.NFT_REG32_SIZE)
|
||||
}
|
||||
if size > blen {
|
||||
return nil, fmt.Errorf("byteorder operation cannot have size greater than length")
|
||||
}
|
||||
if size != 2 && size != 4 && size != 8 {
|
||||
return nil, fmt.Errorf("byteorder operation size must be 2, 4, or 8 bytes")
|
||||
}
|
||||
return &byteorder{sreg: sreg, dreg: dreg, bop: bop, blen: blen, size: size}, nil
|
||||
}
|
||||
|
||||
// evaluate for byteorder performs the byte order operation on the source
|
||||
// register and stores the result in the destination register.
|
||||
func (op byteorder) evaluate(regs *registerSet, pkt *stack.PacketBuffer, rule *Rule) {
|
||||
// Gets the source and destination registers.
|
||||
src := getRegisterBuffer(regs, op.sreg)
|
||||
dst := getRegisterBuffer(regs, op.dreg)
|
||||
|
||||
// Performs the byte order operations on the source register and stores the
|
||||
// result in as many bytes as are available in the destination register.
|
||||
switch op.size {
|
||||
case 8:
|
||||
switch op.bop {
|
||||
case linux.NFT_BYTEORDER_NTOH:
|
||||
for i := uint8(0); i < op.blen; i += 8 {
|
||||
networkNum := binary.BigEndian.Uint64(src[i : i+8])
|
||||
binary.NativeEndian.PutUint64(dst[i:], networkNum)
|
||||
}
|
||||
case linux.NFT_BYTEORDER_HTON:
|
||||
for i := uint8(0); i < op.blen; i += 8 {
|
||||
hostNum := binary.NativeEndian.Uint64(src[i : i+8])
|
||||
binary.BigEndian.PutUint64(dst[i:], hostNum)
|
||||
}
|
||||
}
|
||||
|
||||
case 4:
|
||||
switch op.bop {
|
||||
case linux.NFT_BYTEORDER_NTOH:
|
||||
for i := uint8(0); i < op.blen; i += 4 {
|
||||
networkNum := binary.BigEndian.Uint32(src[i : i+4])
|
||||
binary.NativeEndian.PutUint32(dst[i:], networkNum)
|
||||
}
|
||||
case linux.NFT_BYTEORDER_HTON:
|
||||
for i := uint8(0); i < op.blen; i += 4 {
|
||||
hostNum := binary.NativeEndian.Uint32(src[i : i+4])
|
||||
binary.BigEndian.PutUint32(dst[i:], hostNum)
|
||||
}
|
||||
}
|
||||
|
||||
case 2:
|
||||
switch op.bop {
|
||||
case linux.NFT_BYTEORDER_NTOH:
|
||||
for i := uint8(0); i < op.blen; i += 2 {
|
||||
networkNum := binary.BigEndian.Uint16(src[i : i+2])
|
||||
binary.NativeEndian.PutUint16(dst[i:], networkNum)
|
||||
}
|
||||
case linux.NFT_BYTEORDER_HTON:
|
||||
for i := uint8(0); i < op.blen; i += 2 {
|
||||
hostNum := binary.NativeEndian.Uint16(src[i : i+2])
|
||||
binary.BigEndian.PutUint16(dst[i:], hostNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Zeroes out excess bytes of the destination register.
|
||||
// This is done since comparison can be done in multiples of 4 bytes.
|
||||
if rem := op.blen % 4; rem != 0 {
|
||||
clear(dst[op.blen : op.blen+4-rem])
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Register and Register-Related Implementations.
|
||||
// Note: Registers are represented by type uint8 for the register number.
|
||||
@@ -1481,8 +1606,8 @@ func newBytesData(bytes []byte) bytesData {
|
||||
if len(bytes) == 0 {
|
||||
panic("bytes data cannot be empty")
|
||||
}
|
||||
if len(bytes) > 16 {
|
||||
panic(fmt.Errorf("bytes data cannot be more than 16 bytes: %d", len(bytes)))
|
||||
if len(bytes) > linux.NFT_REG_SIZE {
|
||||
panic(fmt.Errorf("bytes data cannot be more than %d bytes: %d", linux.NFT_REG_SIZE, len(bytes)))
|
||||
}
|
||||
return bytesData{data: bytes}
|
||||
}
|
||||
@@ -1509,8 +1634,8 @@ func (rd bytesData) validateRegister(reg uint8) error {
|
||||
if isVerdictRegister(reg) {
|
||||
return fmt.Errorf("data cannot be stored in verdict register")
|
||||
}
|
||||
if is4ByteRegister(reg) && len(rd.data) > 4 {
|
||||
return fmt.Errorf("%d-byte data cannot be stored in 4-byte register", len(rd.data))
|
||||
if is4ByteRegister(reg) && len(rd.data) > linux.NFT_REG32_SIZE {
|
||||
return fmt.Errorf("%d-byte data cannot be stored in %d-byte register", len(rd.data), linux.NFT_REG32_SIZE)
|
||||
}
|
||||
// 16-byte register can be used for any data (guaranteed to be <= 16 bytes)
|
||||
return nil
|
||||
|
||||
@@ -2570,6 +2570,249 @@ func TestEvaluateRoute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateByteorder tests that the Byteorder operation correctly performs
|
||||
// the appropriate byteorder operation on the source register data and stores
|
||||
// the result in the destination register.
|
||||
// Note: Relies on expected behavior of the Immediate and Comparison operation.
|
||||
func TestEvaluateByteorder(t *testing.T) {
|
||||
// Given a big endian and little endian byte slice of the same number, returns
|
||||
// the correct byte slice based on the host endianness.
|
||||
// Note: Uses enclosure so endianness doesn't need to be passed as an arg or
|
||||
// rechecked for every call.
|
||||
chooseOrder := func() func([]byte, []byte) []byte {
|
||||
hostBytes := binary.NativeEndian.AppendUint16(nil, 0x0102)
|
||||
isBigEndian := hostBytes[0] == 0x01
|
||||
return func(big, little []byte) []byte {
|
||||
if isBigEndian {
|
||||
return big
|
||||
}
|
||||
return little
|
||||
}
|
||||
}()
|
||||
// Like createChooseOrder but takes ints instead of byte slices.
|
||||
chooseOrderN := func(big, little, size int) []byte {
|
||||
return chooseOrder(numToBE(big, size), numToBE(little, size))
|
||||
}
|
||||
for _, test := range []struct {
|
||||
tname string
|
||||
op1 operation // Immediate operation to set source register.
|
||||
op2 operation // Byteorder operation to test.
|
||||
op3 operation // Comparison operation to validate result.
|
||||
}{
|
||||
// Size 2 tests (Lengths 2, 3, 4, 6, 8, 16)
|
||||
{
|
||||
tname: "ntoh size 2 len 2",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG32_01, newBytesData(numToBE(0x0102, 2))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG32_01, linux.NFT_REG32_01, linux.NFT_BYTEORDER_NTOH, 2, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG32_01, linux.NFT_CMP_EQ, chooseOrderN(0x0102, 0x0201, 2)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 2 len 2",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG32_01, newBytesData(numToBE(0x0102, 2))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG32_01, linux.NFT_REG_1, linux.NFT_BYTEORDER_HTON, 2, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, chooseOrderN(0x0102, 0x0201, 2)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 2 len 3",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG32_01, newBytesData(numToBE(0x010203, 3))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG32_01, linux.NFT_REG_1, linux.NFT_BYTEORDER_NTOH, 3, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, chooseOrderN(0x010203, 0x020100, 3)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 2 len 3",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x010203, 3))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG32_01, linux.NFT_BYTEORDER_HTON, 3, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG32_01, linux.NFT_CMP_EQ, chooseOrderN(0x010203, 0x020100, 3)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 2 len 4",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG32_10, newBytesData(numToBE(0x01020304, 4))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG32_10, linux.NFT_REG32_05, linux.NFT_BYTEORDER_NTOH, 4, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG32_05, linux.NFT_CMP_EQ, chooseOrderN(0x01020304, 0x02010403, 4)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 2 len 4",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_4, newBytesData(numToBE(0x01020304, 4))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_4, linux.NFT_REG32_09, linux.NFT_BYTEORDER_HTON, 4, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG32_09, linux.NFT_CMP_EQ, chooseOrderN(0x01020304, 0x02010403, 4)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 2 len 6",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x010203040506, 6))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_1, linux.NFT_BYTEORDER_NTOH, 6, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, chooseOrderN(0x010203040506, 0x020104030605, 6)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 2 len 6",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x010203040506, 6))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_1, linux.NFT_BYTEORDER_HTON, 6, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, chooseOrderN(0x010203040506, 0x020104030605, 6)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 2 len 8",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x0102030405060708, 8))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_4, linux.NFT_BYTEORDER_NTOH, 8, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrderN(0x0102030405060708, 0x0201040306050807, 8)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 2 len 8",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x0102030405060708, 8))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_4, linux.NFT_BYTEORDER_HTON, 8, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrderN(0x0102030405060708, 0x0201040306050807, 8)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 2 len 16",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_3, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_3, linux.NFT_REG_2, linux.NFT_BYTEORDER_NTOH, 16, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
|
||||
[]byte{0x02, 0x01, 0x04, 0x03, 0x06, 0x05, 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, 0x0e, 0x0d, 0x10, 0x0f})),
|
||||
},
|
||||
{
|
||||
tname: "hton size 2 len 16",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_3, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_3, linux.NFT_REG_2, linux.NFT_BYTEORDER_HTON, 16, 2),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
|
||||
[]byte{0x02, 0x01, 0x04, 0x03, 0x06, 0x05, 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, 0x0e, 0x0d, 0x10, 0x0f})),
|
||||
},
|
||||
// Size 4 tests (Lengths 4, 6, 8, 16)
|
||||
{
|
||||
tname: "ntoh size 4 len 4",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG32_05, newBytesData(numToBE(0x01020304, 4))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG32_05, linux.NFT_REG_2, linux.NFT_BYTEORDER_NTOH, 4, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrderN(0x01020304, 0x04030201, 4)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 4 len 4",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_4, newBytesData(numToBE(0x01020304, 4))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_4, linux.NFT_REG32_09, linux.NFT_BYTEORDER_HTON, 4, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG32_09, linux.NFT_CMP_EQ, chooseOrderN(0x01020304, 0x04030201, 4)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 4 len 6",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_4, newBytesData(numToBE(0x010203040506, 6))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_4, linux.NFT_REG_2, linux.NFT_BYTEORDER_NTOH, 6, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrderN(0x010203040506, 0x040302010000, 6)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 4 len 6",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_4, newBytesData(numToBE(0x010203040506, 6))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_4, linux.NFT_REG_2, linux.NFT_BYTEORDER_HTON, 6, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrderN(0x010203040506, 0x040302010000, 6)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 4 len 8",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x0102030405060708, 8))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_4, linux.NFT_BYTEORDER_NTOH, 8, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrderN(0x0102030405060708, 0x0403020108070605, 8)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 4 len 8",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x0102030405060708, 8))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_4, linux.NFT_BYTEORDER_HTON, 8, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrderN(0x0102030405060708, 0x0403020108070605, 8)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 4 len 16",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_3, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_3, linux.NFT_REG_2, linux.NFT_BYTEORDER_NTOH, 16, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
|
||||
[]byte{0x04, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, 0x10, 0x0f, 0x0e, 0x0d})),
|
||||
},
|
||||
{
|
||||
tname: "hton size 4 len 16",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_3, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_3, linux.NFT_REG_2, linux.NFT_BYTEORDER_HTON, 16, 4),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
|
||||
[]byte{0x04, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, 0x10, 0x0f, 0x0e, 0x0d})),
|
||||
},
|
||||
// Size 8 tests (Lengths 8, 12, 16)
|
||||
{
|
||||
tname: "ntoh size 8 len 8",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x0102030405060708, 8))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_4, linux.NFT_BYTEORDER_NTOH, 8, 8),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrderN(0x0102030405060708, 0x0807060504030201, 8)),
|
||||
},
|
||||
{
|
||||
tname: "hton size 8 len 8",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_1, newBytesData(numToBE(0x0102030405060708, 8))),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_4, linux.NFT_BYTEORDER_HTON, 8, 8),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrderN(0x0102030405060708, 0x0807060504030201, 8)),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 8 len 12",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_3, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_3, linux.NFT_REG_2, linux.NFT_BYTEORDER_NTOH, 12, 8),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c},
|
||||
[]byte{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00})),
|
||||
},
|
||||
{
|
||||
tname: "hton size 8 len 12",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_3, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_3, linux.NFT_REG_2, linux.NFT_BYTEORDER_HTON, 12, 8),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c},
|
||||
[]byte{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00})),
|
||||
},
|
||||
{
|
||||
tname: "ntoh size 8 len 16",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_4, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_4, linux.NFT_REG_4, linux.NFT_BYTEORDER_NTOH, 16, 8),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
|
||||
[]byte{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09})),
|
||||
},
|
||||
{
|
||||
tname: "hton size 8 len 16",
|
||||
op1: mustCreateImmediate(t, linux.NFT_REG_4, newBytesData([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10})),
|
||||
op2: mustCreateByteorder(t, linux.NFT_REG_4, linux.NFT_REG_4, linux.NFT_BYTEORDER_HTON, 16, 8),
|
||||
op3: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_EQ, chooseOrder([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
|
||||
[]byte{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09})),
|
||||
},
|
||||
} {
|
||||
t.Run(test.tname, func(t *testing.T) {
|
||||
// Sets up an NFTables object with a single table, chain, and rule.
|
||||
nf := newNFTablesStd()
|
||||
tab, err := nf.AddTable(arbitraryFamily, "test", "test table", false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for AddTable: %v", err)
|
||||
}
|
||||
bc, err := tab.AddChain("base_chain", nil, "test chain", false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for AddChain: %v", err)
|
||||
}
|
||||
bc.SetBaseChainInfo(arbitraryInfoPolicyAccept)
|
||||
rule := &Rule{}
|
||||
|
||||
// Adds testing operations.
|
||||
if test.op1 != nil {
|
||||
rule.addOperation(test.op1)
|
||||
}
|
||||
if test.op2 != nil {
|
||||
rule.addOperation(test.op2)
|
||||
}
|
||||
if test.op3 != nil {
|
||||
rule.addOperation(test.op3)
|
||||
}
|
||||
|
||||
// Adds drop operation. Will be final verdict if comparison is true.
|
||||
rule.addOperation(mustCreateImmediate(t, linux.NFT_REG_VERDICT, newVerdictData(Verdict{Code: VC(linux.NF_DROP)})))
|
||||
|
||||
// Registers the rule to the base chain.
|
||||
if err := bc.RegisterRule(rule, -1); err != nil {
|
||||
t.Fatalf("unexpected error for RegisterRule: %v", err)
|
||||
}
|
||||
|
||||
// Runs evaluation and checks verdict.
|
||||
pkt := makeArbitraryPacket(arbitraryReservedHeaderBytes)
|
||||
v, err := nf.EvaluateHook(arbitraryFamily, arbitraryHook, pkt)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for EvaluateHook: %v", err)
|
||||
}
|
||||
if v.Code != VC(linux.NF_DROP) {
|
||||
t.Fatalf("expected verdict Drop for true comparison, got %v", v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoopCheckOnRegisterAndUnregister tests the loop checking and accompanying
|
||||
// logic on registering and unregistering rules.
|
||||
func TestLoopCheckOnRegisterAndUnregister(t *testing.T) {
|
||||
@@ -3242,3 +3485,12 @@ func mustCreateRoute(t *testing.T, key routeKey, dreg uint8) *route {
|
||||
}
|
||||
return rt
|
||||
}
|
||||
|
||||
// mustCreateByteorder wraps the newByteorder function for brevity.
|
||||
func mustCreateByteorder(t *testing.T, sreg, dreg uint8, bop byteorderOp, blen, size uint8) *byteorder {
|
||||
order, err := newByteorder(sreg, dreg, bop, blen, size)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create byteorder: %v", err)
|
||||
}
|
||||
return order
|
||||
}
|
||||
|
||||
+102
-19
@@ -155,6 +155,8 @@ func InterpretOperation(line string, lnIdx int) (operation, error) {
|
||||
return InterpretCounter(line, lnIdx)
|
||||
case "rt":
|
||||
return InterpretRoute(line, lnIdx)
|
||||
case "byteorder":
|
||||
return InterpretByteorder(line, lnIdx)
|
||||
default:
|
||||
return nil, &SyntaxError{lnIdx, 1, fmt.Sprintf("unrecognized operation type: %s", tokens[1])}
|
||||
}
|
||||
@@ -309,7 +311,7 @@ func InterpretPayloadLoad(line string, lnIdx int) (operation, error) {
|
||||
tkIdx++
|
||||
|
||||
// Third token should be the length (in bytes) of the payload followed by 'b'.
|
||||
len, err := parsePayloadLength(tokens[tkIdx], lnIdx, tkIdx)
|
||||
blen, err := parseUint8PlusChar(tokens[tkIdx], 'b', lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -367,7 +369,7 @@ func InterpretPayloadLoad(line string, lnIdx int) (operation, error) {
|
||||
tkIdx++
|
||||
|
||||
// Create the operation with the specified arguments.
|
||||
pdload, err := newPayloadLoad(base, offset, len, reg)
|
||||
pdload, err := newPayloadLoad(base, offset, blen, reg)
|
||||
if err != nil {
|
||||
return nil, &LogicError{lnIdx, tkIdx, err}
|
||||
}
|
||||
@@ -424,7 +426,7 @@ func InterpretPayloadSet(line string, lnIdx int) (operation, error) {
|
||||
tkIdx++
|
||||
|
||||
// Sixth token should be the length (in bytes) of the payload followed by 'b'.
|
||||
len, err := parsePayloadLength(tokens[tkIdx], lnIdx, tkIdx)
|
||||
blen, err := parseUint8PlusChar(tokens[tkIdx], 'b', lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -502,7 +504,7 @@ func InterpretPayloadSet(line string, lnIdx int) (operation, error) {
|
||||
tkIdx++
|
||||
|
||||
// Create the operation with the specified arguments.
|
||||
pdset, err := newPayloadSet(base, offset, len, reg, csumType, csumOff, csumFlags)
|
||||
pdset, err := newPayloadSet(base, offset, blen, reg, csumType, csumOff, csumFlags)
|
||||
if err != nil {
|
||||
return nil, &LogicError{lnIdx, tkIdx, err}
|
||||
}
|
||||
@@ -733,6 +735,89 @@ func InterpretRoute(line string, lnIdx int) (operation, error) {
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
// InterpretByteorder creates a new Byteorder operation from the given string.
|
||||
func InterpretByteorder(line string, lnIdx int) (operation, error) {
|
||||
tokens := strings.Fields(line)
|
||||
|
||||
// Requires exactly 10 tokens:
|
||||
// "[", "byteorder", "reg", dreg index, "=", byteorder op+"(reg", sreg index+",", size+",", blen+")", "]".
|
||||
if len(tokens) != 10 {
|
||||
return nil, &SyntaxError{lnIdx, 0, fmt.Sprintf("incorrect number of tokens for route operation, should be exactly 10, got %d", len(tokens))}
|
||||
}
|
||||
|
||||
if err := checkOperationBrackets(tokens, lnIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tkIdx := 1
|
||||
|
||||
// First token should be "byteorder".
|
||||
if err := consumeToken("byteorder", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Second token should be "reg".
|
||||
if err := consumeToken("reg", tokens, lnIdx, tkIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Third token should be the uint8 representing destination register index.
|
||||
dreg, err := parseRegister(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 "ntoh(reg".
|
||||
var bop byteorderOp
|
||||
switch tokens[tkIdx] {
|
||||
case "ntoh(reg":
|
||||
bop = linux.NFT_BYTEORDER_NTOH
|
||||
case "hton(reg":
|
||||
bop = linux.NFT_BYTEORDER_HTON
|
||||
default:
|
||||
return nil, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("expected 'ntoh' or 'hton' keyword followed by '(reg' at token %d, got '%s'", tkIdx, tokens[tkIdx])}
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Sixth token should be the source register index followed by ','.
|
||||
sreg, err := parseUint8PlusChar(tokens[tkIdx], ',', lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Seventh token should be the size in bytes followed by ','.
|
||||
size, err := parseUint8PlusChar(tokens[tkIdx], ',', lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Eighth token should be the length in bytes followed by ')'.
|
||||
blen, err := parseUint8PlusChar(tokens[tkIdx], ')', lnIdx, tkIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkIdx++
|
||||
|
||||
// Create the operation with the specified arguments.
|
||||
order, err := newByteorder(dreg, sreg, bop, blen, size)
|
||||
if err != nil {
|
||||
return nil, &LogicError{lnIdx, tkIdx, err}
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Interpreter Helper Functions.
|
||||
//
|
||||
@@ -870,8 +955,9 @@ func parseHexData(tokens []string, lnIdx int, tkIdx int) (int, []byte, error) {
|
||||
slices.Reverse(bytes4)
|
||||
bytes = append(bytes, bytes4...)
|
||||
}
|
||||
if len(bytes) > 16 {
|
||||
return 0, nil, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("cannot have more than 16 bytes of hexadecimal data, got %d", len(bytes))}
|
||||
if len(bytes) > linux.NFT_REG_SIZE {
|
||||
return 0, nil, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("cannot have more than %d bytes of hexadecimal data, got %d",
|
||||
linux.NFT_REG_SIZE, len(bytes))}
|
||||
}
|
||||
return tkIdx, bytes, nil
|
||||
}
|
||||
@@ -896,22 +982,19 @@ 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)}
|
||||
// parseUint8PlusChar parses the a uint8 followed by the given character from
|
||||
// the given string.
|
||||
func parseUint8PlusChar(numString string, char byte, lnIdx int, tkIdx int) (uint8, error) {
|
||||
lastChar := numString[len(numString)-1]
|
||||
if lastChar != char {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("expected '%c' at the end of the uint8, got '%c'", char, lastChar)}
|
||||
}
|
||||
numStr := lenString[:len(lenString)-1]
|
||||
len, err := strconv.ParseUint(numStr, 10, 8)
|
||||
numStr := numString[:len(numString)-1]
|
||||
num, err := strconv.ParseUint(numStr, 10, 8)
|
||||
if err != nil {
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("could not parse uint8 payload length: '%s'", numStr)}
|
||||
return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("could not parse uint8: '%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
|
||||
return uint8(num), nil
|
||||
}
|
||||
|
||||
// parsePayloadBase parses the payload base header from the given string.
|
||||
|
||||
@@ -884,6 +884,79 @@ func checkRouteOp(tname string, expected operation, actual operation) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestInterpretByteorderOps tests interpretation of byteorder operations.
|
||||
// Note: Most byteorder operations have been revealed in the nft binary
|
||||
// debug output through bitshifts (which oddly do not use the native bitwise
|
||||
// operation lshift and rshift operators). Thus, many of following commands are
|
||||
// simply variations of lshift and rshift commands.
|
||||
func TestInterpretByteorderOps(t *testing.T) {
|
||||
for _, test := range []interpretOperationTestAction{
|
||||
{ // cmd: add rule ip tab ch tcp dport rshift 4 == 0x5678
|
||||
tname: "ntoh size 2 len 2",
|
||||
opStr: "[ byteorder reg 1 = ntoh(reg 1, 2, 2) ]",
|
||||
expected: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_1, linux.NFT_BYTEORDER_NTOH, 2, 2),
|
||||
},
|
||||
{ // cmd: add rule ip tab ch tcp dport rshift 7 == 0x345
|
||||
tname: "ntoh size 2 len 2 again",
|
||||
opStr: "[ byteorder reg 2 = ntoh(reg 11, 2, 2) ]",
|
||||
expected: mustCreateByteorder(t, linux.NFT_REG_2, linux.NFT_REG32_03, linux.NFT_BYTEORDER_NTOH, 2, 2),
|
||||
},
|
||||
{ // cmd: add rule ip filter input @th,24,24 rshift 1 0xabcdef
|
||||
tname: "ntoh size 2 len 3 again",
|
||||
opStr: "[ byteorder reg 15 = ntoh(reg 15, 2, 3) ]",
|
||||
expected: mustCreateByteorder(t, linux.NFT_REG32_07, linux.NFT_REG32_07, linux.NFT_BYTEORDER_NTOH, 3, 2),
|
||||
},
|
||||
{ // cmd: add rule ip filter input ether saddr lshift 1 == 01223456
|
||||
tname: "ntoh size 2 len 6",
|
||||
opStr: "[ byteorder reg 4 = ntoh(reg 3, 2, 6) ]",
|
||||
expected: mustCreateByteorder(t, linux.NFT_REG_4, linux.NFT_REG_3, linux.NFT_BYTEORDER_NTOH, 6, 2),
|
||||
},
|
||||
{ // cmd: add rule ip tab ch ip daddr rshift 20 99900
|
||||
tname: "ntoh size 4 len 4",
|
||||
opStr: "[ byteorder reg 9 = ntoh(reg 1, 4, 4) ]",
|
||||
expected: mustCreateByteorder(t, linux.NFT_REG32_01, linux.NFT_REG_1, linux.NFT_BYTEORDER_NTOH, 4, 4),
|
||||
},
|
||||
{ // cmd: add rule ip6 tab ch ip6 daddr rshift 90 603
|
||||
tname: "ntoh size 8 len 16",
|
||||
opStr: "[ byteorder reg 1 = ntoh(reg 1, 8, 16) ]",
|
||||
expected: mustCreateByteorder(t, linux.NFT_REG_1, linux.NFT_REG_1, linux.NFT_BYTEORDER_NTOH, 16, 8),
|
||||
},
|
||||
{ // cmd: add rule ip filter input meta length gt 1000 accept
|
||||
tname: "hton size 4 len 4",
|
||||
opStr: "[ byteorder reg 8 = hton(reg 1, 4, 4) ]",
|
||||
expected: mustCreateByteorder(t, linux.NFT_REG32_00, linux.NFT_REG_1, linux.NFT_BYTEORDER_HTON, 4, 4),
|
||||
},
|
||||
} {
|
||||
t.Run(test.tname, func(t *testing.T) { checkOp(t, test, checkByteorderOp) })
|
||||
}
|
||||
}
|
||||
|
||||
// checkByteorderOp checks that the given operation is a byteorder operation
|
||||
// and that it matches the expected byteorder operation.
|
||||
func checkByteorderOp(tname string, expected operation, actual operation) error {
|
||||
expectedOrder := expected.(*byteorder)
|
||||
order, ok := actual.(*byteorder)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected operation type to be Byteorder for %s, got %T", tname, actual)
|
||||
}
|
||||
if order.sreg != expectedOrder.sreg {
|
||||
return fmt.Errorf("expected source register to be %d for %s, got %d", expectedOrder.sreg, tname, order.sreg)
|
||||
}
|
||||
if order.dreg != expectedOrder.dreg {
|
||||
return fmt.Errorf("expected destination register to be %d for %s, got %d", expectedOrder.dreg, tname, order.dreg)
|
||||
}
|
||||
if order.bop != expectedOrder.bop {
|
||||
return fmt.Errorf("expected byteorder operator to be %v for %s, got %v", expectedOrder.bop, tname, order.bop)
|
||||
}
|
||||
if order.blen != expectedOrder.blen {
|
||||
return fmt.Errorf("expected byteorder length to be %d for %s, got %d", expectedOrder.blen, tname, order.blen)
|
||||
}
|
||||
if order.size != expectedOrder.size {
|
||||
return fmt.Errorf("expected byteorder size to be %d for %s, got %d", expectedOrder.size, tname, order.size)
|
||||
}
|
||||
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