Class: DHCP::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/net/dhcp/core.rb

Overview

————————————————————– dhcp messages

Direct Known Subclasses

ACK, Discover, Inform, Offer, Release, Request

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Message

Returns a new instance of Message.

Raises:

  • (ArgumentError)


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/net/dhcp/core.rb', line 108

def initialize(params = {})

  # message operation and options. We need at least an operation and a 
  # MessageTypeOption to create a DHCP message!!
  if (([:op, :options]  & params.keys).size != 2)
    raise ArgumentError, 'you need to specify at least values for :op and :options'
  end
  
  self.op = params[:op]
  
  self.options = params[:options]
  found = false
  self.options.each do |opt|
    next unless opt.class == MessageTypeOption
    found = true
  end
  raise ArgumentError, ':options must include a MessageTypeOption' unless found

  #hardware type and length of the hardware address
  self.htype = params.fetch(:htype, $DHCP_HTYPE_ETHERNET)
  self.hlen = params.fetch(:hlen, $DHCP_HLEN_ETHERNET)

  # client sets to zero. relay agents may modify
  self.hops = params.fetch(:hops, 0x00)

  # initialize a random transaction ID
  self.xid  = params.fetch(:xid, rand(2**32))

  # seconds elapsed, flags
  self.secs = params.fetch(:secs, 0x0000)
  self.flags = params.fetch(:flags, 0x0000)

  # client, you, next server  and relay agent addresses
  self.ciaddr = params.fetch(:ciaddr, 0x00000000)
  self.yiaddr = params.fetch(:yiaddr, 0x00000000)
  self.siaddr = params.fetch(:siaddr, 0x00000000)
  self.giaddr = params.fetch(:giaddr, 0x00000000)

  if (params.key?(:chaddr))
    self.chaddr = params[:chaddr]
    raise 'chaddr field should be of 16 bytes' unless self.chaddr.size == 16
  else
    if Gem.respond_to?(:win_platform?) && Gem.win_platform?
      mac_line = `getmac /fo list`.split("\n").grep(/Physical Address/).first
      if mac_line
        mac = mac_line.strip.split(":")[1].gsub("-","").strip
      end
    else
      ifcfg_out = `/sbin/ifconfig`.chomp
      matches1 = ifcfg_out.scan(/HWaddr ([a-h0-9:]+)/i).flatten  # Linux
      matches2 = ifcfg_out.scan(/ether ([a-h0-9:]+)/i).flatten   # Some Linux (EL7), BSDs
      mac = matches1.first || matches2.first
      mac = mac.gsub(/:/,"") if mac
    end
    mac = '000000000000' if mac.empty?
    self.chaddr = [mac].pack('H*').unpack('CCCCCC')
    self.chaddr += [0x00]*(16-self.chaddr.size)
  end

  if (params.key?(:sname))
    sname = params[:sname]
    case 
    when sname.size == 64
      self.sname = sname
    when sname.size < 64 && sname.class == String
      self.sname = sname.unpack('C64').map {|x| x ? x : 0}
    else
      raise 'sname field should be of 64 bytes or a string of less'
    end
  else
    self.sname = [0x00]*64
  end

  if (params.key?(:fname))
    fname = params[:fname]
    case 
    when fname.size == 128
      self.fname = fname
    when fname.size < 128 && fname.class == String
      self.fname = fname.unpack('C128').map {|x| x ? x : 0}
    else
      raise 'fname field should be of 128 bytes or a string of less'
    end
  else
    self.fname = [0x00]*128
  end
  
end

Instance Attribute Details

#chaddrObject

Returns the value of attribute chaddr.



33
34
35
# File 'lib/net/dhcp/core.rb', line 33

def chaddr
  @chaddr
end

#ciaddrObject

Returns the value of attribute ciaddr.



33
34
35
# File 'lib/net/dhcp/core.rb', line 33

def ciaddr
  @ciaddr
end

#flagsObject

Returns the value of attribute flags.



32
33
34
# File 'lib/net/dhcp/core.rb', line 32

def flags
  @flags
end

#fnameObject

Returns the value of attribute fname.



34
35
36
# File 'lib/net/dhcp/core.rb', line 34

def fname
  @fname
end

#giaddrObject

Returns the value of attribute giaddr.



33
34
35
# File 'lib/net/dhcp/core.rb', line 33

def giaddr
  @giaddr
end

#hlenObject

Returns the value of attribute hlen.



30
31
32
# File 'lib/net/dhcp/core.rb', line 30

def hlen
  @hlen
end

#hopsObject

Returns the value of attribute hops.



30
31
32
# File 'lib/net/dhcp/core.rb', line 30

def hops
  @hops
end

#htypeObject

Returns the value of attribute htype.



30
31
32
# File 'lib/net/dhcp/core.rb', line 30

def htype
  @htype
end

#opObject

Returns the value of attribute op.



30
31
32
# File 'lib/net/dhcp/core.rb', line 30

def op
  @op
end

#optionsObject

Returns the value of attribute options.



35
36
37
# File 'lib/net/dhcp/core.rb', line 35

def options
  @options
end

#secsObject

Returns the value of attribute secs.



32
33
34
# File 'lib/net/dhcp/core.rb', line 32

def secs
  @secs
end

#siaddrObject

Returns the value of attribute siaddr.



33
34
35
# File 'lib/net/dhcp/core.rb', line 33

def siaddr
  @siaddr
end

#snameObject

Returns the value of attribute sname.



34
35
36
# File 'lib/net/dhcp/core.rb', line 34

def sname
  @sname
end

#xidObject

Returns the value of attribute xid.



31
32
33
# File 'lib/net/dhcp/core.rb', line 31

def xid
  @xid
end

#yiaddrObject

Returns the value of attribute yiaddr.



33
34
35
# File 'lib/net/dhcp/core.rb', line 33

def yiaddr
  @yiaddr
end

Class Method Details

.from_udp_payload(data, opts = {}) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/net/dhcp/core.rb', line 39

def Message.from_udp_payload(data, opts={})
  opts = {:debug => true}.merge(opts)
  values = data.unpack('C4Nn2N4C16C192NC*')

  params = {
    :op => values.shift,
    :htype => values.shift,
    :hlen => values.shift,
    :hops => values.shift,
  
    :xid => values.shift,
    :secs => values.shift,
    :flags => values.shift,
    :ciaddr => values.shift,
    :yiaddr => values.shift,
    :siaddr => values.shift,
    :giaddr => values.shift,
    :chaddr => values.slice!(0..15),
    :sname => values.slice!(0..63),
    :fname => values.slice!(0..127),
  }
  
  # sname and file are now used
  #not_used = values.slice!(0..191) 127+63 190
  
  return nil unless ($DHCP_MAGIC == values.shift)
  
  #default message class
  msg_class = Message
  #default option class
  opt_class = Option
  
  params[:options] = []
  next_opt = values.shift
  while(next_opt != $DHCP_END)
    p = {
      :type => next_opt,
      :len => values.shift
    }
    p[:payload] = values.slice!(0..p[:len]-1)
    
    # check what is the type of dhcp option
    opt_class = $DHCP_MSG_OPTIONS[p[:type]]
    if opt_class.nil?
      if opts[:debug]
        puts '-------------------- please further investigate!!'
        puts p[:type]
        puts '-------------------- /'
      end
      opt_class = Option
    end
    if (opt_class == MessageTypeOption)
      msg_class = $DHCP_MSG_CLASSES[p[:payload].first]
    end
    params[:options] << opt_class.new(p)
    next_opt = values.shift
  end
  
  if msg_class.nil?
    if opts[:debug]
      puts '-------------------- please further investigate!!'
      p params[:options]
      puts '-------------------- /'
    end
    msg_class = Message
  end
  msg_class.new(params)
end

Instance Method Details

#==Object



37
# File 'lib/net/dhcp/core.rb', line 37

alias == eql?

#eql?(obj) ⇒ Boolean

Returns:

  • (Boolean)


233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/net/dhcp/core.rb', line 233

def eql?(obj)
  # objects must be of the same class
  return false unless (self.class == obj.class)
  
  vars1 = self.instance_variables
  
  # first make sure that the :options var is equal
  opt1 = self.instance_variable_get('@options')
  opt2 = obj.instance_variable_get('@options')
  
  return false unless opt1.eql?(opt2)
  vars1.delete('@options')
  
  # check all the other instance vairables
  vars1.each do |var|
    return false unless (self.instance_variable_get(var) == obj.instance_variable_get(var))
  end
  
  return true
end

#packObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/net/dhcp/core.rb', line 197

def pack()
  out = [
  self.op, self.htype, self.hlen, self.hops, 
  self.xid, 
  self.secs, self.flags, 
  self.ciaddr, 
  self.yiaddr, 
  self.siaddr, 
  self.giaddr
  ].pack('C4Nn2N4')

  if self.chaddr.size >= 16
    out << self.chaddr.pack('C16')
  else
    out << (self.chaddr + [0x00]*(16-self.chaddr.size)).pack('C16')
  end
  # file_name/server_name for pxe
  out << self.sname.pack('C64')
  out << self.fname.pack('C128')

  # sname and file
  # out << ([0x00]*192).pack('C192')

  out << [$DHCP_MAGIC].pack('N')
  self.options.each do |option|
    out << option.pack
  end
  out << [$DHCP_END].pack('C')
  
  # add padding up to 300 bytes
  if out.size < 300
    out << ([$DHCP_PAD]*(300-out.size)).pack('C*')
  end
  return out
end

#to_sObject



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/net/dhcp/core.rb', line 256

def to_s
  out = "DHCP Message\r\n"
  out << "\tFIELDS:\r\n"
  out << "\t\tTransaction ID = 0x#{self.xid.to_s(16)}\r\n"
  out << "\t\tClient IP address = #{[self.ciaddr].pack('N').unpack('C4').join('.')}\r\n"
  out << "\t\tYour IP address = #{[self.yiaddr].pack('N').unpack('C4').join('.')}\r\n"      
  out << "\t\tNext server IP address = #{[self.siaddr].pack('N').unpack('C4').join('.')}\r\n"
  out << "\t\tRelay agent IP address = #{[self.giaddr].pack('N').unpack('C4').join('.')}\r\n"      
  out << "\t\tHardware address = #{self.chaddr.slice(0..(self.hlen-1)).collect do |b| b.to_s(16).upcase.rjust(2,'0') end.join(':')}\r\n"
  out << "\t\tServer Name = #{self.sname.pack('n*').unpack('A*')}\r\n"
  out << "\t\tFile Name = #{self.fname.pack('n*').unpack('A*')}\r\n"
  out << "\tOPT:\r\n"
  self.options.each do |opt|
    out << "\t\t #{opt.to_s}\r\n"
  end
  return out
end