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