OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / bpf / vm_bpf_test.go
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         "net"
9         "runtime"
10         "testing"
11         "time"
12
13         "golang.org/x/net/bpf"
14         "golang.org/x/net/ipv4"
15 )
16
17 // A virtualMachine is a BPF virtual machine which can process an
18 // input packet against a BPF program and render a verdict.
19 type virtualMachine interface {
20         Run(in []byte) (int, error)
21 }
22
23 // canUseOSVM indicates if the OS BPF VM is available on this platform.
24 func canUseOSVM() bool {
25         // OS BPF VM can only be used on platforms where x/net/ipv4 supports
26         // attaching a BPF program to a socket.
27         switch runtime.GOOS {
28         case "linux":
29                 return true
30         }
31
32         return false
33 }
34
35 // All BPF tests against both the Go VM and OS VM are assumed to
36 // be used with a UDP socket. As a result, the entire contents
37 // of a UDP datagram is sent through the BPF program, but only
38 // the body after the UDP header will ever be returned in output.
39
40 // testVM sets up a Go BPF VM, and if available, a native OS BPF VM
41 // for integration testing.
42 func testVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func(), error) {
43         goVM, err := bpf.NewVM(filter)
44         if err != nil {
45                 // Some tests expect an error, so this error must be returned
46                 // instead of fatally exiting the test
47                 return nil, nil, err
48         }
49
50         mvm := &multiVirtualMachine{
51                 goVM: goVM,
52
53                 t: t,
54         }
55
56         // If available, add the OS VM for tests which verify that both the Go
57         // VM and OS VM have exactly the same output for the same input program
58         // and packet.
59         done := func() {}
60         if canUseOSVM() {
61                 osVM, osVMDone := testOSVM(t, filter)
62                 done = func() { osVMDone() }
63                 mvm.osVM = osVM
64         }
65
66         return mvm, done, nil
67 }
68
69 // udpHeaderLen is the length of a UDP header.
70 const udpHeaderLen = 8
71
72 // A multiVirtualMachine is a virtualMachine which can call out to both the Go VM
73 // and the native OS VM, if the OS VM is available.
74 type multiVirtualMachine struct {
75         goVM virtualMachine
76         osVM virtualMachine
77
78         t *testing.T
79 }
80
81 func (mvm *multiVirtualMachine) Run(in []byte) (int, error) {
82         if len(in) < udpHeaderLen {
83                 mvm.t.Fatalf("input must be at least length of UDP header (%d), got: %d",
84                         udpHeaderLen, len(in))
85         }
86
87         // All tests have a UDP header as part of input, because the OS VM
88         // packets always will. For the Go VM, this output is trimmed before
89         // being sent back to tests.
90         goOut, goErr := mvm.goVM.Run(in)
91         if goOut >= udpHeaderLen {
92                 goOut -= udpHeaderLen
93         }
94
95         // If Go output is larger than the size of the packet, packet filtering
96         // interop tests must trim the output bytes to the length of the packet.
97         // The BPF VM should not do this on its own, as other uses of it do
98         // not trim the output byte count.
99         trim := len(in) - udpHeaderLen
100         if goOut > trim {
101                 goOut = trim
102         }
103
104         // When the OS VM is not available, process using the Go VM alone
105         if mvm.osVM == nil {
106                 return goOut, goErr
107         }
108
109         // The OS VM will apply its own UDP header, so remove the pseudo header
110         // that the Go VM needs.
111         osOut, err := mvm.osVM.Run(in[udpHeaderLen:])
112         if err != nil {
113                 mvm.t.Fatalf("error while running OS VM: %v", err)
114         }
115
116         // Verify both VMs return same number of bytes
117         var mismatch bool
118         if goOut != osOut {
119                 mismatch = true
120                 mvm.t.Logf("output byte count does not match:\n- go: %v\n- os: %v", goOut, osOut)
121         }
122
123         if mismatch {
124                 mvm.t.Fatal("Go BPF and OS BPF packet outputs do not match")
125         }
126
127         return goOut, goErr
128 }
129
130 // An osVirtualMachine is a virtualMachine which uses the OS's BPF VM for
131 // processing BPF programs.
132 type osVirtualMachine struct {
133         l net.PacketConn
134         s net.Conn
135 }
136
137 // testOSVM creates a virtualMachine which uses the OS's BPF VM by injecting
138 // packets into a UDP listener with a BPF program attached to it.
139 func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) {
140         l, err := net.ListenPacket("udp4", "127.0.0.1:0")
141         if err != nil {
142                 t.Fatalf("failed to open OS VM UDP listener: %v", err)
143         }
144
145         prog, err := bpf.Assemble(filter)
146         if err != nil {
147                 t.Fatalf("failed to compile BPF program: %v", err)
148         }
149
150         p := ipv4.NewPacketConn(l)
151         if err = p.SetBPF(prog); err != nil {
152                 t.Fatalf("failed to attach BPF program to listener: %v", err)
153         }
154
155         s, err := net.Dial("udp4", l.LocalAddr().String())
156         if err != nil {
157                 t.Fatalf("failed to dial connection to listener: %v", err)
158         }
159
160         done := func() {
161                 _ = s.Close()
162                 _ = l.Close()
163         }
164
165         return &osVirtualMachine{
166                 l: l,
167                 s: s,
168         }, done
169 }
170
171 // Run sends the input bytes into the OS's BPF VM and returns its verdict.
172 func (vm *osVirtualMachine) Run(in []byte) (int, error) {
173         go func() {
174                 _, _ = vm.s.Write(in)
175         }()
176
177         vm.l.SetDeadline(time.Now().Add(50 * time.Millisecond))
178
179         var b [512]byte
180         n, _, err := vm.l.ReadFrom(b[:])
181         if err != nil {
182                 // A timeout indicates that BPF filtered out the packet, and thus,
183                 // no input should be returned.
184                 if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
185                         return n, nil
186                 }
187
188                 return n, err
189         }
190
191         return n, nil
192 }