Browse Source

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
Mikio Hara 11 years ago
parent
commit
353547e1a2

+ 23 - 23
ipv4/control_bsd.go

@@ -16,7 +16,7 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
 	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 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
 			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 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
 		}
 	} 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 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
 			}
 		}
 		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 @@ func (opt *rawOpt) oobLen() (l int) {
 	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 @@ func (opt *rawOpt) marshalControlMessage() (oob []byte) {
 	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 (opt *rawOpt) marshalControlMessage() (oob []byte) {
 }
 
 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[:]
 	}
 }

+ 13 - 13
ipv4/control_linux.go

@@ -13,7 +13,7 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
 	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 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
 		}
 	}
 	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 @@ func (opt *rawOpt) oobLen() (l int) {
 		l += syscall.CmsgSpace(1)
 	}
 	if opt.isset(FlagSrc | FlagDst | FlagInterface) {
-		l += syscall.CmsgSpace(sysSizeofPacketInfo)
+		l += syscall.CmsgSpace(sysSizeofInetPktinfo)
 	}
 	return
 }
@@ -51,34 +51,34 @@ func (opt *rawOpt) marshalControlMessage() (oob []byte) {
 	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[:]
 	}
 }

+ 5 - 5
ipv4/control_pktinfo.go

@@ -16,14 +16,14 @@ func (cm *ControlMessage) marshalPacketInfo() (oob []byte) {
 		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

+ 12 - 8
ipv4/dgramopt_posix.go

@@ -21,7 +21,7 @@ func (c *dgramOpt) MulticastTTL() (int, error) {
 	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 @@ func (c *dgramOpt) SetMulticastTTL(ttl int) error {
 	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 @@ func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
 	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 @@ func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
 	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 @@ func (c *dgramOpt) MulticastLoopback() (bool, error) {
 	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 @@ func (c *dgramOpt) SetMulticastLoopback(on bool) error {
 	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 @@ func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
 	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 @@ func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
 	if grp == nil {
 		return errMissingAddress
 	}
-	return leaveIPv4Group(fd, ifi, grp)
+	return setGroup(fd, &sockOpts[ssoLeaveGroup], ifi, grp)
 }

+ 1 - 1
ipv4/endpoint.go

@@ -174,7 +174,7 @@ func NewRawConn(c net.PacketConn) (*RawConn, error) {
 	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

+ 5 - 7
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 @@ func (c *genericOpt) TOS() (int, error) {
 	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 @@ func (c *genericOpt) SetTOS(tos int) error {
 	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 @@ func (c *genericOpt) TTL() (int, error) {
 	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 @@ func (c *genericOpt) SetTTL(ttl int) error {
 	if err != nil {
 		return err
 	}
-	return setIPv4TTL(fd, ttl)
+	return setInt(fd, &sockOpts[ssoTTL], ttl)
 }

+ 0 - 49
ipv4/helper.go

@@ -35,52 +35,3 @@ func netAddrToIP4(a net.Addr) net.IP {
 	}
 	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
-}

+ 37 - 0
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
+}

+ 83 - 0
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
+}

+ 21 - 0
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
+}

+ 44 - 0
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)))
+}

+ 43 - 0
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))
+}

+ 17 - 0
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
+}

+ 40 - 0
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))
+}

+ 0 - 79
ipv4/sockopt_bsd.go

@@ -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))))
-}

+ 0 - 66
ipv4/sockopt_linux.go

@@ -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)))
-}

+ 0 - 29
ipv4/sockopt_mreq.go

@@ -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))
-}

+ 0 - 29
ipv4/sockopt_mreqn.go

@@ -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))
-}

+ 0 - 15
ipv4/sockopt_nonpktinfo.go

@@ -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
-}

+ 0 - 24
ipv4/sockopt_pktinfo.go

@@ -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)))
-}

+ 17 - 5
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 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 setIPv4HeaderPrepend(fd int, v bool) error {
-	// TODO(mikio): Implement this
+func setGroup(fd int, opt *sockOpt, ifi *net.Interface, ip net.IP) error {
 	return errOpNoSupport
 }

+ 66 - 34
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 {
-		return 0, os.NewSyscallError("getsockopt", err)
+func getInt(fd int, opt *sockOpt) (int, error) {
+	if opt.name < 1 || (opt.typ != ssoTypeByte && opt.typ != ssoTypeInt) {
+		return 0, errOpNoSupport
 	}
-	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 {
+	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 setIPv4TTL(fd int, v int) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptTTL, v))
+	if opt.typ == ssoTypeByte {
+		return int(b), nil
+	}
+	return int(i), nil
 }
 
-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
+	}
+	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 setIPv4ReceiveTTL(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTTL, 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 ipv4HeaderPrepend(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend)
-	if err != nil {
-		return false, os.NewSyscallError("getsockopt", err)
+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
 	}
-	return v == 1, nil
 }
 
-func setIPv4HeaderPrepend(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend, boolint(v)))
+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
+	}
 }

+ 23 - 116
ipv4/sockopt_windows.go

@@ -11,136 +11,43 @@ import (
 	"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
-	l := int32(4)
-	if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTOS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
-		return 0, os.NewSyscallError("getsockopt", err)
+func getInt(fd syscall.Handle, opt *sockOpt) (int, error) {
+	if opt.name < 1 || opt.typ != ssoTypeInt {
+		return 0, errOpNoSupport
 	}
-	return int(v), 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
+	var i int32
 	l := int32(4)
-	if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTTL, (*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 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 setInt(fd syscall.Handle, opt *sockOpt, v int) error {
+	if opt.name < 1 || opt.typ != ssoTypeInt {
+		return errOpNoSupport
 	}
-	return int(v), nil
-}
-
-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
+	i := int32(v)
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, int32(opt.name), (*byte)(unsafe.Pointer(&i)), 4))
 }
 
-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 getInterface(fd syscall.Handle, opt *sockOpt) (*net.Interface, error) {
+	if opt.name < 1 || opt.typ != ssoTypeInterface {
+		return nil, errOpNoSupport
 	}
-	return netIP4ToInterface(net.IPv4(v[0], v[1], v[2], v[3]))
-}
-
-func setIPv4MulticastInterface(fd syscall.Handle, 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.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))
+	return getsockoptInterface(fd, opt.name)
 }
 
-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
+func setInterface(fd syscall.Handle, opt *sockOpt, ifi *net.Interface) error {
+	if opt.name < 1 || opt.typ != ssoTypeInterface {
+		return errOpNoSupport
 	}
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
+	return setsockoptInterface(fd, opt.name, ifi)
 }
 
-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
+func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+	if opt.name < 1 || opt.typ != ssoTypeIPMreq {
+		return errOpNoSupport
 	}
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
+	return setsockoptIPMreq(fd, opt.name, ifi, grp)
 }

+ 24 - 24
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
 
-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
+	sysSizeofInetPktinfo = 0xc
 )
 
-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},
+	}
+)

+ 25 - 1
ipv4/sys_darwin.go

@@ -6,6 +6,24 @@ package ipv4
 
 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 @@ func init() {
 	// 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)
+}

+ 33 - 0
ipv4/sys_freebsd.go

@@ -6,6 +6,39 @@ package ipv4
 
 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
+	}
 }

+ 17 - 38
ipv4/sys_linux.go

@@ -4,44 +4,23 @@
 
 package ipv4
 
-import "syscall"
-
-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
-)
-
-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 sysSockoptLen int32
+
+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},
+	}
 )
 
-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)
 }

+ 0 - 37
ipv4/sys_mreq.go

@@ -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
-}

+ 7 - 3
ipv4/sys.go → 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{}
+)

+ 46 - 18
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
 
-const (
-	// See ws2tcpip.h.
-	sysSockoptPacketInfo = 0x13
+	sysSizeofInetPktinfo  = 0x8
+	sysSizeofIPMreq       = 0x8
+	sysSizeofIPMreqSource = 0xc
 )
 
-const sysSizeofPacketInfo = 0x8
+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},
+	}
+)
 
-type sysPacketInfo struct {
-	IP      [4]byte
-	IfIndex int32
+func (pi *sysInetPktinfo) setIfindex(i int) {
+	pi.Ifindex = int32(i)
 }

+ 31 - 0
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
+}

+ 26 - 0
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
+}

+ 8 - 0
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)