Fix unit tests
[govpp.git] / vendor / github.com / google / gopacket / layers / dhcpv4.go
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         "errors"
13         "fmt"
14         "net"
15
16         "github.com/google/gopacket"
17 )
18
19 // DHCPOp rerprents a bootp operation
20 type DHCPOp byte
21
22 // bootp operations
23 const (
24         DHCPOpRequest DHCPOp = 1
25         DHCPOpReply   DHCPOp = 2
26 )
27
28 // String returns a string version of a DHCPOp.
29 func (o DHCPOp) String() string {
30         switch o {
31         case DHCPOpRequest:
32                 return "Request"
33         case DHCPOpReply:
34                 return "Reply"
35         default:
36                 return "Unknown"
37         }
38 }
39
40 // DHCPMsgType represents a DHCP operation
41 type DHCPMsgType byte
42
43 // Constants that represent DHCP operations
44 const (
45         DHCPMsgTypeUnspecified DHCPMsgType = iota
46         DHCPMsgTypeDiscover
47         DHCPMsgTypeOffer
48         DHCPMsgTypeRequest
49         DHCPMsgTypeDecline
50         DHCPMsgTypeAck
51         DHCPMsgTypeNak
52         DHCPMsgTypeRelease
53         DHCPMsgTypeInform
54 )
55
56 // String returns a string version of a DHCPMsgType.
57 func (o DHCPMsgType) String() string {
58         switch o {
59         case DHCPMsgTypeUnspecified:
60                 return "Unspecified"
61         case DHCPMsgTypeDiscover:
62                 return "Discover"
63         case DHCPMsgTypeOffer:
64                 return "Offer"
65         case DHCPMsgTypeRequest:
66                 return "Request"
67         case DHCPMsgTypeDecline:
68                 return "Decline"
69         case DHCPMsgTypeAck:
70                 return "Ack"
71         case DHCPMsgTypeNak:
72                 return "Nak"
73         case DHCPMsgTypeRelease:
74                 return "Release"
75         case DHCPMsgTypeInform:
76                 return "Inform"
77         default:
78                 return "Unknown"
79         }
80 }
81
82 //DHCPMagic is the RFC 2131 "magic cooke" for DHCP.
83 var DHCPMagic uint32 = 0x63825363
84
85 // DHCPv4 contains data for a single DHCP packet.
86 type DHCPv4 struct {
87         BaseLayer
88         Operation    DHCPOp
89         HardwareType LinkType
90         HardwareLen  uint8
91         HardwareOpts uint8
92         Xid          uint32
93         Secs         uint16
94         Flags        uint16
95         ClientIP     net.IP
96         YourClientIP net.IP
97         NextServerIP net.IP
98         RelayAgentIP net.IP
99         ClientHWAddr net.HardwareAddr
100         ServerName   []byte
101         File         []byte
102         Options      DHCPOptions
103 }
104
105 // DHCPOptions is used to get nicely printed option lists which would normally
106 // be cut off after 5 options.
107 type DHCPOptions []DHCPOption
108
109 // String returns a string version of the options list.
110 func (o DHCPOptions) String() string {
111         buf := &bytes.Buffer{}
112         buf.WriteByte('[')
113         for i, opt := range o {
114                 buf.WriteString(opt.String())
115                 if i+1 != len(o) {
116                         buf.WriteString(", ")
117                 }
118         }
119         buf.WriteByte(']')
120         return buf.String()
121 }
122
123 // LayerType returns gopacket.LayerTypeDHCPv4
124 func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
125
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")
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.
175 func (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.
191 func (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.
240 func (d *DHCPv4) CanDecode() gopacket.LayerClass {
241         return LayerTypeDHCPv4
242 }
243
244 // NextLayerType returns the layer type contained by this DecodingLayer.
245 func (d *DHCPv4) NextLayerType() gopacket.LayerType {
246         return gopacket.LayerTypePayload
247 }
248
249 func 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
260 type DHCPOpt byte
261
262 // Constants for the DHCPOpt options.
263 const (
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.
333 func (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.
473 type DHCPOption struct {
474         Type   DHCPOpt
475         Length uint8
476         Data   []byte
477 }
478
479 // String returns a string version of a DHCP Option.
480 func (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.
527 func 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
536 func (o *DHCPOption) encode(b []byte) error {
537         switch o.Type {
538         case DHCPOptPad, DHCPOptEnd:
539                 b[0] = byte(o.Type)
540         default:
541                 if o.Length > 253 {
542                         return errors.New("data too long to encode")
543                 }
544                 b[0] = byte(o.Type)
545                 b[1] = o.Length
546                 copy(b[2:], o.Data)
547         }
548         return nil
549 }
550
551 func (o *DHCPOption) decode(data []byte) error {
552         if len(data) < 1 {
553                 // Pad/End have a length of 1
554                 return errors.New("Not enough data to decode")
555         }
556         o.Type = DHCPOpt(data[0])
557         switch o.Type {
558         case DHCPOptPad, DHCPOptEnd:
559                 o.Data = nil
560         default:
561                 if len(data) < 3 {
562                         return errors.New("Not enough data to decode")
563                 }
564                 o.Length = data[1]
565                 if o.Length > 253 {
566                         return errors.New("data too long to decode")
567                 }
568                 o.Data = data[2 : 2+o.Length]
569         }
570         return nil
571 }