github.com/gopacket/gopacket@v1.1.0/layers/dhcpv4.go (about) 1 // Copyright 2016 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 "bytes" 11 "encoding/binary" 12 "fmt" 13 "net" 14 15 "github.com/gopacket/gopacket" 16 ) 17 18 // DHCPOp rerprents a bootp operation 19 type DHCPOp byte 20 21 // bootp operations 22 const ( 23 DHCPOpRequest DHCPOp = 1 24 DHCPOpReply DHCPOp = 2 25 ) 26 27 // String returns a string version of a DHCPOp. 28 func (o DHCPOp) String() string { 29 switch o { 30 case DHCPOpRequest: 31 return "Request" 32 case DHCPOpReply: 33 return "Reply" 34 default: 35 return "Unknown" 36 } 37 } 38 39 // DHCPMsgType represents a DHCP operation 40 type DHCPMsgType byte 41 42 // Constants that represent DHCP operations 43 const ( 44 DHCPMsgTypeUnspecified DHCPMsgType = iota 45 DHCPMsgTypeDiscover 46 DHCPMsgTypeOffer 47 DHCPMsgTypeRequest 48 DHCPMsgTypeDecline 49 DHCPMsgTypeAck 50 DHCPMsgTypeNak 51 DHCPMsgTypeRelease 52 DHCPMsgTypeInform 53 ) 54 55 // String returns a string version of a DHCPMsgType. 56 func (o DHCPMsgType) String() string { 57 switch o { 58 case DHCPMsgTypeUnspecified: 59 return "Unspecified" 60 case DHCPMsgTypeDiscover: 61 return "Discover" 62 case DHCPMsgTypeOffer: 63 return "Offer" 64 case DHCPMsgTypeRequest: 65 return "Request" 66 case DHCPMsgTypeDecline: 67 return "Decline" 68 case DHCPMsgTypeAck: 69 return "Ack" 70 case DHCPMsgTypeNak: 71 return "Nak" 72 case DHCPMsgTypeRelease: 73 return "Release" 74 case DHCPMsgTypeInform: 75 return "Inform" 76 default: 77 return "Unknown" 78 } 79 } 80 81 // DHCPMagic is the RFC 2131 "magic cooke" for DHCP. 82 var DHCPMagic uint32 = 0x63825363 83 84 // DHCPv4 contains data for a single DHCP packet. 85 type DHCPv4 struct { 86 BaseLayer 87 Operation DHCPOp 88 HardwareType LinkType 89 HardwareLen uint8 90 HardwareOpts uint8 91 Xid uint32 92 Secs uint16 93 Flags uint16 94 ClientIP net.IP 95 YourClientIP net.IP 96 NextServerIP net.IP 97 RelayAgentIP net.IP 98 ClientHWAddr net.HardwareAddr 99 ServerName []byte 100 File []byte 101 Options DHCPOptions 102 } 103 104 // DHCPOptions is used to get nicely printed option lists which would normally 105 // be cut off after 5 options. 106 type DHCPOptions []DHCPOption 107 108 // String returns a string version of the options list. 109 func (o DHCPOptions) String() string { 110 buf := &bytes.Buffer{} 111 buf.WriteByte('[') 112 for i, opt := range o { 113 buf.WriteString(opt.String()) 114 if i+1 != len(o) { 115 buf.WriteString(", ") 116 } 117 } 118 buf.WriteByte(']') 119 return buf.String() 120 } 121 122 // LayerType returns gopacket.LayerTypeDHCPv4 123 func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 } 124 125 // DecodeFromBytes decodes the given bytes into this layer. 126 func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { 127 if len(data) < 240 { 128 df.SetTruncated() 129 return fmt.Errorf("DHCPv4 length %d too short", len(data)) 130 } 131 d.Options = d.Options[:0] 132 d.Operation = DHCPOp(data[0]) 133 d.HardwareType = LinkType(data[1]) 134 d.HardwareLen = data[2] 135 d.HardwareOpts = data[3] 136 d.Xid = binary.BigEndian.Uint32(data[4:8]) 137 d.Secs = binary.BigEndian.Uint16(data[8:10]) 138 d.Flags = binary.BigEndian.Uint16(data[10:12]) 139 d.ClientIP = net.IP(data[12:16]) 140 d.YourClientIP = net.IP(data[16:20]) 141 d.NextServerIP = net.IP(data[20:24]) 142 d.RelayAgentIP = net.IP(data[24:28]) 143 d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen]) 144 d.ServerName = data[44:108] 145 d.File = data[108:236] 146 if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic { 147 return InvalidMagicCookie 148 } 149 150 if len(data) <= 240 { 151 // DHCP Packet could have no option (??) 152 return nil 153 } 154 155 options := data[240:] 156 157 stop := len(options) 158 start := 0 159 for start < stop { 160 o := DHCPOption{} 161 if err := o.decode(options[start:]); err != nil { 162 return err 163 } 164 if o.Type == DHCPOptEnd { 165 break 166 } 167 d.Options = append(d.Options, o) 168 // Check if the option is a single byte pad 169 if o.Type == DHCPOptPad { 170 start++ 171 } else { 172 start += int(o.Length) + 2 173 } 174 } 175 176 d.Contents = data 177 178 return nil 179 } 180 181 // Len returns the length of a DHCPv4 packet. 182 func (d *DHCPv4) Len() uint16 { 183 n := uint16(240) 184 for _, o := range d.Options { 185 if o.Type == DHCPOptPad { 186 n++ 187 } else { 188 n += uint16(o.Length) + 2 189 } 190 } 191 n++ // for opt end 192 return n 193 } 194 195 // SerializeTo writes the serialized form of this layer into the 196 // SerializationBuffer, implementing gopacket.SerializableLayer. 197 // See the docs for gopacket.SerializableLayer for more info. 198 func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { 199 plen := int(d.Len()) 200 201 data, err := b.PrependBytes(plen) 202 if err != nil { 203 return err 204 } 205 206 data[0] = byte(d.Operation) 207 data[1] = byte(d.HardwareType) 208 if opts.FixLengths { 209 d.HardwareLen = uint8(len(d.ClientHWAddr)) 210 } 211 data[2] = d.HardwareLen 212 data[3] = d.HardwareOpts 213 binary.BigEndian.PutUint32(data[4:8], d.Xid) 214 binary.BigEndian.PutUint16(data[8:10], d.Secs) 215 binary.BigEndian.PutUint16(data[10:12], d.Flags) 216 copy(data[12:16], d.ClientIP.To4()) 217 copy(data[16:20], d.YourClientIP.To4()) 218 copy(data[20:24], d.NextServerIP.To4()) 219 copy(data[24:28], d.RelayAgentIP.To4()) 220 copy(data[28:44], d.ClientHWAddr) 221 copy(data[44:108], d.ServerName) 222 copy(data[108:236], d.File) 223 binary.BigEndian.PutUint32(data[236:240], DHCPMagic) 224 225 if len(d.Options) > 0 { 226 offset := 240 227 for _, o := range d.Options { 228 if err := o.encode(data[offset:]); err != nil { 229 return err 230 } 231 // A pad option is only a single byte 232 if o.Type == DHCPOptPad { 233 offset++ 234 } else { 235 offset += 2 + len(o.Data) 236 } 237 } 238 optend := NewDHCPOption(DHCPOptEnd, nil) 239 if err := optend.encode(data[offset:]); err != nil { 240 return err 241 } 242 } 243 return nil 244 } 245 246 // CanDecode returns the set of layer types that this DecodingLayer can decode. 247 func (d *DHCPv4) CanDecode() gopacket.LayerClass { 248 return LayerTypeDHCPv4 249 } 250 251 // NextLayerType returns the layer type contained by this DecodingLayer. 252 func (d *DHCPv4) NextLayerType() gopacket.LayerType { 253 return gopacket.LayerTypePayload 254 } 255 256 func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error { 257 dhcp := &DHCPv4{} 258 err := dhcp.DecodeFromBytes(data, p) 259 if err != nil { 260 return err 261 } 262 p.AddLayer(dhcp) 263 return p.NextDecoder(gopacket.LayerTypePayload) 264 } 265 266 // DHCPOpt represents a DHCP option or parameter from RFC-2132 267 type DHCPOpt byte 268 269 // Constants for the DHCPOpt options. 270 const ( 271 DHCPOptPad DHCPOpt = 0 272 DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP 273 DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC) 274 DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP 275 DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP 276 DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP 277 DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP 278 DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP 279 DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP 280 DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP 281 DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP 282 DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP 283 DHCPOptHostname DHCPOpt = 12 // n, string 284 DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16 285 DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string 286 DHCPOptDomainName DHCPOpt = 15 // n, string 287 DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP 288 DHCPOptRootPath DHCPOpt = 17 // n, string 289 DHCPOptExtensionsPath DHCPOpt = 18 // n, string 290 DHCPOptIPForwarding DHCPOpt = 19 // 1, bool 291 DHCPOptSourceRouting DHCPOpt = 20 // 1, bool 292 DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP} 293 DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16 294 DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte 295 DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32 296 DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16 297 DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16 298 DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool 299 DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP 300 DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool 301 DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool 302 DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool 303 DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP 304 DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask 305 DHCPOptARPTrailers DHCPOpt = 34 // 1, bool 306 DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32 307 DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool 308 DHCPOptTCPTTL DHCPOpt = 37 // 1, byte 309 DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32 310 DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool 311 DHCPOptNISDomain DHCPOpt = 40 // n, string 312 DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP 313 DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP 314 DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated. 315 DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP 316 DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP 317 DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte 318 DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string 319 DHCPOptXFontServer DHCPOpt = 48 // n, string 320 DHCPOptXDisplayManager DHCPOpt = 49 // n, string 321 DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP 322 DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32 323 DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3 324 DHCPOptMessageType DHCPOpt = 53 // 1, 1-7 325 DHCPOptServerID DHCPOpt = 54 // 4, net.IP 326 DHCPOptParamsRequest DHCPOpt = 55 // n, []byte 327 DHCPOptMessage DHCPOpt = 56 // n, 3 328 DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16 329 DHCPOptT1 DHCPOpt = 58 // 4, uint32 330 DHCPOptT2 DHCPOpt = 59 // 4, uint32 331 DHCPOptClassID DHCPOpt = 60 // n, []byte 332 DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte 333 DHCPOptDomainSearch DHCPOpt = 119 // n, string 334 DHCPOptSIPServers DHCPOpt = 120 // n, url 335 DHCPOptClasslessStaticRoute DHCPOpt = 121 // 336 DHCPOptMUDURLV4 DHCPOpt = 161 // n, string 337 DHCPOptEnd DHCPOpt = 255 338 ) 339 340 // String returns a string version of a DHCPOpt. 341 func (o DHCPOpt) String() string { 342 switch o { 343 case DHCPOptPad: 344 return "(padding)" 345 case DHCPOptSubnetMask: 346 return "SubnetMask" 347 case DHCPOptTimeOffset: 348 return "TimeOffset" 349 case DHCPOptRouter: 350 return "Router" 351 case DHCPOptTimeServer: 352 return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP 353 case DHCPOptNameServer: 354 return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS 355 case DHCPOptDNS: 356 return "DNS" 357 case DHCPOptLogServer: 358 return "mitLCS" // MIT LCS server protocol yada yada w. Syslog 359 case DHCPOptCookieServer: 360 return "CookieServer" 361 case DHCPOptLPRServer: 362 return "LPRServer" 363 case DHCPOptImpressServer: 364 return "ImpressServer" 365 case DHCPOptResLocServer: 366 return "ResourceLocationServer" 367 case DHCPOptHostname: 368 return "Hostname" 369 case DHCPOptBootfileSize: 370 return "BootfileSize" 371 case DHCPOptMeritDumpFile: 372 return "MeritDumpFile" 373 case DHCPOptDomainName: 374 return "DomainName" 375 case DHCPOptSwapServer: 376 return "SwapServer" 377 case DHCPOptRootPath: 378 return "RootPath" 379 case DHCPOptExtensionsPath: 380 return "ExtensionsPath" 381 case DHCPOptIPForwarding: 382 return "IPForwarding" 383 case DHCPOptSourceRouting: 384 return "SourceRouting" 385 case DHCPOptPolicyFilter: 386 return "PolicyFilter" 387 case DHCPOptDatagramMTU: 388 return "DatagramMTU" 389 case DHCPOptDefaultTTL: 390 return "DefaultTTL" 391 case DHCPOptPathMTUAgingTimeout: 392 return "PathMTUAgingTimeout" 393 case DHCPOptPathPlateuTableOption: 394 return "PathPlateuTableOption" 395 case DHCPOptInterfaceMTU: 396 return "InterfaceMTU" 397 case DHCPOptAllSubsLocal: 398 return "AllSubsLocal" 399 case DHCPOptBroadcastAddr: 400 return "BroadcastAddress" 401 case DHCPOptMaskDiscovery: 402 return "MaskDiscovery" 403 case DHCPOptMaskSupplier: 404 return "MaskSupplier" 405 case DHCPOptRouterDiscovery: 406 return "RouterDiscovery" 407 case DHCPOptSolicitAddr: 408 return "SolicitAddr" 409 case DHCPOptStaticRoute: 410 return "StaticRoute" 411 case DHCPOptARPTrailers: 412 return "ARPTrailers" 413 case DHCPOptARPTimeout: 414 return "ARPTimeout" 415 case DHCPOptEthernetEncap: 416 return "EthernetEncap" 417 case DHCPOptTCPTTL: 418 return "TCPTTL" 419 case DHCPOptTCPKeepAliveInt: 420 return "TCPKeepAliveInt" 421 case DHCPOptTCPKeepAliveGarbage: 422 return "TCPKeepAliveGarbage" 423 case DHCPOptNISDomain: 424 return "NISDomain" 425 case DHCPOptNISServers: 426 return "NISServers" 427 case DHCPOptNTPServers: 428 return "NTPServers" 429 case DHCPOptVendorOption: 430 return "VendorOption" 431 case DHCPOptNetBIOSTCPNS: 432 return "NetBIOSOverTCPNS" 433 case DHCPOptNetBIOSTCPDDS: 434 return "NetBiosOverTCPDDS" 435 case DHCPOptNETBIOSTCPNodeType: 436 return "NetBIOSOverTCPNodeType" 437 case DHCPOptNetBIOSTCPScope: 438 return "NetBIOSOverTCPScope" 439 case DHCPOptXFontServer: 440 return "XFontServer" 441 case DHCPOptXDisplayManager: 442 return "XDisplayManager" 443 case DHCPOptEnd: 444 return "(end)" 445 case DHCPOptSIPServers: 446 return "SipServers" 447 case DHCPOptRequestIP: 448 return "RequestIP" 449 case DHCPOptLeaseTime: 450 return "LeaseTime" 451 case DHCPOptExtOptions: 452 return "ExtOpts" 453 case DHCPOptMessageType: 454 return "MessageType" 455 case DHCPOptServerID: 456 return "ServerID" 457 case DHCPOptParamsRequest: 458 return "ParamsRequest" 459 case DHCPOptMessage: 460 return "Message" 461 case DHCPOptMaxMessageSize: 462 return "MaxDHCPSize" 463 case DHCPOptT1: 464 return "Timer1" 465 case DHCPOptT2: 466 return "Timer2" 467 case DHCPOptClassID: 468 return "ClassID" 469 case DHCPOptClientID: 470 return "ClientID" 471 case DHCPOptDomainSearch: 472 return "DomainSearch" 473 case DHCPOptClasslessStaticRoute: 474 return "ClasslessStaticRoute" 475 case DHCPOptMUDURLV4: 476 return "ManufacturerUsageDescriptionURL" 477 default: 478 return "Unknown" 479 } 480 } 481 482 // DHCPOption rerpresents a DHCP option. 483 type DHCPOption struct { 484 Type DHCPOpt 485 Length uint8 486 Data []byte 487 } 488 489 // String returns a string version of a DHCP Option. 490 func (o DHCPOption) String() string { 491 switch o.Type { 492 493 case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath, 494 DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer, 495 DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string 496 return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data)) 497 498 case DHCPOptMessageType: 499 if len(o.Data) != 1 { 500 return fmt.Sprintf("Option(%s:INVALID)", o.Type) 501 } 502 return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0])) 503 504 case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr, 505 DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP 506 if len(o.Data) < 4 { 507 return fmt.Sprintf("Option(%s:INVALID)", o.Type) 508 } 509 return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data)) 510 511 case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout, 512 DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32 513 if len(o.Data) != 4 { 514 return fmt.Sprintf("Option(%s:INVALID)", o.Type) 515 } 516 return fmt.Sprintf("Option(%s:%d)", o.Type, 517 uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3])) 518 519 case DHCPOptParamsRequest: 520 buf := &bytes.Buffer{} 521 buf.WriteString(fmt.Sprintf("Option(%s:", o.Type)) 522 for i, v := range o.Data { 523 buf.WriteString(DHCPOpt(v).String()) 524 if i+1 != len(o.Data) { 525 buf.WriteByte(',') 526 } 527 } 528 buf.WriteString(")") 529 return buf.String() 530 531 default: 532 return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data) 533 } 534 } 535 536 // NewDHCPOption constructs a new DHCPOption with a given type and data. 537 func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption { 538 o := DHCPOption{Type: t} 539 if data != nil { 540 o.Data = data 541 o.Length = uint8(len(data)) 542 } 543 return o 544 } 545 546 func (o *DHCPOption) encode(b []byte) error { 547 switch o.Type { 548 case DHCPOptPad, DHCPOptEnd: 549 b[0] = byte(o.Type) 550 default: 551 b[0] = byte(o.Type) 552 b[1] = o.Length 553 copy(b[2:], o.Data) 554 } 555 return nil 556 } 557 558 func (o *DHCPOption) decode(data []byte) error { 559 if len(data) < 1 { 560 // Pad/End have a length of 1 561 return DecOptionNotEnoughData 562 } 563 o.Type = DHCPOpt(data[0]) 564 switch o.Type { 565 case DHCPOptPad, DHCPOptEnd: 566 o.Data = nil 567 default: 568 if len(data) < 2 { 569 return DecOptionNotEnoughData 570 } 571 o.Length = data[1] 572 if int(o.Length) > len(data[2:]) { 573 return DecOptionMalformed 574 } 575 o.Data = data[2 : 2+int(o.Length)] 576 } 577 return nil 578 } 579 580 // DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts. 581 type DHCPv4Error string 582 583 // DHCPv4Error implements error interface. 584 func (d DHCPv4Error) Error() string { 585 return string(d) 586 } 587 588 const ( 589 // DecOptionNotEnoughData is returned when there is not enough data during option's decode process 590 DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode") 591 // DecOptionMalformed is returned when the option is malformed 592 DecOptionMalformed = DHCPv4Error("Option is malformed") 593 // InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header 594 InvalidMagicCookie = DHCPv4Error("Bad DHCP header") 595 )