1*0e59d0d1Sclaudio /* $OpenBSD: frontend.c,v 1.74 2024/11/21 13:35:20 claudio Exp $ */ 20acf3e2dSflorian 30acf3e2dSflorian /* 40acf3e2dSflorian * Copyright (c) 2017 Florian Obser <florian@openbsd.org> 50acf3e2dSflorian * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 60acf3e2dSflorian * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 70acf3e2dSflorian * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 80acf3e2dSflorian * 90acf3e2dSflorian * Permission to use, copy, modify, and distribute this software for any 100acf3e2dSflorian * purpose with or without fee is hereby granted, provided that the above 110acf3e2dSflorian * copyright notice and this permission notice appear in all copies. 120acf3e2dSflorian * 130acf3e2dSflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 140acf3e2dSflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 150acf3e2dSflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 160acf3e2dSflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 170acf3e2dSflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 180acf3e2dSflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 190acf3e2dSflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 200acf3e2dSflorian */ 210acf3e2dSflorian #include <sys/types.h> 220acf3e2dSflorian #include <sys/ioctl.h> 230acf3e2dSflorian #include <sys/queue.h> 240acf3e2dSflorian #include <sys/socket.h> 250acf3e2dSflorian #include <sys/syslog.h> 260acf3e2dSflorian #include <sys/uio.h> 270acf3e2dSflorian 280acf3e2dSflorian #include <net/if.h> 290acf3e2dSflorian #include <net/if_dl.h> 300acf3e2dSflorian #include <net/if_types.h> 310acf3e2dSflorian #include <net/route.h> 320acf3e2dSflorian 330acf3e2dSflorian #include <arpa/inet.h> 340acf3e2dSflorian 350acf3e2dSflorian #include <netinet/in.h> 360acf3e2dSflorian #include <netinet/if_ether.h> 37060a7308Sflorian #include <netinet6/nd6.h> 38060a7308Sflorian #include <netinet6/in6_var.h> 390acf3e2dSflorian #include <netinet/ip6.h> 400acf3e2dSflorian #include <netinet/icmp6.h> 410acf3e2dSflorian 420acf3e2dSflorian #include <errno.h> 430acf3e2dSflorian #include <event.h> 440acf3e2dSflorian #include <ifaddrs.h> 450acf3e2dSflorian #include <imsg.h> 460acf3e2dSflorian #include <pwd.h> 470acf3e2dSflorian #include <signal.h> 480acf3e2dSflorian #include <stdio.h> 490acf3e2dSflorian #include <stdlib.h> 500acf3e2dSflorian #include <string.h> 510acf3e2dSflorian #include <unistd.h> 520acf3e2dSflorian 530acf3e2dSflorian #include "log.h" 540acf3e2dSflorian #include "slaacd.h" 550acf3e2dSflorian #include "frontend.h" 560acf3e2dSflorian #include "control.h" 570acf3e2dSflorian 580acf3e2dSflorian #define ROUTE_SOCKET_BUF_SIZE 16384 590acf3e2dSflorian #define ALLROUTER "ff02::2" 600acf3e2dSflorian 61dd19964dSflorian struct icmp6_ev { 62dd19964dSflorian struct event ev; 63dd19964dSflorian uint8_t answer[1500]; 64dd19964dSflorian struct msghdr rcvmhdr; 65dd19964dSflorian struct iovec rcviov[1]; 66dd19964dSflorian struct sockaddr_in6 from; 67dd19964dSflorian int refcnt; 68dd19964dSflorian }; 69dd19964dSflorian 70dd19964dSflorian struct iface { 71dd19964dSflorian LIST_ENTRY(iface) entries; 72dd19964dSflorian struct icmp6_ev *icmp6ev; 73dd19964dSflorian struct ether_addr hw_address; 74dd19964dSflorian uint32_t if_index; 75dd19964dSflorian int rdomain; 76dd19964dSflorian int send_solicitation; 7711cc0cd9Sflorian int ll_tentative; 78dd19964dSflorian }; 79dd19964dSflorian 800acf3e2dSflorian __dead void frontend_shutdown(void); 810acf3e2dSflorian void frontend_sig_handler(int, short, void *); 820acf3e2dSflorian void update_iface(uint32_t, char*); 830acf3e2dSflorian void frontend_startup(void); 840acf3e2dSflorian void route_receive(int, short, void *); 850acf3e2dSflorian void handle_route_message(struct rt_msghdr *, struct sockaddr **); 860acf3e2dSflorian void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 870acf3e2dSflorian void icmp6_receive(int, short, void *); 880acf3e2dSflorian int get_flags(char *); 890acf3e2dSflorian int get_xflags(char *); 90c966f6c0Sflorian int get_ifrdomain(char *); 91dd19964dSflorian struct iface *get_iface_by_id(uint32_t); 92dd19964dSflorian void remove_iface(uint32_t); 93dd19964dSflorian struct icmp6_ev *get_icmp6ev_by_rdomain(int); 94eaf8d593Sflorian void unref_icmp6ev(struct iface *); 95dd19964dSflorian void set_icmp6sock(int, int); 960acf3e2dSflorian void send_solicitation(uint32_t); 97060a7308Sflorian #ifndef SMALL 9805b87f88Sflorian const char *flags_to_str(int); 99060a7308Sflorian #endif /* SMALL */ 1000acf3e2dSflorian 101dd19964dSflorian LIST_HEAD(, iface) interfaces; 1025ebbfac0Sflorian static struct imsgev *iev_main; 1035ebbfac0Sflorian static struct imsgev *iev_engine; 1040acf3e2dSflorian struct event ev_route; 1050acf3e2dSflorian struct msghdr sndmhdr; 1060acf3e2dSflorian struct iovec sndiov[4]; 1070acf3e2dSflorian struct nd_router_solicit rs; 1080acf3e2dSflorian struct nd_opt_hdr nd_opt_hdr; 1090acf3e2dSflorian struct ether_addr nd_opt_source_link_addr; 1100acf3e2dSflorian struct sockaddr_in6 dst; 111dd19964dSflorian int ioctlsock; 1120acf3e2dSflorian 1130acf3e2dSflorian void 1140acf3e2dSflorian frontend_sig_handler(int sig, short event, void *bula) 1150acf3e2dSflorian { 1160acf3e2dSflorian /* 1170acf3e2dSflorian * Normal signal handler rules don't apply because libevent 1180acf3e2dSflorian * decouples for us. 1190acf3e2dSflorian */ 1200acf3e2dSflorian 1210acf3e2dSflorian switch (sig) { 1220acf3e2dSflorian case SIGINT: 1230acf3e2dSflorian case SIGTERM: 1240acf3e2dSflorian frontend_shutdown(); 1250acf3e2dSflorian default: 1260acf3e2dSflorian fatalx("unexpected signal"); 1270acf3e2dSflorian } 1280acf3e2dSflorian } 1290acf3e2dSflorian 1300acf3e2dSflorian void 131f9631f15Sflorian frontend(int debug, int verbose) 1320acf3e2dSflorian { 1330acf3e2dSflorian struct event ev_sigint, ev_sigterm; 1340acf3e2dSflorian struct passwd *pw; 1350acf3e2dSflorian struct in6_pktinfo *pi; 1360acf3e2dSflorian struct cmsghdr *cm; 137dd19964dSflorian size_t sndcmsglen; 138f9631f15Sflorian int hoplimit = 255; 139dd19964dSflorian uint8_t *sndcmsgbuf; 1400acf3e2dSflorian 1410acf3e2dSflorian log_init(debug, LOG_DAEMON); 1420acf3e2dSflorian log_setverbose(verbose); 1430acf3e2dSflorian 1440acf3e2dSflorian if ((pw = getpwnam(SLAACD_USER)) == NULL) 1450acf3e2dSflorian fatal("getpwnam"); 1460acf3e2dSflorian 1470acf3e2dSflorian if (chdir("/") == -1) 1480acf3e2dSflorian fatal("chdir(\"/\")"); 1490acf3e2dSflorian 150ec8f555eSflorian if (unveil("/", "") == -1) 151bc5a8259Sbeck fatal("unveil /"); 152ec8f555eSflorian if (unveil(NULL, NULL) == -1) 153bc5a8259Sbeck fatal("unveil"); 154ec8f555eSflorian 15531525baaSflorian setproctitle("%s", "frontend"); 15631525baaSflorian log_procinit("frontend"); 1570acf3e2dSflorian 158df69c215Sderaadt if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 1590acf3e2dSflorian fatal("socket"); 1600acf3e2dSflorian 1610acf3e2dSflorian if (setgroups(1, &pw->pw_gid) || 1620acf3e2dSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 1630acf3e2dSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 1640acf3e2dSflorian fatal("can't drop privileges"); 1650acf3e2dSflorian 1660c8652d6Sflorian if (pledge("stdio unix recvfd route", NULL) == -1) 1670acf3e2dSflorian fatal("pledge"); 1680acf3e2dSflorian 1690acf3e2dSflorian event_init(); 1700acf3e2dSflorian 1710acf3e2dSflorian /* Setup signal handler. */ 1720acf3e2dSflorian signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); 1730acf3e2dSflorian signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); 1740acf3e2dSflorian signal_add(&ev_sigint, NULL); 1750acf3e2dSflorian signal_add(&ev_sigterm, NULL); 1760acf3e2dSflorian signal(SIGPIPE, SIG_IGN); 1770acf3e2dSflorian signal(SIGHUP, SIG_IGN); 1780acf3e2dSflorian 1790acf3e2dSflorian /* Setup pipe and event handler to the parent process. */ 1800acf3e2dSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 1810acf3e2dSflorian fatal(NULL); 182*0e59d0d1Sclaudio if (imsgbuf_init(&iev_main->ibuf, 3) == -1) 183*0e59d0d1Sclaudio fatal(NULL); 184*0e59d0d1Sclaudio imsgbuf_allow_fdpass(&iev_main->ibuf); 1850acf3e2dSflorian iev_main->handler = frontend_dispatch_main; 1860acf3e2dSflorian iev_main->events = EV_READ; 1870acf3e2dSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 1880acf3e2dSflorian iev_main->handler, iev_main); 1890acf3e2dSflorian event_add(&iev_main->ev, NULL); 1900acf3e2dSflorian 1910acf3e2dSflorian sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 1920acf3e2dSflorian CMSG_SPACE(sizeof(int)); 1930acf3e2dSflorian 1940acf3e2dSflorian if ((sndcmsgbuf = malloc(sndcmsglen)) == NULL) 1950acf3e2dSflorian fatal("malloc"); 1960acf3e2dSflorian 1970acf3e2dSflorian rs.nd_rs_type = ND_ROUTER_SOLICIT; 1980acf3e2dSflorian rs.nd_rs_code = 0; 1990acf3e2dSflorian rs.nd_rs_cksum = 0; 2000acf3e2dSflorian rs.nd_rs_reserved = 0; 2010acf3e2dSflorian 2020acf3e2dSflorian nd_opt_hdr.nd_opt_type = ND_OPT_SOURCE_LINKADDR; 2030acf3e2dSflorian nd_opt_hdr.nd_opt_len = 1; 2040acf3e2dSflorian 2050acf3e2dSflorian memset(&dst, 0, sizeof(dst)); 2060acf3e2dSflorian dst.sin6_family = AF_INET6; 2070acf3e2dSflorian if (inet_pton(AF_INET6, ALLROUTER, &dst.sin6_addr.s6_addr) != 1) 2080acf3e2dSflorian fatal("inet_pton"); 2090acf3e2dSflorian 2100acf3e2dSflorian sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 2110acf3e2dSflorian sndmhdr.msg_iov = sndiov; 2120acf3e2dSflorian sndmhdr.msg_iovlen = 3; 2130acf3e2dSflorian sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 2140acf3e2dSflorian sndmhdr.msg_controllen = sndcmsglen; 2150acf3e2dSflorian 2160acf3e2dSflorian sndmhdr.msg_name = (caddr_t)&dst; 2170acf3e2dSflorian sndmhdr.msg_iov[0].iov_base = (caddr_t)&rs; 2180acf3e2dSflorian sndmhdr.msg_iov[0].iov_len = sizeof(rs); 2190acf3e2dSflorian sndmhdr.msg_iov[1].iov_base = (caddr_t)&nd_opt_hdr; 2200acf3e2dSflorian sndmhdr.msg_iov[1].iov_len = sizeof(nd_opt_hdr); 2210acf3e2dSflorian sndmhdr.msg_iov[2].iov_base = (caddr_t)&nd_opt_source_link_addr; 2220acf3e2dSflorian sndmhdr.msg_iov[2].iov_len = sizeof(nd_opt_source_link_addr); 2230acf3e2dSflorian 2240acf3e2dSflorian cm = CMSG_FIRSTHDR(&sndmhdr); 2250acf3e2dSflorian 2260acf3e2dSflorian cm->cmsg_level = IPPROTO_IPV6; 2270acf3e2dSflorian cm->cmsg_type = IPV6_PKTINFO; 2280acf3e2dSflorian cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 2290acf3e2dSflorian pi = (struct in6_pktinfo *)CMSG_DATA(cm); 2300acf3e2dSflorian memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); 2310acf3e2dSflorian pi->ipi6_ifindex = 0; 2320acf3e2dSflorian 2330acf3e2dSflorian cm = CMSG_NXTHDR(&sndmhdr, cm); 2340acf3e2dSflorian cm->cmsg_level = IPPROTO_IPV6; 2350acf3e2dSflorian cm->cmsg_type = IPV6_HOPLIMIT; 2360acf3e2dSflorian cm->cmsg_len = CMSG_LEN(sizeof(int)); 2370acf3e2dSflorian memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 2380acf3e2dSflorian 239dd19964dSflorian LIST_INIT(&interfaces); 240dd19964dSflorian 2410acf3e2dSflorian event_dispatch(); 2420acf3e2dSflorian 2430acf3e2dSflorian frontend_shutdown(); 2440acf3e2dSflorian } 2450acf3e2dSflorian 2460acf3e2dSflorian __dead void 2470acf3e2dSflorian frontend_shutdown(void) 2480acf3e2dSflorian { 2490acf3e2dSflorian /* Close pipes. */ 250dd7efffeSclaudio imsgbuf_write(&iev_engine->ibuf); 2519cbf9e90Sclaudio imsgbuf_clear(&iev_engine->ibuf); 2520acf3e2dSflorian close(iev_engine->ibuf.fd); 253dd7efffeSclaudio imsgbuf_write(&iev_main->ibuf); 2549cbf9e90Sclaudio imsgbuf_clear(&iev_main->ibuf); 2550acf3e2dSflorian close(iev_main->ibuf.fd); 2560acf3e2dSflorian 2570acf3e2dSflorian free(iev_engine); 2580acf3e2dSflorian free(iev_main); 2590acf3e2dSflorian 2600acf3e2dSflorian log_info("frontend exiting"); 2610acf3e2dSflorian exit(0); 2620acf3e2dSflorian } 2630acf3e2dSflorian 2640acf3e2dSflorian int 2650acf3e2dSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data, 2660acf3e2dSflorian uint16_t datalen) 2670acf3e2dSflorian { 2680acf3e2dSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, data, 2690acf3e2dSflorian datalen)); 2700acf3e2dSflorian } 2710acf3e2dSflorian 2720acf3e2dSflorian int 2730acf3e2dSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, 2740acf3e2dSflorian void *data, uint16_t datalen) 2750acf3e2dSflorian { 2760acf3e2dSflorian return (imsg_compose_event(iev_engine, type, peerid, pid, -1, 2770acf3e2dSflorian data, datalen)); 2780acf3e2dSflorian } 2790acf3e2dSflorian 2800acf3e2dSflorian void 2810acf3e2dSflorian frontend_dispatch_main(int fd, short event, void *bula) 2820acf3e2dSflorian { 2830acf3e2dSflorian struct imsg imsg; 2840acf3e2dSflorian struct imsgev *iev = bula; 2850acf3e2dSflorian struct imsgbuf *ibuf = &iev->ibuf; 2860acf3e2dSflorian ssize_t n; 28733d2acb6Sflorian uint32_t type; 288dd19964dSflorian int shut = 0, icmp6sock, rdomain; 2890acf3e2dSflorian 2900acf3e2dSflorian if (event & EV_READ) { 291668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 292dd7efffeSclaudio fatal("imsgbuf_read error"); 2930acf3e2dSflorian if (n == 0) /* Connection closed. */ 2940acf3e2dSflorian shut = 1; 2950acf3e2dSflorian } 2960acf3e2dSflorian if (event & EV_WRITE) { 297dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 298e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 2990acf3e2dSflorian shut = 1; 300e3b6409cSclaudio else 301dd7efffeSclaudio fatal("imsgbuf_write"); 302e3b6409cSclaudio } 3030acf3e2dSflorian } 3040acf3e2dSflorian 3050acf3e2dSflorian for (;;) { 3060acf3e2dSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 3070acf3e2dSflorian fatal("%s: imsg_get error", __func__); 3080acf3e2dSflorian if (n == 0) /* No more messages. */ 3090acf3e2dSflorian break; 3100acf3e2dSflorian 31133d2acb6Sflorian type = imsg_get_type(&imsg); 31233d2acb6Sflorian 31333d2acb6Sflorian switch (type) { 3140acf3e2dSflorian case IMSG_SOCKET_IPC: 3150acf3e2dSflorian /* 3160acf3e2dSflorian * Setup pipe and event handler to the engine 3170acf3e2dSflorian * process. 3180acf3e2dSflorian */ 31958a273d8Sflorian if (iev_engine) 32058a273d8Sflorian fatalx("%s: received unexpected imsg fd " 3210acf3e2dSflorian "to frontend", __func__); 32258a273d8Sflorian 3238de8a5baSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 32458a273d8Sflorian fatalx("%s: expected to receive imsg fd to " 3250acf3e2dSflorian "frontend but didn't receive any", 3260acf3e2dSflorian __func__); 3270acf3e2dSflorian 3280acf3e2dSflorian iev_engine = malloc(sizeof(struct imsgev)); 3290acf3e2dSflorian if (iev_engine == NULL) 3300acf3e2dSflorian fatal(NULL); 3310acf3e2dSflorian 332*0e59d0d1Sclaudio if (imsgbuf_init(&iev_engine->ibuf, fd) == -1) 333*0e59d0d1Sclaudio fatal(NULL); 3340acf3e2dSflorian iev_engine->handler = frontend_dispatch_engine; 3350acf3e2dSflorian iev_engine->events = EV_READ; 3360acf3e2dSflorian 3370acf3e2dSflorian event_set(&iev_engine->ev, iev_engine->ibuf.fd, 3380acf3e2dSflorian iev_engine->events, iev_engine->handler, iev_engine); 3390acf3e2dSflorian event_add(&iev_engine->ev, NULL); 340f9631f15Sflorian break; 341f9631f15Sflorian case IMSG_ICMP6SOCK: 3428de8a5baSclaudio if ((icmp6sock = imsg_get_fd(&imsg)) == -1) 343f9631f15Sflorian fatalx("%s: expected to receive imsg " 344f9631f15Sflorian "ICMPv6 fd but didn't receive any", 345f9631f15Sflorian __func__); 34633d2acb6Sflorian if (imsg_get_data(&imsg, &rdomain, 34733d2acb6Sflorian sizeof(rdomain)) == -1) 34833d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 34933d2acb6Sflorian 350dd19964dSflorian set_icmp6sock(icmp6sock, rdomain); 351f9631f15Sflorian break; 352f9631f15Sflorian case IMSG_ROUTESOCK: 3538de8a5baSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 354f9631f15Sflorian fatalx("%s: expected to receive imsg " 355f9631f15Sflorian "routesocket fd but didn't receive any", 356f9631f15Sflorian __func__); 35733d2acb6Sflorian 358f9631f15Sflorian event_set(&ev_route, fd, EV_READ | EV_PERSIST, 359f9631f15Sflorian route_receive, NULL); 360f9631f15Sflorian break; 3617e5ccecdSflorian case IMSG_STARTUP: 3627e5ccecdSflorian frontend_startup(); 3637e5ccecdSflorian break; 364f9631f15Sflorian #ifndef SMALL 365f9631f15Sflorian case IMSG_CONTROLFD: 3668de8a5baSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 367f9631f15Sflorian fatalx("%s: expected to receive imsg " 368f9631f15Sflorian "control fd but didn't receive any", 369f9631f15Sflorian __func__); 37033d2acb6Sflorian 371f9631f15Sflorian /* Listen on control socket. */ 372bed1cdeeSflorian control_listen(fd); 3730acf3e2dSflorian break; 3740acf3e2dSflorian case IMSG_CTL_END: 3750acf3e2dSflorian control_imsg_relay(&imsg); 3760acf3e2dSflorian break; 377bf0ed931Sflorian #endif /* SMALL */ 3780acf3e2dSflorian default: 37933d2acb6Sflorian log_debug("%s: error handling imsg %d", __func__, type); 3800acf3e2dSflorian break; 3810acf3e2dSflorian } 3820acf3e2dSflorian imsg_free(&imsg); 3830acf3e2dSflorian } 3840acf3e2dSflorian if (!shut) 3850acf3e2dSflorian imsg_event_add(iev); 3860acf3e2dSflorian else { 3870acf3e2dSflorian /* This pipe is dead. Remove its event handler. */ 3880acf3e2dSflorian event_del(&iev->ev); 3890acf3e2dSflorian event_loopexit(NULL); 3900acf3e2dSflorian } 3910acf3e2dSflorian } 3920acf3e2dSflorian 3930acf3e2dSflorian void 3940acf3e2dSflorian frontend_dispatch_engine(int fd, short event, void *bula) 3950acf3e2dSflorian { 3960acf3e2dSflorian struct imsgev *iev = bula; 3970acf3e2dSflorian struct imsgbuf *ibuf = &iev->ibuf; 3980acf3e2dSflorian struct imsg imsg; 3990acf3e2dSflorian ssize_t n; 4000acf3e2dSflorian int shut = 0; 40133d2acb6Sflorian uint32_t if_index, type; 4020acf3e2dSflorian 4030acf3e2dSflorian if (event & EV_READ) { 404668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 405dd7efffeSclaudio fatal("imsgbuf_read error"); 4060acf3e2dSflorian if (n == 0) /* Connection closed. */ 4070acf3e2dSflorian shut = 1; 4080acf3e2dSflorian } 4090acf3e2dSflorian if (event & EV_WRITE) { 410dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 411e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 4120acf3e2dSflorian shut = 1; 413e3b6409cSclaudio else 414dd7efffeSclaudio fatal("imsgbuf_write"); 415e3b6409cSclaudio } 4160acf3e2dSflorian } 4170acf3e2dSflorian 4180acf3e2dSflorian for (;;) { 4190acf3e2dSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 4200acf3e2dSflorian fatal("%s: imsg_get error", __func__); 4210acf3e2dSflorian if (n == 0) /* No more messages. */ 4220acf3e2dSflorian break; 4230acf3e2dSflorian 42433d2acb6Sflorian type = imsg_get_type(&imsg); 42533d2acb6Sflorian 42633d2acb6Sflorian switch (type) { 427bf0ed931Sflorian #ifndef SMALL 4280acf3e2dSflorian case IMSG_CTL_END: 4290acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 4300acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO_RA: 4310acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX: 4320acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS: 4330acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS: 4340acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL: 4350acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS: 4360acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL: 43734435150Sflorian case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSALS: 43834435150Sflorian case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSAL: 4390acf3e2dSflorian control_imsg_relay(&imsg); 4400acf3e2dSflorian break; 441bf0ed931Sflorian #endif /* SMALL */ 4420acf3e2dSflorian case IMSG_CTL_SEND_SOLICITATION: 44333d2acb6Sflorian if (imsg_get_data(&imsg, &if_index, 44433d2acb6Sflorian sizeof(if_index)) == -1) 44533d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 44633d2acb6Sflorian 4470acf3e2dSflorian send_solicitation(if_index); 4480acf3e2dSflorian break; 4490acf3e2dSflorian default: 45033d2acb6Sflorian log_debug("%s: error handling imsg %d", __func__, type); 4510acf3e2dSflorian break; 4520acf3e2dSflorian } 4530acf3e2dSflorian imsg_free(&imsg); 4540acf3e2dSflorian } 4550acf3e2dSflorian if (!shut) 4560acf3e2dSflorian imsg_event_add(iev); 4570acf3e2dSflorian else { 4580acf3e2dSflorian /* This pipe is dead. Remove its event handler. */ 4590acf3e2dSflorian event_del(&iev->ev); 4600acf3e2dSflorian event_loopexit(NULL); 4610acf3e2dSflorian } 4620acf3e2dSflorian } 4630acf3e2dSflorian 4640acf3e2dSflorian int 4650acf3e2dSflorian get_flags(char *if_name) 4660acf3e2dSflorian { 4670acf3e2dSflorian struct ifreq ifr; 4680acf3e2dSflorian 4699bdef370Sflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 4708c4e2bd5Sflorian if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { 4718c4e2bd5Sflorian log_warn("SIOCGIFFLAGS"); 4728c4e2bd5Sflorian return -1; 4738c4e2bd5Sflorian } 4740acf3e2dSflorian return ifr.ifr_flags; 4750acf3e2dSflorian } 4760acf3e2dSflorian 4770acf3e2dSflorian int 4780acf3e2dSflorian get_xflags(char *if_name) 4790acf3e2dSflorian { 4800acf3e2dSflorian struct ifreq ifr; 4810acf3e2dSflorian 4829bdef370Sflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 4838c4e2bd5Sflorian if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) { 4848c4e2bd5Sflorian log_warn("SIOCGIFXFLAGS"); 4858c4e2bd5Sflorian return -1; 4868c4e2bd5Sflorian } 4870acf3e2dSflorian return ifr.ifr_flags; 4880acf3e2dSflorian } 4890acf3e2dSflorian 490c966f6c0Sflorian int 491c966f6c0Sflorian get_ifrdomain(char *if_name) 492c966f6c0Sflorian { 493c966f6c0Sflorian struct ifreq ifr; 494c966f6c0Sflorian 4959bdef370Sflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 496c966f6c0Sflorian if (ioctl(ioctlsock, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) { 497c966f6c0Sflorian log_warn("SIOCGIFRDOMAIN"); 498c966f6c0Sflorian return -1; 499c966f6c0Sflorian } 500c966f6c0Sflorian return ifr.ifr_rdomainid; 501c966f6c0Sflorian } 502c966f6c0Sflorian 5030acf3e2dSflorian void 5040acf3e2dSflorian update_iface(uint32_t if_index, char* if_name) 5050acf3e2dSflorian { 506dd19964dSflorian struct iface *iface; 5078a8bcb8bSflorian struct ifaddrs *ifap, *ifa; 5080acf3e2dSflorian struct imsg_ifinfo imsg_ifinfo; 5098a8bcb8bSflorian struct sockaddr_dl *sdl; 5108a8bcb8bSflorian struct sockaddr_in6 *sin6; 51111cc0cd9Sflorian struct in6_ifreq ifr6; 512c966f6c0Sflorian int flags, xflags, ifrdomain; 5130acf3e2dSflorian 5148c4e2bd5Sflorian if ((flags = get_flags(if_name)) == -1 || (xflags = 515dd19964dSflorian get_xflags(if_name)) == -1) 5168c4e2bd5Sflorian return; 5170acf3e2dSflorian 518c6384676Sflorian if (!(xflags & (IFXF_AUTOCONF6 | IFXF_AUTOCONF6TEMP))) 5190acf3e2dSflorian return; 5200acf3e2dSflorian 521dd19964dSflorian if ((ifrdomain = get_ifrdomain(if_name)) == -1) 522dd19964dSflorian return; 523dd19964dSflorian 524d4a6ab96Sflorian iface = get_iface_by_id(if_index); 525d4a6ab96Sflorian 526d4a6ab96Sflorian if (iface != NULL) { 527d4a6ab96Sflorian if (iface->rdomain != ifrdomain) { 528eaf8d593Sflorian unref_icmp6ev(iface); 529d4a6ab96Sflorian iface->rdomain = ifrdomain; 530d4a6ab96Sflorian iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain); 531d4a6ab96Sflorian } 532d4a6ab96Sflorian } else { 533dd19964dSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 534dd19964dSflorian fatal("calloc"); 535dd19964dSflorian iface->if_index = if_index; 536dd19964dSflorian iface->rdomain = ifrdomain; 537d4a6ab96Sflorian iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain); 53811cc0cd9Sflorian iface->ll_tentative = 1; 539dd19964dSflorian 540dd19964dSflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 541dd19964dSflorian } 542dd19964dSflorian 5430acf3e2dSflorian memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo)); 5440acf3e2dSflorian 5450acf3e2dSflorian imsg_ifinfo.if_index = if_index; 546dd19964dSflorian imsg_ifinfo.rdomain = ifrdomain; 5470acf3e2dSflorian imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | 5480acf3e2dSflorian IFF_RUNNING); 549c6384676Sflorian imsg_ifinfo.autoconf = (xflags & IFXF_AUTOCONF6); 550804ba004Sflorian imsg_ifinfo.temporary = (xflags & IFXF_AUTOCONF6TEMP); 551b9b376fbSflorian imsg_ifinfo.soii = !(xflags & IFXF_INET6_NOSOII); 5520acf3e2dSflorian 5538a8bcb8bSflorian if (getifaddrs(&ifap) != 0) 5548a8bcb8bSflorian fatal("getifaddrs"); 5558a8bcb8bSflorian 5568a8bcb8bSflorian for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 5578a8bcb8bSflorian if (strcmp(if_name, ifa->ifa_name) != 0) 5588a8bcb8bSflorian continue; 5598a8bcb8bSflorian if (ifa->ifa_addr == NULL) 5608a8bcb8bSflorian continue; 5618a8bcb8bSflorian 5628a8bcb8bSflorian switch(ifa->ifa_addr->sa_family) { 5638a8bcb8bSflorian case AF_LINK: 5648a8bcb8bSflorian imsg_ifinfo.link_state = 5658a8bcb8bSflorian ((struct if_data *)ifa->ifa_data)->ifi_link_state; 5668a8bcb8bSflorian sdl = (struct sockaddr_dl *)ifa->ifa_addr; 5678a8bcb8bSflorian if (sdl->sdl_type != IFT_ETHER || 5688a8bcb8bSflorian sdl->sdl_alen != ETHER_ADDR_LEN) 5698a8bcb8bSflorian continue; 5708a8bcb8bSflorian memcpy(iface->hw_address.ether_addr_octet, 5718a8bcb8bSflorian LLADDR(sdl), ETHER_ADDR_LEN); 5728a8bcb8bSflorian memcpy(imsg_ifinfo.hw_address.ether_addr_octet, 5738a8bcb8bSflorian LLADDR(sdl), ETHER_ADDR_LEN); 5748a8bcb8bSflorian case AF_INET6: 5758a8bcb8bSflorian sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; 5768a8bcb8bSflorian #ifdef __KAME__ 5778a8bcb8bSflorian if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) && 5788a8bcb8bSflorian sin6->sin6_scope_id == 0) { 5798a8bcb8bSflorian sin6->sin6_scope_id = ntohs(*(u_int16_t *) 5808a8bcb8bSflorian &sin6->sin6_addr.s6_addr[2]); 5818a8bcb8bSflorian sin6->sin6_addr.s6_addr[2] = 5828a8bcb8bSflorian sin6->sin6_addr.s6_addr[3] = 0; 5838a8bcb8bSflorian } 5848a8bcb8bSflorian #endif 58511cc0cd9Sflorian if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 5868a8bcb8bSflorian memcpy(&imsg_ifinfo.ll_address, sin6, 5878a8bcb8bSflorian sizeof(imsg_ifinfo.ll_address)); 58811cc0cd9Sflorian 58911cc0cd9Sflorian if (!iface->ll_tentative) 59011cc0cd9Sflorian break; 59111cc0cd9Sflorian 59211cc0cd9Sflorian memset(&ifr6, 0, sizeof(ifr6)); 59311cc0cd9Sflorian strlcpy(ifr6.ifr_name, if_name, 59411cc0cd9Sflorian sizeof(ifr6.ifr_name)); 59511cc0cd9Sflorian memcpy(&ifr6.ifr_addr, sin6, 59611cc0cd9Sflorian sizeof(ifr6.ifr_addr)); 59711cc0cd9Sflorian 59811cc0cd9Sflorian if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, 59911cc0cd9Sflorian (caddr_t)&ifr6) == -1) { 60011cc0cd9Sflorian log_warn("SIOCGIFAFLAG_IN6"); 60111cc0cd9Sflorian break; 60211cc0cd9Sflorian } 60311cc0cd9Sflorian 60411cc0cd9Sflorian if (!(ifr6.ifr_ifru.ifru_flags6 & 60511cc0cd9Sflorian IN6_IFF_TENTATIVE)) { 60611cc0cd9Sflorian iface->ll_tentative = 0; 60711cc0cd9Sflorian if (iface->send_solicitation) 60811cc0cd9Sflorian send_solicitation( 60911cc0cd9Sflorian iface->if_index); 61011cc0cd9Sflorian } 61111cc0cd9Sflorian } 6128a8bcb8bSflorian break; 6138a8bcb8bSflorian default: 6148a8bcb8bSflorian break; 6158a8bcb8bSflorian } 6168a8bcb8bSflorian } 6178a8bcb8bSflorian 6188a8bcb8bSflorian freeifaddrs(ifap); 619dd19964dSflorian 6208653da7cSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &imsg_ifinfo, 6210acf3e2dSflorian sizeof(imsg_ifinfo)); 6220acf3e2dSflorian } 6230acf3e2dSflorian 624060a7308Sflorian #ifndef SMALL 62505b87f88Sflorian const char* 62605b87f88Sflorian flags_to_str(int flags) 62705b87f88Sflorian { 62805b87f88Sflorian static char buf[sizeof(" anycast tentative duplicated detached " 629804ba004Sflorian "deprecated autoconf temporary")]; 63005b87f88Sflorian 63105b87f88Sflorian buf[0] = '\0'; 63205b87f88Sflorian if (flags & IN6_IFF_ANYCAST) 6339bdef370Sflorian strlcat(buf, " anycast", sizeof(buf)); 63405b87f88Sflorian if (flags & IN6_IFF_TENTATIVE) 6359bdef370Sflorian strlcat(buf, " tentative", sizeof(buf)); 63605b87f88Sflorian if (flags & IN6_IFF_DUPLICATED) 6379bdef370Sflorian strlcat(buf, " duplicated", sizeof(buf)); 63805b87f88Sflorian if (flags & IN6_IFF_DETACHED) 6399bdef370Sflorian strlcat(buf, " detached", sizeof(buf)); 64005b87f88Sflorian if (flags & IN6_IFF_DEPRECATED) 6419bdef370Sflorian strlcat(buf, " deprecated", sizeof(buf)); 64205b87f88Sflorian if (flags & IN6_IFF_AUTOCONF) 6439bdef370Sflorian strlcat(buf, " autoconf", sizeof(buf)); 644c99d424eSflorian if (flags & IN6_IFF_TEMPORARY) 645804ba004Sflorian strlcat(buf, " temporary", sizeof(buf)); 64605b87f88Sflorian 64705b87f88Sflorian return (buf); 64805b87f88Sflorian } 649060a7308Sflorian #endif /* SMALL */ 650060a7308Sflorian 6510acf3e2dSflorian void 6520acf3e2dSflorian frontend_startup(void) 6530acf3e2dSflorian { 6540acf3e2dSflorian struct if_nameindex *ifnidxp, *ifnidx; 6550acf3e2dSflorian 656f9631f15Sflorian if (!event_initialized(&ev_route)) 657f9631f15Sflorian fatalx("%s: did not receive a route socket from the main " 658f9631f15Sflorian "process", __func__); 659f9631f15Sflorian 6600acf3e2dSflorian event_add(&ev_route, NULL); 661f9631f15Sflorian 6620acf3e2dSflorian if ((ifnidxp = if_nameindex()) == NULL) 6630acf3e2dSflorian fatalx("if_nameindex"); 6640acf3e2dSflorian 6650acf3e2dSflorian for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL; 6665a030e60Sflorian ifnidx++) 6670acf3e2dSflorian update_iface(ifnidx->if_index, ifnidx->if_name); 6680acf3e2dSflorian 6690acf3e2dSflorian if_freenameindex(ifnidxp); 6700acf3e2dSflorian } 6710acf3e2dSflorian 6720acf3e2dSflorian void 6730acf3e2dSflorian route_receive(int fd, short events, void *arg) 6740acf3e2dSflorian { 675d18b479bSotto static uint8_t *buf; 6760acf3e2dSflorian 677d18b479bSotto struct rt_msghdr *rtm; 6780acf3e2dSflorian struct sockaddr *sa, *rti_info[RTAX_MAX]; 6790acf3e2dSflorian ssize_t n; 6800acf3e2dSflorian 681d18b479bSotto if (buf == NULL) { 682d18b479bSotto buf = malloc(ROUTE_SOCKET_BUF_SIZE); 683d18b479bSotto if (buf == NULL) 684d18b479bSotto fatal("malloc"); 685d18b479bSotto } 686d18b479bSotto rtm = (struct rt_msghdr *)buf; 687d18b479bSotto if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { 6880acf3e2dSflorian if (errno == EAGAIN || errno == EINTR) 6890acf3e2dSflorian return; 6900acf3e2dSflorian log_warn("dispatch_rtmsg: read error"); 6910acf3e2dSflorian return; 6920acf3e2dSflorian } 6930acf3e2dSflorian 694be7530aaSflorian if (n == 0) 695be7530aaSflorian fatal("routing socket closed"); 696be7530aaSflorian 697128a03f8Sotto if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { 698d18b479bSotto log_warnx("partial rtm of %zd in buffer", n); 6990acf3e2dSflorian return; 7000acf3e2dSflorian } 7010acf3e2dSflorian 7020acf3e2dSflorian if (rtm->rtm_version != RTM_VERSION) 703be7530aaSflorian return; 7040acf3e2dSflorian 705be7530aaSflorian sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); 7060acf3e2dSflorian get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 7070acf3e2dSflorian 7080acf3e2dSflorian handle_route_message(rtm, rti_info); 7090acf3e2dSflorian } 7100acf3e2dSflorian 7110acf3e2dSflorian void 7120acf3e2dSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) 7130acf3e2dSflorian { 7140acf3e2dSflorian struct if_msghdr *ifm; 715c932786dSflorian struct if_announcemsghdr *ifan; 7160acf3e2dSflorian struct imsg_del_addr del_addr; 717d3ff3477Sflorian struct imsg_del_route del_route; 71805b87f88Sflorian struct imsg_dup_addr dup_addr; 7190acf3e2dSflorian struct sockaddr_rtlabel *rl; 72005b87f88Sflorian struct sockaddr_in6 *sin6; 72105b87f88Sflorian struct in6_ifreq ifr6; 722d3ff3477Sflorian struct in6_addr *in6; 7234b1b7c91Sflorian int xflags, if_index; 7240acf3e2dSflorian char ifnamebuf[IFNAMSIZ]; 7250acf3e2dSflorian char *if_name; 7260acf3e2dSflorian 7270acf3e2dSflorian switch (rtm->rtm_type) { 7280acf3e2dSflorian case RTM_IFINFO: 7290acf3e2dSflorian ifm = (struct if_msghdr *)rtm; 7300acf3e2dSflorian if_index = ifm->ifm_index; 731d0c2b6e3Sflorian if_name = if_indextoname(if_index, ifnamebuf); 732d0c2b6e3Sflorian if (if_name == NULL) { 733d0c2b6e3Sflorian log_debug("RTM_IFINFO: lost if %d", if_index); 7340acf3e2dSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 7350acf3e2dSflorian &if_index, sizeof(if_index)); 736c932786dSflorian remove_iface(if_index); 737d0c2b6e3Sflorian break; 738d0c2b6e3Sflorian } 7390acf3e2dSflorian xflags = get_xflags(if_name); 740c6384676Sflorian if (xflags == -1 || !(xflags & (IFXF_AUTOCONF6 | 741c6384676Sflorian IFXF_AUTOCONF6TEMP))) { 7425a030e60Sflorian log_debug("RTM_IFINFO: %s(%d) no(longer) autoconf6", 7435a030e60Sflorian if_name, if_index); 7440acf3e2dSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 7450acf3e2dSflorian 0, &if_index, sizeof(if_index)); 746dd19964dSflorian remove_iface(if_index); 747060a7308Sflorian } else { 748d0c2b6e3Sflorian update_iface(if_index, if_name); 749060a7308Sflorian } 7500acf3e2dSflorian break; 751c932786dSflorian case RTM_IFANNOUNCE: 752c932786dSflorian ifan = (struct if_announcemsghdr *)rtm; 753c932786dSflorian if_index = ifan->ifan_index; 754c932786dSflorian if (ifan->ifan_what == IFAN_DEPARTURE) { 755c932786dSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 756c932786dSflorian &if_index, sizeof(if_index)); 757c932786dSflorian remove_iface(if_index); 758c932786dSflorian } 759c932786dSflorian break; 7600acf3e2dSflorian case RTM_NEWADDR: 7610acf3e2dSflorian ifm = (struct if_msghdr *)rtm; 762d0c2b6e3Sflorian if_index = ifm->ifm_index; 763d0c2b6e3Sflorian if_name = if_indextoname(if_index, ifnamebuf); 7647f8b5cc9Sflorian if (if_name == NULL) { 7657f8b5cc9Sflorian log_debug("RTM_NEWADDR: lost if %d", if_index); 7667f8b5cc9Sflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 7677f8b5cc9Sflorian &if_index, sizeof(if_index)); 7687f8b5cc9Sflorian remove_iface(if_index); 7697f8b5cc9Sflorian break; 7707f8b5cc9Sflorian } 7717f8b5cc9Sflorian 772d0c2b6e3Sflorian log_debug("RTM_NEWADDR: %s[%u]", if_name, if_index); 773d0c2b6e3Sflorian update_iface(if_index, if_name); 7740acf3e2dSflorian break; 7750acf3e2dSflorian case RTM_DELADDR: 7760acf3e2dSflorian ifm = (struct if_msghdr *)rtm; 777d0c2b6e3Sflorian if_index = ifm->ifm_index; 778d0c2b6e3Sflorian if_name = if_indextoname(if_index, ifnamebuf); 7797f8b5cc9Sflorian if (if_name == NULL) { 7807f8b5cc9Sflorian log_debug("RTM_DELADDR: lost if %d", if_index); 7817f8b5cc9Sflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 7827f8b5cc9Sflorian &if_index, sizeof(if_index)); 7837f8b5cc9Sflorian remove_iface(if_index); 7847f8b5cc9Sflorian break; 7857f8b5cc9Sflorian } 7860acf3e2dSflorian if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family 7870acf3e2dSflorian == AF_INET6) { 788d0c2b6e3Sflorian del_addr.if_index = if_index; 7890acf3e2dSflorian memcpy(&del_addr.addr, rti_info[RTAX_IFA], sizeof( 7900acf3e2dSflorian del_addr.addr)); 7910acf3e2dSflorian frontend_imsg_compose_engine(IMSG_DEL_ADDRESS, 7920acf3e2dSflorian 0, 0, &del_addr, sizeof(del_addr)); 793d0c2b6e3Sflorian log_debug("RTM_DELADDR: %s[%u]", if_name, if_index); 7940acf3e2dSflorian } 7950acf3e2dSflorian break; 79605b87f88Sflorian case RTM_CHGADDRATTR: 79705b87f88Sflorian ifm = (struct if_msghdr *)rtm; 798d0c2b6e3Sflorian if_index = ifm->ifm_index; 799d0c2b6e3Sflorian if_name = if_indextoname(if_index, ifnamebuf); 8007f8b5cc9Sflorian if (if_name == NULL) { 8017f8b5cc9Sflorian log_debug("RTM_CHGADDRATTR: lost if %d", if_index); 8027f8b5cc9Sflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 8037f8b5cc9Sflorian &if_index, sizeof(if_index)); 8047f8b5cc9Sflorian remove_iface(if_index); 8057f8b5cc9Sflorian break; 8067f8b5cc9Sflorian } 80705b87f88Sflorian if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family 80805b87f88Sflorian == AF_INET6) { 80905b87f88Sflorian sin6 = (struct sockaddr_in6 *) rti_info[RTAX_IFA]; 81005b87f88Sflorian 81111cc0cd9Sflorian if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 81211cc0cd9Sflorian update_iface(if_index, if_name); 81305b87f88Sflorian break; 81411cc0cd9Sflorian } 81505b87f88Sflorian 81605b87f88Sflorian memset(&ifr6, 0, sizeof(ifr6)); 8179bdef370Sflorian strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); 81805b87f88Sflorian memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); 81905b87f88Sflorian 82005b87f88Sflorian if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6) 821df69c215Sderaadt == -1) { 82205b87f88Sflorian log_warn("SIOCGIFAFLAG_IN6"); 82305b87f88Sflorian break; 82405b87f88Sflorian } 82505b87f88Sflorian 82605b87f88Sflorian #ifndef SMALL 82705b87f88Sflorian log_debug("RTM_CHGADDRATTR: %s - %s", 82805b87f88Sflorian sin6_to_str(sin6), 82905b87f88Sflorian flags_to_str(ifr6.ifr_ifru.ifru_flags6)); 83005b87f88Sflorian #endif /* SMALL */ 83105b87f88Sflorian 83205b87f88Sflorian if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED) { 833d0c2b6e3Sflorian dup_addr.if_index = if_index; 83405b87f88Sflorian dup_addr.addr = *sin6; 83505b87f88Sflorian frontend_imsg_compose_engine(IMSG_DUP_ADDRESS, 83605b87f88Sflorian 0, 0, &dup_addr, sizeof(dup_addr)); 83705b87f88Sflorian } 83805b87f88Sflorian 83905b87f88Sflorian } 84005b87f88Sflorian break; 841d3ff3477Sflorian case RTM_DELETE: 842d3ff3477Sflorian ifm = (struct if_msghdr *)rtm; 843d3ff3477Sflorian if ((rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY | RTA_LABEL)) != 844d3ff3477Sflorian (RTA_DST | RTA_GATEWAY | RTA_LABEL)) 845d3ff3477Sflorian break; 846d3ff3477Sflorian if (rti_info[RTAX_DST]->sa_family != AF_INET6) 847d3ff3477Sflorian break; 848d3ff3477Sflorian if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) 849d3ff3477Sflorian rti_info[RTAX_DST])->sin6_addr)) 850d3ff3477Sflorian break; 851d3ff3477Sflorian if (rti_info[RTAX_GATEWAY]->sa_family != AF_INET6) 852d3ff3477Sflorian break; 853d3ff3477Sflorian if (rti_info[RTAX_LABEL]->sa_len != 854d3ff3477Sflorian sizeof(struct sockaddr_rtlabel)) 855d3ff3477Sflorian break; 856d3ff3477Sflorian 857d3ff3477Sflorian rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL]; 858d3ff3477Sflorian if (strcmp(rl->sr_label, SLAACD_RTA_LABEL) != 0) 859d3ff3477Sflorian break; 860d0c2b6e3Sflorian if_index = ifm->ifm_index; 861d0c2b6e3Sflorian if_name = if_indextoname(if_index, ifnamebuf); 8627f8b5cc9Sflorian if (if_name == NULL) { 8637f8b5cc9Sflorian log_debug("RTM_DELETE: lost if %d", if_index); 8647f8b5cc9Sflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 8657f8b5cc9Sflorian &if_index, sizeof(if_index)); 8667f8b5cc9Sflorian remove_iface(if_index); 8677f8b5cc9Sflorian break; 8687f8b5cc9Sflorian } 869d3ff3477Sflorian 870d0c2b6e3Sflorian del_route.if_index = if_index; 871d3ff3477Sflorian memcpy(&del_route.gw, rti_info[RTAX_GATEWAY], 872d3ff3477Sflorian sizeof(del_route.gw)); 873d3ff3477Sflorian in6 = &del_route.gw.sin6_addr; 874939e6f5eSflorian #ifdef __KAME__ 875d3ff3477Sflorian /* XXX from route(8) p_sockaddr() */ 876a5c74c4bSflorian if ((IN6_IS_ADDR_LINKLOCAL(in6) || 877d3ff3477Sflorian IN6_IS_ADDR_MC_LINKLOCAL(in6) || 878a5c74c4bSflorian IN6_IS_ADDR_MC_INTFACELOCAL(in6)) && 879a5c74c4bSflorian del_route.gw.sin6_scope_id == 0) { 880d3ff3477Sflorian del_route.gw.sin6_scope_id = 881d3ff3477Sflorian (u_int32_t)ntohs(*(u_short *) &in6->s6_addr[2]); 882d3ff3477Sflorian *(u_short *)&in6->s6_addr[2] = 0; 883d3ff3477Sflorian } 884939e6f5eSflorian #endif 885d3ff3477Sflorian frontend_imsg_compose_engine(IMSG_DEL_ROUTE, 886d3ff3477Sflorian 0, 0, &del_route, sizeof(del_route)); 887d3ff3477Sflorian log_debug("RTM_DELETE: %s[%u]", if_name, 888d3ff3477Sflorian ifm->ifm_index); 889d3ff3477Sflorian 890d3ff3477Sflorian break; 89134435150Sflorian #ifndef SMALL 89234435150Sflorian case RTM_PROPOSAL: 89334435150Sflorian if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { 89434435150Sflorian log_debug("RTP_PROPOSAL_SOLICIT"); 89534435150Sflorian frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS, 89634435150Sflorian 0, 0, NULL, 0); 89734435150Sflorian } 89834435150Sflorian break; 89934435150Sflorian #endif /* SMALL */ 9000acf3e2dSflorian default: 9010acf3e2dSflorian log_debug("unexpected RTM: %d", rtm->rtm_type); 9020acf3e2dSflorian break; 9030acf3e2dSflorian } 9040acf3e2dSflorian 9050acf3e2dSflorian } 9060acf3e2dSflorian 9070acf3e2dSflorian #define ROUNDUP(a) \ 908064707deSfriehm ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 9090acf3e2dSflorian 9100acf3e2dSflorian void 9110acf3e2dSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 9120acf3e2dSflorian { 9130acf3e2dSflorian int i; 9140acf3e2dSflorian 9150acf3e2dSflorian for (i = 0; i < RTAX_MAX; i++) { 9160acf3e2dSflorian if (addrs & (1 << i)) { 9170acf3e2dSflorian rti_info[i] = sa; 9180acf3e2dSflorian sa = (struct sockaddr *)((char *)(sa) + 9190acf3e2dSflorian ROUNDUP(sa->sa_len)); 9200acf3e2dSflorian } else 9210acf3e2dSflorian rti_info[i] = NULL; 9220acf3e2dSflorian } 9230acf3e2dSflorian } 9240acf3e2dSflorian 9250acf3e2dSflorian void 9260acf3e2dSflorian icmp6_receive(int fd, short events, void *arg) 9270acf3e2dSflorian { 9280acf3e2dSflorian struct imsg_ra ra; 929dd19964dSflorian struct icmp6_hdr *icmp6_hdr; 930dd19964dSflorian struct icmp6_ev *icmp6ev; 9310acf3e2dSflorian struct in6_pktinfo *pi = NULL; 9320acf3e2dSflorian struct cmsghdr *cm; 9330acf3e2dSflorian ssize_t len; 9340acf3e2dSflorian int if_index = 0, *hlimp = NULL; 935e56e9441Skn char ntopbuf[INET6_ADDRSTRLEN]; 936e56e9441Skn #ifndef SMALL 937e56e9441Skn char ifnamebuf[IFNAMSIZ]; 938e56e9441Skn #endif /* SMALL */ 9390acf3e2dSflorian 940dd19964dSflorian icmp6ev = arg; 941dd19964dSflorian if ((len = recvmsg(fd, &icmp6ev->rcvmhdr, 0)) == -1) { 9420acf3e2dSflorian log_warn("recvmsg"); 9430acf3e2dSflorian return; 9440acf3e2dSflorian } 9450acf3e2dSflorian 94612ce2480Ssemarie if ((size_t)len < sizeof(struct icmp6_hdr)) 94712ce2480Ssemarie return; 94812ce2480Ssemarie 94912ce2480Ssemarie icmp6_hdr = (struct icmp6_hdr *)icmp6ev->answer; 95012ce2480Ssemarie if (icmp6_hdr->icmp6_type != ND_ROUTER_ADVERT) 95112ce2480Ssemarie return; 95212ce2480Ssemarie 9530acf3e2dSflorian /* extract optional information via Advanced API */ 954dd19964dSflorian for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&icmp6ev->rcvmhdr); cm; 955dd19964dSflorian cm = (struct cmsghdr *)CMSG_NXTHDR(&icmp6ev->rcvmhdr, cm)) { 9560acf3e2dSflorian if (cm->cmsg_level == IPPROTO_IPV6 && 9570acf3e2dSflorian cm->cmsg_type == IPV6_PKTINFO && 9580acf3e2dSflorian cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 9590acf3e2dSflorian pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 9600acf3e2dSflorian if_index = pi->ipi6_ifindex; 9610acf3e2dSflorian } 9620acf3e2dSflorian if (cm->cmsg_level == IPPROTO_IPV6 && 9630acf3e2dSflorian cm->cmsg_type == IPV6_HOPLIMIT && 9640acf3e2dSflorian cm->cmsg_len == CMSG_LEN(sizeof(int))) 9650acf3e2dSflorian hlimp = (int *)CMSG_DATA(cm); 9660acf3e2dSflorian } 9670acf3e2dSflorian 9680acf3e2dSflorian if (if_index == 0) { 9690acf3e2dSflorian log_warnx("failed to get receiving interface"); 9700acf3e2dSflorian return; 9710acf3e2dSflorian } 9720acf3e2dSflorian 9730acf3e2dSflorian if (hlimp == NULL) { 9740acf3e2dSflorian log_warnx("failed to get receiving hop limit"); 9750acf3e2dSflorian return; 9760acf3e2dSflorian } 9770acf3e2dSflorian 9780acf3e2dSflorian if (*hlimp != 255) { 9792ac62a75Sflorian log_warnx("invalid RA with hop limit of %d from %s on %s", 980dd19964dSflorian *hlimp, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr, 9810acf3e2dSflorian ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index, 9820acf3e2dSflorian ifnamebuf)); 9830acf3e2dSflorian return; 9840acf3e2dSflorian } 9850acf3e2dSflorian 9860acf3e2dSflorian if ((size_t)len > sizeof(ra.packet)) { 9872ac62a75Sflorian log_warnx("invalid RA with size %ld from %s on %s", 988dd19964dSflorian len, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr, 9890acf3e2dSflorian ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index, 9900acf3e2dSflorian ifnamebuf)); 9910acf3e2dSflorian return; 9920acf3e2dSflorian } 9930acf3e2dSflorian ra.if_index = if_index; 994dd19964dSflorian memcpy(&ra.from, &icmp6ev->from, sizeof(ra.from)); 9950acf3e2dSflorian ra.len = len; 996dd19964dSflorian memcpy(ra.packet, icmp6ev->answer, len); 9970acf3e2dSflorian 9980acf3e2dSflorian frontend_imsg_compose_engine(IMSG_RA, 0, 0, &ra, sizeof(ra)); 9990acf3e2dSflorian } 10000acf3e2dSflorian 10010acf3e2dSflorian void 10020acf3e2dSflorian send_solicitation(uint32_t if_index) 10030acf3e2dSflorian { 10040acf3e2dSflorian struct in6_pktinfo *pi; 10050acf3e2dSflorian struct cmsghdr *cm; 1006dd19964dSflorian struct iface *iface; 10070acf3e2dSflorian 10080acf3e2dSflorian log_debug("%s(%u)", __func__, if_index); 10090acf3e2dSflorian 1010dd19964dSflorian if ((iface = get_iface_by_id(if_index)) == NULL) 1011411eeab6Sflorian return; 1012411eeab6Sflorian 1013dd19964dSflorian if (!event_initialized(&iface->icmp6ev->ev)) { 1014dd19964dSflorian iface->send_solicitation = 1; 1015dd19964dSflorian return; 101611cc0cd9Sflorian } else if (iface->ll_tentative) { 101711cc0cd9Sflorian iface->send_solicitation = 1; 101811cc0cd9Sflorian return; 1019dd19964dSflorian } 1020411eeab6Sflorian 102111cc0cd9Sflorian iface->send_solicitation = 0; 102211cc0cd9Sflorian 10230acf3e2dSflorian dst.sin6_scope_id = if_index; 10240acf3e2dSflorian 10250acf3e2dSflorian cm = CMSG_FIRSTHDR(&sndmhdr); 10260acf3e2dSflorian pi = (struct in6_pktinfo *)CMSG_DATA(cm); 10270acf3e2dSflorian pi->ipi6_ifindex = if_index; 10280acf3e2dSflorian 1029dd19964dSflorian memcpy(&nd_opt_source_link_addr, &iface->hw_address, 1030dd19964dSflorian sizeof(nd_opt_source_link_addr)); 1031dd19964dSflorian 1032dd19964dSflorian if (sendmsg(EVENT_FD(&iface->icmp6ev->ev), &sndmhdr, 0) != sizeof(rs) + 10330acf3e2dSflorian sizeof(nd_opt_hdr) + sizeof(nd_opt_source_link_addr)) 10340acf3e2dSflorian log_warn("sendmsg"); 10350acf3e2dSflorian } 1036dd19964dSflorian 1037dd19964dSflorian struct iface* 1038dd19964dSflorian get_iface_by_id(uint32_t if_index) 1039dd19964dSflorian { 1040dd19964dSflorian struct iface *iface; 1041dd19964dSflorian 1042dd19964dSflorian LIST_FOREACH (iface, &interfaces, entries) { 1043dd19964dSflorian if (iface->if_index == if_index) 1044dd19964dSflorian return (iface); 1045dd19964dSflorian } 1046dd19964dSflorian 1047dd19964dSflorian return (NULL); 1048dd19964dSflorian } 1049dd19964dSflorian 1050dd19964dSflorian void 1051dd19964dSflorian remove_iface(uint32_t if_index) 1052dd19964dSflorian { 1053dd19964dSflorian struct iface *iface; 1054dd19964dSflorian 1055dd19964dSflorian iface = get_iface_by_id(if_index); 1056dd19964dSflorian 1057dd19964dSflorian if (iface == NULL) 1058dd19964dSflorian return; 1059dd19964dSflorian 1060dd19964dSflorian LIST_REMOVE(iface, entries); 1061dd19964dSflorian 1062eaf8d593Sflorian unref_icmp6ev(iface); 1063dd19964dSflorian free(iface); 1064dd19964dSflorian } 1065dd19964dSflorian 1066dd19964dSflorian struct icmp6_ev* 1067dd19964dSflorian get_icmp6ev_by_rdomain(int rdomain) 1068dd19964dSflorian { 1069dd19964dSflorian struct iface *iface; 1070d4a6ab96Sflorian struct icmp6_ev *icmp6ev = NULL; 1071dd19964dSflorian 1072dd19964dSflorian LIST_FOREACH (iface, &interfaces, entries) { 1073d4a6ab96Sflorian if (iface->rdomain == rdomain) { 1074d4a6ab96Sflorian icmp6ev = iface->icmp6ev; 1075d4a6ab96Sflorian break; 1076d4a6ab96Sflorian } 1077dd19964dSflorian } 1078dd19964dSflorian 1079d4a6ab96Sflorian if (icmp6ev == NULL) { 1080d4a6ab96Sflorian if ((icmp6ev = calloc(1, sizeof(*icmp6ev))) == NULL) 1081d4a6ab96Sflorian fatal("calloc"); 1082d4a6ab96Sflorian icmp6ev->rcviov[0].iov_base = (caddr_t)icmp6ev->answer; 1083d4a6ab96Sflorian icmp6ev->rcviov[0].iov_len = sizeof(icmp6ev->answer); 1084d4a6ab96Sflorian icmp6ev->rcvmhdr.msg_name = (caddr_t)&icmp6ev->from; 1085d4a6ab96Sflorian icmp6ev->rcvmhdr.msg_namelen = sizeof(icmp6ev->from); 1086d4a6ab96Sflorian icmp6ev->rcvmhdr.msg_iov = icmp6ev->rcviov; 1087d4a6ab96Sflorian icmp6ev->rcvmhdr.msg_iovlen = 1; 1088d4a6ab96Sflorian icmp6ev->rcvmhdr.msg_controllen = 1089d4a6ab96Sflorian CMSG_SPACE(sizeof(struct in6_pktinfo)) + 1090d4a6ab96Sflorian CMSG_SPACE(sizeof(int)); 1091d4a6ab96Sflorian if ((icmp6ev->rcvmhdr.msg_control = malloc(icmp6ev-> 1092d4a6ab96Sflorian rcvmhdr.msg_controllen)) == NULL) 1093d4a6ab96Sflorian fatal("malloc"); 1094d4a6ab96Sflorian frontend_imsg_compose_main(IMSG_OPEN_ICMP6SOCK, 0, 1095d4a6ab96Sflorian &rdomain, sizeof(rdomain)); 1096d4a6ab96Sflorian } 1097d4a6ab96Sflorian icmp6ev->refcnt++; 1098d4a6ab96Sflorian return (icmp6ev); 1099dd19964dSflorian } 1100dd19964dSflorian 1101dd19964dSflorian void 1102eaf8d593Sflorian unref_icmp6ev(struct iface *iface) 1103eaf8d593Sflorian { 11049a9a88f5Snaddy struct icmp6_ev *icmp6ev = iface->icmp6ev; 11059a9a88f5Snaddy 1106c552023eSflorian iface->icmp6ev = NULL; 1107c552023eSflorian 11089a9a88f5Snaddy if (icmp6ev != NULL) { 11099a9a88f5Snaddy icmp6ev->refcnt--; 11109a9a88f5Snaddy if (icmp6ev->refcnt == 0) { 11119a9a88f5Snaddy event_del(&icmp6ev->ev); 11129a9a88f5Snaddy close(EVENT_FD(&icmp6ev->ev)); 11139a9a88f5Snaddy free(icmp6ev); 1114eaf8d593Sflorian } 1115eaf8d593Sflorian } 1116eaf8d593Sflorian } 1117eaf8d593Sflorian 1118eaf8d593Sflorian void 1119dd19964dSflorian set_icmp6sock(int icmp6sock, int rdomain) 1120dd19964dSflorian { 1121dd19964dSflorian struct iface *iface; 1122dd19964dSflorian 1123dd19964dSflorian LIST_FOREACH (iface, &interfaces, entries) { 1124dd19964dSflorian if (!event_initialized(&iface->icmp6ev->ev) && iface->rdomain 1125dd19964dSflorian == rdomain) { 1126dd19964dSflorian event_set(&iface->icmp6ev->ev, icmp6sock, EV_READ | 1127dd19964dSflorian EV_PERSIST, icmp6_receive, iface->icmp6ev); 1128dd19964dSflorian event_add(&iface->icmp6ev->ev, NULL); 1129dd19964dSflorian icmp6sock = -1; 1130dd19964dSflorian break; 1131dd19964dSflorian } 1132dd19964dSflorian } 1133dd19964dSflorian 1134909dc701Sflorian if (icmp6sock != -1) { 1135909dc701Sflorian /* 1136909dc701Sflorian * The interface disappeared or changed rdomain while we were 1137909dc701Sflorian * waiting for the parent process to open the raw socket. 1138909dc701Sflorian */ 1139909dc701Sflorian close(icmp6sock); 1140909dc701Sflorian return; 1141909dc701Sflorian } 1142dd19964dSflorian 1143dd19964dSflorian LIST_FOREACH (iface, &interfaces, entries) { 1144dd19964dSflorian if (event_initialized(&iface->icmp6ev->ev) && 114511cc0cd9Sflorian iface->send_solicitation) 1146dd19964dSflorian send_solicitation(iface->if_index); 1147dd19964dSflorian } 1148dd19964dSflorian } 1149