- spelling
[oweals/busybox.git] / networking / udhcp / dhcpd.c
1 /* dhcpd.c
2  *
3  * udhcp 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  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  */
11
12 #include <fcntl.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <sys/wait.h>
16 #include <arpa/inet.h>
17 #include <netdb.h>
18 #include <netinet/in.h>
19 #include <sys/socket.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <errno.h>
23 #include <sys/ioctl.h>
24 #include <time.h>
25
26 #include "dhcpd.h"
27 #include "arpping.h"
28 #include "socket.h"
29 #include "options.h"
30 #include "files.h"
31 #include "serverpacket.h"
32 #include "common.h"
33 #include "signalpipe.h"
34 #include "static_leases.h"
35
36
37 /* globals */
38 struct dhcpOfferedAddr *leases;
39 struct server_config_t server_config;
40
41
42 int udhcpd_main(int argc, char *argv[])
43 {
44         fd_set rfds;
45         struct timeval tv;
46         int server_socket = -1, bytes, retval, max_sock;
47         struct dhcpMessage packet;
48         uint8_t *state, *server_id, *requested;
49         uint32_t server_id_align, requested_align, static_lease_ip;
50         unsigned long timeout_end, num_ips;
51         struct option_set *option;
52         struct dhcpOfferedAddr *lease, static_lease;
53
54         read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
55
56         /* Start the log, sanitize fd's, and write a pid file */
57         udhcp_start_log_and_pid("udhcpd", server_config.pidfile);
58
59         if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
60                 memcpy(&server_config.lease, option->data + 2, 4);
61                 server_config.lease = ntohl(server_config.lease);
62         }
63         else server_config.lease = LEASE_TIME;
64
65         /* Sanity check */
66         num_ips = ntohl(server_config.end) - ntohl(server_config.start) + 1;
67         if (server_config.max_leases > num_ips) {
68                 LOG(LOG_ERR, "max_leases value (%lu) not sane, "
69                         "setting to %lu instead",
70                         server_config.max_leases, num_ips);
71                 server_config.max_leases = num_ips;
72         }
73
74         leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr));
75         read_leases(server_config.lease_file);
76
77         if (read_interface(server_config.interface, &server_config.ifindex,
78                            &server_config.server, server_config.arp) < 0)
79                 return 1;
80
81         if (!ENABLE_FEATURE_UDHCP_DEBUG)
82                 udhcp_background(server_config.pidfile); /* hold lock during fork. */
83
84         /* Setup the signal pipe */
85         udhcp_sp_setup();
86
87         timeout_end = time(0) + server_config.auto_time;
88         while(1) { /* loop until universe collapses */
89
90                 if (server_socket < 0)
91                         if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) {
92                                 LOG(LOG_ERR, "FATAL: couldn't create server socket, %m");
93                                 return 2;
94                         }
95
96                 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
97                 if (server_config.auto_time) {
98                         tv.tv_sec = timeout_end - time(0);
99                         tv.tv_usec = 0;
100                 }
101                 if (!server_config.auto_time || tv.tv_sec > 0) {
102                         retval = select(max_sock + 1, &rfds, NULL, NULL,
103                                         server_config.auto_time ? &tv : NULL);
104                 } else retval = 0; /* If we already timed out, fall through */
105
106                 if (retval == 0) {
107                         write_leases();
108                         timeout_end = time(0) + server_config.auto_time;
109                         continue;
110                 } else if (retval < 0 && errno != EINTR) {
111                         DEBUG(LOG_INFO, "error on select");
112                         continue;
113                 }
114
115                 switch (udhcp_sp_read(&rfds)) {
116                 case SIGUSR1:
117                         LOG(LOG_INFO, "Received a SIGUSR1");
118                         write_leases();
119                         /* why not just reset the timeout, eh */
120                         timeout_end = time(0) + server_config.auto_time;
121                         continue;
122                 case SIGTERM:
123                         LOG(LOG_INFO, "Received a SIGTERM");
124                         return 0;
125                 case 0: break;          /* no signal */
126                 default: continue;      /* signal or error (probably EINTR) */
127                 }
128
129                 if ((bytes = udhcp_get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
130                         if (bytes == -1 && errno != EINTR) {
131                                 DEBUG(LOG_INFO, "error on read, %m, reopening socket");
132                                 close(server_socket);
133                                 server_socket = -1;
134                         }
135                         continue;
136                 }
137
138                 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
139                         DEBUG(LOG_ERR, "couldn't get option from packet, ignoring");
140                         continue;
141                 }
142
143                 /* Look for a static lease */
144                 static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
145
146                 if(static_lease_ip)
147                 {
148                         printf("Found static lease: %x\n", static_lease_ip);
149
150                         memcpy(&static_lease.chaddr, &packet.chaddr, 16);
151                         static_lease.yiaddr = static_lease_ip;
152                         static_lease.expires = 0;
153
154                         lease = &static_lease;
155
156                 }
157                 else
158                 {
159                 lease = find_lease_by_chaddr(packet.chaddr);
160                 }
161
162                 switch (state[0]) {
163                 case DHCPDISCOVER:
164                         DEBUG(LOG_INFO,"received DISCOVER");
165
166                         if (sendOffer(&packet) < 0) {
167                                 LOG(LOG_ERR, "send OFFER failed");
168                         }
169                         break;
170                 case DHCPREQUEST:
171                         DEBUG(LOG_INFO, "received REQUEST");
172
173                         requested = get_option(&packet, DHCP_REQUESTED_IP);
174                         server_id = get_option(&packet, DHCP_SERVER_ID);
175
176                         if (requested) memcpy(&requested_align, requested, 4);
177                         if (server_id) memcpy(&server_id_align, server_id, 4);
178
179                         if (lease) {
180                                 if (server_id) {
181                                         /* SELECTING State */
182                                         DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
183                                         if (server_id_align == server_config.server && requested &&
184                                             requested_align == lease->yiaddr) {
185                                                 sendACK(&packet, lease->yiaddr);
186                                         }
187                                 } else {
188                                         if (requested) {
189                                                 /* INIT-REBOOT State */
190                                                 if (lease->yiaddr == requested_align)
191                                                         sendACK(&packet, lease->yiaddr);
192                                                 else sendNAK(&packet);
193                                         } else {
194                                                 /* RENEWING or REBINDING State */
195                                                 if (lease->yiaddr == packet.ciaddr)
196                                                         sendACK(&packet, lease->yiaddr);
197                                                 else {
198                                                         /* don't know what to do!!!! */
199                                                         sendNAK(&packet);
200                                                 }
201                                         }
202                                 }
203
204                         /* what to do if we have no record of the client */
205                         } else if (server_id) {
206                                 /* SELECTING State */
207
208                         } else if (requested) {
209                                 /* INIT-REBOOT State */
210                                 if ((lease = find_lease_by_yiaddr(requested_align))) {
211                                         if (lease_expired(lease)) {
212                                                 /* probably best if we drop this lease */
213                                                 memset(lease->chaddr, 0, 16);
214                                         /* make some contention for this address */
215                                         } else sendNAK(&packet);
216                                 } else if (requested_align < server_config.start ||
217                                            requested_align > server_config.end) {
218                                         sendNAK(&packet);
219                                 } /* else remain silent */
220
221                         } else {
222                                  /* RENEWING or REBINDING State */
223                         }
224                         break;
225                 case DHCPDECLINE:
226                         DEBUG(LOG_INFO,"received DECLINE");
227                         if (lease) {
228                                 memset(lease->chaddr, 0, 16);
229                                 lease->expires = time(0) + server_config.decline_time;
230                         }
231                         break;
232                 case DHCPRELEASE:
233                         DEBUG(LOG_INFO,"received RELEASE");
234                         if (lease) lease->expires = time(0);
235                         break;
236                 case DHCPINFORM:
237                         DEBUG(LOG_INFO,"received INFORM");
238                         send_inform(&packet);
239                         break;
240                 default:
241                         LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
242                 }
243         }
244
245         return 0;
246 }
247