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