git » go-net » commit ef00b37

ipv6: support attaching packet filters to PacketConn.

author David Anderson
2016-05-15 03:16:23 UTC
committer Mikio Hara
2016-05-15 06:10:11 UTC
parent 58b2fb074ec0cc06e95f2409a11ff51dd11798e2

ipv6: support attaching packet filters to PacketConn.

Fixes golang/go#14974

Change-Id: I58c41acf29329aedf61b9ca59eb271e4536c80ea
Reviewed-on: https://go-review.googlesource.com/23107
Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com>

ipv6/bpf_test.go +93 -0
ipv6/bpfopt_linux.go +27 -0
ipv6/bpfopt_stub.go +16 -0
ipv6/defs_linux.go +9 -0
ipv6/zsys_linux_386.go +16 -0
ipv6/zsys_linux_amd64.go +16 -0
ipv6/zsys_linux_arm.go +16 -0
ipv6/zsys_linux_arm64.go +16 -0
ipv6/zsys_linux_mips64.go +16 -0
ipv6/zsys_linux_mips64le.go +16 -0
ipv6/zsys_linux_ppc64.go +16 -0
ipv6/zsys_linux_ppc64le.go +16 -0

diff --git a/ipv6/bpf_test.go b/ipv6/bpf_test.go
new file mode 100644
index 0000000..03d478d
--- /dev/null
+++ b/ipv6/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 ipv6_test
+
+import (
+	"net"
+	"runtime"
+	"testing"
+	"time"
+
+	"golang.org/x/net/bpf"
+	"golang.org/x/net/ipv6"
+)
+
+func TestBPF(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skipf("not supported on %s", runtime.GOOS)
+	}
+
+	l, err := net.ListenPacket("udp6", "[::1]:0")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer l.Close()
+
+	p := ipv6.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("udp6", 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/ipv6/bpfopt_linux.go b/ipv6/bpfopt_linux.go
new file mode 100644
index 0000000..066ef20
--- /dev/null
+++ b/ipv6/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 ipv6
+
+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/ipv6/bpfopt_stub.go b/ipv6/bpfopt_stub.go
new file mode 100644
index 0000000..2e4de5f
--- /dev/null
+++ b/ipv6/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 ipv6
+
+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/ipv6/defs_linux.go b/ipv6/defs_linux.go
index d83abce..664305d 100644
--- a/ipv6/defs_linux.go
+++ b/ipv6/defs_linux.go
@@ -13,6 +13,8 @@ package ipv6
 #include <linux/in6.h>
 #include <linux/ipv6.h>
 #include <linux/icmpv6.h>
+#include <linux/filter.h>
+#include <sys/socket.h>
 */
 import "C"
 
@@ -104,6 +106,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = C.ICMPV6_FILTER_BLOCKOTHERS
 	sysICMPV6_FILTER_PASSONLY    = C.ICMPV6_FILTER_PASSONLY
 
+	sysSOL_SOCKET       = C.SOL_SOCKET
+	sysSO_ATTACH_FILTER = C.SO_ATTACH_FILTER
+
 	sysSizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage
 	sysSizeofSockaddrInet6         = C.sizeof_struct_sockaddr_in6
 	sysSizeofInet6Pktinfo          = C.sizeof_struct_in6_pktinfo
@@ -134,3 +139,7 @@ type sysGroupReq C.struct_group_req
 type sysGroupSourceReq C.struct_group_source_req
 
 type sysICMPv6Filter C.struct_icmp6_filter
+
+type sysSockFProg C.struct_sock_fprog
+
+type sysSockFilter C.struct_sock_filter
diff --git a/ipv6/zsys_linux_386.go b/ipv6/zsys_linux_386.go
index 2727929..36fccbb 100644
--- a/ipv6/zsys_linux_386.go
+++ b/ipv6/zsys_linux_386.go
@@ -84,6 +84,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -150,3 +153,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]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/ipv6/zsys_linux_amd64.go b/ipv6/zsys_linux_amd64.go
index 2f742e9..7461e7e 100644
--- a/ipv6/zsys_linux_amd64.go
+++ b/ipv6/zsys_linux_amd64.go
@@ -84,6 +84,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -152,3 +155,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]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/ipv6/zsys_linux_arm.go b/ipv6/zsys_linux_arm.go
index 2727929..36fccbb 100644
--- a/ipv6/zsys_linux_arm.go
+++ b/ipv6/zsys_linux_arm.go
@@ -84,6 +84,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -150,3 +153,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]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/ipv6/zsys_linux_arm64.go b/ipv6/zsys_linux_arm64.go
index ab10464..ed35f60 100644
--- a/ipv6/zsys_linux_arm64.go
+++ b/ipv6/zsys_linux_arm64.go
@@ -86,6 +86,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -154,3 +157,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]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/ipv6/zsys_linux_mips64.go b/ipv6/zsys_linux_mips64.go
index ec8ce15..141c869 100644
--- a/ipv6/zsys_linux_mips64.go
+++ b/ipv6/zsys_linux_mips64.go
@@ -86,6 +86,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -154,3 +157,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]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/ipv6/zsys_linux_mips64le.go b/ipv6/zsys_linux_mips64le.go
index 2341ae6..d50eb63 100644
--- a/ipv6/zsys_linux_mips64le.go
+++ b/ipv6/zsys_linux_mips64le.go
@@ -86,6 +86,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -154,3 +157,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]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/ipv6/zsys_linux_ppc64.go b/ipv6/zsys_linux_ppc64.go
index b99b8a5..c1d775f 100644
--- a/ipv6/zsys_linux_ppc64.go
+++ b/ipv6/zsys_linux_ppc64.go
@@ -86,6 +86,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -154,3 +157,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]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/ipv6/zsys_linux_ppc64le.go b/ipv6/zsys_linux_ppc64le.go
index 992b56e..e385fb7 100644
--- a/ipv6/zsys_linux_ppc64le.go
+++ b/ipv6/zsys_linux_ppc64le.go
@@ -86,6 +86,9 @@ const (
 	sysICMPV6_FILTER_BLOCKOTHERS = 0x3
 	sysICMPV6_FILTER_PASSONLY    = 0x4
 
+	sysSOL_SOCKET       = 0x1
+	sysSO_ATTACH_FILTER = 0x1a
+
 	sysSizeofKernelSockaddrStorage = 0x80
 	sysSizeofSockaddrInet6         = 0x1c
 	sysSizeofInet6Pktinfo          = 0x14
@@ -154,3 +157,16 @@ type sysGroupSourceReq struct {
 type sysICMPv6Filter struct {
 	Data [8]uint32
 }
+
+type sysSockFProg struct {
+	Len       uint16
+	Pad_cgo_0 [6]byte
+	Filter    *sysSockFilter
+}
+
+type sysSockFilter struct {
+	Code uint16
+	Jt   uint8
+	Jf   uint8
+	K    uint32
+}