1 /* vi: set sw=4 ts=4: */
3 * files.c -- DHCP server file manipulation *
4 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
6 * Licensed under GPLv2, see file LICENSE in this tarball for details.
9 #include <netinet/ether.h>
16 static inline uint64_t hton64(uint64_t v
)
18 return (((uint64_t)htonl(v
)) << 32) | htonl(v
>> 32);
23 #define ntoh64(v) hton64(v)
26 /* on these functions, make sure your datatype matches */
27 static int read_ip(const char *line
, void *arg
)
29 len_and_sockaddr
*lsa
;
31 lsa
= host_and_af2sockaddr(line
, 0, AF_INET
);
34 *(uint32_t*)arg
= lsa
->u
.sin
.sin_addr
.s_addr
;
40 static int read_mac(const char *line
, void *arg
)
42 return NULL
== ether_aton_r(line
, (struct ether_addr
*)arg
);
46 static int read_str(const char *line
, void *arg
)
51 *dest
= xstrdup(line
);
56 static int read_u32(const char *line
, void *arg
)
58 *(uint32_t*)arg
= bb_strtou32(line
, NULL
, 10);
63 static int read_yn(const char *line
, void *arg
)
67 if (!strcasecmp("yes", line
)) {
71 if (!strcasecmp("no", line
)) {
79 /* find option 'code' in opt_list */
80 struct option_set
* FAST_FUNC
find_option(struct option_set
*opt_list
, uint8_t code
)
82 while (opt_list
&& opt_list
->data
[OPT_CODE
] < code
)
83 opt_list
= opt_list
->next
;
85 if (opt_list
&& opt_list
->data
[OPT_CODE
] == code
)
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
)
95 struct option_set
*existing
, *new, **curr
;
97 existing
= find_option(*opt_list
, option
->code
);
99 DEBUG("Attaching option %02x to list", option
->code
);
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
);
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
);
115 while (*curr
&& (*curr
)->data
[OPT_CODE
] < option
->code
)
116 curr
= &(*curr
)->next
;
120 #if ENABLE_FEATURE_UDHCP_RFC3397
121 if ((option
->flags
& TYPE_MASK
) == OPTION_STR1035
&& buffer
!= NULL
)
127 /* add it to an existing option */
128 DEBUG("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
);
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)
143 /* add space separator between STRING options in a list */
144 existing
->data
[existing
->data
[OPT_LEN
] + 2] = ' ';
145 existing
->data
[OPT_LEN
]++;
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
)
154 } /* else, ignore the new data */
158 /* read a dhcp option and add it to opt_list */
159 static int read_opt(const char *const_line
, void *arg
)
161 struct option_set
**opt_list
= arg
;
162 char *opt
, *val
, *endptr
;
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
;
170 /* Cheat, the only const line we'll actually get is "" */
171 line
= (char *) const_line
;
172 opt
= strtok(line
, " \t=");
176 idx
= index_in_strings(dhcp_option_strings
, opt
); /* NB: was strcasecmp! */
179 option
= &dhcp_options
[idx
];
183 val
= strtok(NULL
, ", \t");
185 length
= dhcp_option_lengths
[option
->flags
& TYPE_MASK
];
187 opt
= buffer
; /* new meaning for variable opt */
188 switch (option
->flags
& TYPE_MASK
) {
190 retval
= read_ip(val
, buffer
);
193 retval
= read_ip(val
, buffer
);
194 val
= strtok(NULL
, ", \t/-");
198 retval
= read_ip(val
, buffer
+ 4);
201 #if ENABLE_FEATURE_UDHCP_RFC3397
204 length
= strlen(val
);
206 if (length
> 254) length
= 254;
212 retval
= read_yn(val
, buffer
);
215 buffer
[0] = strtoul(val
, &endptr
, 0);
216 retval
= (endptr
[0] == '\0');
218 /* htonX are macros in older libc's, using temp var
219 * in code below for safety */
220 /* TODO: use bb_strtoX? */
222 unsigned long tmp
= strtoul(val
, &endptr
, 0);
223 *result_u16
= htons(tmp
);
224 retval
= (endptr
[0] == '\0' /*&& tmp < 0x10000*/);
228 long tmp
= strtol(val
, &endptr
, 0);
229 *result_u16
= htons(tmp
);
230 retval
= (endptr
[0] == '\0');
234 unsigned long tmp
= strtoul(val
, &endptr
, 0);
235 *result_u32
= htonl(tmp
);
236 retval
= (endptr
[0] == '\0');
240 long tmp
= strtol(val
, &endptr
, 0);
241 *result_u32
= htonl(tmp
);
242 retval
= (endptr
[0] == '\0');
249 attach_option(opt_list
, option
, opt
, length
);
250 } while (retval
&& option
->flags
& OPTION_LIST
);
254 static int read_staticlease(const char *const_line
, void *arg
)
259 struct ether_addr mac_bytes
;
263 line
= (char *) const_line
;
264 mac_string
= strtok_r(line
, " \t", &line
);
265 read_mac(mac_string
, &mac_bytes
);
268 ip_string
= strtok_r(NULL
, " \t", &line
);
269 read_ip(ip_string
, &ip
);
271 addStaticLease(arg
, (uint8_t*) &mac_bytes
, ip
);
273 if (ENABLE_UDHCP_DEBUG
) printStaticLeases(arg
);
279 struct config_keyword
{
281 int (*handler
)(const char *line
, void *var
);
286 static const struct config_keyword keywords
[] = {
287 /* keyword handler variable address default */
288 {"start", read_ip
, &(server_config
.start_ip
), "192.168.0.20"},
289 {"end", read_ip
, &(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 // {"remaining", read_yn, &(server_config.remaining), "yes"},
295 {"auto_time", read_u32
, &(server_config
.auto_time
), "7200"},
296 {"decline_time", read_u32
, &(server_config
.decline_time
), "3600"},
297 {"conflict_time",read_u32
, &(server_config
.conflict_time
),"3600"},
298 {"offer_time", read_u32
, &(server_config
.offer_time
), "60"},
299 {"min_lease", read_u32
, &(server_config
.min_lease
), "60"},
300 {"lease_file", read_str
, &(server_config
.lease_file
), LEASES_FILE
},
301 {"pidfile", read_str
, &(server_config
.pidfile
), "/var/run/udhcpd.pid"},
302 {"siaddr", read_ip
, &(server_config
.siaddr
), "0.0.0.0"},
303 /* keywords with no defaults must be last! */
304 {"option", read_opt
, &(server_config
.options
), ""},
305 {"opt", read_opt
, &(server_config
.options
), ""},
306 {"notify_file", read_str
, &(server_config
.notify_file
), ""},
307 {"sname", read_str
, &(server_config
.sname
), ""},
308 {"boot_file", read_str
, &(server_config
.boot_file
), ""},
309 {"static_lease", read_staticlease
, &(server_config
.static_leases
), ""},
311 enum { KWS_WITH_DEFAULTS
= ARRAY_SIZE(keywords
) - 6 };
313 void FAST_FUNC
read_config(const char *file
)
316 const struct config_keyword
*k
;
320 for (i
= 0; i
< KWS_WITH_DEFAULTS
; i
++)
321 keywords
[i
].handler(keywords
[i
].def
, keywords
[i
].var
);
323 parser
= config_open(file
);
324 while (config_read(parser
, token
, 2, 2, "# \t", PARSE_NORMAL
)) {
325 for (k
= keywords
, i
= 0; i
< ARRAY_SIZE(keywords
); k
++, i
++) {
326 if (!strcasecmp(token
[0], k
->keyword
)) {
327 if (!k
->handler(token
[1], k
->var
)) {
328 bb_error_msg("can't parse line %u in %s",
329 parser
->lineno
, file
);
330 /* reset back to the default value */
331 k
->handler(k
->def
, k
->var
);
337 config_close(parser
);
339 server_config
.start_ip
= ntohl(server_config
.start_ip
);
340 server_config
.end_ip
= ntohl(server_config
.end_ip
);
344 void FAST_FUNC
write_leases(void)
351 fd
= open_or_warn(server_config
.lease_file
, O_WRONLY
|O_CREAT
|O_TRUNC
);
355 curr
= written_at
= time(NULL
);
357 written_at
= hton64(written_at
);
358 full_write(fd
, &written_at
, sizeof(written_at
));
360 for (i
= 0; i
< server_config
.max_leases
; i
++) {
361 leasetime_t tmp_time
;
363 if (leases
[i
].yiaddr
== 0)
366 /* Screw with the time in the struct, for easier writing */
367 tmp_time
= leases
[i
].expires
;
369 leases
[i
].expires
-= curr
;
370 if ((signed_leasetime_t
) leases
[i
].expires
< 0)
371 leases
[i
].expires
= 0;
372 leases
[i
].expires
= htonl(leases
[i
].expires
);
374 /* No error check. If the file gets truncated,
375 * we lose some leases on restart. Oh well. */
376 full_write(fd
, &leases
[i
], sizeof(leases
[i
]));
378 /* Then restore it when done */
379 leases
[i
].expires
= tmp_time
;
383 if (server_config
.notify_file
) {
384 // TODO: vfork-based child creation
385 char *cmd
= xasprintf("%s %s", server_config
.notify_file
, server_config
.lease_file
);
392 void FAST_FUNC
read_leases(const char *file
)
394 struct dhcpOfferedAddr lease
;
395 int64_t written_at
, time_passed
;
397 USE_UDHCP_DEBUG(unsigned i
;)
399 fd
= open_or_warn(file
, O_RDONLY
);
403 if (full_read(fd
, &written_at
, sizeof(written_at
)) != sizeof(written_at
))
405 written_at
= ntoh64(written_at
);
407 time_passed
= time(NULL
) - written_at
;
408 /* Strange written_at, or lease file from old version of udhcpd
409 * which had no "written_at" field? */
410 if ((uint64_t)time_passed
> 12 * 60 * 60)
413 USE_UDHCP_DEBUG(i
= 0;)
414 while (full_read(fd
, &lease
, sizeof(lease
)) == sizeof(lease
)) {
415 /* ADDME: what if it matches some static lease? */
416 uint32_t y
= ntohl(lease
.yiaddr
);
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
;
421 /* NB: add_lease takes "relative time", IOW,
422 * lease duration, not lease deadline. */
423 if (!(add_lease(lease
.chaddr
, lease
.yiaddr
, expires
, NULL
/* was lease.hostname. bug in add_lease, disabled */ ))) {
424 bb_error_msg("too many leases while loading %s", file
);
427 USE_UDHCP_DEBUG(i
++;)
430 DEBUG("Read %d leases", i
);