Class: DHCP::Server
- Inherits:
-
Object
- Object
- DHCP::Server
- Defined in:
- lib/dhcp/server.rb
Constant Summary collapse
- ZERO_IP =
IPAddress('0.0.0.0')
Instance Method Summary collapse
-
#dispatch_packet(data, source_ip, source_port) ⇒ Object
Hand off raw UDP packet data here for parsing and dispatch:.
-
#handle_decline(packet, source_ip, relay) ⇒ Object
Handle DHCPDECLINE packet:.
-
#handle_discover(packet, source_ip, relay) ⇒ Object
Handle DHCPDISCOVER packet:.
-
#handle_inform(packet, source_ip, relay) ⇒ Object
Handle DHCPINFORM packet:.
-
#handle_leasequery(packet, source_ip, relay) ⇒ Object
Handle DHCPLEASEQUERY packet:.
-
#handle_request(packet, source_ip, relay) ⇒ Object
Handle DHCPREQUEST packet:.
-
#initialize(opt = {}) ⇒ Server
constructor
A new instance of Server.
- #relay_authorized?(source_ip, giaddr) ⇒ Boolean
-
#run ⇒ Object
Main server event loop (blocking):.
-
#run_once ⇒ Object
Main server event single-iteration function (non-blocking):.
- #show_packet(pk) ⇒ Object
Constructor Details
#initialize(opt = {}) ⇒ Server
Returns a new instance of Server.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/dhcp/server.rb', line 12 def initialize(opt={}) @interval = opt[:interval] || 0.5 ## Sleep (seconds) if no data @log = opt[:log] || Syslog ## Logging object (should be open already) @server_ip = opt[:ip] || '0.0.0.0' ## Listen on this IP @server_port = opt[:port] || 67 ## Listen on this UDP port @debug = opt[:debug] || false ## Bind to UDP server port: @log.info("Starting DHCP on [#{@server_ip}]:#{@server_port} server at #{Time.now}") @sock = UDPSocket.new @sock.do_not_reverse_lookup = true @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true) ## Permit sending to broadcast address unless @sock.bind(@server_ip, 67) raise "Failed to bind" end end |
Instance Method Details
#dispatch_packet(data, source_ip, source_port) ⇒ Object
Hand off raw UDP packet data here for parsing and dispatch:
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 107 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 |
# File 'lib/dhcp/server.rb', line 65 def dispatch_packet(data, source_ip, source_port) now = Time.now @log.debug("Packet (#{data.size} bytes) from [#{source_ip}]:#{source_port} received at #{now}") if data.size < 300 @log.debug("Ignoring small packet (less than BOOTP minimum size.") if @debug return end packet = nil begin packet = DHCP::Packet.new(data) rescue => e show_packet(packet) if @debug @log.debug("Error parsing DHCP packet.") if @debug return end relay = nil if source_port == 67 ## DHCP relay via an intermediary relay = true ## Quick relay sanity-check on GIADDR: if packet.giaddr == ZERO_IP @log.debug("Packet from relay (port 67) has no GIADDR address set. Ignoring.") if @debug return end unless (source_ip, packet.giaddr) @log.debug("Ignoring DHCP packet from unauthorized relay [#{source_ip}].") if @debug return end elsif source_port == 68 ## DHCP on directly attached subnet relay = false ## Quick relay sanity-check on GIADDR: if packet.giaddr != ZERO_IP @log.debug("Direct (non-relay) packet has set GIADDR to [#{packet.giaddr}] in violation of RFC. Ignoring.") if @debug return end else @log.debug("Ignoring packet from UDP port other than 67 (relay) or 68 (direct)") if @debug return end ## Ethernet hardware type sanity check: if packet.htype != DHCP::HTYPE[:htype_10mb_ethernet][0] || packet.hlen != DHCP::HTYPE[:htype_10mb_ethernet][1] @log.debug("Request hardware type or length doesn't match ETHERNET type and length. Ignoring.") if @debug return end if packet.op != DHCP::BOOTREQUEST @log.debug("Recived a non-BOOTREQUEST packet. Ignoring.") if @debug return end ## Dispatch packet: case packet.type when DHCP::DHCPDISCOVER handle_discover(packet, source_ip, relay) when DHCP::DHCPREQUEST handle_request(packet, source_ip, relay) when DHCP::DHCPINFORM handle_inform(packet, source_ip, relay) when DHCP::DHCPRELEASE handle_release(packet, source_ip, relay) when DHCP::DHCPDECLINE handle_decline(packet, source_ip, relay) when DHCP::DHCPLEASEQUERY handle_leasequery(packet, source_ip, relay) when DHCP::DHCPOFFER, DHCP::DHCPACK, DHCP::DHCPNAK, DHCP::DHCPFORCERENEW, DHCP::DHCPLEASEUNASSIGNED, DHCP::DHCPLEASEACTIVE, DHCP::DHCPLEASEUNKNOWN show_packet(packet) if @debug @log.debug("Packet type #{packet.type_name} in a BOOTREQUEST is invalid.") if @debug else show_packet(packet) if @debug @log.debug("Invalid, unknown, or unhandled DHCP packet type received.") if @debug end end |
#handle_decline(packet, source_ip, relay) ⇒ Object
Handle DHCPDECLINE packet:
166 167 168 169 |
# File 'lib/dhcp/server.rb', line 166 def handle_decline(packet, source_ip, relay) show_packet(packet) if @debug @log.debug("handle_decline") if @debug end |
#handle_discover(packet, source_ip, relay) ⇒ Object
Handle DHCPDISCOVER packet:
148 149 150 151 |
# File 'lib/dhcp/server.rb', line 148 def handle_discover(packet, source_ip, relay) show_packet(packet) if @debug @log.debug("handle_discover") if @debug end |
#handle_inform(packet, source_ip, relay) ⇒ Object
Handle DHCPINFORM packet:
160 161 162 163 |
# File 'lib/dhcp/server.rb', line 160 def handle_inform(packet, source_ip, relay) show_packet(packet) if @debug @log.debug("handle_inform") if @debug end |
#handle_leasequery(packet, source_ip, relay) ⇒ Object
Handle DHCPLEASEQUERY packet:
172 173 174 175 |
# File 'lib/dhcp/server.rb', line 172 def handle_leasequery(packet, source_ip, relay) show_packet(packet) if @debug @log.debug("handle_leasequery") if @debug end |
#handle_request(packet, source_ip, relay) ⇒ Object
Handle DHCPREQUEST packet:
154 155 156 157 |
# File 'lib/dhcp/server.rb', line 154 def handle_request(packet, source_ip, relay) show_packet(packet) if @debug @log.debug("handle_request") if @debug end |
#relay_authorized?(source_ip, giaddr) ⇒ Boolean
143 144 145 |
# File 'lib/dhcp/server.rb', line 143 def (source_ip, giaddr) true end |
#run ⇒ Object
Main server event loop (blocking):
49 50 51 52 53 54 |
# File 'lib/dhcp/server.rb', line 49 def run loop do result = run_once sleep @interval if !result ## Sleep if no data was received and no errors occured end end |
#run_once ⇒ Object
Main server event single-iteration function (non-blocking):
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/dhcp/server.rb', line 30 def run_once r,w,e = IO.select([@sock], nil, [@sock], 0) if !r.nil? && r.size == 1 data, src = @sock.recvfrom_nonblock(1500) if data.bytesize < 300 @log.debug("Ignoring packet smaller than BOOTP minimum size") if @debug else dispatch_packet(data, src[3], src[1]) end return true end if !e.nil? && e.size == 1 ## TODO: Handle errors... raise "Unhandled error on socket" end return false end |
#show_packet(pk) ⇒ Object
56 57 58 59 60 61 62 |
# File 'lib/dhcp/server.rb', line 56 def show_packet(pk) @log.debug(">>> PACKET: #{pk.type} '#{pk.type_name}' at #{Time.now} >>>") pk.to_s.gsub(/\\/,'\\\\').gsub(/[^\x20-\x7e\n]/){|x| '\x' + x.unpack('H2')[0].upcase}.split("\n").each do |i| @log.debug("..." + i) end @log.debug("<<< END OF PACKET <<<") end |