blondy-dhcpd 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/blondy-dhcpd.png)](http://badge.fury.io/rb/net-dhcp) [![Build Status](https://travis-ci.org/presto53/blondy-dhcpd.png)](https://travis-ci.org/presto53/blondy-dhcpd)
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/blondy-dhcpd.png)](http://badge.fury.io/rb/net-dhcp) [![Build Status](https://travis-ci.org/presto53/blondy-dhcpd.png)](https://travis-ci.org/presto53/blondy-dhcpd) [![Code Climate](https://codeclimate.com/repos/52eb8ff6e30ba06ec2002a03/badges/132cfa29229385341bee/gpa.png)](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
|