golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/bpf/vm_test.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package bpf_test 6 7 import ( 8 "fmt" 9 "testing" 10 11 "golang.org/x/net/bpf" 12 ) 13 14 var _ bpf.Instruction = unknown{} 15 16 type unknown struct{} 17 18 func (unknown) Assemble() (bpf.RawInstruction, error) { 19 return bpf.RawInstruction{}, nil 20 } 21 22 func TestVMUnknownInstruction(t *testing.T) { 23 vm, done, err := testVM(t, []bpf.Instruction{ 24 bpf.LoadConstant{ 25 Dst: bpf.RegA, 26 Val: 100, 27 }, 28 // Should terminate the program with an error immediately 29 unknown{}, 30 bpf.RetA{}, 31 }) 32 if err != nil { 33 t.Fatalf("unexpected error: %v", err) 34 } 35 defer done() 36 37 _, err = vm.Run([]byte{ 38 0xff, 0xff, 0xff, 0xff, 39 0xff, 0xff, 0xff, 0xff, 40 0x00, 0x00, 41 }) 42 if errStr(err) != "unknown Instruction at index 1: bpf_test.unknown" { 43 t.Fatalf("unexpected error while running program: %v", err) 44 } 45 } 46 47 func TestVMNoReturnInstruction(t *testing.T) { 48 _, _, err := testVM(t, []bpf.Instruction{ 49 bpf.LoadConstant{ 50 Dst: bpf.RegA, 51 Val: 1, 52 }, 53 }) 54 if errStr(err) != "BPF program must end with RetA or RetConstant" { 55 t.Fatalf("unexpected error: %v", err) 56 } 57 } 58 59 func TestVMNoInputInstructions(t *testing.T) { 60 _, _, err := testVM(t, []bpf.Instruction{}) 61 if errStr(err) != "one or more Instructions must be specified" { 62 t.Fatalf("unexpected error: %v", err) 63 } 64 } 65 66 // ExampleNewVM demonstrates usage of a VM, using an Ethernet frame 67 // as input and checking its EtherType to determine if it should be accepted. 68 func ExampleNewVM() { 69 // Offset | Length | Comment 70 // ------------------------- 71 // 00 | 06 | Ethernet destination MAC address 72 // 06 | 06 | Ethernet source MAC address 73 // 12 | 02 | Ethernet EtherType 74 const ( 75 etOff = 12 76 etLen = 2 77 78 etARP = 0x0806 79 ) 80 81 // Set up a VM to filter traffic based on if its EtherType 82 // matches the ARP EtherType. 83 vm, err := bpf.NewVM([]bpf.Instruction{ 84 // Load EtherType value from Ethernet header 85 bpf.LoadAbsolute{ 86 Off: etOff, 87 Size: etLen, 88 }, 89 // If EtherType is equal to the ARP EtherType, jump to allow 90 // packet to be accepted 91 bpf.JumpIf{ 92 Cond: bpf.JumpEqual, 93 Val: etARP, 94 SkipTrue: 1, 95 }, 96 // EtherType does not match the ARP EtherType 97 bpf.RetConstant{ 98 Val: 0, 99 }, 100 // EtherType matches the ARP EtherType, accept up to 1500 101 // bytes of packet 102 bpf.RetConstant{ 103 Val: 1500, 104 }, 105 }) 106 if err != nil { 107 panic(fmt.Sprintf("failed to load BPF program: %v", err)) 108 } 109 110 // Create an Ethernet frame with the ARP EtherType for testing 111 frame := []byte{ 112 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 113 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 114 0x08, 0x06, 115 // Payload omitted for brevity 116 } 117 118 // Run our VM's BPF program using the Ethernet frame as input 119 out, err := vm.Run(frame) 120 if err != nil { 121 panic(fmt.Sprintf("failed to accept Ethernet frame: %v", err)) 122 } 123 124 // BPF VM can return a byte count greater than the number of input 125 // bytes, so trim the output to match the input byte length 126 if out > len(frame) { 127 out = len(frame) 128 } 129 130 fmt.Printf("out: %d bytes", out) 131 132 // Output: 133 // out: 14 bytes 134 } 135 136 // errStr returns the string representation of an error, or 137 // "<nil>" if it is nil. 138 func errStr(err error) string { 139 if err == nil { 140 return "<nil>" 141 } 142 143 return err.Error() 144 }