Commit e92559f3 authored by Mikio Hara's avatar Mikio Hara

x/net/ipv4: add support for icmp filter on linux

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/179510043
parent 0d0e5840
...@@ -223,3 +223,29 @@ func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source ...@@ -223,3 +223,29 @@ func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source
} }
return setSourceGroup(fd, &sockOpts[ssoUnblockSourceGroup], ifi, grp, src) return setSourceGroup(fd, &sockOpts[ssoUnblockSourceGroup], ifi, grp, src)
} }
// ICMPFilter returns an ICMP filter.
// Currently only Linux supports this.
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
if !c.ok() {
return nil, syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return nil, err
}
return getICMPFilter(fd, &sockOpts[ssoICMPFilter])
}
// SetICMPFilter deploys the ICMP filter.
// Currently only Linux supports this.
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
return setICMPFilter(fd, &sockOpts[ssoICMPFilter], f)
}
...@@ -92,3 +92,15 @@ func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source ...@@ -92,3 +92,15 @@ func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source
func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
return errOpNoSupport return errOpNoSupport
} }
// ICMPFilter returns an ICMP filter.
// Currently only Linux supports this.
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
return nil, errOpNoSupport
}
// SetICMPFilter deploys the ICMP filter.
// Currently only Linux supports this.
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
return errOpNoSupport
}
...@@ -21,3 +21,37 @@ func (typ ICMPType) String() string { ...@@ -21,3 +21,37 @@ func (typ ICMPType) String() string {
func (typ ICMPType) Protocol() int { func (typ ICMPType) Protocol() int {
return iana.ProtocolICMP return iana.ProtocolICMP
} }
// An ICMPFilter represents an ICMP message filter for incoming
// packets. The filter belongs to a packet delivery path on a host and
// it cannot interact with forwarding packets or tunnel-outer packets.
//
// Note: RFC 2460 defines a reasonable role model and it works not
// only for IPv6 but IPv4. A node means a device that implements IP.
// A router means a node that forwards IP packets not explicitly
// addressed to itself, and a host means a node that is not a router.
type ICMPFilter struct {
sysICMPFilter
}
// Accept accepts incoming ICMP packets including the type field value
// typ.
func (f *ICMPFilter) Accept(typ ICMPType) {
f.accept(typ)
}
// Block blocks incoming ICMP packets including the type field value
// typ.
func (f *ICMPFilter) Block(typ ICMPType) {
f.block(typ)
}
// SetAll sets the filter action to the filter.
func (f *ICMPFilter) SetAll(block bool) {
f.setAll(block)
}
// WillBlock reports whether the ICMP type will be blocked.
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
return f.willBlock(typ)
}
// Copyright 2014 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
func (f *sysICMPFilter) accept(typ ICMPType) {
f.Data &^= 1 << (uint32(typ) & 31)
}
func (f *sysICMPFilter) block(typ ICMPType) {
f.Data |= 1 << (uint32(typ) & 31)
}
func (f *sysICMPFilter) setAll(block bool) {
if block {
f.Data = 1<<32 - 1
} else {
f.Data = 0
}
}
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
return f.Data&(1<<(uint32(typ)&31)) != 0
}
// Copyright 2014 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
const sysSizeofICMPFilter = 0x0
type sysICMPFilter struct {
}
func (f *sysICMPFilter) accept(typ ICMPType) {
}
func (f *sysICMPFilter) block(typ ICMPType) {
}
func (f *sysICMPFilter) setAll(block bool) {
}
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
return false
}
// Copyright 2014 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"
"os"
"reflect"
"runtime"
"testing"
"golang.org/x/net/ipv4"
)
var icmpStringTests = []struct {
in ipv4.ICMPType
out string
}{
{ipv4.ICMPTypeDestinationUnreachable, "destination unreachable"},
{256, "<nil>"},
}
func TestICMPString(t *testing.T) {
for _, tt := range icmpStringTests {
s := tt.in.String()
if s != tt.out {
t.Errorf("got %s; want %s", s, tt.out)
}
}
}
func TestICMPFilter(t *testing.T) {
switch runtime.GOOS {
case "linux":
default:
t.Skipf("not supported on %q", runtime.GOOS)
}
var f ipv4.ICMPFilter
for _, toggle := range []bool{false, true} {
f.SetAll(toggle)
for _, typ := range []ipv4.ICMPType{
ipv4.ICMPTypeDestinationUnreachable,
ipv4.ICMPTypeEchoReply,
ipv4.ICMPTypeTimeExceeded,
ipv4.ICMPTypeParameterProblem,
} {
f.Accept(typ)
if f.WillBlock(typ) {
t.Errorf("ipv4.ICMPFilter.Set(%v, false) failed", typ)
}
f.Block(typ)
if !f.WillBlock(typ) {
t.Errorf("ipv4.ICMPFilter.Set(%v, true) failed", typ)
}
}
}
}
func TestSetICMPFilter(t *testing.T) {
switch runtime.GOOS {
case "linux":
default:
t.Skipf("not supported on %q", runtime.GOOS)
}
if os.Getuid() != 0 {
t.Skip("must be root")
}
c, err := net.ListenPacket("ip4:icmp", "127.0.0.1")
if err != nil {
t.Fatal(err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
var f ipv4.ICMPFilter
f.SetAll(true)
f.Accept(ipv4.ICMPTypeEcho)
f.Accept(ipv4.ICMPTypeEchoReply)
if err := p.SetICMPFilter(&f); err != nil {
t.Fatal(err)
}
kf, err := p.ICMPFilter()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(kf, &f) {
t.Fatalf("got %#v; want %#v", kf, f)
}
}
...@@ -17,6 +17,7 @@ const ( ...@@ -17,6 +17,7 @@ const (
ssoPacketInfo // incbound or outbound packet path ssoPacketInfo // incbound or outbound packet path
ssoHeaderPrepend // ipv4 header prepend ssoHeaderPrepend // ipv4 header prepend
ssoStripHeader // strip ipv4 header ssoStripHeader // strip ipv4 header
ssoICMPFilter // icmp filter
ssoJoinGroup // any-source multicast ssoJoinGroup // any-source multicast
ssoLeaveGroup // any-source multicast ssoLeaveGroup // any-source multicast
ssoJoinSourceGroup // source-specific multicast ssoJoinSourceGroup // source-specific multicast
...@@ -31,6 +32,7 @@ const ( ...@@ -31,6 +32,7 @@ const (
ssoTypeByte = iota + 1 ssoTypeByte = iota + 1
ssoTypeInt ssoTypeInt
ssoTypeInterface ssoTypeInterface
ssoTypeICMPFilter
ssoTypeIPMreq ssoTypeIPMreq
ssoTypeIPMreqn ssoTypeIPMreqn
ssoTypeGroupReq ssoTypeGroupReq
......
...@@ -79,6 +79,25 @@ func setInterface(fd int, opt *sockOpt, ifi *net.Interface) error { ...@@ -79,6 +79,25 @@ func setInterface(fd int, opt *sockOpt, ifi *net.Interface) error {
} }
} }
func getICMPFilter(fd int, opt *sockOpt) (*ICMPFilter, error) {
if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
return nil, errOpNoSupport
}
var f ICMPFilter
l := sysSockoptLen(sysSizeofICMPFilter)
if err := getsockopt(fd, iana.ProtocolReserved, opt.name, unsafe.Pointer(&f.sysICMPFilter), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return &f, nil
}
func setICMPFilter(fd int, opt *sockOpt, f *ICMPFilter) error {
if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
return errOpNoSupport
}
return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolReserved, opt.name, unsafe.Pointer(&f.sysICMPFilter), sysSizeofICMPFilter))
}
func setGroup(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error { func setGroup(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
if opt.name < 1 { if opt.name < 1 {
return errOpNoSupport return errOpNoSupport
......
...@@ -47,6 +47,14 @@ func setInterface(fd syscall.Handle, opt *sockOpt, ifi *net.Interface) error { ...@@ -47,6 +47,14 @@ func setInterface(fd syscall.Handle, opt *sockOpt, ifi *net.Interface) error {
return setsockoptInterface(fd, opt.name, ifi) return setsockoptInterface(fd, opt.name, ifi)
} }
func getICMPFilter(fd syscall.Handle, opt *sockOpt) (*ICMPFilter, error) {
return nil, errOpNoSupport
}
func setICMPFilter(fd syscall.Handle, opt *sockOpt, f *ICMPFilter) error {
return errOpNoSupport
}
func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) error { func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
if opt.name < 1 || opt.typ != ssoTypeIPMreq { if opt.name < 1 || opt.typ != ssoTypeIPMreq {
return errOpNoSupport return errOpNoSupport
......
...@@ -27,6 +27,7 @@ var ( ...@@ -27,6 +27,7 @@ var (
ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt}, ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
ssoPacketInfo: {sysIP_PKTINFO, ssoTypeInt}, ssoPacketInfo: {sysIP_PKTINFO, ssoTypeInt},
ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt}, ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
ssoICMPFilter: {sysICMP_FILTER, ssoTypeICMPFilter},
ssoJoinGroup: {sysMCAST_JOIN_GROUP, ssoTypeGroupReq}, ssoJoinGroup: {sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
ssoLeaveGroup: {sysMCAST_LEAVE_GROUP, ssoTypeGroupReq}, ssoLeaveGroup: {sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
ssoJoinSourceGroup: {sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq}, ssoJoinSourceGroup: {sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment