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