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 Nov 7, 2017
1 parent a337091 commit 513257c
Show file tree
Hide file tree
Showing 16 changed files with 185 additions and 21 deletions.
2 changes: 1 addition & 1 deletion internal/socket/cmsghdr.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 darwin dragonfly freebsd linux netbsd openbsd solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows

package socket

Expand Down
2 changes: 1 addition & 1 deletion internal/socket/cmsghdr_stub.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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows

package socket

Expand Down
20 changes: 20 additions & 0 deletions internal/socket/cmsghdr_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2017 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 socket

// WSACMSGHDR
type cmsghdr struct {
Len uintptr
Level int32
Type int32
}

const sizeofCmsghdr = 0xc

func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uintptr(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
2 changes: 1 addition & 1 deletion internal/socket/iovec_stub.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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows

package socket

Expand Down
21 changes: 21 additions & 0 deletions internal/socket/iovec_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2017 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 socket

import (
"syscall"
"unsafe"
)

type iovec syscall.WSABuf

func (v *iovec) set(b []byte) {
l := len(b)
if l == 0 {
return
}
v.Buf = (*byte)(unsafe.Pointer(&b[0]))
v.Len = uint32(l)
}
2 changes: 1 addition & 1 deletion internal/socket/msghdr_stub.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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows

package socket

Expand Down
51 changes: 51 additions & 0 deletions internal/socket/msghdr_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2017 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 socket

import (
"syscall"
"unsafe"
)

type msghdr syscall.WSAMsg

func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.setIov(vs)
if len(oob) > 0 {
h.Control.Buf = (*byte)(unsafe.Pointer(&oob[0]))
h.Control.Len = uint32(len(oob))
}
if sa != nil {
h.Name = uintptr(unsafe.Pointer(&sa[0]))
h.Namelen = int32(len(sa))
}
}

func (h *msghdr) name() []byte {
if h.Name != 0 && h.Namelen > 0 {
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
}
return nil
}

func (h *msghdr) controllen() int {
return int(h.Control.Len)
}

func (h *msghdr) flags() int {
return int(h.Flags)
}

func (h *msghdr) setIov(vs []iovec) {
l := len(vs)
if l == 0 {
return
}
h.Buffers = (*syscall.WSABuf)(unsafe.Pointer(&vs[0]))
h.Bufferslen = uint32(l)
}
10 changes: 8 additions & 2 deletions internal/socket/sys_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,17 @@ func setsockopt(s uintptr, level, name int, b []byte) error {
}

func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
return 0, errors.New("not implemented")
var bytesReceived uint32
msg := (*syscall.WSAMsg)(h)
msg.Flags = uint32(flags)
err := syscall.WSARecvMsg(syscall.Handle(s), msg, &bytesReceived, nil, 0)
return int(bytesReceived), err
}

func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
return 0, errors.New("not implemented")
var bytesSent uint32
err := syscall.WSASendMsg(syscall.Handle(s), (*syscall.WSAMsg)(h), uint32(flags), &bytesSent, nil, 0)
return int(bytesSent), err
}

func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
Expand Down
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
}
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
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[:])
}

0 comments on commit 513257c

Please sign in to comment.