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
5package ipv6_test
6
7import (
8	"net"
9	"runtime"
10	"testing"
11	"time"
12
13	"golang.org/x/net/bpf"
14	"golang.org/x/net/ipv6"
15	"golang.org/x/net/nettest"
16)
17
18func TestBPF(t *testing.T) {
19	if runtime.GOOS != "linux" {
20		t.Skipf("not supported on %s", runtime.GOOS)
21	}
22	if !nettest.SupportsIPv6() {
23		t.Skip("ipv6 is not supported")
24	}
25
26	l, err := net.ListenPacket("udp6", "[::1]:0")
27	if err != nil {
28		t.Fatal(err)
29	}
30	defer l.Close()
31
32	p := ipv6.NewPacketConn(l)
33
34	// This filter accepts UDP packets whose first payload byte is
35	// even.
36	prog, err := bpf.Assemble([]bpf.Instruction{
37		// Load the first byte of the payload (skipping UDP header).
38		bpf.LoadAbsolute{Off: 8, Size: 1},
39		// Select LSB of the byte.
40		bpf.ALUOpConstant{Op: bpf.ALUOpAnd, Val: 1},
41		// Byte is even?
42		bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0, SkipFalse: 1},
43		// Accept.
44		bpf.RetConstant{Val: 4096},
45		// Ignore.
46		bpf.RetConstant{Val: 0},
47	})
48	if err != nil {
49		t.Fatalf("compiling BPF: %s", err)
50	}
51
52	if err = p.SetBPF(prog); err != nil {
53		t.Fatalf("attaching filter to Conn: %s", err)
54	}
55
56	s, err := net.Dial("udp6", l.LocalAddr().String())
57	if err != nil {
58		t.Fatal(err)
59	}
60	defer s.Close()
61	go func() {
62		for i := byte(0); i < 10; i++ {
63			s.Write([]byte{i})
64		}
65	}()
66
67	l.SetDeadline(time.Now().Add(2 * time.Second))
68	seen := make([]bool, 5)
69	for {
70		var b [512]byte
71		n, _, err := l.ReadFrom(b[:])
72		if err != nil {
73			t.Fatalf("reading from listener: %s", err)
74		}
75		if n != 1 {
76			t.Fatalf("unexpected packet length, want 1, got %d", n)
77		}
78		if b[0] >= 10 {
79			t.Fatalf("unexpected byte, want 0-9, got %d", b[0])
80		}
81		if b[0]%2 != 0 {
82			t.Fatalf("got odd byte %d, wanted only even bytes", b[0])
83		}
84		seen[b[0]/2] = true
85
86		seenAll := true
87		for _, v := range seen {
88			if !v {
89				seenAll = false
90				break
91			}
92		}
93		if seenAll {
94			break
95		}
96	}
97}
98