Skip to content

Commit 48359f4

Browse files
committed
ipv6: add {Read,Write}Batch methods to PacketConn
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 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 can help to marshal and parse information contained in IO messages. Updates golang/go#3661. Change-Id: I94484e2e135f4969ddf5c2548bf6be0cc16888bf Reviewed-on: https://go-review.googlesource.com/38276 Run-TryBot: Mikio Hara <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent b7a1f62 commit 48359f4

14 files changed

+1001
-221
lines changed

ipv6/batch.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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 ipv6
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+
// A Message represents an IO message.
21+
//
22+
// type Message struct {
23+
// Buffers [][]byte
24+
// OOB []byte
25+
// Addr net.Addr
26+
// N int
27+
// NN int
28+
// Flags int
29+
// }
30+
//
31+
// The Buffers fields represents a list of contiguous buffers, which
32+
// can be used for vectored IO, for example, putting a header and a
33+
// payload in each slice.
34+
// When writing, the Buffers field must contain at least one byte to
35+
// write.
36+
// When reading, the Buffers field will always contain a byte to read.
37+
//
38+
// The OOB field contains protocol-specific control or miscellaneous
39+
// ancillary data known as out-of-band data.
40+
// It can be nil when not required.
41+
//
42+
// The Addr field specifies a destination address when writing.
43+
// It can be nil when the underlying protocol of the endpoint uses
44+
// connection-oriented communication.
45+
// After a successful read, it may contain the source address on the
46+
// received packet.
47+
//
48+
// The N field indicates the number of bytes read or written from/to
49+
// Buffers.
50+
//
51+
// The NN field indicates the number of bytes read or written from/to
52+
// OOB.
53+
//
54+
// The Flags field contains protocol-specific information on the
55+
// received message.
56+
type Message = socket.Message
57+
58+
// ReadBatch reads a batch of messages.
59+
//
60+
// The provided flags is a set of platform-dependent flags, such as
61+
// syscall.MSG_PEEK.
62+
//
63+
// On a successful read it returns the number of messages received, up
64+
// to len(ms).
65+
//
66+
// On Linux, a batch read will be optimized.
67+
// On other platforms, this method will read only a single message.
68+
func (c *payloadHandler) ReadBatch(ms []Message, flags int) (int, error) {
69+
if !c.ok() {
70+
return 0, syscall.EINVAL
71+
}
72+
switch runtime.GOOS {
73+
case "linux":
74+
n, err := c.RecvMsgs([]socket.Message(ms), flags)
75+
if err != nil {
76+
err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
77+
}
78+
return n, err
79+
default:
80+
n := 1
81+
err := c.RecvMsg(&ms[0], flags)
82+
if err != nil {
83+
n = 0
84+
err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
85+
}
86+
return n, err
87+
}
88+
}
89+
90+
// WriteBatch writes a batch of messages.
91+
//
92+
// The provided flags is a set of platform-dependent flags, such as
93+
// syscall.MSG_DONTROUTE.
94+
//
95+
// It returns the number of messages written on a successful write.
96+
//
97+
// On Linux, a batch write will be optimized.
98+
// On other platforms, this method will write only a single message.
99+
func (c *payloadHandler) WriteBatch(ms []Message, flags int) (int, error) {
100+
if !c.ok() {
101+
return 0, syscall.EINVAL
102+
}
103+
switch runtime.GOOS {
104+
case "linux":
105+
n, err := c.SendMsgs([]socket.Message(ms), flags)
106+
if err != nil {
107+
err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
108+
}
109+
return n, err
110+
default:
111+
n := 1
112+
err := c.SendMsg(&ms[0], flags)
113+
if err != nil {
114+
n = 0
115+
err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
116+
}
117+
return n, err
118+
}
119+
}

ipv6/control.go

Lines changed: 102 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
// Note that RFC 3542 obsoletes RFC 2292 but OS X Snow Leopard and the
@@ -66,6 +69,105 @@ func (cm *ControlMessage) String() string {
6669
return fmt.Sprintf("tclass=%#x hoplim=%d src=%v dst=%v ifindex=%d nexthop=%v mtu=%d", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU)
6770
}
6871

72+
// Marshal returns the binary encoding of cm.
73+
func (cm *ControlMessage) Marshal() []byte {
74+
if cm == nil {
75+
return nil
76+
}
77+
var l int
78+
tclass := false
79+
if ctlOpts[ctlTrafficClass].name > 0 && cm.TrafficClass > 0 {
80+
tclass = true
81+
l += socket.ControlMessageSpace(ctlOpts[ctlTrafficClass].length)
82+
}
83+
hoplimit := false
84+
if ctlOpts[ctlHopLimit].name > 0 && cm.HopLimit > 0 {
85+
hoplimit = true
86+
l += socket.ControlMessageSpace(ctlOpts[ctlHopLimit].length)
87+
}
88+
pktinfo := false
89+
if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To16() != nil && cm.Src.To4() == nil || cm.IfIndex > 0) {
90+
pktinfo = true
91+
l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length)
92+
}
93+
nexthop := false
94+
if ctlOpts[ctlNextHop].name > 0 && cm.NextHop.To16() != nil && cm.NextHop.To4() == nil {
95+
nexthop = true
96+
l += socket.ControlMessageSpace(ctlOpts[ctlNextHop].length)
97+
}
98+
var b []byte
99+
if l > 0 {
100+
b = make([]byte, l)
101+
bb := b
102+
if tclass {
103+
bb = ctlOpts[ctlTrafficClass].marshal(bb, cm)
104+
}
105+
if hoplimit {
106+
bb = ctlOpts[ctlHopLimit].marshal(bb, cm)
107+
}
108+
if pktinfo {
109+
bb = ctlOpts[ctlPacketInfo].marshal(bb, cm)
110+
}
111+
if nexthop {
112+
bb = ctlOpts[ctlNextHop].marshal(bb, cm)
113+
}
114+
}
115+
return b
116+
}
117+
118+
// Parse parses b as a control message and stores the result in cm.
119+
func (cm *ControlMessage) Parse(b []byte) error {
120+
ms, err := socket.ControlMessage(b).Parse()
121+
if err != nil {
122+
return err
123+
}
124+
for _, m := range ms {
125+
lvl, typ, l, err := m.ParseHeader()
126+
if err != nil {
127+
return err
128+
}
129+
if lvl != iana.ProtocolIPv6 {
130+
continue
131+
}
132+
switch typ {
133+
case ctlOpts[ctlTrafficClass].name:
134+
ctlOpts[ctlTrafficClass].parse(cm, m.Data(l))
135+
case ctlOpts[ctlHopLimit].name:
136+
ctlOpts[ctlHopLimit].parse(cm, m.Data(l))
137+
case ctlOpts[ctlPacketInfo].name:
138+
ctlOpts[ctlPacketInfo].parse(cm, m.Data(l))
139+
case ctlOpts[ctlPathMTU].name:
140+
ctlOpts[ctlPathMTU].parse(cm, m.Data(l))
141+
}
142+
}
143+
return nil
144+
}
145+
146+
// NewControlMessage returns a new control message.
147+
//
148+
// The returned message is large enough for options specified by cf.
149+
func NewControlMessage(cf ControlFlags) []byte {
150+
opt := rawOpt{cflags: cf}
151+
var l int
152+
if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
153+
l += socket.ControlMessageSpace(ctlOpts[ctlTrafficClass].length)
154+
}
155+
if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
156+
l += socket.ControlMessageSpace(ctlOpts[ctlHopLimit].length)
157+
}
158+
if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
159+
l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length)
160+
}
161+
if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
162+
l += socket.ControlMessageSpace(ctlOpts[ctlPathMTU].length)
163+
}
164+
var b []byte
165+
if l > 0 {
166+
b = make([]byte, l)
167+
}
168+
return b
169+
}
170+
69171
// Ancillary data socket options
70172
const (
71173
ctlTrafficClass = iota // header field

ipv6/control_rfc2292_unix.go

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,42 @@
77
package ipv6
88

99
import (
10-
"syscall"
1110
"unsafe"
1211

1312
"golang.org/x/net/internal/iana"
13+
"golang.org/x/net/internal/socket"
1414
)
1515

1616
func marshal2292HopLimit(b []byte, cm *ControlMessage) []byte {
17-
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
18-
m.Level = iana.ProtocolIPv6
19-
m.Type = sysIPV6_2292HOPLIMIT
20-
m.SetLen(syscall.CmsgLen(4))
17+
m := socket.ControlMessage(b)
18+
m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_2292HOPLIMIT, 4)
2119
if cm != nil {
22-
data := b[syscall.CmsgLen(0):]
23-
nativeEndian.PutUint32(data[:4], uint32(cm.HopLimit))
20+
nativeEndian.PutUint32(m.Data(4), uint32(cm.HopLimit))
2421
}
25-
return b[syscall.CmsgSpace(4):]
22+
return m.Next(4)
2623
}
2724

2825
func marshal2292PacketInfo(b []byte, cm *ControlMessage) []byte {
29-
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
30-
m.Level = iana.ProtocolIPv6
31-
m.Type = sysIPV6_2292PKTINFO
32-
m.SetLen(syscall.CmsgLen(sizeofInet6Pktinfo))
26+
m := socket.ControlMessage(b)
27+
m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_2292PKTINFO, sizeofInet6Pktinfo)
3328
if cm != nil {
34-
pi := (*inet6Pktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
29+
pi := (*inet6Pktinfo)(unsafe.Pointer(&m.Data(sizeofInet6Pktinfo)[0]))
3530
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
3631
copy(pi.Addr[:], ip)
3732
}
3833
if cm.IfIndex > 0 {
3934
pi.setIfindex(cm.IfIndex)
4035
}
4136
}
42-
return b[syscall.CmsgSpace(sizeofInet6Pktinfo):]
37+
return m.Next(sizeofInet6Pktinfo)
4338
}
4439

4540
func marshal2292NextHop(b []byte, cm *ControlMessage) []byte {
46-
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
47-
m.Level = iana.ProtocolIPv6
48-
m.Type = sysIPV6_2292NEXTHOP
49-
m.SetLen(syscall.CmsgLen(sizeofSockaddrInet6))
41+
m := socket.ControlMessage(b)
42+
m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_2292NEXTHOP, sizeofSockaddrInet6)
5043
if cm != nil {
51-
sa := (*sockaddrInet6)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
44+
sa := (*sockaddrInet6)(unsafe.Pointer(&m.Data(sizeofSockaddrInet6)[0]))
5245
sa.setSockaddr(cm.NextHop, cm.IfIndex)
5346
}
54-
return b[syscall.CmsgSpace(sizeofSockaddrInet6):]
47+
return m.Next(sizeofSockaddrInet6)
5548
}

0 commit comments

Comments
 (0)