1 // Copyright 2016 Google, Inc. All rights reserved.
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
16 "github.com/google/gopacket"
19 // DHCPOp rerprents a bootp operation
24 DHCPOpRequest DHCPOp = 1
25 DHCPOpReply DHCPOp = 2
28 // String returns a string version of a DHCPOp.
29 func (o DHCPOp) String() string {
40 // DHCPMsgType represents a DHCP operation
43 // Constants that represent DHCP operations
45 DHCPMsgTypeUnspecified DHCPMsgType = iota
56 // String returns a string version of a DHCPMsgType.
57 func (o DHCPMsgType) String() string {
59 case DHCPMsgTypeUnspecified:
61 case DHCPMsgTypeDiscover:
63 case DHCPMsgTypeOffer:
65 case DHCPMsgTypeRequest:
67 case DHCPMsgTypeDecline:
73 case DHCPMsgTypeRelease:
75 case DHCPMsgTypeInform:
82 //DHCPMagic is the RFC 2131 "magic cooke" for DHCP.
83 var DHCPMagic uint32 = 0x63825363
85 // DHCPv4 contains data for a single DHCP packet.
99 ClientHWAddr net.HardwareAddr
105 // DHCPOptions is used to get nicely printed option lists which would normally
106 // be cut off after 5 options.
107 type DHCPOptions []DHCPOption
109 // String returns a string version of the options list.
110 func (o DHCPOptions) String() string {
111 buf := &bytes.Buffer{}
113 for i, opt := range o {
114 buf.WriteString(opt.String())
116 buf.WriteString(", ")
123 // LayerType returns gopacket.LayerTypeDHCPv4
124 func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
126 // DecodeFromBytes decodes the given bytes into this layer.
127 func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
128 d.Operation = DHCPOp(data[0])
129 d.HardwareType = LinkType(data[1])
130 d.HardwareLen = data[2]
131 d.HardwareOpts = data[3]
132 d.Xid = binary.BigEndian.Uint32(data[4:8])
133 d.Secs = binary.BigEndian.Uint16(data[8:10])
134 d.Flags = binary.BigEndian.Uint16(data[10:12])
135 d.ClientIP = net.IP(data[12:16])
136 d.YourClientIP = net.IP(data[16:20])
137 d.NextServerIP = net.IP(data[20:24])
138 d.RelayAgentIP = net.IP(data[24:28])
139 d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen])
140 d.ServerName = data[44:108]
141 d.File = data[108:236]
142 if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic {
143 return errors.New("Bad DHCP header")
146 if len(data) <= 240 {
147 // DHCP Packet could have no option (??)
151 options := data[240:]
157 if err := o.decode(options[start:]); err != nil {
160 if o.Type == DHCPOptEnd {
163 d.Options = append(d.Options, o)
164 // Check if the option is a single byte pad
165 if o.Type == DHCPOptPad {
168 start += int(o.Length) + 2
174 // Len returns the length of a DHCPv4 packet.
175 func (d *DHCPv4) Len() uint16 {
177 for _, o := range d.Options {
178 if o.Type == DHCPOptPad {
181 n += uint16(o.Length) + 2
188 // SerializeTo writes the serialized form of this layer into the
189 // SerializationBuffer, implementing gopacket.SerializableLayer.
190 // See the docs for gopacket.SerializableLayer for more info.
191 func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
194 data, err := b.PrependBytes(plen)
199 data[0] = byte(d.Operation)
200 data[1] = byte(d.HardwareType)
202 d.HardwareLen = uint8(len(d.ClientHWAddr))
204 data[2] = d.HardwareLen
205 data[3] = d.HardwareOpts
206 binary.BigEndian.PutUint32(data[4:8], d.Xid)
207 binary.BigEndian.PutUint16(data[8:10], d.Secs)
208 binary.BigEndian.PutUint16(data[10:12], d.Flags)
209 copy(data[12:16], d.ClientIP.To4())
210 copy(data[16:20], d.YourClientIP.To4())
211 copy(data[20:24], d.NextServerIP.To4())
212 copy(data[24:28], d.RelayAgentIP.To4())
213 copy(data[28:44], d.ClientHWAddr)
214 copy(data[44:108], d.ServerName)
215 copy(data[108:236], d.File)
216 binary.BigEndian.PutUint32(data[236:240], DHCPMagic)
218 if len(d.Options) > 0 {
220 for _, o := range d.Options {
221 if err := o.encode(data[offset:]); err != nil {
224 // A pad option is only a single byte
225 if o.Type == DHCPOptPad {
228 offset += 2 + len(o.Data)
231 optend := NewDHCPOption(DHCPOptEnd, nil)
232 if err := optend.encode(data[offset:]); err != nil {
239 // CanDecode returns the set of layer types that this DecodingLayer can decode.
240 func (d *DHCPv4) CanDecode() gopacket.LayerClass {
241 return LayerTypeDHCPv4
244 // NextLayerType returns the layer type contained by this DecodingLayer.
245 func (d *DHCPv4) NextLayerType() gopacket.LayerType {
246 return gopacket.LayerTypePayload
249 func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
251 err := dhcp.DecodeFromBytes(data, p)
256 return p.NextDecoder(gopacket.LayerTypePayload)
259 // DHCPOpt represents a DHCP option or parameter from RFC-2132
262 // Constants for the DHCPOpt options.
264 DHCPOptPad DHCPOpt = 0
265 DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP
266 DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC)
267 DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP
268 DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP
269 DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP
270 DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP
271 DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP
272 DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP
273 DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP
274 DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP
275 DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP
276 DHCPOptHostname DHCPOpt = 12 // n, string
277 DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16
278 DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string
279 DHCPOptDomainName DHCPOpt = 15 // n, string
280 DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP
281 DHCPOptRootPath DHCPOpt = 17 // n, string
282 DHCPOptExtensionsPath DHCPOpt = 18 // n, string
283 DHCPOptIPForwarding DHCPOpt = 19 // 1, bool
284 DHCPOptSourceRouting DHCPOpt = 20 // 1, bool
285 DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP}
286 DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16
287 DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte
288 DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32
289 DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16
290 DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16
291 DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool
292 DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP
293 DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool
294 DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool
295 DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool
296 DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP
297 DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask
298 DHCPOptARPTrailers DHCPOpt = 34 // 1, bool
299 DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32
300 DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool
301 DHCPOptTCPTTL DHCPOpt = 37 // 1, byte
302 DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32
303 DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool
304 DHCPOptNISDomain DHCPOpt = 40 // n, string
305 DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP
306 DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP
307 DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated.
308 DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP
309 DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP
310 DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte
311 DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string
312 DHCPOptXFontServer DHCPOpt = 48 // n, string
313 DHCPOptXDisplayManager DHCPOpt = 49 // n, string
314 DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP
315 DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32
316 DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3
317 DHCPOptMessageType DHCPOpt = 53 // 1, 1-7
318 DHCPOptServerID DHCPOpt = 54 // 4, net.IP
319 DHCPOptParamsRequest DHCPOpt = 55 // n, []byte
320 DHCPOptMessage DHCPOpt = 56 // n, 3
321 DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16
322 DHCPOptT1 DHCPOpt = 58 // 4, uint32
323 DHCPOptT2 DHCPOpt = 59 // 4, uint32
324 DHCPOptClassID DHCPOpt = 60 // n, []byte
325 DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte
326 DHCPOptDomainSearch DHCPOpt = 119 // n, string
327 DHCPOptSIPServers DHCPOpt = 120 // n, url
328 DHCPOptClasslessStaticRoute DHCPOpt = 121 //
329 DHCPOptEnd DHCPOpt = 255
332 // String returns a string version of a DHCPOpt.
333 func (o DHCPOpt) String() string {
337 case DHCPOptSubnetMask:
339 case DHCPOptTimeOffset:
343 case DHCPOptTimeServer:
344 return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP
345 case DHCPOptNameServer:
346 return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS
349 case DHCPOptLogServer:
350 return "mitLCS" // MIT LCS server protocol yada yada w. Syslog
351 case DHCPOptCookieServer:
352 return "CookieServer"
353 case DHCPOptLPRServer:
355 case DHCPOptImpressServer:
356 return "ImpressServer"
357 case DHCPOptResLocServer:
358 return "ResourceLocationServer"
359 case DHCPOptHostname:
361 case DHCPOptBootfileSize:
362 return "BootfileSize"
363 case DHCPOptMeritDumpFile:
364 return "MeritDumpFile"
365 case DHCPOptDomainName:
367 case DHCPOptSwapServer:
369 case DHCPOptRootPath:
371 case DHCPOptExtensionsPath:
372 return "ExtensionsPath"
373 case DHCPOptIPForwarding:
374 return "IPForwarding"
375 case DHCPOptSourceRouting:
376 return "SourceRouting"
377 case DHCPOptPolicyFilter:
378 return "PolicyFilter"
379 case DHCPOptDatagramMTU:
381 case DHCPOptDefaultTTL:
383 case DHCPOptPathMTUAgingTimeout:
384 return "PathMTUAgingTimeout"
385 case DHCPOptPathPlateuTableOption:
386 return "PathPlateuTableOption"
387 case DHCPOptInterfaceMTU:
388 return "InterfaceMTU"
389 case DHCPOptAllSubsLocal:
390 return "AllSubsLocal"
391 case DHCPOptBroadcastAddr:
392 return "BroadcastAddress"
393 case DHCPOptMaskDiscovery:
394 return "MaskDiscovery"
395 case DHCPOptMaskSupplier:
396 return "MaskSupplier"
397 case DHCPOptRouterDiscovery:
398 return "RouterDiscovery"
399 case DHCPOptSolicitAddr:
401 case DHCPOptStaticRoute:
403 case DHCPOptARPTrailers:
405 case DHCPOptARPTimeout:
407 case DHCPOptEthernetEncap:
408 return "EthernetEncap"
411 case DHCPOptTCPKeepAliveInt:
412 return "TCPKeepAliveInt"
413 case DHCPOptTCPKeepAliveGarbage:
414 return "TCPKeepAliveGarbage"
415 case DHCPOptNISDomain:
417 case DHCPOptNISServers:
419 case DHCPOptNTPServers:
421 case DHCPOptVendorOption:
422 return "VendorOption"
423 case DHCPOptNetBIOSTCPNS:
424 return "NetBIOSOverTCPNS"
425 case DHCPOptNetBIOSTCPDDS:
426 return "NetBiosOverTCPDDS"
427 case DHCPOptNETBIOSTCPNodeType:
428 return "NetBIOSOverTCPNodeType"
429 case DHCPOptNetBIOSTCPScope:
430 return "NetBIOSOverTCPScope"
431 case DHCPOptXFontServer:
433 case DHCPOptXDisplayManager:
434 return "XDisplayManager"
437 case DHCPOptSIPServers:
439 case DHCPOptRequestIP:
441 case DHCPOptLeaseTime:
443 case DHCPOptExtOptions:
445 case DHCPOptMessageType:
447 case DHCPOptServerID:
449 case DHCPOptParamsRequest:
450 return "ParamsRequest"
453 case DHCPOptMaxMessageSize:
461 case DHCPOptClientID:
463 case DHCPOptDomainSearch:
464 return "DomainSearch"
465 case DHCPOptClasslessStaticRoute:
466 return "ClasslessStaticRoute"
472 // DHCPOption rerpresents a DHCP option.
473 type DHCPOption struct {
479 // String returns a string version of a DHCP Option.
480 func (o DHCPOption) String() string {
483 case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath,
484 DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer,
485 DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string
486 return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data))
488 case DHCPOptMessageType:
489 if len(o.Data) != 1 {
490 return fmt.Sprintf("Option(%s:INVALID)", o.Type)
492 return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0]))
494 case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr,
495 DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP
497 return fmt.Sprintf("Option(%s:INVALID)", o.Type)
499 return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data))
501 case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout,
502 DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32
503 if len(o.Data) != 4 {
504 return fmt.Sprintf("Option(%s:INVALID)", o.Type)
506 return fmt.Sprintf("Option(%s:%d)", o.Type,
507 uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3]))
509 case DHCPOptParamsRequest:
510 buf := &bytes.Buffer{}
511 buf.WriteString(fmt.Sprintf("Option(%s:", o.Type))
512 for i, v := range o.Data {
513 buf.WriteString(DHCPOpt(v).String())
514 if i+1 != len(o.Data) {
522 return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
526 // NewDHCPOption constructs a new DHCPOption with a given type and data.
527 func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption {
528 o := DHCPOption{Type: t}
531 o.Length = uint8(len(data))
536 func (o *DHCPOption) encode(b []byte) error {
538 case DHCPOptPad, DHCPOptEnd:
542 return errors.New("data too long to encode")
551 func (o *DHCPOption) decode(data []byte) error {
553 // Pad/End have a length of 1
554 return errors.New("Not enough data to decode")
556 o.Type = DHCPOpt(data[0])
558 case DHCPOptPad, DHCPOptEnd:
562 return errors.New("Not enough data to decode")
566 return errors.New("data too long to decode")
568 o.Data = data[2 : 2+o.Length]