go.net/ipv4: restructure sticky socket option handling
This CL chops existing sticky socket option handlers and puts them
into platform dependent sticky socket option binding table for
supporting multicast features such as source filtering for any-source
multicast, source-specific multicast.
Also adds tiny syscall shims to help to support solaris, to improve
existing platform support.
LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/140640045
diff --git a/ipv4/control_bsd.go b/ipv4/control_bsd.go
index 38ef01f..1a887cf 100644
--- a/ipv4/control_bsd.go
+++ b/ipv4/control_bsd.go
@@ -16,7 +16,7 @@
opt.Lock()
defer opt.Unlock()
if cf&FlagTTL != 0 {
- if err := setIPv4ReceiveTTL(fd, on); err != nil {
+ if err := setInt(fd, &sockOpts[ssoReceiveTTL], boolint(on)); err != nil {
return err
}
if on {
@@ -25,9 +25,9 @@
opt.clear(FlagTTL)
}
}
- if supportsPacketInfo {
+ if sockOpts[ssoPacketInfo].name > 0 {
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
- if err := setIPv4PacketInfo(fd, on); err != nil {
+ if err := setInt(fd, &sockOpts[ssoPacketInfo], boolint(on)); err != nil {
return err
}
if on {
@@ -39,7 +39,7 @@
}
} else {
if cf&FlagDst != 0 {
- if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil {
+ if err := setInt(fd, &sockOpts[ssoReceiveDst], boolint(on)); err != nil {
return err
}
if on {
@@ -49,7 +49,7 @@
}
}
if cf&FlagInterface != 0 {
- if err := setIPv4ReceiveInterface(fd, on); err != nil {
+ if err := setInt(fd, &sockOpts[ssoReceiveInterface], boolint(on)); err != nil {
return err
}
if on {
@@ -66,9 +66,9 @@
if opt.isset(FlagTTL) {
l += syscall.CmsgSpace(1)
}
- if supportsPacketInfo {
+ if sockOpts[ssoPacketInfo].name > 0 {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
- l += syscall.CmsgSpace(sysSizeofPacketInfo)
+ l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
} else {
if opt.isset(FlagDst) {
@@ -87,30 +87,30 @@
if opt.isset(FlagTTL) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
- m.Type = sysSockoptReceiveTTL
+ m.Type = sysIP_RECVTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
}
- if supportsPacketInfo {
+ if sockOpts[ssoPacketInfo].name > 0 {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
- m.Type = sysSockoptPacketInfo
- m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
- off += syscall.CmsgSpace(sysSizeofPacketInfo)
+ m.Type = sysIP_PKTINFO
+ m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
+ off += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
} else {
if opt.isset(FlagDst) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
- m.Type = sysSockoptReceiveDst
+ m.Type = sysIP_RECVDSTADDR
m.SetLen(syscall.CmsgLen(net.IPv4len))
off += syscall.CmsgSpace(net.IPv4len)
}
if opt.isset(FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
- m.Type = sysSockoptReceiveInterface
+ m.Type = sysIP_RECVIF
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
off += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
}
@@ -119,24 +119,24 @@
}
func (cm *ControlMessage) oobLen() (l int) {
- if supportsPacketInfo && (cm.Src.To4() != nil || cm.IfIndex != 0) {
- l += syscall.CmsgSpace(sysSizeofPacketInfo)
+ if sockOpts[ssoPacketInfo].name > 0 && (cm.Src.To4() != nil || cm.IfIndex != 0) {
+ l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) {
switch m.Header.Type {
- case sysSockoptReceiveTTL:
+ case sysIP_RECVTTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
- case sysSockoptReceiveDst:
+ case sysIP_RECVDSTADDR:
cm.Dst = m.Data[:net.IPv4len]
- case sysSockoptReceiveInterface:
+ case sysIP_RECVIF:
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(sadl.Index)
- case sysSockoptPacketInfo:
- pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
- cm.IfIndex = int(pi.IfIndex)
- cm.Dst = pi.IP[:]
+ case sysIP_PKTINFO:
+ pi := (*sysInetPktinfo)(unsafe.Pointer(&m.Data[0]))
+ cm.IfIndex = int(pi.Ifindex)
+ cm.Dst = pi.Addr[:]
}
}
diff --git a/ipv4/control_linux.go b/ipv4/control_linux.go
index 5685367..a9091ad 100644
--- a/ipv4/control_linux.go
+++ b/ipv4/control_linux.go
@@ -13,7 +13,7 @@
opt.Lock()
defer opt.Unlock()
if cf&FlagTTL != 0 {
- if err := setIPv4ReceiveTTL(fd, on); err != nil {
+ if err := setInt(fd, &sockOpts[ssoReceiveTTL], boolint(on)); err != nil {
return err
}
if on {
@@ -23,7 +23,7 @@
}
}
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
- if err := setIPv4PacketInfo(fd, on); err != nil {
+ if err := setInt(fd, &sockOpts[ssoPacketInfo], boolint(on)); err != nil {
return err
}
if on {
@@ -40,7 +40,7 @@
l += syscall.CmsgSpace(1)
}
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
- l += syscall.CmsgSpace(sysSizeofPacketInfo)
+ l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
@@ -51,34 +51,34 @@
if opt.isset(FlagTTL) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
- m.Type = sysSockoptReceiveTTL
+ m.Type = sysIP_RECVTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
}
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[0]))
m.Level = ianaProtocolIP
- m.Type = sysSockoptPacketInfo
- m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
- off += syscall.CmsgSpace(sysSizeofPacketInfo)
+ m.Type = sysIP_PKTINFO
+ m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
+ off += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
func (cm *ControlMessage) oobLen() (l int) {
if cm.Src.To4() != nil || cm.IfIndex != 0 {
- l += syscall.CmsgSpace(sysSizeofPacketInfo)
+ l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) {
switch m.Header.Type {
- case sysSockoptTTL:
+ case sysIP_TTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
- case sysSockoptPacketInfo:
- pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
- cm.IfIndex = int(pi.IfIndex)
- cm.Dst = pi.IP[:]
+ case sysIP_PKTINFO:
+ pi := (*sysInetPktinfo)(unsafe.Pointer(&m.Data[0]))
+ cm.IfIndex = int(pi.Ifindex)
+ cm.Dst = pi.Addr[:]
}
}
diff --git a/ipv4/control_pktinfo.go b/ipv4/control_pktinfo.go
index 4778ef1..1a7c457 100644
--- a/ipv4/control_pktinfo.go
+++ b/ipv4/control_pktinfo.go
@@ -16,14 +16,14 @@
oob = make([]byte, l)
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[0]))
m.Level = ianaProtocolIP
- m.Type = sysSockoptPacketInfo
- m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
- pi := (*sysPacketInfo)(unsafe.Pointer(&oob[syscall.CmsgLen(0)]))
+ m.Type = sysIP_PKTINFO
+ m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
+ pi := (*sysInetPktinfo)(unsafe.Pointer(&oob[syscall.CmsgLen(0)]))
if ip := cm.Src.To4(); ip != nil {
- copy(pi.IP[:], ip)
+ copy(pi.Addr[:], ip)
}
if cm.IfIndex != 0 {
- pi.IfIndex = int32(cm.IfIndex)
+ pi.setIfindex(cm.IfIndex)
}
}
return
diff --git a/ipv4/dgramopt_posix.go b/ipv4/dgramopt_posix.go
index 874afde..fce881a 100644
--- a/ipv4/dgramopt_posix.go
+++ b/ipv4/dgramopt_posix.go
@@ -21,7 +21,7 @@
if err != nil {
return 0, err
}
- return ipv4MulticastTTL(fd)
+ return getInt(fd, &sockOpts[ssoMulticastTTL])
}
// SetMulticastTTL sets the time-to-live field value for future
@@ -34,7 +34,7 @@
if err != nil {
return err
}
- return setIPv4MulticastTTL(fd, ttl)
+ return setInt(fd, &sockOpts[ssoMulticastTTL], ttl)
}
// MulticastInterface returns the default interface for multicast
@@ -47,7 +47,7 @@
if err != nil {
return nil, err
}
- return ipv4MulticastInterface(fd)
+ return getInterface(fd, &sockOpts[ssoMulticastInterface])
}
// SetMulticastInterface sets the default interface for future
@@ -60,7 +60,7 @@
if err != nil {
return err
}
- return setIPv4MulticastInterface(fd, ifi)
+ return setInterface(fd, &sockOpts[ssoMulticastInterface], ifi)
}
// MulticastLoopback reports whether transmitted multicast packets
@@ -73,7 +73,11 @@
if err != nil {
return false, err
}
- return ipv4MulticastLoopback(fd)
+ on, err := getInt(fd, &sockOpts[ssoMulticastLoopback])
+ if err != nil {
+ return false, err
+ }
+ return on == 1, nil
}
// SetMulticastLoopback sets whether transmitted multicast packets
@@ -86,7 +90,7 @@
if err != nil {
return err
}
- return setIPv4MulticastLoopback(fd, on)
+ return setInt(fd, &sockOpts[ssoMulticastLoopback], boolint(on))
}
// JoinGroup joins the group address group on the interface ifi.
@@ -105,7 +109,7 @@
if grp == nil {
return errMissingAddress
}
- return joinIPv4Group(fd, ifi, grp)
+ return setGroup(fd, &sockOpts[ssoJoinGroup], ifi, grp)
}
// LeaveGroup leaves the group address group on the interface ifi.
@@ -121,5 +125,5 @@
if grp == nil {
return errMissingAddress
}
- return leaveIPv4Group(fd, ifi, grp)
+ return setGroup(fd, &sockOpts[ssoLeaveGroup], ifi, grp)
}
diff --git a/ipv4/endpoint.go b/ipv4/endpoint.go
index 44bd2bc..27cc580 100644
--- a/ipv4/endpoint.go
+++ b/ipv4/endpoint.go
@@ -174,7 +174,7 @@
if err != nil {
return nil, err
}
- if err := setIPv4HeaderPrepend(fd, true); err != nil {
+ if err := setInt(fd, &sockOpts[ssoHeaderPrepend], boolint(true)); err != nil {
return nil, err
}
return r, nil
diff --git a/ipv4/genericopt_posix.go b/ipv4/genericopt_posix.go
index 483c53a..fefa0be 100644
--- a/ipv4/genericopt_posix.go
+++ b/ipv4/genericopt_posix.go
@@ -6,9 +6,7 @@
package ipv4
-import (
- "syscall"
-)
+import "syscall"
// TOS returns the type-of-service field value for outgoing packets.
func (c *genericOpt) TOS() (int, error) {
@@ -19,7 +17,7 @@
if err != nil {
return 0, err
}
- return ipv4TOS(fd)
+ return getInt(fd, &sockOpts[ssoTOS])
}
// SetTOS sets the type-of-service field value for future outgoing
@@ -32,7 +30,7 @@
if err != nil {
return err
}
- return setIPv4TOS(fd, tos)
+ return setInt(fd, &sockOpts[ssoTOS], tos)
}
// TTL returns the time-to-live field value for outgoing packets.
@@ -44,7 +42,7 @@
if err != nil {
return 0, err
}
- return ipv4TTL(fd)
+ return getInt(fd, &sockOpts[ssoTTL])
}
// SetTTL sets the time-to-live field value for future outgoing
@@ -57,5 +55,5 @@
if err != nil {
return err
}
- return setIPv4TTL(fd, ttl)
+ return setInt(fd, &sockOpts[ssoTTL], ttl)
}
diff --git a/ipv4/helper.go b/ipv4/helper.go
index fd4a820..20c2d24 100644
--- a/ipv4/helper.go
+++ b/ipv4/helper.go
@@ -35,52 +35,3 @@
}
return nil
}
-
-func netIP4ToInterface(ip net.IP) (*net.Interface, error) {
- ift, err := net.Interfaces()
- if err != nil {
- return nil, err
- }
- for _, ifi := range ift {
- ifat, err := ifi.Addrs()
- if err != nil {
- return nil, err
- }
- for _, ifa := range ifat {
- switch v := ifa.(type) {
- case *net.IPAddr:
- if ip.Equal(v.IP) {
- return &ifi, nil
- }
- case *net.IPNet:
- if ip.Equal(v.IP) {
- return &ifi, nil
- }
- }
- }
- }
- return nil, errNoSuchInterface
-}
-
-func netInterfaceToIP4(ifi *net.Interface) (net.IP, error) {
- if ifi == nil {
- return net.IPv4zero, nil
- }
- ifat, err := ifi.Addrs()
- if err != nil {
- return nil, err
- }
- for _, ifa := range ifat {
- switch v := ifa.(type) {
- case *net.IPAddr:
- if v.IP.To4() != nil {
- return v.IP, nil
- }
- case *net.IPNet:
- if v.IP.To4() != nil {
- return v.IP, nil
- }
- }
- }
- return nil, errNoSuchInterface
-}
diff --git a/ipv4/sockopt.go b/ipv4/sockopt.go
new file mode 100644
index 0000000..4f4e5b5
--- /dev/null
+++ b/ipv4/sockopt.go
@@ -0,0 +1,37 @@
+// 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
+
+// Sticky socket options
+const (
+ ssoTOS = iota // header field for unicast packet
+ ssoTTL // header field for unicast packet
+ ssoMulticastTTL // header field for multicast packet
+ ssoMulticastInterface // outbound interface for multicast packet
+ ssoMulticastLoopback // loopback for multicast packet
+ ssoReceiveTTL // header field on received packet
+ ssoReceiveDst // header field on received packet
+ ssoReceiveInterface // inbound interface on received packet
+ ssoPacketInfo // incbound or outbound packet path
+ ssoHeaderPrepend // ipv4 header
+ ssoJoinGroup // any-source multicast
+ ssoLeaveGroup // any-source multicast
+ ssoMax
+)
+
+// Sticky socket option value types
+const (
+ ssoTypeByte = iota + 1
+ ssoTypeInt
+ ssoTypeInterface
+ ssoTypeIPMreq
+ ssoTypeIPMreqn
+)
+
+// A sockOpt represents a binding for sticky socket option.
+type sockOpt struct {
+ name int // option name, must be equal or greater than 1
+ typ int // option value type, must be equal or greater than 1
+}
diff --git a/ipv4/sockopt_asmreq.go b/ipv4/sockopt_asmreq.go
new file mode 100644
index 0000000..4a6aa78
--- /dev/null
+++ b/ipv4/sockopt_asmreq.go
@@ -0,0 +1,83 @@
+// Copyright 2012 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 darwin dragonfly freebsd netbsd openbsd windows
+
+package ipv4
+
+import "net"
+
+func setIPMreqInterface(mreq *sysIPMreq, ifi *net.Interface) error {
+ if ifi == nil {
+ return nil
+ }
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ return err
+ }
+ for _, ifa := range ifat {
+ switch ifa := ifa.(type) {
+ case *net.IPAddr:
+ if ip := ifa.IP.To4(); ip != nil {
+ copy(mreq.Interface[:], ip)
+ return nil
+ }
+ case *net.IPNet:
+ if ip := ifa.IP.To4(); ip != nil {
+ copy(mreq.Interface[:], ip)
+ return nil
+ }
+ }
+ }
+ return errNoSuchInterface
+}
+
+func netIP4ToInterface(ip net.IP) (*net.Interface, error) {
+ ift, err := net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifi := range ift {
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifa := range ifat {
+ switch ifa := ifa.(type) {
+ case *net.IPAddr:
+ if ip.Equal(ifa.IP) {
+ return &ifi, nil
+ }
+ case *net.IPNet:
+ if ip.Equal(ifa.IP) {
+ return &ifi, nil
+ }
+ }
+ }
+ }
+ return nil, errNoSuchInterface
+}
+
+func netInterfaceToIP4(ifi *net.Interface) (net.IP, error) {
+ if ifi == nil {
+ return net.IPv4zero.To4(), nil
+ }
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifa := range ifat {
+ switch ifa := ifa.(type) {
+ case *net.IPAddr:
+ if ip := ifa.IP.To4(); ip != nil {
+ return ip, nil
+ }
+ case *net.IPNet:
+ if ip := ifa.IP.To4(); ip != nil {
+ return ip, nil
+ }
+ }
+ }
+ return nil, errNoSuchInterface
+}
diff --git a/ipv4/sockopt_asmreq_stub.go b/ipv4/sockopt_asmreq_stub.go
new file mode 100644
index 0000000..4555152
--- /dev/null
+++ b/ipv4/sockopt_asmreq_stub.go
@@ -0,0 +1,21 @@
+// Copyright 2012 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 !darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!windows
+
+package ipv4
+
+import "net"
+
+func setsockoptIPMreq(fd, name int, ifi *net.Interface, grp net.IP) error {
+ return errOpNoSupport
+}
+
+func getsockoptInterface(fd, name int) (*net.Interface, error) {
+ return nil, errOpNoSupport
+}
+
+func setsockoptInterface(fd, name int, ifi *net.Interface) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/sockopt_asmreq_unix.go b/ipv4/sockopt_asmreq_unix.go
new file mode 100644
index 0000000..f2aed63
--- /dev/null
+++ b/ipv4/sockopt_asmreq_unix.go
@@ -0,0 +1,44 @@
+// Copyright 2012 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 darwin dragonfly freebsd netbsd openbsd
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "unsafe"
+)
+
+func setsockoptIPMreq(fd, name int, ifi *net.Interface, grp net.IP) error {
+ mreq := sysIPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+ if err := setIPMreqInterface(&mreq, ifi); err != nil {
+ return err
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIP, name, unsafe.Pointer(&mreq), sysSizeofIPMreq))
+}
+
+func getsockoptInterface(fd, name int) (*net.Interface, error) {
+ var b [4]byte
+ l := sysSockoptLen(4)
+ if err := getsockopt(fd, ianaProtocolIP, name, unsafe.Pointer(&b[0]), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ ifi, err := netIP4ToInterface(net.IPv4(b[0], b[1], b[2], b[3]))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setsockoptInterface(fd, name int, ifi *net.Interface) error {
+ ip, err := netInterfaceToIP4(ifi)
+ if err != nil {
+ return err
+ }
+ var b [4]byte
+ copy(b[:], ip)
+ return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIP, name, unsafe.Pointer(&b[0]), sysSockoptLen(4)))
+}
diff --git a/ipv4/sockopt_asmreq_windows.go b/ipv4/sockopt_asmreq_windows.go
new file mode 100644
index 0000000..9591f97
--- /dev/null
+++ b/ipv4/sockopt_asmreq_windows.go
@@ -0,0 +1,43 @@
+// Copyright 2012 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 (
+ "net"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func setsockoptIPMreq(fd syscall.Handle, name int, ifi *net.Interface, grp net.IP) error {
+ mreq := sysIPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+ if err := setIPMreqInterface(&mreq, ifi); err != nil {
+ return err
+ }
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, int32(name), (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofIPMreq)))
+}
+
+func getsockoptInterface(fd syscall.Handle, name int) (*net.Interface, error) {
+ var b [4]byte
+ l := int32(4)
+ if err := syscall.Getsockopt(fd, ianaProtocolIP, int32(name), (*byte)(unsafe.Pointer(&b[0])), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ ifi, err := netIP4ToInterface(net.IPv4(b[0], b[1], b[2], b[3]))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setsockoptInterface(fd syscall.Handle, name int, ifi *net.Interface) error {
+ ip, err := netInterfaceToIP4(ifi)
+ if err != nil {
+ return err
+ }
+ var b [4]byte
+ copy(b[:], ip)
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, int32(name), (*byte)(unsafe.Pointer(&b[0])), 4))
+}
diff --git a/ipv4/sockopt_asmreqn_stub.go b/ipv4/sockopt_asmreqn_stub.go
new file mode 100644
index 0000000..332f403
--- /dev/null
+++ b/ipv4/sockopt_asmreqn_stub.go
@@ -0,0 +1,17 @@
+// 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 !darwin,!freebsd,!linux,!windows
+
+package ipv4
+
+import "net"
+
+func getsockoptIPMreqn(fd, name int) (*net.Interface, error) {
+ return nil, errOpNoSupport
+}
+
+func setsockoptIPMreqn(fd, name int, ifi *net.Interface, grp net.IP) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/sockopt_asmreqn_unix.go b/ipv4/sockopt_asmreqn_unix.go
new file mode 100644
index 0000000..fb18db6
--- /dev/null
+++ b/ipv4/sockopt_asmreqn_unix.go
@@ -0,0 +1,40 @@
+// 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 darwin freebsd linux
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "unsafe"
+)
+
+func getsockoptIPMreqn(fd, name int) (*net.Interface, error) {
+ var mreqn sysIPMreqn
+ l := sysSockoptLen(sysSizeofIPMreqn)
+ if err := getsockopt(fd, ianaProtocolIP, name, unsafe.Pointer(&mreqn), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ if mreqn.Ifindex == 0 {
+ return nil, nil
+ }
+ ifi, err := net.InterfaceByIndex(int(mreqn.Ifindex))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setsockoptIPMreqn(fd, name int, ifi *net.Interface, grp net.IP) error {
+ var mreqn sysIPMreqn
+ if ifi != nil {
+ mreqn.Ifindex = int32(ifi.Index)
+ }
+ if grp != nil {
+ mreqn.Multiaddr = [4]byte{grp[0], grp[1], grp[2], grp[3]}
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIP, name, unsafe.Pointer(&mreqn), sysSizeofIPMreqn))
+}
diff --git a/ipv4/sockopt_bsd.go b/ipv4/sockopt_bsd.go
deleted file mode 100644
index b793f5c..0000000
--- a/ipv4/sockopt_bsd.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2012 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 darwin dragonfly freebsd netbsd openbsd
-
-package ipv4
-
-import (
- "net"
- "os"
- "syscall"
-)
-
-func ipv4MulticastTTL(fd int) (int, error) {
- v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastTTL)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return int(v), nil
-}
-
-func setIPv4MulticastTTL(fd int, v int) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastTTL, byte(v)))
-}
-
-func ipv4ReceiveDestinationAddress(fd int) (bool, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveDst)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveDestinationAddress(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveDst, boolint(v)))
-}
-
-func ipv4ReceiveInterface(fd int) (bool, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveInterface)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveInterface, boolint(v)))
-}
-
-func ipv4MulticastInterface(fd int) (*net.Interface, error) {
- v, err := syscall.GetsockoptInet4Addr(fd, ianaProtocolIP, sysSockoptMulticastInterface)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- return netIP4ToInterface(net.IPv4(v[0], v[1], v[2], v[3]))
-}
-
-func setIPv4MulticastInterface(fd int, ifi *net.Interface) error {
- ip, err := netInterfaceToIP4(ifi)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- var v [4]byte
- copy(v[:], ip.To4())
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd, ianaProtocolIP, sysSockoptMulticastInterface, v))
-}
-
-func ipv4MulticastLoopback(fd int) (bool, error) {
- v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastLoopback)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastLoopback, byte(boolint(v))))
-}
diff --git a/ipv4/sockopt_linux.go b/ipv4/sockopt_linux.go
deleted file mode 100644
index 6ec58de..0000000
--- a/ipv4/sockopt_linux.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2012 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 (
- "net"
- "os"
- "syscall"
-)
-
-func ipv4ReceiveTOS(fd int) (bool, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTOS)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveTOS(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTOS, boolint(v)))
-}
-
-func ipv4MulticastTTL(fd int) (int, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastTTL)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv4MulticastTTL(fd int, v int) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastTTL, v))
-}
-
-func ipv4MulticastInterface(fd int) (*net.Interface, error) {
- mreqn, err := syscall.GetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptMulticastInterface)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- if mreqn.Ifindex == 0 {
- return nil, nil
- }
- return net.InterfaceByIndex(int(mreqn.Ifindex))
-}
-
-func setIPv4MulticastInterface(fd int, ifi *net.Interface) error {
- var mreqn syscall.IPMreqn
- if ifi != nil {
- mreqn.Ifindex = int32(ifi.Index)
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptMulticastInterface, &mreqn))
-}
-
-func ipv4MulticastLoopback(fd int) (bool, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastLoopback)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, boolint(v)))
-}
diff --git a/ipv4/sockopt_mreq.go b/ipv4/sockopt_mreq.go
deleted file mode 100644
index 9d5abbf..0000000
--- a/ipv4/sockopt_mreq.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 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 darwin dragonfly freebsd netbsd openbsd
-
-package ipv4
-
-import (
- "net"
- "os"
- "syscall"
-)
-
-func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
- mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
- if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
- return err
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, sysSockoptJoinGroup, &mreq))
-}
-
-func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
- mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
- if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
- return err
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, sysSockoptLeaveGroup, &mreq))
-}
diff --git a/ipv4/sockopt_mreqn.go b/ipv4/sockopt_mreqn.go
deleted file mode 100644
index 86993d1..0000000
--- a/ipv4/sockopt_mreqn.go
+++ /dev/null
@@ -1,29 +0,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
-
-import (
- "net"
- "os"
- "syscall"
-)
-
-func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
- mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
- if ifi != nil {
- mreqn.Ifindex = int32(ifi.Index)
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptJoinGroup, &mreqn))
-}
-
-func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
- mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
- if ifi != nil {
- mreqn.Ifindex = int32(ifi.Index)
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptLeaveGroup, &mreqn))
-}
diff --git a/ipv4/sockopt_nonpktinfo.go b/ipv4/sockopt_nonpktinfo.go
deleted file mode 100644
index 5592b46..0000000
--- a/ipv4/sockopt_nonpktinfo.go
+++ /dev/null
@@ -1,15 +0,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 !darwin,!linux
-
-package ipv4
-
-func ipv4PacketInfo(fd int) (bool, error) {
- return false, errOpNoSupport
-}
-
-func setIPv4PacketInfo(fd int, v bool) error {
- return errOpNoSupport
-}
diff --git a/ipv4/sockopt_pktinfo.go b/ipv4/sockopt_pktinfo.go
deleted file mode 100644
index 5506b88..0000000
--- a/ipv4/sockopt_pktinfo.go
+++ /dev/null
@@ -1,24 +0,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 darwin linux
-
-package ipv4
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4PacketInfo(fd int) (bool, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptPacketInfo)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4PacketInfo(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptPacketInfo, boolint(v)))
-}
diff --git a/ipv4/sockopt_stub.go b/ipv4/sockopt_stub.go
index 7bcc893..5ba892f 100644
--- a/ipv4/sockopt_stub.go
+++ b/ipv4/sockopt_stub.go
@@ -6,12 +6,24 @@
package ipv4
-func ipv4HeaderPrepend(fd int) (bool, error) {
- // TODO(mikio): Implement this
- return false, errOpNoSupport
+import "net"
+
+func getInt(fd int, opt *sockOpt) (int, error) {
+ return 0, errOpNoSupport
}
-func setIPv4HeaderPrepend(fd int, v bool) error {
- // TODO(mikio): Implement this
+func setInt(fd int, opt *sockOpt, v int) error {
+ return errOpNoSupport
+}
+
+func getInterface(fd int, opt *sockOpt) (*net.Interface, error) {
+ return nil, errOpNoSupport
+}
+
+func setInterface(fd int, opt *sockOpt, ifi *net.Interface) error {
+ return errOpNoSupport
+}
+
+func setGroup(fd int, opt *sockOpt, ifi *net.Interface, ip net.IP) error {
return errOpNoSupport
}
diff --git a/ipv4/sockopt_unix.go b/ipv4/sockopt_unix.go
index a96fe25..a6531ad 100644
--- a/ipv4/sockopt_unix.go
+++ b/ipv4/sockopt_unix.go
@@ -7,54 +7,86 @@
package ipv4
import (
+ "net"
"os"
- "syscall"
+ "unsafe"
)
-func ipv4TOS(fd int) (int, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptTOS)
- if err != nil {
+func getInt(fd int, opt *sockOpt) (int, error) {
+ if opt.name < 1 || (opt.typ != ssoTypeByte && opt.typ != ssoTypeInt) {
+ return 0, errOpNoSupport
+ }
+ var i int32
+ var b byte
+ p := unsafe.Pointer(&i)
+ l := sysSockoptLen(4)
+ if opt.typ == ssoTypeByte {
+ p = unsafe.Pointer(&b)
+ l = sysSockoptLen(1)
+ }
+ if err := getsockopt(fd, ianaProtocolIP, opt.name, p, &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
- return v, nil
-}
-
-func setIPv4TOS(fd int, v int) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptTOS, v))
-}
-
-func ipv4TTL(fd int) (int, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptTTL)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
+ if opt.typ == ssoTypeByte {
+ return int(b), nil
}
- return v, nil
+ return int(i), nil
}
-func setIPv4TTL(fd int, v int) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptTTL, v))
-}
-
-func ipv4ReceiveTTL(fd int) (bool, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTTL)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
+func setInt(fd int, opt *sockOpt, v int) error {
+ if opt.name < 1 || (opt.typ != ssoTypeByte && opt.typ != ssoTypeInt) {
+ return errOpNoSupport
}
- return v == 1, nil
-}
-
-func setIPv4ReceiveTTL(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTTL, boolint(v)))
-}
-
-func ipv4HeaderPrepend(fd int) (bool, error) {
- v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
+ i := int32(v)
+ var b byte
+ p := unsafe.Pointer(&i)
+ l := sysSockoptLen(4)
+ if opt.typ == ssoTypeByte {
+ b = byte(v)
+ p = unsafe.Pointer(&b)
+ l = sysSockoptLen(1)
}
- return v == 1, nil
+ return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIP, opt.name, p, l))
}
-func setIPv4HeaderPrepend(fd int, v bool) error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend, boolint(v)))
+func getInterface(fd int, opt *sockOpt) (*net.Interface, error) {
+ if opt.name < 1 {
+ return nil, errOpNoSupport
+ }
+ switch opt.typ {
+ case ssoTypeInterface:
+ return getsockoptInterface(fd, opt.name)
+ case ssoTypeIPMreqn:
+ return getsockoptIPMreqn(fd, opt.name)
+ default:
+ return nil, errOpNoSupport
+ }
+}
+
+func setInterface(fd int, opt *sockOpt, ifi *net.Interface) error {
+ if opt.name < 1 {
+ return errOpNoSupport
+ }
+ switch opt.typ {
+ case ssoTypeInterface:
+ return setsockoptInterface(fd, opt.name, ifi)
+ case ssoTypeIPMreqn:
+ return setsockoptIPMreqn(fd, opt.name, ifi, nil)
+ default:
+ return errOpNoSupport
+ }
+}
+
+func setGroup(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ if opt.name < 1 {
+ return errOpNoSupport
+ }
+ switch opt.typ {
+ case ssoTypeIPMreq:
+ return setsockoptIPMreq(fd, opt.name, ifi, grp)
+ case ssoTypeIPMreqn:
+ return setsockoptIPMreqn(fd, opt.name, ifi, grp)
+ default:
+ return errOpNoSupport
+ }
}
diff --git a/ipv4/sockopt_windows.go b/ipv4/sockopt_windows.go
index e6bf593..d5e79d4 100644
--- a/ipv4/sockopt_windows.go
+++ b/ipv4/sockopt_windows.go
@@ -11,136 +11,43 @@
"unsafe"
)
-// Please refer to the online manual;
-// http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx
-
-func ipv4TOS(fd syscall.Handle) (int, error) {
- var v int32
+func getInt(fd syscall.Handle, opt *sockOpt) (int, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return 0, errOpNoSupport
+ }
+ var i int32
l := int32(4)
- if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTOS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+ if err := syscall.Getsockopt(fd, ianaProtocolIP, int32(opt.name), (*byte)(unsafe.Pointer(&i)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
- return int(v), nil
+ return int(i), nil
}
-func setIPv4TOS(fd syscall.Handle, v int) error {
- vv := int32(v)
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptTOS, (*byte)(unsafe.Pointer(&vv)), 4))
-}
-
-func ipv4TTL(fd syscall.Handle) (int, error) {
- var v int32
- l := int32(4)
- if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
+func setInt(fd syscall.Handle, opt *sockOpt, v int) error {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return errOpNoSupport
}
- return int(v), nil
+ i := int32(v)
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, int32(opt.name), (*byte)(unsafe.Pointer(&i)), 4))
}
-func setIPv4TTL(fd syscall.Handle, v int) error {
- vv := int32(v)
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptTTL, (*byte)(unsafe.Pointer(&vv)), 4))
-}
-
-func ipv4MulticastTTL(fd syscall.Handle) (int, error) {
- var v int32
- l := int32(4)
- if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastTTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
+func getInterface(fd syscall.Handle, opt *sockOpt) (*net.Interface, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return nil, errOpNoSupport
}
- return int(v), nil
+ return getsockoptInterface(fd, opt.name)
}
-func setIPv4MulticastTTL(fd syscall.Handle, v int) error {
- vv := int32(v)
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptMulticastTTL, (*byte)(unsafe.Pointer(&vv)), 4))
-}
-
-func ipv4ReceiveTTL(fd syscall.Handle) (bool, error) {
- // NOTE: Not supported yet on any Windows
- return false, syscall.EWINDOWS
-}
-
-func setIPv4ReceiveTTL(fd syscall.Handle, v bool) error {
- // NOTE: Not supported yet on any Windows
- return syscall.EWINDOWS
-}
-
-func ipv4ReceiveDestinationAddress(fd syscall.Handle) (bool, error) {
- // TODO(mikio): Implement this for XP and beyond
- return false, syscall.EWINDOWS
-}
-
-func setIPv4ReceiveDestinationAddress(fd syscall.Handle, v bool) error {
- // TODO(mikio): Implement this for XP and beyond
- return syscall.EWINDOWS
-}
-
-func ipv4HeaderPrepend(fd syscall.Handle) (bool, error) {
- // TODO(mikio): Implement this for XP and beyond
- return false, syscall.EWINDOWS
-}
-
-func setIPv4HeaderPrepend(fd syscall.Handle, v bool) error {
- // TODO(mikio): Implement this for XP and beyond
- return syscall.EWINDOWS
-}
-
-func ipv4ReceiveInterface(fd syscall.Handle) (bool, error) {
- // TODO(mikio): Implement this for Vista and beyond
- return false, syscall.EWINDOWS
-}
-
-func setIPv4ReceiveInterface(fd syscall.Handle, v bool) error {
- // TODO(mikio): Implement this for Vista and beyond
- return syscall.EWINDOWS
-}
-
-func ipv4MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
- var v [4]byte
- l := int32(4)
- if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v[0])), &l); err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
+func setInterface(fd syscall.Handle, opt *sockOpt, ifi *net.Interface) error {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return errOpNoSupport
}
- return netIP4ToInterface(net.IPv4(v[0], v[1], v[2], v[3]))
+ return setsockoptInterface(fd, opt.name, ifi)
}
-func setIPv4MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
- ip, err := netInterfaceToIP4(ifi)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
+func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ if opt.name < 1 || opt.typ != ssoTypeIPMreq {
+ return errOpNoSupport
}
- var v [4]byte
- copy(v[:], ip.To4())
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v[0])), 4))
-}
-
-func ipv4MulticastLoopback(fd syscall.Handle) (bool, error) {
- var v int32
- l := int32(4)
- if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd syscall.Handle, v bool) error {
- vv := int32(boolint(v))
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&vv)), 4))
-}
-
-func joinIPv4Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
- mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
- if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
- return err
- }
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
-}
-
-func leaveIPv4Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
- mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
- if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
- return err
- }
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
+ return setsockoptIPMreq(fd, opt.name, ifi, grp)
}
diff --git a/ipv4/sys_bsd.go b/ipv4/sys_bsd.go
index 866dadc..07032d6 100644
--- a/ipv4/sys_bsd.go
+++ b/ipv4/sys_bsd.go
@@ -2,36 +2,36 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd netbsd openbsd
+// +build dragonfly netbsd openbsd
package ipv4
-import "syscall"
+type sysSockoptLen int32
const (
- // See /usr/include/netinet/in.h.
- sysSockoptHeaderPrepend = syscall.IP_HDRINCL
- sysSockoptTOS = syscall.IP_TOS
- sysSockoptTTL = syscall.IP_TTL
- sysSockoptMulticastTTL = syscall.IP_MULTICAST_TTL
- sysSockoptMulticastInterface = syscall.IP_MULTICAST_IF
- sysSockoptMulticastLoopback = syscall.IP_MULTICAST_LOOP
- sysSockoptJoinGroup = syscall.IP_ADD_MEMBERSHIP
- sysSockoptLeaveGroup = syscall.IP_DROP_MEMBERSHIP
+ sysIP_PKTINFO = 0
+
+ sysSizeofInetPktinfo = 0xc
)
-const (
- // See /usr/include/netinet/in.h.
- sysSockoptReceiveTTL = syscall.IP_RECVTTL
- sysSockoptReceiveDst = syscall.IP_RECVDSTADDR
- sysSockoptReceiveInterface = syscall.IP_RECVIF
- sysSockoptPacketInfo = 0x1a // only darwin supports this option for now
-)
-
-const sysSizeofPacketInfo = 0xc
-
-type sysPacketInfo struct {
- IfIndex int32
- RoutedIP [4]byte
- IP [4]byte
+type sysInetPktinfo struct {
+ Ifindex uint32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
}
+
+var (
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeByte},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt},
+ ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
+)
diff --git a/ipv4/sys_darwin.go b/ipv4/sys_darwin.go
index 3dee19d..6c18e96 100644
--- a/ipv4/sys_darwin.go
+++ b/ipv4/sys_darwin.go
@@ -6,6 +6,24 @@
import "syscall"
+type sysSockoptLen int32
+
+var (
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeByte},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt},
+ ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
+)
+
func init() {
// Seems like kern.osreldate is veiled on latest OS X. We use
// kern.osrelease instead.
@@ -22,6 +40,12 @@
// The IP_PKTINFO was introduced in OS X 10.7 (Darwin
// 11.0.0). See http://support.apple.com/kb/HT1633.
if i > 2 || i == 2 && osver[0] >= '1' && osver[1] >= '1' {
- supportsPacketInfo = true
+ sockOpts[ssoPacketInfo].name = sysIP_RECVPKTINFO
+ sockOpts[ssoPacketInfo].typ = ssoTypeInt
+ sockOpts[ssoMulticastInterface].typ = ssoTypeIPMreqn
}
}
+
+func (pi *sysInetPktinfo) setIfindex(i int) {
+ pi.Ifindex = uint32(i)
+}
diff --git a/ipv4/sys_freebsd.go b/ipv4/sys_freebsd.go
index f361234..057d9f1 100644
--- a/ipv4/sys_freebsd.go
+++ b/ipv4/sys_freebsd.go
@@ -6,6 +6,39 @@
import "syscall"
+type sysSockoptLen int32
+
+const (
+ sysIP_PKTINFO = 0
+
+ sysSizeofInetPktinfo = 0xc
+)
+
+type sysInetPktinfo struct {
+ Ifindex uint32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+var (
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeByte},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt},
+ ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
+)
+
func init() {
freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate")
+ if freebsdVersion >= 1000000 {
+ sockOpts[ssoMulticastInterface].typ = ssoTypeIPMreqn
+ }
}
diff --git a/ipv4/sys_linux.go b/ipv4/sys_linux.go
index a81b592..fc7cc79 100644
--- a/ipv4/sys_linux.go
+++ b/ipv4/sys_linux.go
@@ -4,44 +4,23 @@
package ipv4
-import "syscall"
+type sysSockoptLen int32
-const (
- // See /usr/include/linux/in.h.
- sysSockoptHeaderPrepend = syscall.IP_HDRINCL
- sysSockoptTOS = syscall.IP_TOS
- sysSockoptTTL = syscall.IP_TTL
- sysSockoptMulticastTTL = syscall.IP_MULTICAST_TTL
- sysSockoptMulticastInterface = syscall.IP_MULTICAST_IF
- sysSockoptMulticastLoopback = syscall.IP_MULTICAST_LOOP
- sysSockoptJoinGroup = syscall.IP_ADD_MEMBERSHIP
- sysSockoptLeaveGroup = syscall.IP_DROP_MEMBERSHIP
+var (
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeInt},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeIPMreqn},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoPacketInfo: {sysIP_PKTINFO, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreqn},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreqn},
+ }
)
-const (
- // See /usr/include/linux/in.h.
- sysSockoptReceiveTOS = syscall.IP_RECVTOS
- sysSockoptReceiveTTL = syscall.IP_RECVTTL
- sysSockoptPacketInfo = syscall.IP_PKTINFO
-)
-
-const (
- sysSizeofNewMulticastReq = 0xc
- sysSizeofPacketInfo = 0xc
-)
-
-type sysNewMulticastReq struct {
- IP [4]byte
- Interface [4]byte
- IfIndex int32
-}
-
-type sysPacketInfo struct {
- IfIndex int32
- RoutedIP [4]byte
- IP [4]byte
-}
-
-func init() {
- supportsPacketInfo = true
+func (pi *sysInetPktinfo) setIfindex(i int) {
+ pi.Ifindex = int32(i)
}
diff --git a/ipv4/sys_mreq.go b/ipv4/sys_mreq.go
deleted file mode 100644
index 07a1ec2..0000000
--- a/ipv4/sys_mreq.go
+++ /dev/null
@@ -1,37 +0,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 darwin dragonfly freebsd netbsd openbsd windows
-
-package ipv4
-
-import (
- "net"
- "syscall"
-)
-
-func setSysIPMreqInterface(mreq *syscall.IPMreq, ifi *net.Interface) error {
- if ifi == nil {
- return nil
- }
- ifat, err := ifi.Addrs()
- if err != nil {
- return err
- }
- for _, ifa := range ifat {
- switch v := ifa.(type) {
- case *net.IPAddr:
- if ip := v.IP.To4(); ip != nil {
- copy(mreq.Interface[:], ip)
- return nil
- }
- case *net.IPNet:
- if ip := v.IP.To4(); ip != nil {
- copy(mreq.Interface[:], ip)
- return nil
- }
- }
- }
- return errNoSuchInterface
-}
diff --git a/ipv4/sys.go b/ipv4/sys_stub.go
similarity index 62%
rename from ipv4/sys.go
rename to ipv4/sys_stub.go
index 31cfbe2..6f2a4a6 100644
--- a/ipv4/sys.go
+++ b/ipv4/sys_stub.go
@@ -2,8 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build nacl plan9 solaris
+
package ipv4
-// supportsPacketInfo reports whether the platform supports
-// IP_PKTINFO.
-var supportsPacketInfo bool
+type sysSockoptLen int32
+
+var (
+ sockOpts = [ssoMax]sockOpt{}
+)
diff --git a/ipv4/sys_windows.go b/ipv4/sys_windows.go
index 1ef7f2a..979b0cd 100644
--- a/ipv4/sys_windows.go
+++ b/ipv4/sys_windows.go
@@ -4,28 +4,56 @@
package ipv4
-import "syscall"
-
const (
// See ws2tcpip.h.
- sysSockoptHeaderPrepend = 0x2
- sysSockoptTOS = syscall.IP_TOS
- sysSockoptTTL = syscall.IP_TTL
- sysSockoptMulticastTTL = syscall.IP_MULTICAST_TTL
- sysSockoptMulticastInterface = syscall.IP_MULTICAST_IF
- sysSockoptMulticastLoopback = syscall.IP_MULTICAST_LOOP
- sysSockoptJoinGroup = syscall.IP_ADD_MEMBERSHIP
- sysSockoptLeaveGroup = syscall.IP_DROP_MEMBERSHIP
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+ sysIP_DONTFRAGMENT = 0xe
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0xf
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x10
+ sysIP_PKTINFO = 0x13
+
+ sysSizeofInetPktinfo = 0x8
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqSource = 0xc
)
-const (
- // See ws2tcpip.h.
- sysSockoptPacketInfo = 0x13
+type sysInetPktinfo struct {
+ Addr [4]byte
+ Ifindex int32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte
+ Interface [4]byte
+}
+
+type sysIPMreqSource struct {
+ Multiaddr [4]byte
+ Sourceaddr [4]byte
+ Interface [4]byte
+}
+
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx
+var (
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeInt},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
)
-const sysSizeofPacketInfo = 0x8
-
-type sysPacketInfo struct {
- IP [4]byte
- IfIndex int32
+func (pi *sysInetPktinfo) setIfindex(i int) {
+ pi.Ifindex = int32(i)
}
diff --git a/ipv4/syscall_linux_386.go b/ipv4/syscall_linux_386.go
new file mode 100644
index 0000000..ab4ad04
--- /dev/null
+++ b/ipv4/syscall_linux_386.go
@@ -0,0 +1,31 @@
+// 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
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ sysGETSOCKOPT = 0xf
+ sysSETSOCKOPT = 0xe
+)
+
+func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+
+func getsockopt(fd, level, name int, v unsafe.Pointer, l *sysSockoptLen) error {
+ if _, errno := socketcall(sysGETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func setsockopt(fd, level, name int, v unsafe.Pointer, l sysSockoptLen) error {
+ if _, errno := socketcall(sysSETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
diff --git a/ipv4/syscall_unix.go b/ipv4/syscall_unix.go
new file mode 100644
index 0000000..29e8f16
--- /dev/null
+++ b/ipv4/syscall_unix.go
@@ -0,0 +1,26 @@
+// 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 darwin dragonfly freebsd linux,amd64 linux,arm netbsd openbsd
+
+package ipv4
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func getsockopt(fd, level, name int, v unsafe.Pointer, l *sysSockoptLen) error {
+ if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func setsockopt(fd, level, name int, v unsafe.Pointer, l sysSockoptLen) error {
+ if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
diff --git a/ipv4/thunk_linux_386.s b/ipv4/thunk_linux_386.s
new file mode 100644
index 0000000..daa78bc
--- /dev/null
+++ b/ipv4/thunk_linux_386.s
@@ -0,0 +1,8 @@
+// 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 go1.2
+
+TEXT ·socketcall(SB),4,$0-36
+ JMP syscall·socketcall(SB)