| // Copyright 2016 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package ipv4_test | |
| import ( | |
| "net" | |
| "runtime" | |
| "testing" | |
| "time" | |
| "golang.org/x/net/bpf" | |
| "golang.org/x/net/ipv4" | |
| ) | |
| func TestBPF(t *testing.T) { | |
| if runtime.GOOS != "linux" { | |
| t.Skipf("not supported on %s", runtime.GOOS) | |
| } | |
| l, err := net.ListenPacket("udp4", "127.0.0.1:0") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer l.Close() | |
| p := ipv4.NewPacketConn(l) | |
| // This filter accepts UDP packets whose first payload byte is | |
| // even. | |
| prog, err := bpf.Assemble([]bpf.Instruction{ | |
| // Load the first byte of the payload (skipping UDP header). | |
| bpf.LoadAbsolute{Off: 8, Size: 1}, | |
| // Select LSB of the byte. | |
| bpf.ALUOpConstant{Op: bpf.ALUOpAnd, Val: 1}, | |
| // Byte is even? | |
| bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0, SkipFalse: 1}, | |
| // Accept. | |
| bpf.RetConstant{Val: 4096}, | |
| // Ignore. | |
| bpf.RetConstant{Val: 0}, | |
| }) | |
| if err != nil { | |
| t.Fatalf("compiling BPF: %s", err) | |
| } | |
| if err = p.SetBPF(prog); err != nil { | |
| t.Fatalf("attaching filter to Conn: %s", err) | |
| } | |
| s, err := net.Dial("udp4", l.LocalAddr().String()) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer s.Close() | |
| go func() { | |
| for i := byte(0); i < 10; i++ { | |
| s.Write([]byte{i}) | |
| } | |
| }() | |
| l.SetDeadline(time.Now().Add(2 * time.Second)) | |
| seen := make([]bool, 5) | |
| for { | |
| var b [512]byte | |
| n, _, err := l.ReadFrom(b[:]) | |
| if err != nil { | |
| t.Fatalf("reading from listener: %s", err) | |
| } | |
| if n != 1 { | |
| t.Fatalf("unexpected packet length, want 1, got %d", n) | |
| } | |
| if b[0] >= 10 { | |
| t.Fatalf("unexpected byte, want 0-9, got %d", b[0]) | |
| } | |
| if b[0]%2 != 0 { | |
| t.Fatalf("got odd byte %d, wanted only even bytes", b[0]) | |
| } | |
| seen[b[0]/2] = true | |
| seenAll := true | |
| for _, v := range seen { | |
| if !v { | |
| seenAll = false | |
| break | |
| } | |
| } | |
| if seenAll { | |
| break | |
| } | |
| } | |
| } |