xref: /openbsd-src/sbin/slaacd/engine.c (revision 0e59d0d19ca6a10a17663d531bcea1b99c1bfe09)
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