dhcp 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+