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 = {'domain_name_server': ','.join(config.dhcp.dns),
19 'ip_address_lease_time': config.dhcp.leasetime if config.dhcp.has_key('leasetime') else 60*60*24}
24 def findNIC(self, mac):
25 database.clear_cache()
26 return database.NIC.query.filter_by(mac_addr=mac).first()
27 def find_interface(self, packet):
28 chaddr = hwmac(packet.GetHardwareAddress())
29 nic = self.findNIC(str(chaddr))
30 if nic is None or nic.ip is None:
32 ipstr = ''.join(reversed(['%02X' % i for i in ipv4(nic.ip.encode("utf-8")).list()]))
33 for line in open('/proc/net/route'):
36 s.syslog(s.LOG_DEBUG, "find_interface found "+str(nic.ip)+" on "+parts[0])
40 def getParameters(self, **extra):
41 all_options=dict(dhcp_options)
42 all_options.update(extra)
44 for parameter, value in all_options.iteritems():
47 option_type = DhcpOptionsTypes[DhcpOptions[parameter]]
49 if option_type == "ipv4" :
50 # this is a single ip address
51 options[parameter] = map(int,value.split("."))
52 elif option_type == "ipv4+" :
53 # this is multiple ip address
54 iplist = value.split(",")
56 for single in iplist :
57 opt.extend(ipv4(single).list())
58 options[parameter] = opt
59 elif option_type == "32-bits" :
60 # This is probably a number...
62 options[parameter] = [digit>>24&0xFF,(digit>>16)&0xFF,(digit>>8)&0xFF,digit&0xFF]
63 elif option_type == "16-bits" :
65 options[parameter] = [(digit>>8)&0xFF,digit&0xFF]
67 elif option_type == "char" :
69 options[parameter] = [digit&0xFF]
71 elif option_type == "bool" :
72 if value=="False" or value=="false" or value==0 :
73 options[parameter] = [0]
74 else : options[parameter] = [1]
76 elif option_type == "string" :
77 options[parameter] = strlist(value).list()
79 elif option_type == "RFC3397" :
82 components = item.split('.')
83 item_fmt = "".join(chr(len(elt)) + elt for elt in components) + "\x00"
84 parsed_value += item_fmt
86 options[parameter] = strlist(parsed_value).list()
89 options[parameter] = strlist(value).list()
92 def Discover(self, packet):
93 s.syslog(s.LOG_DEBUG, "dhcp_backend : Discover ")
94 chaddr = hwmac(packet.GetHardwareAddress())
95 nic = self.findNIC(str(chaddr))
96 if nic is None or nic.machine is None:
98 ip = nic.ip.encode("utf-8")
99 if ip is None: #Deactivated?
103 options['subnet_mask'] = nic.netmask.encode("utf-8")
104 options['router'] = nic.gateway.encode("utf-8")
105 if nic.hostname and '.' in nic.hostname:
106 options['host_name'], options['domain_name'] = nic.hostname.encode('utf-8').split('.', 1)
107 elif nic.machine.name:
108 options['host_name'] = nic.machine.name.encode('utf-8')
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]
117 s.syslog(s.LOG_DEBUG,"dhcp_backend : Discover result = "+str(ip))
118 packet_parameters = self.getParameters(**options)
120 # FIXME: Other offer parameters go here
121 packet_parameters["yiaddr"] = ip.list()
123 packet.SetMultipleOptions(packet_parameters)
126 def Request(self, packet):
127 s.syslog(s.LOG_DEBUG, "dhcp_backend : Request")
129 discover = self.Discover(packet)
131 chaddr = hwmac(packet.GetHardwareAddress())
132 request = packet.GetOption("request_ip_address")
134 request = packet.GetOption("ciaddr")
135 yiaddr = packet.GetOption("yiaddr")
138 s.syslog(s.LOG_INFO,"Unknown MAC address: "+str(chaddr))
141 if yiaddr!="0.0.0.0" and yiaddr == request :
142 s.syslog(s.LOG_INFO,"Ack ip "+str(yiaddr)+" for "+str(chaddr))
145 s.syslog(s.LOG_INFO,"Requested ip "+str(request)+" not available for "+str(chaddr))
148 def Decline(self, packet):
150 def Release(self, packet):
154 class DhcpServer(pydhcplib.dhcp_network.DhcpServer):
155 def __init__(self, backend, options = {'client_listenport':68,'server_listenport':67}):
156 pydhcplib.dhcp_network.DhcpServer.__init__(self,"0.0.0.0",options["client_listen_port"],options["server_listen_port"],)
157 self.backend = backend
158 s.syslog(s.LOG_DEBUG, "__init__ DhcpServer")
160 def SendDhcpPacketTo(self, To, packet):
161 intf = self.backend.find_interface(packet)
163 self.dhcp_socket.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, intf)
164 ret = self.dhcp_socket.sendto(packet.EncodePacket(), (To,self.emit_port))
165 self.dhcp_socket.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, '')
168 return self.dhcp_socket.sendto(packet.EncodePacket(),(To,self.emit_port))
170 def SendPacket(self, packet):
171 """Encode and send the packet."""
173 giaddr = packet.GetOption('giaddr')
175 # in all case, if giaddr is set, send packet to relay_agent
176 # network address defines by giaddr
177 if giaddr!=[0,0,0,0] :
178 agent_ip = ".".join(map(str,giaddr))
179 self.SendDhcpPacketTo(agent_ip,packet)
180 s.syslog(s.LOG_DEBUG, "SendPacket to agent : "+agent_ip)
182 # FIXME: This shouldn't broadcast if it has an IP address to send
183 # it to instead. See RFC2131 part 4.1 for full details
185 s.syslog(s.LOG_DEBUG, "No agent, broadcast packet.")
186 self.SendDhcpPacketTo("255.255.255.255",packet)
189 def HandleDhcpDiscover(self, packet):
190 """Build and send DHCPOFFER packet in response to DHCPDISCOVER
193 logmsg = "Get DHCPDISCOVER packet from " + hwmac(packet.GetHardwareAddress()).str()
195 s.syslog(s.LOG_INFO, logmsg)
197 offer.CreateDhcpOfferPacketFrom(packet)
199 if self.backend.Discover(offer):
200 self.SendPacket(offer)
201 # FIXME : what if false ?
204 def HandleDhcpRequest(self, packet):
205 """Build and send DHCPACK or DHCPNACK packet in response to
206 DHCPREQUEST packet. 4 types of DHCPREQUEST exists."""
208 ip = packet.GetOption("request_ip_address")
209 sid = packet.GetOption("server_identifier")
210 ciaddr = packet.GetOption("ciaddr")
211 #packet.PrintHeaders()
212 #packet.PrintOptions()
214 if sid != [0,0,0,0] and ciaddr == [0,0,0,0] :
215 s.syslog(s.LOG_INFO, "Get DHCPREQUEST_SELECTING_STATE packet")
217 elif sid == [0,0,0,0] and ciaddr == [0,0,0,0] and ip :
218 s.syslog(s.LOG_INFO, "Get DHCPREQUEST_INITREBOOT_STATE packet")
220 elif sid == [0,0,0,0] and ciaddr != [0,0,0,0] and not ip :
221 s.syslog(s.LOG_INFO,"Get DHCPREQUEST_INITREBOOT_STATE packet")
223 else : s.syslog(s.LOG_INFO,"Get DHCPREQUEST_UNKNOWN_STATE packet : not implemented")
225 if self.backend.Request(packet):
226 packet.TransformToDhcpAckPacket()
227 self.SendPacket(packet)
228 elif self.backend.Discover(packet):
229 packet.TransformToDhcpNackPacket()
230 self.SendPacket(packet)
232 pass # We aren't authoritative, so don't reply if we don't know them.
234 # FIXME: These are not yet implemented.
235 def HandleDhcpDecline(self, packet):
236 s.syslog(s.LOG_INFO, "Get DHCPDECLINE packet")
237 self.backend.Decline(packet)
239 def HandleDhcpRelease(self, packet):
240 s.syslog(s.LOG_INFO,"Get DHCPRELEASE packet")
241 self.backend.Release(packet)
243 def HandleDhcpInform(self, packet):
244 s.syslog(s.LOG_INFO, "Get DHCPINFORM packet")
246 if self.backend.Request(packet) :
247 packet.TransformToDhcpAckPacket()
248 # FIXME : Remove lease_time from options
249 self.SendPacket(packet)
251 # FIXME : what if false ?
253 if '__main__' == __name__:
254 options = { "server_listen_port":67,
255 "client_listen_port":68,
256 "listen_address":"0.0.0.0"}
258 myip = socket.gethostbyname(socket.gethostname())
260 print "invirt-dhcpserver: cannot determine local IP address by looking up %s" % socket.gethostname()
263 dhcp_options['server_identifier'] = ipv4(myip).int()
265 backend = DhcpBackend()
266 server = DhcpServer(backend, options)
268 while True : server.GetNextDhcpPacket()