dhcp 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,652 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: ASCII-8BIT
3
+ #
4
+ # --
5
+ #
6
+ # Ruby DHCP module for parsing and creating IPv4 DHCP options
7
+ # - See http://www.aarongifford.com/computers/dhcp/
8
+ #
9
+ # --
10
+ #
11
+ # Written by Aaron D. Gifford - http://www.aarongifford.com/
12
+ #
13
+ # Copyright (c) 2010-2011 InfoWest, Inc. and Aaron D. Gifford
14
+ #
15
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ # of this software and associated documentation files (the "Software"), to deal
17
+ # in the Software without restriction, including without limitation the rights
18
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ # copies of the Software, and to permit persons to whom the Software is
20
+ # furnished to do so, subject to the following conditions:
21
+ #
22
+ # The above copyright notice and this permission notice shall be included in
23
+ # all copies or substantial portions of the Software.
24
+ #
25
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31
+ # THE SOFTWARE.
32
+ #
33
+ # --
34
+ #
35
+ # NOTE: All strings in this module should be BINARY (ASCII-8BIT) encoded
36
+ # or things won't work correctly.
37
+ #
38
+
39
+ ## Import DHCP module constants:
40
+ require_relative 'dhcp'
41
+
42
+ module DHCP
43
+ ## Base class from which all DHCP options in a DHCP packet derive:
44
+ class Opt
45
+ def initialize(opt, name, ignore=nil)
46
+ @opt = opt
47
+ @name = name
48
+ end
49
+ attr_reader :opt, :name
50
+
51
+ def opt_header
52
+ "OPTION[#{opt},#{@name}]"
53
+ end
54
+
55
+ def to_s
56
+ opt_header
57
+ end
58
+
59
+ def to_opt
60
+ @opt.chr
61
+ end
62
+ end
63
+
64
+
65
+ ## Class for DHCP options that contain data
66
+ class OptData < Opt
67
+ def initialize(opt, name, data=nil)
68
+ super(opt, name)
69
+ @data = data.nil? ? '' : data_to_bin(data)
70
+ end
71
+ attr_accessor :data
72
+
73
+ def data
74
+ @data
75
+ end
76
+
77
+ def data=(data)
78
+ @data = data.dup
79
+ self ## Chainable
80
+ end
81
+
82
+ def set(data)
83
+ self.data = data_to_bin(data)
84
+ self ## Chainable
85
+ end
86
+
87
+ def get
88
+ bin_to_data(@data)
89
+ end
90
+
91
+ def data_to_bin(data) ## Override this in subclasses to interpret data
92
+ data
93
+ end
94
+
95
+ def bin_to_data(data) ## Override this in subclasses to interpret data
96
+ data
97
+ end
98
+
99
+ def opt_header
100
+ super + "(#{data.size})"
101
+ end
102
+
103
+ def to_s
104
+ opt_header + "='#{bin_to_data(@data)}'"
105
+ end
106
+
107
+ def to_opt
108
+ super + @data.size.chr + @data
109
+ end
110
+ end
111
+
112
+
113
+ ## Class for DHCP options containing a fixed number of bytes
114
+ class OptFixedData < OptData
115
+ @size = 0 ## Override this in subclasses
116
+ class << self
117
+ attr_accessor :size
118
+ end
119
+
120
+ def initialize(opt, name, data=nil)
121
+ super(opt, name, data)
122
+ ## Prefill with zeros if needed:
123
+ @data = 0.chr * self.class.size if data.nil? && self.class.size > 0
124
+ end
125
+
126
+ def data=(data)
127
+ raise "Invalid size for #{self.class} (expected #{size} bytes, not #{data.size} bytes)" unless self.class.size == data.size
128
+ super(data)
129
+ end
130
+ end
131
+
132
+ ## Class for DHCP options that contain a lists (like lists of IPs)
133
+ class OptListData < OptData
134
+ include Enumerable
135
+ def initialize(opt, name, data=nil)
136
+ super(opt, name)
137
+ @size = 0
138
+ set(data) unless data.nil?
139
+ end
140
+
141
+ def data=(data)
142
+ set(split_data(data))
143
+ end
144
+
145
+ def get
146
+ split_data(@data) ## Splits and interprets binary data
147
+ end
148
+
149
+ def set(list)
150
+ list = [list] unless is_list?(list)
151
+ @data = ''
152
+ @size = 0
153
+ list.each do |item|
154
+ append(item)
155
+ end
156
+ self ## Chainable
157
+ end
158
+
159
+ def is_list?(list) ## Override if needed in child class
160
+ list.is_a?(Array)
161
+ end
162
+
163
+ def append(item)
164
+ @size += 1
165
+ @data += data_to_bin(item)
166
+ self ## Chainable
167
+ end
168
+
169
+ def split_data(data) ## Override in child class to split and interpret binary data
170
+ raise "Child class #{data.class} MUST override this"
171
+ end
172
+
173
+ def size
174
+ @size
175
+ end
176
+
177
+ def to_s
178
+ opt_header + '=[' + map{|x| x.to_s}.join(',') + ']'
179
+ end
180
+
181
+ def each
182
+ split_data(@data).each do |item|
183
+ yield item
184
+ end
185
+ end
186
+ end
187
+
188
+ ## Class for DHCP option suboptions:
189
+ class SubOpt < OptData
190
+ def opt_header
191
+ "suboption[#{opt}:#{@name}]"
192
+ end
193
+ end
194
+
195
+ ## Class for DHCP option suboptions containing lists
196
+ class SubOptList < OptListData
197
+ def opt_header
198
+ "suboption[#{opt}:#{@name}]"
199
+ end
200
+ end
201
+
202
+ ## Class for DHCP suboption for vendor specific information
203
+ class SubOptVSRInfo < SubOptList
204
+ def is_list?(list)
205
+ raise "Invalid suboption sublist/entry" unless list.is_a?(Array)
206
+ return false if list.size == 2 && list[0].is_a?(Fixnum) && list[1].is_a?(String)
207
+ list.each do |item|
208
+ raise "Invalid suboption sublistlist" unless item.is_a?(Array) && item.size == 2 && item[0].is_a?(Fixnum) && item[1].is_a?(String)
209
+ end
210
+ return true
211
+ end
212
+
213
+ def split_data(data)
214
+ data = data.dup
215
+ list = []
216
+ while data.size > 0
217
+ raise "Invalid suboption data" unless data.size >= 5
218
+ len = data[4,1].ord
219
+ raise "Invalid vendor-specific relay info. data length" unless data.size >= len + 5
220
+ list << [ data[0,4].unpack('N')[0], data[5,len] ]
221
+ data[0,5+len] = ''
222
+ end
223
+ list
224
+ end
225
+
226
+ def bin_to_data(data)
227
+ raise "Invalid data size" unless data.size >= 5 && data.size == data[4,1].ord + 5
228
+ [ data[0,1].ord, data[2,data.size-2] ]
229
+ end
230
+
231
+ def data_to_bin(data)
232
+ raise "Invalid data" unless data.is_a?(Array) && data.size == 2 && data[0].is_a?(Fixnum) && data[1].is_a?(String)
233
+ raise "Invalid data size" unless data[1].size < 256
234
+ data[0].chr + data[1].size.chr + data[1]
235
+ end
236
+ end
237
+
238
+ ## Class for DHCP options that contain sublists (like vendor specific information or relay agent information)
239
+ class OptSubList < OptListData
240
+ def is_list?(list)
241
+ raise "Invalid suboption list/entry" unless list.is_a?(Array)
242
+ return false if list.size == 2 && list[0].is_a?(Fixnum) && list[1].is_a?(String)
243
+ list.each do |item|
244
+ raise "Invalid suboption list" unless item.is_a?(Array) && item.size == 2 && item[0].is_a?(Fixnum) && item[1].is_a?(String)
245
+ end
246
+ return true
247
+ end
248
+
249
+ def split_data(data)
250
+ data = data.dup
251
+ list = []
252
+ while data.size > 0
253
+ raise "Invalid data size" unless data.size >= 2
254
+ len = data[1,1].ord
255
+ raise "Invalid data size" unless data.size >= len + 2
256
+ list << [ data[0,1].ord, data[2,len] ]
257
+ data[0,len+2] = ''
258
+ end
259
+ list
260
+ end
261
+
262
+ def bin_to_data(data)
263
+ raise "Invalid data size" unless data.size >= 2 && data.size == data[1,1].ord + 2
264
+ [ data[0,1].ord, data[2,data.size-2] ]
265
+ end
266
+
267
+ def data_to_bin(data)
268
+ raise "Invalid data" unless data.is_a?(Array) && data.size == 2 && data[0].is_a?(Fixnum) && data[1].is_a?(String)
269
+ raise "Invalid data size" unless data[1].size < 256
270
+ data[0].chr + data[1].size.chr + data[1]
271
+ end
272
+
273
+ def to_s
274
+ opt_header + "(#{@size})=[" + map do |i|
275
+ val = ''
276
+ name = case i[0]
277
+ when 1
278
+ val = i[1].scan(/./m).map{|b| b.unpack('H2')[0].upcase}.join(':')
279
+ 'AgentCircuitID'
280
+ when 2
281
+ val = i[1].scan(/./m).map{|b| b.unpack('H2')[0].upcase}.join(':')
282
+ 'AgentRemoteID'
283
+ when 9
284
+ val = (SubOptVSRInfo.new(9, :vendor_specific_relay_suboption).data=i[1]).to_s
285
+ 'VendorSpecificRelaySuboption'
286
+ else
287
+ val = i[1].scan(/./m).map{|b| b.unpack('H2')[0].upcase}.join(':')
288
+ 'Unknown'
289
+ end
290
+ "#{name}:#{i[0]}(#{i[1].size})='#{val}'"
291
+ end.join(',') + ']'
292
+ end
293
+ end
294
+
295
+ ## Class for DHCP options that contain lists of fixed sized data
296
+ class OptListFixedData < OptListData
297
+ @item_size = 0 ## Override this in subclasses
298
+ class << self
299
+ attr_accessor :item_size
300
+ end
301
+
302
+ def split_data(data)
303
+ raise "Child class #{self.class} MUST override class item_size variable with non-zero value!" if self.class.item_size == 0
304
+ raise "Invalid data length #{data.size} (expected even multiple of #{self.class.item_size})" unless data.size % self.class.item_size == 0
305
+ list = []
306
+ data = data.dup
307
+ while data.size > 0
308
+ list << bin_to_data(data.slice!(0,self.class.item_size))
309
+ end
310
+ list
311
+ end
312
+
313
+ def data_to_bin(item) ## Override in child, but call super(item)
314
+ ## with the resulting translated data after
315
+ ## data translation so the size check is
316
+ ## applied (or do a size check in the child):
317
+ raise "Invalid data item length #{item.size} (expected #{self.class.item_size})" unless item.size == self.class.item_size
318
+ item
319
+ end
320
+ end
321
+
322
+ ## Class for DHCP options that contain a single IPv4 address
323
+ class OptIP < OptFixedData
324
+ @size = 4
325
+
326
+ def bin_to_data(data)
327
+ IPAddress::IPv4::parse_data(data).to_s
328
+ end
329
+
330
+ def data_to_bin(data)
331
+ IPAddress::IPv4.new(data).data ## Will raise exception if data is not a valid IP
332
+ end
333
+ end
334
+
335
+ ## Class for DHCP options that contain a list of IPv4 addresses
336
+ class OptIPList < OptListFixedData
337
+ @item_size = 4
338
+
339
+ def bin_to_data(data)
340
+ IPAddress::IPv4::parse_data(data).to_s
341
+ end
342
+
343
+ def data_to_bin(data)
344
+ IPAddress::IPv4.new(data).data ## Will raise exception if data is not a valid IP
345
+ end
346
+ end
347
+
348
+ ## Class for DHCP option 33 (static routes) - Use option 121 instead if possible
349
+ ## WARNING: Option 33 can only handle class A, B, or C networks, not classless
350
+ ## networks with an arbitrary netmask.
351
+ class OptStaticRoutes < OptListFixedData
352
+ @item_size = 8
353
+
354
+ def is_list?(list)
355
+ raise "Invalid route list/entry" unless list.is_a?(Array)
356
+ if list.size == 2
357
+ return false if list[0].is_a?(String) && list[1].is_a?(String)
358
+ return true if list[0].is_a?(Array) && list[1].is_a?(Array)
359
+ raise "Invalid route list/entry"
360
+ end
361
+ list.each do |item|
362
+ raise "Invalid route list" unless item.is_a?(Array) && item[0].is_a?(String) && item[1].is_a?(String)
363
+ end
364
+ return true
365
+ end
366
+
367
+ def data_to_bin(data)
368
+ raise "Invalid static route" unless data.is_a?(Array) && data.size == 2
369
+ net, gateway = *data
370
+ net = IPAddress::IPv4.new(net)
371
+ raise "Invalid classful static route network" unless net.network?
372
+ raise "Invalid classful static route network" unless (
373
+ (net.a? && net.prefix == 8 ) ||
374
+ (net.b? && net.prefix == 16) ||
375
+ (net.c? && net.prefix == 24)
376
+ )
377
+ gateway = IPAddress::IPv4.new("#{gateway}/#{net.prefix}")
378
+ raise "Invalid classful static route gateway" unless gateway.member?(net)
379
+ net.data + gateway.data
380
+ end
381
+
382
+ def bin_to_data(data)
383
+ [IPAddress::IPv4::parse_classful_data(data[0,4]).net.to_string, IPAddress::IPv4::parse_data(data[4,4]).to_s]
384
+ end
385
+
386
+ def to_s
387
+ opt_header + '=[' + map{|i| i[0] + '=>' + i[1]}.join(',') + ']'
388
+ end
389
+ end
390
+
391
+ ## Class for DHCP options containing lists of IPv4 CIDR routes (like option 121 or MS's 249)
392
+ ## See RFC 3442 "compact encoding" of destination
393
+ class OptRouteList < OptListData
394
+ def split_data(data)
395
+ data = data.dup
396
+ list = []
397
+ while data.size > 0
398
+ raise "Invalid binary data" unless data.size > 4 || data[0,1].ord > 32
399
+ octets = (data[0,1].ord + 7)/8
400
+ raise "Invalid binary data" unless data.size >= octets + 5
401
+ list << bin_to_data(data.slice!(0,octets+5))
402
+ end
403
+ list
404
+ end
405
+
406
+ def data_to_bin(data)
407
+ raise "Invalid classless static route" unless data.is_a?(Array) && data.size == 2
408
+ net, gateway = *data
409
+ raise "Invalid classless static route network" if net.index('/').nil?
410
+ net = IPAddress::IPv4.new(net)
411
+ raise "Invalid classless static route network" unless net.network?
412
+ gateway = IPAddress::IPv4.new("#{gateway}/#{net.prefix}")
413
+ raise "Invalid classless static route gateway" unless gateway.member?(net)
414
+ net.prefix.to_i.chr + net.data[0,(net.prefix+7)/8] + gateway.data
415
+ end
416
+
417
+ def bin_to_data(data)
418
+ raise "Invalid binary classless route data" unless data.size > 4 || data[0,1].ord > 32
419
+ maskbits = data[0,1].ord
420
+ octets = (maskbits+7)/8
421
+ raise "Invalid binary classless route data" unless data.size == octets + 5
422
+ dest = IPAddress::IPv4.parse_data(data[1,octets] + 0.chr * (4 - octets))
423
+ dest.prefix = maskbits
424
+ gateway = IPAddress::IPv4.parse_data(data[octets+1,4])
425
+ gateway.prefix = maskbits ## Unnecessary...
426
+ [dest.to_string, gateway.to_s]
427
+ end
428
+ end
429
+
430
+ ## Class for boolean DHCP options
431
+ class OptBool < OptFixedData
432
+ @size = 1
433
+
434
+ def data_to_bin(data)
435
+ raise "Invalid boolean data #{data.class} (expected TrueClass or FalseClass)" unless data.is_a?(TrueClass) || data.is_a?(FalseClass)
436
+ data ? 1.chr : 0.chr
437
+ end
438
+
439
+ def bin_to_data(data)
440
+ raise "Invalid boolean binary data" if data.size != 1 || data.ord > 1
441
+ data.ord == 0 ? false : true
442
+ end
443
+ end
444
+
445
+ ## Class for single-byte unsigned integer value DHCP options
446
+ ## Also acts as parent class for fixed-sized multi-byte value
447
+ ## DHCP options
448
+ class OptInt8 < OptFixedData
449
+ @size = 1
450
+
451
+ def data_to_bin(data)
452
+ raise "Invalid numeric data" unless data.is_a?(Fixnum) && data >= 0
453
+ raise "Invalid number" unless data == data & ([0xff] * self.class.size).inject(0){|sum,byte| sum<<8|byte}
454
+ bytes = ''
455
+ while data != 0
456
+ bytes = (data & 0xff).chr + bytes
457
+ data >>= 8
458
+ end
459
+ raise "Impossible: Numeric byte size #{bytes.size} exceeds #{self.class.size}" if bytes.size > self.class.size
460
+ 0.chr * (self.class.size - bytes.size) + bytes
461
+ end
462
+
463
+ def bin_to_data(data)
464
+ data.each_byte.inject(0){|sum,byte| sum<<8|byte}
465
+ end
466
+
467
+ def to_s
468
+ opt_header + "=#{self.get}"
469
+ end
470
+ end
471
+
472
+ ## Class for two-byte unsigned integer value DHCP options
473
+ class OptInt16 < OptInt8
474
+ @size = 2
475
+ end
476
+
477
+ ## Class for four-byte unsigned integer value DHCP options
478
+ class OptInt32 < OptInt8
479
+ @size = 4
480
+ end
481
+
482
+ ## Class for four-byte signed integer value DHCP options
483
+ class OptSInt32 < OptInt32
484
+ @size = 4
485
+ ## Convert signed data to unsigned form
486
+ def data_to_bin(data)
487
+ super(data % 2**32)
488
+ end
489
+
490
+ ## Convert unsigned form back to signed data
491
+ def bin_to_data(data)
492
+ (super(data) + 2**31) % 2**32 - 2**31
493
+ end
494
+ end
495
+
496
+ ## Class for DHCP options containing a list of 8-bit integers (like
497
+ ## lists of requested DHCP options). Also acts as parent class to
498
+ ## lists of larger fixed-sized numeric types.
499
+ class OptInt8List < OptListFixedData
500
+ @item_size = 1
501
+
502
+ def bin_to_data(data)
503
+ data.each_byte.inject(0){|sum,byte| sum<<8|byte}
504
+ end
505
+
506
+ def data_to_bin(data)
507
+ raise "Invalid numeric data" unless data.is_a?(Fixnum) && data >= 0
508
+ raise "Invalid number" unless data == data & ([0xff] * self.class.item_size).inject(0){|sum,byte| sum<<8|byte}
509
+ bytes = ''
510
+ while data != 0
511
+ bytes = (data & 0xff).chr + bytes
512
+ data >>= 8
513
+ end
514
+ raise "Impossible: Numeric byte size #{bytes.size} exceeds #{self.class.item_size}" if bytes.size > self.class.item_size
515
+ 0.chr * (self.class.item_size - bytes.size) + bytes
516
+ end
517
+
518
+ def to_s
519
+ opt_header + '=[' + map{|x| x.to_s}.join(',') + ']'
520
+ end
521
+ end
522
+
523
+ ## Class for DHCP options containing a list of 16-bit unsigned integers:
524
+ class OptInt16List < OptInt8List
525
+ @item_size = 2
526
+ end
527
+
528
+ ## Class for DHCP options containing data that is most often displayed as a string of hexadecimal digit pairs joined by colons (i.e. ethernet MAC addresses)
529
+ class OptHexString < OptData
530
+ def data_to_bin(data)
531
+ data = data.gsub(/[ \.:_\-]/,'') ## Allow various octet separator characters (trim them out)
532
+ ['0' * (data.size % 2)].pack('H*') ## Pad hex string to even multiple and translate to binary
533
+ end
534
+
535
+ def bin_to_data(data)
536
+ data.each_byte.map{|b| "%0.2X" % [b]}.join(':') ## Convert each byte to hex string and join bytes with ':'
537
+ end
538
+ end
539
+
540
+ ## Class for DHCP options containing DNS host names
541
+ class OptHost < OptData
542
+ def data_to_bin(data)
543
+ raise "Invalid host name" unless /^(?:[a-zA-Z0-9][a-zA-Z0-9-]{0,62}\.)*[a-zA-Z0-9][a-zA-Z0-9-]{0,62}$/.match(data)
544
+ data
545
+ end
546
+ end
547
+
548
+ ## Class for DHCP options containing DNS domain names
549
+ class OptDomain < OptData
550
+ def data_to_bin(data)
551
+ raise "Invalid domain name" unless /^(?:[a-zA-Z0-9][a-zA-Z0-9-]{0,62}\.)*[a-zA-Z0-9][a-zA-Z0-9-]{0,62}\.?$/.match(data)
552
+ end
553
+ end
554
+
555
+ ## Options 0-18 and 254 are defined in RFC 1497 (BOOTP)
556
+ ## TODO: Add in as yet unhandled options
557
+ OPTIONS = {
558
+ :pad => [ 0, Opt ], ## Pad (RFC 2132) - Padding option
559
+ :subnet_mask => [ 1, OptIP ],
560
+ :time_offset => [ 2, OptSInt32 ], ## Offset from GMT (signed 32-bit integer seconds)
561
+ :routers => [ 3, OptIPList ], ## Default gateway(s)
562
+ :time_servers => [ 4, OptIPList ],
563
+ :name_servers => [ 5, OptIPList ], ## IEN-116 name servers
564
+ :dns_servers => [ 6, OptIPList ], ## DNS server(s) (RFC-1034/1025)
565
+ :log_servers => [ 7, OptIPList ], ## Log server(s) (MIT-LCS UDP log servers)
566
+ :cookie_servers => [ 8, OptIPList ], ## Cookie/Quote-of-the-day (RFC 865) server(s)
567
+ :lpr_servers => [ 9, OptIPList ], ## LPR server(s) (RFC 1179)
568
+ :impress_servers => [ 10, OptIPList ], ## Impress server(s) (in pref. order)
569
+ :rlp_servers => [ 11, OptIPList ], ## RLP server(s) (RFC 887)
570
+ :host_name => [ 12, OptHost ], ## May or may not be qualified with local domain name (RFC 1035)
571
+ :boot_file_size => [ 13, OptInt16 ], ## Boot file size (number of 512-byte blocks as unsigned 16-bit integer)
572
+ :merit_dump_file => [ 14, OptData ], ## File name client should dump core to
573
+ :domain_name => [ 15, OptHost ], ## RFC 1034/1035 domain name
574
+ :swap_server => [ 16, OptIP ], ## Swap server
575
+ :root_path => [ 17, OptData ], ## Pathname to mount as root disk
576
+ :extensions_path => [ 18, OptData ], ## TFTP-available file containing info to be interpreted the same way as 64-byte vendor-extension field in a BOOTP response with some exceptions (See RFC 1497)
577
+ :ip_forwarding => [ 19, OptBool ], ## Host should enable/disable IP forwarding (0=disable/1=enable)
578
+ :nonlocal_source_routing => [ 20, OptBool ], ## Enable/disable source routing
579
+ :max_dgram_reassembly_size => [ 22, OptInt16 ], ## Maximum Datagram Reassembly Size (RFC 1533) - Min. value 576
580
+ :default_ttl => [ 23, OptInt8 ], ## Default IP Time-to-live (TTL) (RFC 1533) - Value in the range 1..255 inclusive
581
+ :path_mtu_aging_timeout => [ 24, OptInt32 ], ## Path MTU Aging Timeout Option (RFC 2132) - Timeout to use when aging Path MTU values discovered according to RFC 1191
582
+ :path_mtu_plateau_table => [ 25, OptInt16List ], ## Path MTU Plateau Table Option (RFC 2132) - List of 16-bit unsigned integers ordered smallest to largest, minimum MTU value NOT smaller than 68, minimum of at least one list entry
583
+ :interface_mtu => [ 26, OptInt16 ], ## Interface MTU (RFC 1533) - Minimum value 68
584
+ :all_subnets_are_local => [ 27, OptBool ], ## All Subnets Are Local (RFC 1533) - 0 = client should assume some subnets of directly connected net(s) may have smaller MTUs, 1 = all subnets share single MTU value
585
+ :broadcast_address => [ 28, OptIP ], ## Broadcast Address (RFC 1533) - Client's broadcast IP on client's subnet
586
+ :perform_mask_discovery => [ 29, OptBool ], ## Perform Mask Discovery (RFC 1533) - 0 = client should perform mask discovery, 1 = client should not
587
+ :mask_supplier => [ 30, OptBool ], ## Mask Supplier (RFC 1533) - 0 = client should NOT respond to subnet mask requests using ICMP, 1 = client should respond
588
+ :perform_router_discovery => [ 31, OptBool ], ## Perform Router Discover (RFC 1265) - 0 = client should NOT perform router discovery, 1 = client should
589
+ :router_solicitation_address => [ 32, OptIP ], ## Router Solicitaion Address (RFC 1533) - IP address to which client transmits router solicitation requests
590
+ :static_routes => [ 33, OptStaticRoutes ], ## Static Route (RFC 15333) - List of static routes client should install in routing cache, listed in descending order of priority (if multiple routes to same dest. are specified) - Use option 121 instead - Must NOT specify default route with this! (Illegal destination '0.0.0.0' for this option.)
591
+ :arp_cache_timeout => [ 35, OptInt32 ], ## ARP Cache Timeout (RFC 1533) - Unsigned 32-bit integer timeout in seconds for ARP cache entries
592
+ :ethernet_encapsulation => [ 36, OptBool ], ## Ethernet Encapsulation (RFC 1533) - = 0 = use ethernet v2 RFC 894 encapsulation, 1 = use 802.3 RFC 1042 encapsulation
593
+ :tcp_default_ttl => [ 37, OptInt8 ], ## TCP Default TTL (RFC 1533) - Minimum value of 1
594
+ :tcp_keepalive_interval => [ 38, OptInt32 ], ## TCP Keepalive Interval (RFC 1533) - 0 = client should not generate keepalive messages unless requested by application - No. of seconds client should wait before sending keepalive messages on TCP connections
595
+ :ntp_servers => [ 42, OptIPList ],
596
+ :vendor_specific_information => [ 43, OptSubList ],
597
+ :netbios_name_server => [ 44, OptIPList ], ## NetBIOS name server list
598
+ :netbios_over_tcpip_node_type => [ 46, OptInt8 ], ## NetBIOS node type: 1=B-node, 2=P-node, 4=M-node, 8=H-node
599
+ :netbios_over_tcpip_scope => [ 47, OptData ], ## NetBIOS scope
600
+ :requested_ip_address => [ 50, OptIP ], ## Client's requested IP
601
+ :ip_address_lease_time => [ 51, OptInt32 ], ## How long the lease lasts
602
+ :option_overload => [ 52, OptInt8 ], ## Option Overload (RFC 2132) - 1, 2, or 3 == 'file' has options, 'sname' has options, both have options (RFC 2132)
603
+ :dhcp_message_type => [ 53, OptInt8 ], ## One of the above-defined DHCP MESSAGE TYPEs
604
+ :server_identifier => [ 54, OptIP ], ## How the client differentiates between DHCP servers
605
+ :parameter_request_list => [ 55, OptInt8List ], ## List of options the CLIENT is requesting in response
606
+ :message => [ 56, OptData ], ## Message in DHCPNAK or DHCPDECLINE saying why that response was sent
607
+ :maximum_dhcp_message_size => [ 57, OptInt16 ], ## Maximum DHCP Message Size (RFD 2132) - Client tells server max. message size it will accept. Minimum allowed value is 576 octets. Do NOT include in DHCPDECLINE messages. On an ethernet with a 1500-byte MTU, subtracting 20 bytes for IP overhead and 8 for UDP overhead, the maximum packet size to use would be 1472 bytes.
608
+ :vendor_class_identifier => [ 60, OptData ], ## For example, some MS boxes send "MSFT 98" or "MSFT 5.0"
609
+ :client_identifier => [ 61, OptHexString ], ## Client's identifier (client picks ANYTHING)
610
+ :netware_ip_domain_name => [ 62, OptData ], ## NetWare/IP Domain Name (RFC 2242)
611
+ :netware_ip_information => [ 63, OptSubList ], ## NetWare/IP Information (RFC 2242)
612
+ :nis_domain_name => [ 64, OptData ], ## Network Information Service+ Domain (RFC 2132)
613
+ :nis_servers => [ 65, OptIPList ], ## Network Information Service+ Servers (RFC 2132) (one or more IPs)
614
+ :tftp_server_name => [ 66, OptData ], ## TFTP Server Name (RFC 2132) - Used when the 'sname' field has been used for DHCP options (option 52 has value of 2 or 3)
615
+ :bootfile_name => [ 67, OptData ], ## Bootfile Name (RFC 2132) - Used when the 'file' field has been used for DHCP options (option 52 has value of 1 or 3)
616
+ :mobile_ip_home_agent => [ 68, OptIPList ], ## Mobile IP Home Agent (RFC 2132) list of IP addresses indicating mobile IP home agents available to the client in order of preference (zero or more IPs)
617
+ :smtp_servers => [ 69, OptIPList ],
618
+ :pop3_servers => [ 70, OptIPList ],
619
+ :client_fqdn => [ 81, OptData ], ## Client's requested FQDN (DHCP server could use to update dynamic DNS)
620
+ :relay_agent_information => [ 82, OptSubList ], ## VERY USEFUL with Cisco CMTS and Motorola Canopy
621
+ :isns_servers => [ 83, OptData ], ## RFC 4184 Internet Storage Name Servers DHCP option (primary and backup)
622
+ :authentication => [ 90, OptData ], ## RFC 3118 authentication option -- NOT IMPLEMENTED
623
+ :client_last_transaction_time => [ 91, OptInt32 ], ## RFC 4388 leasequery option
624
+ :associated_ip => [ 92, OptIPList ], ## RFC 4388 leasequery option
625
+ :tz_posix => [ 100, OptData ], ## RFC 4833 timezone TZ-POSIX string (a POSIX time zone string like "MST7MDT6,M3.2.0/02:00,M11.1.0/02:00" which specifies an offset of 7 hours behind UTC during standard time, 6 during daylight time, with daylight beginning the 2nd Sunday in March at 2:00 AM local time and continuing until the 1st Sunday in November at 2:00 AM local time)
626
+ :tz_database => [ 101, OptData ], ## RFC 4833 timezone TZ-Database string (the name of a time zone in a database, like "America/Denver")
627
+ :classless_static_routes => [ 121, OptRouteList ], ## RFC 3442 classless static routes - obsoletes option 33 - Ignore opt. 33 if 121 is present - Should specify default routes using option 3 if this option is also present (can specify them in this option too) so if a client ignores 121, a default route will still be set up -- If client requests CLASSLESS STATIC ROUTES and either ROUTERS and/or STATIC ROUTES, ONLY respond with this option (see p. 6 RFC 3442)
628
+
629
+ ## START SITE-SPECIFIC OPTIONS (128..254 inclusive):
630
+ :ms_classless_static_routes => [ 249, OptRouteList ], ## Microsoft version of option 121 - does NOT ignore opt. 33 if present (differs from opt. 121)
631
+ :site_local_auto_proxy_config => [ 252, OptData ], ## WPAD site-local proxy configuration
632
+ ## END SITE-SPECIFIC OPTIONS
633
+
634
+ :end => [ 255, Opt ] ## End (RFC 2132) Mark end of options in vendor field - subsequent bytes are pad options
635
+ }
636
+
637
+ ## Create a new DHCP option object based on the symbolic name:
638
+ def self.make_opt_name(name, data=nil)
639
+ raise "Unknown/unhandled option '#{name}'" unless OPTIONS.key?(name)
640
+ OPTIONS[name][1].new(OPTIONS[name][0], name, data)
641
+ end
642
+
643
+ ## Create a new DHCP option object based on the option number:
644
+ def self.make_opt(opt, data=nil)
645
+ OPTIONS.each do |name, info|
646
+ return info[1].new(info[0], name, data) if info[0] == opt
647
+ end
648
+ return nil
649
+ end
650
+
651
+ end
652
+