blob: 67c80b7d5f6a18e515c202ed06db76b9bbcce6d0 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301// 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 DHCPOptMUDURLV4 DHCPOpt = 161 // n, string
337 DHCPOptEnd DHCPOpt = 255
338)
339
340// String returns a string version of a DHCPOpt.
341func (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.
483type DHCPOption struct {
484 Type DHCPOpt
485 Length uint8
486 Data []byte
487}
488
489// String returns a string version of a DHCP Option.
490func (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.
537func 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
546func (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
558func (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.
581type DHCPv4Error string
582
583// DHCPv4Error implements error interface.
584func (d DHCPv4Error) Error() string {
585 return string(d)
586}
587
588const (
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)