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