Initial checkin of a DHCP server (untested)
[invirt/packages/invirt-dhcp.git] / dhcpserver.py
1 #!/usr/bin/python
2 import pydhcplib
3 from pydhcplib.dhcp_packet import *
4 from pydhcplib.type_hw_addr import hwmac
5 from pydhcplib.type_ipv4 import ipv4
6
7 from event_logger import Log
8
9 import sipb_xen_database
10
11 class DhcpBackend:
12     def __init__(self, database=None):
13         if database is not None:
14             sipb_xen_database.connect(database)
15     def findIP(self, mac):
16         value = sipb_xen_database.NIC.get_by(mac_addr=mac)
17         if value is None:
18             return None
19         ip = value.ip
20         if ip is None:  #Deactivated?
21             return None
22         return ip
23
24     def Discover(self, packet):
25         Log.Output(Log.debug,"dhcp_backend : Discover ")
26         chaddr = hwmac(packet.GetHardwareAddress())
27         ip = self.findIP(str(chaddr))
28         if ip is not None:
29             ip = ipv4(ip)
30             Log.Output(Log.debug,"dhcp_backend : Discover result = "+str(ip))
31             packet_parameters = {}
32
33             # FIXME: Other offer parameters go here
34             packet_parameters["yiaddr"] = ip.list()
35             
36             packet.SetMultipleOptions(packet_parameters)
37             return True
38         return False
39         
40     def Request(self, packet):
41         Log.Output(Log.debug, "dhcp_backend : Request")
42         
43         discover = self.Discover(packet)
44         
45         chaddr = hwmac(packet.GetHardwareAddress())
46         request = packet.GetOption("request_ip_address")
47         yiaddr = packet.GetOption("yiaddr")
48
49         if not discover:
50             Log.Output(Log.info,"Unknown MAC address: "+str(chaddr))
51             return False
52         
53         if yiaddr!="0.0.0.0" and yiaddr == request :
54             Log.Output(Log.info,"Ack ip "+str(yiaddr)+" for "+str(chaddr))
55             return True
56         else:
57             Log.Output(Log.info,"Requested ip "+str(request)+" not available for "+str(chaddr))
58         return False
59
60     def Decline(self, packet):
61         pass
62     def Release(self, packet):
63         pass
64     
65
66 class DhcpServer(pydhcplib.dhcp_network.DhcpServer):
67     def __init__(self, backend, options = {'client_listenport':68,'server_listenport':67}):
68         pydhcplib.dhcp_network.DhcpServer.__init__(self,"0.0.0.0",options["client_listen_port"],options["server_listen_port"],)
69         self.backend = backend
70         Log.Output(Log.debug, "__init__ DhcpServer")
71
72     def SendPacket(self, packet):
73             """Encode and send the packet."""
74         
75         giaddr = packet.GetOption('giaddr')
76
77         # in all case, if giaddr is set, send packet to relay_agent
78         # network address defines by giaddr
79         if giaddr!=[0,0,0,0] :
80             agent_ip = ".".join(map(str,giaddr))
81             self.SendDhcpPacketTo(agent_ip,packet)
82             Log.Output(Log.debug, "SendPacket to agent : "+agent_ip)
83
84         # FIXME: This shouldn't broadcast if it has an IP address to send
85         # it to instead. See RFC2131 part 4.1 for full details
86         else :
87             Log.Output(Log.debug, "No agent, broadcast packet.")
88             self.SendDhcpPacketTo("255.255.255.255",packet)
89             
90
91     def HandleDhcpDiscover(self, packet):
92         """Build and send DHCPOFFER packet in response to DHCPDISCOVER
93         packet."""
94
95         logmsg = "Get DHCPDISCOVER packet from " + hwmac(packet.GetHardwareAddress()).str()
96
97         Log.Output(Log.info, logmsg)
98         offer = DhcpPacket()
99         offer.CreateDhcpOfferPacketFrom(packet)
100         
101         if self.backend.Discover(offer) :
102             self.SendPacket(offer)
103         # FIXME : what if false ?
104
105
106     def HandleDhcpRequest(self, packet):
107         """Build and send DHCPACK or DHCPNACK packet in response to
108         DHCPREQUEST packet. 4 types of DHCPREQUEST exists."""
109
110         ip = packet.GetOption("request_ip_address")
111         sid = packet.GetOption("server_identifier")
112         ciaddr = packet.GetOption("ciaddr")
113
114         if sid != [0,0,0,0] and ciaddr == [0,0,0,0] :
115             Log.Output(Log.info, "Get DHCPREQUEST_SELECTING_STATE packet")
116
117         elif sid == [0,0,0,0] and ciaddr == [0,0,0,0] and ip :
118             Log.Output(Log.info, "Get DHCPREQUEST_INITREBOOT_STATE packet")
119
120         elif sid == [0,0,0,0] and ciaddr != [0,0,0,0] and not ip :
121             Log.Output(Log.info,"Get DHCPREQUEST_INITREBOOT_STATE packet")
122
123         else : Log.Output(Log.info,"Get DHCPREQUEST_UNKNOWN_STATE packet : not implemented")
124
125         if self.backend.Request(packet) : packet.TransformToDhcpAckPacket()
126         else : packet.TransformToDhcpNackPacket()
127
128         self.SendPacket(packet)
129
130
131
132     # FIXME: These are not yet implemented.
133     def HandleDhcpDecline(self, packet):
134         Log.Output(Log.info, "Get DHCPDECLINE packet")
135         self.backend.Decline(packet)
136         
137     def HandleDhcpRelease(self, packet):
138         Log.Output(Log.info,"Get DHCPRELEASE packet")
139         self.backend.Release(packet)
140         
141     def HandleDhcpInform(self, packet):
142         Log.Output(Log.info, "Get DHCPINFORM packet")
143
144         if self.backend.Request(packet) :
145             packet.TransformToDhcpAckPacket()
146             # FIXME : Remove lease_time from options
147             self.SendPacket(packet)
148
149         # FIXME : what if false ?
150
151 if '__main__' == __name__:
152     event_logger.init("stdout", event_logger.INFO, {})
153     options = { "server_listen_port":67,
154                 "client_listen_port":68,
155                 "listen_address":"0.0.0.0"}
156     backend = DhcpBackend('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
157     server = DhcpServer(backend, options)
158
159     while True : server.GetNextDhcpPacket()