golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/ipv6/control.go (about) 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 }