Annotation of /trunk/mkinitrd-magellan/busybox/networking/udhcp/files.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: 11741 byte(s)
Sun May 30 11:32:42 2010 UTC (14 years 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 | niro | 532 | /* 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 | niro | 984 | * |
6 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
7 | niro | 532 | */ |
8 | |||
9 | #include <netinet/ether.h> | ||
10 | |||
11 | #include "common.h" | ||
12 | #include "dhcpd.h" | ||
13 | #include "options.h" | ||
14 | |||
15 | niro | 984 | #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 | niro | 532 | |
25 | niro | 984 | |
26 | niro | 816 | /* on these functions, make sure your datatype matches */ |
27 | niro | 984 | static int FAST_FUNC read_nip(const char *line, void *arg) |
28 | niro | 532 | { |
29 | niro | 816 | len_and_sockaddr *lsa; |
30 | niro | 532 | |
31 | niro | 816 | 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 | niro | 532 | } |
38 | |||
39 | niro | 984 | |
40 | static int FAST_FUNC read_mac(const char *line, void *arg) | ||
41 | niro | 532 | { |
42 | niro | 984 | return NULL == ether_aton_r(line, (struct ether_addr *)arg); |
43 | niro | 532 | } |
44 | |||
45 | |||
46 | niro | 984 | static int FAST_FUNC read_str(const char *line, void *arg) |
47 | niro | 532 | { |
48 | char **dest = arg; | ||
49 | |||
50 | free(*dest); | ||
51 | *dest = xstrdup(line); | ||
52 | return 1; | ||
53 | } | ||
54 | |||
55 | |||
56 | niro | 984 | static int FAST_FUNC read_u32(const char *line, void *arg) |
57 | niro | 532 | { |
58 | niro | 816 | *(uint32_t*)arg = bb_strtou32(line, NULL, 10); |
59 | niro | 532 | return errno == 0; |
60 | } | ||
61 | |||
62 | |||
63 | niro | 984 | static int FAST_FUNC read_yn(const char *line, void *arg) |
64 | niro | 532 | { |
65 | char *dest = arg; | ||
66 | |||
67 | niro | 816 | if (!strcasecmp("yes", line)) { |
68 | niro | 532 | *dest = 1; |
69 | niro | 816 | return 1; |
70 | } | ||
71 | if (!strcasecmp("no", line)) { | ||
72 | niro | 532 | *dest = 0; |
73 | niro | 816 | return 1; |
74 | } | ||
75 | return 0; | ||
76 | niro | 532 | } |
77 | |||
78 | |||
79 | /* find option 'code' in opt_list */ | ||
80 | niro | 816 | struct option_set* FAST_FUNC find_option(struct option_set *opt_list, uint8_t code) |
81 | niro | 532 | { |
82 | while (opt_list && opt_list->data[OPT_CODE] < code) | ||
83 | opt_list = opt_list->next; | ||
84 | |||
85 | niro | 816 | if (opt_list && opt_list->data[OPT_CODE] == code) |
86 | return opt_list; | ||
87 | return NULL; | ||
88 | niro | 532 | } |
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 | niro | 816 | if (!existing) { |
99 | niro | 984 | log2("Attaching option %02x to list", option->code); |
100 | niro | 532 | |
101 | niro | 816 | #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 | niro | 532 | /* make a new option */ |
108 | niro | 816 | new = xmalloc(sizeof(*new)); |
109 | niro | 532 | 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 | niro | 816 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
121 | if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) | ||
122 | free(buffer); | ||
123 | #endif | ||
124 | return; | ||
125 | niro | 532 | } |
126 | niro | 816 | |
127 | /* add it to an existing option */ | ||
128 | niro | 984 | log1("Attaching option %02x to existing member of list", option->code); |
129 | niro | 816 | 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 | niro | 532 | } |
156 | |||
157 | |||
158 | /* read a dhcp option and add it to opt_list */ | ||
159 | niro | 984 | static int FAST_FUNC read_opt(const char *const_line, void *arg) |
160 | niro | 532 | { |
161 | struct option_set **opt_list = arg; | ||
162 | char *opt, *val, *endptr; | ||
163 | niro | 816 | char *line; |
164 | niro | 532 | const struct dhcp_option *option; |
165 | niro | 816 | int retval, length, idx; |
166 | char buffer[8] ALIGNED(4); | ||
167 | niro | 532 | 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 | niro | 816 | if (!opt) |
174 | return 0; | ||
175 | niro | 532 | |
176 | niro | 816 | idx = index_in_strings(dhcp_option_strings, opt); /* NB: was strcasecmp! */ |
177 | if (idx < 0) | ||
178 | return 0; | ||
179 | option = &dhcp_options[idx]; | ||
180 | niro | 532 | |
181 | niro | 816 | retval = 0; |
182 | niro | 532 | do { |
183 | val = strtok(NULL, ", \t"); | ||
184 | if (!val) break; | ||
185 | niro | 816 | length = dhcp_option_lengths[option->flags & TYPE_MASK]; |
186 | niro | 532 | retval = 0; |
187 | opt = buffer; /* new meaning for variable opt */ | ||
188 | switch (option->flags & TYPE_MASK) { | ||
189 | case OPTION_IP: | ||
190 | niro | 984 | retval = read_nip(val, buffer); |
191 | niro | 532 | break; |
192 | case OPTION_IP_PAIR: | ||
193 | niro | 984 | retval = read_nip(val, buffer); |
194 | niro | 816 | val = strtok(NULL, ", \t/-"); |
195 | if (!val) | ||
196 | retval = 0; | ||
197 | if (retval) | ||
198 | niro | 984 | retval = read_nip(val, buffer + 4); |
199 | niro | 532 | break; |
200 | case OPTION_STRING: | ||
201 | niro | 816 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
202 | case OPTION_STR1035: | ||
203 | #endif | ||
204 | niro | 532 | 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 | niro | 816 | /* 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 | niro | 532 | break; |
226 | niro | 816 | } |
227 | case OPTION_S16: { | ||
228 | long tmp = strtol(val, &endptr, 0); | ||
229 | *result_u16 = htons(tmp); | ||
230 | niro | 532 | retval = (endptr[0] == '\0'); |
231 | break; | ||
232 | niro | 816 | } |
233 | case OPTION_U32: { | ||
234 | unsigned long tmp = strtoul(val, &endptr, 0); | ||
235 | *result_u32 = htonl(tmp); | ||
236 | niro | 532 | retval = (endptr[0] == '\0'); |
237 | break; | ||
238 | niro | 816 | } |
239 | case OPTION_S32: { | ||
240 | long tmp = strtol(val, &endptr, 0); | ||
241 | *result_u32 = htonl(tmp); | ||
242 | niro | 532 | retval = (endptr[0] == '\0'); |
243 | break; | ||
244 | niro | 816 | } |
245 | niro | 532 | 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 | niro | 984 | static int FAST_FUNC read_staticlease(const char *const_line, void *arg) |
255 | niro | 532 | { |
256 | char *line; | ||
257 | char *mac_string; | ||
258 | char *ip_string; | ||
259 | niro | 984 | struct ether_addr mac_bytes; |
260 | uint32_t ip; | ||
261 | niro | 532 | |
262 | /* Read mac */ | ||
263 | line = (char *) const_line; | ||
264 | niro | 984 | mac_string = strtok_r(line, " \t", &line); |
265 | read_mac(mac_string, &mac_bytes); | ||
266 | niro | 532 | |
267 | /* Read ip */ | ||
268 | niro | 984 | ip_string = strtok_r(NULL, " \t", &line); |
269 | read_nip(ip_string, &ip); | ||
270 | niro | 532 | |
271 | niro | 984 | add_static_lease(arg, (uint8_t*) &mac_bytes, ip); |
272 | niro | 532 | |
273 | niro | 984 | log_static_leases(arg); |
274 | niro | 532 | |
275 | return 1; | ||
276 | } | ||
277 | |||
278 | |||
279 | niro | 816 | struct config_keyword { |
280 | const char *keyword; | ||
281 | niro | 984 | int (*handler)(const char *line, void *var) FAST_FUNC; |
282 | niro | 816 | void *var; |
283 | const char *def; | ||
284 | }; | ||
285 | |||
286 | niro | 532 | static const struct config_keyword keywords[] = { |
287 | niro | 816 | /* keyword handler variable address default */ |
288 | niro | 984 | {"start", read_nip, &(server_config.start_ip), "192.168.0.20"}, |
289 | {"end", read_nip, &(server_config.end_ip), "192.168.0.254"}, | ||
290 | niro | 816 | {"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 | niro | 984 | {"min_lease", read_u32, &(server_config.min_lease_sec),"60"}, |
299 | niro | 816 | {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, |
300 | {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, | ||
301 | niro | 984 | {"siaddr", read_nip, &(server_config.siaddr_nip), "0.0.0.0"}, |
302 | niro | 816 | /* 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 | niro | 532 | }; |
310 | niro | 816 | enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; |
311 | niro | 532 | |
312 | niro | 816 | void FAST_FUNC read_config(const char *file) |
313 | niro | 532 | { |
314 | niro | 816 | parser_t *parser; |
315 | const struct config_keyword *k; | ||
316 | unsigned i; | ||
317 | char *token[2]; | ||
318 | niro | 532 | |
319 | niro | 816 | for (i = 0; i < KWS_WITH_DEFAULTS; i++) |
320 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
321 | niro | 532 | |
322 | niro | 816 | 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 | niro | 532 | /* reset back to the default value */ |
330 | niro | 816 | k->handler(k->def, k->var); |
331 | niro | 532 | } |
332 | niro | 816 | break; |
333 | } | ||
334 | } | ||
335 | niro | 532 | } |
336 | niro | 816 | 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 | niro | 532 | } |
341 | |||
342 | |||
343 | niro | 816 | void FAST_FUNC write_leases(void) |
344 | niro | 532 | { |
345 | niro | 984 | int fd; |
346 | niro | 532 | unsigned i; |
347 | niro | 984 | leasetime_t curr; |
348 | int64_t written_at; | ||
349 | niro | 532 | |
350 | niro | 984 | fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC); |
351 | if (fd < 0) | ||
352 | niro | 532 | return; |
353 | |||
354 | niro | 984 | curr = written_at = time(NULL); |
355 | |||
356 | written_at = hton64(written_at); | ||
357 | full_write(fd, &written_at, sizeof(written_at)); | ||
358 | |||
359 | niro | 532 | for (i = 0; i < server_config.max_leases; i++) { |
360 | niro | 984 | leasetime_t tmp_time; |
361 | niro | 532 | |
362 | niro | 984 | if (g_leases[i].lease_nip == 0) |
363 | continue; | ||
364 | niro | 532 | |
365 | niro | 984 | /* Screw with the time in the struct, for easier writing */ |
366 | tmp_time = g_leases[i].expires; | ||
367 | niro | 532 | |
368 | niro | 984 | 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 | niro | 532 | } |
380 | niro | 984 | close(fd); |
381 | niro | 532 | |
382 | if (server_config.notify_file) { | ||
383 | niro | 816 | // TODO: vfork-based child creation |
384 | niro | 532 | char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file); |
385 | system(cmd); | ||
386 | free(cmd); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | |||
391 | niro | 816 | void FAST_FUNC read_leases(const char *file) |
392 | niro | 532 | { |
393 | niro | 984 | 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 | niro | 532 | |
400 | niro | 984 | fd = open_or_warn(file, O_RDONLY); |
401 | if (fd < 0) | ||
402 | niro | 532 | return; |
403 | |||
404 | niro | 984 | 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 | niro | 816 | if (y >= server_config.start_ip && y <= server_config.end_ip) { |
418 | niro | 984 | 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 | niro | 532 | bb_error_msg("too many leases while loading %s", file); |
429 | break; | ||
430 | } | ||
431 | niro | 984 | #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 |
432 | niro | 532 | i++; |
433 | niro | 984 | #endif |
434 | niro | 532 | } |
435 | } | ||
436 | niro | 984 | log1("Read %d leases", i); |
437 | ret: | ||
438 | close(fd); | ||
439 | niro | 532 | } |