Contents of /trunk/mkinitrd-magellan/busybox/networking/udhcp/files.c
Parent Directory | Revision Log
Revision 984 -
(show annotations)
(download)
Sun May 30 11:32:42 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 11741 byte(s)
Sun May 30 11:32:42 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 11741 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * files.c -- DHCP server file manipulation * |
4 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 |
5 | * |
6 | * Licensed under GPLv2, see file LICENSE in this tarball for details. |
7 | */ |
8 | |
9 | #include <netinet/ether.h> |
10 | |
11 | #include "common.h" |
12 | #include "dhcpd.h" |
13 | #include "options.h" |
14 | |
15 | #if BB_LITTLE_ENDIAN |
16 | static inline uint64_t hton64(uint64_t v) |
17 | { |
18 | return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32); |
19 | } |
20 | #else |
21 | #define hton64(v) (v) |
22 | #endif |
23 | #define ntoh64(v) hton64(v) |
24 | |
25 | |
26 | /* on these functions, make sure your datatype matches */ |
27 | static int FAST_FUNC read_nip(const char *line, void *arg) |
28 | { |
29 | len_and_sockaddr *lsa; |
30 | |
31 | lsa = host_and_af2sockaddr(line, 0, AF_INET); |
32 | if (!lsa) |
33 | return 0; |
34 | *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr; |
35 | free(lsa); |
36 | return 1; |
37 | } |
38 | |
39 | |
40 | static int FAST_FUNC read_mac(const char *line, void *arg) |
41 | { |
42 | return NULL == ether_aton_r(line, (struct ether_addr *)arg); |
43 | } |
44 | |
45 | |
46 | static int FAST_FUNC read_str(const char *line, void *arg) |
47 | { |
48 | char **dest = arg; |
49 | |
50 | free(*dest); |
51 | *dest = xstrdup(line); |
52 | return 1; |
53 | } |
54 | |
55 | |
56 | static int FAST_FUNC read_u32(const char *line, void *arg) |
57 | { |
58 | *(uint32_t*)arg = bb_strtou32(line, NULL, 10); |
59 | return errno == 0; |
60 | } |
61 | |
62 | |
63 | static int FAST_FUNC read_yn(const char *line, void *arg) |
64 | { |
65 | char *dest = arg; |
66 | |
67 | if (!strcasecmp("yes", line)) { |
68 | *dest = 1; |
69 | return 1; |
70 | } |
71 | if (!strcasecmp("no", line)) { |
72 | *dest = 0; |
73 | return 1; |
74 | } |
75 | return 0; |
76 | } |
77 | |
78 | |
79 | /* find option 'code' in opt_list */ |
80 | struct option_set* FAST_FUNC find_option(struct option_set *opt_list, uint8_t code) |
81 | { |
82 | while (opt_list && opt_list->data[OPT_CODE] < code) |
83 | opt_list = opt_list->next; |
84 | |
85 | if (opt_list && opt_list->data[OPT_CODE] == code) |
86 | return opt_list; |
87 | return NULL; |
88 | } |
89 | |
90 | |
91 | /* add an option to the opt_list */ |
92 | static void attach_option(struct option_set **opt_list, |
93 | const struct dhcp_option *option, char *buffer, int length) |
94 | { |
95 | struct option_set *existing, *new, **curr; |
96 | |
97 | existing = find_option(*opt_list, option->code); |
98 | if (!existing) { |
99 | log2("Attaching option %02x to list", option->code); |
100 | |
101 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
102 | if ((option->flags & TYPE_MASK) == OPTION_STR1035) |
103 | /* reuse buffer and length for RFC1035-formatted string */ |
104 | buffer = (char *)dname_enc(NULL, 0, buffer, &length); |
105 | #endif |
106 | |
107 | /* make a new option */ |
108 | new = xmalloc(sizeof(*new)); |
109 | new->data = xmalloc(length + 2); |
110 | new->data[OPT_CODE] = option->code; |
111 | new->data[OPT_LEN] = length; |
112 | memcpy(new->data + 2, buffer, length); |
113 | |
114 | curr = opt_list; |
115 | while (*curr && (*curr)->data[OPT_CODE] < option->code) |
116 | curr = &(*curr)->next; |
117 | |
118 | new->next = *curr; |
119 | *curr = new; |
120 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
121 | if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) |
122 | free(buffer); |
123 | #endif |
124 | return; |
125 | } |
126 | |
127 | /* add it to an existing option */ |
128 | log1("Attaching option %02x to existing member of list", option->code); |
129 | if (option->flags & OPTION_LIST) { |
130 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
131 | if ((option->flags & TYPE_MASK) == OPTION_STR1035) |
132 | /* reuse buffer and length for RFC1035-formatted string */ |
133 | buffer = (char *)dname_enc(existing->data + 2, |
134 | existing->data[OPT_LEN], buffer, &length); |
135 | #endif |
136 | if (existing->data[OPT_LEN] + length <= 255) { |
137 | existing->data = xrealloc(existing->data, |
138 | existing->data[OPT_LEN] + length + 3); |
139 | if ((option->flags & TYPE_MASK) == OPTION_STRING) { |
140 | /* ' ' can bring us to 256 - bad */ |
141 | if (existing->data[OPT_LEN] + length >= 255) |
142 | return; |
143 | /* add space separator between STRING options in a list */ |
144 | existing->data[existing->data[OPT_LEN] + 2] = ' '; |
145 | existing->data[OPT_LEN]++; |
146 | } |
147 | memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); |
148 | existing->data[OPT_LEN] += length; |
149 | } /* else, ignore the data, we could put this in a second option in the future */ |
150 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
151 | if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) |
152 | free(buffer); |
153 | #endif |
154 | } /* else, ignore the new data */ |
155 | } |
156 | |
157 | |
158 | /* read a dhcp option and add it to opt_list */ |
159 | static int FAST_FUNC read_opt(const char *const_line, void *arg) |
160 | { |
161 | struct option_set **opt_list = arg; |
162 | char *opt, *val, *endptr; |
163 | char *line; |
164 | const struct dhcp_option *option; |
165 | int retval, length, idx; |
166 | char buffer[8] ALIGNED(4); |
167 | uint16_t *result_u16 = (uint16_t *) buffer; |
168 | uint32_t *result_u32 = (uint32_t *) buffer; |
169 | |
170 | /* Cheat, the only const line we'll actually get is "" */ |
171 | line = (char *) const_line; |
172 | opt = strtok(line, " \t="); |
173 | if (!opt) |
174 | return 0; |
175 | |
176 | idx = index_in_strings(dhcp_option_strings, opt); /* NB: was strcasecmp! */ |
177 | if (idx < 0) |
178 | return 0; |
179 | option = &dhcp_options[idx]; |
180 | |
181 | retval = 0; |
182 | do { |
183 | val = strtok(NULL, ", \t"); |
184 | if (!val) break; |
185 | length = dhcp_option_lengths[option->flags & TYPE_MASK]; |
186 | retval = 0; |
187 | opt = buffer; /* new meaning for variable opt */ |
188 | switch (option->flags & TYPE_MASK) { |
189 | case OPTION_IP: |
190 | retval = read_nip(val, buffer); |
191 | break; |
192 | case OPTION_IP_PAIR: |
193 | retval = read_nip(val, buffer); |
194 | val = strtok(NULL, ", \t/-"); |
195 | if (!val) |
196 | retval = 0; |
197 | if (retval) |
198 | retval = read_nip(val, buffer + 4); |
199 | break; |
200 | case OPTION_STRING: |
201 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
202 | case OPTION_STR1035: |
203 | #endif |
204 | length = strlen(val); |
205 | if (length > 0) { |
206 | if (length > 254) length = 254; |
207 | opt = val; |
208 | retval = 1; |
209 | } |
210 | break; |
211 | case OPTION_BOOLEAN: |
212 | retval = read_yn(val, buffer); |
213 | break; |
214 | case OPTION_U8: |
215 | buffer[0] = strtoul(val, &endptr, 0); |
216 | retval = (endptr[0] == '\0'); |
217 | break; |
218 | /* htonX are macros in older libc's, using temp var |
219 | * in code below for safety */ |
220 | /* TODO: use bb_strtoX? */ |
221 | case OPTION_U16: { |
222 | unsigned long tmp = strtoul(val, &endptr, 0); |
223 | *result_u16 = htons(tmp); |
224 | retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/); |
225 | break; |
226 | } |
227 | case OPTION_S16: { |
228 | long tmp = strtol(val, &endptr, 0); |
229 | *result_u16 = htons(tmp); |
230 | retval = (endptr[0] == '\0'); |
231 | break; |
232 | } |
233 | case OPTION_U32: { |
234 | unsigned long tmp = strtoul(val, &endptr, 0); |
235 | *result_u32 = htonl(tmp); |
236 | retval = (endptr[0] == '\0'); |
237 | break; |
238 | } |
239 | case OPTION_S32: { |
240 | long tmp = strtol(val, &endptr, 0); |
241 | *result_u32 = htonl(tmp); |
242 | retval = (endptr[0] == '\0'); |
243 | break; |
244 | } |
245 | default: |
246 | break; |
247 | } |
248 | if (retval) |
249 | attach_option(opt_list, option, opt, length); |
250 | } while (retval && option->flags & OPTION_LIST); |
251 | return retval; |
252 | } |
253 | |
254 | static int FAST_FUNC read_staticlease(const char *const_line, void *arg) |
255 | { |
256 | char *line; |
257 | char *mac_string; |
258 | char *ip_string; |
259 | struct ether_addr mac_bytes; |
260 | uint32_t ip; |
261 | |
262 | /* Read mac */ |
263 | line = (char *) const_line; |
264 | mac_string = strtok_r(line, " \t", &line); |
265 | read_mac(mac_string, &mac_bytes); |
266 | |
267 | /* Read ip */ |
268 | ip_string = strtok_r(NULL, " \t", &line); |
269 | read_nip(ip_string, &ip); |
270 | |
271 | add_static_lease(arg, (uint8_t*) &mac_bytes, ip); |
272 | |
273 | log_static_leases(arg); |
274 | |
275 | return 1; |
276 | } |
277 | |
278 | |
279 | struct config_keyword { |
280 | const char *keyword; |
281 | int (*handler)(const char *line, void *var) FAST_FUNC; |
282 | void *var; |
283 | const char *def; |
284 | }; |
285 | |
286 | static const struct config_keyword keywords[] = { |
287 | /* keyword handler variable address default */ |
288 | {"start", read_nip, &(server_config.start_ip), "192.168.0.20"}, |
289 | {"end", read_nip, &(server_config.end_ip), "192.168.0.254"}, |
290 | {"interface", read_str, &(server_config.interface), "eth0"}, |
291 | /* Avoid "max_leases value not sane" warning by setting default |
292 | * to default_end_ip - default_start_ip + 1: */ |
293 | {"max_leases", read_u32, &(server_config.max_leases), "235"}, |
294 | {"auto_time", read_u32, &(server_config.auto_time), "7200"}, |
295 | {"decline_time", read_u32, &(server_config.decline_time), "3600"}, |
296 | {"conflict_time",read_u32, &(server_config.conflict_time),"3600"}, |
297 | {"offer_time", read_u32, &(server_config.offer_time), "60"}, |
298 | {"min_lease", read_u32, &(server_config.min_lease_sec),"60"}, |
299 | {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, |
300 | {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, |
301 | {"siaddr", read_nip, &(server_config.siaddr_nip), "0.0.0.0"}, |
302 | /* keywords with no defaults must be last! */ |
303 | {"option", read_opt, &(server_config.options), ""}, |
304 | {"opt", read_opt, &(server_config.options), ""}, |
305 | {"notify_file", read_str, &(server_config.notify_file), ""}, |
306 | {"sname", read_str, &(server_config.sname), ""}, |
307 | {"boot_file", read_str, &(server_config.boot_file), ""}, |
308 | {"static_lease", read_staticlease, &(server_config.static_leases), ""}, |
309 | }; |
310 | enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; |
311 | |
312 | void FAST_FUNC read_config(const char *file) |
313 | { |
314 | parser_t *parser; |
315 | const struct config_keyword *k; |
316 | unsigned i; |
317 | char *token[2]; |
318 | |
319 | for (i = 0; i < KWS_WITH_DEFAULTS; i++) |
320 | keywords[i].handler(keywords[i].def, keywords[i].var); |
321 | |
322 | parser = config_open(file); |
323 | while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { |
324 | for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) { |
325 | if (!strcasecmp(token[0], k->keyword)) { |
326 | if (!k->handler(token[1], k->var)) { |
327 | bb_error_msg("can't parse line %u in %s", |
328 | parser->lineno, file); |
329 | /* reset back to the default value */ |
330 | k->handler(k->def, k->var); |
331 | } |
332 | break; |
333 | } |
334 | } |
335 | } |
336 | config_close(parser); |
337 | |
338 | server_config.start_ip = ntohl(server_config.start_ip); |
339 | server_config.end_ip = ntohl(server_config.end_ip); |
340 | } |
341 | |
342 | |
343 | void FAST_FUNC write_leases(void) |
344 | { |
345 | int fd; |
346 | unsigned i; |
347 | leasetime_t curr; |
348 | int64_t written_at; |
349 | |
350 | fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC); |
351 | if (fd < 0) |
352 | return; |
353 | |
354 | curr = written_at = time(NULL); |
355 | |
356 | written_at = hton64(written_at); |
357 | full_write(fd, &written_at, sizeof(written_at)); |
358 | |
359 | for (i = 0; i < server_config.max_leases; i++) { |
360 | leasetime_t tmp_time; |
361 | |
362 | if (g_leases[i].lease_nip == 0) |
363 | continue; |
364 | |
365 | /* Screw with the time in the struct, for easier writing */ |
366 | tmp_time = g_leases[i].expires; |
367 | |
368 | g_leases[i].expires -= curr; |
369 | if ((signed_leasetime_t) g_leases[i].expires < 0) |
370 | g_leases[i].expires = 0; |
371 | g_leases[i].expires = htonl(g_leases[i].expires); |
372 | |
373 | /* No error check. If the file gets truncated, |
374 | * we lose some leases on restart. Oh well. */ |
375 | full_write(fd, &g_leases[i], sizeof(g_leases[i])); |
376 | |
377 | /* Then restore it when done */ |
378 | g_leases[i].expires = tmp_time; |
379 | } |
380 | close(fd); |
381 | |
382 | if (server_config.notify_file) { |
383 | // TODO: vfork-based child creation |
384 | char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file); |
385 | system(cmd); |
386 | free(cmd); |
387 | } |
388 | } |
389 | |
390 | |
391 | void FAST_FUNC read_leases(const char *file) |
392 | { |
393 | struct dyn_lease lease; |
394 | int64_t written_at, time_passed; |
395 | int fd; |
396 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 |
397 | unsigned i = 0; |
398 | #endif |
399 | |
400 | fd = open_or_warn(file, O_RDONLY); |
401 | if (fd < 0) |
402 | return; |
403 | |
404 | if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at)) |
405 | goto ret; |
406 | written_at = ntoh64(written_at); |
407 | |
408 | time_passed = time(NULL) - written_at; |
409 | /* Strange written_at, or lease file from old version of udhcpd |
410 | * which had no "written_at" field? */ |
411 | if ((uint64_t)time_passed > 12 * 60 * 60) |
412 | goto ret; |
413 | |
414 | while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) { |
415 | //FIXME: what if it matches some static lease? |
416 | uint32_t y = ntohl(lease.lease_nip); |
417 | if (y >= server_config.start_ip && y <= server_config.end_ip) { |
418 | signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed; |
419 | if (expires <= 0) |
420 | continue; |
421 | /* NB: add_lease takes "relative time", IOW, |
422 | * lease duration, not lease deadline. */ |
423 | if (add_lease(lease.lease_mac, lease.lease_nip, |
424 | expires, |
425 | lease.hostname, sizeof(lease.hostname) |
426 | ) == 0 |
427 | ) { |
428 | bb_error_msg("too many leases while loading %s", file); |
429 | break; |
430 | } |
431 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 |
432 | i++; |
433 | #endif |
434 | } |
435 | } |
436 | log1("Read %d leases", i); |
437 | ret: |
438 | close(fd); |
439 | } |