Matteo Scandolo | a6a3aee | 2019-11-26 13:30:14 -0700 | [diff] [blame] | 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 | "fmt" |
| 13 | "net" |
| 14 | |
| 15 | "github.com/google/gopacket" |
| 16 | ) |
| 17 | |
| 18 | // DHCPOp rerprents a bootp operation |
| 19 | type DHCPOp byte |
| 20 | |
| 21 | // bootp operations |
| 22 | const ( |
| 23 | DHCPOpRequest DHCPOp = 1 |
| 24 | DHCPOpReply DHCPOp = 2 |
| 25 | ) |
| 26 | |
| 27 | // String returns a string version of a DHCPOp. |
| 28 | func (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 |
| 40 | type DHCPMsgType byte |
| 41 | |
| 42 | // Constants that represent DHCP operations |
| 43 | const ( |
| 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. |
| 56 | func (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. |
| 82 | var DHCPMagic uint32 = 0x63825363 |
| 83 | |
| 84 | // DHCPv4 contains data for a single DHCP packet. |
| 85 | type 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. |
| 106 | type DHCPOptions []DHCPOption |
| 107 | |
| 108 | // String returns a string version of the options list. |
| 109 | func (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 |
| 123 | func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 } |
| 124 | |
| 125 | // DecodeFromBytes decodes the given bytes into this layer. |
| 126 | func (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. |
| 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 | b[0] = byte(o.Type) |
| 542 | b[1] = o.Length |
| 543 | copy(b[2:], o.Data) |
| 544 | } |
| 545 | return nil |
| 546 | } |
| 547 | |
| 548 | func (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. |
| 571 | type DHCPv4Error string |
| 572 | |
| 573 | // DHCPv4Error implements error interface. |
| 574 | func (d DHCPv4Error) Error() string { |
| 575 | return string(d) |
| 576 | } |
| 577 | |
| 578 | const ( |
| 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 | ) |