blondy-dhcpd 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/Gemfile +4 -1
- data/README.md +13 -1
- data/Rakefile +5 -0
- data/TODO.md +3 -0
- data/bin/blondy-dhcpd +67 -0
- data/blondy-dhcpd.gemspec +3 -0
- data/lib/blondy/dhcpd/cache.rb +33 -0
- data/lib/blondy/dhcpd/config.rb +56 -0
- data/lib/blondy/dhcpd/dispatcher.rb +83 -12
- data/lib/blondy/dhcpd/logger.rb +10 -0
- data/lib/blondy/dhcpd/pool.rb +78 -0
- data/lib/blondy/dhcpd/reply.rb +37 -0
- data/lib/blondy/dhcpd/server.rb +35 -2
- data/lib/blondy/dhcpd/version.rb +1 -1
- data/spec/cache_spec.rb +50 -0
- data/spec/dispatcher_spec.rb +194 -35
- data/spec/pool_spec.rb +122 -0
- data/spec/reply_spec.rb +9 -0
- data/spec/server_spec.rb +69 -8
- data/spec/spec_helper.rb +49 -5
- metadata +76 -22
- data/lib/blondy/dhcpd.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cff451085750256df70f852e290cc0486a4662e2
|
4
|
+
data.tar.gz: b04ecbb33b1926fdd552572b1f5b4d4e89114c5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 325c913489b1c2dc5f2030d3f92028b9d0b3336064fba0c0b59e7cacdb814a591fe42bc56dd1f6587ff7b698bfbcb83e3911d029c8d6721c90932668ca1374e2
|
7
|
+
data.tar.gz: d1b8714e9e7054d4ddb07b20620178701da0190fb49732e83c0c9dd60a598f6428e199d931c87eb3db1bd603d20907d5ddc1093bc85daf11d0ea335e41c022e0
|
data/Gemfile
CHANGED
@@ -2,11 +2,14 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
gem 'net-dhcp', :git => 'https://github.com/
|
5
|
+
gem 'net-dhcp', :git => 'https://github.com/presto53/net-dhcp-ruby', :branch => 'fixed'
|
6
6
|
gem 'log4r'
|
7
7
|
gem 'eventmachine'
|
8
|
+
gem 'em-http-request'
|
8
9
|
|
9
10
|
group :test do
|
10
11
|
gem 'rspec'
|
12
|
+
gem 'webmock'
|
13
|
+
gem 'simplecov'
|
11
14
|
end
|
12
15
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[](http://badge.fury.io/rb/net-dhcp) [](https://travis-ci.org/presto53/blondy-dhcpd)
|
1
|
+
[](http://badge.fury.io/rb/net-dhcp) [](https://travis-ci.org/presto53/blondy-dhcpd) [](https://codeclimate.com/repos/52eb8ff6e30ba06ec2002a03/feed)
|
2
2
|
|
3
3
|
blondy-dhcpd
|
4
4
|
============
|
@@ -6,9 +6,21 @@ DHCPd with remote pools
|
|
6
6
|
|
7
7
|
Installation
|
8
8
|
---------------
|
9
|
+
gem install blondy-dhcpd
|
9
10
|
|
10
11
|
Configuration
|
11
12
|
---------------
|
13
|
+
Default config path is /etc/blondy
|
14
|
+
|
15
|
+
You can change it by set BLONDY_CONFIGPATH environment variable.
|
16
|
+
|
17
|
+
**Example config /etc/blondy/dhcpd.yml**:
|
18
|
+
|
19
|
+
log_path: '/var/log/blondy'
|
20
|
+
pid_path: '/var/run/blondy'
|
21
|
+
server_ip: '192.168.1.1'
|
22
|
+
client_key: 'AAAbbbCcC'
|
23
|
+
master: 'https://192.168.1.10'
|
12
24
|
|
13
25
|
Usage
|
14
26
|
---------------
|
data/Rakefile
CHANGED
data/TODO.md
ADDED
data/bin/blondy-dhcpd
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'blondy/dhcpd/logger'
|
4
|
+
require 'blondy/dhcpd/config'
|
5
|
+
require 'blondy/dhcpd/server'
|
6
|
+
|
7
|
+
module Blondy
|
8
|
+
module DHCPD
|
9
|
+
# Check if daemon already running
|
10
|
+
if /^\// =~ CONFIG['pid_path']
|
11
|
+
@pidf = "#{CONFIG['pid_path'].gsub(/\/*$/,'')}/blondy-dhcpd.pid"
|
12
|
+
running_pid = File.open(@pidf, 'r').read.chomp rescue nil
|
13
|
+
running_pgid = Process.getpgid(running_pid.to_i) rescue nil if running_pid
|
14
|
+
if running_pgid
|
15
|
+
Logger.error 'Daemon already running.'
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
else
|
19
|
+
Logger.error 'PID path is wrong or not set.'
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
# Daemonize
|
24
|
+
Process.daemon unless @options[:debug]
|
25
|
+
|
26
|
+
# Write PID
|
27
|
+
File.write(@pidf, "#{Process.pid}\n")
|
28
|
+
Logger.info "Starting dhcpd with pid #{Process.pid}"
|
29
|
+
|
30
|
+
# Signal handlers
|
31
|
+
@signals = Array.new
|
32
|
+
class << self
|
33
|
+
def shutdown(exit_code)
|
34
|
+
Logger.info "Shutdown server..."
|
35
|
+
EM.stop if EM.reactor_running?
|
36
|
+
File.delete(@pidf) if File.exists?(@pidf)
|
37
|
+
exit exit_code
|
38
|
+
end
|
39
|
+
def term_handler
|
40
|
+
Logger.info "Server received TERM signal."
|
41
|
+
shutdown(0)
|
42
|
+
end
|
43
|
+
def int_handler
|
44
|
+
Logger.info "Server received INT signal."
|
45
|
+
shutdown(0)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Start server
|
50
|
+
if Process.uid != 0
|
51
|
+
Logger.error 'Failed to start server. Server should be started by root.'
|
52
|
+
shutdown(1)
|
53
|
+
exit 1
|
54
|
+
else
|
55
|
+
EM.run do
|
56
|
+
Signal.trap('TERM') { @signals << :term }
|
57
|
+
Signal.trap('INT') { @signals << :int }
|
58
|
+
# Check for signals periodically
|
59
|
+
EM.add_periodic_timer(1) do
|
60
|
+
term_handler if @signals.include?(:term)
|
61
|
+
int_handler if @signals.include?(:int)
|
62
|
+
end
|
63
|
+
EM.open_datagram_socket('0.0.0.0', 67, Server)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/blondy-dhcpd.gemspec
CHANGED
@@ -21,8 +21,11 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "rspec", "~> 2.0"
|
24
|
+
spec.add_development_dependency "webmock"
|
25
|
+
spec.add_development_dependency "simplecov"
|
24
26
|
|
25
27
|
spec.add_runtime_dependency 'eventmachine', '>= 0.12.0'
|
28
|
+
spec.add_runtime_dependency 'em-http-request'
|
26
29
|
spec.add_runtime_dependency 'net-dhcp', '>= 1.1.1'
|
27
30
|
spec.add_runtime_dependency 'log4r'
|
28
31
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module Blondy
|
3
|
+
module DHCPD
|
4
|
+
class Cache
|
5
|
+
@cache = Hash.new
|
6
|
+
class << self
|
7
|
+
def add(hwaddr,type, data)
|
8
|
+
@cache[type] = Hash.new unless @cache[type]
|
9
|
+
@cache[type][hwaddr] = Hash.new unless @cache[type][hwaddr]
|
10
|
+
@cache[type][hwaddr][:data] = data
|
11
|
+
@cache[type][hwaddr][:time] = Time.now
|
12
|
+
end
|
13
|
+
def query(hwaddr, type)
|
14
|
+
begin
|
15
|
+
@cache[type][hwaddr][:data] ? @cache[type][hwaddr] : false
|
16
|
+
rescue
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def flush
|
21
|
+
@cache.clear
|
22
|
+
end
|
23
|
+
def purge(sec)
|
24
|
+
@cache.each do |type, data|
|
25
|
+
data.each_key do |hwaddr|
|
26
|
+
@cache[type].delete hwaddr if (Time.now - @cache[type][hwaddr][:time]) >= sec
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'yaml'
|
3
|
+
require 'log4r'
|
4
|
+
|
5
|
+
module Blondy
|
6
|
+
module DHCPD
|
7
|
+
# Set default config
|
8
|
+
default_config = '/etc/blondy/dhcpd.yml'
|
9
|
+
config_file = default_config
|
10
|
+
|
11
|
+
# Read command line options
|
12
|
+
@options = Hash.new
|
13
|
+
OptionParser.new do |opts|
|
14
|
+
opts.banner = 'Usage: dhcpd.rb [options]'
|
15
|
+
opts.on('-d', '--debug', 'Run foreground for debug') { @options[:debug] = true }
|
16
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
17
|
+
puts opts
|
18
|
+
exit 0
|
19
|
+
end
|
20
|
+
end.parse!
|
21
|
+
|
22
|
+
# Load config from file
|
23
|
+
begin
|
24
|
+
config_file = "#{ENV['BLONDY_CONFIGPATH']}/dhcpd.yml" if ENV['BLONDY_CONFIGPATH']
|
25
|
+
CONFIG = YAML::load(File.open(config_file))
|
26
|
+
rescue
|
27
|
+
STDERR.puts "No config file. \nPlease check that #{default_config} exist or BLONDY_CONFIGPATH is set."
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
# Check for client key
|
32
|
+
unless CONFIG['client_key']
|
33
|
+
Logger.error 'You should set client_key.'
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
# Check for master address
|
37
|
+
unless /^http(s)?:\/\/.*/ =~ CONFIG['master']
|
38
|
+
Logger.error 'You should set master server.'
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
# Set logging to file
|
43
|
+
if CONFIG['log_path']
|
44
|
+
begin
|
45
|
+
if /^\// =~ CONFIG['log_path']
|
46
|
+
log_path = CONFIG['log_path'].gsub(/\/*$/,'')
|
47
|
+
format = Log4r::PatternFormatter.new(:pattern => "[%l] [%d] %m")
|
48
|
+
Logger.outputters << Log4r::FileOutputter.new('blondy-dhcpd.log', filename: "#{log_path}/blondy-dhcpd.log" , formatter: format)
|
49
|
+
end
|
50
|
+
Logger.level = ((1..7).include?(CONFIG['log_level']) ? CONFIG['log_level'] : 1)
|
51
|
+
rescue
|
52
|
+
Logger.error 'Error while open log file.'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,26 +1,97 @@
|
|
1
1
|
require 'net-dhcp'
|
2
2
|
require 'ostruct'
|
3
3
|
require 'ipaddr'
|
4
|
+
require_relative 'pool'
|
4
5
|
|
5
6
|
module Blondy
|
6
7
|
module DHCPD
|
7
8
|
class Dispatcher
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
class << self
|
10
|
+
def dispatch(data, ip, port)
|
11
|
+
@data = DHCP::Message.from_udp_payload(data) rescue raise(IncorrectMessage, 'Incorrect message received.')
|
12
|
+
raise(IncorrectMessage, 'Incorrect message received.') unless @data
|
13
|
+
set_hwaddr
|
14
|
+
reply
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def reply
|
20
|
+
msg_class = @data.class.to_s.gsub(/^.*::/, '').downcase
|
21
|
+
handle(msg_class)
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle(msg_class)
|
25
|
+
if %w{discover request inform release}.include?(msg_class)
|
26
|
+
@pool = Pool.query(@data.hwaddr, msg_class.to_sym)
|
27
|
+
@pool ? call("#{msg_class}_handler".to_sym) : false
|
15
28
|
else
|
16
|
-
|
17
|
-
reply.port = 67
|
29
|
+
raise NoMessageHandler, 'No appropriate handler for message.'
|
18
30
|
end
|
19
|
-
reply.data = DHCP::Offer.new
|
20
31
|
end
|
21
|
-
|
22
|
-
|
32
|
+
|
33
|
+
def call(method)
|
34
|
+
@reply = OpenStruct.new
|
35
|
+
send(method)
|
36
|
+
end
|
37
|
+
|
38
|
+
def discover_handler
|
39
|
+
@reply.data = DHCP::Offer.new
|
40
|
+
create_reply
|
41
|
+
end
|
42
|
+
|
43
|
+
def request_handler
|
44
|
+
@reply.data = DHCP::ACK.new
|
45
|
+
create_reply
|
46
|
+
end
|
47
|
+
|
48
|
+
def release_handler
|
49
|
+
@reply.data = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def inform_handler
|
53
|
+
@reply.data = DHCP::ACK.new
|
54
|
+
create_reply
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_reply
|
58
|
+
set_src
|
59
|
+
set_pool_data
|
60
|
+
set_other
|
61
|
+
@reply
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_hwaddr
|
65
|
+
DHCP::Message.class_eval {attr_accessor :hwaddr}
|
66
|
+
@data.hwaddr = @data.chaddr.take(@data.hlen).map {|x| x.to_s(16).size<2 ? '0'+x.to_s(16) : x.to_s(16)}.join(':')
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_src
|
70
|
+
if @data.giaddr == 0 and @data.ciaddr != 0
|
71
|
+
@reply.ip = IPAddr.new(@data.ciaddr, family = Socket::AF_INET).to_s
|
72
|
+
@reply.port = 68
|
73
|
+
elsif @data.giaddr != 0
|
74
|
+
@reply.ip = IPAddr.new(@data.giaddr, family = Socket::AF_INET).to_s
|
75
|
+
@reply.port = 67
|
76
|
+
else
|
77
|
+
@reply.ip = '255.255.255.255'
|
78
|
+
@reply.port = 68
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_pool_data
|
83
|
+
@reply.data.yiaddr = @pool.data.yiaddr
|
84
|
+
@reply.data.fname = @pool.data.fname
|
85
|
+
@reply.data.options = @pool.data.options
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_other
|
89
|
+
@reply.data.siaddr = IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
|
90
|
+
@reply.data.xid = @data.xid if @data.xid
|
91
|
+
end
|
23
92
|
end
|
24
93
|
end
|
25
94
|
end
|
26
95
|
end
|
96
|
+
|
97
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'em-http'
|
2
|
+
require 'json'
|
3
|
+
require 'ipaddr'
|
4
|
+
require_relative 'reply'
|
5
|
+
require_relative 'cache'
|
6
|
+
|
7
|
+
module Blondy
|
8
|
+
module DHCPD
|
9
|
+
class Pool
|
10
|
+
class << self
|
11
|
+
def query(hwaddr, type)
|
12
|
+
reply = Cache.query(hwaddr,type)
|
13
|
+
if reply
|
14
|
+
reply[:data]
|
15
|
+
else
|
16
|
+
http = EM::HttpRequest.new(Blondy::DHCPD::CONFIG['master']).
|
17
|
+
get(head: {'x-blondy-key' => Blondy::DHCPD::CONFIG['client_key']}, query: {'type' => type.to_s, 'hwaddr' => hwaddr})
|
18
|
+
http.callback do
|
19
|
+
if http.response_header.status != 200
|
20
|
+
Logger.error "Remote server reply with #{http.response_header.status} error code."
|
21
|
+
else
|
22
|
+
data = transform(http.response, type)
|
23
|
+
Cache.add(hwaddr,type, data) if data
|
24
|
+
end
|
25
|
+
data
|
26
|
+
end
|
27
|
+
http.errback do
|
28
|
+
Logger.error 'Remote pool server is unavailable.'
|
29
|
+
end
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def transform(json, type)
|
37
|
+
begin
|
38
|
+
data = JSON.parse(json)
|
39
|
+
if type == :discover
|
40
|
+
reply_type = $DHCP_MSG_OFFER
|
41
|
+
elsif type == :request
|
42
|
+
reply_type = $DHCP_MSG_ACK
|
43
|
+
else
|
44
|
+
raise UnsupportedReqType
|
45
|
+
end
|
46
|
+
Reply.new(data, reply_type).get
|
47
|
+
rescue UnsupportedReqType
|
48
|
+
# Unsupported request type
|
49
|
+
Logger.error 'Unsupported type received.'
|
50
|
+
false
|
51
|
+
rescue JSON::ParserError
|
52
|
+
# Wrong json
|
53
|
+
Logger.error 'Remote server send invalid json.'
|
54
|
+
false
|
55
|
+
rescue NoMethodError
|
56
|
+
Logger.error 'Remote server send invalid text data in json.'
|
57
|
+
# Wrong data in json
|
58
|
+
false
|
59
|
+
rescue IPAddr::AddressFamilyError
|
60
|
+
# Wrong data in json (address family must be specified)
|
61
|
+
Logger.error 'Remote server send invalid ip or nemask in json.'
|
62
|
+
false
|
63
|
+
rescue IPAddr::InvalidAddressError
|
64
|
+
# Wrong data in json (invalid address)
|
65
|
+
Logger.error 'Remote server send invalid ip or nemask in json.'
|
66
|
+
false
|
67
|
+
rescue
|
68
|
+
# Unknown error
|
69
|
+
Logger.error 'Unknown error.'
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
class UnsupportedReqType < StandardError
|
78
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'net-dhcp'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'ipaddr'
|
4
|
+
|
5
|
+
module Blondy
|
6
|
+
module DHCPD
|
7
|
+
class Reply
|
8
|
+
def initialize(data, reply_type)
|
9
|
+
@result = OpenStruct.new
|
10
|
+
@result.data = OpenStruct.new
|
11
|
+
@data = data
|
12
|
+
@reply_type = reply_type
|
13
|
+
@result.data.fname = data['fname'].unpack('C128').map {|x| x ? x : 0}
|
14
|
+
@result.data.yiaddr = IPAddr.new(data['yiaddr']).to_i
|
15
|
+
@result.data.options = [
|
16
|
+
DHCP::MessageTypeOption.new({payload: [@reply_type]}),
|
17
|
+
DHCP::ServerIdentifierOption.new({payload: array_from(Blondy::DHCPD::CONFIG['server_ip'])}),
|
18
|
+
DHCP::DomainNameOption.new({payload: data['domain'].unpack('C*')}),
|
19
|
+
DHCP::DomainNameServerOption.new({payload: array_from(data['dns'])}),
|
20
|
+
DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
|
21
|
+
DHCP::SubnetMaskOption.new({payload: array_from(data['netmask'])}),
|
22
|
+
DHCP::RouterOption.new({payload: array_from(data['gw'])})
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
def get
|
27
|
+
@result
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def array_from(ip)
|
33
|
+
ip.split('.').map {|octet| octet.to_i} if IPAddr.new(ip)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/blondy/dhcpd/server.rb
CHANGED
@@ -1,18 +1,51 @@
|
|
1
1
|
require 'net-dhcp'
|
2
2
|
require 'eventmachine'
|
3
3
|
require 'socket'
|
4
|
+
require 'log4r'
|
5
|
+
require_relative 'dispatcher'
|
4
6
|
|
5
7
|
module Blondy
|
6
8
|
module DHCPD
|
7
9
|
# Main class for handling connections
|
8
10
|
class Server < EM::Connection
|
11
|
+
def initialize
|
12
|
+
@buffer = String.new
|
13
|
+
super
|
14
|
+
end
|
9
15
|
# Fires up Dispatcher and send reply back by callback
|
10
16
|
def receive_data(data)
|
17
|
+
@buffer.clear if (@buffer.size + data.size) > 1000
|
18
|
+
@buffer += data
|
19
|
+
|
20
|
+
if @buffer.unpack('C4Nn2N4C16C192NC*').include?($DHCP_MAGIC)
|
21
|
+
process_message(@buffer.dup)
|
22
|
+
@buffer.clear
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def process_message(buffer)
|
11
29
|
ip, port = Socket.unpack_sockaddr_in(get_peername)
|
12
|
-
action = proc
|
13
|
-
|
30
|
+
action = proc do
|
31
|
+
begin
|
32
|
+
Dispatcher.dispatch(buffer, ip, port)
|
33
|
+
rescue NoMessageHandler
|
34
|
+
Logger.warn 'No handler for message found. Ignore.'
|
35
|
+
false
|
36
|
+
rescue IncorrectMessage
|
37
|
+
Logger.warn 'Incorrect message received. Ignore.'
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
callback = proc { |reply| send_datagram(reply.data.pack, reply.ip, reply.port) if reply && reply.data }
|
14
42
|
EM.defer(action,callback)
|
15
43
|
end
|
16
44
|
end
|
17
45
|
end
|
18
46
|
end
|
47
|
+
|
48
|
+
class NoMessageHandler < StandardError
|
49
|
+
end
|
50
|
+
class IncorrectMessage < StandardError
|
51
|
+
end
|
data/lib/blondy/dhcpd/version.rb
CHANGED
data/spec/cache_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Blondy
|
4
|
+
module DHCPD
|
5
|
+
describe 'Cache' do
|
6
|
+
subject(:cache) {Cache}
|
7
|
+
let(:data) do
|
8
|
+
pool_query_result = OpenStruct.new
|
9
|
+
pool_query_result.data = OpenStruct.new
|
10
|
+
pool_query_result.data.fname = 'test.txt'.unpack('C128').map {|x| x ? x : 0}
|
11
|
+
pool_query_result.data.yiaddr = IPAddr.new('192.168.5.150').to_i
|
12
|
+
pool_query_result.data.options = [
|
13
|
+
DHCP::MessageTypeOption.new({payload: [$DHCP_MSG_OFFER]}),
|
14
|
+
DHCP::ServerIdentifierOption.new({payload: Blondy::DHCPD::CONFIG['server_ip'].split('.').map {|octet| octet.to_i}}),
|
15
|
+
DHCP::DomainNameOption.new({payload: 'example.com'.unpack('C*')}),
|
16
|
+
DHCP::DomainNameServerOption.new({payload: [8,8,8,8]}),
|
17
|
+
DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
|
18
|
+
DHCP::SubnetMaskOption.new({payload: [255, 255, 255, 255]}),
|
19
|
+
DHCP::RouterOption.new({payload: [192, 168, 1, 3]})
|
20
|
+
]
|
21
|
+
pool_query_result
|
22
|
+
end
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
cache.flush
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'add host to cache' do
|
29
|
+
cache.query('11:11:11:11:11:11', :discover).should == false
|
30
|
+
cache.add('11:11:11:11:11:11', :discover, data)
|
31
|
+
cache.query('11:11:11:11:11:11', :discover)[:data].should == data
|
32
|
+
end
|
33
|
+
it 'flush cache' do
|
34
|
+
cache.add('11:11:11:11:11:11', :discover, data)
|
35
|
+
cache.add('12:11:11:11:11:11', :discover, data)
|
36
|
+
cache.flush
|
37
|
+
cache.query('11:11:11:11:11:11', :discover).should == false
|
38
|
+
cache.query('12:11:11:11:11:11', :discover).should == false
|
39
|
+
end
|
40
|
+
it 'delete entries older than N' do
|
41
|
+
@time = Time.now
|
42
|
+
Time.stub(:now) {@time}
|
43
|
+
cache.add('11:11:11:11:11:11', :discover, data)
|
44
|
+
Time.stub(:now) {@time+5}
|
45
|
+
cache.purge(3)
|
46
|
+
cache.query('11:11:11:11:11:11', :discover).should == false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|