mirror of
https://github.com/golang/net.git
synced 2026-03-31 10:27:08 +09:00
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:
47
ipv4/control.go
Normal file
47
ipv4/control.go
Normal 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
112
ipv4/control_bsd.go
Normal 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
116
ipv4/control_linux.go
Normal 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
29
ipv4/control_plan9.go
Normal 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
29
ipv4/control_windows.go
Normal 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
50
ipv4/dgramopt_plan9.go
Normal 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
125
ipv4/dgramopt_posix.go
Normal 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
213
ipv4/doc.go
Normal 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
181
ipv4/endpoint.go
Normal 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
252
ipv4/example_test.go
Normal 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
29
ipv4/genericopt_plan9.go
Normal 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
61
ipv4/genericopt_posix.go
Normal 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
194
ipv4/header.go
Normal 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
99
ipv4/header_test.go
Normal 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
85
ipv4/helper.go
Normal 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
29
ipv4/helper_plan9.go
Normal 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
42
ipv4/helper_posix.go
Normal 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
50
ipv4/helper_unix.go
Normal 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
49
ipv4/helper_windows.go
Normal 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
47
ipv4/mockicmp_test.go
Normal 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
|
||||
}
|
||||
133
ipv4/mocktransponder_test.go
Normal file
133
ipv4/mocktransponder_test.go
Normal 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
128
ipv4/multicast_test.go
Normal 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)
|
||||
}
|
||||
244
ipv4/multicastlistener_test.go
Normal file
244
ipv4/multicastlistener_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
129
ipv4/multicastsockopt_test.go
Normal file
129
ipv4/multicastsockopt_test.go
Normal 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
97
ipv4/packet.go
Normal 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
81
ipv4/payload.go
Normal 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
123
ipv4/sockopt_bsd.go
Normal 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
26
ipv4/sockopt_freebsd.go
Normal 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
122
ipv4/sockopt_linux.go
Normal 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
19
ipv4/sockopt_plan9.go
Normal 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
76
ipv4/sockopt_unix.go
Normal 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
179
ipv4/sockopt_windows.go
Normal 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
79
ipv4/unicast_test.go
Normal 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
146
ipv4/unicastsockopt_test.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user