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