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