go.net/ipv4: new package

Package ipv4 implements IP-level socket options for the Internet
Protocol version 4. It also provides raw IP socket access methods
including IPv4 header manipulation.

Fixes golang/go#3684.
Fixes golang/go#3820.

This CL requires CL 6426047;
net: add read, write message methods to IPConn, UDPConn

R=rsc, dave, alex.brainman
CC=gobot, golang-dev
https://golang.org/cl/6482044
This commit is contained in:
Mikio Hara
2012-09-26 21:03:09 +09:00
parent 3b94eae23e
commit d2e5a1215d
34 changed files with 3421 additions and 0 deletions

47
ipv4/control.go Normal file
View File

@@ -0,0 +1,47 @@
// 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 (
"fmt"
"net"
"sync"
)
type rawOpt struct {
mu sync.Mutex
cflags ControlFlags
}
func (o *rawOpt) lock() { o.mu.Lock() }
func (o *rawOpt) unlock() { o.mu.Unlock() }
func (o *rawOpt) set(f ControlFlags) { o.cflags |= f }
func (o *rawOpt) clear(f ControlFlags) { o.cflags ^= f }
func (o *rawOpt) isset(f ControlFlags) bool { return o.cflags&f != 0 }
type ControlFlags uint
const (
FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet
FlagSrc // pass the source address on the received packet
FlagDst // pass the destination address on the received packet
FlagInterface // pass the interface index on the received packet or outgoing packet
)
// A ControlMessage represents control information that contains per
// packet IP-level option data.
type ControlMessage struct {
TTL int // time-to-live
Src net.IP // source address
Dst net.IP // destination address
IfIndex int // interface index
}
func (cm *ControlMessage) String() string {
if cm == nil {
return "<nil>"
}
return fmt.Sprintf("ttl: %v, src: %v, dst: %v, ifindex: %v", cm.TTL, cm.Src, cm.Dst, cm.IfIndex)
}

112
ipv4/control_bsd.go Normal file
View File

@@ -0,0 +1,112 @@
// 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 netbsd openbsd
package ipv4
import (
"net"
"os"
"syscall"
"unsafe"
)
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 {
return err
}
if on {
opt.set(FlagTTL)
} else {
opt.clear(FlagTTL)
}
}
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)
}
}
return nil
}
func newControlMessage(opt *rawOpt) (oob []byte) {
opt.lock()
defer opt.unlock()
if opt.isset(FlagTTL) {
b := make([]byte, syscall.CmsgSpace(1))
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = syscall.IPPROTO_IP
cmsg.Type = syscall.IP_RECVTTL
cmsg.SetLen(syscall.CmsgLen(1))
oob = append(oob, b...)
}
if opt.isset(FlagDst) {
b := make([]byte, syscall.CmsgSpace(net.IPv4len))
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = syscall.IPPROTO_IP
cmsg.Type = syscall.IP_RECVDSTADDR
cmsg.SetLen(syscall.CmsgLen(net.IPv4len))
oob = append(oob, b...)
}
if opt.isset(FlagInterface) {
b := make([]byte, syscall.CmsgSpace(syscall.SizeofSockaddrDatalink))
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = syscall.IPPROTO_IP
cmsg.Type = syscall.IP_RECVIF
cmsg.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
oob = append(oob, b...)
}
return
}
func parseControlMessage(b []byte) (*ControlMessage, error) {
cmsgs, err := syscall.ParseSocketControlMessage(b)
if err != nil {
return nil, os.NewSyscallError("parse socket control message", err)
}
if len(b) == 0 {
return nil, nil
}
cm := &ControlMessage{}
for _, m := range cmsgs {
if m.Header.Level != syscall.IPPROTO_IP {
continue
}
switch m.Header.Type {
case syscall.IP_RECVTTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IP_RECVDSTADDR:
v := m.Data[:4]
cm.Dst = net.IPv4(v[0], v[1], v[2], v[3])
case syscall.IP_RECVIF:
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(sadl.Index)
}
}
return cm, nil
}
func marshalControlMessage(cm *ControlMessage) []byte {
// TODO(mikio): Implement IP_PKTINFO stuff when OS X 10.8 comes
return nil
}

116
ipv4/control_linux.go Normal file
View File

@@ -0,0 +1,116 @@
// 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"
)
// 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()
if cf&FlagTTL != 0 {
if err := setIPv4ReceiveTTL(fd, on); err != nil {
return err
}
if on {
opt.set(FlagTTL)
} else {
opt.clear(FlagTTL)
}
}
if cf&pktinfo != 0 {
if err := setIPv4PacketInfo(fd, on); err != nil {
return err
}
if on {
opt.set(cf & pktinfo)
} else {
opt.clear(cf & pktinfo)
}
}
return nil
}
func newControlMessage(opt *rawOpt) (oob []byte) {
opt.lock()
defer opt.unlock()
if opt.isset(FlagTTL) {
b := make([]byte, syscall.CmsgSpace(1))
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = syscall.IPPROTO_IP
cmsg.Type = syscall.IP_RECVTTL
cmsg.SetLen(syscall.CmsgLen(1))
oob = append(oob, b...)
}
if opt.isset(pktinfo) {
b := make([]byte, syscall.CmsgSpace(syscall.SizeofInet4Pktinfo))
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = syscall.IPPROTO_IP
cmsg.Type = syscall.IP_PKTINFO
cmsg.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
oob = append(oob, b...)
}
return
}
func parseControlMessage(b []byte) (*ControlMessage, error) {
cmsgs, err := syscall.ParseSocketControlMessage(b)
if err != nil {
return nil, os.NewSyscallError("parse socket control message", err)
}
if len(b) == 0 {
return nil, nil
}
cm := &ControlMessage{}
for _, m := range cmsgs {
if m.Header.Level != syscall.IPPROTO_IP {
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 = net.IPv4(pi.Addr[0], pi.Addr[1], pi.Addr[2], pi.Addr[3])
}
}
return cm, nil
}
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm == nil {
return
}
pi := &syscall.Inet4Pktinfo{}
pion := false
if ip := cm.Src.To4(); ip != nil {
copy(pi.Spec_dst[:], ip[0:net.IPv4len])
pion = true
}
if cm.IfIndex != 0 {
pi.Ifindex = int32(cm.IfIndex)
pion = true
}
if pion {
b := make([]byte, syscall.CmsgSpace(syscall.SizeofInet4Pktinfo))
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = syscall.IPPROTO_IP
cmsg.Type = syscall.IP_PKTINFO
cmsg.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
data := b[syscall.CmsgLen(0):]
copy(data[0:syscall.SizeofInet4Pktinfo], (*[syscall.SizeofInet4Pktinfo]byte)(unsafe.Pointer(pi))[:syscall.SizeofInet4Pktinfo])
oob = append(oob, b...)
}
return
}

29
ipv4/control_plan9.go Normal file
View File

@@ -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.
package ipv4
import (
"syscall"
)
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}
func newControlMessage(opt *rawOpt) []byte {
// TODO(mikio): Implement this
return nil
}
func parseControlMessage(b []byte) (*ControlMessage, error) {
// TODO(mikio): Implement this
return nil, syscall.EPLAN9
}
func marshalControlMessage(cm *ControlMessage) []byte {
// TODO(mikio): Implement this
return nil
}

29
ipv4/control_windows.go Normal file
View File

@@ -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.
package ipv4
import (
"syscall"
)
func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
// TODO(mikio): Implement this
return syscall.EWINDOWS
}
func newControlMessage(opt *rawOpt) []byte {
// TODO(mikio): Implement this
return nil
}
func parseControlMessage(b []byte) (*ControlMessage, error) {
// TODO(mikio): Implement this
return nil, syscall.EWINDOWS
}
func marshalControlMessage(cm *ControlMessage) []byte {
// TODO(mikio): Implement this
return nil
}

50
ipv4/dgramopt_plan9.go Normal file
View File

@@ -0,0 +1,50 @@
// 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"
"syscall"
)
func (c *dgramOpt) MulticastTTL() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
}
func (c *dgramOpt) SetMulticastTTL(ttl int) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
// TODO(mikio): Implement this
return nil, syscall.EPLAN9
}
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}
func (c *dgramOpt) MulticastLoopback() (bool, error) {
// TODO(mikio): Implement this
return false, syscall.EPLAN9
}
func (c *dgramOpt) SetMulticastLoopback(on bool) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}
func (c *dgramOpt) JoinGroup(ifi *net.Interface, grp net.Addr) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, grp net.Addr) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}

125
ipv4/dgramopt_posix.go Normal file
View File

@@ -0,0 +1,125 @@
// 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 (
"net"
"syscall"
)
// MulticastTTL returns the time-to-live field value for outgoing
// multicast packets.
func (c *dgramOpt) MulticastTTL() (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return 0, err
}
return ipv4MulticastTTL(fd)
}
// SetMulticastTTL sets the time-to-live field value for future
// outgoing multicast packets.
func (c *dgramOpt) SetMulticastTTL(ttl int) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
return setIPv4MulticastTTL(fd, ttl)
}
// MulticastInterface returns the default interface for multicast
// packet transmissions.
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
if !c.ok() {
return nil, syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return nil, err
}
return ipv4MulticastInterface(fd)
}
// SetMulticastInterface sets the default interface for future
// multicast packet transmissions.
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
return setIPv4MulticastInterface(fd, ifi)
}
// MulticastLoopback reports whether transmitted multicast packets
// should be copied and send back to the originator.
func (c *dgramOpt) MulticastLoopback() (bool, error) {
if !c.ok() {
return false, syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return false, err
}
return ipv4MulticastLoopback(fd)
}
// SetMulticastLoopback sets whether transmitted multicast packets
// should be copied and send back to the originator.
func (c *dgramOpt) SetMulticastLoopback(on bool) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
return setIPv4MulticastLoopback(fd, on)
}
// JoinGroup joins the group address group on the interface ifi.
// It uses the system assigned multicast interface when ifi is nil,
// although this is not recommended because the assignment depends on
// platforms and sometimes it might require routing configuration.
func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
grp := netAddrToIP4(group)
if grp == nil {
return errMissingAddress
}
return joinIPv4Group(fd, ifi, grp)
}
// LeaveGroup leaves the group address group on the interface ifi.
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
grp := netAddrToIP4(group)
if grp == nil {
return errMissingAddress
}
return leaveIPv4Group(fd, ifi, grp)
}

213
ipv4/doc.go Normal file
View File

@@ -0,0 +1,213 @@
// 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 implements IP-level socket options for the Internet
// Protocol version 4.
//
// The package provides IP-level socket options that allow
// manipulation of IPv4 facilities. The IPv4 and basic host
// requirements for IPv4 are defined in RFC 791, 1112 and 1122. A
// series of RFCs 2474, 2475, 2597, 2598 and 3168 describe how to use
// the type-of-service field in a DiffServ, differentiated services
// environment.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv4 transport. When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, ipv4.Conn is used to set the type-of-service field on
// the IPv4 header for each packet.
//
// ln, err := net.Listen("tcp4", "0.0.0.0:1024")
// if err != nil {
// // error handling
// }
// defer ln.Close()
// for {
// c, err := ln.Accept()
// if err != nil {
// // error handling
// }
// go func(c net.Conn) {
// defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, as known as AF11 packets.
//
// err := ipv4.NewConn(c).SetTOS(ipv4.DSCP_AF11)
// if err != nil {
// // error handling
// }
// _, err = c.Write(data)
// if err != nil {
// // error handling
// }
// }(c)
// }
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv4 transport. A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// group addresses.
//
// en0, err := net.InterfaceByName("en0")
// if err != nil {
// // error handling
// }
// en1, err := net.InterfaceByIndex(911)
// if err != nil {
// // error handling
// }
// group := net.IPv4(224, 0, 0, 250)
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
// c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
// if err != nil {
// // error handling
// }
// defer c.Close()
//
// Second, the application joins groups, starts listening to the
// group addresses on the specified network interfaces. Note that
// the service port for transport layer protocol does not matter with
// this operation as joining groups affects only network and link
// layer protocols, such as IPv4 and Ethernet.
//
// p := ipv4.NewPacketConn(c)
// err = p.JoinGroup(en0, &net.UDPAddr{IP: group})
// if err != nil {
// // error handling
// }
// err = p.JoinGroup(en1, &net.UDPAddr{IP: group})
// if err != nil {
// // error handling
// }
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel. When the application
// needs a destination address on an incoming packet,
// SetControlMessage of ipv4.PacketConn is used to enable control
// message transmissons.
//
// err = p.SetControlMessage(ipv4.FlagDst, true)
// if err != nil {
// // error handling
// }
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
// b := make([]byte, 1500)
// for {
// n, cm, src, err := p.Read(b)
// if err != nil {
// // error handling
// }
// if cm.Dst.IsMulticast() {
// if cm.Dst.Equal(group)
// // joined group, do something
// } else {
// // unknown group, discard
// continue
// }
// }
//
// The application can also send both unicast and multicast packets.
//
// p.SetTOS(ipv4.DSCP_CS0)
// p.SetTTL(16)
// _, err = p.Write(data, nil, src)
// if err != nil {
// // error handling
// }
// dst := &net.UDPAddr{IP: group, Port: 1024}
// for _, ifi := range []*net.Interface{en0, en1} {
// err := p.SetMulticastInterface(ifi)
// if err != nil {
// // error handling
// }
// p.SetMulticastTTL(2)
// _, err = p.Write(data, nil, dst)
// if err != nil {
// // error handling
// }
// }
// }
//
//
// More multicasting
//
// An application that uses PacketConn or RawConn might join the
// multiple group addresses. For example, a UDP listener with port
// 1024 might join two different groups across over two different
// network interfaces by using:
//
// c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
// if err != nil {
// // error handling
// }
// defer c.Close()
// p := ipv4.NewPacketConn(c)
// err = p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)})
// if err != nil {
// // error handling
// }
// err = p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)})
// if err != nil {
// // error handling
// }
// err = p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)})
// if err != nil {
// // error handling
// }
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same group address. The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
// c1, err := net.ListenPacket("udp4", "224.0.0.0:1024")
// if err != nil {
// // error handling
// }
// defer c1.Close()
// c2, err := net.ListenPacket("udp4", "224.0.0.0:1024")
// if err != nil {
// // error handling
// }
// defer c2.Close()
// p1 := ipv4.NewPacketConn(c1)
// err = p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)})
// if err != nil {
// // error handling
// }
// p2 := ipv4.NewPacketConn(c2)
// err = p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)})
// if err != nil {
// // error handling
// }
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
// err = p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)})
// if err != nil {
// // error handling
// }
// err = p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)})
// if err != nil {
// // error handling
// }
package ipv4

181
ipv4/endpoint.go Normal file
View File

@@ -0,0 +1,181 @@
// 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"
"syscall"
"time"
)
// A Conn represents a network endpoint that uses the IPv4 transport.
// It is used to control basic IP-level socket options such as TOS and
// TTL.
type Conn struct {
genericOpt
}
type genericOpt struct {
c net.Conn
}
func (c *genericOpt) ok() bool { return c != nil && c.c != nil }
// NewConn returns a new Conn.
func NewConn(c net.Conn) *Conn {
return &Conn{
genericOpt: genericOpt{c},
}
}
// A PacketConn represents a packet network endpoint that uses the
// IPv4 transport. It is used to control several IP-level socket
// options including multicasting. It also provides datagram based
// network I/O methods specific to the IPv4 and higher layer protocols
// such as UDP.
type PacketConn struct {
genericOpt
dgramOpt
payloadHandler
}
type dgramOpt struct {
c net.PacketConn
}
func (c *dgramOpt) ok() bool { return c != nil && c.c != nil }
// SetControlMessage sets the per packet IP-level socket options.
func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
if !c.payloadHandler.ok() {
return syscall.EINVAL
}
fd, err := c.payloadHandler.sysfd()
if err != nil {
return err
}
return setControlMessage(fd, &c.payloadHandler.rawOpt, cf, on)
}
// SetDeadline sets the read and write deadlines associated with the
// endpoint.
func (c *PacketConn) SetDeadline(t time.Time) error {
if !c.payloadHandler.ok() {
return syscall.EINVAL
}
return c.payloadHandler.c.SetDeadline(t)
}
// SetReadDeadline sets the read deadline associated with the
// endpoint.
func (c *PacketConn) SetReadDeadline(t time.Time) error {
if !c.payloadHandler.ok() {
return syscall.EINVAL
}
return c.payloadHandler.c.SetReadDeadline(t)
}
// SetWriteDeadline sets the write deadline associated with the
// endpoint.
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
if !c.payloadHandler.ok() {
return syscall.EINVAL
}
return c.payloadHandler.c.SetWriteDeadline(t)
}
// Close closes the endpoint.
func (c *PacketConn) Close() error {
if !c.payloadHandler.ok() {
return syscall.EINVAL
}
return c.payloadHandler.c.Close()
}
// NewPacketConn returns a new PacketConn using c as its underlying
// transport.
func NewPacketConn(c net.PacketConn) *PacketConn {
return &PacketConn{
genericOpt: genericOpt{c.(net.Conn)},
dgramOpt: dgramOpt{c},
payloadHandler: payloadHandler{c: c},
}
}
// A RawConn represents a packet network endpoint that uses the IPv4
// transport. It is used to control several IP-level socket options
// including IPv4 header manipulation. It also provides datagram
// based network I/O methods specific to the IPv4 and higher layer
// protocols that handle IPv4 datagram directly such as OSPF, GRE.
type RawConn struct {
genericOpt
dgramOpt
packetHandler
}
// SetControlMessage sets the per packet IP-level socket options.
func (c *RawConn) SetControlMessage(cf ControlFlags, on bool) error {
if !c.packetHandler.ok() {
return syscall.EINVAL
}
fd, err := c.packetHandler.sysfd()
if err != nil {
return err
}
return setControlMessage(fd, &c.packetHandler.rawOpt, cf, on)
}
// SetDeadline sets the read and write deadlines associated with the
// endpoint.
func (c *RawConn) SetDeadline(t time.Time) error {
if !c.packetHandler.ok() {
return syscall.EINVAL
}
return c.packetHandler.c.SetDeadline(t)
}
// SetReadDeadline sets the read deadline associated with the
// endpoint.
func (c *RawConn) SetReadDeadline(t time.Time) error {
if !c.packetHandler.ok() {
return syscall.EINVAL
}
return c.packetHandler.c.SetReadDeadline(t)
}
// SetWriteDeadline sets the write deadline associated with the
// endpoint.
func (c *RawConn) SetWriteDeadline(t time.Time) error {
if !c.packetHandler.ok() {
return syscall.EINVAL
}
return c.packetHandler.c.SetWriteDeadline(t)
}
// Close closes the endpoint.
func (c *RawConn) Close() error {
if !c.packetHandler.ok() {
return syscall.EINVAL
}
return c.packetHandler.c.Close()
}
// NewRawConn returns a new RawConn using c as it sunderlying
// transport.
func NewRawConn(c net.PacketConn) (*RawConn, error) {
r := &RawConn{
genericOpt: genericOpt{c.(net.Conn)},
dgramOpt: dgramOpt{c},
packetHandler: packetHandler{c: c.(*net.IPConn)},
}
fd, err := r.packetHandler.sysfd()
if err != nil {
return nil, err
}
if err := setIPv4HeaderPrepend(fd, true); err != nil {
return nil, err
}
return r, nil
}

252
ipv4/example_test.go Normal file
View File

@@ -0,0 +1,252 @@
// 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_test
import (
"code.google.com/p/go.net/ipv4"
"log"
"net"
)
func ExampleUnicastTCPListener() {
ln, err := net.Listen("tcp4", "0.0.0.0:1024")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
for {
c, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
go func(c net.Conn) {
defer c.Close()
err := ipv4.NewConn(c).SetTOS(ipv4.DSCP_AF11)
if err != nil {
log.Fatal(err)
}
_, err = c.Write([]byte("HELLO-R-U-THERE-ACK"))
if err != nil {
log.Fatal(err)
}
}(c)
}
}
func ExampleMulticastUDPListener() {
en0, err := net.InterfaceByName("en0")
if err != nil {
log.Fatal(err)
}
en1, err := net.InterfaceByIndex(911)
if err != nil {
log.Fatal(err)
}
group := net.IPv4(224, 0, 0, 250)
c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
if err != nil {
log.Fatal(err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
err = p.JoinGroup(en0, &net.UDPAddr{IP: group})
if err != nil {
log.Fatal(err)
}
err = p.JoinGroup(en1, &net.UDPAddr{IP: group})
if err != nil {
log.Fatal(err)
}
err = p.SetControlMessage(ipv4.FlagDst, true)
if err != nil {
log.Fatal(err)
}
b := make([]byte, 1500)
for {
n, cm, src, err := p.Read(b)
if err != nil {
log.Fatal(err)
}
if cm.Dst.IsMulticast() {
if cm.Dst.Equal(group) {
// joined group, do something
} else {
// unknown group, discard
continue
}
}
p.SetTOS(ipv4.DSCP_CS7)
p.SetTTL(16)
_, err = p.Write(b[:n], nil, src)
if err != nil {
log.Fatal(err)
}
dst := &net.UDPAddr{IP: group, Port: 1024}
for _, ifi := range []*net.Interface{en0, en1} {
err := p.SetMulticastInterface(ifi)
if err != nil {
log.Fatal(err)
}
p.SetMulticastTTL(2)
_, err = p.Write(b[:n], nil, dst)
if err != nil {
log.Fatal(err)
}
}
}
err = p.LeaveGroup(en1, &net.UDPAddr{IP: group})
if err != nil {
log.Fatal(err)
}
newgroup := net.IPv4(224, 0, 0, 249)
err = p.JoinGroup(en1, &net.UDPAddr{IP: newgroup})
if err != nil {
log.Fatal(err)
}
}
type OSPFHeader struct {
Version byte
Type byte
Len uint16
RouterID uint32
AreaID uint32
Checksum uint16
}
const (
OSPFHeaderLen = 14
OSPFHelloHeaderLen = 20
OSPF_VERSION = 2
OSPF_TYPE_HELLO = iota + 1
OSPF_TYPE_DB_DESCRIPTION
OSPF_TYPE_LS_REQUEST
OSPF_TYPE_LS_UPDATE
OSPF_TYPE_LS_ACK
)
var (
AllSPFRouters = net.IPv4(224, 0, 0, 5)
AllDRouters = net.IPv4(224, 0, 0, 6)
)
func ExampleIPOSPFListener() {
var ifs []*net.Interface
en0, err := net.InterfaceByName("en0")
if err != nil {
log.Fatal(err)
}
ifs = append(ifs, en0)
en1, err := net.InterfaceByIndex(911)
if err != nil {
log.Fatal(err)
}
ifs = append(ifs, en1)
c, err := net.ListenPacket("ip4:89", "0.0.0.0")
if err != nil {
log.Fatal(err)
}
defer c.Close()
r, err := ipv4.NewRawConn(c)
if err != nil {
log.Fatal(err)
}
for _, ifi := range ifs {
err := r.JoinGroup(ifi, &net.IPAddr{IP: AllSPFRouters})
if err != nil {
log.Fatal(err)
}
err = r.JoinGroup(ifi, &net.IPAddr{IP: AllDRouters})
if err != nil {
log.Fatal(err)
}
}
err = r.SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)
if err != nil {
log.Fatal(err)
}
r.SetTOS(ipv4.DSCP_CS6)
parseOSPFHeader := func(b []byte) *OSPFHeader {
if len(b) < OSPFHeaderLen {
return nil
}
return &OSPFHeader{
Version: b[0],
Type: b[1],
Len: uint16(b[2])<<8 | uint16(b[3]),
RouterID: uint32(b[4])<<24 | uint32(b[5])<<16 | uint32(b[6])<<8 | uint32(b[7]),
AreaID: uint32(b[8])<<24 | uint32(b[9])<<16 | uint32(b[10])<<8 | uint32(b[11]),
Checksum: uint16(b[12])<<8 | uint16(b[13]),
}
}
b := make([]byte, 1500)
for {
iph, p, _, err := r.Read(b)
if err != nil {
log.Fatal(err)
}
if iph.Version != ipv4.Version {
continue
}
if iph.Dst.IsMulticast() {
if !iph.Dst.Equal(AllSPFRouters) && !iph.Dst.Equal(AllDRouters) {
continue
}
}
ospfh := parseOSPFHeader(p)
if ospfh == nil {
continue
}
if ospfh.Version != OSPF_VERSION {
continue
}
switch ospfh.Type {
case OSPF_TYPE_HELLO:
case OSPF_TYPE_DB_DESCRIPTION:
case OSPF_TYPE_LS_REQUEST:
case OSPF_TYPE_LS_UPDATE:
case OSPF_TYPE_LS_ACK:
}
}
}
func ExampleWriteIPOSPFHello(c *ipv4.RawConn, ifs []*net.Interface) {
hello := make([]byte, OSPFHelloHeaderLen)
ospf := make([]byte, OSPFHeaderLen)
ospf[0] = OSPF_VERSION
ospf[1] = OSPF_TYPE_HELLO
ospf = append(ospf, hello...)
iph := &ipv4.Header{}
iph.Version = ipv4.Version
iph.Len = ipv4.HeaderLen
iph.TOS = ipv4.DSCP_CS6
iph.TotalLen = ipv4.HeaderLen + len(ospf)
iph.TTL = 1
iph.Protocol = 89
iph.Dst = AllSPFRouters
for _, ifi := range ifs {
err := c.SetMulticastInterface(ifi)
if err != nil {
return
}
err = c.Write(iph, ospf, nil)
if err != nil {
return
}
}
}

29
ipv4/genericopt_plan9.go Normal file
View File

@@ -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.
package ipv4
import (
"syscall"
)
func (c *genericOpt) TOS() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
}
func (c *genericOpt) SetTOS(tos int) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}
func (c *genericOpt) TTL() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
}
func (c *genericOpt) SetTTL(ttl int) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}

61
ipv4/genericopt_posix.go Normal file
View File

@@ -0,0 +1,61 @@
// 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 (
"syscall"
)
// TOS returns the type-of-service field value for outgoing packets.
func (c *genericOpt) TOS() (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return 0, err
}
return ipv4TOS(fd)
}
// SetTOS sets the type-of-service field value for future outgoing
// packets.
func (c *genericOpt) SetTOS(tos int) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
return setIPv4TOS(fd, tos)
}
// TTL returns the time-to-live field value for outgoing packets.
func (c *genericOpt) TTL() (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return 0, err
}
return ipv4TTL(fd)
}
// SetTTL sets the time-to-live field value for future outgoing
// packets.
func (c *genericOpt) SetTTL(ttl int) error {
if !c.ok() {
return syscall.EINVAL
}
fd, err := c.sysfd()
if err != nil {
return err
}
return setIPv4TTL(fd, ttl)
}

194
ipv4/header.go Normal file
View File

@@ -0,0 +1,194 @@
// 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 (
"errors"
"fmt"
"net"
"runtime"
"syscall"
"unsafe"
)
var (
errMissingAddress = errors.New("missing address")
errMissingHeader = errors.New("missing header")
errHeaderTooShort = errors.New("header too short")
errBufferTooShort = errors.New("buffer too short")
errInvalidConnType = errors.New("invalid conn type")
)
// References:
//
// RFC 791 Internet Protocol
// http://tools.ietf.org/html/rfc791
// RFC 1112 Host Extensions for IP Multicasting
// http://tools.ietf.org/html/rfc1112
// RFC 1122 Requirements for Internet Hosts
// http://tools.ietf.org/html/rfc1122
// RFC 2474 Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers
// http://tools.ietf.org/html/rfc2474
// RFC 2475 An Architecture for Differentiated Services
// http://tools.ietf.org/html/rfc2475
// RFC 2597 Assured Forwarding PHB Group
// http://tools.ietf.org/html/rfc2597
// RFC 2598 An Expedited Forwarding PHB
// http://tools.ietf.org/html/rfc2598
// RFC 3168 The Addition of Explicit Congestion Notification (ECN) to IP
// http://tools.ietf.org/html/rfc3168
// RFC 3260 New Terminology and Clarifications for Diffserv
// http://tools.ietf.org/html/rfc3260
const (
Version = 4 // protocol version
HeaderLen = 20 // header length without extension headers
maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields
)
const (
// DiffServ class selector codepoints in RFC 2474.
DSCP_CS0 = 0x00 // best effort
DSCP_CS1 = 0x20 // class 1
DSCP_CS2 = 0x40 // class 2
DSCP_CS3 = 0x60 // class 3
DSCP_CS4 = 0x80 // class 4
DSCP_CS5 = 0xa0 // expedited forwarding
DSCP_CS6 = 0xc0 // subsume deprecated IP precedence, internet control (routing information update)
DSCP_CS7 = 0xe0 // subsume deprecated IP precedence, network control (link, neighbor liveliness check)
// DiffServ assured forwarding codepoints in RFC 2474, 2475, 2597 and 3260.
DSCP_AF11 = 0x28 // class 1 low drop precedence
DSCP_AF12 = 0x30 // class 1 medium drop precedence
DSCP_AF13 = 0x38 // class 1 high drop precedence
DSCP_AF21 = 0x48 // class 2 low drop precedence
DSCP_AF22 = 0x50 // class 2 medium drop precedence
DSCP_AF23 = 0x58 // class 2 high drop precedence
DSCP_AF31 = 0x68 // class 3 low drop precedence
DSCP_AF32 = 0x70 // class 3 medium drop precedence
DSCP_AF33 = 0x78 // class 3 high drop precedence
DSCP_AF41 = 0x88 // class 4 low drop precedence
DSCP_AF42 = 0x90 // class 4 medium drop precedence
DSCP_AF43 = 0x98 // class 4 high drop precedence
DSCP_EF = 0xb8 // expedited forwarding
// ECN codepoints in RFC 3168.
ECN_NOTECT = 0x00 // not ECN-capable transport
ECN_ECT1 = 0x01 // ECN-capable transport, ECT(1)
ECN_ECT0 = 0x02 // ECN-capable transport, ECT(0)
ECN_CE = 0x03 // congestion experienced
)
type headerField int
const (
posTOS headerField = 1 // type-of-service
posTotalLen = 2 // packet total length
posID = 4 // identification
posFragOff = 6 // fragment offset
posTTL = 8 // time-to-live
posProtocol = 9 // next protocol
posChecksum = 10 // checksum
posSrc = 12 // source address
posDst = 16 // destination address
)
// A Header represents an IPv4 header.
type Header struct {
Version int // protocol version
Len int // header length
TOS int // type-of-service
TotalLen int // packet total length
ID int // identification
FragOff int // fragment offset
TTL int // time-to-live
Protocol int // next protocol
Checksum int // checksum
Src net.IP // source address
Dst net.IP // destination address
Options []byte // options, extension headers
}
func (h *Header) String() string {
if h == nil {
return "<nil>"
}
return fmt.Sprintf("ver: %v, hdrlen: %v, tos: %#x, totallen: %v, id: %#x, fragoff: %#x, ttl: %v, proto: %v, cksum: %#x, src: %v, dst: %v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst)
}
// Please refer to the online manual; IP(4) on Darwin, FreeBSD and
// OpenBSD. IP(7) on Linux.
var supportsNewIPInput = runtime.GOOS == "linux" || runtime.GOOS == "openbsd"
// Marshal returns the binary encoding of the IPv4 header h.
func (h *Header) Marshal() ([]byte, error) {
if h == nil {
return nil, syscall.EINVAL
}
if h.Len < HeaderLen {
return nil, errHeaderTooShort
}
hdrlen := HeaderLen + len(h.Options)
b := make([]byte, hdrlen)
b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f))
b[posTOS] = byte(h.TOS)
if supportsNewIPInput {
b[posTotalLen], b[posTotalLen+1] = byte(h.TotalLen>>8), byte(h.TotalLen)
b[posFragOff], b[posFragOff+1] = byte(h.FragOff>>8), byte(h.FragOff)
} else {
*(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0])) = uint16(h.TotalLen)
*(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])) = uint16(h.FragOff)
}
b[posID], b[posID+1] = byte(h.ID>>8), byte(h.ID)
b[posTTL] = byte(h.TTL)
b[posProtocol] = byte(h.Protocol)
b[posChecksum], b[posChecksum+1] = byte(h.Checksum>>8), byte(h.Checksum)
if ip := h.Src.To4(); ip != nil {
copy(b[posSrc:posSrc+net.IPv4len], ip[0:net.IPv4len])
}
if ip := h.Dst.To4(); ip != nil {
copy(b[posDst:posDst+net.IPv4len], ip[0:net.IPv4len])
} else {
return nil, errMissingAddress
}
if len(h.Options) > 0 {
copy(b[HeaderLen:], h.Options)
}
return b, nil
}
// ParseHeader parses b as an IPv4 header.
func ParseHeader(b []byte) (*Header, error) {
if len(b) < HeaderLen {
return nil, errHeaderTooShort
}
hdrlen := (int(b[0]) & 0x0f) << 2
if hdrlen > len(b) {
return nil, errBufferTooShort
}
h := &Header{}
h.Version = int(b[0] >> 4)
h.Len = hdrlen
h.TOS = int(b[posTOS])
if supportsNewIPInput {
h.TotalLen = int(b[posTotalLen])<<8 | int(b[posTotalLen+1])
h.FragOff = int(b[posFragOff])<<8 | int(b[posFragOff+1])
} else {
h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0])))
h.TotalLen += hdrlen
h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])))
}
h.ID = int(b[posID])<<8 | int(b[posID+1])
h.TTL = int(b[posTTL])
h.Protocol = int(b[posProtocol])
h.Checksum = int(b[posChecksum])<<8 | int(b[posChecksum+1])
h.Src = net.IPv4(b[posSrc], b[posSrc+1], b[posSrc+2], b[posSrc+3])
h.Dst = net.IPv4(b[posDst], b[posDst+1], b[posDst+2], b[posDst+3])
if hdrlen-HeaderLen > 0 {
h.Options = make([]byte, hdrlen-HeaderLen)
copy(h.Options, b[HeaderLen:])
}
return h, nil
}

99
ipv4/header_test.go Normal file
View File

@@ -0,0 +1,99 @@
// 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_test
import (
"bytes"
"code.google.com/p/go.net/ipv4"
"net"
"reflect"
"runtime"
"testing"
)
var (
wireHeaderFromKernel = [ipv4.HeaderLen]byte{
0x45, 0x01, 0xbe, 0xef,
0xca, 0xfe, 0x05, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
}
wireHeaderToKernel = [ipv4.HeaderLen]byte{
0x45, 0x01, 0xbe, 0xef,
0xca, 0xfe, 0x05, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
}
wireHeaderFromTradBSDKernel = [ipv4.HeaderLen]byte{
0x45, 0x01, 0xdb, 0xbe,
0xca, 0xfe, 0xdc, 0x05,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
}
wireHeaderToTradBSDKernel = [ipv4.HeaderLen]byte{
0x45, 0x01, 0xef, 0xbe,
0xca, 0xfe, 0xdc, 0x05,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
}
// TODO(mikio): Add platform dependent wire header formats when
// we support new platforms.
)
func testHeader() *ipv4.Header {
h := &ipv4.Header{}
h.Version = ipv4.Version
h.Len = ipv4.HeaderLen
h.TOS = 1
h.TotalLen = 0xbeef
h.ID = 0xcafe
h.FragOff = 1500
h.TTL = 255
h.Protocol = 1
h.Checksum = 0xdead
h.Src = net.IPv4(172, 16, 254, 254)
h.Dst = net.IPv4(192, 168, 0, 1)
return h
}
func TestMarshalHeader(t *testing.T) {
th := testHeader()
b, err := th.Marshal()
if err != nil {
t.Fatalf("ipv4.Header.Marshal failed: %v", err)
}
var wh []byte
switch runtime.GOOS {
case "linux", "openbsd":
wh = wireHeaderToKernel[:]
default:
wh = wireHeaderToTradBSDKernel[:]
}
if !bytes.Equal(b, wh) {
t.Fatalf("ipv4.Header.Marshal failed: %#v not equal %#v", b, wh)
}
}
func TestParseHeader(t *testing.T) {
var wh []byte
switch runtime.GOOS {
case "linux", "openbsd":
wh = wireHeaderFromKernel[:]
default:
wh = wireHeaderFromTradBSDKernel[:]
}
h, err := ipv4.ParseHeader(wh)
if err != nil {
t.Fatalf("ipv4.ParseHeader failed: %v", err)
}
th := testHeader()
if !reflect.DeepEqual(h, th) {
t.Fatalf("ipv4.ParseHeader failed: %#v not equal %#v", h, th)
}
}

85
ipv4/helper.go Normal file
View File

@@ -0,0 +1,85 @@
// 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 (
"errors"
"net"
)
var (
errNoSuchInterface = errors.New("no such interface")
errNoSuchMulticastInterface = errors.New("no such multicast interface")
)
func boolint(b bool) int {
if b {
return 1
}
return 0
}
func netAddrToIP4(a net.Addr) net.IP {
switch v := a.(type) {
case *net.UDPAddr:
if ip := v.IP.To4(); ip != nil {
return ip
}
case *net.IPAddr:
if ip := v.IP.To4(); ip != nil {
return 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
}

29
ipv4/helper_plan9.go Normal file
View File

@@ -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.
package ipv4
import (
"syscall"
)
func (c *genericOpt) sysfd() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
}
func (c *dgramOpt) sysfd() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
}
func (c *payloadHandler) sysfd() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
}
func (c *packetHandler) sysfd() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
}

42
ipv4/helper_posix.go Normal file
View File

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

50
ipv4/helper_unix.go Normal file
View File

@@ -0,0 +1,50 @@
// 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
package ipv4
import (
"net"
"reflect"
)
func (c *genericOpt) sysfd() (int, error) {
switch p := c.c.(type) {
case *net.TCPConn, *net.UDPConn, *net.IPConn:
return sysfd(p)
}
return 0, errInvalidConnType
}
func (c *dgramOpt) sysfd() (int, error) {
switch p := c.c.(type) {
case *net.UDPConn, *net.IPConn:
return sysfd(p.(net.Conn))
}
return 0, errInvalidConnType
}
func (c *payloadHandler) sysfd() (int, error) {
return sysfd(c.c.(net.Conn))
}
func (c *packetHandler) sysfd() (int, error) {
return sysfd(c.c)
}
func sysfd(c net.Conn) (int, error) {
cv := reflect.ValueOf(c)
switch ce := cv.Elem(); ce.Kind() {
case reflect.Struct:
nfd := ce.FieldByName("conn").FieldByName("fd")
switch fe := nfd.Elem(); fe.Kind() {
case reflect.Struct:
fd := fe.FieldByName("sysfd")
return int(fd.Int()), nil
}
}
return 0, errInvalidConnType
}

49
ipv4/helper_windows.go Normal file
View File

@@ -0,0 +1,49 @@
// 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"
"reflect"
"syscall"
)
func (c *genericOpt) sysfd() (syscall.Handle, error) {
switch p := c.c.(type) {
case *net.TCPConn, *net.UDPConn, *net.IPConn:
return sysfd(p)
}
return syscall.InvalidHandle, errInvalidConnType
}
func (c *dgramOpt) sysfd() (syscall.Handle, error) {
switch p := c.c.(type) {
case *net.UDPConn, *net.IPConn:
return sysfd(p.(net.Conn))
}
return syscall.InvalidHandle, errInvalidConnType
}
func (c *payloadHandler) sysfd() (syscall.Handle, error) {
return sysfd(c.c.(net.Conn))
}
func (c *packetHandler) sysfd() (syscall.Handle, error) {
return sysfd(c.c)
}
func sysfd(c net.Conn) (syscall.Handle, error) {
cv := reflect.ValueOf(c)
switch ce := cv.Elem(); ce.Kind() {
case reflect.Struct:
fd := ce.FieldByName("conn").FieldByName("fd")
switch fe := fd.Elem(); fe.Kind() {
case reflect.Struct:
sysfd := fe.FieldByName("sysfd")
return syscall.Handle(sysfd.Uint()), nil
}
}
return syscall.InvalidHandle, errInvalidConnType
}

47
ipv4/mockicmp_test.go Normal file
View File

@@ -0,0 +1,47 @@
// 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_test
import (
"bytes"
"flag"
)
var testExternal = flag.Bool("external", true, "allow use of external networks during long test")
func newICMPEchoRequest(id, seqnum, msglen int, filler []byte) []byte {
b := newICMPInfoMessage(id, seqnum, msglen, filler)
b[0] = 8
// calculate ICMP checksum
cklen := len(b)
s := uint32(0)
for i := 0; i < cklen-1; i += 2 {
s += uint32(b[i+1])<<8 | uint32(b[i])
}
if cklen&1 == 1 {
s += uint32(b[cklen-1])
}
s = (s >> 16) + (s & 0xffff)
s = s + (s >> 16)
// place checksum back in header; using ^= avoids the
// assumption the checksum bytes are zero
b[2] ^= byte(^s & 0xff)
b[3] ^= byte(^s >> 8)
return b
}
func newICMPInfoMessage(id, seqnum, msglen int, filler []byte) []byte {
b := make([]byte, msglen)
copy(b[8:], bytes.Repeat(filler, (msglen-8)/len(filler)+1))
b[0] = 0 // type
b[1] = 0 // code
b[2] = 0 // checksum
b[3] = 0 // checksum
b[4] = byte(id >> 8) // identifier
b[5] = byte(id & 0xff) // identifier
b[6] = byte(seqnum >> 8) // sequence number
b[7] = byte(seqnum & 0xff) // sequence number
return b
}

View File

@@ -0,0 +1,133 @@
// 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
package ipv4_test
import (
"code.google.com/p/go.net/ipv4"
"net"
"testing"
"time"
)
// runPayloadTransponder transmits IPv4 datagram payloads to the
// loopback address or interface and captures the loopback'd datagram
// payloads.
func runPayloadTransponder(t *testing.T, c *ipv4.PacketConn, wb []byte, dst net.Addr) {
cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
rb := make([]byte, 1500)
for i, toggle := range []bool{true, false, true} {
if err := c.SetControlMessage(cf, toggle); err != nil {
t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err)
}
c.SetTOS(i + 1)
var ip net.IP
switch v := dst.(type) {
case *net.UDPAddr:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip.IsMulticast() {
c.SetMulticastTTL(i + 1)
} else {
c.SetTTL(i + 1)
}
c.SetDeadline(time.Now().Add(100 * time.Millisecond))
if _, err := c.Write(wb, nil, dst); err != nil {
t.Fatalf("ipv4.PacketConn.Write failed: %v", err)
}
_, cm, _, err := c.Read(rb)
if err != nil {
t.Fatalf("ipv4.PacketConn.Read failed: %v", err)
}
t.Logf("rcvd cmsg: %v", cm)
}
}
// runDatagramTransponder transmits ICMP for IPv4 datagrams to the
// loopback address or interface and captures the response datagrams
// from the protocol stack within the kernel.
func runDatagramTransponder(t *testing.T, c *ipv4.RawConn, wb []byte, src, dst net.Addr) {
cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
rb := make([]byte, ipv4.HeaderLen+len(wb))
for i, toggle := range []bool{true, false, true} {
if err := c.SetControlMessage(cf, toggle); err != nil {
t.Fatalf("ipv4.RawConn.SetControlMessage failed: %v", err)
}
wh := &ipv4.Header{}
wh.Version = ipv4.Version
wh.Len = ipv4.HeaderLen
wh.TOS = i + 1
wh.TotalLen = ipv4.HeaderLen + len(wb)
wh.TTL = i + 1
wh.Protocol = 1
if src != nil {
wh.Src = src.(*net.IPAddr).IP
}
if dst != nil {
wh.Dst = dst.(*net.IPAddr).IP
}
c.SetDeadline(time.Now().Add(100 * time.Millisecond))
if err := c.Write(wh, wb, nil); err != nil {
t.Fatalf("ipv4.RawConn.Write failed: %v", err)
}
rh, _, cm, err := c.Read(rb)
if err != nil {
t.Fatalf("ipv4.RawConn.Read failed: %v", err)
}
t.Logf("rcvd cmsg: %v", cm.String())
t.Logf("rcvd hdr: %v", rh.String())
}
}
func loopbackInterface() *net.Interface {
ift, err := net.Interfaces()
if err != nil {
return nil
}
for _, ifi := range ift {
if ifi.Flags&net.FlagLoopback != 0 {
return &ifi
}
}
return nil
}
func isGoodForMulticast(ifi *net.Interface) (net.IP, bool) {
if ifi.Flags&net.FlagUp == 0 {
return nil, false
}
// We need a unicast IPv4 address that can be used to specify
// the IPv4 multicast interface.
ifat, err := ifi.Addrs()
if err != nil {
return nil, false
}
if len(ifat) == 0 {
return nil, false
}
var ip net.IP
for _, ifa := range ifat {
switch v := ifa.(type) {
case *net.IPAddr:
ip = v.IP
case *net.IPNet:
ip = v.IP
default:
continue
}
if ip.To4() == nil {
ip = nil
continue
}
break
}
if ip == nil {
return nil, false
}
return ip, true
}

128
ipv4/multicast_test.go Normal file
View File

@@ -0,0 +1,128 @@
// 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
package ipv4_test
import (
"code.google.com/p/go.net/ipv4"
"net"
"os"
"testing"
)
func TestReadWriteMulticastIPPayloadUDP(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
c, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
ifi := loopbackInterface()
if ifi == nil {
t.Logf("skipping test; an appropriate interface not found")
return
}
dst, err := net.ResolveUDPAddr("udp4", "224.0.0.254:1024") // see RFC 4727
if err != nil {
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
}
p := ipv4.NewPacketConn(c)
if err := p.JoinGroup(ifi, dst); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
if err := p.SetMulticastInterface(ifi); err != nil {
t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err)
}
if err := p.SetMulticastLoopback(true); err != nil {
t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err)
}
runPayloadTransponder(t, p, []byte("HELLO-R-U-THERE"), dst)
}
func TestReadWriteMulticastIPPayloadICMP(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
ifi := loopbackInterface()
if ifi == nil {
t.Logf("skipping test; an appropriate interface not found")
return
}
dst, err := net.ResolveIPAddr("ip4", "224.0.0.254") // see RFC 4727
if err != nil {
t.Fatalf("net.ResolveIPAddr failed: %v", err)
}
p := ipv4.NewPacketConn(c)
if err := p.JoinGroup(ifi, dst); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
if err := p.SetMulticastInterface(ifi); err != nil {
t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err)
}
id := os.Getpid() & 0xffff
pld := newICMPEchoRequest(id, 1, 128, []byte("HELLO-R-U-THERE"))
runPayloadTransponder(t, p, pld, dst)
}
func TestReadWriteMulticastIPDatagram(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
ifi := loopbackInterface()
if ifi == nil {
t.Logf("skipping test; an appropriate interface not found")
return
}
dst, err := net.ResolveIPAddr("ip4", "224.0.0.254") // see RFC 4727
if err != nil {
t.Fatalf("ResolveIPAddr failed: %v", err)
}
r, err := ipv4.NewRawConn(c)
if err != nil {
t.Fatalf("ipv4.NewRawConn failed: %v", err)
}
if err := r.JoinGroup(ifi, dst); err != nil {
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
}
if err := r.SetMulticastInterface(ifi); err != nil {
t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err)
}
id := os.Getpid() & 0xffff
pld := newICMPEchoRequest(id, 1, 128, []byte("HELLO-R-U-THERE"))
runDatagramTransponder(t, r, pld, nil, dst)
}

View File

@@ -0,0 +1,244 @@
// 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
package ipv4_test
import (
"code.google.com/p/go.net/ipv4"
"net"
"os"
"testing"
)
var udpMultipleGroupListenerTests = []struct {
gaddr *net.UDPAddr
}{
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}}, // see RFC 4727
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}}, // see RFC 4727
{&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}}, // see RFC 4727
}
func TestUDPSingleConnWithMultipleGroupListeners(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
for _, tt := range udpMultipleGroupListenerTests {
// listen to a wildcard address with no reusable port
c, err := net.ListenPacket("udp4", "0.0.0.0:0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
var mift []*net.Interface
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
if _, ok := isGoodForMulticast(&ifi); !ok {
continue
}
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
}
}
}
}
func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
for _, tt := range udpMultipleGroupListenerTests {
// listen to a group address, actually a wildcard address
// with reusable port
c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c1.Close()
c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c2.Close()
var ps [2]*ipv4.PacketConn
ps[0] = ipv4.NewPacketConn(c1)
ps[1] = ipv4.NewPacketConn(c2)
var mift []*net.Interface
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
if _, ok := isGoodForMulticast(&ifi); !ok {
continue
}
for _, p := range ps {
if err := p.JoinGroup(&ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup %v on %v failed: %v", tt.gaddr, ifi, err)
}
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
for _, p := range ps {
if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", tt.gaddr, ifi, err)
}
}
}
}
}
func TestIPSingleConnWithSingleGroupListener(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
// listen to a wildcard address
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
r, err := ipv4.NewRawConn(c)
if err != nil {
t.Fatalf("ipv4.RawConn failed: %v", err)
}
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
var mift []*net.Interface
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
if _, ok := isGoodForMulticast(&ifi); !ok {
continue
}
if err := r.JoinGroup(&ifi, gaddr); err != nil {
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
if err := r.LeaveGroup(ifi, gaddr); err != nil {
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", ifi, err)
}
}
}
func TestUDPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
type ml struct {
c *ipv4.PacketConn
ifi *net.Interface
}
var mlt []*ml
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
ip, ok := isGoodForMulticast(&ifi)
if !ok {
continue
}
// listen to a unicast interface address
c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // see RFC 4727
if err != nil {
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
if err := p.JoinGroup(&ifi, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{p, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}
}
func TestIPPerInterfaceSingleConnWithSingleGroupListener(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
gaddr := &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
type ml struct {
c *ipv4.RawConn
ifi *net.Interface
}
var mlt []*ml
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
ip, ok := isGoodForMulticast(&ifi)
if !ok {
continue
}
// listen to a unicast interface address
c, err := net.ListenPacket("ip4:253", ip.String()) // see RFC 4727
if err != nil {
t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
}
defer c.Close()
r, err := ipv4.NewRawConn(c)
if err != nil {
t.Fatalf("ipv4.NewRawConn failed: %v", err)
}
if err := r.JoinGroup(&ifi, gaddr); err != nil {
t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{r, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}
}

View File

@@ -0,0 +1,129 @@
// 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_test
import (
"code.google.com/p/go.net/ipv4"
"net"
"os"
"runtime"
"testing"
)
type testMulticastConn interface {
testUnicastConn
MulticastTTL() (int, error)
SetMulticastTTL(ttl int) error
MulticastLoopback() (bool, error)
SetMulticastLoopback(bool) error
JoinGroup(*net.Interface, net.Addr) error
LeaveGroup(*net.Interface, net.Addr) error
}
type multicastSockoptTest struct {
tos int
ttl int
mcttl int
mcloop bool
gaddr net.IP
}
var multicastSockoptTests = []multicastSockoptTest{
{ipv4.DSCP_CS0 | ipv4.ECN_NOTECT, 127, 128, false, net.IPv4(224, 0, 0, 249)}, // see RFC 4727
{ipv4.DSCP_AF11 | ipv4.ECN_NOTECT, 255, 254, true, net.IPv4(224, 0, 0, 250)}, // see RFC 4727
}
func TestUDPMulticastSockopt(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
for _, tt := range multicastSockoptTests {
c, err := net.ListenPacket("udp4", "0.0.0.0:0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
testMulticastSockopt(t, tt, p, &net.UDPAddr{IP: tt.gaddr})
}
}
func TestIPMulticastSockopt(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
for _, tt := range multicastSockoptTests {
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
r, _ := ipv4.NewRawConn(c)
testMulticastSockopt(t, tt, r, &net.IPAddr{IP: tt.gaddr})
}
}
func testMulticastSockopt(t *testing.T, tt multicastSockoptTest, c testMulticastConn, gaddr net.Addr) {
switch runtime.GOOS {
case "windows":
// IP_TOS option is supported on Windows 8 and beyond.
t.Logf("skipping IP_TOS test on %q", runtime.GOOS)
default:
if err := c.SetTOS(tt.tos); err != nil {
t.Fatalf("ipv4.PacketConn.SetTOS failed: %v", err)
}
if v, err := c.TOS(); err != nil {
t.Fatalf("ipv4.PacketConn.TOS failed: %v", err)
} else if v != tt.tos {
t.Fatalf("Got unexpected TOS value %v; expected %v", v, tt.tos)
}
}
if err := c.SetTTL(tt.ttl); err != nil {
t.Fatalf("ipv4.PacketConn.SetTTL failed: %v", err)
}
if v, err := c.TTL(); err != nil {
t.Fatalf("ipv4.PacketConn.TTL failed: %v", err)
} else if v != tt.ttl {
t.Fatalf("Got unexpected TTL value %v; expected %v", v, tt.ttl)
}
if err := c.SetMulticastTTL(tt.mcttl); err != nil {
t.Fatalf("ipv4.PacketConn.SetMulticastTTL failed: %v", err)
}
if v, err := c.MulticastTTL(); err != nil {
t.Fatalf("ipv4.PacketConn.MulticastTTL failed: %v", err)
} else if v != tt.mcttl {
t.Fatalf("Got unexpected MulticastTTL value %v; expected %v", v, tt.mcttl)
}
if err := c.SetMulticastLoopback(tt.mcloop); err != nil {
t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err)
}
if v, err := c.MulticastLoopback(); err != nil {
t.Fatalf("ipv4.PacketConn.MulticastLoopback failed: %v", err)
} else if v != tt.mcloop {
t.Fatalf("Got unexpected MulticastLoopback value %v; expected %v", v, tt.mcloop)
}
if err := c.JoinGroup(nil, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.JoinGroup(%v) failed: %v", gaddr, err)
}
if err := c.LeaveGroup(nil, gaddr); err != nil {
t.Fatalf("ipv4.PacketConn.LeaveGroup(%v) failed: %v", gaddr, err)
}
}

97
ipv4/packet.go Normal file
View File

@@ -0,0 +1,97 @@
// 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"
"syscall"
)
// A packetHandler represents the IPv4 datagram handler.
type packetHandler struct {
c *net.IPConn
rawOpt
}
func (c *packetHandler) ok() bool { return c != nil && c.c != nil }
// Read reads an IPv4 datagram from the endpoint c, copying the
// datagram into b. It returns the received datagram as the IPv4
// header h, the payload p and the control message cm.
func (c *packetHandler) Read(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) {
if !c.ok() {
return nil, nil, nil, syscall.EINVAL
}
oob := newControlMessage(&c.rawOpt)
n, oobn, _, src, err := c.c.ReadMsgIP(b, oob)
if err != nil {
return nil, nil, nil, err
}
var hs []byte
if hs, p, err = slicePacket(b[:n]); err != nil {
return nil, nil, nil, err
}
if h, err = ParseHeader(hs); err != nil {
return nil, nil, nil, err
}
if cm, err = parseControlMessage(oob[:oobn]); err != nil {
return nil, nil, nil, err
}
if src != nil && cm != nil {
cm.Src = src.IP
}
return
}
func slicePacket(b []byte) (h, p []byte, err error) {
if len(b) < HeaderLen {
return nil, nil, errHeaderTooShort
}
hdrlen := (int(b[0]) & 0x0f) << 2
return b[:hdrlen], b[hdrlen:], nil
}
// Write 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.
//
// The IPv4 header h must contain appropriate fields that include:
//
// Version = ipv4.Version
// Len = <must be specified>
// TOS = <must be specified>
// TotalLen = <must be specified>
// ID = platform sets an appropriate value if ID is zero
// FragOff = <must be specified>
// TTL = <must be specified>
// Protocol = <must be specified>
// Checksum = platform sets an appropriate value if Checksum is zero
// Src = platform sets an appropriate value if Src is nil
// Dst = <must be specified>
// h.Options = optional
func (c *packetHandler) Write(h *Header, p []byte, cm *ControlMessage) error {
if !c.ok() {
return syscall.EINVAL
}
oob := marshalControlMessage(cm)
wh, err := h.Marshal()
if err != nil {
return err
}
dst := &net.IPAddr{}
if cm != nil {
if ip := cm.Dst.To4(); ip != nil {
dst.IP = ip
}
}
if dst.IP == nil {
dst.IP = h.Dst
}
wh = append(wh, p...)
_, _, err = c.c.WriteMsgIP(wh, oob, dst)
return err
}

81
ipv4/payload.go Normal file
View File

@@ -0,0 +1,81 @@
// 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"
"syscall"
)
// A payloadHandler represents the IPv4 datagram payload handler.
type payloadHandler struct {
c net.PacketConn
rawOpt
}
func (c *payloadHandler) ok() bool { return c != nil && c.c != nil }
// Read 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) Read(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 rd := c.c.(type) {
case *net.UDPConn:
if n, oobn, _, src, err = rd.ReadMsgUDP(b, oob); err != nil {
return 0, nil, nil, err
}
case *net.IPConn:
nb := make([]byte, len(b)+maxHeaderLen)
if n, oobn, _, src, err = rd.ReadMsgIP(nb, oob); err != nil {
return 0, nil, nil, err
}
hdrlen := (int(b[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
}
// Write 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) Write(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 wr := c.c.(type) {
case *net.UDPConn:
n, _, err = wr.WriteMsgUDP(b, oob, dst.(*net.UDPAddr))
case *net.IPConn:
n, _, err = wr.WriteMsgIP(b, oob, dst.(*net.IPAddr))
default:
return 0, errInvalidConnType
}
if err != nil {
return 0, err
}
return
}

123
ipv4/sockopt_bsd.go Normal file
View File

@@ -0,0 +1,123 @@
// 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 netbsd openbsd
package ipv4
import (
"net"
"os"
"syscall"
)
func ipv4MulticastTTL(fd int) (int, error) {
v, err := syscall.GetsockoptByte(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
}
func setIPv4MulticastTTL(fd int, v int) error {
err := syscall.SetsockoptByte(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, byte(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4ReceiveDestinationAddress(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVDSTADDR)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4ReceiveDestinationAddress(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVDSTADDR, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4ReceiveInterface(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4ReceiveInterface(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4MulticastInterface(fd int) (*net.Interface, error) {
a, err := syscall.GetsockoptInet4Addr(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return netIP4ToInterface(net.IPv4(a[0], a[1], a[2], a[3]))
}
func setIPv4MulticastInterface(fd int, ifi *net.Interface) error {
ip, err := netInterfaceToIP4(ifi)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
var a [4]byte
copy(a[:], ip.To4())
err = syscall.SetsockoptInet4Addr(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4MulticastLoopback(fd int) (bool, error) {
v, err := syscall.GetsockoptByte(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4MulticastLoopback(fd int, v bool) error {
err := syscall.SetsockoptByte(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
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
}
err := syscall.SetsockoptIPMreq(fd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
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
}
err := syscall.SetsockoptIPMreq(fd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}

26
ipv4/sockopt_freebsd.go Normal file
View File

@@ -0,0 +1,26 @@
// 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 (
"os"
"syscall"
)
func ipv4SendSourceAddress(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_SENDSRCADDR)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4SendSourceAddress(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_SENDSRCADDR, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}

122
ipv4/sockopt_linux.go Normal file
View File

@@ -0,0 +1,122 @@
// 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, syscall.IPPROTO_IP, syscall.IP_RECVTOS)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4ReceiveTOS(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVTOS, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4MulticastTTL(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
}
func setIPv4MulticastTTL(fd int, v int) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4PacketInfo(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_PKTINFO)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4PacketInfo(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_PKTINFO, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4MulticastInterface(fd int) (*net.Interface, error) {
mreqn, err := syscall.GetsockoptIPMreqn(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
if int(mreqn.Ifindex) == 0 {
return nil, nil
}
return net.InterfaceByIndex(int(mreqn.Ifindex))
}
func setIPv4MulticastInterface(fd int, ifi *net.Interface) error {
mreqn := &syscall.IPMreqn{}
if ifi != nil {
mreqn.Ifindex = int32(ifi.Index)
}
err := syscall.SetsockoptIPMreqn(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreqn)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4MulticastLoopback(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4MulticastLoopback(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
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)
}
err := syscall.SetsockoptIPMreqn(fd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreqn)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
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)
}
err := syscall.SetsockoptIPMreqn(fd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreqn)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}

19
ipv4/sockopt_plan9.go Normal file
View File

@@ -0,0 +1,19 @@
// 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 (
"syscall"
)
func ipv4HeaderPrepend(fd int) (bool, error) {
// TODO(mikio): Implement this
return false, syscall.EPLAN9
}
func setIPv4HeaderPrepend(fd int, v bool) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
}

76
ipv4/sockopt_unix.go Normal file
View File

@@ -0,0 +1,76 @@
// 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
package ipv4
import (
"os"
"syscall"
)
func ipv4TOS(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TOS)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
}
func setIPv4TOS(fd int, v int) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TOS, v)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4TTL(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TTL)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
}
func setIPv4TTL(fd int, v int) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TTL, v)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4ReceiveTTL(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVTTL)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4ReceiveTTL(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_RECVTTL, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4HeaderPrepend(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_HDRINCL)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4HeaderPrepend(fd int, v bool) error {
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_HDRINCL, boolint(v))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}

179
ipv4/sockopt_windows.go Normal file
View File

@@ -0,0 +1,179 @@
// 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"
)
// 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)
err := syscall.Getsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_TOS), (*byte)(unsafe.Pointer(&v)), &l)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
}
func setIPv4TOS(fd syscall.Handle, v int) error {
vv := int32(v)
err := syscall.Setsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_TOS), (*byte)(unsafe.Pointer(&vv)), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4TTL(fd syscall.Handle) (int, error) {
var v int32
l := int32(4)
err := syscall.Getsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_TTL), (*byte)(unsafe.Pointer(&v)), &l)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
}
func setIPv4TTL(fd syscall.Handle, v int) error {
vv := int32(v)
err := syscall.Setsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_TTL), (*byte)(unsafe.Pointer(&vv)), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4MulticastTTL(fd syscall.Handle) (int, error) {
var v int32
l := int32(4)
err := syscall.Getsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_TTL), (*byte)(unsafe.Pointer(&v)), &l)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
}
func setIPv4MulticastTTL(fd syscall.Handle, v int) error {
vv := int32(v)
err := syscall.Setsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_TTL), (*byte)(unsafe.Pointer(&vv)), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4ReceiveTTL(fd syscall.Handle) (bool, error) {
// NOTE: Not supported yet on any Windows
return false, syscall.EWINDOWS
}
func setIPv4ReceiveTTL(fd syscall.Handle, v bool) error {
// NOTE: Not supported yet on any Windows
return syscall.EWINDOWS
}
func ipv4ReceiveDestinationAddress(fd syscall.Handle) (bool, error) {
// TODO(mikio): Implement this for XP and beyond
return false, syscall.EWINDOWS
}
func setIPv4ReceiveDestinationAddress(fd syscall.Handle, v bool) error {
// TODO(mikio): Implement this for XP and beyond
return syscall.EWINDOWS
}
func ipv4HeaderPrepend(fd syscall.Handle) (bool, error) {
// TODO(mikio): Implement this for XP and beyond
return false, syscall.EWINDOWS
}
func setIPv4HeaderPrepend(fd syscall.Handle, v bool) error {
// TODO(mikio): Implement this for XP and beyond
return syscall.EWINDOWS
}
func ipv4ReceiveInterface(fd syscall.Handle) (bool, error) {
// TODO(mikio): Implement this for Vista and beyond
return false, syscall.EWINDOWS
}
func setIPv4ReceiveInterface(fd syscall.Handle, v bool) error {
// TODO(mikio): Implement this for Vista and beyond
return syscall.EWINDOWS
}
func ipv4MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
var a [4]byte
l := int32(4)
err := syscall.Getsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_IF), (*byte)(unsafe.Pointer(&a[0])), &l)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return netIP4ToInterface(net.IPv4(a[0], a[1], a[2], a[3]))
}
func setIPv4MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
ip, err := netInterfaceToIP4(ifi)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
var a [4]byte
copy(a[:], ip.To4())
err = syscall.Setsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_IF), (*byte)(unsafe.Pointer(&a[0])), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
func ipv4MulticastLoopback(fd syscall.Handle) (bool, error) {
var v int32
l := int32(4)
err := syscall.Getsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&v)), &l)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv4MulticastLoopback(fd syscall.Handle, v bool) error {
vv := int32(boolint(v))
err := syscall.Setsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&vv)), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
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 {
return err
}
err := syscall.Setsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_ADD_MEMBERSHIP), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq)))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
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 {
return err
}
err := syscall.Setsockopt(fd, int32(syscall.IPPROTO_IP), int32(syscall.IP_DROP_MEMBERSHIP), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq)))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}

79
ipv4/unicast_test.go Normal file
View File

@@ -0,0 +1,79 @@
// 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
package ipv4_test
import (
"code.google.com/p/go.net/ipv4"
"net"
"os"
"testing"
)
func TestReadWriteUnicastIPPayloadUDP(t *testing.T) {
c, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
dst, err := net.ResolveUDPAddr("udp4", c.LocalAddr().String())
if err != nil {
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
}
p := ipv4.NewPacketConn(c)
runPayloadTransponder(t, p, []byte("HELLO-R-U-THERE"), dst)
}
func TestReadWriteUnicastIPPayloadICMP(t *testing.T) {
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
if err != nil {
t.Fatalf("ResolveIPAddr failed: %v", err)
}
p := ipv4.NewPacketConn(c)
id := os.Getpid() & 0xffff
pld := newICMPEchoRequest(id, 1, 128, []byte("HELLO-R-U-THERE"))
runPayloadTransponder(t, p, pld, dst)
}
func TestReadWriteUnicastIPDatagram(t *testing.T) {
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
if err != nil {
t.Fatalf("ResolveIPAddr failed: %v", err)
}
r, err := ipv4.NewRawConn(c)
if err != nil {
t.Fatalf("ipv4.NewRawConn failed: %v", err)
}
id := os.Getpid() & 0xffff
pld := newICMPEchoRequest(id, 1, 128, []byte("HELLO-R-U-THERE"))
runDatagramTransponder(t, r, pld, nil, dst)
}

146
ipv4/unicastsockopt_test.go Normal file
View File

@@ -0,0 +1,146 @@
// 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_test
import (
"code.google.com/p/go.net/ipv4"
"errors"
"net"
"os"
"runtime"
"testing"
)
type testUnicastConn interface {
TOS() (int, error)
SetTOS(int) error
TTL() (int, error)
SetTTL(int) error
}
type unicastSockoptTest struct {
tos int
ttl int
}
var unicastSockoptTests = []unicastSockoptTest{
{ipv4.DSCP_CS0 | ipv4.ECN_NOTECT, 127},
{ipv4.DSCP_AF11 | ipv4.ECN_NOTECT, 255},
}
func TestTCPUnicastSockopt(t *testing.T) {
for _, tt := range unicastSockoptTests {
listener := make(chan net.Listener)
go tcpListener(t, "127.0.0.1:0", listener)
ln := <-listener
if ln == nil {
return
}
defer ln.Close()
c, err := net.Dial("tcp4", ln.Addr().String())
if err != nil {
t.Errorf("net.Dial failed: %v", err)
return
}
defer c.Close()
cc := ipv4.NewConn(c)
if err := testUnicastSockopt(t, tt, cc); err != nil {
break
}
}
}
func tcpListener(t *testing.T, addr string, listener chan<- net.Listener) {
ln, err := net.Listen("tcp4", addr)
if err != nil {
t.Errorf("net.Listen failed: %v", err)
listener <- nil
return
}
listener <- ln
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
func TestUDPUnicastSockopt(t *testing.T) {
for _, tt := range unicastSockoptTests {
c, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
t.Errorf("net.ListenPacket failed: %v", err)
return
}
defer c.Close()
p := ipv4.NewPacketConn(c)
if err := testUnicastSockopt(t, tt, p); err != nil {
break
}
}
}
func TestIPUnicastSockopt(t *testing.T) {
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
for _, tt := range unicastSockoptTests {
c, err := net.ListenPacket("ip4:icmp", "127.0.0.1")
if err != nil {
t.Errorf("net.ListenPacket failed: %v", err)
return
}
defer c.Close()
r, err := ipv4.NewRawConn(c)
if err != nil {
t.Errorf("ipv4.NewRawConn failed: %v", err)
return
}
if err := testUnicastSockopt(t, tt, r); err != nil {
break
}
}
}
func testUnicastSockopt(t *testing.T, tt unicastSockoptTest, c testUnicastConn) error {
switch runtime.GOOS {
case "windows":
// IP_TOS option is supported on Windows 8 and beyond.
t.Logf("skipping IP_TOS test on %q", runtime.GOOS)
default:
if err := c.SetTOS(tt.tos); err != nil {
t.Errorf("ipv4.Conn.SetTOS failed: %v", err)
return err
}
if v, err := c.TOS(); err != nil {
t.Errorf("ipv4.Conn.TOS failed: %v", err)
return err
} else if v != tt.tos {
t.Errorf("Got unexpected TOS value %v; expected %v", v, tt.tos)
return errors.New("Got unexpected TOS value")
}
}
if err := c.SetTTL(tt.ttl); err != nil {
t.Errorf("ipv4.Conn.SetTTL failed: %v", err)
return err
}
if v, err := c.TTL(); err != nil {
t.Errorf("ipv4.Conn.TTL failed: %v", err)
return err
} else if v != tt.ttl {
t.Errorf("Got unexpected TTL value %v; expected %v", v, tt.ttl)
return errors.New("Got unexpected TTL value")
}
return nil
}