diff --git a/pkg/tcpip/nftables/nftables.go b/pkg/tcpip/nftables/nftables.go index c4debd2f3..33f39a461 100644 --- a/pkg/tcpip/nftables/nftables.go +++ b/pkg/tcpip/nftables/nftables.go @@ -650,13 +650,14 @@ func NewComparison(sreg uint8, op int, data RegisterData) (*Comparison, error) { // evaluate for Comparison compares the data in the source register to the given // data and breaks from the rule if the comparison is false. func (op Comparison) evaluate(regs *RegisterSet, pkt *stack.PacketBuffer) { - // Gets the data from the source register. - regBuf := getRegisterData(regs, op.sreg, op.data.Type()) // Gets the data to compare to. bytesData, ok := op.data.(BytesData) if !ok { panic("comparison operation data is not BytesData") } + // Gets the data from the source register. + regBuf := bytesData.getRegisterBuffer(regs, op.sreg) + // Compares from left to right in 4-byte chunks starting with the rightmost // byte of every 4-byte chunk since the data is little endian. // For example, 16-byte IPv6 address 2001:000a:130f:0000:0000:09c0:876a:130b @@ -715,24 +716,8 @@ func isRegister(reg uint8) bool { return isVerdictRegister(reg) || is16ByteRegister(reg) || is4ByteRegister(reg) } -// RegisterDataType is the type of data to be set in a register. -type RegisterDataType int - -const ( - // DataVerdict represents a verdict to be stored in a register. - DataVerdict RegisterDataType = iota - // Data4Bytes represents 4 bytes of data to be stored in a register. - Data4Bytes - // Data16Bytes represents 16 bytes of data to be stored in a register. - Data16Bytes -) - // RegisterData represents the data to be set in a register. type RegisterData interface { - - // Type returns the register data type. - Type() RegisterDataType - // String returns a string representation of the register data. String() string @@ -759,9 +744,6 @@ type VerdictData struct { // NewVerdictData creates a RegisterData for a verdict. func NewVerdictData(verdict Verdict) RegisterData { return VerdictData{data: verdict} } -// Type returns the DataVerdict register data type for VerdictData. -func (rd VerdictData) Type() RegisterDataType { return DataVerdict } - // String returns a string representation of the verdict data. func (rd VerdictData) String() string { return rd.data.String() @@ -769,13 +751,20 @@ func (rd VerdictData) String() string { // Equal compares the verdict data to another RegisterData object. func (rd VerdictData) Equal(other RegisterData) bool { - return other != nil && other.Type() == DataVerdict && rd.data == other.(VerdictData).data + if other == nil { + return false + } + otherVD, ok := other.(VerdictData) + if !ok { + return false + } + return rd.data == otherVD.data } // ValidateRegister ensures the register is compatible with VerdictData. func (rd VerdictData) ValidateRegister(reg uint8) error { if !isVerdictRegister(reg) { - return fmt.Errorf("verdict data type is only valid for register 0") + return fmt.Errorf("verdict can only be stored in verdict register") } return nil } @@ -788,28 +777,19 @@ func (rd VerdictData) StoreData(regs *RegisterSet, reg uint8) { regs.verdict = rd.data } -// BytesData represents a 4 or 16 bytes of data to be stored in a register. +// BytesData represents data in 4-byte chunks to be stored in a register. type BytesData struct { data []byte } -// NewBytesData creates a RegisterData for 4 or 16 bytes of data. +// NewBytesData creates a RegisterData for 4, 8, 12, or 16 bytes of data. func NewBytesData(bytes []byte) RegisterData { - if len(bytes) != 4 && len(bytes) != 16 { + if len(bytes)%4 != 0 || len(bytes) > 16 { panic(fmt.Errorf("invalid byte data length: %d", len(bytes))) } return BytesData{data: bytes} } -// Type returns the Data4Bytes or Data16Bytes register data type depending -// on the length of the BytesData. -func (rd BytesData) Type() RegisterDataType { - if len(rd.data) == 4 { - return Data4Bytes - } - return Data16Bytes -} - // String returns a string representation of the bytes data. func (rd BytesData) String() string { return fmt.Sprintf("%x", rd.data) @@ -820,54 +800,47 @@ func (rd BytesData) Equal(other RegisterData) bool { if other == nil { return false } - if other.Type() != rd.Type() { + otherBD, ok := other.(BytesData) + if !ok { return false } - return slices.Equal(rd.data, other.(BytesData).data) + return slices.Equal(rd.data, otherBD.data) } -// ValidateRegister ensures the register is compatible with Bytes4Data. +// ValidateRegister ensures the register is compatible with this bytes data. func (rd BytesData) ValidateRegister(reg uint8) error { - if rd.Type() == Data4Bytes { - if !is4ByteRegister(reg) && !is16ByteRegister(reg) { - return fmt.Errorf("4-byte data type is only valid for 4-byte and 16-byte registers") - } - } else { - if !is16ByteRegister(reg) { - return fmt.Errorf("16-byte data type is only valid for 16-byte registers") - } + 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)) + } + // 16-byte register can be used for any data (guaranteed to be <= 16 bytes) return nil } -// getRegisterData is a helper function that gets the appropriate slice of +// getRegisterBuffer is a helper function that gets the appropriate slice of // register data from the register set. // Note: does not support verdict data and assumes the register is valid for the // given data type. -func getRegisterData(regs *RegisterSet, reg uint8, dataType RegisterDataType) []byte { - // 4-byte data in a 4-byte register. +func (rd BytesData) getRegisterBuffer(regs *RegisterSet, reg uint8) []byte { + // The entire 4-byte register (data must be exactly 4 bytes) if is4ByteRegister(reg) { start := (reg - linux.NFT_REG32_00) * linux.NFT_REG32_SIZE return regs.data[start : start+linux.NFT_REG32_SIZE] } - // 16-byte data in a 16-byte register. - if dataType == Data16Bytes { - start := (reg - linux.NFT_REG_1) * linux.NFT_REG_SIZE - return regs.data[start : start+linux.NFT_REG_SIZE] - } - // 4-byte data in a 16-byte register + // The appropriate (mod 4)-byte data in a 16-byte register // Leaves excess space on the left (bc the data is little endian). - start := (reg-linux.NFT_REG_1)*linux.NFT_REG_SIZE + linux.NFT_REG_SIZE - linux.NFT_REG32_SIZE - return regs.data[start : start+linux.NFT_REG32_SIZE] + end := (int(reg)-linux.NFT_REG_1)*linux.NFT_REG_SIZE + linux.NFT_REG_SIZE + return regs.data[end-len(rd.data) : end] } -// StoreData sets the data in the destination register to the uint32. +// StoreData sets the data in the destination register to the bytes data. func (rd BytesData) StoreData(regs *RegisterSet, reg uint8) { if err := rd.ValidateRegister(reg); err != nil { panic(err) } - regBuf := getRegisterData(regs, reg, rd.Type()) - copy(regBuf, rd.data) + copy(rd.getRegisterBuffer(regs, reg), rd.data) } // RegisterSet represents the set of registers supported by the kernel. @@ -1572,10 +1545,11 @@ func isJumpOrGotoOperation(op Operation) (bool, string) { if !ok { return false, "" } - if imm.data.Type() != DataVerdict { + verdictData, ok := imm.data.(VerdictData) + if !ok { return false, "" } - verdict := imm.data.(VerdictData).data + verdict := verdictData.data if verdict.Code != VC(linux.NFT_JUMP) && verdict.Code != VC(linux.NFT_GOTO) { return false, "" } diff --git a/pkg/tcpip/nftables/nftables_test.go b/pkg/tcpip/nftables/nftables_test.go index d240a5097..271ddaa01 100644 --- a/pkg/tcpip/nftables/nftables_test.go +++ b/pkg/tcpip/nftables/nftables_test.go @@ -300,6 +300,7 @@ func TestEvaluateComparison(t *testing.T) { op2 Operation // will be nil if unused res bool // should be true if we reach end of the rule (no breaks) }{ + // 4-byte data comparisons, alternates between 4-byte and 16-byte registers. { tname: "compare register == 4-byte data, true", op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0, 0, 0, 0})), @@ -396,6 +397,201 @@ func TestEvaluateComparison(t *testing.T) { op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0})), res: false, }, + // 8-byte data comparisons. + { + tname: "compare register == 8-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register == 8-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register != 8-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{1, 7, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 98, 0, 56, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register != 8-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{1, 98, 0, 56, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 98, 0, 56, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register < 8-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{29, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LT, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register < 8-byte data, false eq", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_LT, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register < 8-byte data, false gt", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{200, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register > 8-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0, 0, 0, 1, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GT, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register > 8-byte data, false eq", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_GT, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register > 8-byte data, false lt", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{28, 76, 230, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register <= 8-byte data, true lt", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{29, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LTE, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register <= 8-byte data, true eq", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register <= 8-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{200, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LTE, NewBytesData([]byte{100, 0, 0, 0, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register >= 8-byte data, true gt", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0, 0, 0, 1, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register >= 8-byte data, true eq", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register >= 8-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{28, 76, 230, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GTE, NewBytesData([]byte{29, 76, 230, 0, 0, 0, 0, 0})), + res: false, + }, + // 12-byte data comparisons. + { + tname: "compare register == 12-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare register == 12-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_EQ, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare register != 12-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_NEQ, NewBytesData([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11})), + res: true, + }, + { + tname: "compare register != 12-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})), + res: false, + }, + { + tname: "compare register < 12-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: true, + }, + { + tname: "compare register < 12-byte data, false eq", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_LT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: false, + }, + { + tname: "compare register < 12-byte data, false gt", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x21, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: false, + }, + { + tname: "compare register > 12-byte data, true", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x21, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: true, + }, + { + tname: "compare register > 12-byte data, false eq", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_GT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: false, + }, + { + tname: "compare register > 12-byte data, false lt", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: false, + }, + { + tname: "compare register <= 12-byte data, true lt", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: true, + }, + { + tname: "compare register <= 12-byte data, true eq", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: true, + }, + { + tname: "compare register <= 12-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0xaa, 0xaa, 0xaa, 0x20, 0xaa, 0xaa, 0xaa, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: false, + }, + { + tname: "compare register >= 12-byte data, true gt", + op1: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0xaa, 0xaa, 0xaa, 0x20, 0xaa, 0xaa, 0xaa, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: true, + }, + { + tname: "compare register >= 12-byte data, true eq", + op1: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0xab, 0xbc, 0xcd, 0xde, 0xef, 0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56})), + op2: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0xab, 0xbc, 0xcd, 0xde, 0xef, 0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56})), + res: true, + }, + { + tname: "compare register >= 12-byte data, false", + op1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x19, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + res: false, + }, + // 16-byte data comparisons. { tname: "compare register == 16-byte data, true", op1: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})), @@ -492,6 +688,7 @@ func TestEvaluateComparison(t *testing.T) { op2: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00, 0x0b, 0x13, 0x6a, 0x87})), res: false, }, + // Empty register comparisons. { tname: "compare empty 4-byte register, true", op1: mustCreateComparison(t, linux.NFT_REG32_10, linux.NFT_CMP_EQ, NewBytesData([]byte{0, 0, 0, 0})), @@ -502,6 +699,26 @@ func TestEvaluateComparison(t *testing.T) { op1: mustCreateComparison(t, linux.NFT_REG32_11, linux.NFT_CMP_EQ, NewBytesData([]byte{1, 0, 0, 0})), res: false, }, + { + tname: "compare empty 8-byte register, true", + op1: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_NEQ, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0})), + res: true, + }, + { + tname: "compare empty 8-byte register, false", + op1: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_GT, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0})), + res: false, + }, + { + tname: "compare empty 12-byte register, true", + op1: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LTE, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0, 8, 9, 10, 11})), + res: true, + }, + { + tname: "compare empty 12-byte register, false", + op1: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_NEQ, NewBytesData([]byte{0, 0, 0, 0, 0, 0, 0, 0})), + res: false, + }, { tname: "compare empty 16-byte register, true", op1: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_LT, NewBytesData([]byte{1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})), diff --git a/pkg/tcpip/nftables/nftinterp.go b/pkg/tcpip/nftables/nftinterp.go index 8e3727659..bc53589ac 100644 --- a/pkg/tcpip/nftables/nftinterp.go +++ b/pkg/tcpip/nftables/nftinterp.go @@ -306,14 +306,14 @@ func parseRegisterData(reg uint8, tokens []string, lnIdx int, tkIdx int) (int, R } return nextIdx, NewVerdictData(verdict), nil } - // Handles hex data (4- or 16-byte). + // Handles hex data (4-, 8-, 12-, or 16-byte). if len(tokens[tkIdx]) > 1 && tokens[tkIdx][:2] == "0x" { nextIdx, data, err := parseHexData(tokens, lnIdx, tkIdx) if err != nil { return 0, nil, err } - // Validates the register data type. 4-byte data is valid for both 4- and - // 16-byte registers, but 16-byte data is only valid for 16-byte registers. + // 4-byte data is only valid for 4-byte register. Any byte data can be + // stored in 16-byte registerValidates the register data type. if err := data.ValidateRegister(reg); err != nil { return 0, nil, &LogicError{lnIdx, tkIdx, err} } @@ -385,10 +385,10 @@ func parseHexData(tokens []string, lnIdx int, tkIdx int) (int, RegisterData, err } bytes = append(bytes, bytes4...) } - if len(bytes) == 4 || len(bytes) == 16 { - return tkIdx, NewBytesData(bytes), nil + 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))} } - return 0, nil, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("incorrect number of bytes for hexadecimal data, should be 4 or 16, got %d", len(bytes))} + return tkIdx, NewBytesData(bytes), nil } // parseCmpOp parses the int representing the cmpOp from the given string. diff --git a/pkg/tcpip/nftables/nftinterp_test.go b/pkg/tcpip/nftables/nftinterp_test.go index 1edeb17c8..77332ddf7 100644 --- a/pkg/tcpip/nftables/nftinterp_test.go +++ b/pkg/tcpip/nftables/nftinterp_test.go @@ -21,8 +21,6 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" ) -// interpretOperationTestAction is a generic action for testing the -// interpretation of an operation. type interpretOperationTestAction struct { tname string opStr string @@ -107,10 +105,30 @@ func TestInterpretImmediateOps(t *testing.T) { opStr: "[ immediate reg 2 jump -> next_chain ]", expected: nil, }, + { + tname: "16-byte register with 2-byte data", + opStr: "[ immediate reg 2 0xb80d ]", + expected: nil, // can handle 2-byte data but must be padded to 4-bytes + }, { tname: "16-byte register with 4-byte data", - opStr: "[ immediate reg 3 0x0201a8c0 ]", - expected: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0x02, 0x01, 0xa8, 0xc0})), + opStr: "[ immediate reg 1 0x0201a8c0 ]", + expected: mustCreateImmediate(t, linux.NFT_REG_1, NewBytesData([]byte{0x02, 0x01, 0xa8, 0xc0})), + }, + { + tname: "16-byte register with 6-byte data", + opStr: "[ immediate reg 2 0xb80d0120 0x0050 ]", + expected: nil, // can handle 6-byte data but must be padded to 8-bytes + }, + { + tname: "16-byte register with 8-byte data", + opStr: "[ immediate reg 2 0xb80d0120 0x00000050 ]", + expected: mustCreateImmediate(t, linux.NFT_REG_2, NewBytesData([]byte{0xb8, 0x0d, 0x01, 0x20, 0x00, 0x00, 0x00, 0x50})), + }, + { + tname: "16-byte register with 12-byte data", + opStr: "[ immediate reg 3 0xb80d0120 0x00000050 0xb80d0120 ]", + expected: mustCreateImmediate(t, linux.NFT_REG_3, NewBytesData([]byte{0xb8, 0x0d, 0x01, 0x20, 0x00, 0x00, 0x00, 0x50, 0xb8, 0x0d, 0x01, 0x20})), }, { tname: "16-byte register with 16-byte data", @@ -118,9 +136,9 @@ func TestInterpretImmediateOps(t *testing.T) { expected: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0xb8, 0x0d, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00})), }, { - tname: "16-byte register with 8-byte data", - opStr: "[ immediate reg 4 0xb80d0120 0x00000050 ]", - expected: nil, + tname: "16-byte register with uneven bytes data", + opStr: "[ immediate reg 2 0xb80d0120 0x0f60a0 ]", + expected: nil, // can handle uneven but must be padded to 4-byte multiple }, { tname: "4-byte register with verdict data", @@ -132,6 +150,11 @@ func TestInterpretImmediateOps(t *testing.T) { opStr: "[ immediate reg 9 goto -> next_chain ]", expected: nil, }, + { + tname: "4-byte register with 2-byte data", + opStr: "[ immediate reg 8 0xb80d ]", + expected: nil, // can handle 2-byte data but must be padded to 4-bytes + }, { tname: "4-byte register with 4-byte data", opStr: "[ immediate reg 10 0x0201a8c0 ]", @@ -173,72 +196,182 @@ func TestInterpretComparisonOps(t *testing.T) { expected: nil, }, { - tname: "register == 4-byte data", - opStr: "[ cmp eq reg 1 0x0302010a ]", - expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a})), + tname: "verdict register with 8-byte data comparison", + opStr: "[ cmp lt reg 0 0xb80d0120 0x02000000 ]", + expected: nil, }, { - tname: "register != 4-byte data", - opStr: "[ cmp neq reg 2 0x00000064 ]", - expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64})), - }, - { - tname: "register < 4-byte data", - opStr: "[ cmp lt reg 3 0x00000000 ]", - expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00})), - }, - { - tname: "register <= 4-byte data", - opStr: "[ cmp lte reg 4 0x00000164 ]", - expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64})), - }, - { - tname: "register > 4-byte data", - opStr: "[ cmp gt reg 8 0xe8030000 ]", - expected: mustCreateComparison(t, linux.NFT_REG32_00, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00})), - }, - { - tname: "register >= 4-byte data", - opStr: "[ cmp gte reg 9 0xc02b0000 ]", - expected: mustCreateComparison(t, linux.NFT_REG32_01, linux.NFT_CMP_GTE, NewBytesData([]byte{0xc0, 0x2b, 0x00, 0x00})), + tname: "verdict register with 12-byte data comparison", + opStr: "[ cmp gte reg 0 0xb80d0120 0x18305290 0x02000000 ]", + expected: nil, }, { tname: "verdict register with 16-byte data comparison", - opStr: "[ cmp gt reg 0 0xb80d0120 0x00000000 0x00000000 0x02000000 ]", + opStr: "[ cmp neq reg 0 0xb80d0120 0x18305290 0x18305290 0x02000000 ]", + expected: nil, + }, + { + tname: "4-byte register == 4-byte data", + opStr: "[ cmp eq reg 8 0x0302010a ]", + expected: mustCreateComparison(t, linux.NFT_REG32_00, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a})), + }, + { + tname: "4-byte register != 4-byte data", + opStr: "[ cmp neq reg 9 0x00000064 ]", + expected: mustCreateComparison(t, linux.NFT_REG32_01, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64})), + }, + { + tname: "4-byte register < 4-byte data", + opStr: "[ cmp lt reg 10 0x00000000 ]", + expected: mustCreateComparison(t, linux.NFT_REG32_02, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00})), + }, + { + tname: "4-byte register <= 4-byte data", + opStr: "[ cmp lte reg 11 0x00000164 ]", + expected: mustCreateComparison(t, linux.NFT_REG32_03, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64})), + }, + { + tname: "4-byte register > 4-byte data", + opStr: "[ cmp gt reg 12 0xe8030000 ]", + expected: mustCreateComparison(t, linux.NFT_REG32_04, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00})), + }, + { + tname: "4-byte register >= 4-byte data", + opStr: "[ cmp gte reg 13 0xc02b0000 ]", + expected: mustCreateComparison(t, linux.NFT_REG32_05, linux.NFT_CMP_GTE, NewBytesData([]byte{0xc0, 0x2b, 0x00, 0x00})), + }, + { + tname: "4-byte register with 8-byte data comparison", + opStr: "[ cmp eq reg 14 0xb80d0120 0x02000000 ]", + expected: nil, + }, + { + tname: "4-byte register with 12-byte data comparison", + opStr: "[ cmp lte reg 15 0xb80d0120 0x18305290 0x02000000 ]", expected: nil, }, { tname: "4-byte register with 16-byte data comparison", - opStr: "[ cmp lte reg 8 0x0302010a 0x00000000 0x00000000 0x02000001 ]", + opStr: "[ cmp gt reg 16 0x0302010a 0x00000000 0x00000000 0x02000001 ]", expected: nil, }, { - tname: "register == 16-byte data", + tname: "16-byte register == 4-byte data", + opStr: "[ cmp eq reg 1 0x0302010a ]", + expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a})), + }, + { + tname: "16-byte register != 4-byte data", + opStr: "[ cmp neq reg 2 0x00000064 ]", + expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64})), + }, + { + tname: "16-byte register < 4-byte data", + opStr: "[ cmp lt reg 3 0x00000000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00})), + }, + { + tname: "16-byte register <= 4-byte data", + opStr: "[ cmp lte reg 4 0x00000164 ]", + expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64})), + }, + { + tname: "16-byte register > 4-byte data", + opStr: "[ cmp gt reg 1 0xe8030000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00})), + }, + { + tname: "16-byte register >= 4-byte data", + opStr: "[ cmp gte reg 2 0xc02b0000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GTE, NewBytesData([]byte{0xc0, 0x2b, 0x00, 0x00})), + }, + { + tname: "16-byte register == 8-byte data", + opStr: "[ cmp eq reg 1 0x0302010a 0x12345678 ]", + expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a, 0x12, 0x34, 0x56, 0x78})), + }, + { + tname: "16-byte register != 8-byte data", + opStr: "[ cmp neq reg 2 0x00000064 0x00000020 ]", + expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x20})), + }, + { + tname: "16-byte register < 8-byte data", + opStr: "[ cmp lt reg 3 0x00000000 0x00000000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + }, + { + tname: "16-byte register <= 8-byte data", + opStr: "[ cmp lte reg 4 0x00000164 0x00000164 ]", + expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64})), + }, + { + tname: "16-byte register > 8-byte data", + opStr: "[ cmp gt reg 2 0xe8030000 0x00000f13 ]", + expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x13})), + }, + { + tname: "16-byte register >= 8-byte data", + opStr: "[ cmp gte reg 3 0x0a000120 0xc0090000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0xc0, 0x09, 0x00, 0x00})), + }, + { + tname: "16-byte register == 12-byte data", + opStr: "[ cmp eq reg 1 0x0302010a 0x00000000 0x12345678 ]", + expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78})), + }, + { + tname: "16-byte register != 12-byte data", + opStr: "[ cmp neq reg 2 0x00000064 0x00000000 0x00000020 ]", + expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20})), + }, + { + tname: "16-byte register < 12-byte data", + opStr: "[ cmp lt reg 3 0x00000000 0x00000000 0x00000000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + }, + { + tname: "16-byte register <= 12-byte data", + opStr: "[ cmp lte reg 4 0x00000164 0x00000164 0x00000164 ]", + expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64})), + }, + { + tname: "16-byte register > 12-byte data", + opStr: "[ cmp gt reg 2 0xe8030000 0x00000f13 0xc0090000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + }, + { + tname: "16-byte register >= 12-byte data", + opStr: "[ cmp gte reg 3 0x0a000120 0x00000f13 0xc0090000 ]", + expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00})), + }, + { + tname: "16-byte register == 16-byte data", opStr: "[ cmp eq reg 1 0x0302010a 0x00000000 0x00000000 0x02000002 ]", expected: mustCreateComparison(t, linux.NFT_REG_1, linux.NFT_CMP_EQ, NewBytesData([]byte{0x03, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02})), }, { - tname: "register != 16-byte data", + tname: "16-byte register != 16-byte data", opStr: "[ cmp neq reg 2 0x00000064 0x00000000 0x00000000 0x02000000 ]", expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_NEQ, NewBytesData([]byte{0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00})), }, { - tname: "register < 16-byte data", + tname: "16-byte register < 16-byte data", opStr: "[ cmp lt reg 3 0x00000000 0x00000000 0x00000000 0x00000000 ]", expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_LT, NewBytesData([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), }, { - tname: "register <= 16-byte data", + tname: "16-byte register <= 16-byte data", opStr: "[ cmp lte reg 4 0x00000164 0x00000164 0x00000164 0x00000164 ]", expected: mustCreateComparison(t, linux.NFT_REG_4, linux.NFT_CMP_LTE, NewBytesData([]byte{0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x01, 0x64})), }, { - tname: "register > 16-byte data", + tname: "16-byte register > 16-byte data", opStr: "[ cmp gt reg 2 0xe8030000 0x00000f13 0xc0090000 0x0b136a87 ]", expected: mustCreateComparison(t, linux.NFT_REG_2, linux.NFT_CMP_GT, NewBytesData([]byte{0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00, 0x0b, 0x13, 0x6a, 0x87})), }, { - tname: "register >= 16-byte data", + tname: "16-byte register >= 16-byte data", opStr: "[ cmp gte reg 3 0x0a000120 0x00000f13 0xc0090000 0x0b136a87 ]", expected: mustCreateComparison(t, linux.NFT_REG_3, linux.NFT_CMP_GTE, NewBytesData([]byte{0x0a, 0x00, 0x01, 0x20, 0x00, 0x00, 0x0f, 0x13, 0xc0, 0x09, 0x00, 0x00, 0x0b, 0x13, 0x6a, 0x87})), },