github.com/gopacket/gopacket@v1.1.0/layers/dhcpv6.go (about) 1 // Copyright 2018 Google, Inc. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the LICENSE file in the root of the source 5 // tree. 6 7 package layers 8 9 import ( 10 "encoding/binary" 11 "fmt" 12 "net" 13 14 "github.com/gopacket/gopacket" 15 ) 16 17 // DHCPv6MsgType represents a DHCPv6 operation 18 type DHCPv6MsgType byte 19 20 // Constants that represent DHCP operations 21 const ( 22 DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota 23 DHCPv6MsgTypeSolicit 24 DHCPv6MsgTypeAdvertise 25 DHCPv6MsgTypeRequest 26 DHCPv6MsgTypeConfirm 27 DHCPv6MsgTypeRenew 28 DHCPv6MsgTypeRebind 29 DHCPv6MsgTypeReply 30 DHCPv6MsgTypeRelease 31 DHCPv6MsgTypeDecline 32 DHCPv6MsgTypeReconfigure 33 DHCPv6MsgTypeInformationRequest 34 DHCPv6MsgTypeRelayForward 35 DHCPv6MsgTypeRelayReply 36 ) 37 38 // String returns a string version of a DHCPv6MsgType. 39 func (o DHCPv6MsgType) String() string { 40 switch o { 41 case DHCPv6MsgTypeUnspecified: 42 return "Unspecified" 43 case DHCPv6MsgTypeSolicit: 44 return "Solicit" 45 case DHCPv6MsgTypeAdvertise: 46 return "Advertise" 47 case DHCPv6MsgTypeRequest: 48 return "Request" 49 case DHCPv6MsgTypeConfirm: 50 return "Confirm" 51 case DHCPv6MsgTypeRenew: 52 return "Renew" 53 case DHCPv6MsgTypeRebind: 54 return "Rebind" 55 case DHCPv6MsgTypeReply: 56 return "Reply" 57 case DHCPv6MsgTypeRelease: 58 return "Release" 59 case DHCPv6MsgTypeDecline: 60 return "Decline" 61 case DHCPv6MsgTypeReconfigure: 62 return "Reconfigure" 63 case DHCPv6MsgTypeInformationRequest: 64 return "InformationRequest" 65 case DHCPv6MsgTypeRelayForward: 66 return "RelayForward" 67 case DHCPv6MsgTypeRelayReply: 68 return "RelayReply" 69 default: 70 return "Unknown" 71 } 72 } 73 74 // DHCPv6 contains data for a single DHCP packet. 75 type DHCPv6 struct { 76 BaseLayer 77 MsgType DHCPv6MsgType 78 HopCount uint8 79 LinkAddr net.IP 80 PeerAddr net.IP 81 TransactionID []byte 82 Options DHCPv6Options 83 } 84 85 // LayerType returns gopacket.LayerTypeDHCPv6 86 func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 } 87 88 // DecodeFromBytes decodes the given bytes into this layer. 89 func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { 90 if len(data) < 4 { 91 df.SetTruncated() 92 return fmt.Errorf("DHCPv6 length %d too short", len(data)) 93 } 94 d.BaseLayer = BaseLayer{Contents: data} 95 d.Options = d.Options[:0] 96 d.MsgType = DHCPv6MsgType(data[0]) 97 98 offset := 0 99 if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply { 100 if len(data) < 34 { 101 df.SetTruncated() 102 return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType) 103 } 104 d.HopCount = data[1] 105 d.LinkAddr = net.IP(data[2:18]) 106 d.PeerAddr = net.IP(data[18:34]) 107 offset = 34 108 } else { 109 d.TransactionID = data[1:4] 110 offset = 4 111 } 112 113 stop := len(data) 114 for offset < stop { 115 o := DHCPv6Option{} 116 if err := o.decode(data[offset:]); err != nil { 117 return err 118 } 119 d.Options = append(d.Options, o) 120 offset += int(o.Length) + 4 // 2 from option code, 2 from option length 121 } 122 123 return nil 124 } 125 126 // Len returns the length of a DHCPv6 packet. 127 func (d *DHCPv6) Len() int { 128 n := 1 129 if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply { 130 n += 33 131 } else { 132 n += 3 133 } 134 135 for _, o := range d.Options { 136 n += int(o.Length) + 4 // 2 from option code, 2 from option length 137 } 138 139 return n 140 } 141 142 // SerializeTo writes the serialized form of this layer into the 143 // SerializationBuffer, implementing gopacket.SerializableLayer. 144 // See the docs for gopacket.SerializableLayer for more info. 145 func (d *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { 146 plen := int(d.Len()) 147 148 data, err := b.PrependBytes(plen) 149 if err != nil { 150 return err 151 } 152 153 offset := 0 154 data[0] = byte(d.MsgType) 155 if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply { 156 data[1] = byte(d.HopCount) 157 copy(data[2:18], d.LinkAddr.To16()) 158 copy(data[18:34], d.PeerAddr.To16()) 159 offset = 34 160 } else { 161 copy(data[1:4], d.TransactionID) 162 offset = 4 163 } 164 165 if len(d.Options) > 0 { 166 for _, o := range d.Options { 167 if err := o.encode(data[offset:], opts); err != nil { 168 return err 169 } 170 offset += int(o.Length) + 4 // 2 from option code, 2 from option length 171 } 172 } 173 return nil 174 } 175 176 // CanDecode returns the set of layer types that this DecodingLayer can decode. 177 func (d *DHCPv6) CanDecode() gopacket.LayerClass { 178 return LayerTypeDHCPv6 179 } 180 181 // NextLayerType returns the layer type contained by this DecodingLayer. 182 func (d *DHCPv6) NextLayerType() gopacket.LayerType { 183 return gopacket.LayerTypePayload 184 } 185 186 func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error { 187 dhcp := &DHCPv6{} 188 err := dhcp.DecodeFromBytes(data, p) 189 if err != nil { 190 return err 191 } 192 p.AddLayer(dhcp) 193 return p.NextDecoder(gopacket.LayerTypePayload) 194 } 195 196 // DHCPv6StatusCode represents a DHCP status code - RFC-3315 197 type DHCPv6StatusCode uint16 198 199 // Constants for the DHCPv6StatusCode. 200 const ( 201 DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota 202 DHCPv6StatusCodeUnspecFail 203 DHCPv6StatusCodeNoAddrsAvail 204 DHCPv6StatusCodeNoBinding 205 DHCPv6StatusCodeNotOnLink 206 DHCPv6StatusCodeUseMulticast 207 ) 208 209 // String returns a string version of a DHCPv6StatusCode. 210 func (o DHCPv6StatusCode) String() string { 211 switch o { 212 case DHCPv6StatusCodeSuccess: 213 return "Success" 214 case DHCPv6StatusCodeUnspecFail: 215 return "UnspecifiedFailure" 216 case DHCPv6StatusCodeNoAddrsAvail: 217 return "NoAddressAvailable" 218 case DHCPv6StatusCodeNoBinding: 219 return "NoBinding" 220 case DHCPv6StatusCodeNotOnLink: 221 return "NotOnLink" 222 case DHCPv6StatusCodeUseMulticast: 223 return "UseMulticast" 224 default: 225 return "Unknown" 226 } 227 } 228 229 // DHCPv6DUIDType represents a DHCP DUID - RFC-3315 230 type DHCPv6DUIDType uint16 231 232 // Constants for the DHCPv6DUIDType. 233 const ( 234 DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1 235 DHCPv6DUIDTypeEN 236 DHCPv6DUIDTypeLL 237 ) 238 239 // String returns a string version of a DHCPv6DUIDType. 240 func (o DHCPv6DUIDType) String() string { 241 switch o { 242 case DHCPv6DUIDTypeLLT: 243 return "LLT" 244 case DHCPv6DUIDTypeEN: 245 return "EN" 246 case DHCPv6DUIDTypeLL: 247 return "LL" 248 default: 249 return "Unknown" 250 } 251 } 252 253 // DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19) 254 type DHCPv6DUID struct { 255 Type DHCPv6DUIDType 256 // LLT, LL 257 HardwareType []byte 258 // EN 259 EnterpriseNumber []byte 260 // LLT 261 Time []byte 262 // LLT, LL 263 LinkLayerAddress net.HardwareAddr 264 // EN 265 Identifier []byte 266 } 267 268 // DecodeFromBytes decodes the given bytes into a DHCPv6DUID 269 func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error { 270 if len(data) < 2 { 271 return fmt.Errorf("Not enough bytes to decode: %d", len(data)) 272 } 273 274 d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2])) 275 if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL { 276 if len(data) < 4 { 277 return fmt.Errorf("Not enough bytes to decode: %d", len(data)) 278 } 279 d.HardwareType = data[2:4] 280 } 281 282 if d.Type == DHCPv6DUIDTypeLLT { 283 if len(data) < 8 { 284 return fmt.Errorf("Not enough bytes to decode: %d", len(data)) 285 } 286 d.Time = data[4:8] 287 d.LinkLayerAddress = net.HardwareAddr(data[8:]) 288 } else if d.Type == DHCPv6DUIDTypeEN { 289 if len(data) < 6 { 290 return fmt.Errorf("Not enough bytes to decode: %d", len(data)) 291 } 292 d.EnterpriseNumber = data[2:6] 293 d.Identifier = data[6:] 294 } else { // DHCPv6DUIDTypeLL 295 if len(data) < 4 { 296 return fmt.Errorf("Not enough bytes to decode: %d", len(data)) 297 } 298 d.LinkLayerAddress = net.HardwareAddr(data[4:]) 299 } 300 301 return nil 302 } 303 304 // Encode encodes the DHCPv6DUID in a slice of bytes 305 func (d *DHCPv6DUID) Encode() []byte { 306 length := d.Len() 307 data := make([]byte, length) 308 binary.BigEndian.PutUint16(data[0:2], uint16(d.Type)) 309 310 if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL { 311 copy(data[2:4], d.HardwareType) 312 } 313 314 if d.Type == DHCPv6DUIDTypeLLT { 315 copy(data[4:8], d.Time) 316 copy(data[8:], d.LinkLayerAddress) 317 } else if d.Type == DHCPv6DUIDTypeEN { 318 copy(data[2:6], d.EnterpriseNumber) 319 copy(data[6:], d.Identifier) 320 } else { 321 copy(data[4:], d.LinkLayerAddress) 322 } 323 324 return data 325 } 326 327 // Len returns the length of the DHCPv6DUID, respecting the type 328 func (d *DHCPv6DUID) Len() int { 329 length := 2 // d.Type 330 if d.Type == DHCPv6DUIDTypeLLT { 331 length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress) 332 } else if d.Type == DHCPv6DUIDTypeEN { 333 length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier) 334 } else { // LL 335 length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress) 336 } 337 338 return length 339 } 340 341 func (d *DHCPv6DUID) String() string { 342 duid := "Type: " + d.Type.String() + ", " 343 if d.Type == DHCPv6DUIDTypeLLT { 344 duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress) 345 } else if d.Type == DHCPv6DUIDTypeEN { 346 duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier) 347 } else { // DHCPv6DUIDTypeLL 348 duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress) 349 } 350 return duid 351 } 352 353 func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) { 354 duid := &DHCPv6DUID{} 355 err := duid.DecodeFromBytes(data) 356 if err != nil { 357 return nil, err 358 } 359 return duid, nil 360 }