aboutsummaryrefslogtreecommitdiff
path: root/networking/udhcp/dhcpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/udhcp/dhcpd.c')
-rw-r--r--networking/udhcp/dhcpd.c223
1 files changed, 221 insertions, 2 deletions
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index e93a9f1da..8bd65df52 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -33,11 +33,232 @@
33//usage: "\n -P N Use port N (default 67)" 33//usage: "\n -P N Use port N (default 67)"
34//usage: ) 34//usage: )
35 35
36#include <netinet/ether.h>
36#include <syslog.h> 37#include <syslog.h>
37#include "common.h" 38#include "common.h"
38#include "dhcpc.h" 39#include "dhcpc.h"
39#include "dhcpd.h" 40#include "dhcpd.h"
40 41
42/* on these functions, make sure your datatype matches */
43static int FAST_FUNC read_str(const char *line, void *arg)
44{
45 char **dest = arg;
46
47 free(*dest);
48 *dest = xstrdup(line);
49 return 1;
50}
51
52static int FAST_FUNC read_u32(const char *line, void *arg)
53{
54 *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
55 return errno == 0;
56}
57
58static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
59{
60 char *line;
61 char *mac_string;
62 char *ip_string;
63 struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
64 uint32_t nip;
65
66 /* Read mac */
67 line = (char *) const_line;
68 mac_string = strtok_r(line, " \t", &line);
69 if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
70 return 0;
71
72 /* Read ip */
73 ip_string = strtok_r(NULL, " \t", &line);
74 if (!ip_string || !udhcp_str2nip(ip_string, &nip))
75 return 0;
76
77 add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
78
79 log_static_leases(arg);
80
81 return 1;
82}
83
84struct config_keyword {
85 const char *keyword;
86 int (*handler)(const char *line, void *var) FAST_FUNC;
87 unsigned ofs;
88 const char *def;
89};
90
91#define OFS(field) offsetof(struct server_config_t, field)
92
93static const struct config_keyword keywords[] = {
94 /* keyword handler variable address default */
95 {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
96 {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
97 {"interface" , read_str , OFS(interface ), "eth0"},
98 /* Avoid "max_leases value not sane" warning by setting default
99 * to default_end_ip - default_start_ip + 1: */
100 {"max_leases" , read_u32 , OFS(max_leases ), "235"},
101 {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
102 {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
103 {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
104 {"offer_time" , read_u32 , OFS(offer_time ), "60"},
105 {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
106 {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
107 {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"},
108 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
109 /* keywords with no defaults must be last! */
110 {"option" , udhcp_str2optset, OFS(options ), ""},
111 {"opt" , udhcp_str2optset, OFS(options ), ""},
112 {"notify_file" , read_str , OFS(notify_file ), NULL},
113 {"sname" , read_str , OFS(sname ), NULL},
114 {"boot_file" , read_str , OFS(boot_file ), NULL},
115 {"static_lease" , read_staticlease, OFS(static_leases), ""},
116};
117enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
118
119static NOINLINE void read_config(const char *file)
120{
121 parser_t *parser;
122 const struct config_keyword *k;
123 unsigned i;
124 char *token[2];
125
126 for (i = 0; i < KWS_WITH_DEFAULTS; i++)
127 keywords[i].handler(keywords[i].def, (char*)&server_config + keywords[i].ofs);
128
129 parser = config_open(file);
130 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
131 for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
132 if (strcasecmp(token[0], k->keyword) == 0) {
133 if (!k->handler(token[1], (char*)&server_config + k->ofs)) {
134 bb_error_msg("can't parse line %u in %s",
135 parser->lineno, file);
136 /* reset back to the default value */
137 k->handler(k->def, (char*)&server_config + k->ofs);
138 }
139 break;
140 }
141 }
142 }
143 config_close(parser);
144
145 server_config.start_ip = ntohl(server_config.start_ip);
146 server_config.end_ip = ntohl(server_config.end_ip);
147}
148
149static void write_leases(void)
150{
151 int fd;
152 unsigned i;
153 leasetime_t curr;
154 int64_t written_at;
155
156 fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
157 if (fd < 0)
158 return;
159
160 curr = written_at = time(NULL);
161
162 written_at = SWAP_BE64(written_at);
163 full_write(fd, &written_at, sizeof(written_at));
164
165 for (i = 0; i < server_config.max_leases; i++) {
166 leasetime_t tmp_time;
167
168 if (g_leases[i].lease_nip == 0)
169 continue;
170
171 /* Screw with the time in the struct, for easier writing */
172 tmp_time = g_leases[i].expires;
173
174 g_leases[i].expires -= curr;
175 if ((signed_leasetime_t) g_leases[i].expires < 0)
176 g_leases[i].expires = 0;
177 g_leases[i].expires = htonl(g_leases[i].expires);
178
179 /* No error check. If the file gets truncated,
180 * we lose some leases on restart. Oh well. */
181 full_write(fd, &g_leases[i], sizeof(g_leases[i]));
182
183 /* Then restore it when done */
184 g_leases[i].expires = tmp_time;
185 }
186 close(fd);
187
188 if (server_config.notify_file) {
189 char *argv[3];
190 argv[0] = server_config.notify_file;
191 argv[1] = server_config.lease_file;
192 argv[2] = NULL;
193 spawn_and_wait(argv);
194 }
195}
196
197static NOINLINE void read_leases(const char *file)
198{
199 struct dyn_lease lease;
200 int64_t written_at, time_passed;
201 int fd;
202#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
203 unsigned i = 0;
204#endif
205
206 fd = open_or_warn(file, O_RDONLY);
207 if (fd < 0)
208 return;
209
210 if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
211 goto ret;
212 written_at = SWAP_BE64(written_at);
213
214 time_passed = time(NULL) - written_at;
215 /* Strange written_at, or lease file from old version of udhcpd
216 * which had no "written_at" field? */
217 if ((uint64_t)time_passed > 12 * 60 * 60)
218 goto ret;
219
220 while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
221 uint32_t y = ntohl(lease.lease_nip);
222 if (y >= server_config.start_ip && y <= server_config.end_ip) {
223 signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
224 uint32_t static_nip;
225
226 if (expires <= 0)
227 /* We keep expired leases: add_lease() will add
228 * a lease with 0 seconds remaining.
229 * Fewer IP address changes this way for mass reboot scenario.
230 */
231 expires = 0;
232
233 /* Check if there is a different static lease for this IP or MAC */
234 static_nip = get_static_nip_by_mac(server_config.static_leases, lease.lease_mac);
235 if (static_nip) {
236 /* NB: we do not add lease even if static_nip == lease.lease_nip.
237 */
238 continue;
239 }
240 if (is_nip_reserved(server_config.static_leases, lease.lease_nip))
241 continue;
242
243 /* NB: add_lease takes "relative time", IOW,
244 * lease duration, not lease deadline. */
245 if (add_lease(lease.lease_mac, lease.lease_nip,
246 expires,
247 lease.hostname, sizeof(lease.hostname)
248 ) == 0
249 ) {
250 bb_error_msg("too many leases while loading %s", file);
251 break;
252 }
253#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
254 i++;
255#endif
256 }
257 }
258 log1("read %d leases", i);
259 ret:
260 close(fd);
261}
41 262
42/* Send a packet to a specific mac address and ip address by creating our own ip packet */ 263/* Send a packet to a specific mac address and ip address by creating our own ip packet */
43static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast) 264static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
@@ -290,12 +511,10 @@ static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
290 send_packet(&packet, /*force_bcast:*/ 0); 511 send_packet(&packet, /*force_bcast:*/ 0);
291} 512}
292 513
293
294/* globals */ 514/* globals */
295struct dyn_lease *g_leases; 515struct dyn_lease *g_leases;
296/* struct server_config_t server_config is in bb_common_bufsiz1 */ 516/* struct server_config_t server_config is in bb_common_bufsiz1 */
297 517
298
299int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 518int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
300int udhcpd_main(int argc UNUSED_PARAM, char **argv) 519int udhcpd_main(int argc UNUSED_PARAM, char **argv)
301{ 520{