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 7package layers 8 9import ( 10 "bytes" 11 "encoding/binary" 12 "fmt" 13 "net" 14 15 "github.com/google/gopacket" 16) 17 18// DHCPOp rerprents a bootp operation 19type DHCPOp byte 20 21// bootp operations 22const ( 23 DHCPOpRequest DHCPOp = 1 24 DHCPOpReply DHCPOp = 2 25) 26 27// String returns a string version of a DHCPOp. 28func (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 40type DHCPMsgType byte 41 42// Constants that represent DHCP operations 43const ( 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. 56func (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. 82var DHCPMagic uint32 = 0x63825363 83 84// DHCPv4 contains data for a single DHCP packet. 85type 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. 106type DHCPOptions []DHCPOption 107 108// String returns a string version of the options list. 109func (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 123func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 } 124 125// DecodeFromBytes decodes the given bytes into this layer. 126func (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. 182func (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. 198func (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. 247func (d *DHCPv4) CanDecode() gopacket.LayerClass { 248 return LayerTypeDHCPv4 249} 250 251// NextLayerType returns the layer type contained by this DecodingLayer. 252func (d *DHCPv4) NextLayerType() gopacket.LayerType { 253 return gopacket.LayerTypePayload 254} 255 256func 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 267type DHCPOpt byte 268 269// Constants for the DHCPOpt options. 270const ( 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 DHCPOptEnd DHCPOpt = 255 337) 338 339// String returns a string version of a DHCPOpt. 340func (o DHCPOpt) String() string { 341 switch o { 342 case DHCPOptPad: 343 return "(padding)" 344 case DHCPOptSubnetMask: 345 return "SubnetMask" 346 case DHCPOptTimeOffset: 347 return "TimeOffset" 348 case DHCPOptRouter: 349 return "Router" 350 case DHCPOptTimeServer: 351 return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP 352 case DHCPOptNameServer: 353 return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS 354 case DHCPOptDNS: 355 return "DNS" 356 case DHCPOptLogServer: 357 return "mitLCS" // MIT LCS server protocol yada yada w. Syslog 358 case DHCPOptCookieServer: 359 return "CookieServer" 360 case DHCPOptLPRServer: 361 return "LPRServer" 362 case DHCPOptImpressServer: 363 return "ImpressServer" 364 case DHCPOptResLocServer: 365 return "ResourceLocationServer" 366 case DHCPOptHostname: 367 return "Hostname" 368 case DHCPOptBootfileSize: 369 return "BootfileSize" 370 case DHCPOptMeritDumpFile: 371 return "MeritDumpFile" 372 case DHCPOptDomainName: 373 return "DomainName" 374 case DHCPOptSwapServer: 375 return "SwapServer" 376 case DHCPOptRootPath: 377 return "RootPath" 378 case DHCPOptExtensionsPath: 379 return "ExtensionsPath" 380 case DHCPOptIPForwarding: 381 return "IPForwarding" 382 case DHCPOptSourceRouting: 383 return "SourceRouting" 384 case DHCPOptPolicyFilter: 385 return "PolicyFilter" 386 case DHCPOptDatagramMTU: 387 return "DatagramMTU" 388 case DHCPOptDefaultTTL: 389 return "DefaultTTL" 390 case DHCPOptPathMTUAgingTimeout: 391 return "PathMTUAgingTimeout" 392 case DHCPOptPathPlateuTableOption: 393 return "PathPlateuTableOption" 394 case DHCPOptInterfaceMTU: 395 return "InterfaceMTU" 396 case DHCPOptAllSubsLocal: 397 return "AllSubsLocal" 398 case DHCPOptBroadcastAddr: 399 return "BroadcastAddress" 400 case DHCPOptMaskDiscovery: 401 return "MaskDiscovery" 402 case DHCPOptMaskSupplier: 403 return "MaskSupplier" 404 case DHCPOptRouterDiscovery: 405 return "RouterDiscovery" 406 case DHCPOptSolicitAddr: 407 return "SolicitAddr" 408 case DHCPOptStaticRoute: 409 return "StaticRoute" 410 case DHCPOptARPTrailers: 411 return "ARPTrailers" 412 case DHCPOptARPTimeout: 413 return "ARPTimeout" 414 case DHCPOptEthernetEncap: 415 return "EthernetEncap" 416 case DHCPOptTCPTTL: 417 return "TCPTTL" 418 case DHCPOptTCPKeepAliveInt: 419 return "TCPKeepAliveInt" 420 case DHCPOptTCPKeepAliveGarbage: 421 return "TCPKeepAliveGarbage" 422 case DHCPOptNISDomain: 423 return "NISDomain" 424 case DHCPOptNISServers: 425 return "NISServers" 426 case DHCPOptNTPServers: 427 return "NTPServers" 428 case DHCPOptVendorOption: 429 return "VendorOption" 430 case DHCPOptNetBIOSTCPNS: 431 return "NetBIOSOverTCPNS" 432 case DHCPOptNetBIOSTCPDDS: 433 return "NetBiosOverTCPDDS" 434 case DHCPOptNETBIOSTCPNodeType: 435 return "NetBIOSOverTCPNodeType" 436 case DHCPOptNetBIOSTCPScope: 437 return "NetBIOSOverTCPScope" 438 case DHCPOptXFontServer: 439 return "XFontServer" 440 case DHCPOptXDisplayManager: 441 return "XDisplayManager" 442 case DHCPOptEnd: 443 return "(end)" 444 case DHCPOptSIPServers: 445 return "SipServers" 446 case DHCPOptRequestIP: 447 return "RequestIP" 448 case DHCPOptLeaseTime: 449 return "LeaseTime" 450 case DHCPOptExtOptions: 451 return "ExtOpts" 452 case DHCPOptMessageType: 453 return "MessageType" 454 case DHCPOptServerID: 455 return "ServerID" 456 case DHCPOptParamsRequest: 457 return "ParamsRequest" 458 case DHCPOptMessage: 459 return "Message" 460 case DHCPOptMaxMessageSize: 461 return "MaxDHCPSize" 462 case DHCPOptT1: 463 return "Timer1" 464 case DHCPOptT2: 465 return "Timer2" 466 case DHCPOptClassID: 467 return "ClassID" 468 case DHCPOptClientID: 469 return "ClientID" 470 case DHCPOptDomainSearch: 471 return "DomainSearch" 472 case DHCPOptClasslessStaticRoute: 473 return "ClasslessStaticRoute" 474 default: 475 return "Unknown" 476 } 477} 478 479// DHCPOption rerpresents a DHCP option. 480type DHCPOption struct { 481 Type DHCPOpt 482 Length uint8 483 Data []byte 484} 485 486// String returns a string version of a DHCP Option. 487func (o DHCPOption) String() string { 488 switch o.Type { 489 490 case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath, 491 DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer, 492 DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string 493 return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data)) 494 495 case DHCPOptMessageType: 496 if len(o.Data) != 1 { 497 return fmt.Sprintf("Option(%s:INVALID)", o.Type) 498 } 499 return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0])) 500 501 case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr, 502 DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP 503 if len(o.Data) < 4 { 504 return fmt.Sprintf("Option(%s:INVALID)", o.Type) 505 } 506 return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data)) 507 508 case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout, 509 DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32 510 if len(o.Data) != 4 { 511 return fmt.Sprintf("Option(%s:INVALID)", o.Type) 512 } 513 return fmt.Sprintf("Option(%s:%d)", o.Type, 514 uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3])) 515 516 case DHCPOptParamsRequest: 517 buf := &bytes.Buffer{} 518 buf.WriteString(fmt.Sprintf("Option(%s:", o.Type)) 519 for i, v := range o.Data { 520 buf.WriteString(DHCPOpt(v).String()) 521 if i+1 != len(o.Data) { 522 buf.WriteByte(',') 523 } 524 } 525 buf.WriteString(")") 526 return buf.String() 527 528 default: 529 return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data) 530 } 531} 532 533// NewDHCPOption constructs a new DHCPOption with a given type and data. 534func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption { 535 o := DHCPOption{Type: t} 536 if data != nil { 537 o.Data = data 538 o.Length = uint8(len(data)) 539 } 540 return o 541} 542 543func (o *DHCPOption) encode(b []byte) error { 544 switch o.Type { 545 case DHCPOptPad, DHCPOptEnd: 546 b[0] = byte(o.Type) 547 default: 548 b[0] = byte(o.Type) 549 b[1] = o.Length 550 copy(b[2:], o.Data) 551 } 552 return nil 553} 554 555func (o *DHCPOption) decode(data []byte) error { 556 if len(data) < 1 { 557 // Pad/End have a length of 1 558 return DecOptionNotEnoughData 559 } 560 o.Type = DHCPOpt(data[0]) 561 switch o.Type { 562 case DHCPOptPad, DHCPOptEnd: 563 o.Data = nil 564 default: 565 if len(data) < 2 { 566 return DecOptionNotEnoughData 567 } 568 o.Length = data[1] 569 if int(o.Length) > len(data[2:]) { 570 return DecOptionMalformed 571 } 572 o.Data = data[2 : 2+int(o.Length)] 573 } 574 return nil 575} 576 577// DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts. 578type DHCPv4Error string 579 580// DHCPv4Error implements error interface. 581func (d DHCPv4Error) Error() string { 582 return string(d) 583} 584 585const ( 586 // DecOptionNotEnoughData is returned when there is not enough data during option's decode process 587 DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode") 588 // DecOptionMalformed is returned when the option is malformed 589 DecOptionMalformed = DHCPv4Error("Option is malformed") 590 // InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header 591 InvalidMagicCookie = DHCPv4Error("Bad DHCP header") 592) 593