Annotation of /trunk/mkinitrd-magellan/busybox/networking/udhcp/dhcpd.c
Parent Directory | Revision Log
Revision 984 -
(hide annotations)
(download)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 7811 byte(s)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 7811 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 | niro | 532 | /* 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 | niro | 816 | #include <syslog.h> |
14 | niro | 532 | #include "common.h" |
15 | niro | 816 | #include "dhcpc.h" |
16 | niro | 532 | #include "dhcpd.h" |
17 | #include "options.h" | ||
18 | |||
19 | |||
20 | /* globals */ | ||
21 | niro | 984 | struct dyn_lease *g_leases; |
22 | niro | 816 | /* struct server_config_t server_config is in bb_common_bufsiz1 */ |
23 | niro | 532 | |
24 | |||
25 | niro | 816 | int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
26 | int udhcpd_main(int argc UNUSED_PARAM, char **argv) | ||
27 | niro | 532 | { |
28 | fd_set rfds; | ||
29 | niro | 984 | int server_socket = -1, retval, max_sock; |
30 | struct dhcp_packet packet; | ||
31 | uint8_t *state; | ||
32 | uint32_t static_lease_ip; | ||
33 | niro | 816 | unsigned timeout_end; |
34 | unsigned num_ips; | ||
35 | unsigned opt; | ||
36 | niro | 532 | struct option_set *option; |
37 | niro | 984 | struct dyn_lease *lease, fake_lease; |
38 | IF_FEATURE_UDHCP_PORT(char *str_P;) | ||
39 | niro | 532 | |
40 | niro | 816 | #if ENABLE_FEATURE_UDHCP_PORT |
41 | SERVER_PORT = 67; | ||
42 | CLIENT_PORT = 68; | ||
43 | #endif | ||
44 | niro | 532 | |
45 | niro | 984 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 |
46 | opt_complementary = "vv"; | ||
47 | #endif | ||
48 | opt = getopt32(argv, "fSv" | ||
49 | IF_FEATURE_UDHCP_PORT("P:", &str_P) | ||
50 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 | ||
51 | , &dhcp_verbose | ||
52 | #endif | ||
53 | ); | ||
54 | niro | 816 | argv += optind; |
55 | if (!(opt & 1)) { /* no -f */ | ||
56 | bb_daemonize_or_rexec(0, argv); | ||
57 | niro | 984 | logmode = LOGMODE_NONE; |
58 | niro | 816 | } |
59 | if (opt & 2) { /* -S */ | ||
60 | niro | 984 | openlog(applet_name, LOG_PID, LOG_DAEMON); |
61 | niro | 816 | logmode |= LOGMODE_SYSLOG; |
62 | } | ||
63 | #if ENABLE_FEATURE_UDHCP_PORT | ||
64 | niro | 984 | if (opt & 8) { /* -P */ |
65 | niro | 816 | SERVER_PORT = xatou16(str_P); |
66 | CLIENT_PORT = SERVER_PORT + 1; | ||
67 | } | ||
68 | #endif | ||
69 | /* Would rather not do read_config before daemonization - | ||
70 | * otherwise NOMMU machines will parse config twice */ | ||
71 | read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE); | ||
72 | |||
73 | /* Make sure fd 0,1,2 are open */ | ||
74 | bb_sanitize_stdio(); | ||
75 | /* Equivalent of doing a fflush after every \n */ | ||
76 | setlinebuf(stdout); | ||
77 | |||
78 | /* Create pidfile */ | ||
79 | write_pidfile(server_config.pidfile); | ||
80 | niro | 984 | /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */ |
81 | niro | 816 | |
82 | bb_info_msg("%s (v"BB_VER") started", applet_name); | ||
83 | |||
84 | option = find_option(server_config.options, DHCP_LEASE_TIME); | ||
85 | niro | 984 | server_config.max_lease_sec = LEASE_TIME; |
86 | niro | 816 | if (option) { |
87 | niro | 984 | move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA); |
88 | server_config.max_lease_sec = ntohl(server_config.max_lease_sec); | ||
89 | niro | 532 | } |
90 | |||
91 | /* Sanity check */ | ||
92 | niro | 816 | num_ips = server_config.end_ip - server_config.start_ip + 1; |
93 | niro | 532 | if (server_config.max_leases > num_ips) { |
94 | niro | 816 | bb_error_msg("max_leases=%u is too big, setting to %u", |
95 | (unsigned)server_config.max_leases, num_ips); | ||
96 | niro | 532 | server_config.max_leases = num_ips; |
97 | } | ||
98 | |||
99 | niro | 984 | g_leases = xzalloc(server_config.max_leases * sizeof(g_leases[0])); |
100 | niro | 532 | read_leases(server_config.lease_file); |
101 | |||
102 | niro | 984 | if (udhcp_read_interface(server_config.interface, |
103 | &server_config.ifindex, | ||
104 | &server_config.server_nip, | ||
105 | server_config.server_mac) | ||
106 | ) { | ||
107 | niro | 816 | retval = 1; |
108 | goto ret; | ||
109 | } | ||
110 | niro | 532 | |
111 | /* Setup the signal pipe */ | ||
112 | udhcp_sp_setup(); | ||
113 | |||
114 | niro | 816 | timeout_end = monotonic_sec() + server_config.auto_time; |
115 | niro | 532 | while (1) { /* loop until universe collapses */ |
116 | niro | 984 | int bytes; |
117 | struct timeval tv; | ||
118 | niro | 532 | |
119 | if (server_socket < 0) { | ||
120 | niro | 816 | server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, |
121 | server_config.interface); | ||
122 | niro | 532 | } |
123 | |||
124 | max_sock = udhcp_sp_fd_set(&rfds, server_socket); | ||
125 | if (server_config.auto_time) { | ||
126 | niro | 816 | tv.tv_sec = timeout_end - monotonic_sec(); |
127 | niro | 532 | tv.tv_usec = 0; |
128 | } | ||
129 | niro | 816 | retval = 0; |
130 | niro | 532 | if (!server_config.auto_time || tv.tv_sec > 0) { |
131 | retval = select(max_sock + 1, &rfds, NULL, NULL, | ||
132 | server_config.auto_time ? &tv : NULL); | ||
133 | niro | 816 | } |
134 | niro | 532 | if (retval == 0) { |
135 | write_leases(); | ||
136 | niro | 816 | timeout_end = monotonic_sec() + server_config.auto_time; |
137 | niro | 532 | continue; |
138 | niro | 816 | } |
139 | if (retval < 0 && errno != EINTR) { | ||
140 | niro | 984 | log1("Error on select"); |
141 | niro | 532 | continue; |
142 | } | ||
143 | |||
144 | switch (udhcp_sp_read(&rfds)) { | ||
145 | case SIGUSR1: | ||
146 | bb_info_msg("Received a SIGUSR1"); | ||
147 | write_leases(); | ||
148 | /* why not just reset the timeout, eh */ | ||
149 | niro | 816 | timeout_end = monotonic_sec() + server_config.auto_time; |
150 | niro | 532 | continue; |
151 | case SIGTERM: | ||
152 | bb_info_msg("Received a SIGTERM"); | ||
153 | niro | 816 | goto ret0; |
154 | niro | 984 | case 0: /* no signal: read a packet */ |
155 | break; | ||
156 | default: /* signal or error (probably EINTR): back to select */ | ||
157 | continue; | ||
158 | niro | 532 | } |
159 | |||
160 | niro | 984 | bytes = udhcp_recv_kernel_packet(&packet, server_socket); |
161 | niro | 816 | if (bytes < 0) { |
162 | niro | 984 | /* bytes can also be -2 ("bad packet data") */ |
163 | niro | 532 | if (bytes == -1 && errno != EINTR) { |
164 | niro | 984 | log1("Read error: %s, reopening socket", strerror(errno)); |
165 | niro | 532 | close(server_socket); |
166 | server_socket = -1; | ||
167 | } | ||
168 | continue; | ||
169 | } | ||
170 | |||
171 | niro | 984 | if (packet.hlen != 6) { |
172 | bb_error_msg("MAC length != 6, ignoring packet"); | ||
173 | continue; | ||
174 | } | ||
175 | |||
176 | niro | 816 | state = get_option(&packet, DHCP_MESSAGE_TYPE); |
177 | if (state == NULL) { | ||
178 | niro | 984 | bb_error_msg("no message type option, ignoring packet"); |
179 | niro | 532 | continue; |
180 | } | ||
181 | |||
182 | /* Look for a static lease */ | ||
183 | niro | 984 | static_lease_ip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr); |
184 | niro | 532 | if (static_lease_ip) { |
185 | bb_info_msg("Found static lease: %x", static_lease_ip); | ||
186 | |||
187 | niro | 984 | memcpy(&fake_lease.lease_mac, &packet.chaddr, 6); |
188 | fake_lease.lease_nip = static_lease_ip; | ||
189 | fake_lease.expires = 0; | ||
190 | niro | 532 | |
191 | niro | 984 | lease = &fake_lease; |
192 | niro | 532 | } else { |
193 | niro | 984 | lease = find_lease_by_mac(packet.chaddr); |
194 | niro | 532 | } |
195 | |||
196 | switch (state[0]) { | ||
197 | case DHCPDISCOVER: | ||
198 | niro | 984 | log1("Received DISCOVER"); |
199 | niro | 532 | |
200 | niro | 816 | if (send_offer(&packet) < 0) { |
201 | niro | 532 | bb_error_msg("send OFFER failed"); |
202 | } | ||
203 | break; | ||
204 | niro | 984 | case DHCPREQUEST: { |
205 | uint8_t *server_id_opt, *requested_opt; | ||
206 | uint32_t server_id_net = server_id_net; /* for compiler */ | ||
207 | uint32_t requested_nip = requested_nip; /* for compiler */ | ||
208 | niro | 532 | |
209 | niro | 984 | log1("Received REQUEST"); |
210 | niro | 532 | |
211 | niro | 984 | requested_opt = get_option(&packet, DHCP_REQUESTED_IP); |
212 | server_id_opt = get_option(&packet, DHCP_SERVER_ID); | ||
213 | if (requested_opt) | ||
214 | move_from_unaligned32(requested_nip, requested_opt); | ||
215 | if (server_id_opt) | ||
216 | move_from_unaligned32(server_id_net, server_id_opt); | ||
217 | niro | 532 | |
218 | if (lease) { | ||
219 | niro | 984 | if (server_id_opt) { |
220 | niro | 532 | /* SELECTING State */ |
221 | niro | 984 | if (server_id_net == server_config.server_nip |
222 | && requested_opt | ||
223 | && requested_nip == lease->lease_nip | ||
224 | niro | 816 | ) { |
225 | niro | 984 | send_ACK(&packet, lease->lease_nip); |
226 | niro | 532 | } |
227 | niro | 984 | } else if (requested_opt) { |
228 | niro | 816 | /* INIT-REBOOT State */ |
229 | niro | 984 | if (lease->lease_nip == requested_nip) |
230 | send_ACK(&packet, lease->lease_nip); | ||
231 | niro | 816 | else |
232 | send_NAK(&packet); | ||
233 | niro | 984 | } else if (lease->lease_nip == packet.ciaddr) { |
234 | niro | 816 | /* RENEWING or REBINDING State */ |
235 | niro | 984 | send_ACK(&packet, lease->lease_nip); |
236 | niro | 816 | } else { /* don't know what to do!!!! */ |
237 | send_NAK(&packet); | ||
238 | niro | 532 | } |
239 | |||
240 | /* what to do if we have no record of the client */ | ||
241 | niro | 984 | } else if (server_id_opt) { |
242 | niro | 532 | /* SELECTING State */ |
243 | |||
244 | niro | 984 | } else if (requested_opt) { |
245 | niro | 532 | /* INIT-REBOOT State */ |
246 | niro | 984 | lease = find_lease_by_nip(requested_nip); |
247 | niro | 816 | if (lease) { |
248 | niro | 984 | if (is_expired_lease(lease)) { |
249 | niro | 532 | /* probably best if we drop this lease */ |
250 | niro | 984 | memset(lease->lease_mac, 0, sizeof(lease->lease_mac)); |
251 | } else { | ||
252 | /* make some contention for this address */ | ||
253 | niro | 816 | send_NAK(&packet); |
254 | niro | 984 | } |
255 | niro | 816 | } else { |
256 | niro | 984 | uint32_t r = ntohl(requested_nip); |
257 | niro | 816 | if (r < server_config.start_ip |
258 | || r > server_config.end_ip | ||
259 | ) { | ||
260 | send_NAK(&packet); | ||
261 | } | ||
262 | /* else remain silent */ | ||
263 | } | ||
264 | niro | 532 | |
265 | } else { | ||
266 | niro | 816 | /* RENEWING or REBINDING State */ |
267 | niro | 532 | } |
268 | break; | ||
269 | niro | 984 | } |
270 | niro | 532 | case DHCPDECLINE: |
271 | niro | 984 | log1("Received DECLINE"); |
272 | niro | 532 | if (lease) { |
273 | niro | 984 | memset(lease->lease_mac, 0, sizeof(lease->lease_mac)); |
274 | lease->expires = time(NULL) + server_config.decline_time; | ||
275 | niro | 532 | } |
276 | break; | ||
277 | case DHCPRELEASE: | ||
278 | niro | 984 | log1("Received RELEASE"); |
279 | niro | 816 | if (lease) |
280 | niro | 984 | lease->expires = time(NULL); |
281 | niro | 532 | break; |
282 | case DHCPINFORM: | ||
283 | niro | 984 | log1("Received INFORM"); |
284 | niro | 532 | send_inform(&packet); |
285 | break; | ||
286 | default: | ||
287 | bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]); | ||
288 | } | ||
289 | } | ||
290 | niro | 816 | ret0: |
291 | retval = 0; | ||
292 | ret: | ||
293 | /*if (server_config.pidfile) - server_config.pidfile is never NULL */ | ||
294 | remove_pidfile(server_config.pidfile); | ||
295 | return retval; | ||
296 | niro | 532 | } |