1*0e59d0d1Sclaudio /* $OpenBSD: engine.c,v 1.99 2024/11/21 13:35:20 claudio Exp $ */ 20acf3e2dSflorian 30acf3e2dSflorian /* 40acf3e2dSflorian * Copyright (c) 2017 Florian Obser <florian@openbsd.org> 50acf3e2dSflorian * Copyright (c) 2004, 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 220acf3e2dSflorian /* 230acf3e2dSflorian * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 240acf3e2dSflorian * All rights reserved. 250acf3e2dSflorian * 260acf3e2dSflorian * Redistribution and use in source and binary forms, with or without 270acf3e2dSflorian * modification, are permitted provided that the following conditions 280acf3e2dSflorian * are met: 290acf3e2dSflorian * 1. Redistributions of source code must retain the above copyright 300acf3e2dSflorian * notice, this list of conditions and the following disclaimer. 310acf3e2dSflorian * 2. Redistributions in binary form must reproduce the above copyright 320acf3e2dSflorian * notice, this list of conditions and the following disclaimer in the 330acf3e2dSflorian * documentation and/or other materials provided with the distribution. 340acf3e2dSflorian * 3. Neither the name of the project nor the names of its contributors 350acf3e2dSflorian * may be used to endorse or promote products derived from this software 360acf3e2dSflorian * without specific prior written permission. 370acf3e2dSflorian * 380acf3e2dSflorian * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 390acf3e2dSflorian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 400acf3e2dSflorian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 410acf3e2dSflorian * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 420acf3e2dSflorian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 430acf3e2dSflorian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 440acf3e2dSflorian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 450acf3e2dSflorian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 460acf3e2dSflorian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 470acf3e2dSflorian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 480acf3e2dSflorian * SUCH DAMAGE. 490acf3e2dSflorian */ 500acf3e2dSflorian 510acf3e2dSflorian #include <sys/types.h> 520acf3e2dSflorian #include <sys/queue.h> 530acf3e2dSflorian #include <sys/socket.h> 540acf3e2dSflorian #include <sys/syslog.h> 550acf3e2dSflorian #include <sys/uio.h> 560acf3e2dSflorian 570acf3e2dSflorian #include <net/if.h> 580acf3e2dSflorian #include <net/route.h> 590acf3e2dSflorian #include <arpa/inet.h> 600acf3e2dSflorian #include <netinet/in.h> 610acf3e2dSflorian #include <netinet/if_ether.h> 620acf3e2dSflorian #include <netinet/ip6.h> 630acf3e2dSflorian #include <netinet6/nd6.h> 640acf3e2dSflorian #include <netinet/icmp6.h> 650acf3e2dSflorian 66b9b376fbSflorian #include <crypto/sha2.h> 67b9b376fbSflorian 680acf3e2dSflorian #include <errno.h> 690acf3e2dSflorian #include <event.h> 700acf3e2dSflorian #include <imsg.h> 710acf3e2dSflorian #include <pwd.h> 720acf3e2dSflorian #include <signal.h> 730acf3e2dSflorian #include <stddef.h> 740acf3e2dSflorian #include <stdlib.h> 750acf3e2dSflorian #include <string.h> 760acf3e2dSflorian #include <time.h> 770acf3e2dSflorian #include <unistd.h> 780acf3e2dSflorian 790acf3e2dSflorian #include "log.h" 800acf3e2dSflorian #include "slaacd.h" 810acf3e2dSflorian #include "engine.h" 820acf3e2dSflorian 835f4648e4Sflorian #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 845f4648e4Sflorian 850acf3e2dSflorian #define MAX_RTR_SOLICITATION_DELAY 1 860acf3e2dSflorian #define MAX_RTR_SOLICITATION_DELAY_USEC MAX_RTR_SOLICITATION_DELAY * 1000000 870acf3e2dSflorian #define RTR_SOLICITATION_INTERVAL 4 880acf3e2dSflorian #define MAX_RTR_SOLICITATIONS 3 890acf3e2dSflorian 9079e3154eSflorian /* 91804ba004Sflorian * Constants for RFC 8981 temporary address extensions 9279e3154eSflorian * 9379e3154eSflorian * PRIV_PREFERRED_LIFETIME > (PRIV_MAX_DESYNC_FACTOR + PRIV_REGEN_ADVANCE) 9479e3154eSflorian */ 95ee610c8dSflorian #define PRIV_VALID_LIFETIME 172800 /* 2 days */ 96ee610c8dSflorian #define PRIV_PREFERRED_LIFETIME 86400 /* 1 day */ 9779e3154eSflorian #define PRIV_MAX_DESYNC_FACTOR 34560 /* PRIV_PREFERRED_LIFETIME * 0.4 */ 985f4648e4Sflorian #define PRIV_REGEN_ADVANCE 5 /* 5 seconds */ 994fec1b25Sflorian 1000acf3e2dSflorian enum if_state { 1010acf3e2dSflorian IF_DOWN, 1025a030e60Sflorian IF_INIT, 1035a030e60Sflorian IF_BOUND, 1040acf3e2dSflorian }; 1050acf3e2dSflorian 1060acf3e2dSflorian enum proposal_state { 1075a030e60Sflorian PROPOSAL_IF_DOWN, 1080acf3e2dSflorian PROPOSAL_NOT_CONFIGURED, 1090acf3e2dSflorian PROPOSAL_CONFIGURED, 1100acf3e2dSflorian PROPOSAL_NEARLY_EXPIRED, 1110acf3e2dSflorian PROPOSAL_WITHDRAWN, 11205b87f88Sflorian PROPOSAL_DUPLICATED, 113b64c4682Spamela PROPOSAL_STALE, 1140acf3e2dSflorian }; 1150acf3e2dSflorian 1160acf3e2dSflorian const char* rpref_name[] = { 1170acf3e2dSflorian "Low", 1180acf3e2dSflorian "Medium", 1190acf3e2dSflorian "High", 1200acf3e2dSflorian }; 1210acf3e2dSflorian 1220acf3e2dSflorian struct radv_prefix { 1230acf3e2dSflorian LIST_ENTRY(radv_prefix) entries; 1240acf3e2dSflorian struct in6_addr prefix; 1250acf3e2dSflorian uint8_t prefix_len; /*XXX int */ 1260acf3e2dSflorian int onlink; 1270acf3e2dSflorian int autonomous; 1280acf3e2dSflorian uint32_t vltime; 1290acf3e2dSflorian uint32_t pltime; 13005b87f88Sflorian int dad_counter; 1310acf3e2dSflorian }; 1320acf3e2dSflorian 1330acf3e2dSflorian struct radv_rdns { 1340acf3e2dSflorian LIST_ENTRY(radv_rdns) entries; 1350acf3e2dSflorian struct in6_addr rdns; 1360acf3e2dSflorian }; 1370acf3e2dSflorian 1380acf3e2dSflorian struct radv { 1390acf3e2dSflorian LIST_ENTRY(radv) entries; 1400acf3e2dSflorian struct sockaddr_in6 from; 1410acf3e2dSflorian struct timespec when; 1420acf3e2dSflorian struct timespec uptime; 1430acf3e2dSflorian struct event timer; 1440acf3e2dSflorian uint32_t min_lifetime; 1450acf3e2dSflorian uint8_t curhoplimit; 1460acf3e2dSflorian int managed; 1470acf3e2dSflorian int other; 1480acf3e2dSflorian enum rpref rpref; 1490acf3e2dSflorian uint16_t router_lifetime; /* in seconds */ 1500acf3e2dSflorian uint32_t reachable_time; /* in milliseconds */ 1510acf3e2dSflorian uint32_t retrans_time; /* in milliseconds */ 1520acf3e2dSflorian LIST_HEAD(, radv_prefix) prefixes; 1530acf3e2dSflorian uint32_t rdns_lifetime; 1540acf3e2dSflorian LIST_HEAD(, radv_rdns) rdns_servers; 1552d9ad356Sbket uint32_t mtu; 1560acf3e2dSflorian }; 1570acf3e2dSflorian 1580acf3e2dSflorian struct address_proposal { 1590acf3e2dSflorian LIST_ENTRY(address_proposal) entries; 1600acf3e2dSflorian struct event timer; 1610acf3e2dSflorian int64_t id; 1620acf3e2dSflorian enum proposal_state state; 1635a030e60Sflorian struct timeval timo; 1645f4648e4Sflorian struct timespec created; 1650acf3e2dSflorian struct timespec when; 1660acf3e2dSflorian struct timespec uptime; 1670acf3e2dSflorian uint32_t if_index; 1680acf3e2dSflorian struct ether_addr hw_address; 1695a030e60Sflorian struct sockaddr_in6 from; 1700acf3e2dSflorian struct sockaddr_in6 addr; 1710acf3e2dSflorian struct in6_addr mask; 1720acf3e2dSflorian struct in6_addr prefix; 173804ba004Sflorian int temporary; 1740acf3e2dSflorian uint8_t prefix_len; 1750acf3e2dSflorian uint32_t vltime; 1760acf3e2dSflorian uint32_t pltime; 17779e3154eSflorian uint32_t desync_factor; 178b9b376fbSflorian uint8_t soiikey[SLAACD_SOIIKEY_LEN]; 1792d9ad356Sbket uint32_t mtu; 1800acf3e2dSflorian }; 1810acf3e2dSflorian 1820acf3e2dSflorian struct dfr_proposal { 1830acf3e2dSflorian LIST_ENTRY(dfr_proposal) entries; 1840acf3e2dSflorian struct event timer; 1850acf3e2dSflorian int64_t id; 1860acf3e2dSflorian enum proposal_state state; 1875a030e60Sflorian struct timeval timo; 1880acf3e2dSflorian struct timespec when; 1890acf3e2dSflorian struct timespec uptime; 1900acf3e2dSflorian uint32_t if_index; 191dd19964dSflorian int rdomain; 1920acf3e2dSflorian struct sockaddr_in6 addr; 1930acf3e2dSflorian uint32_t router_lifetime; 1940acf3e2dSflorian enum rpref rpref; 1950acf3e2dSflorian }; 1960acf3e2dSflorian 19734435150Sflorian struct rdns_proposal { 19834435150Sflorian LIST_ENTRY(rdns_proposal) entries; 19934435150Sflorian struct event timer; 20034435150Sflorian int64_t id; 20134435150Sflorian enum proposal_state state; 2025a030e60Sflorian struct timeval timo; 20334435150Sflorian struct timespec when; 20434435150Sflorian struct timespec uptime; 20534435150Sflorian uint32_t if_index; 206dd19964dSflorian int rdomain; 20734435150Sflorian struct sockaddr_in6 from; 20834435150Sflorian int rdns_count; 20934435150Sflorian struct in6_addr rdns[MAX_RDNS_COUNT]; 21034435150Sflorian uint32_t rdns_lifetime; 21134435150Sflorian }; 21234435150Sflorian 2130acf3e2dSflorian struct slaacd_iface { 2140acf3e2dSflorian LIST_ENTRY(slaacd_iface) entries; 2150acf3e2dSflorian enum if_state state; 2160acf3e2dSflorian struct event timer; 2175a030e60Sflorian struct timeval timo; 2185a030e60Sflorian struct timespec last_sol; 2190acf3e2dSflorian int probes; 2200acf3e2dSflorian uint32_t if_index; 221dd19964dSflorian uint32_t rdomain; 2220acf3e2dSflorian int running; 223c6384676Sflorian int autoconf; 224804ba004Sflorian int temporary; 225b9b376fbSflorian int soii; 2260acf3e2dSflorian struct ether_addr hw_address; 2270acf3e2dSflorian struct sockaddr_in6 ll_address; 228b9b376fbSflorian uint8_t soiikey[SLAACD_SOIIKEY_LEN]; 22996316c8dSflorian int link_state; 2302d9ad356Sbket uint32_t cur_mtu; 2310acf3e2dSflorian LIST_HEAD(, radv) radvs; 2320acf3e2dSflorian LIST_HEAD(, address_proposal) addr_proposals; 2330acf3e2dSflorian LIST_HEAD(, dfr_proposal) dfr_proposals; 23434435150Sflorian LIST_HEAD(, rdns_proposal) rdns_proposals; 2350acf3e2dSflorian }; 2360acf3e2dSflorian 2370acf3e2dSflorian LIST_HEAD(, slaacd_iface) slaacd_interfaces; 2380acf3e2dSflorian 2390acf3e2dSflorian __dead void engine_shutdown(void); 2400acf3e2dSflorian void engine_sig_handler(int sig, short, void *); 2410acf3e2dSflorian void engine_dispatch_frontend(int, short, void *); 2420acf3e2dSflorian void engine_dispatch_main(int, short, void *); 243bf0ed931Sflorian #ifndef SMALL 2440acf3e2dSflorian void send_interface_info(struct slaacd_iface *, pid_t); 2459a7d784aSflorian void engine_showinfo_ctl(pid_t, uint32_t); 24686f19603Sflorian void debug_log_ra(struct imsg_ra *); 247060a7308Sflorian int in6_mask2prefixlen(struct in6_addr *); 24806d929a1Skn #endif /* SMALL */ 2490acf3e2dSflorian struct slaacd_iface *get_slaacd_iface_by_id(uint32_t); 2500acf3e2dSflorian void remove_slaacd_iface(uint32_t); 2510acf3e2dSflorian void free_ra(struct radv *); 2525a030e60Sflorian void iface_state_transition(struct slaacd_iface *, enum 2535a030e60Sflorian if_state); 2545a030e60Sflorian void addr_proposal_state_transition(struct 2555a030e60Sflorian address_proposal *, enum proposal_state); 2565a030e60Sflorian void dfr_proposal_state_transition(struct dfr_proposal *, 2575a030e60Sflorian enum proposal_state); 2585a030e60Sflorian void rdns_proposal_state_transition(struct rdns_proposal *, 2595a030e60Sflorian enum proposal_state); 260883c684aSflorian void engine_update_iface(struct imsg_ifinfo *); 2615a030e60Sflorian void request_solicitation(struct slaacd_iface *); 2620acf3e2dSflorian void parse_ra(struct slaacd_iface *, struct imsg_ra *); 2630acf3e2dSflorian void gen_addr(struct slaacd_iface *, struct radv_prefix *, 2640acf3e2dSflorian struct address_proposal *, int); 2650acf3e2dSflorian void gen_address_proposal(struct slaacd_iface *, struct 2660acf3e2dSflorian radv *, struct radv_prefix *, int); 2677455062cSflorian void free_address_proposal(struct address_proposal *); 268b64c4682Spamela void withdraw_addr(struct address_proposal *); 2690acf3e2dSflorian void configure_address(struct address_proposal *); 2700acf3e2dSflorian void in6_prefixlen2mask(struct in6_addr *, int len); 2710acf3e2dSflorian void gen_dfr_proposal(struct slaacd_iface *, struct 2720acf3e2dSflorian radv *); 2730acf3e2dSflorian void configure_dfr(struct dfr_proposal *); 2740acf3e2dSflorian void free_dfr_proposal(struct dfr_proposal *); 2750acf3e2dSflorian void withdraw_dfr(struct dfr_proposal *); 276a671359fSflorian void update_iface_ra_rdns(struct slaacd_iface *, 277a671359fSflorian struct radv *); 27834435150Sflorian void gen_rdns_proposal(struct slaacd_iface *, struct 27934435150Sflorian radv *); 28034435150Sflorian void free_rdns_proposal(struct rdns_proposal *); 2815a030e60Sflorian void withdraw_rdns(struct rdns_proposal *); 282dd19964dSflorian void compose_rdns_proposal(uint32_t, int); 2830acf3e2dSflorian void update_iface_ra(struct slaacd_iface *, struct radv *); 284cdd6ec5cSflorian void update_iface_ra_dfr(struct slaacd_iface *, 285cdd6ec5cSflorian struct radv *); 286881f44e2Sflorian void update_iface_ra_prefix(struct slaacd_iface *, 287881f44e2Sflorian struct radv *, struct radv_prefix *prefix); 2880acf3e2dSflorian void address_proposal_timeout(int, short, void *); 2890acf3e2dSflorian void dfr_proposal_timeout(int, short, void *); 29034435150Sflorian void rdns_proposal_timeout(int, short, void *); 2910acf3e2dSflorian void iface_timeout(int, short, void *); 2920acf3e2dSflorian struct radv *find_ra(struct slaacd_iface *, struct sockaddr_in6 *); 2930acf3e2dSflorian struct address_proposal *find_address_proposal_by_addr(struct slaacd_iface *, 2940acf3e2dSflorian struct sockaddr_in6 *); 295d3ff3477Sflorian struct dfr_proposal *find_dfr_proposal_by_gw(struct slaacd_iface *, 296d3ff3477Sflorian struct sockaddr_in6 *); 29734435150Sflorian struct rdns_proposal *find_rdns_proposal_by_gw(struct slaacd_iface *, 29834435150Sflorian struct sockaddr_in6 *); 2995a030e60Sflorian struct radv_prefix *find_prefix(struct radv *, struct in6_addr *, uint8_t); 3000acf3e2dSflorian int engine_imsg_compose_main(int, pid_t, void *, uint16_t); 3010acf3e2dSflorian uint32_t real_lifetime(struct timespec *, uint32_t); 30205b87f88Sflorian void merge_dad_couters(struct radv *, struct radv *); 3030acf3e2dSflorian 3045ebbfac0Sflorian static struct imsgev *iev_frontend; 3055ebbfac0Sflorian static struct imsgev *iev_main; 3060acf3e2dSflorian int64_t proposal_id; 3070acf3e2dSflorian 3085a030e60Sflorian 3095a030e60Sflorian #define CASE(x) case x : return #x 3105a030e60Sflorian 311f10889deSkn #ifndef SMALL 3125a030e60Sflorian static const char* 3135a030e60Sflorian if_state_name(enum if_state ifs) 3145a030e60Sflorian { 3155a030e60Sflorian switch (ifs) { 3165a030e60Sflorian CASE(IF_DOWN); 3175a030e60Sflorian CASE(IF_INIT); 3185a030e60Sflorian CASE(IF_BOUND); 3195a030e60Sflorian } 3205a030e60Sflorian } 3215a030e60Sflorian 3225a030e60Sflorian static const char* 3235a030e60Sflorian proposal_state_name(enum proposal_state ps) 3245a030e60Sflorian { 3255a030e60Sflorian switch (ps) { 3265a030e60Sflorian CASE(PROPOSAL_IF_DOWN); 3275a030e60Sflorian CASE(PROPOSAL_NOT_CONFIGURED); 3285a030e60Sflorian CASE(PROPOSAL_CONFIGURED); 3295a030e60Sflorian CASE(PROPOSAL_NEARLY_EXPIRED); 3305a030e60Sflorian CASE(PROPOSAL_WITHDRAWN); 3315a030e60Sflorian CASE(PROPOSAL_DUPLICATED); 3325a030e60Sflorian CASE(PROPOSAL_STALE); 3335a030e60Sflorian } 3345a030e60Sflorian } 335f10889deSkn #endif 3365a030e60Sflorian 3370acf3e2dSflorian void 3380acf3e2dSflorian engine_sig_handler(int sig, short event, void *arg) 3390acf3e2dSflorian { 3400acf3e2dSflorian /* 3410acf3e2dSflorian * Normal signal handler rules don't apply because libevent 3420acf3e2dSflorian * decouples for us. 3430acf3e2dSflorian */ 3440acf3e2dSflorian 3450acf3e2dSflorian switch (sig) { 3460acf3e2dSflorian case SIGINT: 3470acf3e2dSflorian case SIGTERM: 3480acf3e2dSflorian engine_shutdown(); 3490acf3e2dSflorian default: 3500acf3e2dSflorian fatalx("unexpected signal"); 3510acf3e2dSflorian } 3520acf3e2dSflorian } 3530acf3e2dSflorian 3540acf3e2dSflorian void 3550acf3e2dSflorian engine(int debug, int verbose) 3560acf3e2dSflorian { 3570acf3e2dSflorian struct event ev_sigint, ev_sigterm; 3580acf3e2dSflorian struct passwd *pw; 3590acf3e2dSflorian 3600acf3e2dSflorian log_init(debug, LOG_DAEMON); 3610acf3e2dSflorian log_setverbose(verbose); 3620acf3e2dSflorian 3630acf3e2dSflorian if ((pw = getpwnam(SLAACD_USER)) == NULL) 3640acf3e2dSflorian fatal("getpwnam"); 3650acf3e2dSflorian 3660acf3e2dSflorian if (chdir("/") == -1) 3670acf3e2dSflorian fatal("chdir(\"/\")"); 3680acf3e2dSflorian 369ec8f555eSflorian if (unveil("/", "") == -1) 370bc5a8259Sbeck fatal("unveil /"); 371ec8f555eSflorian if (unveil(NULL, NULL) == -1) 372bc5a8259Sbeck fatal("unveil"); 373ec8f555eSflorian 37431525baaSflorian setproctitle("%s", "engine"); 37531525baaSflorian log_procinit("engine"); 3760acf3e2dSflorian 3770acf3e2dSflorian if (setgroups(1, &pw->pw_gid) || 3780acf3e2dSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 3790acf3e2dSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 3800acf3e2dSflorian fatal("can't drop privileges"); 3810acf3e2dSflorian 3820acf3e2dSflorian if (pledge("stdio recvfd", NULL) == -1) 3830acf3e2dSflorian fatal("pledge"); 3840acf3e2dSflorian 3850acf3e2dSflorian event_init(); 3860acf3e2dSflorian 3870acf3e2dSflorian /* Setup signal handler(s). */ 3880acf3e2dSflorian signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL); 3890acf3e2dSflorian signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL); 3900acf3e2dSflorian signal_add(&ev_sigint, NULL); 3910acf3e2dSflorian signal_add(&ev_sigterm, NULL); 3920acf3e2dSflorian signal(SIGPIPE, SIG_IGN); 3930acf3e2dSflorian signal(SIGHUP, SIG_IGN); 3940acf3e2dSflorian 3950acf3e2dSflorian /* Setup pipe and event handler to the main process. */ 3960acf3e2dSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 3970acf3e2dSflorian fatal(NULL); 3980acf3e2dSflorian 399*0e59d0d1Sclaudio if (imsgbuf_init(&iev_main->ibuf, 3) == -1) 400*0e59d0d1Sclaudio fatal(NULL); 401*0e59d0d1Sclaudio imsgbuf_allow_fdpass(&iev_main->ibuf); 4020acf3e2dSflorian iev_main->handler = engine_dispatch_main; 4030acf3e2dSflorian 4040acf3e2dSflorian /* Setup event handlers. */ 4050acf3e2dSflorian iev_main->events = EV_READ; 4060acf3e2dSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 4070acf3e2dSflorian iev_main->handler, iev_main); 4080acf3e2dSflorian event_add(&iev_main->ev, NULL); 4090acf3e2dSflorian 4100acf3e2dSflorian LIST_INIT(&slaacd_interfaces); 4110acf3e2dSflorian 4120acf3e2dSflorian event_dispatch(); 4130acf3e2dSflorian 4140acf3e2dSflorian engine_shutdown(); 4150acf3e2dSflorian } 4160acf3e2dSflorian 4170acf3e2dSflorian __dead void 4180acf3e2dSflorian engine_shutdown(void) 4190acf3e2dSflorian { 4200acf3e2dSflorian /* Close pipes. */ 4219cbf9e90Sclaudio imsgbuf_clear(&iev_frontend->ibuf); 4220acf3e2dSflorian close(iev_frontend->ibuf.fd); 4239cbf9e90Sclaudio imsgbuf_clear(&iev_main->ibuf); 4240acf3e2dSflorian close(iev_main->ibuf.fd); 4250acf3e2dSflorian 4260acf3e2dSflorian free(iev_frontend); 4270acf3e2dSflorian free(iev_main); 4280acf3e2dSflorian 4290acf3e2dSflorian log_info("engine exiting"); 4300acf3e2dSflorian exit(0); 4310acf3e2dSflorian } 4320acf3e2dSflorian 4330acf3e2dSflorian int 4340acf3e2dSflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data, 4350acf3e2dSflorian uint16_t datalen) 4360acf3e2dSflorian { 4370acf3e2dSflorian return (imsg_compose_event(iev_frontend, type, 0, pid, -1, 4380acf3e2dSflorian data, datalen)); 4390acf3e2dSflorian } 4400acf3e2dSflorian 4410acf3e2dSflorian int 4420acf3e2dSflorian engine_imsg_compose_main(int type, pid_t pid, void *data, 4430acf3e2dSflorian uint16_t datalen) 4440acf3e2dSflorian { 4450acf3e2dSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, 4460acf3e2dSflorian data, datalen)); 4470acf3e2dSflorian } 4480acf3e2dSflorian 4490acf3e2dSflorian void 4500acf3e2dSflorian engine_dispatch_frontend(int fd, short event, void *bula) 4510acf3e2dSflorian { 4520acf3e2dSflorian struct imsgev *iev = bula; 4530acf3e2dSflorian struct imsgbuf *ibuf = &iev->ibuf; 4540acf3e2dSflorian struct imsg imsg; 4550acf3e2dSflorian struct slaacd_iface *iface; 4560acf3e2dSflorian struct imsg_ra ra; 4570acf3e2dSflorian struct address_proposal *addr_proposal = NULL; 4580acf3e2dSflorian struct dfr_proposal *dfr_proposal = NULL; 4590acf3e2dSflorian struct imsg_del_addr del_addr; 460d3ff3477Sflorian struct imsg_del_route del_route; 46105b87f88Sflorian struct imsg_dup_addr dup_addr; 4620acf3e2dSflorian ssize_t n; 463bf0ed931Sflorian int shut = 0; 464bf0ed931Sflorian #ifndef SMALL 465bf0ed931Sflorian int verbose; 466bf0ed931Sflorian #endif /* SMALL */ 46733d2acb6Sflorian uint32_t if_index, type; 4680acf3e2dSflorian 4690acf3e2dSflorian if (event & EV_READ) { 470668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 471dd7efffeSclaudio fatal("imsgbuf_read error"); 4720acf3e2dSflorian if (n == 0) /* Connection closed. */ 4730acf3e2dSflorian shut = 1; 4740acf3e2dSflorian } 4750acf3e2dSflorian if (event & EV_WRITE) { 476dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 477e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 4780acf3e2dSflorian shut = 1; 479e3b6409cSclaudio else 480dd7efffeSclaudio fatal("imsgbuf_write"); 481e3b6409cSclaudio } 4820acf3e2dSflorian } 4830acf3e2dSflorian 4840acf3e2dSflorian for (;;) { 4850acf3e2dSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 4860acf3e2dSflorian fatal("%s: imsg_get error", __func__); 4870acf3e2dSflorian if (n == 0) /* No more messages. */ 4880acf3e2dSflorian break; 4890acf3e2dSflorian 49033d2acb6Sflorian type = imsg_get_type(&imsg); 49133d2acb6Sflorian 49233d2acb6Sflorian switch (type) { 493bf0ed931Sflorian #ifndef SMALL 4940acf3e2dSflorian case IMSG_CTL_LOG_VERBOSE: 49533d2acb6Sflorian if (imsg_get_data(&imsg, &verbose, 49633d2acb6Sflorian sizeof(verbose)) == -1) 49733d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 49833d2acb6Sflorian 4990acf3e2dSflorian log_setverbose(verbose); 5000acf3e2dSflorian break; 5010acf3e2dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 50233d2acb6Sflorian if (imsg_get_data(&imsg, &if_index, 50333d2acb6Sflorian sizeof(if_index)) == -1) 50433d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 50533d2acb6Sflorian 5069a7d784aSflorian engine_showinfo_ctl(imsg_get_pid(&imsg), if_index); 5070acf3e2dSflorian break; 508bf0ed931Sflorian #endif /* SMALL */ 5090acf3e2dSflorian case IMSG_REMOVE_IF: 51033d2acb6Sflorian if (imsg_get_data(&imsg, &if_index, 51133d2acb6Sflorian sizeof(if_index)) == -1) 51233d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 51333d2acb6Sflorian 5140acf3e2dSflorian remove_slaacd_iface(if_index); 5150acf3e2dSflorian break; 5160acf3e2dSflorian case IMSG_RA: 51733d2acb6Sflorian if (imsg_get_data(&imsg, &ra, sizeof(ra)) == -1) 51833d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 51933d2acb6Sflorian 5200acf3e2dSflorian iface = get_slaacd_iface_by_id(ra.if_index); 5215a030e60Sflorian 5225a030e60Sflorian /* 5235a030e60Sflorian * Ignore unsolicitated router advertisements 5245a030e60Sflorian * if we think the interface is still down. 5255a030e60Sflorian * Otherwise we confuse the state machine. 5265a030e60Sflorian */ 5275a030e60Sflorian if (iface != NULL && iface->state != IF_DOWN) 5280acf3e2dSflorian parse_ra(iface, &ra); 5290acf3e2dSflorian break; 5300acf3e2dSflorian case IMSG_CTL_SEND_SOLICITATION: 53133d2acb6Sflorian if (imsg_get_data(&imsg, &if_index, 53233d2acb6Sflorian sizeof(if_index)) == -1) 53333d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 53433d2acb6Sflorian 5350acf3e2dSflorian iface = get_slaacd_iface_by_id(if_index); 5360acf3e2dSflorian if (iface == NULL) 5372ac62a75Sflorian log_warnx("requested to send solicitation on " 5380acf3e2dSflorian "non-autoconf interface: %u", if_index); 5395a030e60Sflorian else { 5405a030e60Sflorian iface->last_sol.tv_sec = 0; /* no rate limit */ 5415a030e60Sflorian request_solicitation(iface); 5425a030e60Sflorian } 5430acf3e2dSflorian break; 5440acf3e2dSflorian case IMSG_DEL_ADDRESS: 54533d2acb6Sflorian if (imsg_get_data(&imsg, &del_addr, 54633d2acb6Sflorian sizeof(del_addr)) == -1) 54733d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 54833d2acb6Sflorian 5490acf3e2dSflorian iface = get_slaacd_iface_by_id(del_addr.if_index); 5500acf3e2dSflorian if (iface == NULL) { 5510acf3e2dSflorian log_debug("IMSG_DEL_ADDRESS: unknown interface" 5520acf3e2dSflorian ", ignoring"); 5530acf3e2dSflorian break; 5540acf3e2dSflorian } 5550acf3e2dSflorian 5560acf3e2dSflorian addr_proposal = find_address_proposal_by_addr(iface, 5570acf3e2dSflorian &del_addr.addr); 5585a030e60Sflorian /* 5595a030e60Sflorian * If it's in state PROPOSAL_WITHDRAWN we just 5605a030e60Sflorian * deleted it ourself but want to keep it around 5615a030e60Sflorian * so we can renew it 5625a030e60Sflorian */ 5635a030e60Sflorian if (addr_proposal && addr_proposal->state != 5645a030e60Sflorian PROPOSAL_WITHDRAWN) 5657455062cSflorian free_address_proposal(addr_proposal); 5660acf3e2dSflorian break; 567d3ff3477Sflorian case IMSG_DEL_ROUTE: 56833d2acb6Sflorian if (imsg_get_data(&imsg, &del_route, 56933d2acb6Sflorian sizeof(del_route)) == -1) 57033d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 57133d2acb6Sflorian 572e022a9a4Sflorian iface = get_slaacd_iface_by_id(del_route.if_index); 573d3ff3477Sflorian if (iface == NULL) { 574d3ff3477Sflorian log_debug("IMSG_DEL_ROUTE: unknown interface" 575d3ff3477Sflorian ", ignoring"); 576d3ff3477Sflorian break; 577d3ff3477Sflorian } 578d3ff3477Sflorian 579d3ff3477Sflorian dfr_proposal = find_dfr_proposal_by_gw(iface, 580d3ff3477Sflorian &del_route.gw); 581d3ff3477Sflorian 582d3ff3477Sflorian if (dfr_proposal) { 583d3ff3477Sflorian dfr_proposal->state = PROPOSAL_WITHDRAWN; 584d3ff3477Sflorian free_dfr_proposal(dfr_proposal); 585d3ff3477Sflorian } 586d3ff3477Sflorian break; 58705b87f88Sflorian case IMSG_DUP_ADDRESS: 58833d2acb6Sflorian if (imsg_get_data(&imsg, &dup_addr, 58933d2acb6Sflorian sizeof(dup_addr)) == -1) 59033d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 59133d2acb6Sflorian 59205b87f88Sflorian iface = get_slaacd_iface_by_id(dup_addr.if_index); 59305b87f88Sflorian if (iface == NULL) { 59405b87f88Sflorian log_debug("IMSG_DUP_ADDRESS: unknown interface" 59505b87f88Sflorian ", ignoring"); 59605b87f88Sflorian break; 59705b87f88Sflorian } 59805b87f88Sflorian 59905b87f88Sflorian addr_proposal = find_address_proposal_by_addr(iface, 60005b87f88Sflorian &dup_addr.addr); 60105b87f88Sflorian 6025a030e60Sflorian if (addr_proposal) 6035a030e60Sflorian addr_proposal_state_transition(addr_proposal, 6045a030e60Sflorian PROPOSAL_DUPLICATED); 60505b87f88Sflorian break; 60634435150Sflorian case IMSG_REPROPOSE_RDNS: 607fdb4a585Sflorian LIST_FOREACH (iface, &slaacd_interfaces, entries) 608dd19964dSflorian compose_rdns_proposal(iface->if_index, 609dd19964dSflorian iface->rdomain); 61034435150Sflorian break; 6110acf3e2dSflorian default: 61233d2acb6Sflorian log_debug("%s: unexpected imsg %d", __func__, type); 6130acf3e2dSflorian break; 6140acf3e2dSflorian } 6150acf3e2dSflorian imsg_free(&imsg); 6160acf3e2dSflorian } 6170acf3e2dSflorian if (!shut) 6180acf3e2dSflorian imsg_event_add(iev); 6190acf3e2dSflorian else { 6200acf3e2dSflorian /* This pipe is dead. Remove its event handler. */ 6210acf3e2dSflorian event_del(&iev->ev); 6220acf3e2dSflorian event_loopexit(NULL); 6230acf3e2dSflorian } 6240acf3e2dSflorian } 6250acf3e2dSflorian 6260acf3e2dSflorian void 6270acf3e2dSflorian engine_dispatch_main(int fd, short event, void *bula) 6280acf3e2dSflorian { 6290acf3e2dSflorian struct imsg imsg; 6300acf3e2dSflorian struct imsgev *iev = bula; 6310acf3e2dSflorian struct imsgbuf *ibuf = &iev->ibuf; 6328653da7cSflorian struct imsg_ifinfo imsg_ifinfo; 6330acf3e2dSflorian ssize_t n; 63433d2acb6Sflorian uint32_t type; 6350acf3e2dSflorian int shut = 0; 6360acf3e2dSflorian 6370acf3e2dSflorian if (event & EV_READ) { 638668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 639dd7efffeSclaudio fatal("imsgbuf_read error"); 6400acf3e2dSflorian if (n == 0) /* Connection closed. */ 6410acf3e2dSflorian shut = 1; 6420acf3e2dSflorian } 6430acf3e2dSflorian if (event & EV_WRITE) { 644dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 645e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 6460acf3e2dSflorian shut = 1; 647e3b6409cSclaudio else 648dd7efffeSclaudio fatal("imsgbuf_write"); 649e3b6409cSclaudio } 6500acf3e2dSflorian } 6510acf3e2dSflorian 6520acf3e2dSflorian for (;;) { 6530acf3e2dSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 6540acf3e2dSflorian fatal("%s: imsg_get error", __func__); 6550acf3e2dSflorian if (n == 0) /* No more messages. */ 6560acf3e2dSflorian break; 6570acf3e2dSflorian 65833d2acb6Sflorian type = imsg_get_type(&imsg); 65933d2acb6Sflorian 66033d2acb6Sflorian switch (type) { 6610acf3e2dSflorian case IMSG_SOCKET_IPC: 6620acf3e2dSflorian /* 6630acf3e2dSflorian * Setup pipe and event handler to the frontend 6640acf3e2dSflorian * process. 6650acf3e2dSflorian */ 66658a273d8Sflorian if (iev_frontend) 66758a273d8Sflorian fatalx("%s: received unexpected imsg fd " 6680acf3e2dSflorian "to engine", __func__); 66958a273d8Sflorian 6708de8a5baSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 67158a273d8Sflorian fatalx("%s: expected to receive imsg fd to " 6720acf3e2dSflorian "engine but didn't receive any", __func__); 6730acf3e2dSflorian 6740acf3e2dSflorian iev_frontend = malloc(sizeof(struct imsgev)); 6750acf3e2dSflorian if (iev_frontend == NULL) 6760acf3e2dSflorian fatal(NULL); 6770acf3e2dSflorian 678*0e59d0d1Sclaudio if (imsgbuf_init(&iev_frontend->ibuf, fd) == -1) 679*0e59d0d1Sclaudio fatal(NULL); 6800acf3e2dSflorian iev_frontend->handler = engine_dispatch_frontend; 6810acf3e2dSflorian iev_frontend->events = EV_READ; 6820acf3e2dSflorian 6830acf3e2dSflorian event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, 6840acf3e2dSflorian iev_frontend->events, iev_frontend->handler, 6850acf3e2dSflorian iev_frontend); 6860acf3e2dSflorian event_add(&iev_frontend->ev, NULL); 6870acf3e2dSflorian 6880acf3e2dSflorian if (pledge("stdio", NULL) == -1) 6890acf3e2dSflorian fatal("pledge"); 6900acf3e2dSflorian break; 6918653da7cSflorian case IMSG_UPDATE_IF: 69233d2acb6Sflorian if (imsg_get_data(&imsg, &imsg_ifinfo, 69333d2acb6Sflorian sizeof(imsg_ifinfo)) == -1) 69433d2acb6Sflorian fatalx("%s: invalid %s", __func__, i2s(type)); 69533d2acb6Sflorian 696883c684aSflorian engine_update_iface(&imsg_ifinfo); 6978653da7cSflorian break; 6980acf3e2dSflorian default: 69933d2acb6Sflorian log_debug("%s: unexpected imsg %d", __func__, type); 7000acf3e2dSflorian break; 7010acf3e2dSflorian } 7020acf3e2dSflorian imsg_free(&imsg); 7030acf3e2dSflorian } 7040acf3e2dSflorian if (!shut) 7050acf3e2dSflorian imsg_event_add(iev); 7060acf3e2dSflorian else { 7070acf3e2dSflorian /* This pipe is dead. Remove its event handler. */ 7080acf3e2dSflorian event_del(&iev->ev); 7090acf3e2dSflorian event_loopexit(NULL); 7100acf3e2dSflorian } 7110acf3e2dSflorian } 7120acf3e2dSflorian 713bf0ed931Sflorian #ifndef SMALL 7140acf3e2dSflorian void 7150acf3e2dSflorian send_interface_info(struct slaacd_iface *iface, pid_t pid) 7160acf3e2dSflorian { 7170acf3e2dSflorian struct ctl_engine_info cei; 7180acf3e2dSflorian struct ctl_engine_info_ra cei_ra; 7190acf3e2dSflorian struct ctl_engine_info_ra_prefix cei_ra_prefix; 7200acf3e2dSflorian struct ctl_engine_info_ra_rdns cei_ra_rdns; 7210acf3e2dSflorian struct ctl_engine_info_address_proposal cei_addr_proposal; 7220acf3e2dSflorian struct ctl_engine_info_dfr_proposal cei_dfr_proposal; 72334435150Sflorian struct ctl_engine_info_rdns_proposal cei_rdns_proposal; 7240acf3e2dSflorian struct radv *ra; 7250acf3e2dSflorian struct radv_prefix *prefix; 7260acf3e2dSflorian struct radv_rdns *rdns; 7270acf3e2dSflorian struct address_proposal *addr_proposal; 7280acf3e2dSflorian struct dfr_proposal *dfr_proposal; 72934435150Sflorian struct rdns_proposal *rdns_proposal; 7300acf3e2dSflorian 7310acf3e2dSflorian memset(&cei, 0, sizeof(cei)); 7320acf3e2dSflorian cei.if_index = iface->if_index; 7330acf3e2dSflorian cei.running = iface->running; 734c6384676Sflorian cei.autoconf = iface->autoconf; 735804ba004Sflorian cei.temporary = iface->temporary; 736b9b376fbSflorian cei.soii = iface->soii; 7370acf3e2dSflorian memcpy(&cei.hw_address, &iface->hw_address, sizeof(struct ether_addr)); 7380acf3e2dSflorian memcpy(&cei.ll_address, &iface->ll_address, 7390acf3e2dSflorian sizeof(struct sockaddr_in6)); 7400acf3e2dSflorian engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei, 7410acf3e2dSflorian sizeof(cei)); 7420acf3e2dSflorian LIST_FOREACH(ra, &iface->radvs, entries) { 7430acf3e2dSflorian memset(&cei_ra, 0, sizeof(cei_ra)); 7440acf3e2dSflorian memcpy(&cei_ra.from, &ra->from, sizeof(cei_ra.from)); 7450acf3e2dSflorian memcpy(&cei_ra.when, &ra->when, sizeof(cei_ra.when)); 7460acf3e2dSflorian memcpy(&cei_ra.uptime, &ra->uptime, sizeof(cei_ra.uptime)); 7470acf3e2dSflorian cei_ra.curhoplimit = ra->curhoplimit; 7480acf3e2dSflorian cei_ra.managed = ra->managed; 7490acf3e2dSflorian cei_ra.other = ra->other; 7500acf3e2dSflorian if (strlcpy(cei_ra.rpref, rpref_name[ra->rpref], sizeof( 7510acf3e2dSflorian cei_ra.rpref)) >= sizeof(cei_ra.rpref)) 7522ac62a75Sflorian log_warnx("truncated router preference"); 7530acf3e2dSflorian cei_ra.router_lifetime = ra->router_lifetime; 7540acf3e2dSflorian cei_ra.reachable_time = ra->reachable_time; 7550acf3e2dSflorian cei_ra.retrans_time = ra->retrans_time; 756884d1deeSbket cei_ra.mtu = ra->mtu; 7570acf3e2dSflorian engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO_RA, 7580acf3e2dSflorian pid, &cei_ra, sizeof(cei_ra)); 7590acf3e2dSflorian 7600acf3e2dSflorian LIST_FOREACH(prefix, &ra->prefixes, entries) { 7610acf3e2dSflorian memset(&cei_ra_prefix, 0, sizeof(cei_ra_prefix)); 7620acf3e2dSflorian 7630acf3e2dSflorian cei_ra_prefix.prefix = prefix->prefix; 7640acf3e2dSflorian cei_ra_prefix.prefix_len = prefix->prefix_len; 7650acf3e2dSflorian cei_ra_prefix.onlink = prefix->onlink; 7660acf3e2dSflorian cei_ra_prefix.autonomous = prefix->autonomous; 7670acf3e2dSflorian cei_ra_prefix.vltime = prefix->vltime; 7680acf3e2dSflorian cei_ra_prefix.pltime = prefix->pltime; 7690acf3e2dSflorian engine_imsg_compose_frontend( 7700acf3e2dSflorian IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX, pid, 7710acf3e2dSflorian &cei_ra_prefix, sizeof(cei_ra_prefix)); 7720acf3e2dSflorian } 7730acf3e2dSflorian 7740acf3e2dSflorian LIST_FOREACH(rdns, &ra->rdns_servers, entries) { 7750acf3e2dSflorian memset(&cei_ra_rdns, 0, sizeof(cei_ra_rdns)); 7760acf3e2dSflorian memcpy(&cei_ra_rdns.rdns, &rdns->rdns, 7770acf3e2dSflorian sizeof(cei_ra_rdns.rdns)); 7780acf3e2dSflorian cei_ra_rdns.lifetime = ra->rdns_lifetime; 7790acf3e2dSflorian engine_imsg_compose_frontend( 7800acf3e2dSflorian IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS, pid, 7810acf3e2dSflorian &cei_ra_rdns, sizeof(cei_ra_rdns)); 7820acf3e2dSflorian } 7830acf3e2dSflorian } 7840acf3e2dSflorian 7850acf3e2dSflorian if (!LIST_EMPTY(&iface->addr_proposals)) 7860acf3e2dSflorian engine_imsg_compose_frontend( 7870acf3e2dSflorian IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS, pid, NULL, 0); 7880acf3e2dSflorian 7890acf3e2dSflorian LIST_FOREACH(addr_proposal, &iface->addr_proposals, entries) { 7900acf3e2dSflorian memset(&cei_addr_proposal, 0, sizeof(cei_addr_proposal)); 7910acf3e2dSflorian cei_addr_proposal.id = addr_proposal->id; 7920acf3e2dSflorian if (strlcpy(cei_addr_proposal.state, 7935a030e60Sflorian proposal_state_name(addr_proposal->state), 7940acf3e2dSflorian sizeof(cei_addr_proposal.state)) >= 7950acf3e2dSflorian sizeof(cei_addr_proposal.state)) 7962ac62a75Sflorian log_warnx("truncated state name"); 7975a030e60Sflorian cei_addr_proposal.next_timeout = addr_proposal->timo.tv_sec; 7980acf3e2dSflorian cei_addr_proposal.when = addr_proposal->when; 7990acf3e2dSflorian cei_addr_proposal.uptime = addr_proposal->uptime; 8000acf3e2dSflorian memcpy(&cei_addr_proposal.addr, &addr_proposal->addr, sizeof( 8010acf3e2dSflorian cei_addr_proposal.addr)); 8020acf3e2dSflorian memcpy(&cei_addr_proposal.prefix, &addr_proposal->prefix, 8030acf3e2dSflorian sizeof(cei_addr_proposal.prefix)); 8040acf3e2dSflorian cei_addr_proposal.prefix_len = addr_proposal->prefix_len; 805804ba004Sflorian cei_addr_proposal.temporary = addr_proposal->temporary; 8060acf3e2dSflorian cei_addr_proposal.vltime = addr_proposal->vltime; 8070acf3e2dSflorian cei_addr_proposal.pltime = addr_proposal->pltime; 8080acf3e2dSflorian 8090acf3e2dSflorian engine_imsg_compose_frontend( 8100acf3e2dSflorian IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL, pid, 8110acf3e2dSflorian &cei_addr_proposal, sizeof(cei_addr_proposal)); 8120acf3e2dSflorian } 8130acf3e2dSflorian 8140acf3e2dSflorian if (!LIST_EMPTY(&iface->dfr_proposals)) 8150acf3e2dSflorian engine_imsg_compose_frontend( 8160acf3e2dSflorian IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS, pid, NULL, 0); 8170acf3e2dSflorian 8180acf3e2dSflorian LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries) { 8190acf3e2dSflorian memset(&cei_dfr_proposal, 0, sizeof(cei_dfr_proposal)); 8200acf3e2dSflorian cei_dfr_proposal.id = dfr_proposal->id; 8210acf3e2dSflorian if (strlcpy(cei_dfr_proposal.state, 8225a030e60Sflorian proposal_state_name(dfr_proposal->state), 8230acf3e2dSflorian sizeof(cei_dfr_proposal.state)) >= 8240acf3e2dSflorian sizeof(cei_dfr_proposal.state)) 8252ac62a75Sflorian log_warnx("truncated state name"); 8265a030e60Sflorian cei_dfr_proposal.next_timeout = dfr_proposal->timo.tv_sec; 8270acf3e2dSflorian cei_dfr_proposal.when = dfr_proposal->when; 8280acf3e2dSflorian cei_dfr_proposal.uptime = dfr_proposal->uptime; 8290acf3e2dSflorian memcpy(&cei_dfr_proposal.addr, &dfr_proposal->addr, sizeof( 8300acf3e2dSflorian cei_dfr_proposal.addr)); 8310acf3e2dSflorian cei_dfr_proposal.router_lifetime = 8320acf3e2dSflorian dfr_proposal->router_lifetime; 8330acf3e2dSflorian if (strlcpy(cei_dfr_proposal.rpref, 8340acf3e2dSflorian rpref_name[dfr_proposal->rpref], 8350acf3e2dSflorian sizeof(cei_dfr_proposal.rpref)) >= 8360acf3e2dSflorian sizeof(cei_dfr_proposal.rpref)) 8372ac62a75Sflorian log_warnx("truncated router preference"); 8380acf3e2dSflorian engine_imsg_compose_frontend( 8390acf3e2dSflorian IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL, pid, 8400acf3e2dSflorian &cei_dfr_proposal, sizeof(cei_dfr_proposal)); 8410acf3e2dSflorian } 84234435150Sflorian 84334435150Sflorian if (!LIST_EMPTY(&iface->rdns_proposals)) 84434435150Sflorian engine_imsg_compose_frontend( 84534435150Sflorian IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSALS, pid, NULL, 0); 84634435150Sflorian 84734435150Sflorian LIST_FOREACH(rdns_proposal, &iface->rdns_proposals, entries) { 84834435150Sflorian memset(&cei_rdns_proposal, 0, sizeof(cei_rdns_proposal)); 84934435150Sflorian cei_rdns_proposal.id = rdns_proposal->id; 85034435150Sflorian if (strlcpy(cei_rdns_proposal.state, 8515a030e60Sflorian proposal_state_name(rdns_proposal->state), 85234435150Sflorian sizeof(cei_rdns_proposal.state)) >= 85334435150Sflorian sizeof(cei_rdns_proposal.state)) 85434435150Sflorian log_warnx("truncated state name"); 8555a030e60Sflorian cei_rdns_proposal.next_timeout = rdns_proposal->timo.tv_sec; 85634435150Sflorian cei_rdns_proposal.when = rdns_proposal->when; 85734435150Sflorian cei_rdns_proposal.uptime = rdns_proposal->uptime; 85834435150Sflorian memcpy(&cei_rdns_proposal.from, &rdns_proposal->from, sizeof( 85934435150Sflorian cei_rdns_proposal.from)); 86034435150Sflorian cei_rdns_proposal.rdns_count = rdns_proposal->rdns_count; 86134435150Sflorian memcpy(&cei_rdns_proposal.rdns, 86234435150Sflorian &rdns_proposal->rdns, sizeof(cei_rdns_proposal.rdns)); 86334435150Sflorian cei_rdns_proposal.rdns_lifetime = 86434435150Sflorian rdns_proposal->rdns_lifetime; 86534435150Sflorian engine_imsg_compose_frontend( 86634435150Sflorian IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSAL, pid, 86734435150Sflorian &cei_rdns_proposal, sizeof(cei_rdns_proposal)); 86834435150Sflorian } 8690acf3e2dSflorian } 8700acf3e2dSflorian 8710acf3e2dSflorian void 8729a7d784aSflorian engine_showinfo_ctl(pid_t pid, uint32_t if_index) 8730acf3e2dSflorian { 8740acf3e2dSflorian struct slaacd_iface *iface; 8750acf3e2dSflorian 8760acf3e2dSflorian if (if_index == 0) { 8770acf3e2dSflorian LIST_FOREACH (iface, &slaacd_interfaces, entries) 8789a7d784aSflorian send_interface_info(iface, pid); 8790acf3e2dSflorian } else { 8800acf3e2dSflorian if ((iface = get_slaacd_iface_by_id(if_index)) != NULL) 8819a7d784aSflorian send_interface_info(iface, pid); 8820acf3e2dSflorian } 8839a7d784aSflorian engine_imsg_compose_frontend(IMSG_CTL_END, pid, NULL, 0); 8840acf3e2dSflorian } 8856ae7209fSflorian 88606d929a1Skn #endif /* SMALL */ 88706d929a1Skn 8880acf3e2dSflorian struct slaacd_iface* 8890acf3e2dSflorian get_slaacd_iface_by_id(uint32_t if_index) 8900acf3e2dSflorian { 8910acf3e2dSflorian struct slaacd_iface *iface; 8920acf3e2dSflorian LIST_FOREACH (iface, &slaacd_interfaces, entries) { 8930acf3e2dSflorian if (iface->if_index == if_index) 8940acf3e2dSflorian return (iface); 8950acf3e2dSflorian } 8960acf3e2dSflorian 8970acf3e2dSflorian return (NULL); 8980acf3e2dSflorian } 8990acf3e2dSflorian 9000acf3e2dSflorian void 9010acf3e2dSflorian remove_slaacd_iface(uint32_t if_index) 9020acf3e2dSflorian { 9037db0c098Sflorian struct slaacd_iface *iface; 9040acf3e2dSflorian struct radv *ra; 9050acf3e2dSflorian struct address_proposal *addr_proposal; 9060acf3e2dSflorian struct dfr_proposal *dfr_proposal; 90734435150Sflorian struct rdns_proposal *rdns_proposal; 9080acf3e2dSflorian 9097db0c098Sflorian iface = get_slaacd_iface_by_id(if_index); 9107db0c098Sflorian 9117db0c098Sflorian if (iface == NULL) 9127db0c098Sflorian return; 9137db0c098Sflorian 9140acf3e2dSflorian LIST_REMOVE(iface, entries); 9150acf3e2dSflorian while(!LIST_EMPTY(&iface->radvs)) { 9160acf3e2dSflorian ra = LIST_FIRST(&iface->radvs); 9170acf3e2dSflorian LIST_REMOVE(ra, entries); 9180acf3e2dSflorian free_ra(ra); 9190acf3e2dSflorian } 9200acf3e2dSflorian while(!LIST_EMPTY(&iface->addr_proposals)) { 9217db0c098Sflorian addr_proposal = LIST_FIRST(&iface->addr_proposals); 9227455062cSflorian free_address_proposal(addr_proposal); 9230acf3e2dSflorian } 9240acf3e2dSflorian while(!LIST_EMPTY(&iface->dfr_proposals)) { 9257db0c098Sflorian dfr_proposal = LIST_FIRST(&iface->dfr_proposals); 9260acf3e2dSflorian free_dfr_proposal(dfr_proposal); 9270acf3e2dSflorian } 92834435150Sflorian while(!LIST_EMPTY(&iface->rdns_proposals)) { 92934435150Sflorian rdns_proposal = LIST_FIRST(&iface->rdns_proposals); 93034435150Sflorian free_rdns_proposal(rdns_proposal); 93134435150Sflorian } 932dd19964dSflorian compose_rdns_proposal(iface->if_index, iface->rdomain); 9337455062cSflorian evtimer_del(&iface->timer); 9340acf3e2dSflorian free(iface); 9350acf3e2dSflorian } 9360acf3e2dSflorian 9370acf3e2dSflorian void 9380acf3e2dSflorian free_ra(struct radv *ra) 9390acf3e2dSflorian { 9400acf3e2dSflorian struct radv_prefix *prefix; 9410acf3e2dSflorian struct radv_rdns *rdns; 9420acf3e2dSflorian 9430acf3e2dSflorian if (ra == NULL) 9440acf3e2dSflorian return; 9450acf3e2dSflorian 9460acf3e2dSflorian evtimer_del(&ra->timer); 9470acf3e2dSflorian 9480acf3e2dSflorian while (!LIST_EMPTY(&ra->prefixes)) { 9490acf3e2dSflorian prefix = LIST_FIRST(&ra->prefixes); 9500acf3e2dSflorian LIST_REMOVE(prefix, entries); 9510acf3e2dSflorian free(prefix); 9520acf3e2dSflorian } 9530acf3e2dSflorian 9540acf3e2dSflorian while (!LIST_EMPTY(&ra->rdns_servers)) { 9550acf3e2dSflorian rdns = LIST_FIRST(&ra->rdns_servers); 9560acf3e2dSflorian LIST_REMOVE(rdns, entries); 9570acf3e2dSflorian free(rdns); 9580acf3e2dSflorian } 9590acf3e2dSflorian 9600acf3e2dSflorian free(ra); 9610acf3e2dSflorian } 9620acf3e2dSflorian 9630acf3e2dSflorian void 9645a030e60Sflorian iface_state_transition(struct slaacd_iface *iface, enum if_state new_state) 9655a030e60Sflorian { 9665a030e60Sflorian enum if_state old_state = iface->state; 9675a030e60Sflorian struct address_proposal *addr_proposal; 9685a030e60Sflorian struct dfr_proposal *dfr_proposal; 9695a030e60Sflorian struct rdns_proposal *rdns_proposal; 9705a030e60Sflorian 9715a030e60Sflorian iface->state = new_state; 9725a030e60Sflorian 9735a030e60Sflorian switch (new_state) { 9745a030e60Sflorian case IF_DOWN: 9755a030e60Sflorian if (old_state != IF_DOWN) { 9765a030e60Sflorian LIST_FOREACH (addr_proposal, &iface->addr_proposals, 9775a030e60Sflorian entries) 9785a030e60Sflorian addr_proposal_state_transition(addr_proposal, 9795a030e60Sflorian PROPOSAL_IF_DOWN); 9805a030e60Sflorian LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, 9815a030e60Sflorian entries) 9825a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, 9835a030e60Sflorian PROPOSAL_IF_DOWN); 9845a030e60Sflorian LIST_FOREACH (rdns_proposal, &iface->rdns_proposals, 9855a030e60Sflorian entries) 9865a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 9875a030e60Sflorian PROPOSAL_IF_DOWN); 9885a030e60Sflorian } 9895a030e60Sflorian 9905a030e60Sflorian /* nothing else to do until interface comes back up */ 9915a030e60Sflorian iface->timo.tv_sec = -1; 9925a030e60Sflorian break; 9935a030e60Sflorian case IF_INIT: 9945a030e60Sflorian switch (old_state) { 9955a030e60Sflorian case IF_INIT: 9965a030e60Sflorian iface->probes++; 9975a030e60Sflorian break; 9985a030e60Sflorian case IF_DOWN: 9995a030e60Sflorian LIST_FOREACH (addr_proposal, &iface->addr_proposals, 10005a030e60Sflorian entries) 10015a030e60Sflorian addr_proposal_state_transition(addr_proposal, 10025a030e60Sflorian PROPOSAL_WITHDRAWN); 10035a030e60Sflorian LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, 10045a030e60Sflorian entries) 10055a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, 10065a030e60Sflorian PROPOSAL_WITHDRAWN); 10075a030e60Sflorian LIST_FOREACH (rdns_proposal, &iface->rdns_proposals, 10085a030e60Sflorian entries) 10095a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 10105a030e60Sflorian PROPOSAL_WITHDRAWN); 10115a030e60Sflorian default: 10125a030e60Sflorian iface->probes = 0; 10135a030e60Sflorian } 10145a030e60Sflorian if (iface->probes < MAX_RTR_SOLICITATIONS) { 10155a030e60Sflorian iface->timo.tv_sec = RTR_SOLICITATION_INTERVAL; 10165a030e60Sflorian request_solicitation(iface); 10175a030e60Sflorian } else 10185a030e60Sflorian /* no router available, stop probing */ 10195a030e60Sflorian iface->timo.tv_sec = -1; 10205a030e60Sflorian break; 10215a030e60Sflorian case IF_BOUND: 10225a030e60Sflorian iface->timo.tv_sec = -1; 10235a030e60Sflorian break; 10245a030e60Sflorian } 10255a030e60Sflorian 1026cf6341ecSflorian if (log_getverbose()) { 1027cf6341ecSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 10285a030e60Sflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 1029cf6341ecSflorian log_debug("%s[%s] %s -> %s, timo: %lld", __func__, 1030cf6341ecSflorian if_name == NULL ? "?" : if_name, if_state_name(old_state), 1031cf6341ecSflorian if_state_name(new_state), iface->timo.tv_sec); 1032cf6341ecSflorian } 10335a030e60Sflorian 10345a030e60Sflorian if (iface->timo.tv_sec == -1) { 10355a030e60Sflorian if (evtimer_pending(&iface->timer, NULL)) 10365a030e60Sflorian evtimer_del(&iface->timer); 10375a030e60Sflorian } else 10385a030e60Sflorian evtimer_add(&iface->timer, &iface->timo); 10395a030e60Sflorian } 10405a030e60Sflorian 10415a030e60Sflorian void addr_proposal_state_transition(struct address_proposal *addr_proposal, 10425a030e60Sflorian enum proposal_state new_state) 10435a030e60Sflorian { 10445a030e60Sflorian enum proposal_state old_state = addr_proposal->state; 10455a030e60Sflorian struct slaacd_iface *iface; 10465a030e60Sflorian uint32_t lifetime; 10475a030e60Sflorian 10485a030e60Sflorian addr_proposal->state = new_state; 10495a030e60Sflorian 10505a030e60Sflorian if ((iface = get_slaacd_iface_by_id(addr_proposal->if_index)) == NULL) 10515a030e60Sflorian return; 10525a030e60Sflorian 10535a030e60Sflorian switch (addr_proposal->state) { 10545a030e60Sflorian case PROPOSAL_IF_DOWN: 10555a030e60Sflorian if (old_state == PROPOSAL_IF_DOWN) { 10565a030e60Sflorian withdraw_addr(addr_proposal); 10575a030e60Sflorian addr_proposal->timo.tv_sec = -1; 10585a030e60Sflorian } else { 10595a030e60Sflorian addr_proposal->timo.tv_sec = 10605a030e60Sflorian real_lifetime(&addr_proposal->uptime, 10615a030e60Sflorian addr_proposal->vltime); 10625a030e60Sflorian } 10635a030e60Sflorian break; 10645a030e60Sflorian case PROPOSAL_NOT_CONFIGURED: 10655a030e60Sflorian break; 10665a030e60Sflorian case PROPOSAL_CONFIGURED: 10675a030e60Sflorian lifetime = real_lifetime(&addr_proposal->uptime, 10685a030e60Sflorian addr_proposal->pltime); 10695a030e60Sflorian if (lifetime == 0) 10705a030e60Sflorian lifetime = real_lifetime(&addr_proposal->uptime, 10715a030e60Sflorian addr_proposal->vltime); 10725a030e60Sflorian if (lifetime > MAX_RTR_SOLICITATIONS * 10735a030e60Sflorian (RTR_SOLICITATION_INTERVAL + 1)) 10745a030e60Sflorian addr_proposal->timo.tv_sec = lifetime - 10755a030e60Sflorian MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; 10765a030e60Sflorian else 10775a030e60Sflorian addr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; 10785a030e60Sflorian break; 10795a030e60Sflorian case PROPOSAL_NEARLY_EXPIRED: 10805a030e60Sflorian lifetime = real_lifetime(&addr_proposal->uptime, 10815a030e60Sflorian addr_proposal->pltime); 10825a030e60Sflorian if (lifetime == 0) 10835a030e60Sflorian lifetime = real_lifetime(&addr_proposal->uptime, 10845a030e60Sflorian addr_proposal->vltime); 10855a030e60Sflorian if (lifetime > MAX_RTR_SOLICITATIONS * 10865a030e60Sflorian (RTR_SOLICITATION_INTERVAL + 1)) 10875a030e60Sflorian addr_proposal->timo.tv_sec = lifetime - 10885a030e60Sflorian MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; 10895a030e60Sflorian else 10905a030e60Sflorian addr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; 10915a030e60Sflorian request_solicitation(iface); 10925a030e60Sflorian break; 10935a030e60Sflorian case PROPOSAL_WITHDRAWN: 10945a030e60Sflorian withdraw_addr(addr_proposal); 10955a030e60Sflorian addr_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS * 10965a030e60Sflorian RTR_SOLICITATION_INTERVAL; 10975a030e60Sflorian break; 10985a030e60Sflorian case PROPOSAL_DUPLICATED: 10995a030e60Sflorian addr_proposal->timo.tv_sec = 0; 11005a030e60Sflorian break; 11015a030e60Sflorian case PROPOSAL_STALE: 11025a030e60Sflorian addr_proposal->timo.tv_sec = 0; /* remove immediately */ 11035a030e60Sflorian break; 11045a030e60Sflorian } 11055a030e60Sflorian 1106cf6341ecSflorian if (log_getverbose()) { 1107cf6341ecSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 11085a030e60Sflorian if_name = if_indextoname(addr_proposal->if_index, ifnamebuf); 1109cf6341ecSflorian log_debug("%s[%s] %s -> %s, timo: %lld", __func__, 1110cf6341ecSflorian if_name == NULL ? "?" : if_name, 1111cf6341ecSflorian proposal_state_name(old_state), 1112cf6341ecSflorian proposal_state_name(new_state), addr_proposal->timo.tv_sec); 1113cf6341ecSflorian } 11145a030e60Sflorian 11155a030e60Sflorian if (addr_proposal->timo.tv_sec == -1) { 11165a030e60Sflorian if (evtimer_pending(&addr_proposal->timer, NULL)) 11175a030e60Sflorian evtimer_del(&addr_proposal->timer); 11185a030e60Sflorian } else 11195a030e60Sflorian evtimer_add(&addr_proposal->timer, &addr_proposal->timo); 11205a030e60Sflorian } 11215a030e60Sflorian 11225a030e60Sflorian void dfr_proposal_state_transition(struct dfr_proposal *dfr_proposal, 11235a030e60Sflorian enum proposal_state new_state) 11245a030e60Sflorian { 11255a030e60Sflorian enum proposal_state old_state = dfr_proposal->state; 11265a030e60Sflorian struct slaacd_iface *iface; 11275a030e60Sflorian uint32_t lifetime; 11285a030e60Sflorian 11295a030e60Sflorian dfr_proposal->state = new_state; 11305a030e60Sflorian 11315a030e60Sflorian if ((iface = get_slaacd_iface_by_id(dfr_proposal->if_index)) == NULL) 11325a030e60Sflorian return; 11335a030e60Sflorian 11345a030e60Sflorian switch (dfr_proposal->state) { 11355a030e60Sflorian case PROPOSAL_IF_DOWN: 11365a030e60Sflorian if (old_state == PROPOSAL_IF_DOWN) { 11375a030e60Sflorian withdraw_dfr(dfr_proposal); 11385a030e60Sflorian dfr_proposal->timo.tv_sec = -1; 11395a030e60Sflorian } else { 11405a030e60Sflorian dfr_proposal->timo.tv_sec = 11415a030e60Sflorian real_lifetime(&dfr_proposal->uptime, 11425a030e60Sflorian dfr_proposal->router_lifetime); 11435a030e60Sflorian } 11445a030e60Sflorian break; 11455a030e60Sflorian case PROPOSAL_NOT_CONFIGURED: 11465a030e60Sflorian break; 11475a030e60Sflorian case PROPOSAL_CONFIGURED: 11485a030e60Sflorian lifetime = real_lifetime(&dfr_proposal->uptime, 11495a030e60Sflorian dfr_proposal->router_lifetime); 11505a030e60Sflorian if (lifetime > MAX_RTR_SOLICITATIONS * 11515a030e60Sflorian (RTR_SOLICITATION_INTERVAL + 1)) 11525a030e60Sflorian dfr_proposal->timo.tv_sec = lifetime - 11535a030e60Sflorian MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; 11545a030e60Sflorian else 11555a030e60Sflorian dfr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; 11565a030e60Sflorian break; 11575a030e60Sflorian case PROPOSAL_NEARLY_EXPIRED: 11585a030e60Sflorian lifetime = real_lifetime(&dfr_proposal->uptime, 11595a030e60Sflorian dfr_proposal->router_lifetime); 11605a030e60Sflorian if (lifetime > MAX_RTR_SOLICITATIONS * 11615a030e60Sflorian (RTR_SOLICITATION_INTERVAL + 1)) 11625a030e60Sflorian dfr_proposal->timo.tv_sec = lifetime - 11635a030e60Sflorian MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; 11645a030e60Sflorian else 11655a030e60Sflorian dfr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; 11665a030e60Sflorian request_solicitation(iface); 11675a030e60Sflorian break; 11685a030e60Sflorian case PROPOSAL_WITHDRAWN: 11695a030e60Sflorian withdraw_dfr(dfr_proposal); 11705a030e60Sflorian dfr_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS * 11715a030e60Sflorian RTR_SOLICITATION_INTERVAL; 11725a030e60Sflorian break; 11735a030e60Sflorian case PROPOSAL_STALE: 11745a030e60Sflorian dfr_proposal->timo.tv_sec = 0; /* remove immediately */ 11755a030e60Sflorian break; 11765a030e60Sflorian case PROPOSAL_DUPLICATED: 11775a030e60Sflorian fatalx("invalid dfr state: PROPOSAL_DUPLICATED"); 11785a030e60Sflorian break; 11795a030e60Sflorian } 11805a030e60Sflorian 1181cf6341ecSflorian if (log_getverbose()) { 1182cf6341ecSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 1183cf6341ecSflorian 11845a030e60Sflorian if_name = if_indextoname(dfr_proposal->if_index, ifnamebuf); 1185cf6341ecSflorian log_debug("%s[%s] %s -> %s, timo: %lld", __func__, 1186cf6341ecSflorian if_name == NULL ? "?" : if_name, 1187cf6341ecSflorian proposal_state_name(old_state), 1188cf6341ecSflorian proposal_state_name(new_state), dfr_proposal->timo.tv_sec); 1189cf6341ecSflorian } 11905a030e60Sflorian 11915a030e60Sflorian if (dfr_proposal->timo.tv_sec == -1) { 11925a030e60Sflorian if (evtimer_pending(&dfr_proposal->timer, NULL)) 11935a030e60Sflorian evtimer_del(&dfr_proposal->timer); 11945a030e60Sflorian } else 11955a030e60Sflorian evtimer_add(&dfr_proposal->timer, &dfr_proposal->timo); 11965a030e60Sflorian 11975a030e60Sflorian } 11985a030e60Sflorian 11995a030e60Sflorian void rdns_proposal_state_transition(struct rdns_proposal *rdns_proposal, 12005a030e60Sflorian enum proposal_state new_state) 12015a030e60Sflorian { 12025a030e60Sflorian enum proposal_state old_state = rdns_proposal->state; 12035a030e60Sflorian struct slaacd_iface *iface; 12045a030e60Sflorian uint32_t lifetime; 12055a030e60Sflorian 12065a030e60Sflorian rdns_proposal->state = new_state; 12075a030e60Sflorian 12085a030e60Sflorian if ((iface = get_slaacd_iface_by_id(rdns_proposal->if_index)) == NULL) 12095a030e60Sflorian return; 12105a030e60Sflorian 12115a030e60Sflorian switch (rdns_proposal->state) { 12125a030e60Sflorian case PROPOSAL_IF_DOWN: 12135a030e60Sflorian if (old_state == PROPOSAL_IF_DOWN) { 12145a030e60Sflorian withdraw_rdns(rdns_proposal); 12155a030e60Sflorian rdns_proposal->timo.tv_sec = -1; 12165a030e60Sflorian } else { 12175a030e60Sflorian rdns_proposal->timo.tv_sec = 12185a030e60Sflorian real_lifetime(&rdns_proposal->uptime, 12195a030e60Sflorian rdns_proposal->rdns_lifetime); 12205a030e60Sflorian } 12215a030e60Sflorian break; 12225a030e60Sflorian case PROPOSAL_NOT_CONFIGURED: 12235a030e60Sflorian break; 12245a030e60Sflorian case PROPOSAL_CONFIGURED: 12255a030e60Sflorian lifetime = real_lifetime(&rdns_proposal->uptime, 12265a030e60Sflorian rdns_proposal->rdns_lifetime); 12275a030e60Sflorian if (lifetime > MAX_RTR_SOLICITATIONS * 12285a030e60Sflorian (RTR_SOLICITATION_INTERVAL + 1)) 12295a030e60Sflorian rdns_proposal->timo.tv_sec = lifetime - 12305a030e60Sflorian MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; 12315a030e60Sflorian else 12325a030e60Sflorian rdns_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; 12335a030e60Sflorian break; 12345a030e60Sflorian case PROPOSAL_NEARLY_EXPIRED: 12355a030e60Sflorian lifetime = real_lifetime(&rdns_proposal->uptime, 12365a030e60Sflorian rdns_proposal->rdns_lifetime); 12375a030e60Sflorian if (lifetime > MAX_RTR_SOLICITATIONS * 12385a030e60Sflorian (RTR_SOLICITATION_INTERVAL + 1)) 12395a030e60Sflorian rdns_proposal->timo.tv_sec = lifetime - 12405a030e60Sflorian MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; 12415a030e60Sflorian else 12425a030e60Sflorian rdns_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; 12435a030e60Sflorian request_solicitation(iface); 12445a030e60Sflorian break; 12455a030e60Sflorian case PROPOSAL_WITHDRAWN: 12465a030e60Sflorian withdraw_rdns(rdns_proposal); 12475a030e60Sflorian rdns_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS * 12485a030e60Sflorian RTR_SOLICITATION_INTERVAL; 12495a030e60Sflorian break; 12505a030e60Sflorian case PROPOSAL_STALE: 12515a030e60Sflorian rdns_proposal->timo.tv_sec = 0; /* remove immediately */ 12525a030e60Sflorian break; 12535a030e60Sflorian case PROPOSAL_DUPLICATED: 12545a030e60Sflorian fatalx("invalid rdns state: PROPOSAL_DUPLICATED"); 12555a030e60Sflorian break; 12565a030e60Sflorian } 12575a030e60Sflorian 1258cf6341ecSflorian if (log_getverbose()) { 1259cf6341ecSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 1260cf6341ecSflorian 12615a030e60Sflorian if_name = if_indextoname(rdns_proposal->if_index, ifnamebuf); 1262cf6341ecSflorian log_debug("%s[%s] %s -> %s, timo: %lld", __func__, 1263cf6341ecSflorian if_name == NULL ? "?" : if_name, 1264cf6341ecSflorian proposal_state_name(old_state), 1265cf6341ecSflorian proposal_state_name(new_state), rdns_proposal->timo.tv_sec); 1266cf6341ecSflorian } 12675a030e60Sflorian 12685a030e60Sflorian if (rdns_proposal->timo.tv_sec == -1) { 12695a030e60Sflorian if (evtimer_pending(&rdns_proposal->timer, NULL)) 12705a030e60Sflorian evtimer_del(&rdns_proposal->timer); 12715a030e60Sflorian } else 12725a030e60Sflorian evtimer_add(&rdns_proposal->timer, &rdns_proposal->timo); 12735a030e60Sflorian } 12745a030e60Sflorian 12755a030e60Sflorian void 12765a030e60Sflorian request_solicitation(struct slaacd_iface *iface) 12775a030e60Sflorian { 12785a030e60Sflorian struct timespec now, diff, sol_delay = {RTR_SOLICITATION_INTERVAL, 0}; 12795a030e60Sflorian 12805a030e60Sflorian clock_gettime(CLOCK_MONOTONIC, &now); 12815a030e60Sflorian timespecsub(&now, &iface->last_sol, &diff); 12825a030e60Sflorian if (timespeccmp(&diff, &sol_delay, <)) { 1283cee6cd9cSflorian log_debug("last solicitation less than %d seconds ago", 12845a030e60Sflorian RTR_SOLICITATION_INTERVAL); 12855a030e60Sflorian return; 12865a030e60Sflorian } 12875a030e60Sflorian 12885a030e60Sflorian iface->last_sol = now; 12895a030e60Sflorian engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, 0, 12905a030e60Sflorian &iface->if_index, sizeof(iface->if_index)); 12915a030e60Sflorian } 12925a030e60Sflorian 12935a030e60Sflorian void 1294883c684aSflorian engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) 1295883c684aSflorian { 1296883c684aSflorian struct slaacd_iface *iface; 12973c3fa1cbSflorian int need_refresh = 0; 1298883c684aSflorian 1299883c684aSflorian iface = get_slaacd_iface_by_id(imsg_ifinfo->if_index); 1300883c684aSflorian if (iface == NULL) { 1301883c684aSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 1302883c684aSflorian fatal("calloc"); 13033c3fa1cbSflorian iface->state = IF_DOWN; 13045a030e60Sflorian iface->timo.tv_usec = arc4random_uniform(1000000); 1305883c684aSflorian evtimer_set(&iface->timer, iface_timeout, iface); 1306883c684aSflorian iface->if_index = imsg_ifinfo->if_index; 1307883c684aSflorian iface->rdomain = imsg_ifinfo->rdomain; 1308883c684aSflorian iface->running = imsg_ifinfo->running; 13098a8bcb8bSflorian iface->link_state = imsg_ifinfo->link_state; 1310c6384676Sflorian iface->autoconf = imsg_ifinfo->autoconf; 1311804ba004Sflorian iface->temporary = imsg_ifinfo->temporary; 1312883c684aSflorian iface->soii = imsg_ifinfo->soii; 1313883c684aSflorian memcpy(&iface->hw_address, &imsg_ifinfo->hw_address, 1314883c684aSflorian sizeof(struct ether_addr)); 1315883c684aSflorian memcpy(&iface->ll_address, &imsg_ifinfo->ll_address, 1316883c684aSflorian sizeof(struct sockaddr_in6)); 1317883c684aSflorian memcpy(iface->soiikey, imsg_ifinfo->soiikey, 1318883c684aSflorian sizeof(iface->soiikey)); 1319883c684aSflorian LIST_INIT(&iface->radvs); 1320883c684aSflorian LIST_INSERT_HEAD(&slaacd_interfaces, iface, entries); 1321883c684aSflorian LIST_INIT(&iface->addr_proposals); 1322883c684aSflorian LIST_INIT(&iface->dfr_proposals); 1323883c684aSflorian LIST_INIT(&iface->rdns_proposals); 13243c3fa1cbSflorian need_refresh = 1; 1325883c684aSflorian } else { 13263c3fa1cbSflorian memcpy(&iface->ll_address, &imsg_ifinfo->ll_address, 13273c3fa1cbSflorian sizeof(struct sockaddr_in6)); 1328883c684aSflorian 1329c6384676Sflorian if (iface->autoconf != imsg_ifinfo->autoconf) { 1330c6384676Sflorian iface->autoconf = imsg_ifinfo->autoconf; 1331c6384676Sflorian need_refresh = 1; 1332c6384676Sflorian } 1333c6384676Sflorian 1334804ba004Sflorian if (iface->temporary != imsg_ifinfo->temporary) { 1335804ba004Sflorian iface->temporary = imsg_ifinfo->temporary; 1336883c684aSflorian need_refresh = 1; 1337883c684aSflorian } 1338883c684aSflorian 1339883c684aSflorian if (iface->soii != imsg_ifinfo->soii) { 1340883c684aSflorian iface->soii = imsg_ifinfo->soii; 1341883c684aSflorian need_refresh = 1; 1342883c684aSflorian } 1343883c684aSflorian 1344883c684aSflorian if (memcmp(&iface->hw_address, &imsg_ifinfo->hw_address, 1345883c684aSflorian sizeof(struct ether_addr)) != 0) { 1346883c684aSflorian memcpy(&iface->hw_address, &imsg_ifinfo->hw_address, 1347883c684aSflorian sizeof(struct ether_addr)); 1348883c684aSflorian need_refresh = 1; 1349883c684aSflorian } 13503c3fa1cbSflorian 1351883c684aSflorian if (memcmp(iface->soiikey, imsg_ifinfo->soiikey, 1352883c684aSflorian sizeof(iface->soiikey)) != 0) { 1353883c684aSflorian memcpy(iface->soiikey, imsg_ifinfo->soiikey, 1354883c684aSflorian sizeof(iface->soiikey)); 1355883c684aSflorian need_refresh = 1; 1356883c684aSflorian } 1357883c684aSflorian 13583c3fa1cbSflorian if (imsg_ifinfo->running != iface->running) { 13593c3fa1cbSflorian iface->running = imsg_ifinfo->running; 13603c3fa1cbSflorian need_refresh = 1; 13613c3fa1cbSflorian } 13628a8bcb8bSflorian if (imsg_ifinfo->link_state != iface->link_state) { 13638a8bcb8bSflorian iface->link_state = imsg_ifinfo->link_state; 13648a8bcb8bSflorian need_refresh = 1; 13658a8bcb8bSflorian } 13663c3fa1cbSflorian } 13673c3fa1cbSflorian 13683c3fa1cbSflorian if (!need_refresh) 13693c3fa1cbSflorian return; 13703c3fa1cbSflorian 13718a8bcb8bSflorian if (iface->running && LINK_STATE_IS_UP(iface->link_state)) 13725a030e60Sflorian iface_state_transition(iface, IF_INIT); 1373883c684aSflorian 13745a030e60Sflorian else 13755a030e60Sflorian iface_state_transition(iface, IF_DOWN); 1376883c684aSflorian } 1377883c684aSflorian 1378883c684aSflorian void 13790acf3e2dSflorian parse_ra(struct slaacd_iface *iface, struct imsg_ra *ra) 13800acf3e2dSflorian { 1381dd19964dSflorian struct icmp6_hdr *icmp6_hdr; 13820acf3e2dSflorian struct nd_router_advert *nd_ra; 13830acf3e2dSflorian struct radv *radv; 13840acf3e2dSflorian struct radv_prefix *prefix; 13850acf3e2dSflorian struct radv_rdns *rdns; 13860acf3e2dSflorian ssize_t len = ra->len; 13872f9dc02cSflorian const char *hbuf; 13880acf3e2dSflorian uint8_t *p; 13890acf3e2dSflorian 1390012bf552Sflorian #ifndef SMALL 1391012bf552Sflorian if (log_getverbose() > 1) 13920acf3e2dSflorian debug_log_ra(ra); 1393012bf552Sflorian #endif /* SMALL */ 13940acf3e2dSflorian 13952f9dc02cSflorian hbuf = sin6_to_str(&ra->from); 1396dd19964dSflorian if ((size_t)len < sizeof(struct icmp6_hdr)) { 1397dd19964dSflorian log_warnx("received too short message (%ld) from %s", len, 1398dd19964dSflorian hbuf); 1399dd19964dSflorian return; 1400dd19964dSflorian } 1401dd19964dSflorian 1402dd19964dSflorian p = ra->packet; 1403dd19964dSflorian icmp6_hdr = (struct icmp6_hdr *)p; 1404dd19964dSflorian if (icmp6_hdr->icmp6_type != ND_ROUTER_ADVERT) 1405dd19964dSflorian return; 1406dd19964dSflorian 14070acf3e2dSflorian if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) { 14083b6c4256Sflorian log_debug("RA from non link local address %s", hbuf); 14090acf3e2dSflorian return; 14100acf3e2dSflorian } 14110acf3e2dSflorian 14120acf3e2dSflorian if ((size_t)len < sizeof(struct nd_router_advert)) { 14132ac62a75Sflorian log_warnx("received too short message (%ld) from %s", len, 14142ac62a75Sflorian hbuf); 14150acf3e2dSflorian return; 14160acf3e2dSflorian } 14170acf3e2dSflorian 14180acf3e2dSflorian if ((radv = calloc(1, sizeof(*radv))) == NULL) 14190acf3e2dSflorian fatal("calloc"); 14200acf3e2dSflorian 14210acf3e2dSflorian LIST_INIT(&radv->prefixes); 14220acf3e2dSflorian LIST_INIT(&radv->rdns_servers); 14230acf3e2dSflorian 14240acf3e2dSflorian radv->min_lifetime = UINT32_MAX; 14250acf3e2dSflorian 14260acf3e2dSflorian nd_ra = (struct nd_router_advert *)p; 14270acf3e2dSflorian len -= sizeof(struct nd_router_advert); 14280acf3e2dSflorian p += sizeof(struct nd_router_advert); 14290acf3e2dSflorian 14300acf3e2dSflorian log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld", 14310acf3e2dSflorian nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len); 14320acf3e2dSflorian 14330acf3e2dSflorian if (nd_ra->nd_ra_code != 0) { 14342ac62a75Sflorian log_warnx("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code, 14350acf3e2dSflorian hbuf); 14360acf3e2dSflorian goto err; 14370acf3e2dSflorian } 14380acf3e2dSflorian 14390acf3e2dSflorian memcpy(&radv->from, &ra->from, sizeof(ra->from)); 14400acf3e2dSflorian 14410acf3e2dSflorian if (clock_gettime(CLOCK_REALTIME, &radv->when)) 14420acf3e2dSflorian fatal("clock_gettime"); 14430acf3e2dSflorian if (clock_gettime(CLOCK_MONOTONIC, &radv->uptime)) 14440acf3e2dSflorian fatal("clock_gettime"); 14450acf3e2dSflorian 14460acf3e2dSflorian radv->curhoplimit = nd_ra->nd_ra_curhoplimit; 14470acf3e2dSflorian radv->managed = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED; 14480acf3e2dSflorian radv->other = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER; 14490acf3e2dSflorian 14500acf3e2dSflorian switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) { 14510acf3e2dSflorian case ND_RA_FLAG_RTPREF_HIGH: 14520acf3e2dSflorian radv->rpref=HIGH; 14530acf3e2dSflorian break; 14540acf3e2dSflorian case ND_RA_FLAG_RTPREF_LOW: 14550acf3e2dSflorian radv->rpref=LOW; 14560acf3e2dSflorian break; 14570acf3e2dSflorian case ND_RA_FLAG_RTPREF_MEDIUM: 14580acf3e2dSflorian /* fallthrough */ 14590acf3e2dSflorian default: 14600acf3e2dSflorian radv->rpref=MEDIUM; 14610acf3e2dSflorian break; 14620acf3e2dSflorian } 14630acf3e2dSflorian radv->router_lifetime = ntohs(nd_ra->nd_ra_router_lifetime); 14640acf3e2dSflorian if (radv->router_lifetime != 0) 14650acf3e2dSflorian radv->min_lifetime = radv->router_lifetime; 14660acf3e2dSflorian radv->reachable_time = ntohl(nd_ra->nd_ra_reachable); 14670acf3e2dSflorian radv->retrans_time = ntohl(nd_ra->nd_ra_retransmit); 14680acf3e2dSflorian 14690acf3e2dSflorian while ((size_t)len >= sizeof(struct nd_opt_hdr)) { 14700acf3e2dSflorian struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p; 14710acf3e2dSflorian struct nd_opt_prefix_info *prf; 14720acf3e2dSflorian struct nd_opt_rdnss *rdnss; 14732d9ad356Sbket struct nd_opt_mtu *mtu; 14740acf3e2dSflorian struct in6_addr *in6; 14750acf3e2dSflorian int i; 14760acf3e2dSflorian 14770acf3e2dSflorian len -= sizeof(struct nd_opt_hdr); 14780acf3e2dSflorian p += sizeof(struct nd_opt_hdr); 14790acf3e2dSflorian 14800acf3e2dSflorian if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) { 14810acf3e2dSflorian log_warnx("invalid option len: %u > %ld", 14820acf3e2dSflorian nd_opt_hdr->nd_opt_len, len); 14830acf3e2dSflorian goto err; 14840acf3e2dSflorian } 14850acf3e2dSflorian 14860acf3e2dSflorian switch (nd_opt_hdr->nd_opt_type) { 14870acf3e2dSflorian case ND_OPT_PREFIX_INFORMATION: 14880acf3e2dSflorian if (nd_opt_hdr->nd_opt_len != 4) { 14892ac62a75Sflorian log_warnx("invalid ND_OPT_PREFIX_INFORMATION: " 14900acf3e2dSflorian "len != 4"); 14910acf3e2dSflorian goto err; 14920acf3e2dSflorian } 14930acf3e2dSflorian 14940acf3e2dSflorian if ((prefix = calloc(1, sizeof(*prefix))) == NULL) 14950acf3e2dSflorian fatal("calloc"); 14960acf3e2dSflorian 14970acf3e2dSflorian prf = (struct nd_opt_prefix_info*) nd_opt_hdr; 14980acf3e2dSflorian prefix->prefix = prf->nd_opt_pi_prefix; 14990acf3e2dSflorian prefix->prefix_len = prf->nd_opt_pi_prefix_len; 15000acf3e2dSflorian prefix->onlink = prf->nd_opt_pi_flags_reserved & 15010acf3e2dSflorian ND_OPT_PI_FLAG_ONLINK; 15020acf3e2dSflorian prefix->autonomous = prf->nd_opt_pi_flags_reserved & 15030acf3e2dSflorian ND_OPT_PI_FLAG_AUTO; 15040acf3e2dSflorian prefix->vltime = ntohl(prf->nd_opt_pi_valid_time); 15050acf3e2dSflorian prefix->pltime = ntohl(prf->nd_opt_pi_preferred_time); 15060acf3e2dSflorian if (radv->min_lifetime > prefix->pltime) 15070acf3e2dSflorian radv->min_lifetime = prefix->pltime; 15080acf3e2dSflorian 15090acf3e2dSflorian LIST_INSERT_HEAD(&radv->prefixes, prefix, entries); 15100acf3e2dSflorian 15110acf3e2dSflorian break; 15120acf3e2dSflorian 15130acf3e2dSflorian case ND_OPT_RDNSS: 15140acf3e2dSflorian if (nd_opt_hdr->nd_opt_len < 3) { 15150acf3e2dSflorian log_warnx("invalid ND_OPT_RDNSS: len < 24"); 15160acf3e2dSflorian goto err; 15170acf3e2dSflorian } 15180acf3e2dSflorian 15190acf3e2dSflorian if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) { 15200acf3e2dSflorian log_warnx("invalid ND_OPT_RDNSS: length with" 15210acf3e2dSflorian "out header is not multiply of 16: %d", 15220acf3e2dSflorian (nd_opt_hdr->nd_opt_len - 1) * 8); 15230acf3e2dSflorian goto err; 15240acf3e2dSflorian } 15250acf3e2dSflorian 15260acf3e2dSflorian rdnss = (struct nd_opt_rdnss*) nd_opt_hdr; 15270acf3e2dSflorian 15280acf3e2dSflorian radv->rdns_lifetime = ntohl( 15290acf3e2dSflorian rdnss->nd_opt_rdnss_lifetime); 15300acf3e2dSflorian if (radv->min_lifetime > radv->rdns_lifetime) 15310acf3e2dSflorian radv->min_lifetime = radv->rdns_lifetime; 15320acf3e2dSflorian 15330acf3e2dSflorian in6 = (struct in6_addr*) (p + 6); 15340acf3e2dSflorian for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++, 15350acf3e2dSflorian in6++) { 15360acf3e2dSflorian if ((rdns = calloc(1, sizeof(*rdns))) == NULL) 15370acf3e2dSflorian fatal("calloc"); 15380acf3e2dSflorian memcpy(&rdns->rdns, in6, sizeof(rdns->rdns)); 15390acf3e2dSflorian LIST_INSERT_HEAD(&radv->rdns_servers, rdns, 15400acf3e2dSflorian entries); 15410acf3e2dSflorian } 15420acf3e2dSflorian break; 15432d9ad356Sbket case ND_OPT_MTU: 15442d9ad356Sbket if (nd_opt_hdr->nd_opt_len != 1) { 15452d9ad356Sbket log_warnx("invalid ND_OPT_MTU: len != 1"); 15462d9ad356Sbket goto err; 15472d9ad356Sbket } 15482d9ad356Sbket mtu = (struct nd_opt_mtu*) nd_opt_hdr; 15492d9ad356Sbket radv->mtu = ntohl(mtu->nd_opt_mtu_mtu); 15502d9ad356Sbket 15512d9ad356Sbket /* path MTU cannot be less than IPV6_MMTU */ 15522d9ad356Sbket if (radv->mtu < IPV6_MMTU) { 15532d9ad356Sbket radv->mtu = 0; 15542d9ad356Sbket log_warnx("invalid advertised MTU"); 15552d9ad356Sbket } 15562d9ad356Sbket 15572d9ad356Sbket break; 15583a8f32edSflorian case ND_OPT_DNSSL: 15590acf3e2dSflorian case ND_OPT_REDIRECTED_HEADER: 15600acf3e2dSflorian case ND_OPT_SOURCE_LINKADDR: 15610acf3e2dSflorian case ND_OPT_TARGET_LINKADDR: 15620acf3e2dSflorian case ND_OPT_ROUTE_INFO: 15630acf3e2dSflorian #if 0 15640acf3e2dSflorian log_debug("\tOption: %u (len: %u) not implemented", 15650acf3e2dSflorian nd_opt_hdr->nd_opt_type, nd_opt_hdr->nd_opt_len * 15660acf3e2dSflorian 8); 15670acf3e2dSflorian #endif 15680acf3e2dSflorian break; 15690acf3e2dSflorian default: 15700acf3e2dSflorian log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type); 15710acf3e2dSflorian break; 15720acf3e2dSflorian 15730acf3e2dSflorian } 15740acf3e2dSflorian len -= nd_opt_hdr->nd_opt_len * 8 - 2; 15750acf3e2dSflorian p += nd_opt_hdr->nd_opt_len * 8 - 2; 15760acf3e2dSflorian } 15770acf3e2dSflorian update_iface_ra(iface, radv); 15780acf3e2dSflorian return; 15790acf3e2dSflorian 15800acf3e2dSflorian err: 15810acf3e2dSflorian free_ra(radv); 15820acf3e2dSflorian } 15830acf3e2dSflorian 15840acf3e2dSflorian void 15850acf3e2dSflorian gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct 1586804ba004Sflorian address_proposal *addr_proposal, int temporary) 15870acf3e2dSflorian { 1588b9b376fbSflorian SHA2_CTX ctx; 1589bddc80baSphessler struct in6_addr iid; 1590ef26bdebSflorian int i; 1591b9b376fbSflorian u_int8_t digest[SHA512_DIGEST_LENGTH]; 15920acf3e2dSflorian 15933c1b2505Sflorian memset(&iid, 0, sizeof(iid)); 15943c1b2505Sflorian 15950acf3e2dSflorian /* from in6_ifadd() in nd6_rtr.c */ 15960acf3e2dSflorian /* XXX from in6.h, guarded by #ifdef _KERNEL XXX nonstandard */ 15970acf3e2dSflorian #define s6_addr32 __u6_addr.__u6_addr32 15980acf3e2dSflorian 15990acf3e2dSflorian in6_prefixlen2mask(&addr_proposal->mask, addr_proposal->prefix_len); 16000acf3e2dSflorian 16010acf3e2dSflorian memset(&addr_proposal->addr, 0, sizeof(addr_proposal->addr)); 16020acf3e2dSflorian 16030acf3e2dSflorian addr_proposal->addr.sin6_family = AF_INET6; 16040acf3e2dSflorian addr_proposal->addr.sin6_len = sizeof(addr_proposal->addr); 16050acf3e2dSflorian 16060acf3e2dSflorian memcpy(&addr_proposal->addr.sin6_addr, &prefix->prefix, 16070acf3e2dSflorian sizeof(addr_proposal->addr.sin6_addr)); 16080acf3e2dSflorian 1609ef26bdebSflorian for (i = 0; i < 4; i++) 1610ef26bdebSflorian addr_proposal->addr.sin6_addr.s6_addr32[i] &= 1611ef26bdebSflorian addr_proposal->mask.s6_addr32[i]; 16120acf3e2dSflorian 1613804ba004Sflorian if (temporary) { 1614bddc80baSphessler arc4random_buf(&iid.s6_addr, sizeof(iid.s6_addr)); 1615bddc80baSphessler } else if (iface->soii) { 1616b9b376fbSflorian SHA512Init(&ctx); 1617b9b376fbSflorian SHA512Update(&ctx, &prefix->prefix, 1618b9b376fbSflorian sizeof(prefix->prefix)); 1619b9b376fbSflorian SHA512Update(&ctx, &iface->hw_address, 1620b9b376fbSflorian sizeof(iface->hw_address)); 162105b87f88Sflorian SHA512Update(&ctx, &prefix->dad_counter, 162205b87f88Sflorian sizeof(prefix->dad_counter)); 1623b9b376fbSflorian SHA512Update(&ctx, addr_proposal->soiikey, 1624b9b376fbSflorian sizeof(addr_proposal->soiikey)); 1625b9b376fbSflorian SHA512Final(digest, &ctx); 1626bddc80baSphessler 16273c1b2505Sflorian memcpy(&iid.s6_addr, digest + (sizeof(digest) - 16283c1b2505Sflorian sizeof(iid.s6_addr)), sizeof(iid.s6_addr)); 1629b9b376fbSflorian } else { 1630bddc80baSphessler /* This is safe, because we have a 64 prefix len */ 1631bddc80baSphessler memcpy(&iid.s6_addr, &iface->ll_address.sin6_addr, 1632bddc80baSphessler sizeof(iid.s6_addr)); 1633bddc80baSphessler } 1634bddc80baSphessler 1635ef26bdebSflorian for (i = 0; i < 4; i++) 1636ef26bdebSflorian addr_proposal->addr.sin6_addr.s6_addr32[i] |= 1637ef26bdebSflorian (iid.s6_addr32[i] & ~addr_proposal->mask.s6_addr32[i]); 16380acf3e2dSflorian #undef s6_addr32 16390acf3e2dSflorian } 16400acf3e2dSflorian 16410acf3e2dSflorian /* from sys/netinet6/in6.c */ 16420acf3e2dSflorian void 16430acf3e2dSflorian in6_prefixlen2mask(struct in6_addr *maskp, int len) 16440acf3e2dSflorian { 16450acf3e2dSflorian u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; 16460acf3e2dSflorian int bytelen, bitlen, i; 16470acf3e2dSflorian 16480acf3e2dSflorian if (0 > len || len > 128) 1649325205b9Spamela fatalx("%s: invalid prefix length(%d)\n", __func__, len); 16500acf3e2dSflorian 16510acf3e2dSflorian bzero(maskp, sizeof(*maskp)); 16520acf3e2dSflorian bytelen = len / 8; 16530acf3e2dSflorian bitlen = len % 8; 16540acf3e2dSflorian for (i = 0; i < bytelen; i++) 16550acf3e2dSflorian maskp->s6_addr[i] = 0xff; 16560acf3e2dSflorian /* len == 128 is ok because bitlen == 0 then */ 16570acf3e2dSflorian if (bitlen) 16580acf3e2dSflorian maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; 16590acf3e2dSflorian } 16600acf3e2dSflorian 166186f19603Sflorian #ifndef SMALL 1662060a7308Sflorian /* from kame via ifconfig, where it's called prefix() */ 1663060a7308Sflorian int 1664060a7308Sflorian in6_mask2prefixlen(struct in6_addr *in6) 1665060a7308Sflorian { 1666060a7308Sflorian u_char *nam = (u_char *)in6; 1667060a7308Sflorian int byte, bit, plen = 0, size = sizeof(struct in6_addr); 1668060a7308Sflorian 1669060a7308Sflorian for (byte = 0; byte < size; byte++, plen += 8) 1670060a7308Sflorian if (nam[byte] != 0xff) 1671060a7308Sflorian break; 1672060a7308Sflorian if (byte == size) 1673060a7308Sflorian return (plen); 1674060a7308Sflorian for (bit = 7; bit != 0; bit--, plen++) 1675060a7308Sflorian if (!(nam[byte] & (1 << bit))) 1676060a7308Sflorian break; 1677060a7308Sflorian for (; bit != 0; bit--) 1678060a7308Sflorian if (nam[byte] & (1 << bit)) 1679060a7308Sflorian return (0); 1680060a7308Sflorian byte++; 1681060a7308Sflorian for (; byte < size; byte++) 1682060a7308Sflorian if (nam[byte]) 1683060a7308Sflorian return (0); 1684060a7308Sflorian return (plen); 1685060a7308Sflorian } 1686060a7308Sflorian 16870acf3e2dSflorian void 16880acf3e2dSflorian debug_log_ra(struct imsg_ra *ra) 16890acf3e2dSflorian { 16900acf3e2dSflorian struct nd_router_advert *nd_ra; 16910acf3e2dSflorian ssize_t len = ra->len; 16922f9dc02cSflorian char ntopbuf[INET6_ADDRSTRLEN]; 16932f9dc02cSflorian const char *hbuf; 16940acf3e2dSflorian uint8_t *p; 16950acf3e2dSflorian 16962f9dc02cSflorian hbuf = sin6_to_str(&ra->from); 16970acf3e2dSflorian 16980acf3e2dSflorian if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) { 16992ac62a75Sflorian log_warnx("RA from non link local address %s", hbuf); 17000acf3e2dSflorian return; 17010acf3e2dSflorian } 17020acf3e2dSflorian 17030acf3e2dSflorian if ((size_t)len < sizeof(struct nd_router_advert)) { 17042ac62a75Sflorian log_warnx("received too short message (%ld) from %s", len, 17052ac62a75Sflorian hbuf); 17060acf3e2dSflorian return; 17070acf3e2dSflorian } 17080acf3e2dSflorian 17090acf3e2dSflorian p = ra->packet; 17100acf3e2dSflorian nd_ra = (struct nd_router_advert *)p; 17110acf3e2dSflorian len -= sizeof(struct nd_router_advert); 17120acf3e2dSflorian p += sizeof(struct nd_router_advert); 17130acf3e2dSflorian 17140acf3e2dSflorian log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld", 17150acf3e2dSflorian nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len); 17160acf3e2dSflorian 17170acf3e2dSflorian if (nd_ra->nd_ra_type != ND_ROUTER_ADVERT) { 17180acf3e2dSflorian log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_type, 17190acf3e2dSflorian hbuf); 17200acf3e2dSflorian return; 17210acf3e2dSflorian } 17220acf3e2dSflorian 17230acf3e2dSflorian if (nd_ra->nd_ra_code != 0) { 17242ac62a75Sflorian log_warnx("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code, 17250acf3e2dSflorian hbuf); 17260acf3e2dSflorian return; 17270acf3e2dSflorian } 17280acf3e2dSflorian 17290acf3e2dSflorian log_debug("---"); 17300acf3e2dSflorian log_debug("RA from %s", hbuf); 17310acf3e2dSflorian log_debug("\tCur Hop Limit: %u", nd_ra->nd_ra_curhoplimit); 17320acf3e2dSflorian log_debug("\tManaged address configuration: %d", 17330acf3e2dSflorian (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ? 1 : 0); 17340acf3e2dSflorian log_debug("\tOther configuration: %d", 17350acf3e2dSflorian (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) ? 1 : 0); 17360acf3e2dSflorian switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) { 17370acf3e2dSflorian case ND_RA_FLAG_RTPREF_HIGH: 17380acf3e2dSflorian log_debug("\tRouter Preference: high"); 17390acf3e2dSflorian break; 17400acf3e2dSflorian case ND_RA_FLAG_RTPREF_MEDIUM: 17410acf3e2dSflorian log_debug("\tRouter Preference: medium"); 17420acf3e2dSflorian break; 17430acf3e2dSflorian case ND_RA_FLAG_RTPREF_LOW: 17440acf3e2dSflorian log_debug("\tRouter Preference: low"); 17450acf3e2dSflorian break; 17460acf3e2dSflorian case ND_RA_FLAG_RTPREF_RSV: 17470acf3e2dSflorian log_debug("\tRouter Preference: reserved"); 17480acf3e2dSflorian break; 17490acf3e2dSflorian } 17500acf3e2dSflorian log_debug("\tRouter Lifetime: %hds", 17510acf3e2dSflorian ntohs(nd_ra->nd_ra_router_lifetime)); 17520acf3e2dSflorian log_debug("\tReachable Time: %ums", ntohl(nd_ra->nd_ra_reachable)); 17530acf3e2dSflorian log_debug("\tRetrans Timer: %ums", ntohl(nd_ra->nd_ra_retransmit)); 17540acf3e2dSflorian 17550acf3e2dSflorian while ((size_t)len >= sizeof(struct nd_opt_hdr)) { 17560acf3e2dSflorian struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p; 17570acf3e2dSflorian struct nd_opt_mtu *mtu; 17580acf3e2dSflorian struct nd_opt_prefix_info *prf; 17590acf3e2dSflorian struct nd_opt_rdnss *rdnss; 17600acf3e2dSflorian struct in6_addr *in6; 17610acf3e2dSflorian int i; 17620acf3e2dSflorian 17630acf3e2dSflorian len -= sizeof(struct nd_opt_hdr); 17640acf3e2dSflorian p += sizeof(struct nd_opt_hdr); 17650acf3e2dSflorian if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) { 17660acf3e2dSflorian log_warnx("invalid option len: %u > %ld", 17670acf3e2dSflorian nd_opt_hdr->nd_opt_len, len); 17680acf3e2dSflorian return; 17690acf3e2dSflorian } 17700acf3e2dSflorian log_debug("\tOption: %u (len: %u)", nd_opt_hdr->nd_opt_type, 17710acf3e2dSflorian nd_opt_hdr->nd_opt_len * 8); 17720acf3e2dSflorian switch (nd_opt_hdr->nd_opt_type) { 17730acf3e2dSflorian case ND_OPT_SOURCE_LINKADDR: 17740acf3e2dSflorian if (nd_opt_hdr->nd_opt_len == 1) 17750acf3e2dSflorian log_debug("\t\tND_OPT_SOURCE_LINKADDR: " 17760acf3e2dSflorian "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", 17770acf3e2dSflorian p[0], p[1], p[2], p[3], p[4], p[5], p[6], 17780acf3e2dSflorian p[7]); 17790acf3e2dSflorian else 17800acf3e2dSflorian log_debug("\t\tND_OPT_SOURCE_LINKADDR"); 17810acf3e2dSflorian break; 17820acf3e2dSflorian case ND_OPT_TARGET_LINKADDR: 17830acf3e2dSflorian if (nd_opt_hdr->nd_opt_len == 1) 17840acf3e2dSflorian log_debug("\t\tND_OPT_TARGET_LINKADDR: " 17850acf3e2dSflorian "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", 17860acf3e2dSflorian p[0], p[1], p[2], p[3], p[4], p[5], p[6], 17870acf3e2dSflorian p[7]); 17880acf3e2dSflorian else 17890acf3e2dSflorian log_debug("\t\tND_OPT_TARGET_LINKADDR"); 17900acf3e2dSflorian break; 17910acf3e2dSflorian case ND_OPT_PREFIX_INFORMATION: 17920acf3e2dSflorian if (nd_opt_hdr->nd_opt_len != 4) { 17932ac62a75Sflorian log_warnx("invalid ND_OPT_PREFIX_INFORMATION: " 17940acf3e2dSflorian "len != 4"); 17950acf3e2dSflorian return; 17960acf3e2dSflorian } 17970acf3e2dSflorian prf = (struct nd_opt_prefix_info*) nd_opt_hdr; 17980acf3e2dSflorian 17990acf3e2dSflorian log_debug("\t\tND_OPT_PREFIX_INFORMATION: %s/%u", 18000acf3e2dSflorian inet_ntop(AF_INET6, &prf->nd_opt_pi_prefix, 18010acf3e2dSflorian ntopbuf, INET6_ADDRSTRLEN), 18020acf3e2dSflorian prf->nd_opt_pi_prefix_len); 18030acf3e2dSflorian log_debug("\t\t\tOn-link: %d", 18040acf3e2dSflorian prf->nd_opt_pi_flags_reserved & 18050acf3e2dSflorian ND_OPT_PI_FLAG_ONLINK ? 1:0); 18060acf3e2dSflorian log_debug("\t\t\tAutonomous address-configuration: %d", 18070acf3e2dSflorian prf->nd_opt_pi_flags_reserved & 18080acf3e2dSflorian ND_OPT_PI_FLAG_AUTO ? 1 : 0); 18090acf3e2dSflorian log_debug("\t\t\tvltime: %u", 18100acf3e2dSflorian ntohl(prf->nd_opt_pi_valid_time)); 18110acf3e2dSflorian log_debug("\t\t\tpltime: %u", 18120acf3e2dSflorian ntohl(prf->nd_opt_pi_preferred_time)); 18130acf3e2dSflorian break; 18140acf3e2dSflorian case ND_OPT_REDIRECTED_HEADER: 18150acf3e2dSflorian log_debug("\t\tND_OPT_REDIRECTED_HEADER"); 18160acf3e2dSflorian break; 18170acf3e2dSflorian case ND_OPT_MTU: 18180acf3e2dSflorian if (nd_opt_hdr->nd_opt_len != 1) { 18192ac62a75Sflorian log_warnx("invalid ND_OPT_MTU: len != 1"); 18200acf3e2dSflorian return; 18210acf3e2dSflorian } 18220acf3e2dSflorian mtu = (struct nd_opt_mtu*) nd_opt_hdr; 18230acf3e2dSflorian log_debug("\t\tND_OPT_MTU: %u", 18240acf3e2dSflorian ntohl(mtu->nd_opt_mtu_mtu)); 18250acf3e2dSflorian break; 18260acf3e2dSflorian case ND_OPT_ROUTE_INFO: 18270acf3e2dSflorian log_debug("\t\tND_OPT_ROUTE_INFO"); 18280acf3e2dSflorian break; 18290acf3e2dSflorian case ND_OPT_RDNSS: 18300acf3e2dSflorian if (nd_opt_hdr->nd_opt_len < 3) { 18310acf3e2dSflorian log_warnx("invalid ND_OPT_RDNSS: len < 24"); 18320acf3e2dSflorian return; 18330acf3e2dSflorian } 18340acf3e2dSflorian if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) { 18350acf3e2dSflorian log_warnx("invalid ND_OPT_RDNSS: length with" 18360acf3e2dSflorian "out header is not multiply of 16: %d", 18370acf3e2dSflorian (nd_opt_hdr->nd_opt_len - 1) * 8); 18380acf3e2dSflorian return; 18390acf3e2dSflorian } 18400acf3e2dSflorian rdnss = (struct nd_opt_rdnss*) nd_opt_hdr; 18410acf3e2dSflorian log_debug("\t\tND_OPT_RDNSS: lifetime: %u", ntohl( 18420acf3e2dSflorian rdnss->nd_opt_rdnss_lifetime)); 18430acf3e2dSflorian in6 = (struct in6_addr*) (p + 6); 18440acf3e2dSflorian for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++, 18450acf3e2dSflorian in6++) { 18460acf3e2dSflorian log_debug("\t\t\t%s", inet_ntop(AF_INET6, in6, 18470acf3e2dSflorian ntopbuf, INET6_ADDRSTRLEN)); 18480acf3e2dSflorian } 18490acf3e2dSflorian break; 18500acf3e2dSflorian default: 18510acf3e2dSflorian log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type); 18520acf3e2dSflorian break; 18530acf3e2dSflorian 18540acf3e2dSflorian } 18550acf3e2dSflorian len -= nd_opt_hdr->nd_opt_len * 8 - 2; 18560acf3e2dSflorian p += nd_opt_hdr->nd_opt_len * 8 - 2; 18570acf3e2dSflorian } 18580acf3e2dSflorian } 185986f19603Sflorian #endif /* SMALL */ 18600acf3e2dSflorian 18610acf3e2dSflorian void update_iface_ra(struct slaacd_iface *iface, struct radv *ra) 18620acf3e2dSflorian { 18630acf3e2dSflorian struct radv *old_ra; 18640acf3e2dSflorian struct radv_prefix *prefix; 18650acf3e2dSflorian 18660acf3e2dSflorian if ((old_ra = find_ra(iface, &ra->from)) == NULL) 18670acf3e2dSflorian LIST_INSERT_HEAD(&iface->radvs, ra, entries); 18680acf3e2dSflorian else { 18690acf3e2dSflorian LIST_REPLACE(old_ra, ra, entries); 187005b87f88Sflorian merge_dad_couters(old_ra, ra); 18710acf3e2dSflorian free_ra(old_ra); 18720acf3e2dSflorian } 18739a36d583Sflorian 1874cdd6ec5cSflorian update_iface_ra_dfr(iface, ra); 18759a36d583Sflorian 18760acf3e2dSflorian LIST_FOREACH(prefix, &ra->prefixes, entries) { 1877ebc6da2eSflorian if (!prefix->autonomous || prefix->vltime == 0 || 1878ebc6da2eSflorian prefix->pltime > prefix->vltime || 18796a302ef9Sflorian IN6_IS_ADDR_LINKLOCAL(&prefix->prefix)) 18806a302ef9Sflorian continue; 1881881f44e2Sflorian update_iface_ra_prefix(iface, ra, prefix); 18820acf3e2dSflorian } 18830acf3e2dSflorian 1884a671359fSflorian update_iface_ra_rdns(iface, ra); 1885a671359fSflorian } 1886a671359fSflorian 1887cdd6ec5cSflorian void 1888cdd6ec5cSflorian update_iface_ra_dfr(struct slaacd_iface *iface, struct radv *ra) 1889cdd6ec5cSflorian { 1890cdd6ec5cSflorian struct dfr_proposal *dfr_proposal; 1891cdd6ec5cSflorian 1892cdd6ec5cSflorian dfr_proposal = find_dfr_proposal_by_gw(iface, &ra->from); 1893cdd6ec5cSflorian 1894cdd6ec5cSflorian if (ra->router_lifetime == 0) { 1895cdd6ec5cSflorian free_dfr_proposal(dfr_proposal); 1896cdd6ec5cSflorian return; 1897cdd6ec5cSflorian } 1898cdd6ec5cSflorian 1899cdd6ec5cSflorian if (!dfr_proposal) { 1900cdd6ec5cSflorian /* new proposal */ 1901cdd6ec5cSflorian gen_dfr_proposal(iface, ra); 1902cdd6ec5cSflorian return; 1903cdd6ec5cSflorian } 1904cdd6ec5cSflorian 1905cdd6ec5cSflorian dfr_proposal->when = ra->when; 1906cdd6ec5cSflorian dfr_proposal->uptime = ra->uptime; 1907cdd6ec5cSflorian dfr_proposal->router_lifetime = ra->router_lifetime; 1908cdd6ec5cSflorian 1909cdd6ec5cSflorian log_debug("%s, dfr state: %s, rl: %d", __func__, 19105a030e60Sflorian proposal_state_name(dfr_proposal->state), 1911cdd6ec5cSflorian real_lifetime(&dfr_proposal->uptime, 1912cdd6ec5cSflorian dfr_proposal->router_lifetime)); 1913cdd6ec5cSflorian 1914cdd6ec5cSflorian switch (dfr_proposal->state) { 1915cdd6ec5cSflorian case PROPOSAL_CONFIGURED: 1916cdd6ec5cSflorian case PROPOSAL_NEARLY_EXPIRED: 19175a030e60Sflorian /* routes do not expire in the kernel, update timeout */ 19185a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, 19195a030e60Sflorian PROPOSAL_CONFIGURED); 19205a030e60Sflorian break; 19215a030e60Sflorian case PROPOSAL_IF_DOWN: 19225a030e60Sflorian case PROPOSAL_WITHDRAWN: 1923cdd6ec5cSflorian log_debug("updating dfr"); 1924cdd6ec5cSflorian configure_dfr(dfr_proposal); 1925cdd6ec5cSflorian break; 1926cdd6ec5cSflorian default: 1927cdd6ec5cSflorian log_debug("%s: iface %d: %s", __func__, iface->if_index, 1928cdd6ec5cSflorian sin6_to_str(&dfr_proposal->addr)); 1929cdd6ec5cSflorian break; 1930cdd6ec5cSflorian } 1931cdd6ec5cSflorian } 1932cdd6ec5cSflorian 1933881f44e2Sflorian void 1934881f44e2Sflorian update_iface_ra_prefix(struct slaacd_iface *iface, struct radv *ra, 1935881f44e2Sflorian struct radv_prefix *prefix) 1936881f44e2Sflorian { 1937881f44e2Sflorian struct address_proposal *addr_proposal; 1938ad2cde20Sphessler uint32_t pltime, vltime; 1939804ba004Sflorian int found, found_temporary, duplicate_found; 1940881f44e2Sflorian 1941804ba004Sflorian found = found_temporary = duplicate_found = 0; 1942881f44e2Sflorian 1943547546f2Sflorian if (!!iface->autoconf != !!iface->temporary) { 1944547546f2Sflorian struct address_proposal *tmp; 1945547546f2Sflorian /* 1946547546f2Sflorian * If only the autoconf or temporary flag is set, check if we 1947547546f2Sflorian * have the "other kind" of address configured and delete it. 1948547546f2Sflorian */ 1949547546f2Sflorian LIST_FOREACH_SAFE (addr_proposal, &iface->addr_proposals, 1950547546f2Sflorian entries, tmp) { 1951547546f2Sflorian if ((!addr_proposal->temporary && !iface->autoconf) || 1952547546f2Sflorian (addr_proposal->temporary && !iface->temporary)) 1953547546f2Sflorian free_address_proposal(addr_proposal); 1954547546f2Sflorian } 1955547546f2Sflorian } 1956547546f2Sflorian 1957881f44e2Sflorian LIST_FOREACH(addr_proposal, &iface->addr_proposals, entries) { 1958881f44e2Sflorian if (prefix->prefix_len == addr_proposal-> prefix_len && 1959881f44e2Sflorian memcmp(&prefix->prefix, &addr_proposal->prefix, 1960881f44e2Sflorian sizeof(struct in6_addr)) != 0) 1961881f44e2Sflorian continue; 1962881f44e2Sflorian 1963881f44e2Sflorian if (memcmp(&addr_proposal->hw_address, 1964881f44e2Sflorian &iface->hw_address, 1965881f44e2Sflorian sizeof(addr_proposal->hw_address)) != 0) 1966881f44e2Sflorian continue; 1967881f44e2Sflorian 1968881f44e2Sflorian if (memcmp(&addr_proposal->soiikey, &iface->soiikey, 1969881f44e2Sflorian sizeof(addr_proposal->soiikey)) != 0) 1970881f44e2Sflorian continue; 1971881f44e2Sflorian 1972ee5ee76aSflorian if (addr_proposal->state == PROPOSAL_DUPLICATED) { 1973ee5ee76aSflorian duplicate_found = 1; 1974ee5ee76aSflorian continue; 1975ee5ee76aSflorian } 1976ee5ee76aSflorian 19775f4648e4Sflorian vltime = prefix->vltime; 19785f4648e4Sflorian 1979804ba004Sflorian if (addr_proposal->temporary) { 19805f4648e4Sflorian struct timespec now; 198179e3154eSflorian int64_t ltime, mtime; 19825f4648e4Sflorian 19835f4648e4Sflorian if (clock_gettime(CLOCK_MONOTONIC, &now)) 19845f4648e4Sflorian fatal("clock_gettime"); 19855f4648e4Sflorian 198679e3154eSflorian mtime = addr_proposal->created.tv_sec + 198779e3154eSflorian PRIV_PREFERRED_LIFETIME - 198879e3154eSflorian addr_proposal->desync_factor; 198979e3154eSflorian 199079e3154eSflorian ltime = MINIMUM(mtime, now.tv_sec + prefix->pltime) - 199179e3154eSflorian now.tv_sec; 199279e3154eSflorian 19935f4648e4Sflorian pltime = ltime > 0 ? ltime : 0; 19945f4648e4Sflorian 19955f4648e4Sflorian ltime = MINIMUM(addr_proposal->created.tv_sec + 19965f4648e4Sflorian PRIV_VALID_LIFETIME, now.tv_sec + vltime) - 19975f4648e4Sflorian now.tv_sec; 19985f4648e4Sflorian vltime = ltime > 0 ? ltime : 0; 19995f4648e4Sflorian 200079e3154eSflorian if ((mtime - now.tv_sec) > PRIV_REGEN_ADVANCE) 2001804ba004Sflorian found_temporary = 1; 20025f4648e4Sflorian } else { 20035f4648e4Sflorian pltime = prefix->pltime; 20045f4648e4Sflorian found = 1; 20055f4648e4Sflorian } 20065f4648e4Sflorian 20075a030e60Sflorian addr_proposal->from = ra->from; 20085f4648e4Sflorian addr_proposal->when = ra->when; 20095f4648e4Sflorian addr_proposal->uptime = ra->uptime; 20105f4648e4Sflorian 20115f4648e4Sflorian addr_proposal->vltime = vltime; 20125f4648e4Sflorian addr_proposal->pltime = pltime; 2013881f44e2Sflorian 2014881f44e2Sflorian if (ra->mtu == iface->cur_mtu) 2015881f44e2Sflorian addr_proposal->mtu = 0; 2016881f44e2Sflorian else { 2017881f44e2Sflorian addr_proposal->mtu = ra->mtu; 2018881f44e2Sflorian iface->cur_mtu = ra->mtu; 2019881f44e2Sflorian } 2020881f44e2Sflorian 2021881f44e2Sflorian log_debug("%s, addr state: %s", __func__, 20225a030e60Sflorian proposal_state_name(addr_proposal->state)); 2023881f44e2Sflorian 2024881f44e2Sflorian switch (addr_proposal->state) { 2025881f44e2Sflorian case PROPOSAL_CONFIGURED: 2026881f44e2Sflorian case PROPOSAL_NEARLY_EXPIRED: 20275a030e60Sflorian case PROPOSAL_IF_DOWN: 20285a030e60Sflorian case PROPOSAL_WITHDRAWN: 2029881f44e2Sflorian log_debug("updating address"); 2030881f44e2Sflorian configure_address(addr_proposal); 2031881f44e2Sflorian break; 2032881f44e2Sflorian default: 2033881f44e2Sflorian log_debug("%s: iface %d: %s", __func__, iface->if_index, 2034881f44e2Sflorian sin6_to_str(&addr_proposal->addr)); 2035881f44e2Sflorian break; 2036881f44e2Sflorian } 2037881f44e2Sflorian } 2038881f44e2Sflorian 2039c6384676Sflorian if (!found && iface->autoconf && duplicate_found && iface->soii) { 2040881f44e2Sflorian prefix->dad_counter++; 2041881f44e2Sflorian log_debug("%s dad_counter: %d", __func__, prefix->dad_counter); 204215bf244bSflorian gen_address_proposal(iface, ra, prefix, 0); 2043c6384676Sflorian } else if (!found && iface->autoconf && (iface->soii || 2044c6384676Sflorian prefix->prefix_len <= 64)) 2045881f44e2Sflorian /* new proposal */ 2046881f44e2Sflorian gen_address_proposal(iface, ra, prefix, 0); 2047881f44e2Sflorian 2048804ba004Sflorian /* temporary addresses do not depend on eui64 */ 2049804ba004Sflorian if (!found_temporary && iface->temporary) { 2050830e7b70Sflorian if (prefix->pltime >= PRIV_REGEN_ADVANCE) { 2051804ba004Sflorian /* new temporary proposal */ 2052830e7b70Sflorian gen_address_proposal(iface, ra, prefix, 1); 2053830e7b70Sflorian } else if (prefix->pltime > 0) { 2054881f44e2Sflorian log_warnx("%s: pltime from %s is too small: %d < %d; " 2055804ba004Sflorian "not generating temporary address", __func__, 2056881f44e2Sflorian sin6_to_str(&ra->from), prefix->pltime, 205779e3154eSflorian PRIV_REGEN_ADVANCE); 2058830e7b70Sflorian } 2059881f44e2Sflorian } 2060881f44e2Sflorian } 2061881f44e2Sflorian 2062a671359fSflorian void 2063a671359fSflorian update_iface_ra_rdns(struct slaacd_iface *iface, struct radv *ra) 2064a671359fSflorian { 2065a671359fSflorian struct rdns_proposal *rdns_proposal; 20665a030e60Sflorian struct radv_rdns *radv_rdns; 20675a030e60Sflorian struct in6_addr rdns[MAX_RDNS_COUNT]; 20685a030e60Sflorian int rdns_count; 2069a671359fSflorian 207034435150Sflorian rdns_proposal = find_rdns_proposal_by_gw(iface, &ra->from); 2071a671359fSflorian 2072a671359fSflorian if (!rdns_proposal) { 2073a671359fSflorian /* new proposal */ 20745a030e60Sflorian if (!LIST_EMPTY(&ra->rdns_servers)) 2075a671359fSflorian gen_rdns_proposal(iface, ra); 2076a671359fSflorian return; 2077a671359fSflorian } 2078a671359fSflorian 20795a030e60Sflorian rdns_count = 0; 20805a030e60Sflorian memset(&rdns, 0, sizeof(rdns)); 20815a030e60Sflorian LIST_FOREACH(radv_rdns, &ra->rdns_servers, entries) { 20825a030e60Sflorian memcpy(&rdns[rdns_count++], 20835a030e60Sflorian &radv_rdns->rdns, sizeof(struct in6_addr)); 20845a030e60Sflorian if (rdns_proposal->rdns_count == MAX_RDNS_COUNT) 20855a030e60Sflorian break; 20865a030e60Sflorian } 20875a030e60Sflorian 20885a030e60Sflorian if (rdns_count == 0) { 20895a030e60Sflorian free_rdns_proposal(rdns_proposal); 2090a671359fSflorian return; 2091a671359fSflorian } 2092a671359fSflorian 20935a030e60Sflorian if (rdns_proposal->rdns_count != rdns_count || 20945a030e60Sflorian memcmp(&rdns_proposal->rdns, &rdns, sizeof(rdns)) != 0) { 20955a030e60Sflorian memcpy(&rdns_proposal->rdns, &rdns, sizeof(rdns)); 20965a030e60Sflorian rdns_proposal->rdns_count = rdns_count; 20975a030e60Sflorian rdns_proposal->state = PROPOSAL_NOT_CONFIGURED; 20985a030e60Sflorian } 209934435150Sflorian rdns_proposal->when = ra->when; 210034435150Sflorian rdns_proposal->uptime = ra->uptime; 21014c458369Sflorian rdns_proposal->rdns_lifetime = ra->rdns_lifetime; 210234435150Sflorian 21034c458369Sflorian log_debug("%s, rdns state: %s, rl: %d", __func__, 21045a030e60Sflorian proposal_state_name(rdns_proposal->state), 210534435150Sflorian real_lifetime(&rdns_proposal->uptime, 210634435150Sflorian rdns_proposal->rdns_lifetime)); 210734435150Sflorian 210834435150Sflorian switch (rdns_proposal->state) { 21095a030e60Sflorian case PROPOSAL_CONFIGURED: 211034435150Sflorian case PROPOSAL_NEARLY_EXPIRED: 21115a030e60Sflorian /* rdns are not expired by the kernel, update timeout */ 21125a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 21135a030e60Sflorian PROPOSAL_CONFIGURED); 21145a030e60Sflorian break; 21155a030e60Sflorian case PROPOSAL_IF_DOWN: 21165a030e60Sflorian case PROPOSAL_WITHDRAWN: 21175a030e60Sflorian case PROPOSAL_NOT_CONFIGURED: 211834435150Sflorian log_debug("updating rdns"); 21195a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 21205a030e60Sflorian PROPOSAL_CONFIGURED); 21215a030e60Sflorian compose_rdns_proposal(rdns_proposal->if_index, 21225a030e60Sflorian rdns_proposal->rdomain); 212334435150Sflorian break; 212434435150Sflorian default: 2125a671359fSflorian log_debug("%s: iface %d: %s", __func__, iface->if_index, 2126a671359fSflorian sin6_to_str(&rdns_proposal->from)); 212734435150Sflorian break; 212834435150Sflorian } 212934435150Sflorian } 21300acf3e2dSflorian 2131c3a6ceb7Sflorian 2132c3a6ceb7Sflorian void 2133c3a6ceb7Sflorian configure_address(struct address_proposal *addr_proposal) 2134c3a6ceb7Sflorian { 2135c3a6ceb7Sflorian struct imsg_configure_address address; 21365a030e60Sflorian struct slaacd_iface *iface; 21370acf3e2dSflorian 21380acf3e2dSflorian log_debug("%s: %d", __func__, addr_proposal->if_index); 21390acf3e2dSflorian 21400acf3e2dSflorian address.if_index = addr_proposal->if_index; 21410acf3e2dSflorian memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr)); 2142e1cb65bbSflorian memcpy(&address.gw, &addr_proposal->from, sizeof(address.gw)); 21430acf3e2dSflorian memcpy(&address.mask, &addr_proposal->mask, sizeof(address.mask)); 21440acf3e2dSflorian address.vltime = addr_proposal->vltime; 21450acf3e2dSflorian address.pltime = addr_proposal->pltime; 2146804ba004Sflorian address.temporary = addr_proposal->temporary; 21472d9ad356Sbket address.mtu = addr_proposal->mtu; 21480acf3e2dSflorian 21490acf3e2dSflorian engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address, 21500acf3e2dSflorian sizeof(address)); 21515a030e60Sflorian 21525a030e60Sflorian if ((iface = get_slaacd_iface_by_id(addr_proposal->if_index)) != NULL) 21535a030e60Sflorian iface_state_transition(iface, IF_BOUND); 21545a030e60Sflorian addr_proposal_state_transition(addr_proposal, PROPOSAL_CONFIGURED); 21550acf3e2dSflorian } 21560acf3e2dSflorian 21570acf3e2dSflorian void 21580acf3e2dSflorian gen_address_proposal(struct slaacd_iface *iface, struct radv *ra, struct 2159804ba004Sflorian radv_prefix *prefix, int temporary) 21600acf3e2dSflorian { 21610acf3e2dSflorian struct address_proposal *addr_proposal; 21622f9dc02cSflorian const char *hbuf; 21630acf3e2dSflorian 21640acf3e2dSflorian if ((addr_proposal = calloc(1, sizeof(*addr_proposal))) == NULL) 21650acf3e2dSflorian fatal("calloc"); 2166254347c6Sflorian addr_proposal->id = ++proposal_id; 21670acf3e2dSflorian evtimer_set(&addr_proposal->timer, address_proposal_timeout, 21680acf3e2dSflorian addr_proposal); 21695a030e60Sflorian addr_proposal->timo.tv_sec = 1; 21705a030e60Sflorian addr_proposal->timo.tv_usec = arc4random_uniform(1000000); 21710acf3e2dSflorian addr_proposal->state = PROPOSAL_NOT_CONFIGURED; 21725f4648e4Sflorian if (clock_gettime(CLOCK_MONOTONIC, &addr_proposal->created)) 21735f4648e4Sflorian fatal("clock_gettime"); 21740acf3e2dSflorian addr_proposal->when = ra->when; 21750acf3e2dSflorian addr_proposal->uptime = ra->uptime; 21760acf3e2dSflorian addr_proposal->if_index = iface->if_index; 21775a030e60Sflorian memcpy(&addr_proposal->from, &ra->from, 21785a030e60Sflorian sizeof(addr_proposal->from)); 21790acf3e2dSflorian memcpy(&addr_proposal->hw_address, &iface->hw_address, 21800acf3e2dSflorian sizeof(addr_proposal->hw_address)); 2181b9b376fbSflorian memcpy(&addr_proposal->soiikey, &iface->soiikey, 2182b9b376fbSflorian sizeof(addr_proposal->soiikey)); 2183804ba004Sflorian addr_proposal->temporary = temporary; 21840acf3e2dSflorian memcpy(&addr_proposal->prefix, &prefix->prefix, 21850acf3e2dSflorian sizeof(addr_proposal->prefix)); 21860acf3e2dSflorian addr_proposal->prefix_len = prefix->prefix_len; 21870acf3e2dSflorian 2188804ba004Sflorian if (temporary) { 21895f4648e4Sflorian addr_proposal->vltime = MINIMUM(prefix->vltime, 21905f4648e4Sflorian PRIV_VALID_LIFETIME); 219179e3154eSflorian addr_proposal->desync_factor = 219279e3154eSflorian arc4random_uniform(PRIV_MAX_DESYNC_FACTOR); 219379e3154eSflorian 21945f4648e4Sflorian addr_proposal->pltime = MINIMUM(prefix->pltime, 219579e3154eSflorian PRIV_PREFERRED_LIFETIME - addr_proposal->desync_factor); 21960acf3e2dSflorian } else { 21970acf3e2dSflorian addr_proposal->vltime = prefix->vltime; 21980acf3e2dSflorian addr_proposal->pltime = prefix->pltime; 21990acf3e2dSflorian } 22000acf3e2dSflorian 22012d9ad356Sbket if (ra->mtu == iface->cur_mtu) 22022d9ad356Sbket addr_proposal->mtu = 0; 22032d9ad356Sbket else { 22042d9ad356Sbket addr_proposal->mtu = ra->mtu; 22052d9ad356Sbket iface->cur_mtu = ra->mtu; 22062d9ad356Sbket } 22072d9ad356Sbket 2208804ba004Sflorian gen_addr(iface, prefix, addr_proposal, temporary); 22090acf3e2dSflorian 22100acf3e2dSflorian LIST_INSERT_HEAD(&iface->addr_proposals, addr_proposal, entries); 2211b50d7ae0Sflorian configure_address(addr_proposal); 22120acf3e2dSflorian 22132f9dc02cSflorian hbuf = sin6_to_str(&addr_proposal->addr); 2214b50d7ae0Sflorian log_debug("%s: iface %d: %s", __func__, iface->if_index, hbuf); 22150acf3e2dSflorian } 22160acf3e2dSflorian 22170acf3e2dSflorian void 22187455062cSflorian free_address_proposal(struct address_proposal *addr_proposal) 22197455062cSflorian { 22207455062cSflorian if (addr_proposal == NULL) 22217455062cSflorian return; 22227455062cSflorian 2223b0650510Sflorian LIST_REMOVE(addr_proposal, entries); 22247455062cSflorian evtimer_del(&addr_proposal->timer); 2225b64c4682Spamela switch (addr_proposal->state) { 2226bf180fccSflorian case PROPOSAL_CONFIGURED: 2227bf180fccSflorian case PROPOSAL_NEARLY_EXPIRED: 2228b64c4682Spamela case PROPOSAL_STALE: 2229b64c4682Spamela withdraw_addr(addr_proposal); 2230b64c4682Spamela break; 2231b64c4682Spamela default: 2232b64c4682Spamela break; 2233b64c4682Spamela } 22347455062cSflorian free(addr_proposal); 22357455062cSflorian } 22367455062cSflorian 22377455062cSflorian void 2238b64c4682Spamela withdraw_addr(struct address_proposal *addr_proposal) 2239b64c4682Spamela { 2240b64c4682Spamela struct imsg_configure_address address; 2241b64c4682Spamela 2242b64c4682Spamela log_debug("%s: %d", __func__, addr_proposal->if_index); 2243b64c4682Spamela memset(&address, 0, sizeof(address)); 2244b64c4682Spamela address.if_index = addr_proposal->if_index; 2245b64c4682Spamela memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr)); 2246b64c4682Spamela 2247b64c4682Spamela engine_imsg_compose_main(IMSG_WITHDRAW_ADDRESS, 0, &address, 2248b64c4682Spamela sizeof(address)); 2249b64c4682Spamela } 2250b64c4682Spamela 2251b64c4682Spamela void 22520acf3e2dSflorian gen_dfr_proposal(struct slaacd_iface *iface, struct radv *ra) 22530acf3e2dSflorian { 22540acf3e2dSflorian struct dfr_proposal *dfr_proposal; 22552f9dc02cSflorian const char *hbuf; 22560acf3e2dSflorian 22570acf3e2dSflorian if ((dfr_proposal = calloc(1, sizeof(*dfr_proposal))) == NULL) 22580acf3e2dSflorian fatal("calloc"); 2259254347c6Sflorian dfr_proposal->id = ++proposal_id; 22600acf3e2dSflorian evtimer_set(&dfr_proposal->timer, dfr_proposal_timeout, 22610acf3e2dSflorian dfr_proposal); 22625a030e60Sflorian dfr_proposal->timo.tv_sec = 1; 22635a030e60Sflorian dfr_proposal->timo.tv_usec = arc4random_uniform(1000000); 22640acf3e2dSflorian dfr_proposal->state = PROPOSAL_NOT_CONFIGURED; 22650acf3e2dSflorian dfr_proposal->when = ra->when; 22660acf3e2dSflorian dfr_proposal->uptime = ra->uptime; 22670acf3e2dSflorian dfr_proposal->if_index = iface->if_index; 2268dd19964dSflorian dfr_proposal->rdomain = iface->rdomain; 22690acf3e2dSflorian memcpy(&dfr_proposal->addr, &ra->from, 22700acf3e2dSflorian sizeof(dfr_proposal->addr)); 22710acf3e2dSflorian dfr_proposal->router_lifetime = ra->router_lifetime; 22720acf3e2dSflorian dfr_proposal->rpref = ra->rpref; 22730acf3e2dSflorian 22740acf3e2dSflorian LIST_INSERT_HEAD(&iface->dfr_proposals, dfr_proposal, entries); 2275b50d7ae0Sflorian configure_dfr(dfr_proposal); 22760acf3e2dSflorian 22772f9dc02cSflorian hbuf = sin6_to_str(&dfr_proposal->addr); 2278b50d7ae0Sflorian log_debug("%s: iface %d: %s", __func__, iface->if_index, hbuf); 22790acf3e2dSflorian } 22800acf3e2dSflorian 22810acf3e2dSflorian void 22820acf3e2dSflorian configure_dfr(struct dfr_proposal *dfr_proposal) 22830acf3e2dSflorian { 22840acf3e2dSflorian struct imsg_configure_dfr dfr; 22850acf3e2dSflorian 22860acf3e2dSflorian log_debug("%s: %d", __func__, dfr_proposal->if_index); 22870acf3e2dSflorian 22880acf3e2dSflorian dfr.if_index = dfr_proposal->if_index; 2289dd19964dSflorian dfr.rdomain = dfr_proposal->rdomain; 22900acf3e2dSflorian memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr)); 22910acf3e2dSflorian dfr.router_lifetime = dfr_proposal->router_lifetime; 22920acf3e2dSflorian 22930acf3e2dSflorian engine_imsg_compose_main(IMSG_CONFIGURE_DFR, 0, &dfr, sizeof(dfr)); 22945a030e60Sflorian 22955a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, PROPOSAL_CONFIGURED); 22960acf3e2dSflorian } 22970acf3e2dSflorian 22980acf3e2dSflorian void 22990acf3e2dSflorian withdraw_dfr(struct dfr_proposal *dfr_proposal) 23000acf3e2dSflorian { 23010acf3e2dSflorian struct imsg_configure_dfr dfr; 23020acf3e2dSflorian 23030acf3e2dSflorian log_debug("%s: %d", __func__, dfr_proposal->if_index); 23040acf3e2dSflorian 23050acf3e2dSflorian dfr.if_index = dfr_proposal->if_index; 2306dd19964dSflorian dfr.rdomain = dfr_proposal->rdomain; 23070acf3e2dSflorian memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr)); 23080acf3e2dSflorian dfr.router_lifetime = dfr_proposal->router_lifetime; 23090acf3e2dSflorian 23100acf3e2dSflorian engine_imsg_compose_main(IMSG_WITHDRAW_DFR, 0, &dfr, sizeof(dfr)); 23110acf3e2dSflorian } 23120acf3e2dSflorian 23130acf3e2dSflorian void 23140acf3e2dSflorian free_dfr_proposal(struct dfr_proposal *dfr_proposal) 23150acf3e2dSflorian { 2316ed6fb014Spamela if (dfr_proposal == NULL) 2317ed6fb014Spamela return; 23180acf3e2dSflorian 23190acf3e2dSflorian LIST_REMOVE(dfr_proposal, entries); 23200acf3e2dSflorian evtimer_del(&dfr_proposal->timer); 23210acf3e2dSflorian switch (dfr_proposal->state) { 23220acf3e2dSflorian case PROPOSAL_CONFIGURED: 23230acf3e2dSflorian case PROPOSAL_NEARLY_EXPIRED: 2324b64c4682Spamela case PROPOSAL_STALE: 23250acf3e2dSflorian withdraw_dfr(dfr_proposal); 23260acf3e2dSflorian break; 23270acf3e2dSflorian default: 23280acf3e2dSflorian break; 23290acf3e2dSflorian } 23300acf3e2dSflorian free(dfr_proposal); 23310acf3e2dSflorian } 23320acf3e2dSflorian 233334435150Sflorian void 233434435150Sflorian gen_rdns_proposal(struct slaacd_iface *iface, struct radv *ra) 233534435150Sflorian { 233634435150Sflorian struct rdns_proposal *rdns_proposal; 233734435150Sflorian struct radv_rdns *rdns; 233834435150Sflorian const char *hbuf; 233934435150Sflorian 234034435150Sflorian if ((rdns_proposal = calloc(1, sizeof(*rdns_proposal))) == NULL) 234134435150Sflorian fatal("calloc"); 234234435150Sflorian rdns_proposal->id = ++proposal_id; 234334435150Sflorian evtimer_set(&rdns_proposal->timer, rdns_proposal_timeout, 234434435150Sflorian rdns_proposal); 23455a030e60Sflorian rdns_proposal->timo.tv_sec = 1; 23465a030e60Sflorian rdns_proposal->timo.tv_usec = arc4random_uniform(1000000); 234734435150Sflorian rdns_proposal->state = PROPOSAL_NOT_CONFIGURED; 234834435150Sflorian rdns_proposal->when = ra->when; 234934435150Sflorian rdns_proposal->uptime = ra->uptime; 235034435150Sflorian rdns_proposal->if_index = iface->if_index; 2351dd19964dSflorian rdns_proposal->rdomain = iface->rdomain; 235234435150Sflorian memcpy(&rdns_proposal->from, &ra->from, 235334435150Sflorian sizeof(rdns_proposal->from)); 235434435150Sflorian rdns_proposal->rdns_lifetime = ra->rdns_lifetime; 235534435150Sflorian LIST_FOREACH(rdns, &ra->rdns_servers, entries) { 235634435150Sflorian memcpy(&rdns_proposal->rdns[rdns_proposal->rdns_count++], 2357997c6a4aSflorian &rdns->rdns, sizeof(struct in6_addr)); 235834435150Sflorian if (rdns_proposal->rdns_count == MAX_RDNS_COUNT) 235934435150Sflorian break; 236034435150Sflorian } 236134435150Sflorian 236234435150Sflorian LIST_INSERT_HEAD(&iface->rdns_proposals, rdns_proposal, entries); 23635a030e60Sflorian compose_rdns_proposal(iface->if_index, iface->rdomain); 236434435150Sflorian 236534435150Sflorian hbuf = sin6_to_str(&rdns_proposal->from); 236634435150Sflorian log_debug("%s: iface %d: %s", __func__, iface->if_index, hbuf); 236734435150Sflorian } 236834435150Sflorian 236934435150Sflorian void 2370dd19964dSflorian compose_rdns_proposal(uint32_t if_index, int rdomain) 237134435150Sflorian { 237234435150Sflorian struct imsg_propose_rdns rdns; 2373fdb4a585Sflorian struct slaacd_iface *iface; 2374fdb4a585Sflorian struct rdns_proposal *rdns_proposal; 2375fdb4a585Sflorian int i; 237634435150Sflorian 2377fdb4a585Sflorian memset(&rdns, 0, sizeof(rdns)); 2378fdb4a585Sflorian rdns.if_index = if_index; 2379dd19964dSflorian rdns.rdomain = rdomain; 238034435150Sflorian 2381fdb4a585Sflorian if ((iface = get_slaacd_iface_by_id(if_index)) != NULL) { 2382fdb4a585Sflorian LIST_FOREACH(rdns_proposal, &iface->rdns_proposals, entries) { 23835a030e60Sflorian if (rdns_proposal->state == PROPOSAL_WITHDRAWN || 23845a030e60Sflorian rdns_proposal->state == PROPOSAL_STALE) 23855a030e60Sflorian continue; 23865a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 23875a030e60Sflorian PROPOSAL_CONFIGURED); 2388fdb4a585Sflorian for (i = 0; i < rdns_proposal->rdns_count && 2389fdb4a585Sflorian rdns.rdns_count < MAX_RDNS_COUNT; i++) { 2390fdb4a585Sflorian rdns.rdns[rdns.rdns_count++] = 2391fdb4a585Sflorian rdns_proposal->rdns[i]; 2392fdb4a585Sflorian } 2393fdb4a585Sflorian } 2394fdb4a585Sflorian } 2395fdb4a585Sflorian 2396fdb4a585Sflorian engine_imsg_compose_main(IMSG_PROPOSE_RDNS, 0, &rdns, sizeof(rdns)); 239734435150Sflorian } 239834435150Sflorian 239934435150Sflorian void 240034435150Sflorian free_rdns_proposal(struct rdns_proposal *rdns_proposal) 240134435150Sflorian { 240234435150Sflorian if (rdns_proposal == NULL) 240334435150Sflorian return; 240434435150Sflorian 240534435150Sflorian LIST_REMOVE(rdns_proposal, entries); 240634435150Sflorian evtimer_del(&rdns_proposal->timer); 24075a030e60Sflorian switch (rdns_proposal->state) { 24085a030e60Sflorian case PROPOSAL_CONFIGURED: 24095a030e60Sflorian case PROPOSAL_NEARLY_EXPIRED: 24105a030e60Sflorian case PROPOSAL_STALE: 24115a030e60Sflorian withdraw_rdns(rdns_proposal); 24125a030e60Sflorian break; 24135a030e60Sflorian default: 24145a030e60Sflorian break; 24155a030e60Sflorian } 241634435150Sflorian free(rdns_proposal); 241734435150Sflorian } 241834435150Sflorian 24190acf3e2dSflorian void 24205a030e60Sflorian withdraw_rdns(struct rdns_proposal *rdns_proposal) 24210acf3e2dSflorian { 24225a030e60Sflorian log_debug("%s: %d", __func__, rdns_proposal->if_index); 24230acf3e2dSflorian 24245a030e60Sflorian rdns_proposal->state = PROPOSAL_WITHDRAWN; 24250acf3e2dSflorian 24265a030e60Sflorian /* we have to re-propose all rdns servers, minus one */ 24275a030e60Sflorian compose_rdns_proposal(rdns_proposal->if_index, rdns_proposal->rdomain); 24280acf3e2dSflorian } 24290acf3e2dSflorian 24300acf3e2dSflorian void 24310acf3e2dSflorian address_proposal_timeout(int fd, short events, void *arg) 24320acf3e2dSflorian { 24330acf3e2dSflorian struct address_proposal *addr_proposal; 24345a030e60Sflorian struct slaacd_iface *iface = NULL; 24355a030e60Sflorian struct radv *ra = NULL; 24365a030e60Sflorian struct radv_prefix *prefix = NULL; 24372f9dc02cSflorian const char *hbuf; 24380acf3e2dSflorian 24390acf3e2dSflorian addr_proposal = (struct address_proposal *)arg; 24400acf3e2dSflorian 24412f9dc02cSflorian hbuf = sin6_to_str(&addr_proposal->addr); 24420acf3e2dSflorian log_debug("%s: iface %d: %s [%s], priv: %s", __func__, 24430acf3e2dSflorian addr_proposal->if_index, hbuf, 24445a030e60Sflorian proposal_state_name(addr_proposal->state), 2445804ba004Sflorian addr_proposal->temporary ? "y" : "n"); 24460acf3e2dSflorian 24470acf3e2dSflorian switch (addr_proposal->state) { 24485a030e60Sflorian case PROPOSAL_IF_DOWN: 24495a030e60Sflorian addr_proposal_state_transition(addr_proposal, PROPOSAL_STALE); 24505a030e60Sflorian break; 24510acf3e2dSflorian case PROPOSAL_CONFIGURED: 24525a030e60Sflorian addr_proposal_state_transition(addr_proposal, 24535a030e60Sflorian PROPOSAL_NEARLY_EXPIRED); 24540acf3e2dSflorian break; 24550acf3e2dSflorian case PROPOSAL_NEARLY_EXPIRED: 24560acf3e2dSflorian if (real_lifetime(&addr_proposal->uptime, 24575a030e60Sflorian addr_proposal->vltime) > 0) 24585a030e60Sflorian addr_proposal_state_transition(addr_proposal, 24595a030e60Sflorian PROPOSAL_NEARLY_EXPIRED); 24605a030e60Sflorian else 24615a030e60Sflorian addr_proposal_state_transition(addr_proposal, 24625a030e60Sflorian PROPOSAL_STALE); 24630acf3e2dSflorian break; 246405b87f88Sflorian case PROPOSAL_DUPLICATED: 24655a030e60Sflorian iface = get_slaacd_iface_by_id(addr_proposal->if_index); 24665a030e60Sflorian if (iface != NULL) 24675a030e60Sflorian ra = find_ra(iface, &addr_proposal->from); 24685a030e60Sflorian if (ra != NULL) 24695a030e60Sflorian prefix = find_prefix(ra, &addr_proposal->prefix, 24705a030e60Sflorian addr_proposal->prefix_len); 24715a030e60Sflorian if (prefix != NULL) { 24725a030e60Sflorian if (!addr_proposal->temporary) { 24735a030e60Sflorian prefix->dad_counter++; 24745a030e60Sflorian gen_address_proposal(iface, ra, prefix, 0); 24755a030e60Sflorian } else 24765a030e60Sflorian gen_address_proposal(iface, ra, prefix, 1); 24775a030e60Sflorian } 24785a030e60Sflorian addr_proposal_state_transition(addr_proposal, PROPOSAL_STALE); 247905b87f88Sflorian break; 2480b64c4682Spamela case PROPOSAL_STALE: 24815a030e60Sflorian free_address_proposal(addr_proposal); 24825a030e60Sflorian addr_proposal = NULL; 24835a030e60Sflorian break; 24845a030e60Sflorian case PROPOSAL_WITHDRAWN: 24855a030e60Sflorian free_address_proposal(addr_proposal); 24865a030e60Sflorian addr_proposal = NULL; 2487b64c4682Spamela break; 24880acf3e2dSflorian default: 24890acf3e2dSflorian log_debug("%s: unhandled state: %s", __func__, 24905a030e60Sflorian proposal_state_name(addr_proposal->state)); 24910acf3e2dSflorian } 24920acf3e2dSflorian } 24930acf3e2dSflorian 24940acf3e2dSflorian void 24950acf3e2dSflorian dfr_proposal_timeout(int fd, short events, void *arg) 24960acf3e2dSflorian { 24970acf3e2dSflorian struct dfr_proposal *dfr_proposal; 24982f9dc02cSflorian const char *hbuf; 24990acf3e2dSflorian 25000acf3e2dSflorian dfr_proposal = (struct dfr_proposal *)arg; 25010acf3e2dSflorian 25022f9dc02cSflorian hbuf = sin6_to_str(&dfr_proposal->addr); 25030acf3e2dSflorian log_debug("%s: iface %d: %s [%s]", __func__, dfr_proposal->if_index, 25045a030e60Sflorian hbuf, proposal_state_name(dfr_proposal->state)); 25050acf3e2dSflorian 25060acf3e2dSflorian switch (dfr_proposal->state) { 25075a030e60Sflorian case PROPOSAL_IF_DOWN: 25085a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, PROPOSAL_STALE); 25095a030e60Sflorian break; 25100acf3e2dSflorian case PROPOSAL_CONFIGURED: 25115a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, 25125a030e60Sflorian PROPOSAL_NEARLY_EXPIRED); 25130acf3e2dSflorian break; 25140acf3e2dSflorian case PROPOSAL_NEARLY_EXPIRED: 25150acf3e2dSflorian if (real_lifetime(&dfr_proposal->uptime, 25165a030e60Sflorian dfr_proposal->router_lifetime) > 0) 25175a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, 25185a030e60Sflorian PROPOSAL_NEARLY_EXPIRED); 25195a030e60Sflorian else 25205a030e60Sflorian dfr_proposal_state_transition(dfr_proposal, 25215a030e60Sflorian PROPOSAL_STALE); 25225a030e60Sflorian break; 25235a030e60Sflorian case PROPOSAL_STALE: 25240acf3e2dSflorian free_dfr_proposal(dfr_proposal); 25255a030e60Sflorian dfr_proposal = NULL; 25260acf3e2dSflorian break; 25275a030e60Sflorian case PROPOSAL_WITHDRAWN: 25285a030e60Sflorian free_dfr_proposal(dfr_proposal); 25295a030e60Sflorian dfr_proposal = NULL; 25300acf3e2dSflorian break; 25315a030e60Sflorian 25320acf3e2dSflorian default: 25330acf3e2dSflorian log_debug("%s: unhandled state: %s", __func__, 25345a030e60Sflorian proposal_state_name(dfr_proposal->state)); 25350acf3e2dSflorian } 25360acf3e2dSflorian } 25370acf3e2dSflorian 253834435150Sflorian void 253934435150Sflorian rdns_proposal_timeout(int fd, short events, void *arg) 254034435150Sflorian { 254134435150Sflorian struct rdns_proposal *rdns_proposal; 254234435150Sflorian const char *hbuf; 254334435150Sflorian 254434435150Sflorian rdns_proposal = (struct rdns_proposal *)arg; 254534435150Sflorian 254634435150Sflorian hbuf = sin6_to_str(&rdns_proposal->from); 254734435150Sflorian log_debug("%s: iface %d: %s [%s]", __func__, rdns_proposal->if_index, 25485a030e60Sflorian hbuf, proposal_state_name(rdns_proposal->state)); 254934435150Sflorian 255034435150Sflorian switch (rdns_proposal->state) { 25515a030e60Sflorian case PROPOSAL_IF_DOWN: 25525a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, PROPOSAL_STALE); 25535a030e60Sflorian break; 25545a030e60Sflorian case PROPOSAL_CONFIGURED: 25555a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 25565a030e60Sflorian PROPOSAL_NEARLY_EXPIRED); 255734435150Sflorian break; 255834435150Sflorian case PROPOSAL_NEARLY_EXPIRED: 255934435150Sflorian if (real_lifetime(&rdns_proposal->uptime, 25605a030e60Sflorian rdns_proposal->rdns_lifetime) > 0) 25615a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 25625a030e60Sflorian PROPOSAL_NEARLY_EXPIRED); 25635a030e60Sflorian else 25645a030e60Sflorian rdns_proposal_state_transition(rdns_proposal, 25655a030e60Sflorian PROPOSAL_STALE); 25665a030e60Sflorian break; 25675a030e60Sflorian case PROPOSAL_STALE: 256834435150Sflorian free_rdns_proposal(rdns_proposal); 25695a030e60Sflorian rdns_proposal = NULL; 257034435150Sflorian break; 25715a030e60Sflorian case PROPOSAL_WITHDRAWN: 25725a030e60Sflorian free_rdns_proposal(rdns_proposal); 25735a030e60Sflorian rdns_proposal = NULL; 257434435150Sflorian break; 25755a030e60Sflorian 257634435150Sflorian default: 257734435150Sflorian log_debug("%s: unhandled state: %s", __func__, 25785a030e60Sflorian proposal_state_name(rdns_proposal->state)); 257934435150Sflorian } 258034435150Sflorian } 258134435150Sflorian 25820acf3e2dSflorian void 25830acf3e2dSflorian iface_timeout(int fd, short events, void *arg) 25840acf3e2dSflorian { 25850acf3e2dSflorian struct slaacd_iface *iface = (struct slaacd_iface *)arg; 25860acf3e2dSflorian 25870acf3e2dSflorian log_debug("%s[%d]: %s", __func__, iface->if_index, 25885a030e60Sflorian if_state_name(iface->state)); 25890acf3e2dSflorian 25900acf3e2dSflorian switch (iface->state) { 25910acf3e2dSflorian case IF_DOWN: 25925a030e60Sflorian fatalx("%s: timeout in wrong state IF_DOWN", __func__); 25935a030e60Sflorian break; 25945a030e60Sflorian case IF_INIT: 25955a030e60Sflorian iface_state_transition(iface, IF_INIT); 25965a030e60Sflorian break; 25970acf3e2dSflorian default: 25980acf3e2dSflorian break; 25990acf3e2dSflorian } 26000acf3e2dSflorian } 26010acf3e2dSflorian 26020acf3e2dSflorian struct radv* 26030acf3e2dSflorian find_ra(struct slaacd_iface *iface, struct sockaddr_in6 *from) 26040acf3e2dSflorian { 26050acf3e2dSflorian struct radv *ra; 26060acf3e2dSflorian 26070acf3e2dSflorian LIST_FOREACH (ra, &iface->radvs, entries) { 26080acf3e2dSflorian if (memcmp(&ra->from.sin6_addr, &from->sin6_addr, 26090acf3e2dSflorian sizeof(from->sin6_addr)) == 0) 26100acf3e2dSflorian return (ra); 26110acf3e2dSflorian } 26120acf3e2dSflorian 26130acf3e2dSflorian return (NULL); 26140acf3e2dSflorian } 26150acf3e2dSflorian 26160acf3e2dSflorian struct address_proposal* 26170acf3e2dSflorian find_address_proposal_by_addr(struct slaacd_iface *iface, struct sockaddr_in6 26180acf3e2dSflorian *addr) 26190acf3e2dSflorian { 26200acf3e2dSflorian struct address_proposal *addr_proposal; 26210acf3e2dSflorian 26220acf3e2dSflorian LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) { 26230acf3e2dSflorian if (memcmp(&addr_proposal->addr, addr, sizeof(*addr)) == 0) 26240acf3e2dSflorian return (addr_proposal); 26250acf3e2dSflorian } 26260acf3e2dSflorian 26270acf3e2dSflorian return (NULL); 26280acf3e2dSflorian } 26290acf3e2dSflorian 26300acf3e2dSflorian struct dfr_proposal* 2631d3ff3477Sflorian find_dfr_proposal_by_gw(struct slaacd_iface *iface, struct sockaddr_in6 2632d3ff3477Sflorian *addr) 2633d3ff3477Sflorian { 2634d3ff3477Sflorian struct dfr_proposal *dfr_proposal; 2635d3ff3477Sflorian 2636d3ff3477Sflorian LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, entries) { 2637d3ff3477Sflorian if (memcmp(&dfr_proposal->addr, addr, sizeof(*addr)) == 0) 2638d3ff3477Sflorian return (dfr_proposal); 2639d3ff3477Sflorian } 2640d3ff3477Sflorian 2641d3ff3477Sflorian return (NULL); 2642d3ff3477Sflorian } 2643d3ff3477Sflorian 264434435150Sflorian struct rdns_proposal* 264534435150Sflorian find_rdns_proposal_by_gw(struct slaacd_iface *iface, struct sockaddr_in6 264634435150Sflorian *from) 264734435150Sflorian { 264834435150Sflorian struct rdns_proposal *rdns_proposal; 264934435150Sflorian 265034435150Sflorian LIST_FOREACH (rdns_proposal, &iface->rdns_proposals, entries) { 265134435150Sflorian if (memcmp(&rdns_proposal->from, from, sizeof(*from)) == 0) 265234435150Sflorian return (rdns_proposal); 265334435150Sflorian } 265434435150Sflorian 265534435150Sflorian return (NULL); 265634435150Sflorian } 26570acf3e2dSflorian 265805b87f88Sflorian struct radv_prefix * 26595a030e60Sflorian find_prefix(struct radv *ra, struct in6_addr *prefix, uint8_t prefix_len) 26600acf3e2dSflorian { 266105b87f88Sflorian struct radv_prefix *result; 26620acf3e2dSflorian 26630acf3e2dSflorian 266405b87f88Sflorian LIST_FOREACH(result, &ra->prefixes, entries) { 26655a030e60Sflorian if (memcmp(&result->prefix, prefix, 26665a030e60Sflorian sizeof(result->prefix)) == 0 && result->prefix_len == 26675a030e60Sflorian prefix_len) 266805b87f88Sflorian return (result); 26690acf3e2dSflorian } 267005b87f88Sflorian return (NULL); 26710acf3e2dSflorian } 26720acf3e2dSflorian 26730acf3e2dSflorian uint32_t 26740acf3e2dSflorian real_lifetime(struct timespec *received_uptime, uint32_t ltime) 26750acf3e2dSflorian { 26760acf3e2dSflorian struct timespec now, diff; 26770acf3e2dSflorian int64_t remaining; 26780acf3e2dSflorian 26790acf3e2dSflorian if (clock_gettime(CLOCK_MONOTONIC, &now)) 26800acf3e2dSflorian fatal("clock_gettime"); 26810acf3e2dSflorian 26820acf3e2dSflorian timespecsub(&now, received_uptime, &diff); 26830acf3e2dSflorian 26840acf3e2dSflorian remaining = ((int64_t)ltime) - diff.tv_sec; 26850acf3e2dSflorian 26860acf3e2dSflorian if (remaining < 0) 26870acf3e2dSflorian remaining = 0; 26880acf3e2dSflorian 26890acf3e2dSflorian return (remaining); 26900acf3e2dSflorian } 269105b87f88Sflorian 269205b87f88Sflorian void 269305b87f88Sflorian merge_dad_couters(struct radv *old_ra, struct radv *new_ra) 269405b87f88Sflorian { 269505b87f88Sflorian 269605b87f88Sflorian struct radv_prefix *old_prefix, *new_prefix; 269705b87f88Sflorian 269805b87f88Sflorian LIST_FOREACH(old_prefix, &old_ra->prefixes, entries) { 269905b87f88Sflorian if (!old_prefix->dad_counter) 270005b87f88Sflorian continue; 27015a030e60Sflorian if ((new_prefix = find_prefix(new_ra, &old_prefix->prefix, 27025a030e60Sflorian old_prefix->prefix_len)) != NULL) 270305b87f88Sflorian new_prefix->dad_counter = old_prefix->dad_counter; 270405b87f88Sflorian } 270505b87f88Sflorian } 2706