github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/net/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  	"github.com/hxx258456/ccgo/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  }