Skip to content

Commit b7a1f62

Browse files
committed
ipv4: add {Read,Write}Batch methods to {Packet,Raw}Conn
This change provides message IO functionality that may support the construction of modern datagram transport protocols. The modern datagram transport protocols on a multihomed node basically need to control each packet path for traffic engineering by using information belongs to network- or link-layer implementation. In addtion, it's desirable to be able to do simultaneous transmission across multiple network- or link-layer adjacencies wihtout any additional cost. The ReadBatch and WriteBatch methods of PacketConn and RawConn can be used to read and write an IO message that contains the information of network- or link-layer implementation, and read and write a batch of IO messages on Linux. The Marshal and Parse methods of ControlMessage and Header can help to marshal and parse information contained in IO messages. Updates golang/go#3661. Change-Id: Ia84a9d3bc51641406eaaf4258f2a3066945cc323 Reviewed-on: https://go-review.googlesource.com/38275 Run-TryBot: Mikio Hara <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 3470a06 commit b7a1f62

20 files changed

+1411
-352
lines changed

ipv4/batch.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Copyright 2017 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build go1.9
6+
7+
package ipv4
8+
9+
import (
10+
"net"
11+
"runtime"
12+
"syscall"
13+
14+
"golang.org/x/net/internal/socket"
15+
)
16+
17+
// BUG(mikio): On Windows, the ReadBatch and WriteBatch methods of
18+
// PacketConn are not implemented.
19+
20+
// BUG(mikio): On Windows, the ReadBatch and WriteBatch methods of
21+
// RawConn are not implemented.
22+
23+
// A Message represents an IO message.
24+
//
25+
// type Message struct {
26+
// Buffers [][]byte
27+
// OOB []byte
28+
// Addr net.Addr
29+
// N int
30+
// NN int
31+
// Flags int
32+
// }
33+
//
34+
// The Buffers fields represents a list of contiguous buffers, which
35+
// can be used for vectored IO, for example, putting a header and a
36+
// payload in each slice.
37+
// When writing, the Buffers field must contain at least one byte to
38+
// write.
39+
// When reading, the Buffers field will always contain a byte to read.
40+
//
41+
// The OOB field contains protocol-specific control or miscellaneous
42+
// ancillary data known as out-of-band data.
43+
// It can be nil when not required.
44+
//
45+
// The Addr field specifies a destination address when writing.
46+
// It can be nil when the underlying protocol of the endpoint uses
47+
// connection-oriented communication.
48+
// After a successful read, it may contain the source address on the
49+
// received packet.
50+
//
51+
// The N field indicates the number of bytes read or written from/to
52+
// Buffers.
53+
//
54+
// The NN field indicates the number of bytes read or written from/to
55+
// OOB.
56+
//
57+
// The Flags field contains protocol-specific information on the
58+
// received message.
59+
type Message = socket.Message
60+
61+
// ReadBatch reads a batch of messages.
62+
//
63+
// The provided flags is a set of platform-dependent flags, such as
64+
// syscall.MSG_PEEK.
65+
//
66+
// On a successful read it returns the number of messages received, up
67+
// to len(ms).
68+
//
69+
// On Linux, a batch read will be optimized.
70+
// On other platforms, this method will read only a single message.
71+
//
72+
// Unlike the ReadFrom method, it doesn't strip the IPv4 header
73+
// followed by option headers from the received IPv4 datagram when the
74+
// underlying transport is net.IPConn. Each Buffers field of Message
75+
// must be large enough to accommodate an IPv4 header and option
76+
// headers.
77+
func (c *payloadHandler) ReadBatch(ms []Message, flags int) (int, error) {
78+
if !c.ok() {
79+
return 0, syscall.EINVAL
80+
}
81+
switch runtime.GOOS {
82+
case "linux":
83+
n, err := c.RecvMsgs([]socket.Message(ms), flags)
84+
if err != nil {
85+
err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
86+
}
87+
return n, err
88+
default:
89+
n := 1
90+
err := c.RecvMsg(&ms[0], flags)
91+
if err != nil {
92+
n = 0
93+
err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
94+
}
95+
return n, err
96+
}
97+
}
98+
99+
// WriteBatch writes a batch of messages.
100+
//
101+
// The provided flags is a set of platform-dependent flags, such as
102+
// syscall.MSG_DONTROUTE.
103+
//
104+
// It returns the number of messages written on a successful write.
105+
//
106+
// On Linux, a batch write will be optimized.
107+
// On other platforms, this method will write only a single message.
108+
func (c *payloadHandler) WriteBatch(ms []Message, flags int) (int, error) {
109+
if !c.ok() {
110+
return 0, syscall.EINVAL
111+
}
112+
switch runtime.GOOS {
113+
case "linux":
114+
n, err := c.SendMsgs([]socket.Message(ms), flags)
115+
if err != nil {
116+
err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
117+
}
118+
return n, err
119+
default:
120+
n := 1
121+
err := c.SendMsg(&ms[0], flags)
122+
if err != nil {
123+
n = 0
124+
err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
125+
}
126+
return n, err
127+
}
128+
}
129+
130+
// ReadBatch reads a batch of messages.
131+
//
132+
// The provided flags is a set of platform-dependent flags, such as
133+
// syscall.MSG_PEEK.
134+
//
135+
// On a successful read it returns the number of messages received, up
136+
// to len(ms).
137+
//
138+
// On Linux, a batch read will be optimized.
139+
// On other platforms, this method will read only a single message.
140+
func (c *packetHandler) ReadBatch(ms []Message, flags int) (int, error) {
141+
if !c.ok() {
142+
return 0, syscall.EINVAL
143+
}
144+
switch runtime.GOOS {
145+
case "linux":
146+
n, err := c.RecvMsgs([]socket.Message(ms), flags)
147+
if err != nil {
148+
err = &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
149+
}
150+
return n, err
151+
default:
152+
n := 1
153+
err := c.RecvMsg(&ms[0], flags)
154+
if err != nil {
155+
n = 0
156+
err = &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
157+
}
158+
return n, err
159+
}
160+
}
161+
162+
// WriteBatch writes a batch of messages.
163+
//
164+
// The provided flags is a set of platform-dependent flags, such as
165+
// syscall.MSG_DONTROUTE.
166+
//
167+
// It returns the number of messages written on a successful write.
168+
//
169+
// On Linux, a batch write will be optimized.
170+
// On other platforms, this method will write only a single message.
171+
func (c *packetHandler) WriteBatch(ms []Message, flags int) (int, error) {
172+
if !c.ok() {
173+
return 0, syscall.EINVAL
174+
}
175+
switch runtime.GOOS {
176+
case "linux":
177+
n, err := c.SendMsgs([]socket.Message(ms), flags)
178+
if err != nil {
179+
err = &net.OpError{Op: "write", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
180+
}
181+
return n, err
182+
default:
183+
n := 1
184+
err := c.SendMsg(&ms[0], flags)
185+
if err != nil {
186+
n = 0
187+
err = &net.OpError{Op: "write", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
188+
}
189+
return n, err
190+
}
191+
}

ipv4/control.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88
"fmt"
99
"net"
1010
"sync"
11+
12+
"golang.org/x/net/internal/iana"
13+
"golang.org/x/net/internal/socket"
1114
)
1215

1316
type rawOpt struct {
@@ -51,6 +54,77 @@ func (cm *ControlMessage) String() string {
5154
return fmt.Sprintf("ttl=%d src=%v dst=%v ifindex=%d", cm.TTL, cm.Src, cm.Dst, cm.IfIndex)
5255
}
5356

57+
// Marshal returns the binary encoding of cm.
58+
func (cm *ControlMessage) Marshal() []byte {
59+
if cm == nil {
60+
return nil
61+
}
62+
var m socket.ControlMessage
63+
if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To4() != nil || cm.IfIndex > 0) {
64+
m = socket.NewControlMessage([]int{ctlOpts[ctlPacketInfo].length})
65+
}
66+
if len(m) > 0 {
67+
ctlOpts[ctlPacketInfo].marshal(m, cm)
68+
}
69+
return m
70+
}
71+
72+
// Parse parses b as a control message and stores the result in cm.
73+
func (cm *ControlMessage) Parse(b []byte) error {
74+
ms, err := socket.ControlMessage(b).Parse()
75+
if err != nil {
76+
return err
77+
}
78+
for _, m := range ms {
79+
lvl, typ, l, err := m.ParseHeader()
80+
if err != nil {
81+
return err
82+
}
83+
if lvl != iana.ProtocolIP {
84+
continue
85+
}
86+
switch typ {
87+
case ctlOpts[ctlTTL].name:
88+
ctlOpts[ctlTTL].parse(cm, m.Data(l))
89+
case ctlOpts[ctlDst].name:
90+
ctlOpts[ctlDst].parse(cm, m.Data(l))
91+
case ctlOpts[ctlInterface].name:
92+
ctlOpts[ctlInterface].parse(cm, m.Data(l))
93+
case ctlOpts[ctlPacketInfo].name:
94+
ctlOpts[ctlPacketInfo].parse(cm, m.Data(l))
95+
}
96+
}
97+
return nil
98+
}
99+
100+
// NewControlMessage returns a new control message.
101+
//
102+
// The returned message is large enough for options specified by cf.
103+
func NewControlMessage(cf ControlFlags) []byte {
104+
opt := rawOpt{cflags: cf}
105+
var l int
106+
if opt.isset(FlagTTL) && ctlOpts[ctlTTL].name > 0 {
107+
l += socket.ControlMessageSpace(ctlOpts[ctlTTL].length)
108+
}
109+
if ctlOpts[ctlPacketInfo].name > 0 {
110+
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
111+
l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length)
112+
}
113+
} else {
114+
if opt.isset(FlagDst) && ctlOpts[ctlDst].name > 0 {
115+
l += socket.ControlMessageSpace(ctlOpts[ctlDst].length)
116+
}
117+
if opt.isset(FlagInterface) && ctlOpts[ctlInterface].name > 0 {
118+
l += socket.ControlMessageSpace(ctlOpts[ctlInterface].length)
119+
}
120+
}
121+
var b []byte
122+
if l > 0 {
123+
b = make([]byte, l)
124+
}
125+
return b
126+
}
127+
54128
// Ancillary data socket options
55129
const (
56130
ctlTTL = iota // header field

ipv4/control_bsd.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,26 @@ import (
1212
"unsafe"
1313

1414
"golang.org/x/net/internal/iana"
15+
"golang.org/x/net/internal/socket"
1516
)
1617

1718
func marshalDst(b []byte, cm *ControlMessage) []byte {
18-
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
19-
m.Level = iana.ProtocolIP
20-
m.Type = sysIP_RECVDSTADDR
21-
m.SetLen(syscall.CmsgLen(net.IPv4len))
22-
return b[syscall.CmsgSpace(net.IPv4len):]
19+
m := socket.ControlMessage(b)
20+
m.MarshalHeader(iana.ProtocolIP, sysIP_RECVDSTADDR, net.IPv4len)
21+
return m.Next(net.IPv4len)
2322
}
2423

2524
func parseDst(cm *ControlMessage, b []byte) {
26-
cm.Dst = b[:net.IPv4len]
25+
if len(cm.Dst) < net.IPv4len {
26+
cm.Dst = make(net.IP, net.IPv4len)
27+
}
28+
copy(cm.Dst, b[:net.IPv4len])
2729
}
2830

2931
func marshalInterface(b []byte, cm *ControlMessage) []byte {
30-
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
31-
m.Level = iana.ProtocolIP
32-
m.Type = sysIP_RECVIF
33-
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
34-
return b[syscall.CmsgSpace(syscall.SizeofSockaddrDatalink):]
32+
m := socket.ControlMessage(b)
33+
m.MarshalHeader(iana.ProtocolIP, sysIP_RECVIF, syscall.SizeofSockaddrDatalink)
34+
return m.Next(syscall.SizeofSockaddrDatalink)
3535
}
3636

3737
func parseInterface(cm *ControlMessage, b []byte) {

ipv4/control_pktinfo.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,33 @@
77
package ipv4
88

99
import (
10-
"syscall"
10+
"net"
1111
"unsafe"
1212

1313
"golang.org/x/net/internal/iana"
14+
"golang.org/x/net/internal/socket"
1415
)
1516

1617
func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
17-
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
18-
m.Level = iana.ProtocolIP
19-
m.Type = sysIP_PKTINFO
20-
m.SetLen(syscall.CmsgLen(sizeofInetPktinfo))
18+
m := socket.ControlMessage(b)
19+
m.MarshalHeader(iana.ProtocolIP, sysIP_PKTINFO, sizeofInetPktinfo)
2120
if cm != nil {
22-
pi := (*inetPktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
21+
pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0]))
2322
if ip := cm.Src.To4(); ip != nil {
2423
copy(pi.Spec_dst[:], ip)
2524
}
2625
if cm.IfIndex > 0 {
2726
pi.setIfindex(cm.IfIndex)
2827
}
2928
}
30-
return b[syscall.CmsgSpace(sizeofInetPktinfo):]
29+
return m.Next(sizeofInetPktinfo)
3130
}
3231

3332
func parsePacketInfo(cm *ControlMessage, b []byte) {
3433
pi := (*inetPktinfo)(unsafe.Pointer(&b[0]))
3534
cm.IfIndex = int(pi.Ifindex)
36-
cm.Dst = pi.Addr[:]
35+
if len(cm.Dst) < net.IPv4len {
36+
cm.Dst = make(net.IP, net.IPv4len)
37+
}
38+
copy(cm.Dst, pi.Addr[:])
3739
}

ipv4/control_stub.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,3 @@ import "golang.org/x/net/internal/socket"
1111
func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error {
1212
return errOpNoSupport
1313
}
14-
15-
func newControlMessage(opt *rawOpt) []byte {
16-
return nil
17-
}
18-
19-
func parseControlMessage(b []byte) (*ControlMessage, error) {
20-
return nil, errOpNoSupport
21-
}
22-
23-
func marshalControlMessage(cm *ControlMessage) []byte {
24-
return nil
25-
}

0 commit comments

Comments
 (0)