blob: 3bbd036e3fdbfed92829df408284877d2071d1af [file] [log] [blame]
Takahiro Suzukid7bf8202020-12-17 20:21:59 +09001// 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 d.Options = d.Options[:0]
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 InvalidMagicCookie
144 }
145
146 if len(data) <= 240 {
147 // DHCP Packet could have no option (??)
148 return nil
149 }
150
151 options := data[240:]
152
153 stop := len(options)
154 start := 0
155 for start < stop {
156 o := DHCPOption{}
157 if err := o.decode(options[start:]); err != nil {
158 return err
159 }
160 if o.Type == DHCPOptEnd {
161 break
162 }
163 d.Options = append(d.Options, o)
164 // Check if the option is a single byte pad
165 if o.Type == DHCPOptPad {
166 start++
167 } else {
168 start += int(o.Length) + 2
169 }
170 }
171 return nil
172}
173
174// Len returns the length of a DHCPv4 packet.
175func (d *DHCPv4) Len() uint16 {
176 n := uint16(240)
177 for _, o := range d.Options {
178 if o.Type == DHCPOptPad {
179 n++
180 } else {
181 n += uint16(o.Length) + 2
182 }
183 }
184 n++ // for opt end
185 return n
186}
187
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.
191func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
192 plen := int(d.Len())
193
194 data, err := b.PrependBytes(plen)
195 if err != nil {
196 return err
197 }
198
199 data[0] = byte(d.Operation)
200 data[1] = byte(d.HardwareType)
201 if opts.FixLengths {
202 d.HardwareLen = uint8(len(d.ClientHWAddr))
203 }
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)
217
218 if len(d.Options) > 0 {
219 offset := 240
220 for _, o := range d.Options {
221 if err := o.encode(data[offset:]); err != nil {
222 return err
223 }
224 // A pad option is only a single byte
225 if o.Type == DHCPOptPad {
226 offset++
227 } else {
228 offset += 2 + len(o.Data)
229 }
230 }
231 optend := NewDHCPOption(DHCPOptEnd, nil)
232 if err := optend.encode(data[offset:]); err != nil {
233 return err
234 }
235 }
236 return nil
237}
238
239// CanDecode returns the set of layer types that this DecodingLayer can decode.
240func (d *DHCPv4) CanDecode() gopacket.LayerClass {
241 return LayerTypeDHCPv4
242}
243
244// NextLayerType returns the layer type contained by this DecodingLayer.
245func (d *DHCPv4) NextLayerType() gopacket.LayerType {
246 return gopacket.LayerTypePayload
247}
248
249func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
250 dhcp := &DHCPv4{}
251 err := dhcp.DecodeFromBytes(data, p)
252 if err != nil {
253 return err
254 }
255 p.AddLayer(dhcp)
256 return p.NextDecoder(gopacket.LayerTypePayload)
257}
258
259// DHCPOpt represents a DHCP option or parameter from RFC-2132
260type DHCPOpt byte
261
262// Constants for the DHCPOpt options.
263const (
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
330)
331
332// String returns a string version of a DHCPOpt.
333func (o DHCPOpt) String() string {
334 switch o {
335 case DHCPOptPad:
336 return "(padding)"
337 case DHCPOptSubnetMask:
338 return "SubnetMask"
339 case DHCPOptTimeOffset:
340 return "TimeOffset"
341 case DHCPOptRouter:
342 return "Router"
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
347 case DHCPOptDNS:
348 return "DNS"
349 case DHCPOptLogServer:
350 return "mitLCS" // MIT LCS server protocol yada yada w. Syslog
351 case DHCPOptCookieServer:
352 return "CookieServer"
353 case DHCPOptLPRServer:
354 return "LPRServer"
355 case DHCPOptImpressServer:
356 return "ImpressServer"
357 case DHCPOptResLocServer:
358 return "ResourceLocationServer"
359 case DHCPOptHostname:
360 return "Hostname"
361 case DHCPOptBootfileSize:
362 return "BootfileSize"
363 case DHCPOptMeritDumpFile:
364 return "MeritDumpFile"
365 case DHCPOptDomainName:
366 return "DomainName"
367 case DHCPOptSwapServer:
368 return "SwapServer"
369 case DHCPOptRootPath:
370 return "RootPath"
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:
380 return "DatagramMTU"
381 case DHCPOptDefaultTTL:
382 return "DefaultTTL"
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:
400 return "SolicitAddr"
401 case DHCPOptStaticRoute:
402 return "StaticRoute"
403 case DHCPOptARPTrailers:
404 return "ARPTrailers"
405 case DHCPOptARPTimeout:
406 return "ARPTimeout"
407 case DHCPOptEthernetEncap:
408 return "EthernetEncap"
409 case DHCPOptTCPTTL:
410 return "TCPTTL"
411 case DHCPOptTCPKeepAliveInt:
412 return "TCPKeepAliveInt"
413 case DHCPOptTCPKeepAliveGarbage:
414 return "TCPKeepAliveGarbage"
415 case DHCPOptNISDomain:
416 return "NISDomain"
417 case DHCPOptNISServers:
418 return "NISServers"
419 case DHCPOptNTPServers:
420 return "NTPServers"
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:
432 return "XFontServer"
433 case DHCPOptXDisplayManager:
434 return "XDisplayManager"
435 case DHCPOptEnd:
436 return "(end)"
437 case DHCPOptSIPServers:
438 return "SipServers"
439 case DHCPOptRequestIP:
440 return "RequestIP"
441 case DHCPOptLeaseTime:
442 return "LeaseTime"
443 case DHCPOptExtOptions:
444 return "ExtOpts"
445 case DHCPOptMessageType:
446 return "MessageType"
447 case DHCPOptServerID:
448 return "ServerID"
449 case DHCPOptParamsRequest:
450 return "ParamsRequest"
451 case DHCPOptMessage:
452 return "Message"
453 case DHCPOptMaxMessageSize:
454 return "MaxDHCPSize"
455 case DHCPOptT1:
456 return "Timer1"
457 case DHCPOptT2:
458 return "Timer2"
459 case DHCPOptClassID:
460 return "ClassID"
461 case DHCPOptClientID:
462 return "ClientID"
463 case DHCPOptDomainSearch:
464 return "DomainSearch"
465 case DHCPOptClasslessStaticRoute:
466 return "ClasslessStaticRoute"
467 default:
468 return "Unknown"
469 }
470}
471
472// DHCPOption rerpresents a DHCP option.
473type DHCPOption struct {
474 Type DHCPOpt
475 Length uint8
476 Data []byte
477}
478
479// String returns a string version of a DHCP Option.
480func (o DHCPOption) String() string {
481 switch o.Type {
482
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))
487
488 case DHCPOptMessageType:
489 if len(o.Data) != 1 {
490 return fmt.Sprintf("Option(%s:INVALID)", o.Type)
491 }
492 return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0]))
493
494 case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr,
495 DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP
496 if len(o.Data) < 4 {
497 return fmt.Sprintf("Option(%s:INVALID)", o.Type)
498 }
499 return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data))
500
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)
505 }
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]))
508
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) {
515 buf.WriteByte(',')
516 }
517 }
518 buf.WriteString(")")
519 return buf.String()
520
521 default:
522 return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
523 }
524}
525
526// NewDHCPOption constructs a new DHCPOption with a given type and data.
527func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption {
528 o := DHCPOption{Type: t}
529 if data != nil {
530 o.Data = data
531 o.Length = uint8(len(data))
532 }
533 return o
534}
535
536func (o *DHCPOption) encode(b []byte) error {
537 switch o.Type {
538 case DHCPOptPad, DHCPOptEnd:
539 b[0] = byte(o.Type)
540 default:
541 b[0] = byte(o.Type)
542 b[1] = o.Length
543 copy(b[2:], o.Data)
544 }
545 return nil
546}
547
548func (o *DHCPOption) decode(data []byte) error {
549 if len(data) < 1 {
550 // Pad/End have a length of 1
551 return DecOptionNotEnoughData
552 }
553 o.Type = DHCPOpt(data[0])
554 switch o.Type {
555 case DHCPOptPad, DHCPOptEnd:
556 o.Data = nil
557 default:
558 if len(data) < 2 {
559 return DecOptionNotEnoughData
560 }
561 o.Length = data[1]
562 if int(o.Length) > len(data[2:]) {
563 return DecOptionMalformed
564 }
565 o.Data = data[2 : 2+int(o.Length)]
566 }
567 return nil
568}
569
570// DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts.
571type DHCPv4Error string
572
573// DHCPv4Error implements error interface.
574func (d DHCPv4Error) Error() string {
575 return string(d)
576}
577
578const (
579 // DecOptionNotEnoughData is returned when there is not enough data during option's decode process
580 DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode")
581 // DecOptionMalformed is returned when the option is malformed
582 DecOptionMalformed = DHCPv4Error("Option is malformed")
583 // InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header
584 InvalidMagicCookie = DHCPv4Error("Bad DHCP header")
585)