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