author | David Anderson
<danderson@google.com> 2016-05-14 19:45:11 UTC |
committer | Mikio Hara
<mikioh.mikioh@gmail.com> 2016-05-15 02:52:58 UTC |
parent | 30be488e9140ebb7fac3b2995ac5a4b4d5e715ed |
ipv4/bpf_test.go | +93 | -0 |
ipv4/bpfopt_linux.go | +27 | -0 |
ipv4/bpfopt_stub.go | +16 | -0 |
ipv4/defs_linux.go | +9 | -0 |
ipv4/zsys_linux_386.go | +16 | -0 |
ipv4/zsys_linux_amd64.go | +16 | -0 |
ipv4/zsys_linux_arm.go | +16 | -0 |
ipv4/zsys_linux_arm64.go | +16 | -0 |
ipv4/zsys_linux_mips64.go | +16 | -0 |
ipv4/zsys_linux_mips64le.go | +16 | -0 |
ipv4/zsys_linux_ppc64.go | +16 | -0 |
ipv4/zsys_linux_ppc64le.go | +16 | -0 |
diff --git a/ipv4/bpf_test.go b/ipv4/bpf_test.go new file mode 100644 index 0000000..b44da90 --- /dev/null +++ b/ipv4/bpf_test.go @@ -0,0 +1,93 @@ +// 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 + } + } +} diff --git a/ipv4/bpfopt_linux.go b/ipv4/bpfopt_linux.go new file mode 100644 index 0000000..f2d00b4 --- /dev/null +++ b/ipv4/bpfopt_linux.go @@ -0,0 +1,27 @@ +// 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 + +import ( + "os" + "unsafe" + + "golang.org/x/net/bpf" +) + +// SetBPF attaches a BPF program to the connection. +// +// Only supported on Linux. +func (c *dgramOpt) SetBPF(filter []bpf.RawInstruction) error { + fd, err := c.sysfd() + if err != nil { + return err + } + prog := sysSockFProg{ + Len: uint16(len(filter)), + Filter: (*sysSockFilter)(unsafe.Pointer(&filter[0])), + } + return os.NewSyscallError("setsockopt", setsockopt(fd, sysSOL_SOCKET, sysSO_ATTACH_FILTER, unsafe.Pointer(&prog), uint32(unsafe.Sizeof(prog)))) +} diff --git a/ipv4/bpfopt_stub.go b/ipv4/bpfopt_stub.go new file mode 100644 index 0000000..c4a8481 --- /dev/null +++ b/ipv4/bpfopt_stub.go @@ -0,0 +1,16 @@ +// 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. + +// +build !linux + +package ipv4 + +import "golang.org/x/net/bpf" + +// SetBPF attaches a BPF program to the connection. +// +// Only supported on Linux. +func (c *dgramOpt) SetBPF(filter []bpf.RawInstruction) error { + return errOpNoSupport +} diff --git a/ipv4/defs_linux.go b/ipv4/defs_linux.go index fdba148..c4042eb 100644 --- a/ipv4/defs_linux.go +++ b/ipv4/defs_linux.go @@ -14,6 +14,8 @@ package ipv4 #include <linux/errqueue.h> #include <linux/icmp.h> #include <linux/in.h> +#include <linux/filter.h> +#include <sys/socket.h> */ import "C" @@ -76,6 +78,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = C.SO_EE_ORIGIN_TXSTATUS sysSO_EE_ORIGIN_TIMESTAMPING = C.SO_EE_ORIGIN_TIMESTAMPING + sysSOL_SOCKET = C.SOL_SOCKET + sysSO_ATTACH_FILTER = C.SO_ATTACH_FILTER + sysSizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage sysSizeofSockaddrInet = C.sizeof_struct_sockaddr_in sysSizeofInetPktinfo = C.sizeof_struct_in_pktinfo @@ -109,3 +114,7 @@ type sysGroupReq C.struct_group_req type sysGroupSourceReq C.struct_group_source_req type sysICMPFilter C.struct_icmp_filter + +type sysSockFProg C.struct_sock_fprog + +type sysSockFilter C.struct_sock_filter diff --git a/ipv4/zsys_linux_386.go b/ipv4/zsys_linux_386.go index fc7a9eb..3733152 100644 --- a/ipv4/zsys_linux_386.go +++ b/ipv4/zsys_linux_386.go @@ -55,6 +55,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -128,3 +131,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/ipv4/zsys_linux_amd64.go b/ipv4/zsys_linux_amd64.go index e324b81..afa4519 100644 --- a/ipv4/zsys_linux_amd64.go +++ b/ipv4/zsys_linux_amd64.go @@ -55,6 +55,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -130,3 +133,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/ipv4/zsys_linux_arm.go b/ipv4/zsys_linux_arm.go index fc7a9eb..3733152 100644 --- a/ipv4/zsys_linux_arm.go +++ b/ipv4/zsys_linux_arm.go @@ -55,6 +55,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -128,3 +131,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/ipv4/zsys_linux_arm64.go b/ipv4/zsys_linux_arm64.go index ce4194a..129a20a 100644 --- a/ipv4/zsys_linux_arm64.go +++ b/ipv4/zsys_linux_arm64.go @@ -57,6 +57,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -132,3 +135,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/ipv4/zsys_linux_mips64.go b/ipv4/zsys_linux_mips64.go index 94116bf..7ed9368 100644 --- a/ipv4/zsys_linux_mips64.go +++ b/ipv4/zsys_linux_mips64.go @@ -57,6 +57,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -132,3 +135,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/ipv4/zsys_linux_mips64le.go b/ipv4/zsys_linux_mips64le.go index 698d7db..19fadae 100644 --- a/ipv4/zsys_linux_mips64le.go +++ b/ipv4/zsys_linux_mips64le.go @@ -57,6 +57,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -132,3 +135,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/ipv4/zsys_linux_ppc64.go b/ipv4/zsys_linux_ppc64.go index 9fe5ee2..beaadd5 100644 --- a/ipv4/zsys_linux_ppc64.go +++ b/ipv4/zsys_linux_ppc64.go @@ -57,6 +57,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -132,3 +135,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/ipv4/zsys_linux_ppc64le.go b/ipv4/zsys_linux_ppc64le.go index 3891f54..0eb2623 100644 --- a/ipv4/zsys_linux_ppc64le.go +++ b/ipv4/zsys_linux_ppc64le.go @@ -57,6 +57,9 @@ const ( sysSO_EE_ORIGIN_TXSTATUS = 0x4 sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + sysSizeofKernelSockaddrStorage = 0x80 sysSizeofSockaddrInet = 0x10 sysSizeofInetPktinfo = 0xc @@ -132,3 +135,16 @@ type sysGroupSourceReq struct { type sysICMPFilter struct { Data uint32 } + +type sysSockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sysSockFilter +} + +type sysSockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +}