From 38dcae4017ada1ea5d38e24cb88303b58682ab1a Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Mon, 19 May 2014 12:20:11 +0900 Subject: [PATCH] go.net/ipv4: add support for dragonfly, enable IP_PKTINFO on darwin This CL adds support for dragonfly and IP_PKTINFO support for darwin with less dependency on syscall package. Update golang/go#7175 Fixes golang/go#7172. LGTM=iant R=golang-codereviews, gobot, iant CC=golang-codereviews https://golang.org/cl/97800043 --- ipv4/control_bsd.go | 141 ++++++++++-------- ipv4/control_linux.go | 112 +++++--------- ipv4/control_nonpktinfo.go | 11 ++ ipv4/control_pktinfo.go | 30 ++++ ipv4/control_stub.go | 2 +- ipv4/control_unix.go | 43 ++++++ ipv4/dgramopt_posix.go | 2 +- ipv4/dgramopt_stub.go | 2 +- ipv4/genericopt_posix.go | 2 +- ipv4/genericopt_stub.go | 2 +- ipv4/helper_posix.go | 42 ------ ipv4/helper_stub.go | 2 +- ipv4/helper_unix.go | 2 +- ipv4/packet.go | 4 +- ipv4/payload.go | 68 +-------- ipv4/payload_cmsg.go | 75 ++++++++++ ipv4/payload_noncmsg.go | 42 ++++++ ipv4/sockopt_bsd.go | 38 ++--- ipv4/sockopt_linux.go | 48 ++---- ipv4/sockopt_mreq.go | 29 ++++ ipv4/sockopt_mreqn.go | 29 ++++ ipv4/sockopt_nonpktinfo.go | 15 ++ ...{sockopt_freebsd.go => sockopt_pktinfo.go} | 12 +- ipv4/sockopt_stub.go | 2 +- ipv4/sockopt_unix.go | 18 +-- ipv4/sockopt_windows.go | 28 ++-- ipv4/sys.go | 9 ++ ipv4/sys_bsd.go | 37 +++++ ipv4/sys_darwin.go | 27 ++++ ipv4/sys_linux.go | 47 ++++++ ipv4/sys_mreq.go | 37 +++++ ipv4/sys_windows.go | 31 ++++ 32 files changed, 640 insertions(+), 349 deletions(-) create mode 100644 ipv4/control_nonpktinfo.go create mode 100644 ipv4/control_pktinfo.go create mode 100644 ipv4/control_unix.go delete mode 100644 ipv4/helper_posix.go create mode 100644 ipv4/payload_cmsg.go create mode 100644 ipv4/payload_noncmsg.go create mode 100644 ipv4/sockopt_mreq.go create mode 100644 ipv4/sockopt_mreqn.go create mode 100644 ipv4/sockopt_nonpktinfo.go rename ipv4/{sockopt_freebsd.go => sockopt_pktinfo.go} (50%) create mode 100644 ipv4/sys.go create mode 100644 ipv4/sys_bsd.go create mode 100644 ipv4/sys_darwin.go create mode 100644 ipv4/sys_linux.go create mode 100644 ipv4/sys_mreq.go create mode 100644 ipv4/sys_windows.go diff --git a/ipv4/control_bsd.go b/ipv4/control_bsd.go index 6b1b5577..38ef01f9 100644 --- a/ipv4/control_bsd.go +++ b/ipv4/control_bsd.go @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package ipv4 import ( "net" - "os" "syscall" "unsafe" ) @@ -26,62 +25,92 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { opt.clear(FlagTTL) } } - if cf&FlagDst != 0 { - if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil { - return err + if supportsPacketInfo { + if cf&(FlagSrc|FlagDst|FlagInterface) != 0 { + if err := setIPv4PacketInfo(fd, on); err != nil { + return err + } + if on { + opt.set(cf & (FlagSrc | FlagDst | FlagInterface)) + } else { + opt.clear(cf & (FlagSrc | FlagDst | FlagInterface)) + + } } - if on { - opt.set(FlagDst) - } else { - opt.clear(FlagDst) + } else { + if cf&FlagDst != 0 { + if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil { + return err + } + if on { + opt.set(FlagDst) + } else { + opt.clear(FlagDst) + } } - } - if cf&FlagInterface != 0 { - if err := setIPv4ReceiveInterface(fd, on); err != nil { - return err - } - if on { - opt.set(FlagInterface) - } else { - opt.clear(FlagInterface) + if cf&FlagInterface != 0 { + if err := setIPv4ReceiveInterface(fd, on); err != nil { + return err + } + if on { + opt.set(FlagInterface) + } else { + opt.clear(FlagInterface) + } } } return nil } -func newControlMessage(opt *rawOpt) (oob []byte) { - opt.Lock() - defer opt.Unlock() - l, off := 0, 0 +func (opt *rawOpt) oobLen() (l int) { if opt.isset(FlagTTL) { l += syscall.CmsgSpace(1) } - if opt.isset(FlagDst) { - l += syscall.CmsgSpace(net.IPv4len) + if supportsPacketInfo { + if opt.isset(FlagSrc | FlagDst | FlagInterface) { + l += syscall.CmsgSpace(sysSizeofPacketInfo) + } + } else { + if opt.isset(FlagDst) { + l += syscall.CmsgSpace(net.IPv4len) + } + if opt.isset(FlagInterface) { + l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink) + } } - if opt.isset(FlagInterface) { - l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink) + return +} + +func (opt *rawOpt) marshalControlMessage() (oob []byte) { + var off int + oob = make([]byte, opt.oobLen()) + if opt.isset(FlagTTL) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIP + m.Type = sysSockoptReceiveTTL + m.SetLen(syscall.CmsgLen(1)) + off += syscall.CmsgSpace(1) } - if l > 0 { - oob = make([]byte, l) - if opt.isset(FlagTTL) { + if supportsPacketInfo { + if opt.isset(FlagSrc | FlagDst | FlagInterface) { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP - m.Type = syscall.IP_RECVTTL - m.SetLen(syscall.CmsgLen(1)) - off += syscall.CmsgSpace(1) + m.Type = sysSockoptPacketInfo + m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo)) + off += syscall.CmsgSpace(sysSizeofPacketInfo) } + } else { if opt.isset(FlagDst) { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP - m.Type = syscall.IP_RECVDSTADDR + m.Type = sysSockoptReceiveDst 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 = syscall.IP_RECVIF + m.Type = sysSockoptReceiveInterface m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink)) off += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink) } @@ -89,33 +118,25 @@ func newControlMessage(opt *rawOpt) (oob []byte) { return } -func parseControlMessage(b []byte) (*ControlMessage, error) { - if len(b) == 0 { - return nil, nil +func (cm *ControlMessage) oobLen() (l int) { + if supportsPacketInfo && (cm.Src.To4() != nil || cm.IfIndex != 0) { + l += syscall.CmsgSpace(sysSizeofPacketInfo) } - cmsgs, err := syscall.ParseSocketControlMessage(b) - if err != nil { - return nil, os.NewSyscallError("parse socket control message", err) - } - cm := &ControlMessage{} - for _, m := range cmsgs { - if m.Header.Level != ianaProtocolIP { - continue - } - switch m.Header.Type { - case syscall.IP_RECVTTL: - cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) - case syscall.IP_RECVDSTADDR: - cm.Dst = m.Data[:net.IPv4len] - case syscall.IP_RECVIF: - sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0])) - cm.IfIndex = int(sadl.Index) - } - } - return cm, nil + return } -func marshalControlMessage(cm *ControlMessage) []byte { - // TODO(mikio): Implement IP_PKTINFO stuff when OS X 10.8 comes - return nil +func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) { + switch m.Header.Type { + case sysSockoptReceiveTTL: + cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) + case sysSockoptReceiveDst: + cm.Dst = m.Data[:net.IPv4len] + case sysSockoptReceiveInterface: + 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[:] + } } diff --git a/ipv4/control_linux.go b/ipv4/control_linux.go index fa7630fd..56853675 100644 --- a/ipv4/control_linux.go +++ b/ipv4/control_linux.go @@ -5,15 +5,10 @@ package ipv4 import ( - "os" "syscall" "unsafe" ) -// Linux provides a convenient path control option IP_PKTINFO that -// contains IP_SENDSRCADDR, IP_RECVDSTADDR, IP_RECVIF and IP_SENDIF. -const pktinfo = FlagSrc | FlagDst | FlagInterface - func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { opt.Lock() defer opt.Unlock() @@ -27,100 +22,63 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { opt.clear(FlagTTL) } } - if cf&pktinfo != 0 { + if cf&(FlagSrc|FlagDst|FlagInterface) != 0 { if err := setIPv4PacketInfo(fd, on); err != nil { return err } if on { - opt.set(cf & pktinfo) + opt.set(cf & (FlagSrc | FlagDst | FlagInterface)) } else { - opt.clear(cf & pktinfo) + opt.clear(cf & (FlagSrc | FlagDst | FlagInterface)) } } return nil } -func newControlMessage(opt *rawOpt) (oob []byte) { - opt.Lock() - defer opt.Unlock() - l, off := 0, 0 +func (opt *rawOpt) oobLen() (l int) { if opt.isset(FlagTTL) { l += syscall.CmsgSpace(1) } - if opt.isset(pktinfo) { - l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) - } - if l > 0 { - oob = make([]byte, l) - if opt.isset(FlagTTL) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIP - m.Type = syscall.IP_RECVTTL - m.SetLen(syscall.CmsgLen(1)) - off += syscall.CmsgSpace(1) - } - if opt.isset(pktinfo) { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIP - m.Type = syscall.IP_PKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) - off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) - } + if opt.isset(FlagSrc | FlagDst | FlagInterface) { + l += syscall.CmsgSpace(sysSizeofPacketInfo) } return } -func parseControlMessage(b []byte) (*ControlMessage, error) { - if len(b) == 0 { - return nil, nil +func (opt *rawOpt) marshalControlMessage() (oob []byte) { + var off int + oob = make([]byte, opt.oobLen()) + if opt.isset(FlagTTL) { + m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) + m.Level = ianaProtocolIP + m.Type = sysSockoptReceiveTTL + m.SetLen(syscall.CmsgLen(1)) + off += syscall.CmsgSpace(1) } - cmsgs, err := syscall.ParseSocketControlMessage(b) - if err != nil { - return nil, os.NewSyscallError("parse socket control message", err) + 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) } - cm := &ControlMessage{} - for _, m := range cmsgs { - if m.Header.Level != ianaProtocolIP { - continue - } - switch m.Header.Type { - case syscall.IP_TTL: - cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) - case syscall.IP_PKTINFO: - pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&m.Data[0])) - cm.IfIndex = int(pi.Ifindex) - cm.Dst = pi.Addr[:] - } - } - return cm, nil + return } -func marshalControlMessage(cm *ControlMessage) (oob []byte) { - if cm == nil { - return - } - l, off := 0, 0 - pion := false +func (cm *ControlMessage) oobLen() (l int) { if cm.Src.To4() != nil || cm.IfIndex != 0 { - pion = true - l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) - } - if l > 0 { - oob = make([]byte, l) - if pion { - m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) - m.Level = ianaProtocolIP - m.Type = syscall.IP_PKTINFO - m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) - pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) - if ip := cm.Src.To4(); ip != nil { - copy(pi.Addr[:], ip) - } - if cm.IfIndex != 0 { - pi.Ifindex = int32(cm.IfIndex) - } - off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) - } + l += syscall.CmsgSpace(sysSizeofPacketInfo) } return } + +func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) { + switch m.Header.Type { + case sysSockoptTTL: + 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[:] + } +} diff --git a/ipv4/control_nonpktinfo.go b/ipv4/control_nonpktinfo.go new file mode 100644 index 00000000..c7da635b --- /dev/null +++ b/ipv4/control_nonpktinfo.go @@ -0,0 +1,11 @@ +// 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 (cm *ControlMessage) marshalPacketInfo() (oob []byte) { + return nil +} diff --git a/ipv4/control_pktinfo.go b/ipv4/control_pktinfo.go new file mode 100644 index 00000000..4778ef17 --- /dev/null +++ b/ipv4/control_pktinfo.go @@ -0,0 +1,30 @@ +// 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 ( + "syscall" + "unsafe" +) + +func (cm *ControlMessage) marshalPacketInfo() (oob []byte) { + if l := cm.oobLen(); l > 0 { + 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)])) + if ip := cm.Src.To4(); ip != nil { + copy(pi.IP[:], ip) + } + if cm.IfIndex != 0 { + pi.IfIndex = int32(cm.IfIndex) + } + } + return +} diff --git a/ipv4/control_stub.go b/ipv4/control_stub.go index 2b043a57..49b7f5c3 100644 --- a/ipv4/control_stub.go +++ b/ipv4/control_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly plan9 solaris +// +build plan9 solaris package ipv4 diff --git a/ipv4/control_unix.go b/ipv4/control_unix.go new file mode 100644 index 00000000..f3dd2f59 --- /dev/null +++ b/ipv4/control_unix.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. + +// +build darwin dragonfly freebsd linux netbsd openbsd + +package ipv4 + +import ( + "os" + "syscall" +) + +func newControlMessage(opt *rawOpt) (oob []byte) { + opt.Lock() + defer opt.Unlock() + return opt.marshalControlMessage() +} + +func parseControlMessage(b []byte) (*ControlMessage, error) { + if len(b) == 0 { + return nil, nil + } + cmsgs, err := syscall.ParseSocketControlMessage(b) + if err != nil { + return nil, os.NewSyscallError("parse socket control message", err) + } + cm := &ControlMessage{} + for _, m := range cmsgs { + if m.Header.Level != ianaProtocolIP { + continue + } + cm.parseControlMessage(&m) + } + return cm, nil +} + +func marshalControlMessage(cm *ControlMessage) (oob []byte) { + if cm == nil { + return nil + } + return cm.marshalPacketInfo() +} diff --git a/ipv4/dgramopt_posix.go b/ipv4/dgramopt_posix.go index 0ba81c20..874afdec 100644 --- a/ipv4/dgramopt_posix.go +++ b/ipv4/dgramopt_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package ipv4 diff --git a/ipv4/dgramopt_stub.go b/ipv4/dgramopt_stub.go index b961a9a0..794048fe 100644 --- a/ipv4/dgramopt_stub.go +++ b/ipv4/dgramopt_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly plan9 solaris +// +build plan9 solaris package ipv4 diff --git a/ipv4/genericopt_posix.go b/ipv4/genericopt_posix.go index f1eab2dc..483c53a1 100644 --- a/ipv4/genericopt_posix.go +++ b/ipv4/genericopt_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package ipv4 diff --git a/ipv4/genericopt_stub.go b/ipv4/genericopt_stub.go index b7bf3733..342dfcf3 100644 --- a/ipv4/genericopt_stub.go +++ b/ipv4/genericopt_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly plan9 solaris +// +build plan9 solaris package ipv4 diff --git a/ipv4/helper_posix.go b/ipv4/helper_posix.go deleted file mode 100644 index 0adb3e1a..00000000 --- a/ipv4/helper_posix.go +++ /dev/null @@ -1,42 +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 freebsd linux netbsd openbsd windows - -package ipv4 - -import ( - "bytes" - "net" - "syscall" -) - -func setSyscallIPMreq(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 a := v.IP.To4(); a != nil { - copy(mreq.Interface[:], a) - goto done - } - case *net.IPNet: - if a := v.IP.To4(); a != nil { - copy(mreq.Interface[:], a) - goto done - } - } - } -done: - if bytes.Equal(mreq.Multiaddr[:], net.IPv4zero.To4()) { - return errNoSuchMulticastInterface - } - return nil -} diff --git a/ipv4/helper_stub.go b/ipv4/helper_stub.go index 30248606..03432d38 100644 --- a/ipv4/helper_stub.go +++ b/ipv4/helper_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly plan9 solaris +// +build plan9 solaris package ipv4 diff --git a/ipv4/helper_unix.go b/ipv4/helper_unix.go index 3c125930..345ca7dc 100644 --- a/ipv4/helper_unix.go +++ b/ipv4/helper_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package ipv4 diff --git a/ipv4/packet.go b/ipv4/packet.go index faeeec00..09864314 100644 --- a/ipv4/packet.go +++ b/ipv4/packet.go @@ -56,8 +56,8 @@ func slicePacket(b []byte) (h, p []byte, err error) { // WriteTo writes an IPv4 datagram through the endpoint c, copying the // datagram from the IPv4 header h and the payload p. The control // message cm allows the datagram path and the outgoing interface to be -// specified. Currently only Linux supports this. The cm may be nil -// if control of the outgoing datagram is not required. +// specified. Currently only Darwin and Linux support this. The cm +// may be nil if control of the outgoing datagram is not required. // // The IPv4 header h must contain appropriate fields that include: // diff --git a/ipv4/payload.go b/ipv4/payload.go index 39efb4c4..d7698cbd 100644 --- a/ipv4/payload.go +++ b/ipv4/payload.go @@ -4,10 +4,7 @@ package ipv4 -import ( - "net" - "syscall" -) +import "net" // A payloadHandler represents the IPv4 datagram payload handler. type payloadHandler struct { @@ -16,66 +13,3 @@ type payloadHandler struct { } func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil } - -// ReadFrom reads a payload of the received IPv4 datagram, from the -// endpoint c, copying the payload into b. It returns the number of -// bytes copied into b, the control message cm and the source address -// src of the received datagram. -func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { - if !c.ok() { - return 0, nil, nil, syscall.EINVAL - } - oob := newControlMessage(&c.rawOpt) - var oobn int - switch c := c.PacketConn.(type) { - case *net.UDPConn: - if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil { - return 0, nil, nil, err - } - case *net.IPConn: - nb := make([]byte, maxHeaderLen+len(b)) - if n, oobn, _, src, err = c.ReadMsgIP(nb, oob); err != nil { - return 0, nil, nil, err - } - hdrlen := int(nb[0]&0x0f) << 2 - copy(b, nb[hdrlen:]) - n -= hdrlen - default: - return 0, nil, nil, errInvalidConnType - } - if cm, err = parseControlMessage(oob[:oobn]); err != nil { - return 0, nil, nil, err - } - if cm != nil { - cm.Src = netAddrToIP4(src) - } - return -} - -// WriteTo writes a payload of the IPv4 datagram, to the destination -// address dst through the endpoint c, copying the payload from b. It -// returns the number of bytes written. The control message cm allows -// the datagram path and the outgoing interface to be specified. -// Currently only Linux supports this. The cm may be nil if control -// of the outgoing datagram is not required. -func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - oob := marshalControlMessage(cm) - if dst == nil { - return 0, errMissingAddress - } - switch c := c.PacketConn.(type) { - case *net.UDPConn: - n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr)) - case *net.IPConn: - n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr)) - default: - return 0, errInvalidConnType - } - if err != nil { - return 0, err - } - return -} diff --git a/ipv4/payload_cmsg.go b/ipv4/payload_cmsg.go new file mode 100644 index 00000000..2cda8b99 --- /dev/null +++ b/ipv4/payload_cmsg.go @@ -0,0 +1,75 @@ +// 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 !plan9,!solaris,!windows + +package ipv4 + +import ( + "net" + "syscall" +) + +// ReadFrom reads a payload of the received IPv4 datagram, from the +// endpoint c, copying the payload into b. It returns the number of +// bytes copied into b, the control message cm and the source address +// src of the received datagram. +func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + if !c.ok() { + return 0, nil, nil, syscall.EINVAL + } + oob := newControlMessage(&c.rawOpt) + var oobn int + switch c := c.PacketConn.(type) { + case *net.UDPConn: + if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil { + return 0, nil, nil, err + } + case *net.IPConn: + nb := make([]byte, maxHeaderLen+len(b)) + if n, oobn, _, src, err = c.ReadMsgIP(nb, oob); err != nil { + return 0, nil, nil, err + } + hdrlen := int(nb[0]&0x0f) << 2 + copy(b, nb[hdrlen:]) + n -= hdrlen + default: + return 0, nil, nil, errInvalidConnType + } + if cm, err = parseControlMessage(oob[:oobn]); err != nil { + return 0, nil, nil, err + } + if cm != nil { + cm.Src = netAddrToIP4(src) + } + return +} + +// WriteTo writes a payload of the IPv4 datagram, to the destination +// address dst through the endpoint c, copying the payload from b. It +// returns the number of bytes written. The control message cm allows +// the datagram path and the outgoing interface to be specified. +// Currently only Darwin and Darwin support this. The cm may be nil if +// control of the outgoing datagram is not required. +func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + if !c.ok() { + return 0, syscall.EINVAL + } + oob := marshalControlMessage(cm) + if dst == nil { + return 0, errMissingAddress + } + switch c := c.PacketConn.(type) { + case *net.UDPConn: + n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr)) + case *net.IPConn: + n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr)) + default: + return 0, errInvalidConnType + } + if err != nil { + return 0, err + } + return +} diff --git a/ipv4/payload_noncmsg.go b/ipv4/payload_noncmsg.go new file mode 100644 index 00000000..d128c9c2 --- /dev/null +++ b/ipv4/payload_noncmsg.go @@ -0,0 +1,42 @@ +// 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 plan9 solaris windows + +package ipv4 + +import ( + "net" + "syscall" +) + +// ReadFrom reads a payload of the received IPv4 datagram, from the +// endpoint c, copying the payload into b. It returns the number of +// bytes copied into b, the control message cm and the source address +// src of the received datagram. +func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + if !c.ok() { + return 0, nil, nil, syscall.EINVAL + } + if n, src, err = c.PacketConn.ReadFrom(b); err != nil { + return 0, nil, nil, err + } + return +} + +// WriteTo writes a payload of the IPv4 datagram, to the destination +// address dst through the endpoint c, copying the payload from b. It +// returns the number of bytes written. The control message cm allows +// the datagram path and the outgoing interface to be specified. +// Currently only Darwin and Linux support this. The cm may be nil if +// control of the outgoing datagram is not required. +func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + if !c.ok() { + return 0, syscall.EINVAL + } + if dst == nil { + return 0, errMissingAddress + } + return c.PacketConn.WriteTo(b, dst) +} diff --git a/ipv4/sockopt_bsd.go b/ipv4/sockopt_bsd.go index 71e0bf0a..b793f5c5 100644 --- a/ipv4/sockopt_bsd.go +++ b/ipv4/sockopt_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package ipv4 @@ -13,7 +13,7 @@ import ( ) func ipv4MulticastTTL(fd int) (int, error) { - v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL) + v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastTTL) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } @@ -21,11 +21,11 @@ func ipv4MulticastTTL(fd int) (int, error) { } func setIPv4MulticastTTL(fd int, v int) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, byte(v))) + return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastTTL, byte(v))) } func ipv4ReceiveDestinationAddress(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVDSTADDR) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveDst) if err != nil { return false, os.NewSyscallError("getsockopt", err) } @@ -33,11 +33,11 @@ func ipv4ReceiveDestinationAddress(fd int) (bool, error) { } func setIPv4ReceiveDestinationAddress(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVDSTADDR, boolint(v))) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveDst, boolint(v))) } func ipv4ReceiveInterface(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVIF) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveInterface) if err != nil { return false, os.NewSyscallError("getsockopt", err) } @@ -45,11 +45,11 @@ func ipv4ReceiveInterface(fd int) (bool, error) { } func setIPv4ReceiveInterface(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVIF, boolint(v))) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveInterface, boolint(v))) } func ipv4MulticastInterface(fd int) (*net.Interface, error) { - v, err := syscall.GetsockoptInet4Addr(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF) + v, err := syscall.GetsockoptInet4Addr(fd, ianaProtocolIP, sysSockoptMulticastInterface) if err != nil { return nil, os.NewSyscallError("getsockopt", err) } @@ -63,11 +63,11 @@ func setIPv4MulticastInterface(fd int, ifi *net.Interface) error { } var v [4]byte copy(v[:], ip.To4()) - return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, v)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd, ianaProtocolIP, sysSockoptMulticastInterface, v)) } func ipv4MulticastLoopback(fd int) (bool, error) { - v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP) + v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastLoopback) if err != nil { return false, os.NewSyscallError("getsockopt", err) } @@ -75,21 +75,5 @@ func ipv4MulticastLoopback(fd int) (bool, error) { } func setIPv4MulticastLoopback(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))) -} - -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 := setSyscallIPMreq(&mreq, ifi); err != nil { - return err - } - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, &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 := setSyscallIPMreq(&mreq, ifi); err != nil { - return err - } - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, &mreq)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastLoopback, byte(boolint(v)))) } diff --git a/ipv4/sockopt_linux.go b/ipv4/sockopt_linux.go index cb5f6db5..6ec58dee 100644 --- a/ipv4/sockopt_linux.go +++ b/ipv4/sockopt_linux.go @@ -11,7 +11,7 @@ import ( ) func ipv4ReceiveTOS(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTOS) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTOS) if err != nil { return false, os.NewSyscallError("getsockopt", err) } @@ -19,11 +19,11 @@ func ipv4ReceiveTOS(fd int) (bool, error) { } func setIPv4ReceiveTOS(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTOS, boolint(v))) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTOS, boolint(v))) } func ipv4MulticastTTL(fd int) (int, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastTTL) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } @@ -31,42 +31,30 @@ func ipv4MulticastTTL(fd int) (int, error) { } func setIPv4MulticastTTL(fd int, v int) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, v)) -} - -func ipv4PacketInfo(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_PKTINFO) - 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, syscall.IP_PKTINFO, boolint(v))) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastTTL, v)) } func ipv4MulticastInterface(fd int) (*net.Interface, error) { - mreqn, err := syscall.GetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF) + mreqn, err := syscall.GetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptMulticastInterface) if err != nil { return nil, os.NewSyscallError("getsockopt", err) } - if int(mreqn.Ifindex) == 0 { + if mreqn.Ifindex == 0 { return nil, nil } return net.InterfaceByIndex(int(mreqn.Ifindex)) } func setIPv4MulticastInterface(fd int, ifi *net.Interface) error { - mreqn := syscall.IPMreqn{} + var mreqn syscall.IPMreqn if ifi != nil { mreqn.Ifindex = int32(ifi.Index) } - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, &mreqn)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptMulticastInterface, &mreqn)) } func ipv4MulticastLoopback(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastLoopback) if err != nil { return false, os.NewSyscallError("getsockopt", err) } @@ -74,21 +62,5 @@ func ipv4MulticastLoopback(fd int) (bool, error) { } func setIPv4MulticastLoopback(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, boolint(v))) -} - -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, syscall.IP_ADD_MEMBERSHIP, &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, syscall.IP_DROP_MEMBERSHIP, &mreqn)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, boolint(v))) } diff --git a/ipv4/sockopt_mreq.go b/ipv4/sockopt_mreq.go new file mode 100644 index 00000000..6e591ca1 --- /dev/null +++ b/ipv4/sockopt_mreq.go @@ -0,0 +1,29 @@ +// 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 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 new file mode 100644 index 00000000..a3a87d7a --- /dev/null +++ b/ipv4/sockopt_mreqn.go @@ -0,0 +1,29 @@ +// 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 freebsd 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 new file mode 100644 index 00000000..5592b461 --- /dev/null +++ b/ipv4/sockopt_nonpktinfo.go @@ -0,0 +1,15 @@ +// 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_freebsd.go b/ipv4/sockopt_pktinfo.go similarity index 50% rename from ipv4/sockopt_freebsd.go rename to ipv4/sockopt_pktinfo.go index 55c6de99..5506b88e 100644 --- a/ipv4/sockopt_freebsd.go +++ b/ipv4/sockopt_pktinfo.go @@ -1,7 +1,9 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// 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 ( @@ -9,14 +11,14 @@ import ( "syscall" ) -func ipv4SendSourceAddress(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_SENDSRCADDR) +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 setIPv4SendSourceAddress(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_SENDSRCADDR, boolint(v))) +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 fd9b9fd5..53ae4022 100644 --- a/ipv4/sockopt_stub.go +++ b/ipv4/sockopt_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly plan9 solaris +// +build plan9 solaris package ipv4 diff --git a/ipv4/sockopt_unix.go b/ipv4/sockopt_unix.go index 33016563..a96fe256 100644 --- a/ipv4/sockopt_unix.go +++ b/ipv4/sockopt_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package ipv4 @@ -12,7 +12,7 @@ import ( ) func ipv4TOS(fd int) (int, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_TOS) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptTOS) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } @@ -20,11 +20,11 @@ func ipv4TOS(fd int) (int, error) { } func setIPv4TOS(fd int, v int) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_TOS, v)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptTOS, v)) } func ipv4TTL(fd int) (int, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_TTL) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptTTL) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } @@ -32,11 +32,11 @@ func ipv4TTL(fd int) (int, error) { } func setIPv4TTL(fd int, v int) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_TTL, v)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptTTL, v)) } func ipv4ReceiveTTL(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTTL) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTTL) if err != nil { return false, os.NewSyscallError("getsockopt", err) } @@ -44,11 +44,11 @@ func ipv4ReceiveTTL(fd int) (bool, error) { } func setIPv4ReceiveTTL(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTTL, boolint(v))) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTTL, boolint(v))) } func ipv4HeaderPrepend(fd int) (bool, error) { - v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_HDRINCL) + v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend) if err != nil { return false, os.NewSyscallError("getsockopt", err) } @@ -56,5 +56,5 @@ func ipv4HeaderPrepend(fd int) (bool, error) { } func setIPv4HeaderPrepend(fd int, v bool) error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_HDRINCL, boolint(v))) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend, boolint(v))) } diff --git a/ipv4/sockopt_windows.go b/ipv4/sockopt_windows.go index f7ee0733..e6bf5932 100644 --- a/ipv4/sockopt_windows.go +++ b/ipv4/sockopt_windows.go @@ -17,7 +17,7 @@ import ( func ipv4TOS(fd syscall.Handle) (int, error) { var v int32 l := int32(4) - if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_TOS, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTOS, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil @@ -25,13 +25,13 @@ func ipv4TOS(fd syscall.Handle) (int, error) { func setIPv4TOS(fd syscall.Handle, v int) error { vv := int32(v) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_TOS, (*byte)(unsafe.Pointer(&vv)), 4)) + 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, syscall.IP_TTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil @@ -39,13 +39,13 @@ func ipv4TTL(fd syscall.Handle) (int, error) { func setIPv4TTL(fd syscall.Handle, v int) error { vv := int32(v) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_TTL, (*byte)(unsafe.Pointer(&vv)), 4)) + 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, syscall.IP_MULTICAST_TTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastTTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil @@ -53,7 +53,7 @@ func ipv4MulticastTTL(fd syscall.Handle) (int, error) { func setIPv4MulticastTTL(fd syscall.Handle, v int) error { vv := int32(v) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, (*byte)(unsafe.Pointer(&vv)), 4)) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptMulticastTTL, (*byte)(unsafe.Pointer(&vv)), 4)) } func ipv4ReceiveTTL(fd syscall.Handle) (bool, error) { @@ -99,7 +99,7 @@ func setIPv4ReceiveInterface(fd syscall.Handle, v bool) error { func ipv4MulticastInterface(fd syscall.Handle) (*net.Interface, error) { var v [4]byte l := int32(4) - if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&v[0])), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v[0])), &l); err != nil { return nil, os.NewSyscallError("getsockopt", err) } return netIP4ToInterface(net.IPv4(v[0], v[1], v[2], v[3])) @@ -112,13 +112,13 @@ func setIPv4MulticastInterface(fd syscall.Handle, ifi *net.Interface) error { } var v [4]byte copy(v[:], ip.To4()) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&v[0])), 4)) + 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, syscall.IP_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&v)), &l); err != nil { + if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil @@ -126,21 +126,21 @@ func ipv4MulticastLoopback(fd syscall.Handle) (bool, error) { func setIPv4MulticastLoopback(fd syscall.Handle, v bool) error { vv := int32(boolint(v)) - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&vv)), 4)) + 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 := setSyscallIPMreq(&mreq, ifi); err != nil { + if err := setSysIPMreqInterface(&mreq, ifi); err != nil { return err } - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq)))) + 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 := setSyscallIPMreq(&mreq, ifi); err != nil { + if err := setSysIPMreqInterface(&mreq, ifi); err != nil { return err } - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq)))) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq)))) } diff --git a/ipv4/sys.go b/ipv4/sys.go new file mode 100644 index 00000000..31cfbe29 --- /dev/null +++ b/ipv4/sys.go @@ -0,0 +1,9 @@ +// 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 + +// supportsPacketInfo reports whether the platform supports +// IP_PKTINFO. +var supportsPacketInfo bool diff --git a/ipv4/sys_bsd.go b/ipv4/sys_bsd.go new file mode 100644 index 00000000..866dadc7 --- /dev/null +++ b/ipv4/sys_bsd.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. + +// +build darwin dragonfly freebsd netbsd openbsd + +package ipv4 + +import "syscall" + +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 +) + +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 +} diff --git a/ipv4/sys_darwin.go b/ipv4/sys_darwin.go new file mode 100644 index 00000000..3dee19db --- /dev/null +++ b/ipv4/sys_darwin.go @@ -0,0 +1,27 @@ +// 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" + +func init() { + // Seems like kern.osreldate is veiled on latest OS X. We use + // kern.osrelease instead. + osver, err := syscall.Sysctl("kern.osrelease") + if err != nil { + return + } + var i int + for i = range osver { + if osver[i] != '.' { + continue + } + } + // 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 + } +} diff --git a/ipv4/sys_linux.go b/ipv4/sys_linux.go new file mode 100644 index 00000000..a81b592a --- /dev/null +++ b/ipv4/sys_linux.go @@ -0,0 +1,47 @@ +// 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" + +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 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 +} diff --git a/ipv4/sys_mreq.go b/ipv4/sys_mreq.go new file mode 100644 index 00000000..6d5937c9 --- /dev/null +++ b/ipv4/sys_mreq.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. + +// +build darwin dragonfly 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_windows.go b/ipv4/sys_windows.go new file mode 100644 index 00000000..1ef7f2a6 --- /dev/null +++ b/ipv4/sys_windows.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" + +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 +) + +const ( + // See ws2tcpip.h. + sysSockoptPacketInfo = 0x13 +) + +const sysSizeofPacketInfo = 0x8 + +type sysPacketInfo struct { + IP [4]byte + IfIndex int32 +}