2 * The dhcpd-pools has BSD 2-clause license which also known as "Simplified
3 * BSD License" or "FreeBSD License".
5 * Copyright 2006- Sami Kerola. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the
19 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGE.
31 * The views and conclusions contained in the software and documentation are
32 * those of the authors and should not be interpreted as representing
33 * official policies, either expressed or implied, of Sami Kerola.
42 #else /* Not STDC_HEADERS */
43 extern char *malloc();
44 #define EXIT_FAILURE 1 /* Failing exit status. */
45 #define EXIT_SUCCESS 0 /* Successful exit status. */
46 #endif /* STDC_HEADERS */
54 #include <arpa/inet.h>
61 #include <netinet/in.h>
66 #define _XOPEN_SOURCE 600
69 #include "dhcpd-pools.h"
72 /* Parse dhcpd.leases file. All performance boosts for this function are
74 int parse_leases(void)
77 char *line, *ipstring, *macstring = NULL;
79 struct stat lease_file_stats;
80 struct macaddr_t *macaddr_p = NULL;
81 unsigned long leasesmallocsize;
82 unsigned long touchesmallocsize;
83 unsigned long backupsmallocsize;
84 int sw_active_lease = 0;
86 num_touches = num_leases = num_backups = 0;
88 dhcpd_leases = fopen(config.dhcpdlease_file, "r");
89 if (dhcpd_leases == NULL) {
90 err(EXIT_FAILURE, "parse_leases: %s",
91 config.dhcpdlease_file);
93 #ifdef POSIX_FADV_WILLNEED
94 posix_fadvise((long) dhcpd_leases, 0, 0, POSIX_FADV_WILLNEED);
96 err(EXIT_FAILURE, "parse_leases: fadvise %s",
97 config.dhcpdlease_file);
99 #endif /* POSIX_FADV_WILLNEED */
100 #ifdef POSIX_FADV_SEQUENTIAL
101 posix_fadvise((long) dhcpd_leases, 0, 0, POSIX_FADV_SEQUENTIAL);
103 err(EXIT_FAILURE, "parse_leases: fadvise %s",
104 config.dhcpdlease_file);
106 #endif /* POSIX_FADV_SEQUENTIAL */
108 /* I found out that there's one lease address per 300 bytes in
109 * dhcpd.leases file. Malloc is little bit pessimistic and uses 250.
110 * If someone has higher density in lease file I'm interested to
111 * hear about that. */
112 if (stat(config.dhcpdlease_file, &lease_file_stats)) {
113 err(EXIT_FAILURE, "parse_leases: %s",
114 config.dhcpdlease_file);
116 leasesmallocsize = (lease_file_stats.st_size / 250) + MAXLEN - 2;
117 touchesmallocsize = (lease_file_stats.st_size / 250) + MAXLEN - 2;
118 backupsmallocsize = (lease_file_stats.st_size / 120) + MAXLEN - 2;
119 leases = safe_malloc(sizeof(long int) * leasesmallocsize);
121 safe_malloc((size_t) sizeof(long int) * touchesmallocsize);
123 line = safe_malloc(sizeof(long int) * MAXLEN);
124 ipstring = safe_malloc(sizeof(long int) * MAXLEN);
125 if (config.output_format[0] == 'X') {
126 macstring = safe_malloc(sizeof(char) * 18);
127 macaddr = safe_malloc(sizeof(struct macaddr_t));
129 macaddr_p->next = NULL;
132 while (!feof(dhcpd_leases)) {
133 fgets(line, MAXLEN, dhcpd_leases);
134 /* It's a lease, save IP */
135 if (strstr(line, "lease") == line) {
136 strncpy(ipstring, line, (size_t) MAXLEN);
137 nth_field(2, ipstring, ipstring);
138 inet_aton(ipstring, &inp);
141 /* Copy IP to correct array */
142 else if (strstr(line, "binding state active")) {
143 leases[num_leases] = htonl(inp.s_addr);
145 assert(!(leasesmallocsize < num_leases));
147 } else if (strstr(line, " binding state free")) {
148 touches[num_touches] = htonl(inp.s_addr);
150 assert(!(touchesmallocsize < num_touches));
151 } else if (strstr(line, " binding state backup")) {
152 if (num_backups == 0) {
154 safe_malloc((size_t) sizeof(long int) *
157 backups[num_backups] = htonl(inp.s_addr);
159 assert(!(backupsmallocsize < num_backups));
162 if ((macaddr != NULL)
163 && (sw_active_lease == 1)
164 && (strstr(line, "hardware ethernet"))) {
165 nth_field(3, macstring, line);
166 macstring[17] = '\0';
167 macaddr_p->ethernet = safe_strdup(macstring);
168 macaddr_p->ip = safe_strdup(ipstring);
170 safe_malloc(sizeof(struct macaddr_t));
171 macaddr_p = macaddr_p->next;
172 macaddr_p->next = NULL;
177 if (macaddr != NULL) {
180 fclose(dhcpd_leases);
184 /* Like strcpy but for field which is separated by white spaces. Number of
185 * first field is 1 and not 0 like C programs should have. Question of
186 * semantics, send mail to author if this annoys. All performance boosts for
187 * this function are well come. */
188 int nth_field(int n, char *dest, const char *src)
190 int i, j = 0, wordn = 0, len;
194 for (i = 0; i < len; i++) {
195 if (isspace(src[i])) {
214 /* dhcpd.conf interesting words */
215 int is_interesting_config_clause(char *s)
217 if (strstr(s, "range")) {
219 } else if (strstr(s, "shared-network")) {
221 } else if (strstr(s, "include")) {
228 /* FIXME: This spagetti monster function need to be rewrote at least ones. */
229 void parse_config(int is_include, char *config_file,
230 struct shared_network_t *shared_p)
233 int i = 0, newclause = true, argument = false, comment =
234 false, braces = 0, quote = false;
236 int braces_shared = 1000;
238 struct range_t *range_p;
240 word = safe_malloc(sizeof(char) * MAXLEN);
243 /* Default place holder for ranges "All networks". */
244 shared_p->name = shared_networks->name;
247 /* Open configuration file */
248 dhcpd_config = fopen(config_file, "r");
249 if (dhcpd_config == NULL) {
250 err(EXIT_FAILURE, "parse_config: %s", config_file);
252 #ifdef POSIX_FADV_WILLNEED
253 posix_fadvise((long) dhcpd_config, 0, 0, POSIX_FADV_WILLNEED);
255 err(EXIT_FAILURE, "parse_config: fadvise %s", config_file);
257 #endif /* POSIX_FADV_WILLNEED */
258 #ifdef POSIX_FADV_SEQUENTIAL
259 posix_fadvise((long) dhcpd_config, 0, 0, POSIX_FADV_SEQUENTIAL);
261 err(EXIT_FAILURE, "parse_config: fadvise %s", config_file);
263 #endif /* POSIX_FADV_SEQUENTIAL */
265 /* Very hairy stuff begins. */
266 while (!feof(dhcpd_config)) {
267 c = fgetc(dhcpd_config);
268 /* Certain characters are magical */
270 /* Handle comments if they are not quoted */
272 if (quote == false) {
277 if (comment == false) {
279 /* Either one or zero */
284 /* New line resets comment section, but
286 if (quote == false) {
291 /* Quoted colon does not mean new clause */
295 if (comment == false && argument != 2
299 } else if (argument == 2) {
300 /* Range ends to ; and this hair in code
301 * make two ranges wrote to gether like...
303 * range 10.20.30.40 10.20.30.41;range 10.20.30.42 10.20.30.43;
305 * ...to be interpreted correctly. */
313 if (comment == false) {
316 /* i == 0 detects word that ends to brace like:
318 * shared-network DSL{ ... */
327 if (comment == false) {
329 /* End of shared-network */
330 if (braces_shared == braces) {
331 /* FIXME: Using 1000 is lame, but
333 braces_shared = 1000;
334 shared_p = shared_networks;
336 /* Not literally true, but works for this
345 /* Either inside comment or Nth word of clause. */
347 || (newclause == false && argument == 0)) {
350 /* Strip white spaces before new clause word. */
351 if ((newclause == true || argument != 0) && isspace(c)
355 /* Save to word which clause this is. */
356 if ((newclause == true || argument != 0)
357 && (!isspace(c) || quote == true)) {
360 /* Long word which is almost causing overflow. None
361 * of words are this long which the program is
369 /* See if clause is something that parser is looking for. */
370 else if (newclause == true) {
371 /* Insert string end & set state */
376 argument = is_interesting_config_clause(word);
378 /* words after range, shared-network or include */
379 else if (argument != 0) {
386 /* printf ("range 2nd ip: %s\n", word); */
387 range_p = ranges + num_ranges;
388 inet_aton(word, &inp);
390 range_p->last_ip = htonl(inp.s_addr) + 1;
392 range_p->touched = 0;
393 range_p->backups = 0;
394 range_p->shared_net = shared_p;
396 if (RANGES < num_ranges + 1) {
403 range_p = ranges + num_ranges;
408 /* printf ("range 1nd ip: %s\n", word); */
409 range_p = ranges + num_ranges;
410 if (!(inet_aton(word, &inp))) {
411 /* word was not ip, try
415 range_p->first_ip = htonl(inp.s_addr) - 1;
419 /* printf ("shared-network named: %s\n", word); */
420 num_shared_networks++;
422 shared_networks + num_shared_networks;
423 shared_p->name = safe_strdup(word);
424 shared_p->available = 0;
426 shared_p->touched = 0;
427 shared_p->backups = 0;
428 if (SHARED_NETWORKS <
429 num_shared_networks + 2) {
431 * away by reallocationg
434 "parse_config: increase default.h SHARED_NETWORKS and recompile");
437 braces_shared = braces;
440 /* printf ("include file: %s\n", word); */
442 parse_config(false, word, shared_p);
446 /* printf ("nothing interesting: %s\n", word); */
450 warnx("impossible occurred, report a bug");
456 fclose(dhcpd_config);