David K. Bainbridge | 215e024 | 2017-09-05 23:18:24 -0700 | [diff] [blame] | 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 | } |