1 /* $OpenBSD: frontend.c,v 1.45 2024/11/21 13:35:20 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
7 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/syslog.h>
26 #include <sys/uio.h>
27
28 #include <net/bpf.h>
29 #include <net/if.h>
30 #include <net/if_dl.h>
31 #include <net/if_types.h>
32 #include <net/route.h>
33
34 #include <netinet/in.h>
35 #include <netinet/if_ether.h>
36 #include <netinet/ip.h>
37 #include <netinet/udp.h>
38
39 #include <arpa/inet.h>
40
41 #include <errno.h>
42 #include <event.h>
43 #include <ifaddrs.h>
44 #include <imsg.h>
45 #include <pwd.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include "bpf.h"
53 #include "log.h"
54 #include "dhcpleased.h"
55 #include "frontend.h"
56 #include "control.h"
57 #include "checksum.h"
58
59 #define ROUTE_SOCKET_BUF_SIZE 16384
60 #define BOOTP_MIN_LEN 300 /* fixed bootp packet adds up to 300 */
61
62 struct bpf_ev {
63 struct event ev;
64 uint8_t buf[BPFLEN];
65 };
66
67 struct iface {
68 LIST_ENTRY(iface) entries;
69 struct bpf_ev bpfev;
70 struct imsg_ifinfo ifinfo;
71 int send_discover;
72 uint32_t xid;
73 struct in_addr ciaddr;
74 struct in_addr requested_ip;
75 struct in_addr server_identifier;
76 struct in_addr dhcp_server;
77 int udpsock;
78 };
79
80 __dead void frontend_shutdown(void);
81 void frontend_sig_handler(int, short, void *);
82 void update_iface(struct if_msghdr *, struct sockaddr_dl *);
83 void frontend_startup(void);
84 void init_ifaces(void);
85 void route_receive(int, short, void *);
86 void handle_route_message(struct rt_msghdr *, struct sockaddr **);
87 void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
88 void bpf_receive(int, short, void *);
89 int get_flags(char *);
90 int get_xflags(char *);
91 struct iface *get_iface_by_id(uint32_t);
92 void remove_iface(uint32_t);
93 void set_bpfsock(int, uint32_t);
94 void iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *);
95 ssize_t build_packet(uint8_t, char *, uint32_t, struct ether_addr *,
96 struct in_addr *, struct in_addr *, struct in_addr *);
97 void send_packet(uint8_t, struct iface *);
98 void bpf_send_packet(struct iface *, uint8_t *, ssize_t);
99 int udp_send_packet(struct iface *, uint8_t *, ssize_t);
100 #ifndef SMALL
101 int iface_conf_cmp(struct iface_conf *, struct iface_conf *);
102 #endif /* SMALL */
103
104 LIST_HEAD(, iface) interfaces;
105 struct dhcpleased_conf *frontend_conf;
106 static struct imsgev *iev_main;
107 static struct imsgev *iev_engine;
108 struct event ev_route;
109 int ioctlsock;
110
111 uint8_t dhcp_packet[1500];
112
113 void
frontend_sig_handler(int sig,short event,void * bula)114 frontend_sig_handler(int sig, short event, void *bula)
115 {
116 /*
117 * Normal signal handler rules don't apply because libevent
118 * decouples for us.
119 */
120
121 switch (sig) {
122 case SIGINT:
123 case SIGTERM:
124 frontend_shutdown();
125 default:
126 fatalx("unexpected signal");
127 }
128 }
129
130 void
frontend(int debug,int verbose)131 frontend(int debug, int verbose)
132 {
133 struct event ev_sigint, ev_sigterm;
134 struct passwd *pw;
135
136 #ifndef SMALL
137 frontend_conf = config_new_empty();
138 #endif /* SMALL */
139
140 log_init(debug, LOG_DAEMON);
141 log_setverbose(verbose);
142
143 if ((pw = getpwnam(DHCPLEASED_USER)) == NULL)
144 fatal("getpwnam");
145
146 if (chdir("/") == -1)
147 fatal("chdir(\"/\")");
148
149 if (unveil("/", "") == -1)
150 fatal("unveil /");
151 if (unveil(NULL, NULL) == -1)
152 fatal("unveil");
153
154 setproctitle("%s", "frontend");
155 log_procinit("frontend");
156
157 if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
158 fatal("socket");
159
160 if (setgroups(1, &pw->pw_gid) ||
161 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
162 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
163 fatal("can't drop privileges");
164
165 if (pledge("stdio unix recvfd route", NULL) == -1)
166 fatal("pledge");
167 event_init();
168
169 /* Setup signal handler. */
170 signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
171 signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
172 signal_add(&ev_sigint, NULL);
173 signal_add(&ev_sigterm, NULL);
174 signal(SIGPIPE, SIG_IGN);
175 signal(SIGHUP, SIG_IGN);
176
177 /* Setup pipe and event handler to the parent process. */
178 if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
179 fatal(NULL);
180 if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
181 fatal(NULL);
182 imsgbuf_allow_fdpass(&iev_main->ibuf);
183 iev_main->handler = frontend_dispatch_main;
184 iev_main->events = EV_READ;
185 event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
186 iev_main->handler, iev_main);
187 event_add(&iev_main->ev, NULL);
188
189 LIST_INIT(&interfaces);
190 event_dispatch();
191
192 frontend_shutdown();
193 }
194
195 __dead void
frontend_shutdown(void)196 frontend_shutdown(void)
197 {
198 /* Close pipes. */
199 imsgbuf_write(&iev_engine->ibuf);
200 imsgbuf_clear(&iev_engine->ibuf);
201 close(iev_engine->ibuf.fd);
202 imsgbuf_write(&iev_main->ibuf);
203 imsgbuf_clear(&iev_main->ibuf);
204 close(iev_main->ibuf.fd);
205
206 #ifndef SMALL
207 config_clear(frontend_conf);
208 #endif /* SMALL */
209
210 free(iev_engine);
211 free(iev_main);
212
213 log_info("frontend exiting");
214 exit(0);
215 }
216
217 int
frontend_imsg_compose_main(int type,pid_t pid,void * data,uint16_t datalen)218 frontend_imsg_compose_main(int type, pid_t pid, void *data,
219 uint16_t datalen)
220 {
221 return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
222 datalen));
223 }
224
225 int
frontend_imsg_compose_engine(int type,uint32_t peerid,pid_t pid,void * data,uint16_t datalen)226 frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
227 void *data, uint16_t datalen)
228 {
229 return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
230 data, datalen));
231 }
232
233 void
frontend_dispatch_main(int fd,short event,void * bula)234 frontend_dispatch_main(int fd, short event, void *bula)
235 {
236 static struct dhcpleased_conf *nconf;
237 static struct iface_conf *iface_conf;
238 struct imsg imsg;
239 struct imsgev *iev = bula;
240 struct imsgbuf *ibuf = &iev->ibuf;
241 struct iface *iface;
242 ssize_t n;
243 uint32_t type;
244 int shut = 0, bpfsock, if_index, udpsock;
245
246 if (event & EV_READ) {
247 if ((n = imsgbuf_read(ibuf)) == -1)
248 fatal("imsgbuf_read error");
249 if (n == 0) /* Connection closed. */
250 shut = 1;
251 }
252 if (event & EV_WRITE) {
253 if (imsgbuf_write(ibuf) == -1) {
254 if (errno == EPIPE) /* Connection closed. */
255 shut = 1;
256 else
257 fatal("imsgbuf_write");
258 }
259 }
260
261 for (;;) {
262 if ((n = imsg_get(ibuf, &imsg)) == -1)
263 fatal("%s: imsg_get error", __func__);
264 if (n == 0) /* No more messages. */
265 break;
266
267 type = imsg_get_type(&imsg);
268
269 switch (type) {
270 case IMSG_SOCKET_IPC:
271 /*
272 * Setup pipe and event handler to the engine
273 * process.
274 */
275 if (iev_engine)
276 fatalx("%s: received unexpected imsg fd "
277 "to frontend", __func__);
278
279 if ((fd = imsg_get_fd(&imsg)) == -1)
280 fatalx("%s: expected to receive imsg fd to "
281 "frontend but didn't receive any",
282 __func__);
283
284 iev_engine = malloc(sizeof(struct imsgev));
285 if (iev_engine == NULL)
286 fatal(NULL);
287
288 if (imsgbuf_init(&iev_engine->ibuf, fd) == -1)
289 fatal(NULL);
290 iev_engine->handler = frontend_dispatch_engine;
291 iev_engine->events = EV_READ;
292
293 event_set(&iev_engine->ev, iev_engine->ibuf.fd,
294 iev_engine->events, iev_engine->handler, iev_engine);
295 event_add(&iev_engine->ev, NULL);
296 break;
297 case IMSG_BPFSOCK:
298 if ((bpfsock = imsg_get_fd(&imsg)) == -1)
299 fatalx("%s: expected to receive imsg "
300 "bpf fd but didn't receive any",
301 __func__);
302 if (imsg_get_data(&imsg, &if_index,
303 sizeof(if_index)) == -1)
304 fatalx("%s: invalid %s", __func__, i2s(type));
305
306 set_bpfsock(bpfsock, if_index);
307 break;
308 case IMSG_UDPSOCK:
309 if ((udpsock = imsg_get_fd(&imsg)) == -1)
310 fatalx("%s: expected to receive imsg "
311 "udpsocket fd but didn't receive any",
312 __func__);
313 if (imsg_get_data(&imsg, &if_index,
314 sizeof(if_index)) == -1)
315 fatalx("%s: invalid %s", __func__, i2s(type));
316
317 if ((iface = get_iface_by_id(if_index)) == NULL) {
318 close(udpsock);
319 break;
320 }
321 if (iface->udpsock != -1)
322 fatalx("%s: received unexpected udpsocket",
323 __func__);
324 iface->udpsock = udpsock;
325 break;
326 case IMSG_CLOSE_UDPSOCK:
327 if (imsg_get_data(&imsg, &if_index,
328 sizeof(if_index)) == -1)
329 fatalx("%s: invalid %s", __func__, i2s(type));
330
331 if ((iface = get_iface_by_id(if_index)) != NULL &&
332 iface->udpsock != -1) {
333 close(iface->udpsock);
334 iface->udpsock = -1;
335 }
336 break;
337 case IMSG_ROUTESOCK:
338 if ((fd = imsg_get_fd(&imsg)) == -1)
339 fatalx("%s: expected to receive imsg "
340 "routesocket fd but didn't receive any",
341 __func__);
342 event_set(&ev_route, fd, EV_READ | EV_PERSIST,
343 route_receive, NULL);
344 break;
345 case IMSG_STARTUP:
346 frontend_startup();
347 break;
348 #ifndef SMALL
349 case IMSG_RECONF_CONF:
350 if (nconf != NULL)
351 fatalx("%s: IMSG_RECONF_CONF already in "
352 "progress", __func__);
353 if ((nconf = malloc(sizeof(struct dhcpleased_conf))) ==
354 NULL)
355 fatal(NULL);
356 SIMPLEQ_INIT(&nconf->iface_list);
357 break;
358 case IMSG_RECONF_IFACE:
359 if ((iface_conf = malloc(sizeof(struct iface_conf)))
360 == NULL)
361 fatal(NULL);
362
363 if (imsg_get_data(&imsg, iface_conf,
364 sizeof(struct iface_conf)) == -1)
365 fatalx("%s: invalid %s", __func__, i2s(type));
366
367 iface_conf->vc_id = NULL;
368 iface_conf->vc_id_len = 0;
369 iface_conf->c_id = NULL;
370 iface_conf->c_id_len = 0;
371 iface_conf->h_name = NULL;
372 SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
373 iface_conf, entry);
374 break;
375 case IMSG_RECONF_VC_ID:
376 if (iface_conf == NULL)
377 fatalx("%s: %s without IMSG_RECONF_IFACE",
378 __func__, i2s(type));
379 if (iface_conf->vc_id != NULL)
380 fatalx("%s: multiple %s for the same interface",
381 __func__, i2s(type));
382 if ((iface_conf->vc_id_len = imsg_get_len(&imsg))
383 > 255 + 2 || iface_conf->vc_id_len == 0)
384 fatalx("%s: invalid %s", __func__, i2s(type));
385 if ((iface_conf->vc_id = malloc(iface_conf->vc_id_len))
386 == NULL)
387 fatal(NULL);
388 if (imsg_get_data(&imsg, iface_conf->vc_id,
389 iface_conf->vc_id_len) == -1)
390 fatalx("%s: invalid %s", __func__, i2s(type));
391 break;
392 case IMSG_RECONF_C_ID:
393 if (iface_conf == NULL)
394 fatalx("%s: %s without IMSG_RECONF_IFACE",
395 __func__, i2s(type));
396 if (iface_conf->c_id != NULL)
397 fatalx("%s: multiple %s for the same interface",
398 __func__, i2s(type));
399 if ((iface_conf->c_id_len = imsg_get_len(&imsg))
400 > 255 + 2 || iface_conf->c_id_len == 0)
401 fatalx("%s: invalid %s", __func__, i2s(type));
402 if ((iface_conf->c_id = malloc(iface_conf->c_id_len))
403 == NULL)
404 fatal(NULL);
405 if (imsg_get_data(&imsg, iface_conf->c_id,
406 iface_conf->c_id_len) == -1)
407 fatalx("%s: invalid %s", __func__, i2s(type));
408 break;
409 case IMSG_RECONF_H_NAME: {
410 size_t len;
411
412 if (iface_conf == NULL)
413 fatalx("%s: %s without IMSG_RECONF_IFACE",
414 __func__, i2s(type));
415 if (iface_conf->h_name != NULL)
416 fatalx("%s: multiple %s for the same interface",
417 __func__, i2s(type));
418 if ((len = imsg_get_len(&imsg)) > 256 || len == 0)
419 fatalx("%s: invalid %s", __func__, i2s(type));
420 if ((iface_conf->h_name = malloc(len)) == NULL)
421 fatal(NULL);
422 if (imsg_get_data(&imsg, iface_conf->h_name, len) == -1)
423 fatalx("%s: invalid %s", __func__, i2s(type));
424 if (iface_conf->h_name[len - 1] != '\0')
425 fatalx("Invalid hostname");
426 break;
427 }
428 case IMSG_RECONF_END: {
429 int i;
430 int *ifaces;
431 char ifnamebuf[IF_NAMESIZE], *if_name;
432
433 if (nconf == NULL)
434 fatalx("%s: %s without IMSG_RECONF_CONF",
435 __func__, i2s(type));
436
437 ifaces = changed_ifaces(frontend_conf, nconf);
438 merge_config(frontend_conf, nconf);
439 nconf = NULL;
440 for (i = 0; ifaces[i] != 0; i++) {
441 if_index = ifaces[i];
442 if_name = if_indextoname(if_index, ifnamebuf);
443 log_debug("changed iface: %s[%d]", if_name !=
444 NULL ? if_name : "<unknown>", if_index);
445 frontend_imsg_compose_engine(
446 IMSG_REQUEST_REBOOT, 0, 0, &if_index,
447 sizeof(if_index));
448 }
449 free(ifaces);
450 break;
451 }
452 case IMSG_CONTROLFD:
453 if ((fd = imsg_get_fd(&imsg)) == -1)
454 fatalx("%s: expected to receive imsg "
455 "control fd but didn't receive any",
456 __func__);
457 /* Listen on control socket. */
458 control_listen(fd);
459 break;
460 case IMSG_CTL_END:
461 control_imsg_relay(&imsg);
462 break;
463 #endif /* SMALL */
464 default:
465 log_debug("%s: error handling imsg %d", __func__, type);
466 break;
467 }
468 imsg_free(&imsg);
469 }
470 if (!shut)
471 imsg_event_add(iev);
472 else {
473 /* This pipe is dead. Remove its event handler. */
474 event_del(&iev->ev);
475 event_loopexit(NULL);
476 }
477 }
478
479 void
frontend_dispatch_engine(int fd,short event,void * bula)480 frontend_dispatch_engine(int fd, short event, void *bula)
481 {
482 struct imsgev *iev = bula;
483 struct imsgbuf *ibuf = &iev->ibuf;
484 struct imsg imsg;
485 struct iface *iface;
486 ssize_t n;
487 uint32_t type;
488 int shut = 0;
489
490 if (event & EV_READ) {
491 if ((n = imsgbuf_read(ibuf)) == -1)
492 fatal("imsgbuf_read error");
493 if (n == 0) /* Connection closed. */
494 shut = 1;
495 }
496 if (event & EV_WRITE) {
497 if (imsgbuf_write(ibuf) == -1) {
498 if (errno == EPIPE) /* Connection closed. */
499 shut = 1;
500 else
501 fatal("imsgbuf_write");
502 }
503 }
504
505 for (;;) {
506 if ((n = imsg_get(ibuf, &imsg)) == -1)
507 fatal("%s: imsg_get error", __func__);
508 if (n == 0) /* No more messages. */
509 break;
510
511 type = imsg_get_type(&imsg);
512
513 switch (type) {
514 #ifndef SMALL
515 case IMSG_CTL_END:
516 case IMSG_CTL_SHOW_INTERFACE_INFO:
517 control_imsg_relay(&imsg);
518 break;
519 #endif /* SMALL */
520 case IMSG_SEND_DISCOVER: {
521 struct imsg_req_dhcp imsg_req_dhcp;
522
523 if (imsg_get_data(&imsg, &imsg_req_dhcp,
524 sizeof(imsg_req_dhcp)) == -1)
525 fatalx("%s: invalid %s", __func__, i2s(type));
526
527 iface = get_iface_by_id(imsg_req_dhcp.if_index);
528
529 if (iface == NULL)
530 break;
531
532 iface_data_from_imsg(iface, &imsg_req_dhcp);
533 send_packet(DHCPDISCOVER, iface);
534 break;
535 }
536 case IMSG_SEND_REQUEST: {
537 struct imsg_req_dhcp imsg_req_dhcp;
538
539 if (imsg_get_data(&imsg, &imsg_req_dhcp,
540 sizeof(imsg_req_dhcp)) == -1)
541 fatalx("%s: invalid %s", __func__, i2s(type));
542
543 iface = get_iface_by_id(imsg_req_dhcp.if_index);
544
545 if (iface == NULL)
546 break;
547
548 iface_data_from_imsg(iface, &imsg_req_dhcp);
549 send_packet(DHCPREQUEST, iface);
550 break;
551 }
552 default:
553 log_debug("%s: error handling imsg %d", __func__, type);
554 break;
555 }
556 imsg_free(&imsg);
557 }
558 if (!shut)
559 imsg_event_add(iev);
560 else {
561 /* This pipe is dead. Remove its event handler. */
562 event_del(&iev->ev);
563 event_loopexit(NULL);
564 }
565 }
566
567 int
get_flags(char * if_name)568 get_flags(char *if_name)
569 {
570 struct ifreq ifr;
571
572 strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
573 if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
574 log_warn("SIOCGIFFLAGS");
575 return -1;
576 }
577 return ifr.ifr_flags;
578 }
579
580 int
get_xflags(char * if_name)581 get_xflags(char *if_name)
582 {
583 struct ifreq ifr;
584
585 strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
586 if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) {
587 log_warn("SIOCGIFXFLAGS");
588 return -1;
589 }
590 return ifr.ifr_flags;
591 }
592
593 void
update_iface(struct if_msghdr * ifm,struct sockaddr_dl * sdl)594 update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl)
595 {
596 struct iface *iface;
597 struct imsg_ifinfo ifinfo;
598 uint32_t if_index;
599 int flags, xflags;
600 char ifnamebuf[IF_NAMESIZE], *if_name;
601
602 if_index = ifm->ifm_index;
603
604 flags = ifm->ifm_flags;
605 xflags = ifm->ifm_xflags;
606
607 iface = get_iface_by_id(if_index);
608 if_name = if_indextoname(if_index, ifnamebuf);
609
610 if (if_name == NULL) {
611 if (iface != NULL) {
612 log_debug("interface with idx %d removed", if_index);
613 frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
614 &if_index, sizeof(if_index));
615 remove_iface(if_index);
616 }
617 return;
618 }
619
620 if (!(xflags & IFXF_AUTOCONF4)) {
621 if (iface != NULL) {
622 log_info("Removed autoconf flag from %s", if_name);
623 frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
624 &if_index, sizeof(if_index));
625 remove_iface(if_index);
626 }
627 return;
628 }
629
630 memset(&ifinfo, 0, sizeof(ifinfo));
631 ifinfo.if_index = if_index;
632 ifinfo.link_state = ifm->ifm_data.ifi_link_state;
633 ifinfo.rdomain = ifm->ifm_tableid;
634 ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
635 (IFF_UP | IFF_RUNNING);
636
637 if (sdl != NULL && (sdl->sdl_type == IFT_ETHER ||
638 sdl->sdl_type == IFT_CARP) && sdl->sdl_alen == ETHER_ADDR_LEN)
639 memcpy(ifinfo.hw_address.ether_addr_octet, LLADDR(sdl),
640 ETHER_ADDR_LEN);
641 else if (iface == NULL) {
642 log_warnx("Could not find AF_LINK address for %s.", if_name);
643 return;
644 }
645
646 if (iface == NULL) {
647 if ((iface = calloc(1, sizeof(*iface))) == NULL)
648 fatal("calloc");
649 iface->udpsock = -1;
650 LIST_INSERT_HEAD(&interfaces, iface, entries);
651 frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0,
652 &if_index, sizeof(if_index));
653 } else {
654 if (iface->ifinfo.rdomain != ifinfo.rdomain &&
655 iface->udpsock != -1) {
656 close(iface->udpsock);
657 iface->udpsock = -1;
658 }
659 }
660
661 if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) {
662 memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
663 frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
664 sizeof(iface->ifinfo));
665 }
666 }
667
668 void
frontend_startup(void)669 frontend_startup(void)
670 {
671 if (!event_initialized(&ev_route))
672 fatalx("%s: did not receive a route socket from the main "
673 "process", __func__);
674
675 init_ifaces();
676 if (pledge("stdio unix recvfd", NULL) == -1)
677 fatal("pledge");
678 event_add(&ev_route, NULL);
679 }
680
681 void
init_ifaces(void)682 init_ifaces(void)
683 {
684 struct iface *iface;
685 struct imsg_ifinfo ifinfo;
686 struct if_nameindex *ifnidxp, *ifnidx;
687 struct ifaddrs *ifap, *ifa;
688 uint32_t if_index;
689 int flags, xflags;
690 char *if_name;
691
692 if ((ifnidxp = if_nameindex()) == NULL)
693 fatalx("if_nameindex");
694
695 if (getifaddrs(&ifap) != 0)
696 fatal("getifaddrs");
697
698 for (ifnidx = ifnidxp; ifnidx->if_index != 0 && ifnidx->if_name != NULL;
699 ifnidx++) {
700 if_index = ifnidx->if_index;
701 if_name = ifnidx->if_name;
702 if ((flags = get_flags(if_name)) == -1)
703 continue;
704 if ((xflags = get_xflags(if_name)) == -1)
705 continue;
706 if (!(xflags & IFXF_AUTOCONF4))
707 continue;
708
709 memset(&ifinfo, 0, sizeof(ifinfo));
710 ifinfo.if_index = if_index;
711 ifinfo.link_state = -1;
712 ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
713 (IFF_UP | IFF_RUNNING);
714
715 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
716 if (strcmp(if_name, ifa->ifa_name) != 0)
717 continue;
718 if (ifa->ifa_addr == NULL)
719 continue;
720
721 switch (ifa->ifa_addr->sa_family) {
722 case AF_LINK: {
723 struct if_data *if_data;
724 struct sockaddr_dl *sdl;
725
726 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
727 if ((sdl->sdl_type != IFT_ETHER &&
728 sdl->sdl_type != IFT_CARP) ||
729 sdl->sdl_alen != ETHER_ADDR_LEN)
730 continue;
731 memcpy(ifinfo.hw_address.ether_addr_octet,
732 LLADDR(sdl), ETHER_ADDR_LEN);
733
734 if_data = (struct if_data *)ifa->ifa_data;
735 ifinfo.link_state = if_data->ifi_link_state;
736 ifinfo.rdomain = if_data->ifi_rdomain;
737 goto out;
738 }
739 default:
740 break;
741 }
742 }
743 out:
744 if (ifinfo.link_state == -1)
745 /* no AF_LINK found */
746 continue;
747
748 if ((iface = calloc(1, sizeof(*iface))) == NULL)
749 fatal("calloc");
750 iface->udpsock = -1;
751 memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
752 LIST_INSERT_HEAD(&interfaces, iface, entries);
753 frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0,
754 &if_index, sizeof(if_index));
755 frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
756 sizeof(iface->ifinfo));
757 }
758
759 freeifaddrs(ifap);
760 if_freenameindex(ifnidxp);
761 }
762
763 void
route_receive(int fd,short events,void * arg)764 route_receive(int fd, short events, void *arg)
765 {
766 static uint8_t *buf;
767
768 struct rt_msghdr *rtm;
769 struct sockaddr *sa, *rti_info[RTAX_MAX];
770 ssize_t n;
771
772 if (buf == NULL) {
773 buf = malloc(ROUTE_SOCKET_BUF_SIZE);
774 if (buf == NULL)
775 fatal("malloc");
776 }
777 rtm = (struct rt_msghdr *)buf;
778 if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
779 if (errno == EAGAIN || errno == EINTR)
780 return;
781 log_warn("dispatch_rtmsg: read error");
782 return;
783 }
784
785 if (n == 0)
786 fatal("routing socket closed");
787
788 if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
789 log_warnx("partial rtm of %zd in buffer", n);
790 return;
791 }
792
793 if (rtm->rtm_version != RTM_VERSION)
794 return;
795
796 sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
797 get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
798
799 handle_route_message(rtm, rti_info);
800 }
801
802 void
handle_route_message(struct rt_msghdr * rtm,struct sockaddr ** rti_info)803 handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
804 {
805 struct sockaddr_dl *sdl = NULL;
806 struct if_announcemsghdr *ifan;
807 uint32_t if_index;
808
809 switch (rtm->rtm_type) {
810 case RTM_IFINFO:
811 if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family
812 == AF_LINK)
813 sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
814 update_iface((struct if_msghdr *)rtm, sdl);
815 break;
816 case RTM_IFANNOUNCE:
817 ifan = (struct if_announcemsghdr *)rtm;
818 if_index = ifan->ifan_index;
819 if (ifan->ifan_what == IFAN_DEPARTURE) {
820 frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
821 &if_index, sizeof(if_index));
822 remove_iface(if_index);
823 }
824 break;
825 case RTM_PROPOSAL:
826 if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
827 log_debug("RTP_PROPOSAL_SOLICIT");
828 frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS,
829 0, 0, NULL, 0);
830 }
831 break;
832 default:
833 log_debug("unexpected RTM: %d", rtm->rtm_type);
834 break;
835 }
836 }
837
838 #define ROUNDUP(a) \
839 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
840
841 void
get_rtaddrs(int addrs,struct sockaddr * sa,struct sockaddr ** rti_info)842 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
843 {
844 int i;
845
846 for (i = 0; i < RTAX_MAX; i++) {
847 if (addrs & (1 << i)) {
848 rti_info[i] = sa;
849 sa = (struct sockaddr *)((char *)(sa) +
850 ROUNDUP(sa->sa_len));
851 } else
852 rti_info[i] = NULL;
853 }
854 }
855
856 void
bpf_receive(int fd,short events,void * arg)857 bpf_receive(int fd, short events, void *arg)
858 {
859 struct bpf_hdr *hdr;
860 struct imsg_dhcp imsg_dhcp;
861 struct iface *iface;
862 ssize_t len, rem;
863 uint8_t *p;
864
865 iface = (struct iface *)arg;
866
867 if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) {
868 log_warn("%s: read", __func__);
869 return;
870 }
871
872 if (len == 0)
873 fatal("%s len == 0", __func__);
874
875 memset(&imsg_dhcp, 0, sizeof(imsg_dhcp));
876 imsg_dhcp.if_index = iface->ifinfo.if_index;
877
878 rem = len;
879 p = iface->bpfev.buf;
880
881 while (rem > 0) {
882 if ((size_t)rem < sizeof(*hdr)) {
883 log_warnx("packet too short");
884 return;
885 }
886 hdr = (struct bpf_hdr *)p;
887 if (hdr->bh_caplen != hdr->bh_datalen) {
888 log_warnx("skipping truncated packet");
889 goto cont;
890 }
891 if (rem < hdr->bh_hdrlen + hdr->bh_caplen)
892 /* we are done */
893 break;
894 if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) {
895 log_warn("packet too big");
896 goto cont;
897 }
898 memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen);
899 imsg_dhcp.len = hdr->bh_caplen;
900 imsg_dhcp.csumflags = hdr->bh_csumflags;
901 frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp,
902 sizeof(imsg_dhcp));
903 cont:
904 p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
905 rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
906
907 }
908 }
909
910 void
iface_data_from_imsg(struct iface * iface,struct imsg_req_dhcp * imsg)911 iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg)
912 {
913 iface->xid = imsg->xid;
914 iface->ciaddr = imsg->ciaddr;
915 iface->requested_ip = imsg->requested_ip;
916 iface->server_identifier = imsg->server_identifier;
917 iface->dhcp_server = imsg->dhcp_server;
918 }
919
920 ssize_t
build_packet(uint8_t message_type,char * if_name,uint32_t xid,struct ether_addr * hw_address,struct in_addr * ciaddr,struct in_addr * requested_ip,struct in_addr * server_identifier)921 build_packet(uint8_t message_type, char *if_name, uint32_t xid,
922 struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr
923 *requested_ip, struct in_addr *server_identifier)
924 {
925 static uint8_t dhcp_cookie[] = DHCP_COOKIE;
926 static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1,
927 DHCPDISCOVER};
928 static uint8_t dhcp_hostname[255 + 2] = {DHO_HOST_NAME, 0 /*, ... */};
929 static uint8_t dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7,
930 HTYPE_ETHER, 0, 0, 0, 0, 0, 0};
931 static uint8_t dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
932 8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
933 DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
934 DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES};
935 static uint8_t dhcp_req_list_v6[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
936 9, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
937 DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
938 DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES,
939 DHO_IPV6_ONLY_PREFERRED};
940 static uint8_t dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS,
941 4, 0, 0, 0, 0};
942 static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER,
943 4, 0, 0, 0, 0};
944 #ifndef SMALL
945 struct iface_conf *iface_conf;
946 #endif /* SMALL */
947 struct dhcp_hdr *hdr;
948 ssize_t len;
949 uint8_t *p;
950 char *c;
951
952 #ifndef SMALL
953 iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name);
954 #endif /* SMALL */
955
956 memset(dhcp_packet, 0, sizeof(dhcp_packet));
957 dhcp_message_type[2] = message_type;
958 p = dhcp_packet;
959 hdr = (struct dhcp_hdr *)p;
960 hdr->op = DHCP_BOOTREQUEST;
961 hdr->htype = HTYPE_ETHER;
962 hdr->hlen = 6;
963 hdr->hops = 0;
964 hdr->xid = htonl(xid);
965 hdr->secs = 0;
966 hdr->ciaddr = *ciaddr;
967 memcpy(hdr->chaddr, hw_address, sizeof(*hw_address));
968 p += sizeof(struct dhcp_hdr);
969 memcpy(p, dhcp_cookie, sizeof(dhcp_cookie));
970 p += sizeof(dhcp_cookie);
971 memcpy(p, dhcp_message_type, sizeof(dhcp_message_type));
972 p += sizeof(dhcp_message_type);
973
974 #ifndef SMALL
975 if (iface_conf != NULL && iface_conf->h_name != NULL) {
976 if (iface_conf->h_name[0] != '\0') {
977 dhcp_hostname[1] = strlen(iface_conf->h_name);
978 memcpy(dhcp_hostname + 2, iface_conf->h_name,
979 strlen(iface_conf->h_name));
980 memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
981 p += dhcp_hostname[1] + 2;
982 }
983 } else
984 #endif /* SMALL */
985 {
986 if (gethostname(dhcp_hostname + 2,
987 sizeof(dhcp_hostname) - 2) == 0 &&
988 dhcp_hostname[2] != '\0') {
989 if ((c = strchr(dhcp_hostname + 2, '.')) != NULL)
990 *c = '\0';
991 dhcp_hostname[1] = strlen(dhcp_hostname + 2);
992 memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
993 p += dhcp_hostname[1] + 2;
994 }
995 }
996
997 #ifndef SMALL
998 if (iface_conf != NULL) {
999 if (iface_conf->c_id_len > 0) {
1000 /* XXX check space */
1001 memcpy(p, iface_conf->c_id, iface_conf->c_id_len);
1002 p += iface_conf->c_id_len;
1003 } else {
1004 memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
1005 memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
1006 p += sizeof(dhcp_client_id);
1007 }
1008 if (iface_conf->vc_id_len > 0) {
1009 /* XXX check space */
1010 memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len);
1011 p += iface_conf->vc_id_len;
1012 }
1013 if (iface_conf->prefer_ipv6) {
1014 memcpy(p, dhcp_req_list_v6, sizeof(dhcp_req_list_v6));
1015 p += sizeof(dhcp_req_list_v6);
1016
1017 } else {
1018 memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
1019 p += sizeof(dhcp_req_list);
1020 }
1021 } else
1022 #endif /* SMALL */
1023 {
1024 memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
1025 memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
1026 p += sizeof(dhcp_client_id);
1027 memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
1028 p += sizeof(dhcp_req_list);
1029 }
1030
1031 if (requested_ip->s_addr != INADDR_ANY) {
1032 memcpy(dhcp_requested_address + 2, requested_ip,
1033 sizeof(*requested_ip));
1034 memcpy(p, dhcp_requested_address,
1035 sizeof(dhcp_requested_address));
1036 p += sizeof(dhcp_requested_address);
1037 }
1038
1039 if (server_identifier->s_addr != INADDR_ANY) {
1040 memcpy(dhcp_server_identifier + 2, server_identifier,
1041 sizeof(*server_identifier));
1042 memcpy(p, dhcp_server_identifier,
1043 sizeof(dhcp_server_identifier));
1044 p += sizeof(dhcp_server_identifier);
1045 }
1046
1047 *p = DHO_END;
1048 p += 1;
1049
1050 len = p - dhcp_packet;
1051
1052 /* dhcp_packet is initialized with DHO_PADs */
1053 if (len < BOOTP_MIN_LEN)
1054 len = BOOTP_MIN_LEN;
1055
1056 return (len);
1057 }
1058
1059 void
send_packet(uint8_t message_type,struct iface * iface)1060 send_packet(uint8_t message_type, struct iface *iface)
1061 {
1062 ssize_t pkt_len;
1063 char ifnamebuf[IF_NAMESIZE], *if_name;
1064
1065 if (!event_initialized(&iface->bpfev.ev)) {
1066 iface->send_discover = 1;
1067 return;
1068 }
1069
1070 iface->send_discover = 0;
1071
1072 if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) == NULL)
1073 return; /* iface went away, nothing to do */
1074
1075 log_debug("%s on %s", message_type == DHCPDISCOVER ? "DHCPDISCOVER" :
1076 "DHCPREQUEST", if_name);
1077
1078 pkt_len = build_packet(message_type, if_name, iface->xid,
1079 &iface->ifinfo.hw_address, &iface->ciaddr, &iface->requested_ip,
1080 &iface->server_identifier);
1081 if (iface->dhcp_server.s_addr != INADDR_ANY) {
1082 if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1)
1083 bpf_send_packet(iface, dhcp_packet, pkt_len);
1084 } else
1085 bpf_send_packet(iface, dhcp_packet, pkt_len);
1086 }
1087
1088 int
udp_send_packet(struct iface * iface,uint8_t * packet,ssize_t len)1089 udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
1090 {
1091 struct sockaddr_in to;
1092
1093 memset(&to, 0, sizeof(to));
1094 to.sin_family = AF_INET;
1095 to.sin_len = sizeof(to);
1096 to.sin_addr = iface->dhcp_server;
1097 to.sin_port = ntohs(SERVER_PORT);
1098
1099 if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to,
1100 sizeof(to)) == -1) {
1101 log_warn("sendto");
1102 return -1;
1103 }
1104 return 0;
1105 }
1106 void
bpf_send_packet(struct iface * iface,uint8_t * packet,ssize_t len)1107 bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
1108 {
1109 struct iovec iov[4];
1110 struct ether_header eh;
1111 struct ip ip;
1112 struct udphdr udp;
1113 ssize_t total, result;
1114 int iovcnt = 0, i;
1115
1116 memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
1117 memcpy(eh.ether_shost, &iface->ifinfo.hw_address,
1118 sizeof(eh.ether_dhost));
1119 eh.ether_type = htons(ETHERTYPE_IP);
1120 iov[0].iov_base = &eh;
1121 iov[0].iov_len = sizeof(eh);
1122 iovcnt++;
1123
1124 ip.ip_v = 4;
1125 ip.ip_hl = 5;
1126 ip.ip_tos = IPTOS_LOWDELAY;
1127 ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
1128 ip.ip_id = 0;
1129 ip.ip_off = 0;
1130 ip.ip_ttl = 128;
1131 ip.ip_p = IPPROTO_UDP;
1132 ip.ip_sum = 0;
1133 ip.ip_src.s_addr = INADDR_ANY;
1134 ip.ip_dst.s_addr = INADDR_BROADCAST;
1135 ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
1136 iov[iovcnt].iov_base = &ip;
1137 iov[iovcnt].iov_len = sizeof(ip);
1138 iovcnt++;
1139
1140 udp.uh_sport = htons(CLIENT_PORT);
1141 udp.uh_dport = htons(SERVER_PORT);
1142 udp.uh_ulen = htons(sizeof(udp) + len);
1143 udp.uh_sum = 0;
1144 udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
1145 checksum((unsigned char *)packet, len,
1146 checksum((unsigned char *)&ip.ip_src,
1147 2 * sizeof(ip.ip_src),
1148 IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen)))));
1149 iov[iovcnt].iov_base = &udp;
1150 iov[iovcnt].iov_len = sizeof(udp);
1151 iovcnt++;
1152
1153 iov[iovcnt].iov_base = packet;
1154 iov[iovcnt].iov_len = len;
1155 iovcnt++;
1156
1157 total = 0;
1158 for (i = 0; i < iovcnt; i++)
1159 total += iov[i].iov_len;
1160
1161 result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt);
1162 if (result == -1)
1163 log_warn("%s: writev", __func__);
1164 else if (result < total) {
1165 log_warnx("%s, writev: %zd of %zd bytes", __func__, result,
1166 total);
1167 }
1168 }
1169
1170 struct iface*
get_iface_by_id(uint32_t if_index)1171 get_iface_by_id(uint32_t if_index)
1172 {
1173 struct iface *iface;
1174
1175 LIST_FOREACH (iface, &interfaces, entries) {
1176 if (iface->ifinfo.if_index == if_index)
1177 return (iface);
1178 }
1179
1180 return (NULL);
1181 }
1182
1183 void
remove_iface(uint32_t if_index)1184 remove_iface(uint32_t if_index)
1185 {
1186 struct iface *iface;
1187
1188 iface = get_iface_by_id(if_index);
1189
1190 if (iface == NULL)
1191 return;
1192
1193 LIST_REMOVE(iface, entries);
1194 if (event_initialized(&iface->bpfev.ev)) {
1195 event_del(&iface->bpfev.ev);
1196 close(EVENT_FD(&iface->bpfev.ev));
1197 }
1198 if (iface->udpsock != -1)
1199 close(iface->udpsock);
1200 free(iface);
1201 }
1202
1203 void
set_bpfsock(int bpfsock,uint32_t if_index)1204 set_bpfsock(int bpfsock, uint32_t if_index)
1205 {
1206 struct iface *iface;
1207
1208 iface = get_iface_by_id(if_index);
1209
1210 if (iface == NULL) {
1211 /*
1212 * The interface disappeared while we were waiting for the
1213 * parent process to open the bpf socket.
1214 */
1215 close(bpfsock);
1216 } else if (event_initialized(&iface->bpfev.ev)) {
1217 /*
1218 * The autoconf flag is flapping and we have multiple bpf sockets in
1219 * flight. We don't need this one because we already got one.
1220 */
1221 close(bpfsock);
1222 } else {
1223 event_set(&iface->bpfev.ev, bpfsock, EV_READ |
1224 EV_PERSIST, bpf_receive, iface);
1225 event_add(&iface->bpfev.ev, NULL);
1226 if (iface->send_discover)
1227 send_packet(DHCPDISCOVER, iface);
1228 }
1229 }
1230
1231 #ifndef SMALL
1232 struct iface_conf*
find_iface_conf(struct iface_conf_head * head,char * if_name)1233 find_iface_conf(struct iface_conf_head *head, char *if_name)
1234 {
1235 struct iface_conf *iface_conf;
1236
1237 if (if_name == NULL)
1238 return (NULL);
1239
1240 SIMPLEQ_FOREACH(iface_conf, head, entry) {
1241 if (strcmp(iface_conf->name, if_name) == 0)
1242 return iface_conf;
1243 }
1244 return (NULL);
1245 }
1246
1247 int*
changed_ifaces(struct dhcpleased_conf * oconf,struct dhcpleased_conf * nconf)1248 changed_ifaces(struct dhcpleased_conf *oconf, struct dhcpleased_conf *nconf)
1249 {
1250 struct iface_conf *iface_conf, *oiface_conf;
1251 int *ret, if_index, count = 0, i = 0;
1252
1253 /*
1254 * Worst case: All old interfaces replaced with new interfaces.
1255 * This should still be a small number
1256 */
1257 SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry)
1258 count++;
1259 SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry)
1260 count++;
1261
1262 ret = calloc(count + 1, sizeof(int));
1263
1264 SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) {
1265 if ((if_index = if_nametoindex(iface_conf->name)) == 0)
1266 continue;
1267 oiface_conf = find_iface_conf(&oconf->iface_list,
1268 iface_conf->name);
1269 if (oiface_conf == NULL) {
1270 /* new interface added to config */
1271 ret[i++] = if_index;
1272 } else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) {
1273 /* interface conf changed */
1274 ret[i++] = if_index;
1275 }
1276 }
1277 SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) {
1278 if ((if_index = if_nametoindex(oiface_conf->name)) == 0)
1279 continue;
1280 if (find_iface_conf(&nconf->iface_list, oiface_conf->name) ==
1281 NULL) {
1282 /* interface removed from config */
1283 ret[i++] = if_index;
1284 }
1285 }
1286 return ret;
1287 }
1288
1289 int
iface_conf_cmp(struct iface_conf * a,struct iface_conf * b)1290 iface_conf_cmp(struct iface_conf *a, struct iface_conf *b)
1291 {
1292 if (a->vc_id_len != b->vc_id_len)
1293 return 1;
1294 if (memcmp(a->vc_id, b->vc_id, a->vc_id_len) != 0)
1295 return 1;
1296 if (a->c_id_len != b->c_id_len)
1297 return 1;
1298 if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0)
1299 return 1;
1300 if (a->h_name == NULL || b->h_name == NULL)
1301 return 1;
1302 if (strcmp(a->h_name, b->h_name) != 0)
1303 return 1;
1304 if (a->ignore != b->ignore)
1305 return 1;
1306 if (a->ignore_servers_len != b->ignore_servers_len)
1307 return 1;
1308 if (memcmp(a->ignore_servers, b->ignore_servers,
1309 a->ignore_servers_len * sizeof (struct in_addr)) != 0)
1310 return 1;
1311 return 0;
1312 }
1313 #endif /* SMALL */
1314