dhcpserver.py in trunk/packages/sipb-xen-dhcp/code
– xvm
Please file new bugs on Launchpad: Invirt or XVM (if you're not sure which, just pick one) Search: Login Help/Guide About Trac Preferences Wiki Timeline Roadmap Browse Source View Tickets Search Context Navigation ← Previous Revision Latest Revision Next Revision → Blame Revision Log source: trunk/packages/sipb-xen-dhcp/code/dhcpserver.py @ 302 View diff against: View revision: Visit: trunkbranches/wsgi Last change on this file since 302 was 300, checked in by ecprice, 15 years ago Clear the sql cache for dns and dhcp, fixing #42. Property svn:executable set to * File size: 9.6 KB Line 1 #!/usr/bin/python 2 import sys 3 sys.path.append('pydhcplib/') 4 import pydhcplib 5 import pydhcplib.dhcp_network 6 from pydhcplib.dhcp_packet import * 7 from pydhcplib.type_hw_addr import hwmac 8 from pydhcplib.type_ipv4 import ipv4 9 from pydhcplib.type_strlist import strlist 10 import socket 11 import IN 12 13 import event_logger 14 if '__main__' == __name__: 15 event_logger.init("stdout", 'DEBUG', {}) 16 from event_logger import Log 17 18 import psycopg2 19 import time 20 import sipb_xen_database 21 from sqlalchemy import create_engine 22 23 dhcp_options = {'subnet_mask': '255.255.0.0', 24 'router': '18.181.0.1', 25 'domain_name_server': '18.70.0.160,18.71.0.151,18.72.0.3', 26 'domain_name': 'mit.edu', 27 'ip_address_lease_time': 60*60*24} 28 29 class DhcpBackend: 30 def __init__(self, database=None): 31 if database is not None: 32 self.database = database 33 sipb_xen_database.connect(create_engine(database)) 34 def findNIC(self, mac): 35 sipb_xen_database.clear_cache() 36 for i in range(3): 37 try: 38 value = sipb_xen_database.NIC.get_by(mac_addr=mac) 39 except psycopg2.OperationalError: 40 time.sleep(0.5) 41 if i == 2: #Try twice to reconnect. 42 raise 43 #Sigh. SQLAlchemy should do this itself. 44 sipb_xen_database.connect(create_engine(self.database)) 45 else: 46 break 47 return value 48 def find_interface(self, packet): 49 chaddr = hwmac(packet.GetHardwareAddress()) 50 nic = self.findNIC(str(chaddr)) 51 if nic is None or nic.ip is None: 52 return ("18.181.0.60", None) 53 ipstr = ''.join(reversed(['%02X' % i for i in ipv4(nic.ip).list()])) 54 for line in open('/proc/net/route'): 55 parts = line.split() 56 if parts[1] == ipstr: 57 Log.Output(Log.debug, "find_interface found "+str(nic.ip)+" on "+parts[0]) 58 return ("18.181.0.60", parts[0]) 59 return ("18.181.0.60", None) 60 61 def getParameters(self, **extra): 62 all_options=dict(dhcp_options) 63 all_options.update(extra) 64 options = {} 65 for parameter, value in all_options.iteritems(): 66 if value is None: 67 continue 68 option_type = DhcpOptionsTypes[DhcpOptions[parameter]] 69 70 if option_type == "ipv4" : 71 # this is a single ip address 72 options[parameter] = map(int,value.split(".")) 73 elif option_type == "ipv4+" : 74 # this is multiple ip address 75 iplist = value.split(",") 76 opt = [] 77 for single in iplist : 78 opt.extend(ipv4(single).list()) 79 options[parameter] = opt 80 elif option_type == "32-bits" : 81 # This is probably a number... 82 digit = int(value) 83 options[parameter] = [digit>>24&0xFF,(digit>>16)&0xFF,(digit>>8)&0xFF,digit&0xFF] 84 elif option_type == "16-bits" : 85 digit = int(value) 86 options[parameter] = [(digit>>8)&0xFF,digit&0xFF] 87 88 elif option_type == "char" : 89 digit = int(value) 90 options[parameter] = [digit&0xFF] 91 92 elif option_type == "bool" : 93 if value=="False" or value=="false" or value==0 : 94 options[parameter] = [0] 95 else : options[parameter] = [1] 96 97 elif option_type == "string" : 98 options[parameter] = strlist(value).list() 99 100 else : 101 options[parameter] = strlist(value).list() 102 return options 103 104 def Discover(self, packet): 105 Log.Output(Log.debug,"dhcp_backend : Discover ") 106 chaddr = hwmac(packet.GetHardwareAddress()) 107 nic = self.findNIC(str(chaddr)) 108 if nic is None or nic.machine is None: 109 return False 110 ip = nic.ip 111 if ip is None: #Deactivated? 112 return False 113 if nic.hostname and '.' in nic.hostname: 114 hostname = nic.hostname 115 elif nic.machine.name: 116 hostname = nic.machine.name + '.servers.csail.mit.edu' 117 else: 118 hostname = None 119 if ip is not None: 120 ip = ipv4(ip) 121 Log.Output(Log.debug,"dhcp_backend : Discover result = "+str(ip)) 122 packet_parameters = self.getParameters(host_name=hostname) 123 124 # FIXME: Other offer parameters go here 125 packet_parameters["yiaddr"] = ip.list() 126 127 packet.SetMultipleOptions(packet_parameters) 128 return True 129 return False 130 131 def Request(self, packet): 132 Log.Output(Log.debug, "dhcp_backend : Request") 133 134 discover = self.Discover(packet) 135 136 chaddr = hwmac(packet.GetHardwareAddress()) 137 request = packet.GetOption("request_ip_address") 138 if not request: 139 request = packet.GetOption("ciaddr") 140 yiaddr = packet.GetOption("yiaddr") 141 142 if not discover: 143 Log.Output(Log.info,"Unknown MAC address: "+str(chaddr)) 144 return False 145 146 if yiaddr!="0.0.0.0" and yiaddr == request : 147 Log.Output(Log.info,"Ack ip "+str(yiaddr)+" for "+str(chaddr)) 148 return True 149 else: 150 Log.Output(Log.info,"Requested ip "+str(request)+" not available for "+str(chaddr)) 151 return False 152 153 def Decline(self, packet): 154 pass 155 def Release(self, packet): 156 pass 157 158 159 class DhcpServer(pydhcplib.dhcp_network.DhcpServer): 160 def __init__(self, backend, options = {'client_listenport':68,'server_listenport':67}): 161 pydhcplib.dhcp_network.DhcpServer.__init__(self,"0.0.0.0",options["client_listen_port"],options["server_listen_port"],) 162 self.backend = backend 163 Log.Output(Log.debug, "__init__ DhcpServer") 164 165 def SendDhcpPacketTo(self, To, packet): 166 (ip, intf) = self.backend.find_interface(packet) 167 if intf: 168 out_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 169 out_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST,1) 170 out_socket.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, intf) 171 #out_socket.bind((ip, self.listen_port)) 172 ret = out_socket.sendto(packet.EncodePacket(), (To,self.emit_port)) 173 out_socket.close() 174 return ret 175 else: 176 return self.dhcp_socket.sendto(packet.EncodePacket(),(To,self.emit_port)) 177 178 def SendPacket(self, packet): 179 """Encode and send the packet.""" 180 181 giaddr = packet.GetOption('giaddr') 182 183 # in all case, if giaddr is set, send packet to relay_agent 184 # network address defines by giaddr 185 if giaddr!=[0,0,0,0] : 186 agent_ip = ".".join(map(str,giaddr)) 187 self.SendDhcpPacketTo(agent_ip,packet) 188 Log.Output(Log.debug, "SendPacket to agent : "+agent_ip) 189 190 # FIXME: This shouldn't broadcast if it has an IP address to send 191 # it to instead. See RFC2131 part 4.1 for full details 192 else : 193 Log.Output(Log.debug, "No agent, broadcast packet.") 194 self.SendDhcpPacketTo("255.255.255.255",packet) 195 196 197 def HandleDhcpDiscover(self, packet): 198 """Build and send DHCPOFFER packet in response to DHCPDISCOVER 199 packet.""" 200 201 logmsg = "Get DHCPDISCOVER packet from " + hwmac(packet.GetHardwareAddress()).str() 202 203 Log.Output(Log.info, logmsg) 204 offer = DhcpPacket() 205 offer.CreateDhcpOfferPacketFrom(packet) 206 207 if self.backend.Discover(offer): 208 self.SendPacket(offer) 209 # FIXME : what if false ? 210 211 212 def HandleDhcpRequest(self, packet): 213 """Build and send DHCPACK or DHCPNACK packet in response to 214 DHCPREQUEST packet. 4 types of DHCPREQUEST exists.""" 215 216 ip = packet.GetOption("request_ip_address") 217 sid = packet.GetOption("server_identifier") 218 ciaddr = packet.GetOption("ciaddr") 219 #packet.PrintHeaders() 220 #packet.PrintOptions() 221 222 if sid != [0,0,0,0] and ciaddr == [0,0,0,0] : 223 Log.Output(Log.info, "Get DHCPREQUEST_SELECTING_STATE packet") 224 225 elif sid == [0,0,0,0] and ciaddr == [0,0,0,0] and ip : 226 Log.Output(Log.info, "Get DHCPREQUEST_INITREBOOT_STATE packet") 227 228 elif sid == [0,0,0,0] and ciaddr != [0,0,0,0] and not ip : 229 Log.Output(Log.info,"Get DHCPREQUEST_INITREBOOT_STATE packet") 230 231 else : Log.Output(Log.info,"Get DHCPREQUEST_UNKNOWN_STATE packet : not implemented") 232 233 if self.backend.Request(packet) : packet.TransformToDhcpAckPacket() 234 else : packet.TransformToDhcpNackPacket() 235 236 self.SendPacket(packet) 237 238 239 240 # FIXME: These are not yet implemented. 241 def HandleDhcpDecline(self, packet): 242 Log.Output(Log.info, "Get DHCPDECLINE packet") 243 self.backend.Decline(packet) 244 245 def HandleDhcpRelease(self, packet): 246 Log.Output(Log.info,"Get DHCPRELEASE packet") 247 self.backend.Release(packet) 248 249 def HandleDhcpInform(self, packet): 250 Log.Output(Log.info, "Get DHCPINFORM packet") 251 252 if self.backend.Request(packet) : 253 packet.TransformToDhcpAckPacket() 254 # FIXME : Remove lease_time from options 255 self.SendPacket(packet) 256 257 # FIXME : what if false ? 258 259 if '__main__' == __name__: 260 options = { "server_listen_port":67, 261 "client_listen_port":68, 262 "listen_address":"0.0.0.0"} 263 backend = DhcpBackend('postgres://sipb-xen@sipb-xen-dev/sipb_xen') 264 server = DhcpServer(backend, options) 265 266 while True : server.GetNextDhcpPacket() Note: See TracBrowser for help on using the repository browser. Download in other formats: Plain Text Original Format Powered by Trac 1.0.2 By Edgewall Software. Visit the Trac open source project at http://trac.edgewall.org/