OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / ipv6 / control.go
1 // Copyright 2013 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 package ipv6
6
7 import (
8         "fmt"
9         "net"
10         "sync"
11
12         "golang.org/x/net/internal/iana"
13         "golang.org/x/net/internal/socket"
14 )
15
16 // Note that RFC 3542 obsoletes RFC 2292 but OS X Snow Leopard and the
17 // former still support RFC 2292 only. Please be aware that almost
18 // all protocol implementations prohibit using a combination of RFC
19 // 2292 and RFC 3542 for some practical reasons.
20
21 type rawOpt struct {
22         sync.RWMutex
23         cflags ControlFlags
24 }
25
26 func (c *rawOpt) set(f ControlFlags)        { c.cflags |= f }
27 func (c *rawOpt) clear(f ControlFlags)      { c.cflags &^= f }
28 func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
29
30 // A ControlFlags represents per packet basis IP-level socket option
31 // control flags.
32 type ControlFlags uint
33
34 const (
35         FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet
36         FlagHopLimit                              // pass the hop limit on the received packet
37         FlagSrc                                   // pass the source address on the received packet
38         FlagDst                                   // pass the destination address on the received packet
39         FlagInterface                             // pass the interface index on the received packet
40         FlagPathMTU                               // pass the path MTU on the received packet path
41 )
42
43 const flagPacketInfo = FlagDst | FlagInterface
44
45 // A ControlMessage represents per packet basis IP-level socket
46 // options.
47 type ControlMessage struct {
48         // Receiving socket options: SetControlMessage allows to
49         // receive the options from the protocol stack using ReadFrom
50         // method of PacketConn.
51         //
52         // Specifying socket options: ControlMessage for WriteTo
53         // method of PacketConn allows to send the options to the
54         // protocol stack.
55         //
56         TrafficClass int    // traffic class, must be 1 <= value <= 255 when specifying
57         HopLimit     int    // hop limit, must be 1 <= value <= 255 when specifying
58         Src          net.IP // source address, specifying only
59         Dst          net.IP // destination address, receiving only
60         IfIndex      int    // interface index, must be 1 <= value when specifying
61         NextHop      net.IP // next hop address, specifying only
62         MTU          int    // path MTU, receiving only
63 }
64
65 func (cm *ControlMessage) String() string {
66         if cm == nil {
67                 return "<nil>"
68         }
69         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)
70 }
71
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 {
133                 case typ == ctlOpts[ctlTrafficClass].name && l >= ctlOpts[ctlTrafficClass].length:
134                         ctlOpts[ctlTrafficClass].parse(cm, m.Data(l))
135                 case typ == ctlOpts[ctlHopLimit].name && l >= ctlOpts[ctlHopLimit].length:
136                         ctlOpts[ctlHopLimit].parse(cm, m.Data(l))
137                 case typ == ctlOpts[ctlPacketInfo].name && l >= ctlOpts[ctlPacketInfo].length:
138                         ctlOpts[ctlPacketInfo].parse(cm, m.Data(l))
139                 case typ == ctlOpts[ctlPathMTU].name && l >= ctlOpts[ctlPathMTU].length:
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
171 // Ancillary data socket options
172 const (
173         ctlTrafficClass = iota // header field
174         ctlHopLimit            // header field
175         ctlPacketInfo          // inbound or outbound packet path
176         ctlNextHop             // nexthop
177         ctlPathMTU             // path mtu
178         ctlMax
179 )
180
181 // A ctlOpt represents a binding for ancillary data socket option.
182 type ctlOpt struct {
183         name    int // option name, must be equal or greater than 1
184         length  int // option length
185         marshal func([]byte, *ControlMessage) []byte
186         parse   func(*ControlMessage, []byte)
187 }