mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement Immediate operation and test evaluation from top level.
Currently, the Immediate operation is the only supported operation. PiperOrigin-RevId: 660512320
This commit is contained in:
committed by
gVisor bot
parent
e30fa67177
commit
2e8244c61b
@@ -575,6 +575,33 @@ type Operation interface {
|
||||
evaluate(regs *RegisterSet, pkt *stack.PacketBuffer)
|
||||
}
|
||||
|
||||
// Ensures all operations implement the Operation interface at compile time.
|
||||
var (
|
||||
_ Operation = (*Immediate)(nil)
|
||||
)
|
||||
|
||||
// Immediate is an operation that sets the data in a register.
|
||||
type Immediate struct {
|
||||
data RegisterData // Data to set the destination register to.
|
||||
dreg uint8 // Number of the destination register.
|
||||
}
|
||||
|
||||
// NewImmediate creates a new Immediate operation.
|
||||
func NewImmediate(dreg uint8, data RegisterData) (*Immediate, error) {
|
||||
if err := data.ValidateRegister(dreg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Immediate{dreg: dreg, data: data}, nil
|
||||
}
|
||||
|
||||
// TypeString for Immediate returns "Immediate" as the string operation type.
|
||||
func (op *Immediate) TypeString() string { return "Immediate" }
|
||||
|
||||
// evaluate for Immediate sets the data in the destination register.
|
||||
func (op Immediate) evaluate(regs *RegisterSet, pkt *stack.PacketBuffer) {
|
||||
op.data.StoreData(regs, op.dreg)
|
||||
}
|
||||
|
||||
//
|
||||
// Register and Register-Related Implementations.
|
||||
// Note: Registers are represented by type uint8 for the register number.
|
||||
@@ -1072,15 +1099,15 @@ func (nf *NFTables) EvaluateHook(family AddressFamily, hook Hook, pkt *stack.Pac
|
||||
}
|
||||
|
||||
// Terminates immediately on netfilter terminal verdicts.
|
||||
switch regs.verdict.Code {
|
||||
switch regs.Verdict().Code {
|
||||
case VC(linux.NF_ACCEPT), VC(linux.NF_DROP), VC(linux.NF_STOLEN), VC(linux.NF_QUEUE):
|
||||
return regs.verdict, nil
|
||||
return regs.Verdict(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Returns policy verdict of the last base chain evaluated if no terminal
|
||||
// verdict was issued.
|
||||
switch regs.verdict.Code {
|
||||
switch regs.Verdict().Code {
|
||||
case VC(linux.NFT_CONTINUE), VC(linux.NFT_RETURN):
|
||||
if bc.GetBaseChainInfo().PolicyDrop {
|
||||
return Verdict{Code: VC(linux.NF_DROP)}, nil
|
||||
@@ -1088,7 +1115,7 @@ func (nf *NFTables) EvaluateHook(family AddressFamily, hook Hook, pkt *stack.Pac
|
||||
return Verdict{Code: VC(linux.NF_ACCEPT)}, nil
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unexpected verdict from hook evaluation: %d", regs.verdict.Code))
|
||||
panic(fmt.Sprintf("unexpected verdict from hook evaluation: %d", regs.Verdict().Code))
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
const arbitraryTargetChain = "target_chain"
|
||||
|
||||
// makeTestingPacket creates an arbitrary packet for testing.
|
||||
func makeTestingPacket() *stack.PacketBuffer {
|
||||
return stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
@@ -84,6 +86,190 @@ func TestAcceptAllForSupportedHooks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvaluateImmediate tests that the Immediate operation correctly sets the
|
||||
// register value and behaves as expected during evaluation.
|
||||
func TestEvaluateImmediate(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
tname string
|
||||
baseOp1 Operation // will be nil if unused
|
||||
baseOp2 Operation // will be nil if unused
|
||||
targetOp Operation // will be nil if unused
|
||||
verdict Verdict
|
||||
}{
|
||||
{
|
||||
tname: "no operations",
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)}, // from base chain policy
|
||||
},
|
||||
{
|
||||
tname: "immediately accept",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)},
|
||||
},
|
||||
{
|
||||
tname: "immediately drop",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_DROP)},
|
||||
},
|
||||
{
|
||||
tname: "immediately continue with base chain policy accept",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_CONTINUE)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)}, // from base chain policy
|
||||
},
|
||||
{
|
||||
tname: "immediately return with base chain policy accept",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_RETURN)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)}, // from base chain policy
|
||||
},
|
||||
{
|
||||
tname: "immediately jump to target chain that accepts",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_JUMP), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)},
|
||||
},
|
||||
{
|
||||
tname: "immediately jump to target chain that drops",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_JUMP), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_DROP)},
|
||||
},
|
||||
{
|
||||
tname: "immediately jump to target chain that continues with second rule that accepts",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_JUMP), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_CONTINUE)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)},
|
||||
},
|
||||
{
|
||||
tname: "immediately jump to target chain that continues with second rule that drops",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_JUMP), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_CONTINUE)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_DROP)},
|
||||
},
|
||||
{
|
||||
tname: "immediately goto to target chain that accepts",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_GOTO), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)},
|
||||
},
|
||||
{
|
||||
tname: "immediately goto to target chain that drops",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_GOTO), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_DROP)},
|
||||
},
|
||||
{
|
||||
tname: "immediately goto to target chain that continues with second rule that accepts",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_GOTO), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_CONTINUE)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)}, // from base chain policy
|
||||
},
|
||||
{
|
||||
tname: "immediately goto to target chain that continues with second rule that drops",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_GOTO), ChainName: arbitraryTargetChain})),
|
||||
targetOp: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_CONTINUE)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)}, // from base chain policy
|
||||
},
|
||||
{
|
||||
tname: "add data to register then accept",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG32_13, NewBytesData([]byte{0, 1, 2, 3})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)},
|
||||
},
|
||||
{
|
||||
tname: "add data to register then drop",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG32_15, NewBytesData([]byte{0, 1, 2, 3})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_DROP)},
|
||||
},
|
||||
{
|
||||
tname: "add data to register then continue",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_4, NewBytesData([]byte{0, 1, 2, 3})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NFT_CONTINUE)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)}, // from base chain policy
|
||||
},
|
||||
{
|
||||
tname: "multiple accepts",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)},
|
||||
},
|
||||
{
|
||||
tname: "multiple drops",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_DROP)},
|
||||
},
|
||||
{
|
||||
tname: "immediately accept then drop",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_ACCEPT)},
|
||||
},
|
||||
{
|
||||
tname: "immediately drop then accept",
|
||||
baseOp1: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_DROP)})),
|
||||
baseOp2: mustCreateImmediate(t, linux.NFT_REG_VERDICT, NewVerdictData(Verdict{Code: VC(linux.NF_ACCEPT)})),
|
||||
verdict: Verdict{Code: VC(linux.NF_DROP)},
|
||||
},
|
||||
} {
|
||||
t.Run(test.tname, func(t *testing.T) {
|
||||
// Sets up an NFTables object with a base chain (for 2 rules) and another
|
||||
// target chain (for 1 rule).
|
||||
nf := NewNFTables()
|
||||
tab, err := nf.AddTable(IP, "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)
|
||||
}
|
||||
priority, err := NewStandardPriority("filter", IP, Prerouting)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for NewStandardPriority: %v", err)
|
||||
}
|
||||
bc.SetBaseChainInfo(&BaseChainInfo{
|
||||
BcType: BaseChainTypeFilter,
|
||||
Hook: Prerouting,
|
||||
Priority: priority,
|
||||
})
|
||||
tc, err := tab.AddChain(arbitraryTargetChain, nil, "test chain", false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for AddChain: %v", err)
|
||||
}
|
||||
|
||||
// Adds testing rules and operations.
|
||||
if test.baseOp1 != nil {
|
||||
rule1 := &Rule{}
|
||||
bc.AddRule(rule1)
|
||||
rule1.AddOperation(test.baseOp1)
|
||||
}
|
||||
if test.baseOp2 != nil {
|
||||
rule2 := &Rule{}
|
||||
bc.AddRule(rule2)
|
||||
rule2.AddOperation(test.baseOp2)
|
||||
}
|
||||
if test.targetOp != nil {
|
||||
ruleTarget := &Rule{}
|
||||
tc.AddRule(ruleTarget)
|
||||
ruleTarget.AddOperation(test.targetOp)
|
||||
}
|
||||
|
||||
pkt := makeTestingPacket()
|
||||
v, err := nf.EvaluateHook(IP, Prerouting, pkt)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for EvaluateHook: %v", err)
|
||||
}
|
||||
if v.Code != test.verdict.Code {
|
||||
t.Fatalf("expected verdict %s, got %s", test.verdict.String(), v.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// packetResultString compares 2 packets by equality and returns a string
|
||||
// representation.
|
||||
func packetResultString(initial, final *stack.PacketBuffer) string {
|
||||
@@ -95,3 +281,12 @@ func packetResultString(initial, final *stack.PacketBuffer) string {
|
||||
}
|
||||
return "modified"
|
||||
}
|
||||
|
||||
// mustCreateImmediate wraps the NewImmediate function for brevity.
|
||||
func mustCreateImmediate(t *testing.T, dreg uint8, data RegisterData) *Immediate {
|
||||
imm, err := NewImmediate(dreg, data)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create immediate: %v", err)
|
||||
}
|
||||
return imm
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user