Skip to content

Commit

Permalink
ipv4: implement control messages on windows
Browse files Browse the repository at this point in the history
Fixes golang/go#7175

Change-Id: I583c5713bedf073cf5abe25976275fc6b9193379
  • Loading branch information
tmm1 committed Mar 23, 2018
1 parent 25bc912 commit c05341d
Show file tree
Hide file tree
Showing 14 changed files with 106 additions and 31 deletions.
50 changes: 46 additions & 4 deletions ipv4/control_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,54 @@
package ipv4

import (
"syscall"

"golang.org/x/net/internal/socket"
)

func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error {
// TODO(mikio): implement this
return syscall.EWINDOWS
opt.Lock()
defer opt.Unlock()
if so, ok := sockOpts[ssoReceiveTTL]; ok && cf&FlagTTL != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagTTL)
} else {
opt.clear(FlagTTL)
}
}
if so, ok := sockOpts[ssoPacketInfo]; ok {
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
} else {
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
}
}
} else {
if so, ok := sockOpts[ssoReceiveDst]; ok && cf&FlagDst != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagDst)
} else {
opt.clear(FlagDst)
}
}
if so, ok := sockOpts[ssoReceiveInterface]; ok && cf&FlagInterface != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagInterface)
} else {
opt.clear(FlagInterface)
}
}
}
return nil
}
11 changes: 9 additions & 2 deletions ipv4/multicast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var packetConnReadWriteMulticastUDPTests = []struct {

func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "solaris", "windows":
case "nacl", "plan9", "solaris":
t.Skipf("not supported on %s", runtime.GOOS)
}
ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
Expand Down Expand Up @@ -117,7 +117,9 @@ var packetConnReadWriteMulticastICMPTests = []struct {

func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "solaris", "windows":
case "windows":
t.Skipf("test hangs on windows when run as Administrator")
case "nacl", "plan9", "solaris":
t.Skipf("not supported on %s", runtime.GOOS)
}
if m, ok := nettest.SupportsRawIPSocket(); !ok {
Expand Down Expand Up @@ -227,6 +229,11 @@ var rawConnReadWriteMulticastICMPTests = []struct {
}

func TestRawConnReadWriteMulticastICMP(t *testing.T) {
switch runtime.GOOS {
case "windows":
t.Skipf("test hangs on windows when run as Administrator")
}

if testing.Short() {
t.Skip("to avoid external network")
}
Expand Down
10 changes: 5 additions & 5 deletions ipv4/multicastlistener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var udpMultipleGroupListenerTests = []net.Addr{

func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {

func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -116,7 +116,7 @@ func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {

func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -172,7 +172,7 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {

func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -217,7 +217,7 @@ func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {

func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down
3 changes: 0 additions & 3 deletions ipv4/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import (
"golang.org/x/net/internal/socket"
)

// BUG(mikio): On Windows, the ReadFrom and WriteTo methods of RawConn
// are not implemented.

// A packetHandler represents the IPv4 datagram handler.
type packetHandler struct {
*net.IPConn
Expand Down
3 changes: 0 additions & 3 deletions ipv4/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import (
"golang.org/x/net/internal/socket"
)

// BUG(mikio): On Windows, the ControlMessage for ReadFrom and WriteTo
// methods of PacketConn is not implemented.

// A payloadHandler represents the IPv4 datagram payload handler.
type payloadHandler struct {
net.PacketConn
Expand Down
2 changes: 1 addition & 1 deletion ipv4/payload_cmsg.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !nacl,!plan9,!windows
// +build !nacl,!plan9

package ipv4

Expand Down
2 changes: 1 addition & 1 deletion ipv4/payload_cmsg_go1_9.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.

// +build go1.9
// +build !nacl,!plan9,!windows
// +build !nacl,!plan9

package ipv4

Expand Down
2 changes: 1 addition & 1 deletion ipv4/payload_nocmsg.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build nacl plan9 windows
// +build nacl plan9

package ipv4

Expand Down
4 changes: 2 additions & 2 deletions ipv4/readwrite_go1_9_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (

func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
b.Skipf("not supported on %s", runtime.GOOS)
}

Expand Down Expand Up @@ -172,7 +172,7 @@ func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {

func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}

Expand Down
2 changes: 1 addition & 1 deletion ipv4/readwrite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func BenchmarkReadWriteUnicast(b *testing.B) {

func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}

Expand Down
2 changes: 1 addition & 1 deletion ipv4/sockopt.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const (
ssoReceiveTTL // header field on received packet
ssoReceiveDst // header field on received packet
ssoReceiveInterface // inbound interface on received packet
ssoPacketInfo // incbound or outbound packet path
ssoPacketInfo // inbound or outbound packet path
ssoHeaderPrepend // ipv4 header prepend
ssoStripHeader // strip ipv4 header
ssoICMPFilter // icmp filter
Expand Down
32 changes: 31 additions & 1 deletion ipv4/sys_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
package ipv4

import (
"net"
"unsafe"

"golang.org/x/net/internal/iana"
"golang.org/x/net/internal/socket"
)
Expand Down Expand Up @@ -48,7 +51,9 @@ type ipMreqSource struct {

// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx
var (
ctlOpts = [ctlMax]ctlOpt{}
ctlOpts = [ctlMax]ctlOpt{
ctlPacketInfo: {sysIP_PKTINFO, sizeofInetPktinfo, marshalPacketInfo, parsePacketInfo},
}

sockOpts = map[int]*sockOpt{
ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}},
Expand All @@ -59,9 +64,34 @@ var (
ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}},
ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_ADD_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq},
ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_DROP_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq},
ssoPacketInfo: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_PKTINFO, Len: 4}},
}
)

func (pi *inetPktinfo) setIfindex(i int) {
pi.Ifindex = int32(i)
}

func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
m := socket.ControlMessage(b)
m.MarshalHeader(iana.ProtocolIP, sysIP_PKTINFO, sizeofInetPktinfo)
if cm != nil {
pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0]))
if ip := cm.Src.To4(); ip != nil {
copy(pi.Addr[:], ip)
}
if cm.IfIndex > 0 {
pi.setIfindex(cm.IfIndex)
}
}
return m.Next(sizeofInetPktinfo)
}

func parsePacketInfo(cm *ControlMessage, b []byte) {
pi := (*inetPktinfo)(unsafe.Pointer(&b[0]))
cm.IfIndex = int(pi.Ifindex)
if len(cm.Dst) < net.IPv4len {
cm.Dst = make(net.IP, net.IPv4len)
}
copy(cm.Dst, pi.Addr[:])
}
8 changes: 5 additions & 3 deletions ipv4/unicast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
Expand Down Expand Up @@ -71,7 +71,7 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {

func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if m, ok := nettest.SupportsRawIPSocket(); !ok {
Expand Down Expand Up @@ -157,7 +157,9 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {

func TestRawConnReadWriteUnicastICMP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "windows":
t.Skipf("test hangs on windows when run as Administrator")
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if m, ok := nettest.SupportsRawIPSocket(); !ok {
Expand Down
6 changes: 3 additions & 3 deletions ipv4/unicastsockopt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

func TestConnUnicastSocketOptions(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
Expand Down Expand Up @@ -62,7 +62,7 @@ var packetConnUnicastSocketOptionTests = []struct {

func TestPacketConnUnicastSocketOptions(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
Expand All @@ -88,7 +88,7 @@ func TestPacketConnUnicastSocketOptions(t *testing.T) {

func TestRawConnUnicastSocketOptions(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if m, ok := nettest.SupportsRawIPSocket(); !ok {
Expand Down

0 comments on commit c05341d

Please sign in to comment.