1 |
|
|
/* $OpenBSD: dhclient.c,v 1.379 2016/07/23 15:53:19 stsp Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright 2004 Henning Brauer <henning@openbsd.org> |
5 |
|
|
* Copyright (c) 1995, 1996, 1997, 1998, 1999 |
6 |
|
|
* The Internet Software Consortium. All rights reserved. |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* 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 distribution. |
17 |
|
|
* 3. Neither the name of The Internet Software Consortium nor the names |
18 |
|
|
* of its contributors may be used to endorse or promote products derived |
19 |
|
|
* from this software without specific prior written permission. |
20 |
|
|
* |
21 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND |
22 |
|
|
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
23 |
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
24 |
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
25 |
|
|
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR |
26 |
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
27 |
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
28 |
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
29 |
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
30 |
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
31 |
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
32 |
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 |
|
|
* SUCH DAMAGE. |
34 |
|
|
* |
35 |
|
|
* This software has been written for the Internet Software Consortium |
36 |
|
|
* by Ted Lemon <mellon@fugue.com> in cooperation with Vixie |
37 |
|
|
* Enterprises. To learn more about the Internet Software Consortium, |
38 |
|
|
* see ``http://www.vix.com/isc''. To learn more about Vixie |
39 |
|
|
* Enterprises, see ``http://www.vix.com''. |
40 |
|
|
* |
41 |
|
|
* This client was substantially modified and enhanced by Elliot Poger |
42 |
|
|
* for use on Linux while he was working on the MosquitoNet project at |
43 |
|
|
* Stanford. |
44 |
|
|
* |
45 |
|
|
* The current version owes much to Elliot's Linux enhancements, but |
46 |
|
|
* was substantially reorganized and partially rewritten by Ted Lemon |
47 |
|
|
* so as to use the same networking framework that the Internet Software |
48 |
|
|
* Consortium DHCP server uses. Much system-specific configuration code |
49 |
|
|
* was moved into a shell script so that as support for more operating |
50 |
|
|
* systems is added, it will not be necessary to port and maintain |
51 |
|
|
* system-specific configuration code to these operating systems - instead, |
52 |
|
|
* the shell script can invoke the native tools to accomplish the same |
53 |
|
|
* purpose. |
54 |
|
|
*/ |
55 |
|
|
|
56 |
|
|
#include <sys/types.h> |
57 |
|
|
#include <sys/socket.h> |
58 |
|
|
#include <sys/stat.h> |
59 |
|
|
#include <sys/ioctl.h> |
60 |
|
|
#include <sys/uio.h> |
61 |
|
|
#include <sys/queue.h> |
62 |
|
|
|
63 |
|
|
#include <net/if.h> |
64 |
|
|
#include <net/route.h> |
65 |
|
|
|
66 |
|
|
#include <netinet/in.h> |
67 |
|
|
#include <netinet/if_ether.h> |
68 |
|
|
|
69 |
|
|
#include <arpa/inet.h> |
70 |
|
|
|
71 |
|
|
#include <ctype.h> |
72 |
|
|
#include <errno.h> |
73 |
|
|
#include <fcntl.h> |
74 |
|
|
#include <ifaddrs.h> |
75 |
|
|
#include <imsg.h> |
76 |
|
|
#include <limits.h> |
77 |
|
|
#include <paths.h> |
78 |
|
|
#include <poll.h> |
79 |
|
|
#include <pwd.h> |
80 |
|
|
#include <resolv.h> |
81 |
|
|
#include <signal.h> |
82 |
|
|
#include <stdint.h> |
83 |
|
|
#include <stdlib.h> |
84 |
|
|
#include <string.h> |
85 |
|
|
#include <syslog.h> |
86 |
|
|
#include <unistd.h> |
87 |
|
|
|
88 |
|
|
#include "dhcp.h" |
89 |
|
|
#include "dhcpd.h" |
90 |
|
|
#include "privsep.h" |
91 |
|
|
|
92 |
|
|
char *path_dhclient_conf = _PATH_DHCLIENT_CONF; |
93 |
|
|
char *path_dhclient_db = NULL; |
94 |
|
|
|
95 |
|
|
char path_option_db[PATH_MAX]; |
96 |
|
|
|
97 |
|
|
int log_perror = 1; |
98 |
|
|
int nullfd = -1; |
99 |
|
|
int no_daemon; |
100 |
|
|
int unknown_ok = 1; |
101 |
|
|
int routefd = -1; |
102 |
|
|
|
103 |
|
|
volatile sig_atomic_t quit; |
104 |
|
|
|
105 |
|
|
struct in_addr deleting; |
106 |
|
|
struct in_addr adding; |
107 |
|
|
|
108 |
|
|
const struct in_addr inaddr_any = { INADDR_ANY }; |
109 |
|
|
const struct in_addr inaddr_broadcast = { INADDR_BROADCAST }; |
110 |
|
|
|
111 |
|
|
struct interface_info *ifi; |
112 |
|
|
struct client_state *client; |
113 |
|
|
struct client_config *config; |
114 |
|
|
struct imsgbuf *unpriv_ibuf; |
115 |
|
|
|
116 |
|
|
void sighdlr(int); |
117 |
|
|
int findproto(char *, int); |
118 |
|
|
struct sockaddr *get_ifa(char *, int); |
119 |
|
|
void usage(void); |
120 |
|
|
int res_hnok(const char *dn); |
121 |
|
|
int res_hnok_list(const char *dn); |
122 |
|
|
int addressinuse(struct in_addr, char *); |
123 |
|
|
|
124 |
|
|
void fork_privchld(int, int); |
125 |
|
|
void get_ifname(char *); |
126 |
|
|
char *resolv_conf_contents(struct option_data *, |
127 |
|
|
struct option_data *, struct option_data *); |
128 |
|
|
void write_resolv_conf(u_int8_t *, size_t); |
129 |
|
|
void write_option_db(u_int8_t *, size_t); |
130 |
|
|
|
131 |
|
|
struct client_lease *apply_defaults(struct client_lease *); |
132 |
|
|
struct client_lease *clone_lease(struct client_lease *); |
133 |
|
|
void apply_ignore_list(char *); |
134 |
|
|
|
135 |
|
|
void add_direct_route(struct in_addr, struct in_addr, struct in_addr); |
136 |
|
|
void add_default_route(struct in_addr, struct in_addr); |
137 |
|
|
void add_static_routes(struct option_data *, struct in_addr); |
138 |
|
|
void add_classless_static_routes(struct option_data *, struct in_addr); |
139 |
|
|
|
140 |
|
|
int compare_lease(struct client_lease *, struct client_lease *); |
141 |
|
|
void set_lease_times(struct client_lease *); |
142 |
|
|
|
143 |
|
|
void state_preboot(void); |
144 |
|
|
void state_reboot(void); |
145 |
|
|
void state_init(void); |
146 |
|
|
void state_selecting(void); |
147 |
|
|
void state_bound(void); |
148 |
|
|
void state_panic(void); |
149 |
|
|
|
150 |
|
|
void send_discover(void); |
151 |
|
|
void send_request(void); |
152 |
|
|
void send_decline(void); |
153 |
|
|
|
154 |
|
|
void bind_lease(void); |
155 |
|
|
|
156 |
|
|
void make_discover(struct client_lease *); |
157 |
|
|
void make_request(struct client_lease *); |
158 |
|
|
void make_decline(struct client_lease *); |
159 |
|
|
|
160 |
|
|
void rewrite_client_leases(void); |
161 |
|
|
void rewrite_option_db(struct client_lease *, struct client_lease *); |
162 |
|
|
char *lease_as_string(char *, struct client_lease *); |
163 |
|
|
|
164 |
|
|
struct client_lease *packet_to_lease(struct in_addr, struct option_data *); |
165 |
|
|
void go_daemon(void); |
166 |
|
|
|
167 |
|
|
#define ROUNDUP(a) \ |
168 |
|
|
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
169 |
|
|
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) |
170 |
|
|
|
171 |
|
|
static FILE *leaseFile; |
172 |
|
|
|
173 |
|
|
void |
174 |
|
|
sighdlr(int sig) |
175 |
|
|
{ |
176 |
|
|
quit = sig; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
int |
180 |
|
|
findproto(char *cp, int n) |
181 |
|
|
{ |
182 |
|
|
struct sockaddr *sa; |
183 |
|
|
unsigned int i; |
184 |
|
|
|
185 |
|
|
if (n == 0) |
186 |
|
|
return -1; |
187 |
|
|
for (i = 1; i; i <<= 1) { |
188 |
|
|
if (i & n) { |
189 |
|
|
sa = (struct sockaddr *)cp; |
190 |
|
|
switch (i) { |
191 |
|
|
case RTA_IFA: |
192 |
|
|
case RTA_DST: |
193 |
|
|
case RTA_GATEWAY: |
194 |
|
|
case RTA_NETMASK: |
195 |
|
|
if (sa->sa_family == AF_INET) |
196 |
|
|
return AF_INET; |
197 |
|
|
if (sa->sa_family == AF_INET6) |
198 |
|
|
return AF_INET6; |
199 |
|
|
break; |
200 |
|
|
case RTA_IFP: |
201 |
|
|
break; |
202 |
|
|
} |
203 |
|
|
ADVANCE(cp, sa); |
204 |
|
|
} |
205 |
|
|
} |
206 |
|
|
return (-1); |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
struct sockaddr * |
210 |
|
|
get_ifa(char *cp, int n) |
211 |
|
|
{ |
212 |
|
|
struct sockaddr *sa; |
213 |
|
|
unsigned int i; |
214 |
|
|
|
215 |
|
|
if (n == 0) |
216 |
|
|
return (NULL); |
217 |
|
|
for (i = 1; i; i <<= 1) |
218 |
|
|
if (i & n) { |
219 |
|
|
sa = (struct sockaddr *)cp; |
220 |
|
|
if (i == RTA_IFA) |
221 |
|
|
return (sa); |
222 |
|
|
ADVANCE(cp, sa); |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
return (NULL); |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
void |
229 |
|
|
routehandler(void) |
230 |
|
|
{ |
231 |
|
|
char ntoabuf[INET_ADDRSTRLEN]; |
232 |
|
|
struct in_addr a, b; |
233 |
|
|
ssize_t n; |
234 |
|
|
int linkstat, rslt; |
235 |
|
|
struct ether_addr hw; |
236 |
|
|
struct rt_msghdr *rtm; |
237 |
|
|
struct if_msghdr *ifm; |
238 |
|
|
struct ifa_msghdr *ifam; |
239 |
|
|
struct if_announcemsghdr *ifan; |
240 |
|
|
struct sockaddr *sa; |
241 |
|
|
char *errmsg, *rtmmsg; |
242 |
|
|
|
243 |
|
|
rtmmsg = calloc(1, 2048); |
244 |
|
|
if (rtmmsg == NULL) |
245 |
|
|
error("No memory for rtmmsg"); |
246 |
|
|
|
247 |
|
|
do { |
248 |
|
|
n = read(routefd, rtmmsg, 2048); |
249 |
|
|
} while (n == -1 && errno == EINTR); |
250 |
|
|
|
251 |
|
|
rtm = (struct rt_msghdr *)rtmmsg; |
252 |
|
|
if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen || |
253 |
|
|
rtm->rtm_version != RTM_VERSION) |
254 |
|
|
goto done; |
255 |
|
|
|
256 |
|
|
switch (rtm->rtm_type) { |
257 |
|
|
case RTM_DESYNC: |
258 |
|
|
warning("route socket buffer overflow"); |
259 |
|
|
break; |
260 |
|
|
case RTM_NEWADDR: |
261 |
|
|
ifam = (struct ifa_msghdr *)rtm; |
262 |
|
|
if (ifam->ifam_index != ifi->index) |
263 |
|
|
break; |
264 |
|
|
if (findproto((char *)ifam + ifam->ifam_hdrlen, |
265 |
|
|
ifam->ifam_addrs) != AF_INET) |
266 |
|
|
break; |
267 |
|
|
sa = get_ifa((char *)ifam + ifam->ifam_hdrlen, |
268 |
|
|
ifam->ifam_addrs); |
269 |
|
|
if (sa == NULL) |
270 |
|
|
goto done; |
271 |
|
|
|
272 |
|
|
memcpy(&a, &((struct sockaddr_in *)sa)->sin_addr, sizeof(a)); |
273 |
|
|
if (a.s_addr == INADDR_ANY) |
274 |
|
|
break; |
275 |
|
|
|
276 |
|
|
/* |
277 |
|
|
* If we are in the process of adding a new address, ignore |
278 |
|
|
* messages generated by that process. |
279 |
|
|
*/ |
280 |
|
|
if (a.s_addr == adding.s_addr) { |
281 |
|
|
adding.s_addr = INADDR_ANY; |
282 |
|
|
note("bound to %s -- renewal in %lld seconds.", |
283 |
|
|
inet_ntoa(client->active->address), |
284 |
|
|
(long long)(client->active->renewal - |
285 |
|
|
time(NULL))); |
286 |
|
|
client->flags |= IS_RESPONSIBLE; |
287 |
|
|
go_daemon(); |
288 |
|
|
break; |
289 |
|
|
} |
290 |
|
|
if ((client->flags & IS_RESPONSIBLE) == 0) |
291 |
|
|
/* We're not responsible yet! */ |
292 |
|
|
break; |
293 |
|
|
if (adding.s_addr != INADDR_ANY) { |
294 |
|
|
strlcpy(ntoabuf, inet_ntoa(a), sizeof(ntoabuf)); |
295 |
|
|
rslt = asprintf(&errmsg, "%s, not %s, added to %s", |
296 |
|
|
ntoabuf, inet_ntoa(adding), ifi->name); |
297 |
|
|
} else |
298 |
|
|
rslt = asprintf(&errmsg, "%s added to %s", |
299 |
|
|
inet_ntoa(a), ifi->name); |
300 |
|
|
goto die; |
301 |
|
|
case RTM_DELADDR: |
302 |
|
|
ifam = (struct ifa_msghdr *)rtm; |
303 |
|
|
if (ifam->ifam_index != ifi->index) |
304 |
|
|
break; |
305 |
|
|
if (findproto((char *)ifam + ifam->ifam_hdrlen, |
306 |
|
|
ifam->ifam_addrs) != AF_INET) |
307 |
|
|
break; |
308 |
|
|
sa = get_ifa((char *)ifam + ifam->ifam_hdrlen, |
309 |
|
|
ifam->ifam_addrs); |
310 |
|
|
if (sa == NULL) |
311 |
|
|
goto done; |
312 |
|
|
|
313 |
|
|
memcpy(&a, &((struct sockaddr_in *)sa)->sin_addr, sizeof(a)); |
314 |
|
|
if (a.s_addr == INADDR_ANY) |
315 |
|
|
break; |
316 |
|
|
|
317 |
|
|
/* |
318 |
|
|
* If we are in the process of deleting an address, ignore |
319 |
|
|
* messages generated by that process. |
320 |
|
|
*/ |
321 |
|
|
if (a.s_addr == deleting.s_addr) { |
322 |
|
|
deleting.s_addr = INADDR_ANY; |
323 |
|
|
break; |
324 |
|
|
} |
325 |
|
|
if ((client->flags & IS_RESPONSIBLE) == 0) |
326 |
|
|
/* We're not responsible yet! */ |
327 |
|
|
break; |
328 |
|
|
if (adding.s_addr == INADDR_ANY && client->active && |
329 |
|
|
a.s_addr == client->active->address.s_addr) { |
330 |
|
|
/* Tell the priv process active_addr is gone. */ |
331 |
|
|
warning("Active address (%s) deleted; exiting", |
332 |
|
|
inet_ntoa(client->active->address)); |
333 |
|
|
memset(&b, 0, sizeof(b)); |
334 |
|
|
add_address(b, b); |
335 |
|
|
/* No need to write resolv.conf now. */ |
336 |
|
|
client->flags &= ~IS_RESPONSIBLE; |
337 |
|
|
quit = INTERNALSIG; |
338 |
|
|
break; |
339 |
|
|
} |
340 |
|
|
if (deleting.s_addr != INADDR_ANY) { |
341 |
|
|
strlcpy(ntoabuf, inet_ntoa(a), sizeof(ntoabuf)); |
342 |
|
|
rslt = asprintf(&errmsg, "%s, not %s, deleted from %s", |
343 |
|
|
ntoabuf, inet_ntoa(deleting), ifi->name); |
344 |
|
|
} else |
345 |
|
|
rslt = asprintf(&errmsg, "%s deleted from %s", |
346 |
|
|
inet_ntoa(a), ifi->name); |
347 |
|
|
goto die; |
348 |
|
|
case RTM_IFINFO: |
349 |
|
|
ifm = (struct if_msghdr *)rtm; |
350 |
|
|
if (ifm->ifm_index != ifi->index) |
351 |
|
|
break; |
352 |
|
|
if ((rtm->rtm_flags & RTF_UP) == 0) { |
353 |
|
|
rslt = asprintf(&errmsg, "%s down", ifi->name); |
354 |
|
|
goto die; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
if (ifi->flags & IFI_VALID_LLADDR) { |
358 |
|
|
memcpy(&hw, &ifi->hw_address, sizeof(hw)); |
359 |
|
|
get_hw_address(); |
360 |
|
|
if (memcmp(&hw, &ifi->hw_address, sizeof(hw))) { |
361 |
|
|
warning("LLADDR changed; restarting"); |
362 |
|
|
ifi->flags |= IFI_NEW_LLADDR; |
363 |
|
|
quit = SIGHUP; |
364 |
|
|
goto done; |
365 |
|
|
} |
366 |
|
|
} |
367 |
|
|
|
368 |
|
|
linkstat = interface_status(ifi->name); |
369 |
|
|
if (linkstat != ifi->linkstat) { |
370 |
|
|
#ifdef DEBUG |
371 |
|
|
debug("link state %s -> %s", |
372 |
|
|
ifi->linkstat ? "up" : "down", |
373 |
|
|
linkstat ? "up" : "down"); |
374 |
|
|
#endif |
375 |
|
|
ifi->linkstat = linkstat; |
376 |
|
|
if (ifi->linkstat) { |
377 |
|
|
if (client->state == S_PREBOOT) { |
378 |
|
|
state_preboot(); |
379 |
|
|
get_hw_address(); |
380 |
|
|
} else { |
381 |
|
|
client->state = S_REBOOTING; |
382 |
|
|
state_reboot(); |
383 |
|
|
} |
384 |
|
|
} else { |
385 |
|
|
/* Let monitoring programs see link loss. */ |
386 |
|
|
if (strlen(path_option_db)) |
387 |
|
|
write_option_db("", 0); |
388 |
|
|
/* No need to wait for anything but link. */ |
389 |
|
|
cancel_timeout(); |
390 |
|
|
} |
391 |
|
|
} |
392 |
|
|
break; |
393 |
|
|
case RTM_IFANNOUNCE: |
394 |
|
|
ifan = (struct if_announcemsghdr *)rtm; |
395 |
|
|
if (ifan->ifan_what == IFAN_DEPARTURE && |
396 |
|
|
ifan->ifan_index == ifi->index) { |
397 |
|
|
rslt = asprintf(&errmsg, "%s departured", ifi->name); |
398 |
|
|
goto die; |
399 |
|
|
} |
400 |
|
|
break; |
401 |
|
|
default: |
402 |
|
|
break; |
403 |
|
|
} |
404 |
|
|
|
405 |
|
|
/* Something has happened. Try to write out the resolv.conf. */ |
406 |
|
|
if (client->active && client->active->resolv_conf && |
407 |
|
|
client->flags & IS_RESPONSIBLE) |
408 |
|
|
write_resolv_conf(client->active->resolv_conf, |
409 |
|
|
strlen(client->active->resolv_conf)); |
410 |
|
|
|
411 |
|
|
done: |
412 |
|
|
free(rtmmsg); |
413 |
|
|
return; |
414 |
|
|
|
415 |
|
|
die: |
416 |
|
|
if (rslt == -1) |
417 |
|
|
error("no memory for errmsg"); |
418 |
|
|
error("%s; exiting", errmsg); |
419 |
|
|
} |
420 |
|
|
|
421 |
|
|
char **saved_argv; |
422 |
|
|
|
423 |
|
|
int |
424 |
|
|
main(int argc, char *argv[]) |
425 |
|
|
{ |
426 |
|
|
struct stat sb; |
427 |
|
|
int ch, fd, socket_fd[2]; |
428 |
|
|
extern char *__progname; |
429 |
|
|
struct passwd *pw; |
430 |
|
|
char *ignore_list = NULL; |
431 |
|
|
ssize_t tailn; |
432 |
|
|
int rtfilter, tailfd; |
433 |
|
|
|
434 |
|
|
saved_argv = argv; |
435 |
|
|
|
436 |
|
|
/* Initially, log errors to stderr as well as to syslogd. */ |
437 |
|
|
openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); |
438 |
|
|
#ifdef DEBUG |
439 |
|
|
setlogmask(LOG_UPTO(LOG_DEBUG)); |
440 |
|
|
#else |
441 |
|
|
setlogmask(LOG_UPTO(LOG_INFO)); |
442 |
|
|
#endif |
443 |
|
|
|
444 |
|
|
while ((ch = getopt(argc, argv, "c:di:l:L:qu")) != -1) |
445 |
|
|
switch (ch) { |
446 |
|
|
case 'c': |
447 |
|
|
path_dhclient_conf = optarg; |
448 |
|
|
break; |
449 |
|
|
case 'd': |
450 |
|
|
if (log_perror == 0) |
451 |
|
|
usage(); |
452 |
|
|
no_daemon = log_perror = 1; |
453 |
|
|
break; |
454 |
|
|
case 'i': |
455 |
|
|
ignore_list = optarg; |
456 |
|
|
break; |
457 |
|
|
case 'l': |
458 |
|
|
path_dhclient_db = optarg; |
459 |
|
|
if (lstat(path_dhclient_db, &sb) != -1) { |
460 |
|
|
if (!S_ISREG(sb.st_mode)) |
461 |
|
|
error("'%s' is not a regular file", |
462 |
|
|
path_dhclient_db); |
463 |
|
|
} |
464 |
|
|
break; |
465 |
|
|
case 'L': |
466 |
|
|
strlcat(path_option_db, optarg, PATH_MAX); |
467 |
|
|
if (lstat(path_option_db, &sb) != -1) { |
468 |
|
|
if (!S_ISREG(sb.st_mode)) |
469 |
|
|
error("'%s' is not a regular file", |
470 |
|
|
path_option_db); |
471 |
|
|
} |
472 |
|
|
break; |
473 |
|
|
case 'q': |
474 |
|
|
if (no_daemon == 1) |
475 |
|
|
usage(); |
476 |
|
|
no_daemon = log_perror = 0; |
477 |
|
|
break; |
478 |
|
|
case 'u': |
479 |
|
|
unknown_ok = 0; |
480 |
|
|
break; |
481 |
|
|
default: |
482 |
|
|
usage(); |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
argc -= optind; |
486 |
|
|
argv += optind; |
487 |
|
|
|
488 |
|
|
if (argc != 1) |
489 |
|
|
usage(); |
490 |
|
|
|
491 |
|
|
ifi = calloc(1, sizeof(struct interface_info)); |
492 |
|
|
if (ifi == NULL) |
493 |
|
|
error("ifi calloc"); |
494 |
|
|
get_ifname(argv[0]); |
495 |
|
|
ifi->index = if_nametoindex(ifi->name); |
496 |
|
|
if (ifi->index == 0) |
497 |
|
|
error("%s: no such interface", ifi->name); |
498 |
|
|
|
499 |
|
|
tzset(); |
500 |
|
|
|
501 |
|
|
/* Put us into the correct rdomain */ |
502 |
|
|
ifi->rdomain = get_rdomain(ifi->name); |
503 |
|
|
if (setrtable(ifi->rdomain) == -1) |
504 |
|
|
error("setting routing table to %u: '%s'", ifi->rdomain, |
505 |
|
|
strerror(errno)); |
506 |
|
|
|
507 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, |
508 |
|
|
PF_UNSPEC, socket_fd) == -1) |
509 |
|
|
error("socketpair: %s", strerror(errno)); |
510 |
|
|
|
511 |
|
|
fork_privchld(socket_fd[0], socket_fd[1]); |
512 |
|
|
|
513 |
|
|
close(socket_fd[0]); |
514 |
|
|
if ((unpriv_ibuf = malloc(sizeof(struct imsgbuf))) == NULL) |
515 |
|
|
error("no memory for unpriv_ibuf"); |
516 |
|
|
imsg_init(unpriv_ibuf, socket_fd[1]); |
517 |
|
|
|
518 |
|
|
config = calloc(1, sizeof(struct client_config)); |
519 |
|
|
if (config == NULL) |
520 |
|
|
error("config calloc"); |
521 |
|
|
TAILQ_INIT(&config->reject_list); |
522 |
|
|
|
523 |
|
|
client = calloc(1, sizeof(struct client_state)); |
524 |
|
|
if (client == NULL) |
525 |
|
|
error("client calloc"); |
526 |
|
|
TAILQ_INIT(&client->leases); |
527 |
|
|
TAILQ_INIT(&client->offered_leases); |
528 |
|
|
|
529 |
|
|
read_client_conf(); |
530 |
|
|
|
531 |
|
|
if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) |
532 |
|
|
error("cannot open %s: %s", _PATH_DEVNULL, strerror(errno)); |
533 |
|
|
|
534 |
|
|
if ((pw = getpwnam("_dhcp")) == NULL) |
535 |
|
|
error("no such user: _dhcp"); |
536 |
|
|
|
537 |
|
|
if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s", |
538 |
|
|
_PATH_DHCLIENT_DB, ifi->name) == -1) |
539 |
|
|
error("asprintf"); |
540 |
|
|
|
541 |
|
|
/* 2nd stage (post fork) config setup. */ |
542 |
|
|
if (ignore_list) |
543 |
|
|
apply_ignore_list(ignore_list); |
544 |
|
|
|
545 |
|
|
tailfd = open("/etc/resolv.conf.tail", O_RDONLY); |
546 |
|
|
if (tailfd == -1) { |
547 |
|
|
if (errno != ENOENT) |
548 |
|
|
error("Cannot open /etc/resolv.conf.tail: %s", |
549 |
|
|
strerror(errno)); |
550 |
|
|
} else if (fstat(tailfd, &sb) == -1) { |
551 |
|
|
error("Cannot stat /etc/resolv.conf.tail: %s", |
552 |
|
|
strerror(errno)); |
553 |
|
|
} else { |
554 |
|
|
if (sb.st_size > 0 && sb.st_size < SIZE_MAX) { |
555 |
|
|
config->resolv_tail = calloc(1, sb.st_size + 1); |
556 |
|
|
if (config->resolv_tail == NULL) { |
557 |
|
|
error("no memory for resolv.conf.tail " |
558 |
|
|
"contents: %s", strerror(errno)); |
559 |
|
|
} |
560 |
|
|
tailn = read(tailfd, config->resolv_tail, sb.st_size); |
561 |
|
|
if (tailn == -1) |
562 |
|
|
error("Couldn't read resolv.conf.tail: %s", |
563 |
|
|
strerror(errno)); |
564 |
|
|
else if (tailn == 0) |
565 |
|
|
error("Got no data from resolv.conf.tail"); |
566 |
|
|
else if (tailn != sb.st_size) |
567 |
|
|
error("Short read of resolv.conf.tail"); |
568 |
|
|
} |
569 |
|
|
close(tailfd); |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
if ((fd = open(path_dhclient_db, |
573 |
|
|
O_RDONLY|O_EXLOCK|O_CREAT|O_NOFOLLOW, 0640)) == -1) |
574 |
|
|
error("can't open and lock %s: %s", path_dhclient_db, |
575 |
|
|
strerror(errno)); |
576 |
|
|
read_client_leases(); |
577 |
|
|
if ((leaseFile = fopen(path_dhclient_db, "w")) == NULL) |
578 |
|
|
error("can't open %s: %s", path_dhclient_db, strerror(errno)); |
579 |
|
|
rewrite_client_leases(); |
580 |
|
|
close(fd); |
581 |
|
|
|
582 |
|
|
/* |
583 |
|
|
* Do the initial status check and possible force up before creating |
584 |
|
|
* the routing socket. If we bounce the interface down and up while |
585 |
|
|
* the routing socket is listening, the RTM_IFINFO message with the |
586 |
|
|
* RTF_UP flag reset will cause premature exit. |
587 |
|
|
*/ |
588 |
|
|
ifi->linkstat = interface_status(ifi->name); |
589 |
|
|
if (ifi->linkstat == 0) |
590 |
|
|
interface_link_forceup(ifi->name); |
591 |
|
|
|
592 |
|
|
if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) |
593 |
|
|
error("socket(PF_ROUTE, SOCK_RAW): %s", strerror(errno)); |
594 |
|
|
|
595 |
|
|
rtfilter = ROUTE_FILTER(RTM_NEWADDR) | ROUTE_FILTER(RTM_DELADDR) | |
596 |
|
|
ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE); |
597 |
|
|
|
598 |
|
|
if (setsockopt(routefd, PF_ROUTE, ROUTE_MSGFILTER, |
599 |
|
|
&rtfilter, sizeof(rtfilter)) == -1) |
600 |
|
|
error("setsockopt(ROUTE_MSGFILTER): %s", strerror(errno)); |
601 |
|
|
if (setsockopt(routefd, AF_ROUTE, ROUTE_TABLEFILTER, &ifi->rdomain, |
602 |
|
|
sizeof(ifi->rdomain)) == -1) |
603 |
|
|
error("setsockopt(ROUTE_TABLEFILTER): %s", strerror(errno)); |
604 |
|
|
|
605 |
|
|
/* Register the interface. */ |
606 |
|
|
if_register_receive(); |
607 |
|
|
if_register_send(); |
608 |
|
|
|
609 |
|
|
if (chroot(_PATH_VAREMPTY) == -1) |
610 |
|
|
error("chroot"); |
611 |
|
|
if (chdir("/") == -1) |
612 |
|
|
error("chdir(\"/\")"); |
613 |
|
|
|
614 |
|
|
if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) |
615 |
|
|
error("setresgid"); |
616 |
|
|
if (setgroups(1, &pw->pw_gid) == -1) |
617 |
|
|
error("setgroups"); |
618 |
|
|
if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) |
619 |
|
|
error("setresuid"); |
620 |
|
|
|
621 |
|
|
endpwent(); |
622 |
|
|
|
623 |
|
|
if (no_daemon) { |
624 |
|
|
if (pledge("stdio inet dns route rpath cpath wpath", NULL) == -1) |
625 |
|
|
error("pledge"); |
626 |
|
|
} else { |
627 |
|
|
if (pledge("stdio inet dns route proc rpath cpath wpath", NULL) == -1) |
628 |
|
|
error("pledge"); |
629 |
|
|
} |
630 |
|
|
|
631 |
|
|
setproctitle("%s", ifi->name); |
632 |
|
|
time(&client->startup_time); |
633 |
|
|
|
634 |
|
|
if (ifi->linkstat) { |
635 |
|
|
client->state = S_REBOOTING; |
636 |
|
|
state_reboot(); |
637 |
|
|
} else { |
638 |
|
|
client->state = S_PREBOOT; |
639 |
|
|
state_preboot(); |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
dispatch(); |
643 |
|
|
|
644 |
|
|
/* not reached */ |
645 |
|
|
return (0); |
646 |
|
|
} |
647 |
|
|
|
648 |
|
|
void |
649 |
|
|
usage(void) |
650 |
|
|
{ |
651 |
|
|
extern char *__progname; |
652 |
|
|
|
653 |
|
|
fprintf(stderr, |
654 |
|
|
"usage: %s [-d | -q] [-u] [-c file] [-i options] [-L file] [-l file] " |
655 |
|
|
"interface\n", __progname); |
656 |
|
|
exit(1); |
657 |
|
|
} |
658 |
|
|
|
659 |
|
|
void |
660 |
|
|
state_preboot(void) |
661 |
|
|
{ |
662 |
|
|
static int preamble; |
663 |
|
|
time_t cur_time; |
664 |
|
|
int interval; |
665 |
|
|
|
666 |
|
|
time(&cur_time); |
667 |
|
|
|
668 |
|
|
interval = (int)(cur_time - client->startup_time); |
669 |
|
|
|
670 |
|
|
ifi->linkstat = interface_status(ifi->name); |
671 |
|
|
|
672 |
|
|
if (log_perror && interval > 3) { |
673 |
|
|
if (!preamble && !ifi->linkstat) { |
674 |
|
|
fprintf(stderr, "%s: no link ....", ifi->name); |
675 |
|
|
preamble = 1; |
676 |
|
|
} |
677 |
|
|
if (preamble) { |
678 |
|
|
if (ifi->linkstat) |
679 |
|
|
fprintf(stderr, " got link\n"); |
680 |
|
|
else if (interval > config->link_timeout) |
681 |
|
|
fprintf(stderr, " sleeping\n"); |
682 |
|
|
else |
683 |
|
|
fprintf(stderr, "."); |
684 |
|
|
fflush(stderr); |
685 |
|
|
} |
686 |
|
|
} |
687 |
|
|
|
688 |
|
|
if (ifi->linkstat) { |
689 |
|
|
client->state = S_REBOOTING; |
690 |
|
|
set_timeout_interval(1, state_reboot); |
691 |
|
|
} else { |
692 |
|
|
if (interval > config->link_timeout) |
693 |
|
|
go_daemon(); |
694 |
|
|
client->state = S_PREBOOT; |
695 |
|
|
set_timeout_interval(1, state_preboot); |
696 |
|
|
} |
697 |
|
|
} |
698 |
|
|
|
699 |
|
|
/* |
700 |
|
|
* Called when the interface link becomes active. |
701 |
|
|
*/ |
702 |
|
|
void |
703 |
|
|
state_reboot(void) |
704 |
|
|
{ |
705 |
|
|
char ifname[IF_NAMESIZE]; |
706 |
|
|
struct client_lease *lp; |
707 |
|
|
struct option_data *opt; |
708 |
|
|
time_t cur_time; |
709 |
|
|
|
710 |
|
|
cancel_timeout(); |
711 |
|
|
deleting.s_addr = INADDR_ANY; |
712 |
|
|
adding.s_addr = INADDR_ANY; |
713 |
|
|
|
714 |
|
|
get_hw_address(); |
715 |
|
|
opt = &config->send_options[DHO_DHCP_CLIENT_IDENTIFIER]; |
716 |
|
|
/* |
717 |
|
|
* Check both len && data so |
718 |
|
|
* send dhcp-client-identifier ""; |
719 |
|
|
* can be used to suppress sending the default client |
720 |
|
|
* identifier. |
721 |
|
|
*/ |
722 |
|
|
if (opt->len == 0 && opt->data == NULL) { |
723 |
|
|
/* Build default client identifier. */ |
724 |
|
|
opt->data = calloc(1, ETHER_ADDR_LEN + 1); |
725 |
|
|
if (opt->data != NULL) { |
726 |
|
|
opt->data[0] = HTYPE_ETHER; |
727 |
|
|
memcpy(&opt->data[1], ifi->hw_address.ether_addr_octet, |
728 |
|
|
ETHER_ADDR_LEN); |
729 |
|
|
opt->len = ETHER_ADDR_LEN + 1; |
730 |
|
|
} |
731 |
|
|
} |
732 |
|
|
|
733 |
|
|
time(&cur_time); |
734 |
|
|
if (client->active) { |
735 |
|
|
if (client->active->expiry <= cur_time) |
736 |
|
|
client->active = NULL; |
737 |
|
|
else if (addressinuse(client->active->address, ifname) && |
738 |
|
|
strncmp(ifname, ifi->name, IF_NAMESIZE) != 0) |
739 |
|
|
client->active = NULL; |
740 |
|
|
} |
741 |
|
|
|
742 |
|
|
/* Run through the list of leases and see if one can be used. */ |
743 |
|
|
TAILQ_FOREACH(lp, &client->leases, next) { |
744 |
|
|
if (addressinuse(lp->address, ifname) && |
745 |
|
|
strncmp(ifname, ifi->name, IF_NAMESIZE) != 0) |
746 |
|
|
continue; |
747 |
|
|
if (client->active || lp->is_static) |
748 |
|
|
break; |
749 |
|
|
if (lp->expiry > cur_time) { |
750 |
|
|
client->active = lp; |
751 |
|
|
break; |
752 |
|
|
} |
753 |
|
|
} |
754 |
|
|
|
755 |
|
|
/* If we don't remember an active lease, go straight to INIT. */ |
756 |
|
|
if (!client->active || client->active->is_bootp) { |
757 |
|
|
client->state = S_INIT; |
758 |
|
|
state_init(); |
759 |
|
|
return; |
760 |
|
|
} |
761 |
|
|
|
762 |
|
|
client->xid = arc4random(); |
763 |
|
|
make_request(client->active); |
764 |
|
|
|
765 |
|
|
client->destination.s_addr = INADDR_BROADCAST; |
766 |
|
|
client->first_sending = time(NULL); |
767 |
|
|
client->interval = 0; |
768 |
|
|
|
769 |
|
|
send_request(); |
770 |
|
|
} |
771 |
|
|
|
772 |
|
|
/* |
773 |
|
|
* Called when a lease has completely expired and we've been unable to renew it. |
774 |
|
|
*/ |
775 |
|
|
void |
776 |
|
|
state_init(void) |
777 |
|
|
{ |
778 |
|
|
client->xid = arc4random(); |
779 |
|
|
make_discover(client->active); |
780 |
|
|
|
781 |
|
|
client->destination.s_addr = INADDR_BROADCAST; |
782 |
|
|
client->state = S_SELECTING; |
783 |
|
|
client->first_sending = time(NULL); |
784 |
|
|
client->interval = 0; |
785 |
|
|
|
786 |
|
|
send_discover(); |
787 |
|
|
} |
788 |
|
|
|
789 |
|
|
/* |
790 |
|
|
* Called when one or more DHCPOFFER packets have been received and a |
791 |
|
|
* configurable period of time has passed. |
792 |
|
|
*/ |
793 |
|
|
void |
794 |
|
|
state_selecting(void) |
795 |
|
|
{ |
796 |
|
|
struct client_lease *lease, *picked; |
797 |
|
|
|
798 |
|
|
cancel_timeout(); |
799 |
|
|
|
800 |
|
|
/* Take the first valid DHCPOFFER. */ |
801 |
|
|
TAILQ_FOREACH_SAFE(picked, &client->offered_leases, next, lease) { |
802 |
|
|
if (picked->is_invalid == 0) { |
803 |
|
|
TAILQ_REMOVE(&client->offered_leases, picked, next); |
804 |
|
|
break; |
805 |
|
|
} |
806 |
|
|
} |
807 |
|
|
/* DECLINE the rest of the offers. */ |
808 |
|
|
while (!TAILQ_EMPTY(&client->offered_leases)) { |
809 |
|
|
lease = TAILQ_FIRST(&client->offered_leases); |
810 |
|
|
TAILQ_REMOVE(&client->offered_leases, lease, next); |
811 |
|
|
make_decline(lease); |
812 |
|
|
send_decline(); |
813 |
|
|
free_client_lease(lease); |
814 |
|
|
} |
815 |
|
|
|
816 |
|
|
if (!picked) { |
817 |
|
|
state_panic(); |
818 |
|
|
return; |
819 |
|
|
} |
820 |
|
|
|
821 |
|
|
/* If it was a BOOTREPLY, we can just take the lease right now. */ |
822 |
|
|
if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) { |
823 |
|
|
struct option_data *option; |
824 |
|
|
|
825 |
|
|
client->new = picked; |
826 |
|
|
|
827 |
|
|
/* |
828 |
|
|
* Set (unsigned 32 bit) options |
829 |
|
|
* |
830 |
|
|
* DHO_DHCP_LEASE_TIME (12000 seconds), |
831 |
|
|
* DHO_RENEWAL_TIME (8000 seconds) |
832 |
|
|
* DHO_REBINDING_TIME (10000 seconds) |
833 |
|
|
* |
834 |
|
|
* so bind_lease() can set the lease times. Note that the |
835 |
|
|
* values must be big-endian. |
836 |
|
|
*/ |
837 |
|
|
option = &client->new->options[DHO_DHCP_LEASE_TIME]; |
838 |
|
|
option->data = malloc(4); |
839 |
|
|
if (option->data) { |
840 |
|
|
option->len = 4; |
841 |
|
|
memcpy(option->data, "\x00\x00\x2e\xe0", 4); |
842 |
|
|
} |
843 |
|
|
option = &client->new->options[DHO_DHCP_RENEWAL_TIME]; |
844 |
|
|
option->data = malloc(4); |
845 |
|
|
if (option->data) { |
846 |
|
|
option->len = 4; |
847 |
|
|
memcpy(option->data, "\x00\x00\x1f\x40", 4); |
848 |
|
|
} |
849 |
|
|
option = &client->new->options[DHO_DHCP_REBINDING_TIME]; |
850 |
|
|
option->data = malloc(4); |
851 |
|
|
if (option->data) { |
852 |
|
|
option->len = 4; |
853 |
|
|
memcpy(option->data, "\x00\x00\x27\x10", 4); |
854 |
|
|
} |
855 |
|
|
|
856 |
|
|
client->state = S_REQUESTING; |
857 |
|
|
bind_lease(); |
858 |
|
|
|
859 |
|
|
return; |
860 |
|
|
} |
861 |
|
|
|
862 |
|
|
client->destination.s_addr = INADDR_BROADCAST; |
863 |
|
|
client->state = S_REQUESTING; |
864 |
|
|
client->first_sending = time(NULL); |
865 |
|
|
|
866 |
|
|
client->interval = 0; |
867 |
|
|
|
868 |
|
|
/* |
869 |
|
|
* Make a DHCPREQUEST packet from the lease we picked. Keep |
870 |
|
|
* the current xid, as all offers should have had the same |
871 |
|
|
* one. |
872 |
|
|
*/ |
873 |
|
|
make_request(picked); |
874 |
|
|
|
875 |
|
|
/* Toss the lease we picked - we'll get it back in a DHCPACK. */ |
876 |
|
|
free_client_lease(picked); |
877 |
|
|
|
878 |
|
|
send_request(); |
879 |
|
|
} |
880 |
|
|
|
881 |
|
|
void |
882 |
|
|
dhcpack(struct in_addr client_addr, struct option_data *options, char *info) |
883 |
|
|
{ |
884 |
|
|
struct client_lease *lease; |
885 |
|
|
|
886 |
|
|
if (client->state != S_REBOOTING && |
887 |
|
|
client->state != S_REQUESTING && |
888 |
|
|
client->state != S_RENEWING && |
889 |
|
|
client->state != S_REBINDING) { |
890 |
|
|
#ifdef DEBUG |
891 |
|
|
debug("Unexpected %s. State #%d", info, client->state); |
892 |
|
|
#endif |
893 |
|
|
return; |
894 |
|
|
} |
895 |
|
|
|
896 |
|
|
note("%s", info); |
897 |
|
|
|
898 |
|
|
lease = packet_to_lease(client_addr, options); |
899 |
|
|
if (lease->is_invalid) { |
900 |
|
|
note("Unsatisfactory %s", info); |
901 |
|
|
make_decline(lease); |
902 |
|
|
send_decline(); |
903 |
|
|
free_client_lease(lease); |
904 |
|
|
client->state = S_INIT; |
905 |
|
|
state_init(); |
906 |
|
|
return; |
907 |
|
|
} |
908 |
|
|
|
909 |
|
|
client->new = lease; |
910 |
|
|
|
911 |
|
|
/* Stop resending DHCPREQUEST. */ |
912 |
|
|
cancel_timeout(); |
913 |
|
|
|
914 |
|
|
bind_lease(); |
915 |
|
|
} |
916 |
|
|
|
917 |
|
|
void |
918 |
|
|
bind_lease(void) |
919 |
|
|
{ |
920 |
|
|
struct in_addr gateway, mask; |
921 |
|
|
struct option_data *options, *opt; |
922 |
|
|
struct client_lease *lease, *pl; |
923 |
|
|
time_t cur_time; |
924 |
|
|
int seen; |
925 |
|
|
|
926 |
|
|
/* |
927 |
|
|
* Clear out any old resolv_conf in case the lease has been here |
928 |
|
|
* before (e.g. static lease). |
929 |
|
|
*/ |
930 |
|
|
free(client->new->resolv_conf); |
931 |
|
|
client->new->resolv_conf = NULL; |
932 |
|
|
|
933 |
|
|
lease = apply_defaults(client->new); |
934 |
|
|
options = lease->options; |
935 |
|
|
|
936 |
|
|
set_lease_times(lease); |
937 |
|
|
|
938 |
|
|
client->new->expiry = lease->expiry; |
939 |
|
|
client->new->renewal = lease->renewal; |
940 |
|
|
client->new->rebind = lease->rebind; |
941 |
|
|
|
942 |
|
|
/* |
943 |
|
|
* A duplicate lease once we are responsible & S_RENEWING means we don't |
944 |
|
|
* need to change the interface, routing table or resolv.conf. |
945 |
|
|
*/ |
946 |
|
|
if ((client->flags & IS_RESPONSIBLE) && client->state == S_RENEWING && |
947 |
|
|
compare_lease(client->active, client->new) == 0) { |
948 |
|
|
client->new->resolv_conf = client->active->resolv_conf; |
949 |
|
|
client->active->resolv_conf = NULL; |
950 |
|
|
client->active = client->new; |
951 |
|
|
client->new = NULL; |
952 |
|
|
note("bound to %s -- renewal in %lld seconds.", |
953 |
|
|
inet_ntoa(client->active->address), |
954 |
|
|
(long long)(client->active->renewal - time(NULL))); |
955 |
|
|
goto newlease; |
956 |
|
|
} |
957 |
|
|
|
958 |
|
|
client->new->resolv_conf = resolv_conf_contents( |
959 |
|
|
&options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS], |
960 |
|
|
&options[DHO_DOMAIN_SEARCH]); |
961 |
|
|
|
962 |
|
|
/* Replace the old active lease with the new one. */ |
963 |
|
|
client->active = client->new; |
964 |
|
|
client->new = NULL; |
965 |
|
|
|
966 |
|
|
/* Deleting the addresses also clears out arp entries. */ |
967 |
|
|
delete_addresses(); |
968 |
|
|
flush_routes(); |
969 |
|
|
|
970 |
|
|
opt = &options[DHO_INTERFACE_MTU]; |
971 |
|
|
if (opt->len == sizeof(u_int16_t)) { |
972 |
|
|
u_int16_t mtu; |
973 |
|
|
memcpy(&mtu, opt->data, sizeof(mtu)); |
974 |
|
|
mtu = ntohs(mtu); |
975 |
|
|
/* "The minimum legal value for the MTU is 68." */ |
976 |
|
|
if (mtu < 68) |
977 |
|
|
warning("mtu size %u < 68: ignored", mtu); |
978 |
|
|
else |
979 |
|
|
set_interface_mtu(mtu); |
980 |
|
|
} |
981 |
|
|
|
982 |
|
|
opt = &options[DHO_SUBNET_MASK]; |
983 |
|
|
if (opt->len == sizeof(mask)) |
984 |
|
|
mask.s_addr = ((struct in_addr *)opt->data)->s_addr; |
985 |
|
|
else |
986 |
|
|
mask.s_addr = INADDR_ANY; |
987 |
|
|
|
988 |
|
|
/* |
989 |
|
|
* Add address and default route last, so we know when the binding |
990 |
|
|
* is done by the RTM_NEWADDR message being received. |
991 |
|
|
*/ |
992 |
|
|
add_address(client->active->address, mask); |
993 |
|
|
if (options[DHO_CLASSLESS_STATIC_ROUTES].len) { |
994 |
|
|
add_classless_static_routes( |
995 |
|
|
&options[DHO_CLASSLESS_STATIC_ROUTES], |
996 |
|
|
client->active->address); |
997 |
|
|
} else if (options[DHO_CLASSLESS_MS_STATIC_ROUTES].len) { |
998 |
|
|
add_classless_static_routes( |
999 |
|
|
&options[DHO_CLASSLESS_MS_STATIC_ROUTES], |
1000 |
|
|
client->active->address); |
1001 |
|
|
} else { |
1002 |
|
|
opt = &options[DHO_ROUTERS]; |
1003 |
|
|
if (opt->len >= sizeof(gateway)) { |
1004 |
|
|
/* XXX Only use FIRST router address for now. */ |
1005 |
|
|
gateway.s_addr = ((struct in_addr *)opt->data)->s_addr; |
1006 |
|
|
|
1007 |
|
|
/* |
1008 |
|
|
* To be compatible with ISC DHCP behavior on Linux, if |
1009 |
|
|
* we were given a /32 IP assignment, then add a /32 |
1010 |
|
|
* direct route for the gateway to make it routable. |
1011 |
|
|
*/ |
1012 |
|
|
if (mask.s_addr == INADDR_BROADCAST) { |
1013 |
|
|
add_direct_route(gateway, mask, |
1014 |
|
|
client->active->address); |
1015 |
|
|
} |
1016 |
|
|
|
1017 |
|
|
add_default_route(client->active->address, gateway); |
1018 |
|
|
} |
1019 |
|
|
if (options[DHO_STATIC_ROUTES].len) |
1020 |
|
|
add_static_routes(&options[DHO_STATIC_ROUTES], |
1021 |
|
|
client->active->address); |
1022 |
|
|
} |
1023 |
|
|
|
1024 |
|
|
newlease: |
1025 |
|
|
rewrite_option_db(client->active, lease); |
1026 |
|
|
free_client_lease(lease); |
1027 |
|
|
|
1028 |
|
|
/* |
1029 |
|
|
* Remove previous dynamic lease(es) for this address, and any expired |
1030 |
|
|
* dynamic leases. |
1031 |
|
|
*/ |
1032 |
|
|
seen = 0; |
1033 |
|
|
time(&cur_time); |
1034 |
|
|
TAILQ_FOREACH_SAFE(lease, &client->leases, next, pl) { |
1035 |
|
|
if (lease->is_static) |
1036 |
|
|
break; |
1037 |
|
|
if (client->active == lease) |
1038 |
|
|
seen = 1; |
1039 |
|
|
else if (lease->expiry <= cur_time || lease->address.s_addr == |
1040 |
|
|
client->active->address.s_addr) { |
1041 |
|
|
TAILQ_REMOVE(&client->leases, lease, next); |
1042 |
|
|
free_client_lease(lease); |
1043 |
|
|
} |
1044 |
|
|
} |
1045 |
|
|
if (!client->active->is_static && !seen) |
1046 |
|
|
TAILQ_INSERT_HEAD(&client->leases, client->active, next); |
1047 |
|
|
|
1048 |
|
|
client->state = S_BOUND; |
1049 |
|
|
|
1050 |
|
|
/* Write out new leases file. */ |
1051 |
|
|
rewrite_client_leases(); |
1052 |
|
|
|
1053 |
|
|
/* Set timeout to start the renewal process. */ |
1054 |
|
|
set_timeout(client->active->renewal, state_bound); |
1055 |
|
|
} |
1056 |
|
|
|
1057 |
|
|
/* |
1058 |
|
|
* Called when we've successfully bound to a particular lease, but the renewal |
1059 |
|
|
* time on that lease has expired. We are expected to unicast a DHCPREQUEST to |
1060 |
|
|
* the server that gave us our original lease. |
1061 |
|
|
*/ |
1062 |
|
|
void |
1063 |
|
|
state_bound(void) |
1064 |
|
|
{ |
1065 |
|
|
struct option_data *opt; |
1066 |
|
|
struct in_addr *dest; |
1067 |
|
|
|
1068 |
|
|
client->xid = arc4random(); |
1069 |
|
|
make_request(client->active); |
1070 |
|
|
|
1071 |
|
|
dest = &client->destination; |
1072 |
|
|
opt = &client->active->options[DHO_DHCP_SERVER_IDENTIFIER]; |
1073 |
|
|
|
1074 |
|
|
if (opt->len == sizeof(*dest)) |
1075 |
|
|
dest->s_addr = ((struct in_addr *)opt->data)->s_addr; |
1076 |
|
|
else |
1077 |
|
|
dest->s_addr = INADDR_BROADCAST; |
1078 |
|
|
|
1079 |
|
|
client->first_sending = time(NULL); |
1080 |
|
|
client->interval = 0; |
1081 |
|
|
client->state = S_RENEWING; |
1082 |
|
|
|
1083 |
|
|
send_request(); |
1084 |
|
|
} |
1085 |
|
|
|
1086 |
|
|
void |
1087 |
|
|
dhcpoffer(struct in_addr client_addr, struct option_data *options, char *info) |
1088 |
|
|
{ |
1089 |
|
|
struct client_lease *lease, *lp; |
1090 |
|
|
time_t stop_selecting; |
1091 |
|
|
|
1092 |
|
|
if (client->state != S_SELECTING) { |
1093 |
|
|
#ifdef DEBUG |
1094 |
|
|
debug("Unexpected %s. State #%d.", info, client->state); |
1095 |
|
|
#endif |
1096 |
|
|
return; |
1097 |
|
|
} |
1098 |
|
|
|
1099 |
|
|
note("%s", info); |
1100 |
|
|
|
1101 |
|
|
/* If we've already seen this lease, don't record it again. */ |
1102 |
|
|
TAILQ_FOREACH(lp, &client->offered_leases, next) { |
1103 |
|
|
if (!memcmp(&lp->address.s_addr, &client->packet.yiaddr, |
1104 |
|
|
sizeof(in_addr_t))) { |
1105 |
|
|
#ifdef DEBUG |
1106 |
|
|
debug("Duplicate %s.", info); |
1107 |
|
|
#endif |
1108 |
|
|
return; |
1109 |
|
|
} |
1110 |
|
|
} |
1111 |
|
|
|
1112 |
|
|
lease = packet_to_lease(client_addr, options); |
1113 |
|
|
|
1114 |
|
|
/* |
1115 |
|
|
* If this lease was acquired through a BOOTREPLY, record that |
1116 |
|
|
* fact. |
1117 |
|
|
*/ |
1118 |
|
|
if (!options[DHO_DHCP_MESSAGE_TYPE].len) |
1119 |
|
|
lease->is_bootp = 1; |
1120 |
|
|
|
1121 |
|
|
/* Figure out when we're supposed to stop selecting. */ |
1122 |
|
|
stop_selecting = client->first_sending + config->select_interval; |
1123 |
|
|
|
1124 |
|
|
if (TAILQ_EMPTY(&client->offered_leases)) { |
1125 |
|
|
TAILQ_INSERT_HEAD(&client->offered_leases, lease, next); |
1126 |
|
|
} else if (lease->address.s_addr == client->requested_address.s_addr) { |
1127 |
|
|
/* The lease we expected - put it at the head of the list. */ |
1128 |
|
|
TAILQ_INSERT_HEAD(&client->offered_leases, lease, next); |
1129 |
|
|
} else { |
1130 |
|
|
/* Not the lease we expected - put it at the end of the list. */ |
1131 |
|
|
TAILQ_INSERT_TAIL(&client->offered_leases, lease, next); |
1132 |
|
|
} |
1133 |
|
|
|
1134 |
|
|
if (stop_selecting <= time(NULL)) |
1135 |
|
|
state_selecting(); |
1136 |
|
|
else |
1137 |
|
|
set_timeout(stop_selecting, state_selecting); |
1138 |
|
|
} |
1139 |
|
|
|
1140 |
|
|
int |
1141 |
|
|
addressinuse(struct in_addr address, char *ifname) |
1142 |
|
|
{ |
1143 |
|
|
struct ifaddrs *ifap, *ifa; |
1144 |
|
|
struct sockaddr_in *sin; |
1145 |
|
|
int used = 0; |
1146 |
|
|
|
1147 |
|
|
if (getifaddrs(&ifap) != 0) { |
1148 |
|
|
warning("addressinuse: getifaddrs: %s", strerror(errno)); |
1149 |
|
|
return (0); |
1150 |
|
|
} |
1151 |
|
|
|
1152 |
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
1153 |
|
|
if (ifa->ifa_addr == NULL || |
1154 |
|
|
ifa->ifa_addr->sa_family != AF_INET) |
1155 |
|
|
continue; |
1156 |
|
|
|
1157 |
|
|
sin = (struct sockaddr_in *)ifa->ifa_addr; |
1158 |
|
|
if (memcmp(&address, &sin->sin_addr, sizeof(address)) == 0) { |
1159 |
|
|
strlcpy(ifname, ifa->ifa_name, IF_NAMESIZE); |
1160 |
|
|
used = 1; |
1161 |
|
|
if (strncmp(ifname, ifi->name, IF_NAMESIZE) != 0) |
1162 |
|
|
break; |
1163 |
|
|
} |
1164 |
|
|
} |
1165 |
|
|
|
1166 |
|
|
freeifaddrs(ifap); |
1167 |
|
|
return (used); |
1168 |
|
|
} |
1169 |
|
|
|
1170 |
|
|
/* |
1171 |
|
|
* Allocate a client_lease structure and initialize it from the |
1172 |
|
|
* parameters in the specified packet. |
1173 |
|
|
*/ |
1174 |
|
|
struct client_lease * |
1175 |
|
|
packet_to_lease(struct in_addr client_addr, struct option_data *options) |
1176 |
|
|
{ |
1177 |
|
|
char ifname[IF_NAMESIZE]; |
1178 |
|
|
struct client_lease *lease; |
1179 |
|
|
char *pretty, *buf; |
1180 |
|
|
int i, sz; |
1181 |
|
|
|
1182 |
|
|
lease = calloc(1, sizeof(struct client_lease)); |
1183 |
|
|
if (!lease) { |
1184 |
|
|
warning("dhcpoffer: no memory to create lease."); |
1185 |
|
|
return (NULL); |
1186 |
|
|
} |
1187 |
|
|
|
1188 |
|
|
/* Copy the lease options. */ |
1189 |
|
|
for (i = 0; i < 256; i++) { |
1190 |
|
|
if (options[i].len == 0) |
1191 |
|
|
continue; |
1192 |
|
|
if (!unknown_ok && strncmp("option-", |
1193 |
|
|
dhcp_options[i].name, 7) != 0) { |
1194 |
|
|
warning("dhcpoffer: unknown option %d", i); |
1195 |
|
|
lease->is_invalid = 1; |
1196 |
|
|
} |
1197 |
|
|
pretty = pretty_print_option(i, &options[i], 0); |
1198 |
|
|
if (strlen(pretty) == 0) |
1199 |
|
|
continue; |
1200 |
|
|
switch (i) { |
1201 |
|
|
case DHO_DOMAIN_SEARCH: |
1202 |
|
|
/* Must decode the option into text to check names. */ |
1203 |
|
|
buf = calloc(1, DHCP_DOMAIN_SEARCH_LEN); |
1204 |
|
|
if (buf == NULL) |
1205 |
|
|
error("No memory to decode domain search"); |
1206 |
|
|
sz = pretty_print_domain_search(buf, |
1207 |
|
|
DHCP_DOMAIN_SEARCH_LEN, |
1208 |
|
|
options[i].data, options[i].len); |
1209 |
|
|
if (strlen(buf) == 0) |
1210 |
|
|
continue; |
1211 |
|
|
if (sz == -1 || !res_hnok_list(buf)) |
1212 |
|
|
warning("Bogus data for option %s", |
1213 |
|
|
dhcp_options[i].name); |
1214 |
|
|
free(buf); |
1215 |
|
|
break; |
1216 |
|
|
case DHO_DOMAIN_NAME: |
1217 |
|
|
/* |
1218 |
|
|
* Allow deviant but historically blessed |
1219 |
|
|
* practice of supplying multiple domain names |
1220 |
|
|
* with DHO_DOMAIN_NAME. Thus allowing multiple |
1221 |
|
|
* entries in the resolv.conf 'search' statement. |
1222 |
|
|
*/ |
1223 |
|
|
if (!res_hnok_list(pretty)) { |
1224 |
|
|
warning("Bogus data for option %s", |
1225 |
|
|
dhcp_options[i].name); |
1226 |
|
|
continue; |
1227 |
|
|
} |
1228 |
|
|
break; |
1229 |
|
|
case DHO_HOST_NAME: |
1230 |
|
|
case DHO_NIS_DOMAIN: |
1231 |
|
|
if (!res_hnok(pretty)) { |
1232 |
|
|
warning("Bogus data for option %s", |
1233 |
|
|
dhcp_options[i].name); |
1234 |
|
|
continue; |
1235 |
|
|
} |
1236 |
|
|
break; |
1237 |
|
|
default: |
1238 |
|
|
break; |
1239 |
|
|
} |
1240 |
|
|
lease->options[i] = options[i]; |
1241 |
|
|
options[i].data = NULL; |
1242 |
|
|
options[i].len = 0; |
1243 |
|
|
} |
1244 |
|
|
|
1245 |
|
|
/* |
1246 |
|
|
* If this lease doesn't supply a required parameter, blow it off. |
1247 |
|
|
*/ |
1248 |
|
|
for (i = 0; i < config->required_option_count; i++) { |
1249 |
|
|
if (!lease->options[config->required_options[i]].len) { |
1250 |
|
|
warning("Missing required parameter %s", |
1251 |
|
|
dhcp_options[i].name); |
1252 |
|
|
lease->is_invalid = 1; |
1253 |
|
|
} |
1254 |
|
|
} |
1255 |
|
|
|
1256 |
|
|
/* |
1257 |
|
|
* If this lease is trying to sell us an address we are already |
1258 |
|
|
* using, blow it off. |
1259 |
|
|
*/ |
1260 |
|
|
lease->address.s_addr = client->packet.yiaddr.s_addr; |
1261 |
|
|
memset(ifname, 0, sizeof(ifname)); |
1262 |
|
|
if (addressinuse(lease->address, ifname) && |
1263 |
|
|
strncmp(ifname, ifi->name, IF_NAMESIZE) != 0) { |
1264 |
|
|
warning("%s already configured on %s", |
1265 |
|
|
inet_ntoa(lease->address), ifname); |
1266 |
|
|
lease->is_invalid = 1; |
1267 |
|
|
} |
1268 |
|
|
|
1269 |
|
|
/* Save the siaddr (a.k.a. next-server) info. */ |
1270 |
|
|
lease->next_server.s_addr = client->packet.siaddr.s_addr; |
1271 |
|
|
|
1272 |
|
|
/* If the server name was filled out, copy it. */ |
1273 |
|
|
if ((!lease->options[DHO_DHCP_OPTION_OVERLOAD].len || |
1274 |
|
|
!(lease->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) && |
1275 |
|
|
client->packet.sname[0]) { |
1276 |
|
|
lease->server_name = malloc(DHCP_SNAME_LEN + 1); |
1277 |
|
|
if (!lease->server_name) { |
1278 |
|
|
warning("dhcpoffer: no memory for server name."); |
1279 |
|
|
lease->is_invalid = 1; |
1280 |
|
|
} |
1281 |
|
|
memcpy(lease->server_name, client->packet.sname, |
1282 |
|
|
DHCP_SNAME_LEN); |
1283 |
|
|
lease->server_name[DHCP_SNAME_LEN] = '\0'; |
1284 |
|
|
if (!res_hnok(lease->server_name)) { |
1285 |
|
|
warning("Bogus server name %s", lease->server_name); |
1286 |
|
|
lease->is_invalid = 1; |
1287 |
|
|
} |
1288 |
|
|
} |
1289 |
|
|
|
1290 |
|
|
/* Ditto for the filename. */ |
1291 |
|
|
if ((!lease->options[DHO_DHCP_OPTION_OVERLOAD].len || |
1292 |
|
|
!(lease->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) && |
1293 |
|
|
client->packet.file[0]) { |
1294 |
|
|
/* Don't count on the NUL terminator. */ |
1295 |
|
|
lease->filename = malloc(DHCP_FILE_LEN + 1); |
1296 |
|
|
if (!lease->filename) { |
1297 |
|
|
warning("dhcpoffer: no memory for filename."); |
1298 |
|
|
lease->is_invalid = 1; |
1299 |
|
|
} |
1300 |
|
|
memcpy(lease->filename, client->packet.file, DHCP_FILE_LEN); |
1301 |
|
|
lease->filename[DHCP_FILE_LEN] = '\0'; |
1302 |
|
|
} |
1303 |
|
|
return lease; |
1304 |
|
|
} |
1305 |
|
|
|
1306 |
|
|
void |
1307 |
|
|
dhcpnak(struct in_addr client_addr, struct option_data *options, char *info) |
1308 |
|
|
{ |
1309 |
|
|
if (client->state != S_REBOOTING && |
1310 |
|
|
client->state != S_REQUESTING && |
1311 |
|
|
client->state != S_RENEWING && |
1312 |
|
|
client->state != S_REBINDING) { |
1313 |
|
|
#ifdef DEBUG |
1314 |
|
|
debug("Unexpected %s. State #%d", info, client->state); |
1315 |
|
|
#endif |
1316 |
|
|
return; |
1317 |
|
|
} |
1318 |
|
|
|
1319 |
|
|
if (!client->active) { |
1320 |
|
|
#ifdef DEBUG |
1321 |
|
|
debug("Unexpected %s. No active lease.", info); |
1322 |
|
|
#endif |
1323 |
|
|
return; |
1324 |
|
|
} |
1325 |
|
|
|
1326 |
|
|
note("%s", info); |
1327 |
|
|
|
1328 |
|
|
/* XXX Do we really want to remove a NAK'd lease from the database? */ |
1329 |
|
|
if (!client->active->is_static) { |
1330 |
|
|
TAILQ_REMOVE(&client->leases, client->active, next); |
1331 |
|
|
free_client_lease(client->active); |
1332 |
|
|
} |
1333 |
|
|
|
1334 |
|
|
client->active = NULL; |
1335 |
|
|
|
1336 |
|
|
/* Stop sending DHCPREQUEST packets. */ |
1337 |
|
|
cancel_timeout(); |
1338 |
|
|
|
1339 |
|
|
client->state = S_INIT; |
1340 |
|
|
state_init(); |
1341 |
|
|
} |
1342 |
|
|
|
1343 |
|
|
/* |
1344 |
|
|
* Send out a DHCPDISCOVER packet, and set a timeout to send out another |
1345 |
|
|
* one after the right interval has expired. If we don't get an offer by |
1346 |
|
|
* the time we reach the panic interval, call the panic function. |
1347 |
|
|
*/ |
1348 |
|
|
void |
1349 |
|
|
send_discover(void) |
1350 |
|
|
{ |
1351 |
|
|
time_t cur_time; |
1352 |
|
|
ssize_t rslt; |
1353 |
|
|
int interval; |
1354 |
|
|
|
1355 |
|
|
time(&cur_time); |
1356 |
|
|
|
1357 |
|
|
/* Figure out how long it's been since we started transmitting. */ |
1358 |
|
|
interval = cur_time - client->first_sending; |
1359 |
|
|
|
1360 |
|
|
if (interval > config->timeout) { |
1361 |
|
|
state_panic(); |
1362 |
|
|
return; |
1363 |
|
|
} |
1364 |
|
|
|
1365 |
|
|
/* |
1366 |
|
|
* If we're supposed to increase the interval, do so. If it's |
1367 |
|
|
* currently zero (i.e., we haven't sent any packets yet), set |
1368 |
|
|
* it to initial_interval; otherwise, add to it a random |
1369 |
|
|
* number between zero and two times itself. On average, this |
1370 |
|
|
* means that it will double with every transmission. |
1371 |
|
|
*/ |
1372 |
|
|
if (!client->interval) |
1373 |
|
|
client->interval = config->initial_interval; |
1374 |
|
|
else { |
1375 |
|
|
client->interval += arc4random_uniform(2 * client->interval); |
1376 |
|
|
} |
1377 |
|
|
|
1378 |
|
|
/* Don't backoff past cutoff. */ |
1379 |
|
|
if (client->interval > config->backoff_cutoff) |
1380 |
|
|
client->interval = (config->backoff_cutoff / 2) + |
1381 |
|
|
arc4random_uniform(config->backoff_cutoff); |
1382 |
|
|
|
1383 |
|
|
/* If the backoff would take us to the panic timeout, just use that |
1384 |
|
|
as the interval. */ |
1385 |
|
|
if (cur_time + client->interval > |
1386 |
|
|
client->first_sending + config->timeout) |
1387 |
|
|
client->interval = (client->first_sending + |
1388 |
|
|
config->timeout) - cur_time + 1; |
1389 |
|
|
|
1390 |
|
|
/* Record the number of seconds since we started sending. */ |
1391 |
|
|
if (interval < UINT16_MAX) |
1392 |
|
|
client->bootrequest_packet.secs = htons(interval); |
1393 |
|
|
else |
1394 |
|
|
client->bootrequest_packet.secs = htons(UINT16_MAX); |
1395 |
|
|
client->secs = client->bootrequest_packet.secs; |
1396 |
|
|
|
1397 |
|
|
note("DHCPDISCOVER on %s - interval %lld", ifi->name, |
1398 |
|
|
(long long)client->interval); |
1399 |
|
|
|
1400 |
|
|
rslt = send_packet(inaddr_any, inaddr_broadcast); |
1401 |
|
|
if (rslt == -1 && errno == EAFNOSUPPORT) { |
1402 |
|
|
warning("dhclient cannot be used on %s", ifi->name); |
1403 |
|
|
quit = INTERNALSIG; |
1404 |
|
|
} else |
1405 |
|
|
set_timeout_interval(client->interval, send_discover); |
1406 |
|
|
} |
1407 |
|
|
|
1408 |
|
|
/* |
1409 |
|
|
* Called if we haven't received any offers in a preset amount of time. When |
1410 |
|
|
* this happens, we try to use existing leases that haven't yet expired. |
1411 |
|
|
*/ |
1412 |
|
|
void |
1413 |
|
|
state_panic(void) |
1414 |
|
|
{ |
1415 |
|
|
char ifname[IF_NAMESIZE]; |
1416 |
|
|
struct client_lease *lp; |
1417 |
|
|
time_t cur_time; |
1418 |
|
|
|
1419 |
|
|
time(&cur_time); |
1420 |
|
|
note("No acceptable DHCPOFFERS received."); |
1421 |
|
|
|
1422 |
|
|
/* Run through the list of leases and see if one can be used. */ |
1423 |
|
|
time(&cur_time); |
1424 |
|
|
TAILQ_FOREACH(lp, &client->leases, next) { |
1425 |
|
|
if (addressinuse(lp->address, ifname) && |
1426 |
|
|
strncmp(ifname, ifi->name, IF_NAMESIZE) != 0) |
1427 |
|
|
continue; |
1428 |
|
|
if (lp->is_static) { |
1429 |
|
|
set_lease_times(lp); |
1430 |
|
|
note("Trying static lease %s", inet_ntoa(lp->address)); |
1431 |
|
|
} else if (lp->expiry <= cur_time) { |
1432 |
|
|
continue; |
1433 |
|
|
} else |
1434 |
|
|
note("Trying recorded lease %s", |
1435 |
|
|
inet_ntoa(lp->address)); |
1436 |
|
|
|
1437 |
|
|
client->new = lp; |
1438 |
|
|
client->state = S_REQUESTING; |
1439 |
|
|
bind_lease(); |
1440 |
|
|
|
1441 |
|
|
return; |
1442 |
|
|
} |
1443 |
|
|
|
1444 |
|
|
/* |
1445 |
|
|
* No leases were available, or what was available didn't work |
1446 |
|
|
*/ |
1447 |
|
|
note("No working leases in persistent database - sleeping."); |
1448 |
|
|
client->state = S_INIT; |
1449 |
|
|
set_timeout_interval(config->retry_interval, state_init); |
1450 |
|
|
go_daemon(); |
1451 |
|
|
} |
1452 |
|
|
|
1453 |
|
|
void |
1454 |
|
|
send_request(void) |
1455 |
|
|
{ |
1456 |
|
|
struct sockaddr_in destination; |
1457 |
|
|
struct in_addr from; |
1458 |
|
|
time_t cur_time; |
1459 |
|
|
int interval; |
1460 |
|
|
|
1461 |
|
|
time(&cur_time); |
1462 |
|
|
|
1463 |
|
|
/* Figure out how long it's been since we started transmitting. */ |
1464 |
|
|
interval = (int)(cur_time - client->first_sending); |
1465 |
|
|
|
1466 |
|
|
/* |
1467 |
|
|
* If we're in the INIT-REBOOT or REQUESTING state and we're |
1468 |
|
|
* past the reboot timeout, go to INIT and see if we can |
1469 |
|
|
* DISCOVER an address. |
1470 |
|
|
* |
1471 |
|
|
* XXX In the INIT-REBOOT state, if we don't get an ACK, it |
1472 |
|
|
* means either that we're on a network with no DHCP server, |
1473 |
|
|
* or that our server is down. In the latter case, assuming |
1474 |
|
|
* that there is a backup DHCP server, DHCPDISCOVER will get |
1475 |
|
|
* us a new address, but we could also have successfully |
1476 |
|
|
* reused our old address. In the former case, we're hosed |
1477 |
|
|
* anyway. This is not a win-prone situation. |
1478 |
|
|
*/ |
1479 |
|
|
if ((client->state == S_REBOOTING || |
1480 |
|
|
client->state == S_REQUESTING) && |
1481 |
|
|
interval > config->reboot_timeout) { |
1482 |
|
|
client->state = S_INIT; |
1483 |
|
|
cancel_timeout(); |
1484 |
|
|
state_init(); |
1485 |
|
|
return; |
1486 |
|
|
} |
1487 |
|
|
|
1488 |
|
|
/* |
1489 |
|
|
* If the lease has expired, relinquish the address and go back to the |
1490 |
|
|
* INIT state. |
1491 |
|
|
*/ |
1492 |
|
|
if (client->state != S_REQUESTING && |
1493 |
|
|
cur_time > client->active->expiry) { |
1494 |
|
|
if (client->active) |
1495 |
|
|
delete_address(client->active->address); |
1496 |
|
|
client->state = S_INIT; |
1497 |
|
|
state_init(); |
1498 |
|
|
return; |
1499 |
|
|
} |
1500 |
|
|
|
1501 |
|
|
/* Do the exponential backoff. */ |
1502 |
|
|
if (!client->interval) |
1503 |
|
|
client->interval = config->initial_interval; |
1504 |
|
|
else |
1505 |
|
|
client->interval += arc4random_uniform(2 * client->interval); |
1506 |
|
|
|
1507 |
|
|
/* Don't backoff past cutoff. */ |
1508 |
|
|
if (client->interval > config->backoff_cutoff) |
1509 |
|
|
client->interval = (config->backoff_cutoff / 2) + |
1510 |
|
|
arc4random_uniform(client->interval); |
1511 |
|
|
|
1512 |
|
|
/* |
1513 |
|
|
* If the backoff would take us to the expiry time, just set the |
1514 |
|
|
* timeout to the expiry time. |
1515 |
|
|
*/ |
1516 |
|
|
if (client->state != S_REQUESTING && cur_time + client->interval > |
1517 |
|
|
client->active->expiry) |
1518 |
|
|
client->interval = client->active->expiry - cur_time + 1; |
1519 |
|
|
|
1520 |
|
|
/* |
1521 |
|
|
* If the reboot timeout has expired, or the lease rebind time has |
1522 |
|
|
* elapsed, or if we're not yet bound, broadcast the DHCPREQUEST rather |
1523 |
|
|
* than unicasting. |
1524 |
|
|
*/ |
1525 |
|
|
memset(&destination, 0, sizeof(destination)); |
1526 |
|
|
if (client->state == S_REQUESTING || |
1527 |
|
|
client->state == S_REBOOTING || |
1528 |
|
|
cur_time > client->active->rebind || |
1529 |
|
|
interval > config->reboot_timeout) |
1530 |
|
|
destination.sin_addr.s_addr = INADDR_BROADCAST; |
1531 |
|
|
else |
1532 |
|
|
destination.sin_addr.s_addr = client->destination.s_addr; |
1533 |
|
|
|
1534 |
|
|
if (client->state != S_REQUESTING) |
1535 |
|
|
from.s_addr = client->active->address.s_addr; |
1536 |
|
|
else |
1537 |
|
|
from.s_addr = INADDR_ANY; |
1538 |
|
|
|
1539 |
|
|
/* Record the number of seconds since we started sending. */ |
1540 |
|
|
if (client->state == S_REQUESTING) |
1541 |
|
|
client->bootrequest_packet.secs = client->secs; |
1542 |
|
|
else { |
1543 |
|
|
if (interval < UINT16_MAX) |
1544 |
|
|
client->bootrequest_packet.secs = htons(interval); |
1545 |
|
|
else |
1546 |
|
|
client->bootrequest_packet.secs = htons(UINT16_MAX); |
1547 |
|
|
} |
1548 |
|
|
|
1549 |
|
|
note("DHCPREQUEST on %s to %s", ifi->name, |
1550 |
|
|
inet_ntoa(destination.sin_addr)); |
1551 |
|
|
|
1552 |
|
|
send_packet(from, destination.sin_addr); |
1553 |
|
|
|
1554 |
|
|
set_timeout_interval(client->interval, send_request); |
1555 |
|
|
} |
1556 |
|
|
|
1557 |
|
|
void |
1558 |
|
|
send_decline(void) |
1559 |
|
|
{ |
1560 |
|
|
note("DHCPDECLINE on %s", ifi->name); |
1561 |
|
|
|
1562 |
|
|
send_packet(inaddr_any, inaddr_broadcast); |
1563 |
|
|
} |
1564 |
|
|
|
1565 |
|
|
void |
1566 |
|
|
make_discover(struct client_lease *lease) |
1567 |
|
|
{ |
1568 |
|
|
struct option_data options[256]; |
1569 |
|
|
struct dhcp_packet *packet = &client->bootrequest_packet; |
1570 |
|
|
unsigned char discover = DHCPDISCOVER; |
1571 |
|
|
int i; |
1572 |
|
|
|
1573 |
|
|
memset(options, 0, sizeof(options)); |
1574 |
|
|
memset(packet, 0, sizeof(*packet)); |
1575 |
|
|
|
1576 |
|
|
/* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ |
1577 |
|
|
i = DHO_DHCP_MESSAGE_TYPE; |
1578 |
|
|
options[i].data = &discover; |
1579 |
|
|
options[i].len = sizeof(discover); |
1580 |
|
|
|
1581 |
|
|
/* Request the options we want */ |
1582 |
|
|
i = DHO_DHCP_PARAMETER_REQUEST_LIST; |
1583 |
|
|
options[i].data = config->requested_options; |
1584 |
|
|
options[i].len = config->requested_option_count; |
1585 |
|
|
|
1586 |
|
|
/* If we had an address, try to get it again. */ |
1587 |
|
|
if (lease) { |
1588 |
|
|
client->requested_address = lease->address; |
1589 |
|
|
i = DHO_DHCP_REQUESTED_ADDRESS; |
1590 |
|
|
options[i].data = (char *)&lease->address; |
1591 |
|
|
options[i].len = sizeof(lease->address); |
1592 |
|
|
} else |
1593 |
|
|
client->requested_address.s_addr = INADDR_ANY; |
1594 |
|
|
|
1595 |
|
|
/* Send any options requested in the config file. */ |
1596 |
|
|
for (i = 0; i < 256; i++) |
1597 |
|
|
if (!options[i].data && |
1598 |
|
|
config->send_options[i].data) { |
1599 |
|
|
options[i].data = config->send_options[i].data; |
1600 |
|
|
options[i].len = config->send_options[i].len; |
1601 |
|
|
} |
1602 |
|
|
|
1603 |
|
|
/* Set up the option buffer to fit in a minimal UDP packet. */ |
1604 |
|
|
i = cons_options(options); |
1605 |
|
|
if (i == -1 || packet->options[i] != DHO_END) |
1606 |
|
|
error("options do not fit in DHCPDISCOVER packet."); |
1607 |
|
|
client->bootrequest_packet_length = DHCP_FIXED_NON_UDP+i+1; |
1608 |
|
|
if (client->bootrequest_packet_length < BOOTP_MIN_LEN) |
1609 |
|
|
client->bootrequest_packet_length = BOOTP_MIN_LEN; |
1610 |
|
|
|
1611 |
|
|
packet->op = BOOTREQUEST; |
1612 |
|
|
packet->htype = HTYPE_ETHER ; |
1613 |
|
|
packet->hlen = ETHER_ADDR_LEN; |
1614 |
|
|
packet->hops = 0; |
1615 |
|
|
packet->xid = client->xid; |
1616 |
|
|
packet->secs = 0; /* filled in by send_discover. */ |
1617 |
|
|
packet->flags = 0; |
1618 |
|
|
|
1619 |
|
|
packet->ciaddr.s_addr = INADDR_ANY; |
1620 |
|
|
packet->yiaddr.s_addr = INADDR_ANY; |
1621 |
|
|
packet->siaddr.s_addr = INADDR_ANY; |
1622 |
|
|
packet->giaddr.s_addr = INADDR_ANY; |
1623 |
|
|
|
1624 |
|
|
memcpy(&packet->chaddr, ifi->hw_address.ether_addr_octet, |
1625 |
|
|
ETHER_ADDR_LEN); |
1626 |
|
|
} |
1627 |
|
|
|
1628 |
|
|
void |
1629 |
|
|
make_request(struct client_lease * lease) |
1630 |
|
|
{ |
1631 |
|
|
struct option_data options[256]; |
1632 |
|
|
struct dhcp_packet *packet = &client->bootrequest_packet; |
1633 |
|
|
unsigned char request = DHCPREQUEST; |
1634 |
|
|
int i; |
1635 |
|
|
|
1636 |
|
|
memset(options, 0, sizeof(options)); |
1637 |
|
|
memset(packet, 0, sizeof(*packet)); |
1638 |
|
|
|
1639 |
|
|
/* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ |
1640 |
|
|
i = DHO_DHCP_MESSAGE_TYPE; |
1641 |
|
|
options[i].data = &request; |
1642 |
|
|
options[i].len = sizeof(request); |
1643 |
|
|
|
1644 |
|
|
/* Request the options we want */ |
1645 |
|
|
i = DHO_DHCP_PARAMETER_REQUEST_LIST; |
1646 |
|
|
options[i].data = config->requested_options; |
1647 |
|
|
options[i].len = config->requested_option_count; |
1648 |
|
|
|
1649 |
|
|
/* |
1650 |
|
|
* If we are requesting an address that hasn't yet been assigned |
1651 |
|
|
* to us, use the DHCP Requested Address option. |
1652 |
|
|
*/ |
1653 |
|
|
if (client->state == S_REQUESTING) { |
1654 |
|
|
/* Send back the server identifier. */ |
1655 |
|
|
i = DHO_DHCP_SERVER_IDENTIFIER; |
1656 |
|
|
options[i].data = lease->options[i].data; |
1657 |
|
|
options[i].len = lease->options[i].len; |
1658 |
|
|
} |
1659 |
|
|
if (client->state == S_REQUESTING || |
1660 |
|
|
client->state == S_REBOOTING) { |
1661 |
|
|
client->requested_address = lease->address; |
1662 |
|
|
i = DHO_DHCP_REQUESTED_ADDRESS; |
1663 |
|
|
options[i].data = (char *)&lease->address.s_addr; |
1664 |
|
|
options[i].len = sizeof(in_addr_t); |
1665 |
|
|
} |
1666 |
|
|
|
1667 |
|
|
/* Send any options requested in the config file. */ |
1668 |
|
|
for (i = 0; i < 256; i++) |
1669 |
|
|
if (!options[i].data && config->send_options[i].data) { |
1670 |
|
|
options[i].data = config->send_options[i].data; |
1671 |
|
|
options[i].len = config->send_options[i].len; |
1672 |
|
|
} |
1673 |
|
|
|
1674 |
|
|
/* Set up the option buffer to fit in a minimal UDP packet. */ |
1675 |
|
|
i = cons_options(options); |
1676 |
|
|
if (i == -1 || packet->options[i] != DHO_END) |
1677 |
|
|
error("options do not fit in DHCPREQUEST packet."); |
1678 |
|
|
client->bootrequest_packet_length = DHCP_FIXED_NON_UDP+i+1; |
1679 |
|
|
if (client->bootrequest_packet_length < BOOTP_MIN_LEN) |
1680 |
|
|
client->bootrequest_packet_length = BOOTP_MIN_LEN; |
1681 |
|
|
|
1682 |
|
|
packet->op = BOOTREQUEST; |
1683 |
|
|
packet->htype = HTYPE_ETHER ; |
1684 |
|
|
packet->hlen = ETHER_ADDR_LEN; |
1685 |
|
|
packet->hops = 0; |
1686 |
|
|
packet->xid = client->xid; |
1687 |
|
|
packet->secs = 0; /* Filled in by send_request. */ |
1688 |
|
|
packet->flags = 0; |
1689 |
|
|
|
1690 |
|
|
/* |
1691 |
|
|
* If we own the address we're requesting, put it in ciaddr. Otherwise |
1692 |
|
|
* set ciaddr to zero. |
1693 |
|
|
*/ |
1694 |
|
|
if (client->state == S_BOUND || |
1695 |
|
|
client->state == S_RENEWING || |
1696 |
|
|
client->state == S_REBINDING) |
1697 |
|
|
packet->ciaddr.s_addr = lease->address.s_addr; |
1698 |
|
|
else |
1699 |
|
|
packet->ciaddr.s_addr = INADDR_ANY; |
1700 |
|
|
|
1701 |
|
|
packet->yiaddr.s_addr = INADDR_ANY; |
1702 |
|
|
packet->siaddr.s_addr = INADDR_ANY; |
1703 |
|
|
packet->giaddr.s_addr = INADDR_ANY; |
1704 |
|
|
|
1705 |
|
|
memcpy(&packet->chaddr, ifi->hw_address.ether_addr_octet, |
1706 |
|
|
ETHER_ADDR_LEN); |
1707 |
|
|
} |
1708 |
|
|
|
1709 |
|
|
void |
1710 |
|
|
make_decline(struct client_lease *lease) |
1711 |
|
|
{ |
1712 |
|
|
struct option_data options[256]; |
1713 |
|
|
struct dhcp_packet *packet = &client->bootrequest_packet; |
1714 |
|
|
unsigned char decline = DHCPDECLINE; |
1715 |
|
|
int i; |
1716 |
|
|
|
1717 |
|
|
memset(options, 0, sizeof(options)); |
1718 |
|
|
memset(packet, 0, sizeof(*packet)); |
1719 |
|
|
|
1720 |
|
|
/* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ |
1721 |
|
|
i = DHO_DHCP_MESSAGE_TYPE; |
1722 |
|
|
options[i].data = &decline; |
1723 |
|
|
options[i].len = sizeof(decline); |
1724 |
|
|
|
1725 |
|
|
/* Send back the server identifier. */ |
1726 |
|
|
i = DHO_DHCP_SERVER_IDENTIFIER; |
1727 |
|
|
options[i].data = lease->options[i].data; |
1728 |
|
|
options[i].len = lease->options[i].len; |
1729 |
|
|
|
1730 |
|
|
/* Send back the address we're declining. */ |
1731 |
|
|
i = DHO_DHCP_REQUESTED_ADDRESS; |
1732 |
|
|
options[i].data = (char *)&lease->address.s_addr; |
1733 |
|
|
options[i].len = sizeof(in_addr_t); |
1734 |
|
|
|
1735 |
|
|
/* Send the uid if the user supplied one. */ |
1736 |
|
|
i = DHO_DHCP_CLIENT_IDENTIFIER; |
1737 |
|
|
if (config->send_options[i].len) { |
1738 |
|
|
options[i].data = config->send_options[i].data; |
1739 |
|
|
options[i].len = config->send_options[i].len; |
1740 |
|
|
} |
1741 |
|
|
|
1742 |
|
|
/* Set up the option buffer to fit in a minimal UDP packet. */ |
1743 |
|
|
i = cons_options(options); |
1744 |
|
|
if (i == -1 || packet->options[i] != DHO_END) |
1745 |
|
|
error("options do not fit in DHCPDECLINE packet."); |
1746 |
|
|
client->bootrequest_packet_length = DHCP_FIXED_NON_UDP+i+1; |
1747 |
|
|
if (client->bootrequest_packet_length < BOOTP_MIN_LEN) |
1748 |
|
|
client->bootrequest_packet_length = BOOTP_MIN_LEN; |
1749 |
|
|
|
1750 |
|
|
packet->op = BOOTREQUEST; |
1751 |
|
|
packet->htype = HTYPE_ETHER ; |
1752 |
|
|
packet->hlen = ETHER_ADDR_LEN; |
1753 |
|
|
packet->hops = 0; |
1754 |
|
|
packet->xid = client->xid; |
1755 |
|
|
packet->secs = 0; /* Filled in by send_request. */ |
1756 |
|
|
packet->flags = 0; |
1757 |
|
|
|
1758 |
|
|
/* ciaddr must always be zero. */ |
1759 |
|
|
packet->ciaddr.s_addr = INADDR_ANY; |
1760 |
|
|
packet->yiaddr.s_addr = INADDR_ANY; |
1761 |
|
|
packet->siaddr.s_addr = INADDR_ANY; |
1762 |
|
|
packet->giaddr.s_addr = INADDR_ANY; |
1763 |
|
|
|
1764 |
|
|
memcpy(&packet->chaddr, ifi->hw_address.ether_addr_octet, |
1765 |
|
|
ETHER_ADDR_LEN); |
1766 |
|
|
} |
1767 |
|
|
|
1768 |
|
|
void |
1769 |
|
|
free_client_lease(struct client_lease *lease) |
1770 |
|
|
{ |
1771 |
|
|
int i; |
1772 |
|
|
|
1773 |
|
|
/* Static leases are forever. */ |
1774 |
|
|
if (lease->is_static) |
1775 |
|
|
return; |
1776 |
|
|
|
1777 |
|
|
free(lease->server_name); |
1778 |
|
|
free(lease->filename); |
1779 |
|
|
free(lease->resolv_conf); |
1780 |
|
|
for (i = 0; i < 256; i++) |
1781 |
|
|
free(lease->options[i].data); |
1782 |
|
|
|
1783 |
|
|
free(lease); |
1784 |
|
|
} |
1785 |
|
|
|
1786 |
|
|
void |
1787 |
|
|
rewrite_client_leases(void) |
1788 |
|
|
{ |
1789 |
|
|
struct client_lease *lp; |
1790 |
|
|
char *leasestr; |
1791 |
|
|
time_t cur_time; |
1792 |
|
|
|
1793 |
|
|
if (!leaseFile) /* XXX */ |
1794 |
|
|
error("lease file not open"); |
1795 |
|
|
|
1796 |
|
|
fflush(leaseFile); |
1797 |
|
|
rewind(leaseFile); |
1798 |
|
|
|
1799 |
|
|
/* |
1800 |
|
|
* The leases file is kept in chronological order, with the |
1801 |
|
|
* most recently bound lease last. When the file was read |
1802 |
|
|
* leases that were not expired were added to the head of the |
1803 |
|
|
* TAILQ client->leases as they were read. Therefore write out |
1804 |
|
|
* the leases in client->leases in reverse order to recreate |
1805 |
|
|
* the chonological order required. |
1806 |
|
|
*/ |
1807 |
|
|
time(&cur_time); |
1808 |
|
|
TAILQ_FOREACH_REVERSE(lp, &client->leases, _leases, next) { |
1809 |
|
|
/* Don't write out static leases from dhclient.conf. */ |
1810 |
|
|
if (lp->is_static) |
1811 |
|
|
continue; |
1812 |
|
|
if (lp->expiry <= cur_time) |
1813 |
|
|
continue; |
1814 |
|
|
leasestr = lease_as_string("lease", lp); |
1815 |
|
|
if (leasestr) |
1816 |
|
|
fprintf(leaseFile, "%s", leasestr); |
1817 |
|
|
else |
1818 |
|
|
warning("cannot make lease into string"); |
1819 |
|
|
} |
1820 |
|
|
|
1821 |
|
|
fflush(leaseFile); |
1822 |
|
|
ftruncate(fileno(leaseFile), ftello(leaseFile)); |
1823 |
|
|
fsync(fileno(leaseFile)); |
1824 |
|
|
} |
1825 |
|
|
|
1826 |
|
|
void |
1827 |
|
|
rewrite_option_db(struct client_lease *offered, struct client_lease *effective) |
1828 |
|
|
{ |
1829 |
|
|
u_int8_t db[8192]; |
1830 |
|
|
char *leasestr; |
1831 |
|
|
size_t n; |
1832 |
|
|
|
1833 |
|
|
if (strlen(path_option_db) == 0) |
1834 |
|
|
return; |
1835 |
|
|
|
1836 |
|
|
memset(db, 0, sizeof(db)); |
1837 |
|
|
|
1838 |
|
|
leasestr = lease_as_string("offered", offered); |
1839 |
|
|
if (leasestr) { |
1840 |
|
|
n = strlcat(db, leasestr, sizeof(db)); |
1841 |
|
|
if (n >= sizeof(db)) |
1842 |
|
|
warning("cannot fit offered lease into option db"); |
1843 |
|
|
} else |
1844 |
|
|
warning("cannot make offered lease into string"); |
1845 |
|
|
|
1846 |
|
|
leasestr = lease_as_string("effective", effective); |
1847 |
|
|
if (leasestr) { |
1848 |
|
|
n = strlcat(db, leasestr, sizeof(db)); |
1849 |
|
|
if (n >= sizeof(db)) |
1850 |
|
|
warning("cannot fit effective lease into option db"); |
1851 |
|
|
} else |
1852 |
|
|
warning("cannot make effective lease into string"); |
1853 |
|
|
|
1854 |
|
|
write_option_db(db, strlen(db)); |
1855 |
|
|
} |
1856 |
|
|
|
1857 |
|
|
char * |
1858 |
|
|
lease_as_string(char *type, struct client_lease *lease) |
1859 |
|
|
{ |
1860 |
|
|
static char leasestr[8192]; |
1861 |
|
|
struct option_data *opt; |
1862 |
|
|
char *p; |
1863 |
|
|
size_t sz, rsltsz; |
1864 |
|
|
int i, rslt; |
1865 |
|
|
|
1866 |
|
|
sz = sizeof(leasestr); |
1867 |
|
|
p = leasestr; |
1868 |
|
|
memset(p, 0, sz); |
1869 |
|
|
|
1870 |
|
|
rslt = snprintf(p, sz, "%s {\n" |
1871 |
|
|
"%s interface \"%s\";\n fixed-address %s;\n", |
1872 |
|
|
type, (lease->is_bootp) ? " bootp;\n" : "", ifi->name, |
1873 |
|
|
inet_ntoa(lease->address)); |
1874 |
|
|
if (rslt == -1 || rslt >= sz) |
1875 |
|
|
return (NULL); |
1876 |
|
|
p += rslt; |
1877 |
|
|
sz -= rslt; |
1878 |
|
|
|
1879 |
|
|
rslt = snprintf(p, sz, " next-server %s;\n", |
1880 |
|
|
inet_ntoa(lease->next_server)); |
1881 |
|
|
if (rslt == -1 || rslt >= sz) |
1882 |
|
|
return (NULL); |
1883 |
|
|
p += rslt; |
1884 |
|
|
sz -= rslt; |
1885 |
|
|
|
1886 |
|
|
if (lease->filename) { |
1887 |
|
|
rslt = snprintf(p, sz, " filename "); |
1888 |
|
|
if (rslt == -1 || rslt >= sz) |
1889 |
|
|
return (NULL); |
1890 |
|
|
p += rslt; |
1891 |
|
|
sz -= rslt; |
1892 |
|
|
rslt = pretty_print_string(p, sz, lease->filename, |
1893 |
|
|
strlen(lease->filename), 1); |
1894 |
|
|
if (rslt == -1 || rslt >= sz) |
1895 |
|
|
return (NULL); |
1896 |
|
|
p += rslt; |
1897 |
|
|
sz -= rslt; |
1898 |
|
|
rslt = snprintf(p, sz, ";\n"); |
1899 |
|
|
if (rslt == -1 || rslt >= sz) |
1900 |
|
|
return (NULL); |
1901 |
|
|
p += rslt; |
1902 |
|
|
sz -= rslt; |
1903 |
|
|
} |
1904 |
|
|
if (lease->server_name) { |
1905 |
|
|
rslt = snprintf(p, sz, " server-name "); |
1906 |
|
|
if (rslt == -1 || rslt >= sz) |
1907 |
|
|
return (NULL); |
1908 |
|
|
p += rslt; |
1909 |
|
|
sz -= rslt; |
1910 |
|
|
rslt = pretty_print_string(p, sz, lease->server_name, |
1911 |
|
|
strlen(lease->server_name), 1); |
1912 |
|
|
if (rslt == -1 || rslt >= sz) |
1913 |
|
|
return (NULL); |
1914 |
|
|
p += rslt; |
1915 |
|
|
sz -= rslt; |
1916 |
|
|
rslt = snprintf(p, sz, ";\n"); |
1917 |
|
|
if (rslt == -1 || rslt >= sz) |
1918 |
|
|
return (NULL); |
1919 |
|
|
p += rslt; |
1920 |
|
|
sz -= rslt; |
1921 |
|
|
} |
1922 |
|
|
|
1923 |
|
|
for (i = 0; i < 256; i++) { |
1924 |
|
|
if (i == DHO_DHCP_CLIENT_IDENTIFIER) { |
1925 |
|
|
/* Ignore any CLIENT_IDENTIFIER from server. */ |
1926 |
|
|
opt = &config->send_options[i]; |
1927 |
|
|
} else if (lease->options[i].len) |
1928 |
|
|
opt = &lease->options[i]; |
1929 |
|
|
else |
1930 |
|
|
continue; |
1931 |
|
|
|
1932 |
|
|
rslt = snprintf(p, sz, " option %s %s;\n", |
1933 |
|
|
dhcp_options[i].name, pretty_print_option(i, opt, 1)); |
1934 |
|
|
if (rslt == -1 || rslt >= sz) |
1935 |
|
|
return (NULL); |
1936 |
|
|
p += rslt; |
1937 |
|
|
sz -= rslt; |
1938 |
|
|
} |
1939 |
|
|
|
1940 |
|
|
rsltsz = strftime(p, sz, " renew " DB_TIMEFMT ";\n", |
1941 |
|
|
gmtime(&lease->renewal)); |
1942 |
|
|
if (rsltsz == 0) |
1943 |
|
|
return (NULL); |
1944 |
|
|
p += rsltsz; |
1945 |
|
|
sz -= rsltsz; |
1946 |
|
|
rsltsz = strftime(p, sz, " rebind " DB_TIMEFMT ";\n", |
1947 |
|
|
gmtime(&lease->rebind)); |
1948 |
|
|
if (rsltsz == 0) |
1949 |
|
|
return (NULL); |
1950 |
|
|
p += rsltsz; |
1951 |
|
|
sz -= rsltsz; |
1952 |
|
|
rsltsz = strftime(p, sz, " expire " DB_TIMEFMT ";\n", |
1953 |
|
|
gmtime(&lease->expiry)); |
1954 |
|
|
if (rsltsz == 0) |
1955 |
|
|
return (NULL); |
1956 |
|
|
p += rsltsz; |
1957 |
|
|
sz -= rsltsz; |
1958 |
|
|
rslt = snprintf(p, sz, "}\n"); |
1959 |
|
|
if (rslt == -1 || rslt >= sz) |
1960 |
|
|
return (NULL); |
1961 |
|
|
|
1962 |
|
|
return (leasestr); |
1963 |
|
|
} |
1964 |
|
|
|
1965 |
|
|
void |
1966 |
|
|
go_daemon(void) |
1967 |
|
|
{ |
1968 |
|
|
static int state = 0; |
1969 |
|
|
|
1970 |
|
|
if (no_daemon || state) |
1971 |
|
|
return; |
1972 |
|
|
|
1973 |
|
|
state = 1; |
1974 |
|
|
|
1975 |
|
|
/* Stop logging to stderr. */ |
1976 |
|
|
log_perror = 0; |
1977 |
|
|
|
1978 |
|
|
if (daemon(1, 0) == -1) |
1979 |
|
|
error("daemon"); |
1980 |
|
|
|
1981 |
|
|
/* we are chrooted, daemon(3) fails to open /dev/null */ |
1982 |
|
|
if (nullfd != -1) { |
1983 |
|
|
dup2(nullfd, STDIN_FILENO); |
1984 |
|
|
dup2(nullfd, STDOUT_FILENO); |
1985 |
|
|
dup2(nullfd, STDERR_FILENO); |
1986 |
|
|
close(nullfd); |
1987 |
|
|
nullfd = -1; |
1988 |
|
|
} |
1989 |
|
|
|
1990 |
|
|
/* Catch stuff that might be trying to terminate the program. */ |
1991 |
|
|
signal(SIGHUP, sighdlr); |
1992 |
|
|
signal(SIGINT, sighdlr); |
1993 |
|
|
signal(SIGTERM, sighdlr); |
1994 |
|
|
signal(SIGUSR1, sighdlr); |
1995 |
|
|
signal(SIGUSR2, sighdlr); |
1996 |
|
|
|
1997 |
|
|
signal(SIGPIPE, SIG_IGN); |
1998 |
|
|
} |
1999 |
|
|
|
2000 |
|
|
int |
2001 |
|
|
res_hnok(const char *name) |
2002 |
|
|
{ |
2003 |
|
|
const char *dn = name; |
2004 |
|
|
int pch = '.', ch = (unsigned char)*dn++; |
2005 |
|
|
int warn = 0; |
2006 |
|
|
|
2007 |
|
|
while (ch != '\0') { |
2008 |
|
|
int nch = (unsigned char)*dn++; |
2009 |
|
|
|
2010 |
|
|
if (ch == '.') { |
2011 |
|
|
; |
2012 |
|
|
} else if (pch == '.' || nch == '.' || nch == '\0') { |
2013 |
|
|
if (!isalnum(ch)) |
2014 |
|
|
return (0); |
2015 |
|
|
} else if (!isalnum(ch) && ch != '-' && ch != '_') |
2016 |
|
|
return (0); |
2017 |
|
|
else if (ch == '_' && warn == 0) { |
2018 |
|
|
warning("warning: hostname %s contains an " |
2019 |
|
|
"underscore which violates RFC 952", name); |
2020 |
|
|
warn++; |
2021 |
|
|
} |
2022 |
|
|
pch = ch, ch = nch; |
2023 |
|
|
} |
2024 |
|
|
return (1); |
2025 |
|
|
} |
2026 |
|
|
|
2027 |
|
|
/* |
2028 |
|
|
* resolv_conf(5) says a max of DHCP_DOMAIN_SEARCH_CNT domains and total |
2029 |
|
|
* length of DHCP_DOMAIN_SEARCH_LEN bytes are acceptable for the 'search' |
2030 |
|
|
* statement. |
2031 |
|
|
*/ |
2032 |
|
|
int |
2033 |
|
|
res_hnok_list(const char *names) |
2034 |
|
|
{ |
2035 |
|
|
char *dupnames, *hn, *inputstring; |
2036 |
|
|
int count; |
2037 |
|
|
|
2038 |
|
|
if (strlen(names) >= DHCP_DOMAIN_SEARCH_LEN) |
2039 |
|
|
return (0); |
2040 |
|
|
|
2041 |
|
|
dupnames = inputstring = strdup(names); |
2042 |
|
|
if (inputstring == NULL) |
2043 |
|
|
error("Cannot copy domain name list"); |
2044 |
|
|
|
2045 |
|
|
count = 0; |
2046 |
|
|
while ((hn = strsep(&inputstring, " \t")) != NULL) { |
2047 |
|
|
if (strlen(hn) == 0) |
2048 |
|
|
continue; |
2049 |
|
|
if (res_hnok(hn) == 0) |
2050 |
|
|
break; |
2051 |
|
|
count++; |
2052 |
|
|
if (count > DHCP_DOMAIN_SEARCH_CNT) |
2053 |
|
|
break; |
2054 |
|
|
} |
2055 |
|
|
|
2056 |
|
|
free(dupnames); |
2057 |
|
|
|
2058 |
|
|
return (count > 0 && count < 7 && hn == NULL); |
2059 |
|
|
} |
2060 |
|
|
|
2061 |
|
|
void |
2062 |
|
|
fork_privchld(int fd, int fd2) |
2063 |
|
|
{ |
2064 |
|
|
struct imsg_hup imsg; |
2065 |
|
|
struct pollfd pfd[1]; |
2066 |
|
|
struct imsgbuf *priv_ibuf; |
2067 |
|
|
ssize_t n; |
2068 |
|
|
int nfds, rslt; |
2069 |
|
|
|
2070 |
|
|
switch (fork()) { |
2071 |
|
|
case -1: |
2072 |
|
|
error("cannot fork"); |
2073 |
|
|
break; |
2074 |
|
|
case 0: |
2075 |
|
|
break; |
2076 |
|
|
default: |
2077 |
|
|
return; |
2078 |
|
|
} |
2079 |
|
|
|
2080 |
|
|
if (chdir("/") == -1) |
2081 |
|
|
error("chdir(\"/\")"); |
2082 |
|
|
|
2083 |
|
|
setproctitle("%s [priv]", ifi->name); |
2084 |
|
|
|
2085 |
|
|
go_daemon(); |
2086 |
|
|
|
2087 |
|
|
close(fd2); |
2088 |
|
|
|
2089 |
|
|
if ((priv_ibuf = malloc(sizeof(struct imsgbuf))) == NULL) |
2090 |
|
|
error("no memory for priv_ibuf"); |
2091 |
|
|
|
2092 |
|
|
imsg_init(priv_ibuf, fd); |
2093 |
|
|
|
2094 |
|
|
while (quit == 0) { |
2095 |
|
|
pfd[0].fd = priv_ibuf->fd; |
2096 |
|
|
pfd[0].events = POLLIN; |
2097 |
|
|
if ((nfds = poll(pfd, 1, INFTIM)) == -1) { |
2098 |
|
|
if (errno != EINTR) { |
2099 |
|
|
warning("poll error: %s", strerror(errno)); |
2100 |
|
|
quit = INTERNALSIG; |
2101 |
|
|
} |
2102 |
|
|
continue; |
2103 |
|
|
} |
2104 |
|
|
|
2105 |
|
|
if (nfds == 0 || !(pfd[0].revents & POLLIN)) |
2106 |
|
|
continue; |
2107 |
|
|
|
2108 |
|
|
if ((n = imsg_read(priv_ibuf)) == -1 && errno != EAGAIN) { |
2109 |
|
|
warning("imsg_read(priv_ibuf): %s", strerror(errno)); |
2110 |
|
|
quit = INTERNALSIG; |
2111 |
|
|
continue; |
2112 |
|
|
} |
2113 |
|
|
|
2114 |
|
|
if (n == 0) { |
2115 |
|
|
/* Connection closed -- other end should log message. */ |
2116 |
|
|
quit = INTERNALSIG; |
2117 |
|
|
continue; |
2118 |
|
|
} |
2119 |
|
|
|
2120 |
|
|
dispatch_imsg(priv_ibuf); |
2121 |
|
|
} |
2122 |
|
|
|
2123 |
|
|
imsg_clear(priv_ibuf); |
2124 |
|
|
close(fd); |
2125 |
|
|
|
2126 |
|
|
if (strlen(path_option_db)) { |
2127 |
|
|
/* Truncate the file so monitoring process see exit. */ |
2128 |
|
|
rslt = truncate(path_option_db, 0); |
2129 |
|
|
if (rslt == -1) |
2130 |
|
|
warning("Unable to truncate '%s': %s", path_option_db, |
2131 |
|
|
strerror(errno)); |
2132 |
|
|
} |
2133 |
|
|
|
2134 |
|
|
/* |
2135 |
|
|
* SIGTERM is used by system at shut down. Be nice and don't cleanup |
2136 |
|
|
* routes, possibly preventing NFS from properly shutting down. |
2137 |
|
|
*/ |
2138 |
|
|
if (quit != SIGTERM) { |
2139 |
|
|
memset(&imsg, 0, sizeof(imsg)); |
2140 |
|
|
imsg.addr = active_addr; |
2141 |
|
|
priv_cleanup(&imsg); |
2142 |
|
|
} |
2143 |
|
|
|
2144 |
|
|
if (quit == SIGHUP) { |
2145 |
|
|
if (!(ifi->flags & IFI_HUP) && |
2146 |
|
|
(!(ifi->flags & IFI_NEW_LLADDR))) |
2147 |
|
|
warning("%s; restarting.", strsignal(quit)); |
2148 |
|
|
signal(SIGHUP, SIG_IGN); /* will be restored after exec */ |
2149 |
|
|
execvp(saved_argv[0], saved_argv); |
2150 |
|
|
error("RESTART FAILED: '%s': %s", saved_argv[0], |
2151 |
|
|
strerror(errno)); |
2152 |
|
|
} |
2153 |
|
|
|
2154 |
|
|
if (quit != INTERNALSIG) |
2155 |
|
|
warning("%s; exiting", strsignal(quit)); |
2156 |
|
|
|
2157 |
|
|
exit(1); |
2158 |
|
|
} |
2159 |
|
|
|
2160 |
|
|
void |
2161 |
|
|
get_ifname(char *arg) |
2162 |
|
|
{ |
2163 |
|
|
struct ifgroupreq ifgr; |
2164 |
|
|
struct ifg_req *ifg; |
2165 |
|
|
int s, len; |
2166 |
|
|
|
2167 |
|
|
if (strcmp(arg, "egress") == 0) { |
2168 |
|
|
s = socket(AF_INET, SOCK_DGRAM, 0); |
2169 |
|
|
if (s == -1) |
2170 |
|
|
error("socket error"); |
2171 |
|
|
memset(&ifgr, 0, sizeof(ifgr)); |
2172 |
|
|
strlcpy(ifgr.ifgr_name, "egress", sizeof(ifgr.ifgr_name)); |
2173 |
|
|
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { |
2174 |
|
|
if (errno == ENOENT) |
2175 |
|
|
error("no interface in group egress found"); |
2176 |
|
|
error("ioctl SIOCGIFGMEMB: %s", strerror(errno)); |
2177 |
|
|
} |
2178 |
|
|
len = ifgr.ifgr_len; |
2179 |
|
|
if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) |
2180 |
|
|
error("get_ifname"); |
2181 |
|
|
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) |
2182 |
|
|
error("ioctl SIOCGIFGMEMB: %s", strerror(errno)); |
2183 |
|
|
|
2184 |
|
|
arg = NULL; |
2185 |
|
|
for (ifg = ifgr.ifgr_groups; |
2186 |
|
|
ifg && len >= sizeof(struct ifg_req); ifg++) { |
2187 |
|
|
len -= sizeof(struct ifg_req); |
2188 |
|
|
if (arg) |
2189 |
|
|
error("too many interfaces in group egress"); |
2190 |
|
|
arg = ifg->ifgrq_member; |
2191 |
|
|
} |
2192 |
|
|
|
2193 |
|
|
if (strlcpy(ifi->name, arg, IFNAMSIZ) >= IFNAMSIZ) |
2194 |
|
|
error("Interface name too long: %s", strerror(errno)); |
2195 |
|
|
|
2196 |
|
|
free(ifgr.ifgr_groups); |
2197 |
|
|
close(s); |
2198 |
|
|
} else if (strlcpy(ifi->name, arg, IFNAMSIZ) >= IFNAMSIZ) |
2199 |
|
|
error("Interface name too long"); |
2200 |
|
|
} |
2201 |
|
|
|
2202 |
|
|
/* |
2203 |
|
|
* Update resolv.conf. |
2204 |
|
|
*/ |
2205 |
|
|
char * |
2206 |
|
|
resolv_conf_contents(struct option_data *domainname, |
2207 |
|
|
struct option_data *nameservers, struct option_data *domainsearch) |
2208 |
|
|
{ |
2209 |
|
|
char *dn, *ns, *nss[MAXNS], *contents, *courtesy, *p, *buf; |
2210 |
|
|
size_t len; |
2211 |
|
|
int i, rslt, sz; |
2212 |
|
|
|
2213 |
|
|
memset(nss, 0, sizeof(nss)); |
2214 |
|
|
|
2215 |
|
|
if (domainsearch->len) { |
2216 |
|
|
buf = calloc(1, DHCP_DOMAIN_SEARCH_LEN); |
2217 |
|
|
if (buf == NULL) |
2218 |
|
|
error("No memory to decode domain search"); |
2219 |
|
|
sz = pretty_print_domain_search(buf, DHCP_DOMAIN_SEARCH_LEN, |
2220 |
|
|
domainsearch->data, domainsearch->len); |
2221 |
|
|
if (sz == -1) |
2222 |
|
|
dn = strdup(""); |
2223 |
|
|
else { |
2224 |
|
|
rslt = asprintf(&dn, "search %s\n", buf); |
2225 |
|
|
if (rslt == -1) |
2226 |
|
|
dn = NULL; |
2227 |
|
|
} |
2228 |
|
|
free(buf); |
2229 |
|
|
} else if (domainname->len) { |
2230 |
|
|
rslt = asprintf(&dn, "search %s\n", |
2231 |
|
|
pretty_print_option(DHO_DOMAIN_NAME, domainname, 0)); |
2232 |
|
|
if (rslt == -1) |
2233 |
|
|
dn = NULL; |
2234 |
|
|
} else |
2235 |
|
|
dn = strdup(""); |
2236 |
|
|
if (dn == NULL) |
2237 |
|
|
error("no memory for domainname"); |
2238 |
|
|
|
2239 |
|
|
if (nameservers->len) { |
2240 |
|
|
ns = pretty_print_option(DHO_DOMAIN_NAME_SERVERS, nameservers, |
2241 |
|
|
0); |
2242 |
|
|
for (i = 0; i < MAXNS; i++) { |
2243 |
|
|
p = strsep(&ns, " "); |
2244 |
|
|
if (p == NULL) |
2245 |
|
|
break; |
2246 |
|
|
if (*p == '\0') |
2247 |
|
|
continue; |
2248 |
|
|
rslt = asprintf(&nss[i], "nameserver %s\n", p); |
2249 |
|
|
if (rslt == -1) |
2250 |
|
|
error("no memory for nameserver"); |
2251 |
|
|
} |
2252 |
|
|
} |
2253 |
|
|
|
2254 |
|
|
len = strlen(dn); |
2255 |
|
|
for (i = 0; i < MAXNS; i++) |
2256 |
|
|
if (nss[i]) |
2257 |
|
|
len += strlen(nss[i]); |
2258 |
|
|
|
2259 |
|
|
if (len > 0 && config->resolv_tail) |
2260 |
|
|
len += strlen(config->resolv_tail); |
2261 |
|
|
|
2262 |
|
|
if (len == 0) { |
2263 |
|
|
free(dn); |
2264 |
|
|
return (NULL); |
2265 |
|
|
} |
2266 |
|
|
|
2267 |
|
|
rslt = asprintf(&courtesy, "# Generated by %s dhclient\n", ifi->name); |
2268 |
|
|
if (rslt == -1) |
2269 |
|
|
error("no memory for courtesy line"); |
2270 |
|
|
len += strlen(courtesy); |
2271 |
|
|
|
2272 |
|
|
len++; /* Need room for terminating NUL. */ |
2273 |
|
|
contents = calloc(1, len); |
2274 |
|
|
if (contents == NULL) |
2275 |
|
|
error("no memory for resolv.conf contents"); |
2276 |
|
|
|
2277 |
|
|
strlcat(contents, courtesy, len); |
2278 |
|
|
free(courtesy); |
2279 |
|
|
|
2280 |
|
|
strlcat(contents, dn, len); |
2281 |
|
|
free(dn); |
2282 |
|
|
|
2283 |
|
|
for (i = 0; i < MAXNS; i++) { |
2284 |
|
|
if (nss[i]) { |
2285 |
|
|
strlcat(contents, nss[i], len); |
2286 |
|
|
free(nss[i]); |
2287 |
|
|
} |
2288 |
|
|
} |
2289 |
|
|
|
2290 |
|
|
if (config->resolv_tail) |
2291 |
|
|
strlcat(contents, config->resolv_tail, len); |
2292 |
|
|
|
2293 |
|
|
return (contents); |
2294 |
|
|
} |
2295 |
|
|
|
2296 |
|
|
struct client_lease * |
2297 |
|
|
apply_defaults(struct client_lease *lease) |
2298 |
|
|
{ |
2299 |
|
|
struct client_lease *newlease; |
2300 |
|
|
int i, j; |
2301 |
|
|
|
2302 |
|
|
newlease = clone_lease(lease); |
2303 |
|
|
if (newlease == NULL) |
2304 |
|
|
error("Unable to clone lease"); |
2305 |
|
|
|
2306 |
|
|
if (config->filename) { |
2307 |
|
|
free(newlease->filename); |
2308 |
|
|
newlease->filename = strdup(config->filename); |
2309 |
|
|
} |
2310 |
|
|
if (config->server_name) { |
2311 |
|
|
free(newlease->server_name); |
2312 |
|
|
newlease->server_name = strdup(config->server_name); |
2313 |
|
|
} |
2314 |
|
|
if (config->address.s_addr != INADDR_ANY) |
2315 |
|
|
newlease->address.s_addr = config->address.s_addr; |
2316 |
|
|
if (config->next_server.s_addr != INADDR_ANY) |
2317 |
|
|
newlease->next_server.s_addr = config->next_server.s_addr; |
2318 |
|
|
|
2319 |
|
|
for (i = 0; i < 256; i++) { |
2320 |
|
|
for (j = 0; j < config->ignored_option_count; j++) { |
2321 |
|
|
if (config->ignored_options[j] == i) { |
2322 |
|
|
free(newlease->options[i].data); |
2323 |
|
|
newlease->options[i].data = NULL; |
2324 |
|
|
newlease->options[i].len = 0; |
2325 |
|
|
break; |
2326 |
|
|
} |
2327 |
|
|
} |
2328 |
|
|
if (j < config->ignored_option_count) |
2329 |
|
|
continue; |
2330 |
|
|
|
2331 |
|
|
switch (config->default_actions[i]) { |
2332 |
|
|
case ACTION_SUPERSEDE: |
2333 |
|
|
free(newlease->options[i].data); |
2334 |
|
|
newlease->options[i].len = config->defaults[i].len; |
2335 |
|
|
newlease->options[i].data = calloc(1, |
2336 |
|
|
config->defaults[i].len); |
2337 |
|
|
if (newlease->options[i].data == NULL) |
2338 |
|
|
goto cleanup; |
2339 |
|
|
memcpy(newlease->options[i].data, |
2340 |
|
|
config->defaults[i].data, config->defaults[i].len); |
2341 |
|
|
break; |
2342 |
|
|
|
2343 |
|
|
case ACTION_PREPEND: |
2344 |
|
|
free(newlease->options[i].data); |
2345 |
|
|
newlease->options[i].len = config->defaults[i].len + |
2346 |
|
|
lease->options[i].len; |
2347 |
|
|
newlease->options[i].data = calloc(1, |
2348 |
|
|
newlease->options[i].len); |
2349 |
|
|
if (newlease->options[i].data == NULL) |
2350 |
|
|
goto cleanup; |
2351 |
|
|
memcpy(newlease->options[i].data, |
2352 |
|
|
config->defaults[i].data, config->defaults[i].len); |
2353 |
|
|
memcpy(newlease->options[i].data + |
2354 |
|
|
config->defaults[i].len, lease->options[i].data, |
2355 |
|
|
lease->options[i].len); |
2356 |
|
|
break; |
2357 |
|
|
|
2358 |
|
|
case ACTION_APPEND: |
2359 |
|
|
free(newlease->options[i].data); |
2360 |
|
|
newlease->options[i].len = config->defaults[i].len + |
2361 |
|
|
lease->options[i].len; |
2362 |
|
|
newlease->options[i].data = calloc(1, |
2363 |
|
|
newlease->options[i].len); |
2364 |
|
|
if (newlease->options[i].data == NULL) |
2365 |
|
|
goto cleanup; |
2366 |
|
|
memcpy(newlease->options[i].data, |
2367 |
|
|
lease->options[i].data, lease->options[i].len); |
2368 |
|
|
memcpy(newlease->options[i].data + |
2369 |
|
|
lease->options[i].len, config->defaults[i].data, |
2370 |
|
|
config->defaults[i].len); |
2371 |
|
|
break; |
2372 |
|
|
|
2373 |
|
|
case ACTION_DEFAULT: |
2374 |
|
|
if ((newlease->options[i].len == 0) && |
2375 |
|
|
(config->defaults[i].len != 0)) { |
2376 |
|
|
newlease->options[i].len = |
2377 |
|
|
config->defaults[i].len; |
2378 |
|
|
newlease->options[i].data = calloc(1, |
2379 |
|
|
config->defaults[i].len); |
2380 |
|
|
if (newlease->options[i].data == NULL) |
2381 |
|
|
goto cleanup; |
2382 |
|
|
memcpy(newlease->options[i].data, |
2383 |
|
|
config->defaults[i].data, |
2384 |
|
|
config->defaults[i].len); |
2385 |
|
|
} |
2386 |
|
|
break; |
2387 |
|
|
|
2388 |
|
|
default: |
2389 |
|
|
break; |
2390 |
|
|
} |
2391 |
|
|
} |
2392 |
|
|
|
2393 |
|
|
return (newlease); |
2394 |
|
|
|
2395 |
|
|
cleanup: |
2396 |
|
|
if (newlease) { |
2397 |
|
|
newlease->is_static = 0; |
2398 |
|
|
free_client_lease(newlease); |
2399 |
|
|
} |
2400 |
|
|
|
2401 |
|
|
error("Unable to apply defaults"); |
2402 |
|
|
/* NOTREACHED */ |
2403 |
|
|
|
2404 |
|
|
return (NULL); |
2405 |
|
|
} |
2406 |
|
|
|
2407 |
|
|
struct client_lease * |
2408 |
|
|
clone_lease(struct client_lease *oldlease) |
2409 |
|
|
{ |
2410 |
|
|
struct client_lease *newlease; |
2411 |
|
|
int i; |
2412 |
|
|
|
2413 |
|
|
newlease = calloc(1, sizeof(struct client_lease)); |
2414 |
|
|
if (newlease == NULL) |
2415 |
|
|
goto cleanup; |
2416 |
|
|
|
2417 |
|
|
newlease->expiry = oldlease->expiry; |
2418 |
|
|
newlease->renewal = oldlease->renewal; |
2419 |
|
|
newlease->rebind = oldlease->rebind; |
2420 |
|
|
newlease->is_static = oldlease->is_static; |
2421 |
|
|
newlease->is_bootp = oldlease->is_bootp; |
2422 |
|
|
newlease->address = oldlease->address; |
2423 |
|
|
newlease->next_server = oldlease->next_server; |
2424 |
|
|
|
2425 |
|
|
if (oldlease->server_name) { |
2426 |
|
|
newlease->server_name = strdup(oldlease->server_name); |
2427 |
|
|
if (newlease->server_name == NULL) |
2428 |
|
|
goto cleanup; |
2429 |
|
|
} |
2430 |
|
|
if (oldlease->filename) { |
2431 |
|
|
newlease->filename = strdup(oldlease->filename); |
2432 |
|
|
if (newlease->filename == NULL) |
2433 |
|
|
goto cleanup; |
2434 |
|
|
} |
2435 |
|
|
if (oldlease->resolv_conf) { |
2436 |
|
|
newlease->resolv_conf = strdup(oldlease->resolv_conf); |
2437 |
|
|
if (newlease->resolv_conf == NULL) |
2438 |
|
|
goto cleanup; |
2439 |
|
|
} |
2440 |
|
|
|
2441 |
|
|
for (i = 0; i < 256; i++) { |
2442 |
|
|
if (oldlease->options[i].len == 0) |
2443 |
|
|
continue; |
2444 |
|
|
newlease->options[i].len = oldlease->options[i].len; |
2445 |
|
|
newlease->options[i].data = calloc(1, newlease->options[i].len); |
2446 |
|
|
if (newlease->options[i].data == NULL) |
2447 |
|
|
goto cleanup; |
2448 |
|
|
memcpy(newlease->options[i].data, oldlease->options[i].data, |
2449 |
|
|
newlease->options[i].len); |
2450 |
|
|
} |
2451 |
|
|
|
2452 |
|
|
return (newlease); |
2453 |
|
|
|
2454 |
|
|
cleanup: |
2455 |
|
|
if (newlease) { |
2456 |
|
|
newlease->is_static = 0; |
2457 |
|
|
free_client_lease(newlease); |
2458 |
|
|
} |
2459 |
|
|
|
2460 |
|
|
return (NULL); |
2461 |
|
|
} |
2462 |
|
|
|
2463 |
|
|
/* |
2464 |
|
|
* Apply the list of options to be ignored that was provided on the |
2465 |
|
|
* command line. This will override any ignore list obtained from |
2466 |
|
|
* dhclient.conf. |
2467 |
|
|
*/ |
2468 |
|
|
void |
2469 |
|
|
apply_ignore_list(char *ignore_list) |
2470 |
|
|
{ |
2471 |
|
|
u_int8_t list[256]; |
2472 |
|
|
char *p; |
2473 |
|
|
int ix, i, j; |
2474 |
|
|
|
2475 |
|
|
memset(list, 0, sizeof(list)); |
2476 |
|
|
ix = 0; |
2477 |
|
|
|
2478 |
|
|
for (p = strsep(&ignore_list, ", "); p != NULL; |
2479 |
|
|
p = strsep(&ignore_list, ", ")) { |
2480 |
|
|
if (*p == '\0') |
2481 |
|
|
continue; |
2482 |
|
|
|
2483 |
|
|
for (i = 1; i < DHO_END; i++) |
2484 |
|
|
if (!strcasecmp(dhcp_options[i].name, p)) |
2485 |
|
|
break; |
2486 |
|
|
|
2487 |
|
|
if (i == DHO_END) { |
2488 |
|
|
note("Invalid option name: '%s'", p); |
2489 |
|
|
return; |
2490 |
|
|
} |
2491 |
|
|
|
2492 |
|
|
/* Avoid storing duplicate options in the list. */ |
2493 |
|
|
for (j = 0; j < ix && list[j] != i; j++) |
2494 |
|
|
; |
2495 |
|
|
if (j == ix) |
2496 |
|
|
list[ix++] = i; |
2497 |
|
|
} |
2498 |
|
|
|
2499 |
|
|
config->ignored_option_count = ix; |
2500 |
|
|
memcpy(config->ignored_options, list, sizeof(config->ignored_options)); |
2501 |
|
|
} |
2502 |
|
|
|
2503 |
|
|
void |
2504 |
|
|
write_resolv_conf(u_int8_t *contents, size_t sz) |
2505 |
|
|
{ |
2506 |
|
|
int rslt; |
2507 |
|
|
|
2508 |
|
|
rslt = imsg_compose(unpriv_ibuf, IMSG_WRITE_RESOLV_CONF, |
2509 |
|
|
0, 0, -1, contents, sz); |
2510 |
|
|
if (rslt == -1) |
2511 |
|
|
warning("write_resolv_conf: imsg_compose: %s", strerror(errno)); |
2512 |
|
|
|
2513 |
|
|
flush_unpriv_ibuf("write_resolv_conf"); |
2514 |
|
|
} |
2515 |
|
|
|
2516 |
|
|
void |
2517 |
|
|
write_option_db(u_int8_t *contents, size_t sz) |
2518 |
|
|
{ |
2519 |
|
|
int rslt; |
2520 |
|
|
|
2521 |
|
|
rslt = imsg_compose(unpriv_ibuf, IMSG_WRITE_OPTION_DB, |
2522 |
|
|
0, 0, -1, contents, sz); |
2523 |
|
|
if (rslt == -1) |
2524 |
|
|
warning("write_option_db: imsg_compose: %s", strerror(errno)); |
2525 |
|
|
|
2526 |
|
|
flush_unpriv_ibuf("write_option_db"); |
2527 |
|
|
} |
2528 |
|
|
|
2529 |
|
|
void |
2530 |
|
|
priv_write_resolv_conf(struct imsg *imsg) |
2531 |
|
|
{ |
2532 |
|
|
u_int8_t *contents; |
2533 |
|
|
size_t sz; |
2534 |
|
|
|
2535 |
|
|
if (imsg->hdr.len < IMSG_HEADER_SIZE) { |
2536 |
|
|
warning("short IMSG_WRITE_RESOLV_CONF"); |
2537 |
|
|
return; |
2538 |
|
|
} |
2539 |
|
|
|
2540 |
|
|
if (!resolv_conf_priority()) |
2541 |
|
|
return; |
2542 |
|
|
|
2543 |
|
|
contents = imsg->data; |
2544 |
|
|
sz = imsg->hdr.len - IMSG_HEADER_SIZE; |
2545 |
|
|
|
2546 |
|
|
priv_write_file("/etc/resolv.conf", |
2547 |
|
|
O_WRONLY | O_CREAT | O_TRUNC, |
2548 |
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, contents, sz); |
2549 |
|
|
} |
2550 |
|
|
|
2551 |
|
|
void |
2552 |
|
|
priv_write_option_db(struct imsg *imsg) |
2553 |
|
|
{ |
2554 |
|
|
u_int8_t *contents; |
2555 |
|
|
size_t sz; |
2556 |
|
|
|
2557 |
|
|
if (imsg->hdr.len < IMSG_HEADER_SIZE) { |
2558 |
|
|
warning("short IMSG_WRITE_OPTION_DB"); |
2559 |
|
|
return; |
2560 |
|
|
} |
2561 |
|
|
|
2562 |
|
|
contents = imsg->data; |
2563 |
|
|
sz = imsg->hdr.len - IMSG_HEADER_SIZE; |
2564 |
|
|
|
2565 |
|
|
priv_write_file(path_option_db, |
2566 |
|
|
O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, |
2567 |
|
|
S_IRUSR | S_IWUSR | S_IRGRP, contents, sz); |
2568 |
|
|
} |
2569 |
|
|
|
2570 |
|
|
void |
2571 |
|
|
priv_write_file(char *path, int flags, mode_t mode, |
2572 |
|
|
u_int8_t *contents, size_t sz) |
2573 |
|
|
{ |
2574 |
|
|
ssize_t n; |
2575 |
|
|
int fd; |
2576 |
|
|
|
2577 |
|
|
fd = open(path, flags, mode); |
2578 |
|
|
if (fd == -1) { |
2579 |
|
|
note("Couldn't open '%s': %s", path, strerror(errno)); |
2580 |
|
|
return; |
2581 |
|
|
} |
2582 |
|
|
|
2583 |
|
|
n = write(fd, contents, sz); |
2584 |
|
|
if (n == -1) |
2585 |
|
|
note("Couldn't write contents to '%s': %s", path, |
2586 |
|
|
strerror(errno)); |
2587 |
|
|
else if (n < sz) |
2588 |
|
|
note("Short contents write to '%s' (%zd vs %zu)", path, |
2589 |
|
|
n, sz); |
2590 |
|
|
|
2591 |
|
|
if (fchown(fd, 0, 0) == -1) |
2592 |
|
|
note("fchown(fd, %d, %d) of '%s' failed (%s)", |
2593 |
|
|
0, 0, path, strerror(errno)); |
2594 |
|
|
if (fchmod(fd, mode) == -1) |
2595 |
|
|
note("fchmod(fd, 0x%x) of '%s' failed (%s)", mode, |
2596 |
|
|
path, strerror(errno)); |
2597 |
|
|
|
2598 |
|
|
close(fd); |
2599 |
|
|
} |
2600 |
|
|
|
2601 |
|
|
/* |
2602 |
|
|
* add_direct_route is the equivalent of |
2603 |
|
|
* |
2604 |
|
|
* route add -net $dest -netmask $mask -cloning -iface $iface |
2605 |
|
|
*/ |
2606 |
|
|
void |
2607 |
|
|
add_direct_route(struct in_addr dest, struct in_addr mask, struct in_addr iface) |
2608 |
|
|
{ |
2609 |
|
|
struct in_addr ifa = { INADDR_ANY }; |
2610 |
|
|
|
2611 |
|
|
add_route(dest, mask, iface, ifa, |
2612 |
|
|
RTA_DST | RTA_NETMASK | RTA_GATEWAY, RTF_CLONING | RTF_STATIC); |
2613 |
|
|
} |
2614 |
|
|
|
2615 |
|
|
/* |
2616 |
|
|
* add_default_route is the equivalent of |
2617 |
|
|
* |
2618 |
|
|
* route -q $rdomain add default -iface $router |
2619 |
|
|
* |
2620 |
|
|
* or |
2621 |
|
|
* |
2622 |
|
|
* route -q $rdomain add default $router |
2623 |
|
|
*/ |
2624 |
|
|
void |
2625 |
|
|
add_default_route(struct in_addr addr, struct in_addr gateway) |
2626 |
|
|
{ |
2627 |
|
|
struct in_addr netmask, dest; |
2628 |
|
|
int addrs, flags; |
2629 |
|
|
|
2630 |
|
|
memset(&netmask, 0, sizeof(netmask)); |
2631 |
|
|
memset(&dest, 0, sizeof(dest)); |
2632 |
|
|
addrs = RTA_DST | RTA_NETMASK; |
2633 |
|
|
flags = 0; |
2634 |
|
|
|
2635 |
|
|
/* |
2636 |
|
|
* When 'addr' and 'gateway' are identical the desired behaviour is |
2637 |
|
|
* to emulate the '-iface' variant of 'route'. This is done by |
2638 |
|
|
* claiming there is no gateway address to use. |
2639 |
|
|
*/ |
2640 |
|
|
if (memcmp(&gateway, &addr, sizeof(addr)) != 0) { |
2641 |
|
|
addrs |= RTA_GATEWAY | RTA_IFA; |
2642 |
|
|
flags |= RTF_GATEWAY | RTF_STATIC; |
2643 |
|
|
} |
2644 |
|
|
|
2645 |
|
|
add_route(dest, netmask, gateway, addr, addrs, flags); |
2646 |
|
|
} |
2647 |
|
|
|
2648 |
|
|
void |
2649 |
|
|
add_static_routes(struct option_data *static_routes, struct in_addr iface) |
2650 |
|
|
{ |
2651 |
|
|
struct in_addr dest, netmask, gateway; |
2652 |
|
|
struct in_addr *addr; |
2653 |
|
|
int i; |
2654 |
|
|
|
2655 |
|
|
netmask.s_addr = INADDR_ANY; /* Not used for CLASSFULL! */ |
2656 |
|
|
|
2657 |
|
|
for (i = 0; (i + 2*sizeof(*addr)) <= static_routes->len; |
2658 |
|
|
i += 2*sizeof(*addr)) { |
2659 |
|
|
addr = (struct in_addr *)&static_routes->data[i]; |
2660 |
|
|
if (addr->s_addr == INADDR_ANY) |
2661 |
|
|
continue; /* RFC 2132 says 0.0.0.0 is not allowed. */ |
2662 |
|
|
|
2663 |
|
|
dest.s_addr = addr->s_addr; |
2664 |
|
|
gateway.s_addr = (addr+1)->s_addr; |
2665 |
|
|
|
2666 |
|
|
/* XXX Order implies priority but we're ignoring that. */ |
2667 |
|
|
add_route(dest, netmask, gateway, iface, |
2668 |
|
|
RTA_DST | RTA_GATEWAY | RTA_IFA, RTF_GATEWAY | RTF_STATIC); |
2669 |
|
|
} |
2670 |
|
|
} |
2671 |
|
|
|
2672 |
|
|
void |
2673 |
|
|
add_classless_static_routes(struct option_data *opt, struct in_addr iface) |
2674 |
|
|
{ |
2675 |
|
|
struct in_addr dest, netmask, gateway; |
2676 |
|
|
int bits, bytes, i; |
2677 |
|
|
|
2678 |
|
|
i = 0; |
2679 |
|
|
while (i < opt->len) { |
2680 |
|
|
bits = opt->data[i++]; |
2681 |
|
|
bytes = (bits + 7) / 8; |
2682 |
|
|
|
2683 |
|
|
if (bytes > sizeof(netmask)) |
2684 |
|
|
return; |
2685 |
|
|
else if (i + bytes > opt->len) |
2686 |
|
|
return; |
2687 |
|
|
|
2688 |
|
|
if (bits) |
2689 |
|
|
netmask.s_addr = htonl(0xffffffff << (32 - bits)); |
2690 |
|
|
else |
2691 |
|
|
netmask.s_addr = INADDR_ANY; |
2692 |
|
|
|
2693 |
|
|
memcpy(&dest, &opt->data[i], bytes); |
2694 |
|
|
dest.s_addr = dest.s_addr & netmask.s_addr; |
2695 |
|
|
i += bytes; |
2696 |
|
|
|
2697 |
|
|
if (i + sizeof(gateway) > opt->len) |
2698 |
|
|
return; |
2699 |
|
|
memcpy(&gateway, &opt->data[i], sizeof(gateway)); |
2700 |
|
|
i += sizeof(gateway); |
2701 |
|
|
|
2702 |
|
|
if (gateway.s_addr == INADDR_ANY) |
2703 |
|
|
add_direct_route(dest, netmask, iface); |
2704 |
|
|
else |
2705 |
|
|
add_route(dest, netmask, gateway, iface, |
2706 |
|
|
RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFA, |
2707 |
|
|
RTF_GATEWAY | RTF_STATIC); |
2708 |
|
|
} |
2709 |
|
|
} |
2710 |
|
|
|
2711 |
|
|
int |
2712 |
|
|
compare_lease(struct client_lease *active, struct client_lease *new) |
2713 |
|
|
{ |
2714 |
|
|
int i; |
2715 |
|
|
|
2716 |
|
|
if (active == new) |
2717 |
|
|
return (0); |
2718 |
|
|
|
2719 |
|
|
if (!new || !active) |
2720 |
|
|
return (1); |
2721 |
|
|
|
2722 |
|
|
if (active->address.s_addr != new->address.s_addr || |
2723 |
|
|
active->is_static != new->is_static || |
2724 |
|
|
active->is_bootp != new->is_bootp) |
2725 |
|
|
return (1); |
2726 |
|
|
|
2727 |
|
|
if (active->server_name != new->server_name) { |
2728 |
|
|
if (!active->server_name || !new->server_name) |
2729 |
|
|
return (1); |
2730 |
|
|
if (strcmp(active->server_name, new->server_name) != 0) |
2731 |
|
|
return (1); |
2732 |
|
|
} |
2733 |
|
|
|
2734 |
|
|
if (active->filename != new->filename) { |
2735 |
|
|
if (!active->filename || !new->filename) |
2736 |
|
|
return (1); |
2737 |
|
|
if (strcmp(active->filename, new->filename) != 0) |
2738 |
|
|
return (1); |
2739 |
|
|
} |
2740 |
|
|
|
2741 |
|
|
for (i = 0; i < 256; i++) { |
2742 |
|
|
if (active->options[i].len != new->options[i].len) |
2743 |
|
|
return (1); |
2744 |
|
|
if (memcmp(active->options[i].data, new->options[i].data, |
2745 |
|
|
active->options[i].len)) |
2746 |
|
|
return (1); |
2747 |
|
|
} |
2748 |
|
|
|
2749 |
|
|
return (0); |
2750 |
|
|
} |
2751 |
|
|
|
2752 |
|
|
void |
2753 |
|
|
set_lease_times(struct client_lease *lease) |
2754 |
|
|
{ |
2755 |
|
|
time_t cur_time, time_max; |
2756 |
|
|
u_int32_t uint32val; |
2757 |
|
|
|
2758 |
|
|
time(&cur_time); |
2759 |
|
|
|
2760 |
|
|
time_max = LLONG_MAX - cur_time; |
2761 |
|
|
if (time_max > UINT32_MAX) |
2762 |
|
|
time_max = UINT32_MAX; |
2763 |
|
|
|
2764 |
|
|
/* |
2765 |
|
|
* Take the server-provided times if available. Otherwise |
2766 |
|
|
* figure them out according to the spec. |
2767 |
|
|
* |
2768 |
|
|
* expiry == time to discard lease. |
2769 |
|
|
* renewal == time to renew lease from server that provided it. |
2770 |
|
|
* rebind == time to renew lease from any server. |
2771 |
|
|
* |
2772 |
|
|
* 0 <= renewal <= rebind <= expiry <= time_max |
2773 |
|
|
* && |
2774 |
|
|
* expiry >= MIN(time_max, 60) |
2775 |
|
|
*/ |
2776 |
|
|
|
2777 |
|
|
lease->expiry = 43200; /* Default to 12 hours */ |
2778 |
|
|
if (lease->options[DHO_DHCP_LEASE_TIME].len == sizeof(uint32val)) { |
2779 |
|
|
memcpy(&uint32val, lease->options[DHO_DHCP_LEASE_TIME].data, |
2780 |
|
|
sizeof(uint32val)); |
2781 |
|
|
lease->expiry = ntohl(uint32val); |
2782 |
|
|
if (lease->expiry < 60) |
2783 |
|
|
lease->expiry = 60; |
2784 |
|
|
} |
2785 |
|
|
if (lease->expiry > time_max) |
2786 |
|
|
lease->expiry = time_max; |
2787 |
|
|
|
2788 |
|
|
lease->renewal = lease->expiry / 2; |
2789 |
|
|
if (lease->options[DHO_DHCP_RENEWAL_TIME].len == sizeof(uint32val)) { |
2790 |
|
|
memcpy(&uint32val, lease->options[DHO_DHCP_RENEWAL_TIME].data, |
2791 |
|
|
sizeof(uint32val)); |
2792 |
|
|
lease->renewal = ntohl(uint32val); |
2793 |
|
|
if (lease->renewal > lease->expiry) |
2794 |
|
|
lease->renewal = lease->expiry; |
2795 |
|
|
} |
2796 |
|
|
|
2797 |
|
|
lease->rebind = (lease->expiry * 7) / 8; |
2798 |
|
|
if (lease->options[DHO_DHCP_REBINDING_TIME].len == sizeof(uint32val)) { |
2799 |
|
|
memcpy(&uint32val, lease->options[DHO_DHCP_REBINDING_TIME].data, |
2800 |
|
|
sizeof(uint32val)); |
2801 |
|
|
lease->rebind = ntohl(uint32val); |
2802 |
|
|
if (lease->rebind > lease->expiry) |
2803 |
|
|
lease->rebind = lease->expiry; |
2804 |
|
|
} |
2805 |
|
|
if (lease->rebind < lease->renewal) |
2806 |
|
|
lease->rebind = lease->renewal; |
2807 |
|
|
|
2808 |
|
|
/* Convert lease lengths to times. */ |
2809 |
|
|
lease->expiry += cur_time; |
2810 |
|
|
lease->renewal += cur_time; |
2811 |
|
|
lease->rebind += cur_time; |
2812 |
|
|
} |