// testVM sets up a Go BPF VM, and if available, a native OS BPF VM // for integration testing. func testVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func(), error) { goVM, err := bpf.NewVM(filter) if err != nil { // Some tests expect an error, so this error must be returned // instead of fatally exiting the test return nil, nil, err } mvm := &multiVirtualMachine{ goVM: goVM, t: t, } // If available, add the OS VM for tests which verify that both the Go // VM and OS VM have exactly the same output for the same input program // and packet. done := func() {} if canUseOSVM() { osVM, osVMDone := testOSVM(t, filter) done = func() { osVMDone() } mvm.osVM = osVM } return mvm, done, nil }
// testBPFProgram builds a BPF program for the input server and compares the // output of the program against the input AoE header, returning whether or // not the header was accepted by the filter. func testBPFProgram(t *testing.T, s *Server, h *aoe.Header) bool { filter, ok := bpf.Disassemble(s.mustAssembleBPF(testMTU)) if !ok { t.Fatal("failed to decode all BPF instructions") } vm, err := bpf.NewVM(filter) if !ok { t.Fatalf("failed to load BPF program: %v", err) } // Fill in empty AoE header fields not relevant to this test h.Version = aoe.Version h.Command = aoe.CommandQueryConfigInformation h.Arg = &aoe.ConfigArg{ Command: aoe.ConfigCommandRead, } hb, err := h.MarshalBinary() if err != nil { t.Fatalf("failed to marshal AoE header to binary: %v", err) } f := ðernet.Frame{ EtherType: aoe.EtherType, Payload: hb, } fb, err := f.MarshalBinary() if err != nil { t.Fatalf("failed to marshal Ethernet frame to binary: %v", err) } out, err := vm.Run(fb) if err != nil { t.Fatalf("failed to run BPF program: %v", err) } return out > 0 }
// ExampleNewVM demonstrates usage of a VM, using an Ethernet frame // as input and checking its EtherType to determine if it should be accepted. func ExampleNewVM() { // Offset | Length | Comment // ------------------------- // 00 | 06 | Ethernet destination MAC address // 06 | 06 | Ethernet source MAC address // 12 | 02 | Ethernet EtherType const ( etOff = 12 etLen = 2 etARP = 0x0806 ) // Set up a VM to filter traffic based on if its EtherType // matches the ARP EtherType. vm, err := bpf.NewVM([]bpf.Instruction{ // Load EtherType value from Ethernet header bpf.LoadAbsolute{ Off: etOff, Size: etLen, }, // If EtherType is equal to the ARP EtherType, jump to allow // packet to be accepted bpf.JumpIf{ Cond: bpf.JumpEqual, Val: etARP, SkipTrue: 1, }, // EtherType does not match the ARP EtherType bpf.RetConstant{ Val: 0, }, // EtherType matches the ARP EtherType, accept up to 1500 // bytes of packet bpf.RetConstant{ Val: 1500, }, }) if err != nil { panic(fmt.Sprintf("failed to load BPF program: %v", err)) } // Create an Ethernet frame with the ARP EtherType for testing frame := []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x06, // Payload omitted for brevity } // Run our VM's BPF program using the Ethernet frame as input out, err := vm.Run(frame) if err != nil { panic(fmt.Sprintf("failed to accept Ethernet frame: %v", err)) } // BPF VM can return a byte count greater than the number of input // bytes, so trim the output to match the input byte length if out > len(frame) { out = len(frame) } fmt.Printf("out: %d bytes", out) // Output: // out: 14 bytes }