# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / udhcp / dhcpd.c
1 /* dhcpd.c
2  *
3  * Moreton Bay DHCP Server
4  * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
5  *                      Chris Trew <ctrew@moreton.com.au>
6  *
7  * Modified by Wuha Team <cjyu@tecom.com.tw> Aug 2005
8  *
9  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25
26 #include <fcntl.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <sys/wait.h>
30 #include <sys/stat.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #include <netinet/in.h>
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <sys/ioctl.h>
41 #include <time.h>
42 #include <sys/time.h>
43
44 #include "debug.h"
45 #include "dhcpd.h"
46 #include "arpping.h"
47 #include "socket.h"
48 #include "options.h"
49 #include "files.h"
50 #include "leases.h"
51 #include "packet.h"
52 #include "serverpacket.h"
53 #include "pidfile.h"
54 #include "pclisttab.h"
55
56
57 /* globals */
58 struct dhcpOfferedAddr *leases;
59 struct server_config_t server_config;
60
61
62 /* Exit and cleanup */
63 static void exit_server(int retval)
64 {
65         pidfile_delete(server_config.pidfile);
66         CLOSE_LOG();
67         exit(retval);
68 }
69
70
71 /* SIGTERM handler */
72 static void udhcpd_killed(int sig)
73 {
74         sig = 0;
75         LOG(LOG_INFO, "Received SIGTERM");
76         exit_server(0);
77 }
78
79
80 #ifdef COMBINED_BINARY  
81 int udhcpd(int argc, char *argv[])
82 #else
83 int main(int argc, char *argv[])
84 #endif
85 {       
86         fd_set rfds;
87         struct timeval tv;
88         //BRCM --initialize server_socket to -1
89         int server_socket = -1;
90         int bytes, retval;
91         struct dhcpMessage packet;
92         unsigned char *state;
93         char *server_id, *requested, *hostname;
94         u_int32_t server_id_align, requested_align;
95         unsigned long timeout_end;
96         struct option_set *option;
97         struct dhcpOfferedAddr *lease;
98         struct sockaddr_in *sin;
99         int pid_fd;
100                         
101         /* server ip addr */
102         int fd = -1;
103         struct ifreq ifr;
104
105         argc = argv[0][0]; /* get rid of some warnings */
106         
107         OPEN_LOG("udhcpd");
108         LOG(LOG_INFO, "udhcp server (v%s) started", VERSION);
109         
110         pid_fd = pidfile_acquire(server_config.pidfile);
111         pidfile_write_release(pid_fd);
112
113         memset(&server_config, 0, sizeof(struct server_config_t));
114         
115         read_config(DHCPD_CONF_FILE);
116         if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
117                 memcpy(&server_config.lease, option->data + 2, 4);
118                 server_config.lease = ntohl(server_config.lease);
119         }
120         else server_config.lease = LEASE_TIME;
121         
122         leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
123         memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
124         read_leases(server_config.lease_file);
125
126         if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
127                 ifr.ifr_addr.sa_family = AF_INET;
128                 strcpy(ifr.ifr_name, server_config.interface);
129                 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
130                         sin = (struct sockaddr_in *) &ifr.ifr_addr;
131                         server_config.server = sin->sin_addr.s_addr;
132                         DEBUG(LOG_INFO, "%s (server_ip) = %s", ifr.ifr_name, inet_ntoa(sin->sin_addr));
133                 } else {
134                         LOG(LOG_ERR, "SIOCGIFADDR failed!");
135                         exit_server(1);
136                 }
137                 if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
138                         DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
139                         server_config.ifindex = ifr.ifr_ifindex;
140                 } else {
141                         LOG(LOG_ERR, "SIOCGIFINDEX failed!");
142                         exit_server(1);
143                 }
144                 if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
145                         memcpy(server_config.arp, ifr.ifr_hwaddr.sa_data, 6);
146                         DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
147                                 server_config.arp[0], server_config.arp[1], server_config.arp[2], 
148                                 server_config.arp[3], server_config.arp[4], server_config.arp[5]);
149                 } else {
150                         LOG(LOG_ERR, "SIOCGIFHWADDR failed!");
151                         exit_server(1);
152                 }
153         } else {
154                 LOG(LOG_ERR, "socket failed!");
155                 exit_server(1);
156         }
157         close(fd);
158
159 #ifndef DEBUGGING
160         pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */
161         switch(fork()) {
162         case -1:
163                 perror("fork");
164                 exit_server(1);
165                 /*NOTREACHED*/
166         case 0:
167                 break; /* child continues */
168         default:
169                 exit(0); /* parent exits */
170                 /*NOTREACHED*/
171                 }
172         close(0);
173         setsid();
174         pidfile_write_release(pid_fd);
175 #endif
176
177
178         signal(SIGUSR1, write_leases);
179         signal(SIGTERM, udhcpd_killed);
180     /* setup pclist table  */
181     PcListInit();
182         signal(SIGUSR2,PcListReInit);
183     /* end setup */
184
185         timeout_end = time(0) + server_config.auto_time;
186
187         while(1) { /* loop until universe collapses */
188                 //BRCM_begin
189                 if (server_socket < 0) {
190                      server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface);
191                      if(server_socket < 0) {
192                            LOG(LOG_ERR, "couldn't create server socket -- au revoir");
193                            exit_server(0);
194                      }                  
195                 }  //BRCM_end
196
197                 FD_ZERO(&rfds);
198                 FD_SET(server_socket, &rfds);
199                 if (server_config.auto_time) {
200                         tv.tv_sec = timeout_end - time(0);
201                         if (tv.tv_sec <= 0) {
202                                 tv.tv_sec = server_config.auto_time;
203                                 timeout_end = time(0) + server_config.auto_time;
204                                 write_leases(0);
205                         }
206                         tv.tv_usec = 0;
207                 }
208                 retval = select(server_socket + 1, &rfds, NULL, NULL, server_config.auto_time ? &tv : NULL);
209                 if (retval == 0) {
210                         write_leases(0);
211                         timeout_end = time(0) + server_config.auto_time;
212                         close(server_socket);
213                         //BRCM
214                         server_socket = -1;
215                         continue;
216                 } else if (retval < 0) {
217                         DEBUG(LOG_INFO, "error on select");
218                         close(server_socket);
219                         //BRCM
220                         server_socket = -1;
221                         continue;
222                 }
223                 
224                 bytes = get_packet(&packet, server_socket); /* this waits for a packet - idle */
225                 //BRCM_BEGIN
226                 //close(server_socket);
227                 if(bytes < 0)
228                         continue;
229
230                 if((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
231                         DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
232                         continue;
233                 }
234                 lease = find_lease_by_chaddr(packet.chaddr);
235                 switch (state[0]) {
236                 case DHCPDISCOVER:
237                         DEBUG(LOG_INFO,"received DISCOVER");
238                         
239                         if (sendOffer(&packet) < 0) {
240                                 LOG(LOG_ERR, "send OFFER failed -- ignoring");
241                         }
242                         break;                  
243                 case DHCPREQUEST:
244                         DEBUG(LOG_INFO,"received REQUEST");
245
246                         requested = get_option(&packet, DHCP_REQUESTED_IP);
247                         server_id = get_option(&packet, DHCP_SERVER_ID);
248                         hostname = get_option(&packet, DHCP_HOST_NAME);
249
250                         if (requested) memcpy(&requested_align, requested, 4);
251                         if (server_id) memcpy(&server_id_align, server_id, 4);
252                 
253                         if (lease) {
254                                 if (server_id) {
255                                         /* SELECTING State */
256                                         DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
257                                         if (server_id_align == server_config.server && requested && 
258                                             requested_align == lease->yiaddr) {
259                                                 sendACK(&packet, lease->yiaddr);
260                                         }
261                                 } else {
262                                         if (requested) {
263                                                 /* INIT-REBOOT State */
264                                                 if (lease->yiaddr == requested_align)
265                                                         sendACK(&packet, lease->yiaddr);
266                                                 else sendNAK(&packet);
267                                         } else {
268                                                 /* RENEWING or REBINDING State */
269                                                 if (lease->yiaddr == packet.ciaddr)
270                                                         sendACK(&packet, lease->yiaddr);
271                                                 else {
272                                                         /* don't know what to do!!!! */
273                                                         sendNAK(&packet);
274                                                 }
275                                         }                                               
276                                 }
277                                 if (hostname) {
278                                         bytes = hostname[-1];
279                                         if (bytes >= (int) sizeof(lease->hostname))
280                                                 bytes = sizeof(lease->hostname) - 1;
281                                         strncpy(lease->hostname, hostname, bytes);
282                                         lease->hostname[bytes] = '\0';
283                                 } else
284                                         lease->hostname[0] = '\0';
285                         } else { /* else remain silent */                               
286                         sendNAK(&packet);
287             }
288                         break;
289                 case DHCPDECLINE:
290                         DEBUG(LOG_INFO,"received DECLINE");
291                         if (lease) {
292                                 memset(lease->chaddr, 0, 16);
293                                 lease->expires = time(0) + server_config.decline_time;
294                         }                       
295                         break;
296                 case DHCPRELEASE:
297                         DEBUG(LOG_INFO,"received RELEASE");
298                         if (lease) lease->expires = time(0);
299                         break;
300                 case DHCPINFORM:
301                         DEBUG(LOG_INFO,"received INFORM");
302                         send_inform(&packet);
303                         break;  
304                 default:
305                         LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
306                 }
307         }
308         //BRCM_BEGIN 
309         if (server_socket > 0)
310           close(server_socket);
311         return 0;
312 }
313