4 import pydhcplib.dhcp_network
5 from pydhcplib.dhcp_packet import *
6 from pydhcplib.type_hw_addr import hwmac
7 from pydhcplib.type_ipv4 import ipv4
8 from pydhcplib.type_strlist import strlist
15 from invirt import database
16 from invirt.config import structs as config
18 dhcp_options = {'subnet_mask': config.dhcp.netmask,
19 'router': config.dhcp.gateway,
20 'domain_name_server': ','.join(config.dhcp.dns),
21 'ip_address_lease_time': 60*60*24}
26 def findNIC(self, mac):
27 database.clear_cache()
28 return database.NIC.query().filter_by(mac_addr=mac).first()
29 def find_interface(self, packet):
30 chaddr = hwmac(packet.GetHardwareAddress())
31 nic = self.findNIC(str(chaddr))
32 if nic is None or nic.ip is None:
34 ipstr = ''.join(reversed(['%02X' % i for i in ipv4(nic.ip).list()]))
35 for line in open('/proc/net/route'):
38 s.syslog(s.LOG_DEBUG, "find_interface found "+str(nic.ip)+" on "+parts[0])
42 def getParameters(self, **extra):
43 all_options=dict(dhcp_options)
44 all_options.update(extra)
46 for parameter, value in all_options.iteritems():
49 option_type = DhcpOptionsTypes[DhcpOptions[parameter]]
51 if option_type == "ipv4" :
52 # this is a single ip address
53 options[parameter] = map(int,value.split("."))
54 elif option_type == "ipv4+" :
55 # this is multiple ip address
56 iplist = value.split(",")
58 for single in iplist :
59 opt.extend(ipv4(single).list())
60 options[parameter] = opt
61 elif option_type == "32-bits" :
62 # This is probably a number...
64 options[parameter] = [digit>>24&0xFF,(digit>>16)&0xFF,(digit>>8)&0xFF,digit&0xFF]
65 elif option_type == "16-bits" :
67 options[parameter] = [(digit>>8)&0xFF,digit&0xFF]
69 elif option_type == "char" :
71 options[parameter] = [digit&0xFF]
73 elif option_type == "bool" :
74 if value=="False" or value=="false" or value==0 :
75 options[parameter] = [0]
76 else : options[parameter] = [1]
78 elif option_type == "string" :
79 options[parameter] = strlist(value).list()
81 elif option_type == "RFC3397" :
84 components = item.split('.')
85 item_fmt = "".join(chr(len(elt)) + elt for elt in components) + "\x00"
86 parsed_value += item_fmt
88 options[parameter] = strlist(parsed_value).list()
91 options[parameter] = strlist(value).list()
94 def Discover(self, packet):
95 s.syslog(s.LOG_DEBUG, "dhcp_backend : Discover ")
96 chaddr = hwmac(packet.GetHardwareAddress())
97 nic = self.findNIC(str(chaddr))
98 if nic is None or nic.machine is None:
101 if ip is None: #Deactivated?
105 if nic.hostname and '.' in nic.hostname:
106 options['host_name'], options['domain_name'] = nic.hostname.split('.', 1)
107 elif nic.machine.name:
108 options['host_name'] = nic.machine.name
109 options['domain_name'] = config.dns.domains[0]
112 if DhcpOptions['domain_search'] in packet.GetOption('parameter_request_list'):
113 options['host_name'] += '.' + options['domain_name']
114 del options['domain_name']
115 options['domain_search'] = [config.dhcp.search_domain]
118 s.syslog(s.LOG_DEBUG,"dhcp_backend : Discover result = "+str(ip))
119 packet_parameters = self.getParameters(**options)
121 # FIXME: Other offer parameters go here
122 packet_parameters["yiaddr"] = ip.list()
124 packet.SetMultipleOptions(packet_parameters)
128 def Request(self, packet):
129 s.syslog(s.LOG_DEBUG, "dhcp_backend : Request")
131 discover = self.Discover(packet)
133 chaddr = hwmac(packet.GetHardwareAddress())
134 request = packet.GetOption("request_ip_address")
136 request = packet.GetOption("ciaddr")
137 yiaddr = packet.GetOption("yiaddr")
140 s.syslog(s.LOG_INFO,"Unknown MAC address: "+str(chaddr))
143 if yiaddr!="0.0.0.0" and yiaddr == request :
144 s.syslog(s.LOG_INFO,"Ack ip "+str(yiaddr)+" for "+str(chaddr))
147 s.syslog(s.LOG_INFO,"Requested ip "+str(request)+" not available for "+str(chaddr))
150 def Decline(self, packet):
152 def Release(self, packet):
156 class DhcpServer(pydhcplib.dhcp_network.DhcpServer):
157 def __init__(self, backend, options = {'client_listenport':68,'server_listenport':67}):
158 pydhcplib.dhcp_network.DhcpServer.__init__(self,"0.0.0.0",options["client_listen_port"],options["server_listen_port"],)
159 self.backend = backend
160 s.syslog(s.LOG_DEBUG, "__init__ DhcpServer")
162 def SendDhcpPacketTo(self, To, packet):
163 intf = self.backend.find_interface(packet)
165 self.dhcp_socket.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, intf)
166 ret = self.dhcp_socket.sendto(packet.EncodePacket(), (To,self.emit_port))
167 self.dhcp_socket.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, '')
170 return self.dhcp_socket.sendto(packet.EncodePacket(),(To,self.emit_port))
172 def SendPacket(self, packet):
173 """Encode and send the packet."""
175 giaddr = packet.GetOption('giaddr')
177 # in all case, if giaddr is set, send packet to relay_agent
178 # network address defines by giaddr
179 if giaddr!=[0,0,0,0] :
180 agent_ip = ".".join(map(str,giaddr))
181 self.SendDhcpPacketTo(agent_ip,packet)
182 s.syslog(s.LOG_DEBUG, "SendPacket to agent : "+agent_ip)
184 # FIXME: This shouldn't broadcast if it has an IP address to send
185 # it to instead. See RFC2131 part 4.1 for full details
187 s.syslog(s.LOG_DEBUG, "No agent, broadcast packet.")
188 self.SendDhcpPacketTo("255.255.255.255",packet)
191 def HandleDhcpDiscover(self, packet):
192 """Build and send DHCPOFFER packet in response to DHCPDISCOVER
195 logmsg = "Get DHCPDISCOVER packet from " + hwmac(packet.GetHardwareAddress()).str()
197 s.syslog(s.LOG_INFO, logmsg)
199 offer.CreateDhcpOfferPacketFrom(packet)
201 if self.backend.Discover(offer):
202 self.SendPacket(offer)
203 # FIXME : what if false ?
206 def HandleDhcpRequest(self, packet):
207 """Build and send DHCPACK or DHCPNACK packet in response to
208 DHCPREQUEST packet. 4 types of DHCPREQUEST exists."""
210 ip = packet.GetOption("request_ip_address")
211 sid = packet.GetOption("server_identifier")
212 ciaddr = packet.GetOption("ciaddr")
213 #packet.PrintHeaders()
214 #packet.PrintOptions()
216 if sid != [0,0,0,0] and ciaddr == [0,0,0,0] :
217 s.syslog(s.LOG_INFO, "Get DHCPREQUEST_SELECTING_STATE packet")
219 elif sid == [0,0,0,0] and ciaddr == [0,0,0,0] and ip :
220 s.syslog(s.LOG_INFO, "Get DHCPREQUEST_INITREBOOT_STATE packet")
222 elif sid == [0,0,0,0] and ciaddr != [0,0,0,0] and not ip :
223 s.syslog(s.LOG_INFO,"Get DHCPREQUEST_INITREBOOT_STATE packet")
225 else : s.syslog(s.LOG_INFO,"Get DHCPREQUEST_UNKNOWN_STATE packet : not implemented")
227 if self.backend.Request(packet):
228 packet.TransformToDhcpAckPacket()
229 self.SendPacket(packet)
230 elif self.backend.Discover(packet):
231 packet.TransformToDhcpNackPacket()
232 self.SendPacket(packet)
234 pass # We aren't authoritative, so don't reply if we don't know them.
236 # FIXME: These are not yet implemented.
237 def HandleDhcpDecline(self, packet):
238 s.syslog(s.LOG_INFO, "Get DHCPDECLINE packet")
239 self.backend.Decline(packet)
241 def HandleDhcpRelease(self, packet):
242 s.syslog(s.LOG_INFO,"Get DHCPRELEASE packet")
243 self.backend.Release(packet)
245 def HandleDhcpInform(self, packet):
246 s.syslog(s.LOG_INFO, "Get DHCPINFORM packet")
248 if self.backend.Request(packet) :
249 packet.TransformToDhcpAckPacket()
250 # FIXME : Remove lease_time from options
251 self.SendPacket(packet)
253 # FIXME : what if false ?
255 if '__main__' == __name__:
256 options = { "server_listen_port":67,
257 "client_listen_port":68,
258 "listen_address":"0.0.0.0"}
260 myip = socket.gethostbyname(socket.gethostname())
262 print "invirt-dhcpserver: cannot determine local IP address by looking up %s" % socket.gethostname()
265 dhcp_options['server_identifier'] = myip
267 backend = DhcpBackend()
268 server = DhcpServer(backend, options)
270 while True : server.GetNextDhcpPacket()