xref: /openbsd-src/usr.sbin/bgpd/rtr_proto.c (revision 1cd81e65d1b2d5cd30f3ee6be8f1ae3b2078f2e4)
1*1cd81e65Sclaudio /*	$OpenBSD: rtr_proto.c,v 1.49 2025/01/25 07:23:30 claudio Exp $ */
2bd9df44eSclaudio 
3bd9df44eSclaudio /*
4bd9df44eSclaudio  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
5bd9df44eSclaudio  *
6bd9df44eSclaudio  * Permission to use, copy, modify, and distribute this software for any
7bd9df44eSclaudio  * purpose with or without fee is hereby granted, provided that the above
8bd9df44eSclaudio  * copyright notice and this permission notice appear in all copies.
9bd9df44eSclaudio  *
10bd9df44eSclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bd9df44eSclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bd9df44eSclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bd9df44eSclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bd9df44eSclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bd9df44eSclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bd9df44eSclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bd9df44eSclaudio  */
18bd9df44eSclaudio #include <sys/tree.h>
19bd9df44eSclaudio #include <errno.h>
20bd9df44eSclaudio #include <stdint.h>
21bd9df44eSclaudio #include <poll.h>
22bd9df44eSclaudio #include <stdio.h>
23bd9df44eSclaudio #include <stdlib.h>
24bd9df44eSclaudio #include <string.h>
25bd9df44eSclaudio #include <unistd.h>
26bd9df44eSclaudio 
27bd9df44eSclaudio #include "bgpd.h"
28bd9df44eSclaudio #include "session.h"
29bd9df44eSclaudio #include "log.h"
30bd9df44eSclaudio 
31bd9df44eSclaudio struct rtr_header {
32bd9df44eSclaudio 	uint8_t		version;
33bd9df44eSclaudio 	uint8_t		type;
34228f6bd1Sclaudio 	union {
35228f6bd1Sclaudio 		uint16_t	session_id;
36228f6bd1Sclaudio 		uint16_t	errcode;
37228f6bd1Sclaudio 		struct {
38228f6bd1Sclaudio 			uint8_t	flags;
39228f6bd1Sclaudio 			uint8_t	zero;
40228f6bd1Sclaudio 		};
41228f6bd1Sclaudio 	};
42bd9df44eSclaudio 	uint32_t	length;
4337149e4fSclaudio } __packed;
44bd9df44eSclaudio 
4598aa40b8Sclaudio #define RTR_MAX_PDU_SIZE	65535
46623585daSclaudio #define RTR_MAX_PDU_ERROR_SIZE	256
47bd9df44eSclaudio #define RTR_DEFAULT_REFRESH	3600
48bd9df44eSclaudio #define RTR_DEFAULT_RETRY	600
49bd9df44eSclaudio #define RTR_DEFAULT_EXPIRE	7200
50dfd27b08Sclaudio #define RTR_DEFAULT_ACTIVE	60
51bd9df44eSclaudio 
52bd9df44eSclaudio enum rtr_pdu_type {
53bd9df44eSclaudio 	SERIAL_NOTIFY = 0,
54bd9df44eSclaudio 	SERIAL_QUERY,
55bd9df44eSclaudio 	RESET_QUERY,
56bd9df44eSclaudio 	CACHE_RESPONSE,
57bd9df44eSclaudio 	IPV4_PREFIX,
58bd9df44eSclaudio 	IPV6_PREFIX = 6,
59bd9df44eSclaudio 	END_OF_DATA = 7,
60bd9df44eSclaudio 	CACHE_RESET = 8,
61bd9df44eSclaudio 	ROUTER_KEY = 9,
62bd9df44eSclaudio 	ERROR_REPORT = 10,
6383072fb6Sclaudio 	ASPA = 11,
64bd9df44eSclaudio };
65bd9df44eSclaudio 
6637149e4fSclaudio struct rtr_notify {
6737149e4fSclaudio 	struct rtr_header	hdr;
6837149e4fSclaudio 	uint32_t		serial;
6937149e4fSclaudio } __packed;
7037149e4fSclaudio 
7137149e4fSclaudio struct rtr_query {
7237149e4fSclaudio 	struct rtr_header	hdr;
7337149e4fSclaudio 	uint32_t		serial;
7437149e4fSclaudio } __packed;
7537149e4fSclaudio 
7637149e4fSclaudio struct rtr_reset {
7737149e4fSclaudio 	struct rtr_header	hdr;
7837149e4fSclaudio } __packed;
7937149e4fSclaudio 
8037149e4fSclaudio struct rtr_response {
8137149e4fSclaudio 	struct rtr_header	hdr;
8237149e4fSclaudio } __packed;
8337149e4fSclaudio 
84bd9df44eSclaudio #define FLAG_ANNOUNCE	0x1
85bd9df44eSclaudio #define FLAG_MASK	FLAG_ANNOUNCE
86bd9df44eSclaudio struct rtr_ipv4 {
8737149e4fSclaudio 	struct rtr_header	hdr;
88bd9df44eSclaudio 	uint8_t			flags;
89bd9df44eSclaudio 	uint8_t			prefixlen;
90bd9df44eSclaudio 	uint8_t			maxlen;
91bd9df44eSclaudio 	uint8_t			zero;
92bd9df44eSclaudio 	uint32_t		prefix;
93bd9df44eSclaudio 	uint32_t		asnum;
9437149e4fSclaudio } __packed;
95bd9df44eSclaudio 
96bd9df44eSclaudio struct rtr_ipv6 {
9737149e4fSclaudio 	struct rtr_header	hdr;
98bd9df44eSclaudio 	uint8_t			flags;
99bd9df44eSclaudio 	uint8_t			prefixlen;
100bd9df44eSclaudio 	uint8_t			maxlen;
101bd9df44eSclaudio 	uint8_t			zero;
102bd9df44eSclaudio 	uint32_t		prefix[4];
103bd9df44eSclaudio 	uint32_t		asnum;
10437149e4fSclaudio } __packed;
10537149e4fSclaudio 
10637149e4fSclaudio struct rtr_routerkey {
10737149e4fSclaudio 	struct rtr_header	hdr;
10837149e4fSclaudio 	uint8_t			ski[20];
10937149e4fSclaudio 	uint32_t		asnum;
11037149e4fSclaudio 	/* followed by Subject Public Key Info */
11137149e4fSclaudio } __packed;
112bd9df44eSclaudio 
11383072fb6Sclaudio struct rtr_aspa {
11437149e4fSclaudio 	struct rtr_header	hdr;
11583072fb6Sclaudio 	uint32_t		cas;
116228f6bd1Sclaudio 	/* array of spas filling the rest of the packet */
11737149e4fSclaudio } __packed;
11883072fb6Sclaudio 
119bd9df44eSclaudio struct rtr_endofdata {
12037149e4fSclaudio 	struct rtr_header	hdr;
121bd9df44eSclaudio 	uint32_t		serial;
122bd9df44eSclaudio 	uint32_t		refresh;
123bd9df44eSclaudio 	uint32_t		retry;
124bd9df44eSclaudio 	uint32_t		expire;
12537149e4fSclaudio } __packed;
126bd9df44eSclaudio 
1270d6339a2Sclaudio struct rtr_endofdata_v0 {
1280d6339a2Sclaudio 	struct rtr_header	hdr;
1290d6339a2Sclaudio 	uint32_t		serial;
1300d6339a2Sclaudio } __packed;
1310d6339a2Sclaudio 
132bd9df44eSclaudio enum rtr_event {
133bd9df44eSclaudio 	RTR_EVNT_START,
134bd9df44eSclaudio 	RTR_EVNT_CON_OPEN,
1352ced3cdbSclaudio 	RTR_EVNT_CON_CLOSE,
136bd9df44eSclaudio 	RTR_EVNT_TIMER_REFRESH,
137bd9df44eSclaudio 	RTR_EVNT_TIMER_RETRY,
138bd9df44eSclaudio 	RTR_EVNT_TIMER_EXPIRE,
139dfd27b08Sclaudio 	RTR_EVNT_TIMER_ACTIVE,
140bd9df44eSclaudio 	RTR_EVNT_SEND_ERROR,
141bd9df44eSclaudio 	RTR_EVNT_SERIAL_NOTIFY,
142bd9df44eSclaudio 	RTR_EVNT_CACHE_RESPONSE,
143bd9df44eSclaudio 	RTR_EVNT_END_OF_DATA,
144bd9df44eSclaudio 	RTR_EVNT_CACHE_RESET,
145bd9df44eSclaudio 	RTR_EVNT_NO_DATA,
1462ced3cdbSclaudio 	RTR_EVNT_RESET_AND_CLOSE,
14783072fb6Sclaudio 	RTR_EVNT_UNSUPP_PROTO_VERSION,
14837149e4fSclaudio 	RTR_EVNT_NEGOTIATION_DONE,
149bd9df44eSclaudio };
150bd9df44eSclaudio 
151bd9df44eSclaudio static const char *rtr_eventnames[] = {
152bd9df44eSclaudio 	"start",
153bd9df44eSclaudio 	"connection open",
154bd9df44eSclaudio 	"connection closed",
155bd9df44eSclaudio 	"refresh timer expired",
156bd9df44eSclaudio 	"retry timer expired",
157bd9df44eSclaudio 	"expire timer expired",
158dfd27b08Sclaudio 	"activity timer expired",
159bd9df44eSclaudio 	"sent error",
160bd9df44eSclaudio 	"serial notify received",
161bd9df44eSclaudio 	"cache response received",
162bd9df44eSclaudio 	"end of data received",
163bd9df44eSclaudio 	"cache reset received",
1642ced3cdbSclaudio 	"no data",
1652ced3cdbSclaudio 	"connection closed with reset",
16683072fb6Sclaudio 	"unsupported protocol version",
16737149e4fSclaudio 	"negotiation done",
168bd9df44eSclaudio };
169bd9df44eSclaudio 
170bd9df44eSclaudio enum rtr_state {
171bd9df44eSclaudio 	RTR_STATE_CLOSED,
172bd9df44eSclaudio 	RTR_STATE_ERROR,
1735a04dc7fSclaudio 	/* sessions with a state below this line will poll for incoming data */
1740314fe8bSclaudio 	RTR_STATE_ESTABLISHED,
1750314fe8bSclaudio 	RTR_STATE_EXCHANGE,
17683072fb6Sclaudio 	RTR_STATE_NEGOTIATION,
177bd9df44eSclaudio };
178bd9df44eSclaudio 
179bd9df44eSclaudio static const char *rtr_statenames[] = {
180bd9df44eSclaudio 	"closed",
181bd9df44eSclaudio 	"error",
1820314fe8bSclaudio 	"established",
1830314fe8bSclaudio 	"exchange",
18483072fb6Sclaudio 	"negotiation",
185bd9df44eSclaudio };
186bd9df44eSclaudio 
187bd9df44eSclaudio struct rtr_session {
188bd9df44eSclaudio 	TAILQ_ENTRY(rtr_session)	entry;
189bd9df44eSclaudio 	char				descr[PEER_DESCR_LEN];
190bd9df44eSclaudio 	struct roa_tree			roa_set;
191c0c9c169Sclaudio 	struct aspa_tree		aspa;
192bd9df44eSclaudio 	struct timer_head		timers;
19322b46a1fSclaudio 	struct msgbuf			*w;
194bd9df44eSclaudio 	uint32_t			id;		/* rtr_config id */
195bd9df44eSclaudio 	uint32_t			serial;
196bd9df44eSclaudio 	uint32_t			refresh;
197bd9df44eSclaudio 	uint32_t			retry;
198bd9df44eSclaudio 	uint32_t			expire;
199dfd27b08Sclaudio 	uint32_t			active;
200bd9df44eSclaudio 	int				session_id;
201bd9df44eSclaudio 	int				fd;
202dfd27b08Sclaudio 	int				active_lock;
203bd9df44eSclaudio 	enum rtr_state			state;
204bd9df44eSclaudio 	enum reconf_action		reconf_action;
205bd9df44eSclaudio 	enum rtr_error			last_sent_error;
206bd9df44eSclaudio 	enum rtr_error			last_recv_error;
207bd9df44eSclaudio 	char				last_sent_msg[REASON_LEN];
208bd9df44eSclaudio 	char				last_recv_msg[REASON_LEN];
20983072fb6Sclaudio 	uint8_t				version;
210125ef3d5Sclaudio 	uint8_t				prev_version;
211d87cfbccSclaudio 	uint8_t				min_version;
212d87cfbccSclaudio 	uint8_t				errored;
213d87cfbccSclaudio 
214bd9df44eSclaudio };
215bd9df44eSclaudio 
216bd9df44eSclaudio TAILQ_HEAD(, rtr_session) rtrs = TAILQ_HEAD_INITIALIZER(rtrs);
217bd9df44eSclaudio 
218bd9df44eSclaudio static void	rtr_fsm(struct rtr_session *, enum rtr_event);
219bd9df44eSclaudio 
220bd9df44eSclaudio static const char *
221bd9df44eSclaudio log_rtr(struct rtr_session *rs)
222bd9df44eSclaudio {
223bd9df44eSclaudio 	return rs->descr;
224bd9df44eSclaudio }
225bd9df44eSclaudio 
226bd9df44eSclaudio static const char *
227bd9df44eSclaudio log_rtr_type(enum rtr_pdu_type type)
228bd9df44eSclaudio {
229bd9df44eSclaudio 	static char buf[20];
230bd9df44eSclaudio 
231bd9df44eSclaudio 	switch (type) {
232bd9df44eSclaudio 	case SERIAL_NOTIFY:
233bd9df44eSclaudio 		return "serial notify";
234bd9df44eSclaudio 	case SERIAL_QUERY:
235bd9df44eSclaudio 		return "serial query";
236bd9df44eSclaudio 	case RESET_QUERY:
237bd9df44eSclaudio 		return "reset query";
238bd9df44eSclaudio 	case CACHE_RESPONSE:
239bd9df44eSclaudio 		return "cache response";
240bd9df44eSclaudio 	case IPV4_PREFIX:
241bd9df44eSclaudio 		return "IPv4 prefix";
242bd9df44eSclaudio 	case IPV6_PREFIX:
243bd9df44eSclaudio 		return "IPv6 prefix";
244bd9df44eSclaudio 	case END_OF_DATA:
245bd9df44eSclaudio 		return "end of data";
246bd9df44eSclaudio 	case CACHE_RESET:
247bd9df44eSclaudio 		return "cache reset";
248bd9df44eSclaudio 	case ROUTER_KEY:
249bd9df44eSclaudio 		return "router key";
250bd9df44eSclaudio 	case ERROR_REPORT:
251bd9df44eSclaudio 		return "error report";
25283072fb6Sclaudio 	case ASPA:
2539dad7388Sclaudio 		return "aspa";
254bd9df44eSclaudio 	default:
255bd9df44eSclaudio 		snprintf(buf, sizeof(buf), "unknown %u", type);
256bd9df44eSclaudio 		return buf;
257bd9df44eSclaudio 	}
258bd9df44eSclaudio };
259bd9df44eSclaudio 
260d87cfbccSclaudio static uint8_t
261d87cfbccSclaudio rtr_max_session_version(struct rtr_session *rs)
262d87cfbccSclaudio {
263d87cfbccSclaudio 	if (rs->min_version > RTR_DEFAULT_VERSION)
264d87cfbccSclaudio 		return rs->min_version;
265d87cfbccSclaudio 	return RTR_DEFAULT_VERSION;
266d87cfbccSclaudio }
267d87cfbccSclaudio 
2685a04dc7fSclaudio static void
2695a04dc7fSclaudio rtr_reset_cache(struct rtr_session *rs)
2705a04dc7fSclaudio {
2715a04dc7fSclaudio 	/* reset session */
2725a04dc7fSclaudio 	rs->session_id = -1;
2735a04dc7fSclaudio 	timer_stop(&rs->timers, Timer_Rtr_Expire);
2745a04dc7fSclaudio 	free_roatree(&rs->roa_set);
275c0c9c169Sclaudio 	free_aspatree(&rs->aspa);
2765a04dc7fSclaudio }
2775a04dc7fSclaudio 
278bd9df44eSclaudio static struct ibuf *
27983072fb6Sclaudio rtr_newmsg(struct rtr_session *rs, enum rtr_pdu_type type, uint32_t len,
28083072fb6Sclaudio     uint16_t session_id)
281bd9df44eSclaudio {
282bd9df44eSclaudio 	struct ibuf *buf;
283a2488d3fSclaudio 	int saved_errno;
284bd9df44eSclaudio 
285623585daSclaudio 	if (len > RTR_MAX_PDU_SIZE) {
286bd9df44eSclaudio 		errno = ERANGE;
287bd9df44eSclaudio 		return NULL;
288bd9df44eSclaudio 	}
289de259131Sclaudio 	len += sizeof(struct rtr_header);
290bd9df44eSclaudio 	if ((buf = ibuf_open(len)) == NULL)
291a2488d3fSclaudio 		goto fail;
292a2488d3fSclaudio 	if (ibuf_add_n8(buf, rs->version) == -1)
293a2488d3fSclaudio 		goto fail;
294a2488d3fSclaudio 	if (ibuf_add_n8(buf, type) == -1)
295a2488d3fSclaudio 		goto fail;
296a2488d3fSclaudio 	if (ibuf_add_n16(buf, session_id) == -1)
297a2488d3fSclaudio 		goto fail;
298a2488d3fSclaudio 	if (ibuf_add_n32(buf, len) == -1)
299a2488d3fSclaudio 		goto fail;
300de259131Sclaudio 
301bd9df44eSclaudio 	return buf;
302a2488d3fSclaudio 
303a2488d3fSclaudio  fail:
304a2488d3fSclaudio 	saved_errno = errno;
305a2488d3fSclaudio 	ibuf_free(buf);
306a2488d3fSclaudio 	errno = saved_errno;
307a2488d3fSclaudio 	return NULL;
308bd9df44eSclaudio }
309bd9df44eSclaudio 
3109dad7388Sclaudio static void rtr_send_error(struct rtr_session *, struct ibuf *, enum rtr_error,
3119dad7388Sclaudio     const char *, ...) __attribute__((__format__ (printf, 4, 5)));
3129dad7388Sclaudio 
313bd9df44eSclaudio /*
314bd9df44eSclaudio  * Try to send an error PDU to cache, put connection into error
315bd9df44eSclaudio  * state.
316bd9df44eSclaudio  */
317bd9df44eSclaudio static void
3189dad7388Sclaudio rtr_send_error(struct rtr_session *rs, struct ibuf *pdu, enum rtr_error err,
3199dad7388Sclaudio     const char *fmt, ...)
320bd9df44eSclaudio {
321bd9df44eSclaudio 	struct ibuf *buf;
3229dad7388Sclaudio 	va_list ap;
323b1793e8cSclaudio 	size_t len = 0, mlen = 0;
324bd9df44eSclaudio 
325bd9df44eSclaudio 	rs->last_sent_error = err;
326bd9df44eSclaudio 	memset(rs->last_sent_msg, 0, sizeof(rs->last_sent_msg));
3279dad7388Sclaudio 	if (fmt != NULL) {
3289dad7388Sclaudio 		va_start(ap, fmt);
3299dad7388Sclaudio 		vsnprintf(rs->last_sent_msg, sizeof(rs->last_sent_msg),
3309dad7388Sclaudio 		    fmt, ap);
3319dad7388Sclaudio 		mlen = strlen(rs->last_sent_msg);
3329dad7388Sclaudio 		va_end(ap);
3339dad7388Sclaudio 	}
3349dad7388Sclaudio 
3359dad7388Sclaudio 	log_warnx("rtr %s: sending error: %s%s%s", log_rtr(rs),
3369dad7388Sclaudio 	    log_rtr_error(err), mlen > 0 ? ": " : "", rs->last_sent_msg);
337bd9df44eSclaudio 
338b1793e8cSclaudio 	if (pdu != NULL) {
339b1793e8cSclaudio 		ibuf_rewind(pdu);
340b1793e8cSclaudio 		len = ibuf_size(pdu);
341623585daSclaudio 		if (len > RTR_MAX_PDU_ERROR_SIZE) {
342623585daSclaudio 			len = RTR_MAX_PDU_ERROR_SIZE;
343623585daSclaudio 			/* truncate down can not fail */
344623585daSclaudio 			ibuf_truncate(pdu, RTR_MAX_PDU_ERROR_SIZE);
345623585daSclaudio 		}
346b1793e8cSclaudio 	}
347b1793e8cSclaudio 
348de259131Sclaudio 	buf = rtr_newmsg(rs, ERROR_REPORT, 2 * sizeof(uint32_t) + len + mlen,
34983072fb6Sclaudio 	    err);
350a2488d3fSclaudio 	if (buf == NULL)
351a2488d3fSclaudio 		goto fail;
352a2488d3fSclaudio 	if (ibuf_add_n32(buf, len) == -1)
353a2488d3fSclaudio 		goto fail;
354b1793e8cSclaudio 	if (pdu != NULL) {
355b1793e8cSclaudio 		if (ibuf_add_ibuf(buf, pdu) == -1)
356a2488d3fSclaudio 			goto fail;
357b1793e8cSclaudio 	}
358a2488d3fSclaudio 	if (ibuf_add_n32(buf, mlen) == -1)
359a2488d3fSclaudio 		goto fail;
3609dad7388Sclaudio 	if (ibuf_add(buf, rs->last_sent_msg, mlen) == -1)
361a2488d3fSclaudio 		goto fail;
36205453d67Sclaudio 	ibuf_close(rs->w, buf);
363bd9df44eSclaudio 
3645a04dc7fSclaudio 	rtr_fsm(rs, RTR_EVNT_SEND_ERROR);
365a2488d3fSclaudio 	return;
366a2488d3fSclaudio 
367a2488d3fSclaudio  fail:
368a2488d3fSclaudio 	log_warn("rtr %s: send error report", log_rtr(rs));
369a2488d3fSclaudio 	ibuf_free(buf);
370bd9df44eSclaudio }
371bd9df44eSclaudio 
372bd9df44eSclaudio static void
3735a04dc7fSclaudio rtr_send_reset_query(struct rtr_session *rs)
374bd9df44eSclaudio {
375bd9df44eSclaudio 	struct ibuf *buf;
376bd9df44eSclaudio 
37783072fb6Sclaudio 	buf = rtr_newmsg(rs, RESET_QUERY, 0, 0);
3789dad7388Sclaudio 	if (buf == NULL)
3799dad7388Sclaudio 		goto fail;
38005453d67Sclaudio 	ibuf_close(rs->w, buf);
3819dad7388Sclaudio 	return;
3829dad7388Sclaudio 
3839dad7388Sclaudio  fail:
3849dad7388Sclaudio 	rtr_send_error(rs, NULL, INTERNAL_ERROR,
3859dad7388Sclaudio 	    "send %s: %s", log_rtr_type(RESET_QUERY), strerror(errno));
3869dad7388Sclaudio 	ibuf_free(buf);
387bd9df44eSclaudio }
388bd9df44eSclaudio 
389bd9df44eSclaudio static void
3905a04dc7fSclaudio rtr_send_serial_query(struct rtr_session *rs)
391bd9df44eSclaudio {
392bd9df44eSclaudio 	struct ibuf *buf;
393bd9df44eSclaudio 
394de259131Sclaudio 	buf = rtr_newmsg(rs, SERIAL_QUERY, sizeof(uint32_t), rs->session_id);
395a2488d3fSclaudio 	if (buf == NULL)
396a2488d3fSclaudio 		goto fail;
397a2488d3fSclaudio 	if (ibuf_add_n32(buf, rs->serial) == -1)
398a2488d3fSclaudio 		goto fail;
39905453d67Sclaudio 	ibuf_close(rs->w, buf);
400a2488d3fSclaudio 	return;
401a2488d3fSclaudio 
402a2488d3fSclaudio  fail:
4039dad7388Sclaudio 	rtr_send_error(rs, NULL, INTERNAL_ERROR,
4049dad7388Sclaudio 	    "send %s: %s", log_rtr_type(SERIAL_QUERY), strerror(errno));
405a2488d3fSclaudio 	ibuf_free(buf);
406bd9df44eSclaudio }
407bd9df44eSclaudio 
408bd9df44eSclaudio /*
40937149e4fSclaudio  * Check the session_id of the rtr_header to match the expected value.
41037149e4fSclaudio  * Returns -1 on failure and 0 on success.
41137149e4fSclaudio  */
41237149e4fSclaudio static int
41337149e4fSclaudio rtr_check_session_id(struct rtr_session *rs, uint16_t session_id,
41437149e4fSclaudio     struct rtr_header *rh, struct ibuf *pdu)
41537149e4fSclaudio {
41637149e4fSclaudio 	if (session_id != ntohs(rh->session_id)) {
4179dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA,
4189dad7388Sclaudio 		    "%s: bad session_id %d (expected %d)",
4199dad7388Sclaudio 		    log_rtr_type(rh->type), ntohs(rh->session_id), session_id);
42037149e4fSclaudio 		return -1;
42137149e4fSclaudio 	}
42237149e4fSclaudio 	return 0;
42337149e4fSclaudio }
42437149e4fSclaudio 
42537149e4fSclaudio /*
42622b46a1fSclaudio  * Callback for ibuf_read to get the size of a PDU.
427bd9df44eSclaudio  */
4289509a1e6Sclaudio static struct ibuf *
4299509a1e6Sclaudio rtr_reader_callback(struct ibuf *hdr, void *arg, int *fd)
430bd9df44eSclaudio {
43122b46a1fSclaudio 	struct rtr_session *rs = arg;
432bd9df44eSclaudio 	struct rtr_header rh;
4339509a1e6Sclaudio 	struct ibuf *b;
43422b46a1fSclaudio 	ssize_t len;
435bd9df44eSclaudio 
436b1793e8cSclaudio 	if (ibuf_get(hdr, &rh, sizeof(rh)) == -1)
4379509a1e6Sclaudio 		return NULL;
438bd9df44eSclaudio 
43937149e4fSclaudio 	len = ntohl(rh.length);
440bd9df44eSclaudio 
441623585daSclaudio 	if (len > RTR_MAX_PDU_SIZE) {
4429dad7388Sclaudio 		rtr_send_error(rs, hdr, CORRUPT_DATA, "%s: too big: %zu bytes",
4439dad7388Sclaudio 		    log_rtr_type(rh.type), len);
44422b46a1fSclaudio 		errno = ERANGE;
4459509a1e6Sclaudio 		return NULL;
446bd9df44eSclaudio 	}
44737149e4fSclaudio 
4489509a1e6Sclaudio 	if ((b = ibuf_open(len)) == NULL)
4499509a1e6Sclaudio 		return NULL;
4509509a1e6Sclaudio 	return b;
45122b46a1fSclaudio }
45222b46a1fSclaudio 
45322b46a1fSclaudio /*
45422b46a1fSclaudio  * Parse the common rtr header (first 8 bytes) including the
45522b46a1fSclaudio  * included length field.
45622b46a1fSclaudio  * Returns -1 on failure. On success msgtype and msglen are set
45722b46a1fSclaudio  * and the function return 0.
45822b46a1fSclaudio  */
45922b46a1fSclaudio static int
46022b46a1fSclaudio rtr_parse_header(struct rtr_session *rs, struct ibuf *msg,
46122b46a1fSclaudio     enum rtr_pdu_type *msgtype)
46222b46a1fSclaudio {
46322b46a1fSclaudio 	struct rtr_header rh;
46422b46a1fSclaudio 	struct ibuf hdr;
46522b46a1fSclaudio 	size_t len;
46622b46a1fSclaudio 	uint16_t errcode;
46722b46a1fSclaudio 
46822b46a1fSclaudio 	len = ibuf_size(msg);
46922b46a1fSclaudio 
47022b46a1fSclaudio 	ibuf_from_ibuf(&hdr, msg);
47122b46a1fSclaudio 	if (ibuf_get(&hdr, &rh, sizeof(rh)) == -1)
47222b46a1fSclaudio 		fatal("%s: ibuf_get", __func__);
47322b46a1fSclaudio 
47437149e4fSclaudio 	if (rs->state == RTR_STATE_NEGOTIATION) {
47537149e4fSclaudio 		switch (rh.type) {
47637149e4fSclaudio 		case CACHE_RESPONSE:
47737149e4fSclaudio 		case CACHE_RESET:
478125ef3d5Sclaudio 			/* implicit downgrade */
479125ef3d5Sclaudio 			if (rh.version < rs->version) {
480125ef3d5Sclaudio 				rs->prev_version = rs->version;
48137149e4fSclaudio 				rs->version = rh.version;
482125ef3d5Sclaudio 			}
48337149e4fSclaudio 			rtr_fsm(rs, RTR_EVNT_NEGOTIATION_DONE);
48437149e4fSclaudio 			break;
485125ef3d5Sclaudio 		case ERROR_REPORT:
486a62144a2Sclaudio 			errcode = ntohs(rh.session_id);
487a62144a2Sclaudio 			if (errcode == UNSUPP_PROTOCOL_VERS ||
488a62144a2Sclaudio 			    errcode == NO_DATA_AVAILABLE) {
489a62144a2Sclaudio 				if (rh.version < rs->version) {
490a62144a2Sclaudio 					rs->prev_version = rs->version;
491a62144a2Sclaudio 					rs->version = rh.version;
492a62144a2Sclaudio 				}
493a62144a2Sclaudio 			}
494125ef3d5Sclaudio 			break;
49537149e4fSclaudio 		case SERIAL_NOTIFY:
49637149e4fSclaudio 			/* ignore SERIAL_NOTIFY */
49737149e4fSclaudio 			break;
49837149e4fSclaudio 		default:
49922b46a1fSclaudio 			rtr_send_error(rs, msg, CORRUPT_DATA,
5009dad7388Sclaudio 			    "%s: out of context", log_rtr_type(rh.type));
501bd9df44eSclaudio 			return -1;
502bd9df44eSclaudio 		}
50337149e4fSclaudio 	} else if (rh.version != rs->version && rh.type != ERROR_REPORT) {
50437149e4fSclaudio 		goto badversion;
50537149e4fSclaudio 	}
50637149e4fSclaudio 
50737149e4fSclaudio 	switch (rh.type) {
50837149e4fSclaudio 	case SERIAL_NOTIFY:
50937149e4fSclaudio 		if (len != sizeof(struct rtr_notify))
51037149e4fSclaudio 			goto badlen;
51137149e4fSclaudio 		break;
51237149e4fSclaudio 	case CACHE_RESPONSE:
51337149e4fSclaudio 		if (len != sizeof(struct rtr_response))
51437149e4fSclaudio 			goto badlen;
51537149e4fSclaudio 		break;
51637149e4fSclaudio 	case IPV4_PREFIX:
51737149e4fSclaudio 		if (len != sizeof(struct rtr_ipv4))
51837149e4fSclaudio 			goto badlen;
51937149e4fSclaudio 		break;
52037149e4fSclaudio 	case IPV6_PREFIX:
52137149e4fSclaudio 		if (len != sizeof(struct rtr_ipv6))
52237149e4fSclaudio 			goto badlen;
52337149e4fSclaudio 		break;
52437149e4fSclaudio 	case END_OF_DATA:
5250d6339a2Sclaudio 		if (rs->version == 0) {
5260d6339a2Sclaudio 			if (len != sizeof(struct rtr_endofdata_v0))
5270d6339a2Sclaudio 				goto badlen;
5280d6339a2Sclaudio 		} else {
52937149e4fSclaudio 			if (len != sizeof(struct rtr_endofdata))
53037149e4fSclaudio 				goto badlen;
5310d6339a2Sclaudio 		}
53237149e4fSclaudio 		break;
53337149e4fSclaudio 	case CACHE_RESET:
53437149e4fSclaudio 		if (len != sizeof(struct rtr_reset))
53537149e4fSclaudio 			goto badlen;
53637149e4fSclaudio 		break;
53737149e4fSclaudio 	case ROUTER_KEY:
53837149e4fSclaudio 		if (rs->version < 1)
53937149e4fSclaudio 			goto badversion;
5404fa7a5b4Sclaudio 		if (len < sizeof(struct rtr_routerkey))
5414fa7a5b4Sclaudio 			goto badlen;
54237149e4fSclaudio 		break;
54337149e4fSclaudio 	case ERROR_REPORT:
54437149e4fSclaudio 		if (len < 16)
54537149e4fSclaudio 			goto badlen;
54637149e4fSclaudio 		break;
54783072fb6Sclaudio 	case ASPA:
54883072fb6Sclaudio 		if (rs->version < 2)
54983072fb6Sclaudio 			goto badversion;
5504fa7a5b4Sclaudio 		if (len < sizeof(struct rtr_aspa) || (len % 4) != 0)
5514fa7a5b4Sclaudio 			goto badlen;
55283072fb6Sclaudio 		break;
553bd9df44eSclaudio 	default:
55422b46a1fSclaudio 		rtr_send_error(rs, msg, UNSUPP_PDU_TYPE, "type %s",
5559dad7388Sclaudio 		    log_rtr_type(rh.type));
556bd9df44eSclaudio 		return -1;
557bd9df44eSclaudio 	}
558bd9df44eSclaudio 
55937149e4fSclaudio 	*msgtype = rh.type;
56037149e4fSclaudio 
56137149e4fSclaudio 	return 0;
56237149e4fSclaudio 
56337149e4fSclaudio  badlen:
56422b46a1fSclaudio 	rtr_send_error(rs, msg, CORRUPT_DATA, "%s: bad length: %zu bytes",
5659dad7388Sclaudio 	    log_rtr_type(rh.type), len);
566bd9df44eSclaudio 	return -1;
567bd9df44eSclaudio 
56837149e4fSclaudio  badversion:
56922b46a1fSclaudio 	rtr_send_error(rs, msg, UNEXP_PROTOCOL_VERS, "%s: version %d",
5709dad7388Sclaudio 	    log_rtr_type(rh.type), rh.version);
571bd9df44eSclaudio 	return -1;
572bd9df44eSclaudio }
573bd9df44eSclaudio 
574bd9df44eSclaudio static int
575b1793e8cSclaudio rtr_parse_notify(struct rtr_session *rs, struct ibuf *pdu)
576bd9df44eSclaudio {
57737149e4fSclaudio 	struct rtr_notify notify;
57837149e4fSclaudio 
57937149e4fSclaudio 	/* ignore SERIAL_NOTIFY during startup */
58037149e4fSclaudio 	if (rs->state == RTR_STATE_NEGOTIATION)
58137149e4fSclaudio 		return 0;
58237149e4fSclaudio 
5839dad7388Sclaudio 	if (ibuf_get(pdu, &notify, sizeof(notify)) == -1)
5849dad7388Sclaudio 		goto badlen;
58537149e4fSclaudio 
586a62144a2Sclaudio 	/* set session_id if not yet happened */
587a62144a2Sclaudio 	if (rs->session_id == -1)
588a62144a2Sclaudio 		rs->session_id = ntohs(notify.hdr.session_id);
589a62144a2Sclaudio 
59037149e4fSclaudio 	if (rtr_check_session_id(rs, rs->session_id, &notify.hdr, pdu) == -1)
59137149e4fSclaudio 		return -1;
59237149e4fSclaudio 
593964d6687Sclaudio 	if (rs->state != RTR_STATE_ESTABLISHED) {
5945a04dc7fSclaudio 		log_warnx("rtr %s: received %s: while in state %s (ignored)",
5955a04dc7fSclaudio 		    log_rtr(rs), log_rtr_type(SERIAL_NOTIFY),
5965a04dc7fSclaudio 		    rtr_statenames[rs->state]);
597bd9df44eSclaudio 		return 0;
598bd9df44eSclaudio 	}
599bd9df44eSclaudio 
600bd9df44eSclaudio 	rtr_fsm(rs, RTR_EVNT_SERIAL_NOTIFY);
601bd9df44eSclaudio 	return 0;
6029dad7388Sclaudio 
6039dad7388Sclaudio  badlen:
6049dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
6059dad7388Sclaudio 	    log_rtr_type(SERIAL_NOTIFY));
6069dad7388Sclaudio 	return -1;
607bd9df44eSclaudio }
608bd9df44eSclaudio 
609bd9df44eSclaudio static int
610b1793e8cSclaudio rtr_parse_cache_response(struct rtr_session *rs, struct ibuf *pdu)
611bd9df44eSclaudio {
61237149e4fSclaudio 	struct rtr_response resp;
61337149e4fSclaudio 
6149dad7388Sclaudio 	if (ibuf_get(pdu, &resp, sizeof(resp)) == -1)
6159dad7388Sclaudio 		goto badlen;
61637149e4fSclaudio 
61737149e4fSclaudio 	/* set session_id if not yet happened */
61837149e4fSclaudio 	if (rs->session_id == -1)
61937149e4fSclaudio 		rs->session_id = ntohs(resp.hdr.session_id);
62037149e4fSclaudio 
62137149e4fSclaudio 	if (rtr_check_session_id(rs, rs->session_id, &resp.hdr, pdu) == -1)
62237149e4fSclaudio 		return -1;
62337149e4fSclaudio 
62437149e4fSclaudio 	if (rs->state != RTR_STATE_ESTABLISHED) {
6259dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context",
6269dad7388Sclaudio 		    log_rtr_type(CACHE_RESPONSE));
627bd9df44eSclaudio 		return -1;
628bd9df44eSclaudio 	}
629bd9df44eSclaudio 
630bd9df44eSclaudio 	rtr_fsm(rs, RTR_EVNT_CACHE_RESPONSE);
631bd9df44eSclaudio 	return 0;
6329dad7388Sclaudio 
6339dad7388Sclaudio  badlen:
6349dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
6359dad7388Sclaudio 	    log_rtr_type(CACHE_RESPONSE));
6369dad7388Sclaudio 	return -1;
637bd9df44eSclaudio }
638bd9df44eSclaudio 
639bd9df44eSclaudio static int
640b1793e8cSclaudio rtr_parse_ipv4_prefix(struct rtr_session *rs, struct ibuf *pdu)
641bd9df44eSclaudio {
642bd9df44eSclaudio 	struct rtr_ipv4 ip4;
643bd9df44eSclaudio 	struct roa *roa;
644bd9df44eSclaudio 
6459dad7388Sclaudio 	if (ibuf_get(pdu, &ip4, sizeof(ip4)) == -1)
6469dad7388Sclaudio 		goto badlen;
647bd9df44eSclaudio 
64837149e4fSclaudio 	if (rtr_check_session_id(rs, 0, &ip4.hdr, pdu) == -1)
64937149e4fSclaudio 		return -1;
65037149e4fSclaudio 
6510314fe8bSclaudio 	if (rs->state != RTR_STATE_EXCHANGE) {
6529dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context",
6539dad7388Sclaudio 		    log_rtr_type(IPV4_PREFIX));
654bd9df44eSclaudio 		return -1;
655bd9df44eSclaudio 	}
656bd9df44eSclaudio 
65769fad272Sclaudio 	if (ip4.prefixlen > 32 || ip4.maxlen > 32 ||
65869fad272Sclaudio 	    ip4.prefixlen > ip4.maxlen) {
6599dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA,
6609dad7388Sclaudio 		    "%s: bad prefixlen / maxlen", log_rtr_type(IPV4_PREFIX));
6611eccb425Sjob 		return -1;
6621eccb425Sjob 	}
663f3760350Stb 
664f3760350Stb 	if ((roa = calloc(1, sizeof(*roa))) == NULL) {
6659dad7388Sclaudio 		rtr_send_error(rs, NULL, INTERNAL_ERROR, "out of memory");
666f3760350Stb 		return -1;
667f3760350Stb 	}
668bd9df44eSclaudio 	roa->aid = AID_INET;
669bd9df44eSclaudio 	roa->prefixlen = ip4.prefixlen;
670bd9df44eSclaudio 	roa->maxlen = ip4.maxlen;
671bd9df44eSclaudio 	roa->asnum = ntohl(ip4.asnum);
672bd9df44eSclaudio 	roa->prefix.inet.s_addr = ip4.prefix;
673bd9df44eSclaudio 
674bd9df44eSclaudio 	if (ip4.flags & FLAG_ANNOUNCE) {
675bd9df44eSclaudio 		if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) {
6769dad7388Sclaudio 			rtr_send_error(rs, pdu, DUP_REC_RECV, "%s %s",
6779dad7388Sclaudio 			    log_rtr_type(IPV4_PREFIX), log_roa(roa));
678bd9df44eSclaudio 			free(roa);
679bd9df44eSclaudio 			return -1;
680bd9df44eSclaudio 		}
681bd9df44eSclaudio 	} else {
682bd9df44eSclaudio 		struct roa *r;
683bd9df44eSclaudio 
684bd9df44eSclaudio 		r = RB_FIND(roa_tree, &rs->roa_set, roa);
685bd9df44eSclaudio 		if (r == NULL) {
6869dad7388Sclaudio 			rtr_send_error(rs, pdu, UNK_REC_WDRAWL, "%s %s",
6879dad7388Sclaudio 			    log_rtr_type(IPV4_PREFIX), log_roa(roa));
688bd9df44eSclaudio 			free(roa);
689bd9df44eSclaudio 			return -1;
690bd9df44eSclaudio 		}
691bd9df44eSclaudio 		RB_REMOVE(roa_tree, &rs->roa_set, r);
692bd9df44eSclaudio 		free(r);
693bd9df44eSclaudio 		free(roa);
694bd9df44eSclaudio 	}
695bd9df44eSclaudio 
696bd9df44eSclaudio 	return 0;
6979dad7388Sclaudio 
6989dad7388Sclaudio  badlen:
6999dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
7009dad7388Sclaudio 	    log_rtr_type(IPV4_PREFIX));
7019dad7388Sclaudio 	return -1;
702bd9df44eSclaudio }
703bd9df44eSclaudio 
704bd9df44eSclaudio static int
705b1793e8cSclaudio rtr_parse_ipv6_prefix(struct rtr_session *rs, struct ibuf *pdu)
706bd9df44eSclaudio {
707bd9df44eSclaudio 	struct rtr_ipv6 ip6;
708bd9df44eSclaudio 	struct roa *roa;
709bd9df44eSclaudio 
7109dad7388Sclaudio 	if (ibuf_get(pdu, &ip6, sizeof(ip6)) == -1)
7119dad7388Sclaudio 		goto badlen;
712bd9df44eSclaudio 
71337149e4fSclaudio 	if (rtr_check_session_id(rs, 0, &ip6.hdr, pdu) == -1)
71437149e4fSclaudio 		return -1;
71537149e4fSclaudio 
7160314fe8bSclaudio 	if (rs->state != RTR_STATE_EXCHANGE) {
7179dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context",
7189dad7388Sclaudio 		    log_rtr_type(IPV6_PREFIX));
719bd9df44eSclaudio 		return -1;
720bd9df44eSclaudio 	}
721bd9df44eSclaudio 
72269fad272Sclaudio 	if (ip6.prefixlen > 128 || ip6.maxlen > 128 ||
72369fad272Sclaudio 	    ip6.prefixlen > ip6.maxlen) {
7249dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA,
7259dad7388Sclaudio 		    "%s: bad prefixlen / maxlen", log_rtr_type(IPV6_PREFIX));
7261eccb425Sjob 		return -1;
7271eccb425Sjob 	}
728f3760350Stb 
729f3760350Stb 	if ((roa = calloc(1, sizeof(*roa))) == NULL) {
7309dad7388Sclaudio 		rtr_send_error(rs, NULL, INTERNAL_ERROR, "out of memory");
731f3760350Stb 		return -1;
732f3760350Stb 	}
733bd9df44eSclaudio 	roa->aid = AID_INET6;
734bd9df44eSclaudio 	roa->prefixlen = ip6.prefixlen;
735bd9df44eSclaudio 	roa->maxlen = ip6.maxlen;
736bd9df44eSclaudio 	roa->asnum = ntohl(ip6.asnum);
737bd9df44eSclaudio 	memcpy(&roa->prefix.inet6, ip6.prefix, sizeof(roa->prefix.inet6));
738bd9df44eSclaudio 
739bd9df44eSclaudio 	if (ip6.flags & FLAG_ANNOUNCE) {
740bd9df44eSclaudio 		if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) {
7419dad7388Sclaudio 			rtr_send_error(rs, pdu, DUP_REC_RECV, "%s %s",
7429dad7388Sclaudio 			    log_rtr_type(IPV6_PREFIX), log_roa(roa));
743bd9df44eSclaudio 			free(roa);
744bd9df44eSclaudio 			return -1;
745bd9df44eSclaudio 		}
746bd9df44eSclaudio 	} else {
747bd9df44eSclaudio 		struct roa *r;
748bd9df44eSclaudio 
749bd9df44eSclaudio 		r = RB_FIND(roa_tree, &rs->roa_set, roa);
750bd9df44eSclaudio 		if (r == NULL) {
7519dad7388Sclaudio 			rtr_send_error(rs, pdu, UNK_REC_WDRAWL, "%s %s",
7529dad7388Sclaudio 			    log_rtr_type(IPV6_PREFIX), log_roa(roa));
753bd9df44eSclaudio 			free(roa);
754bd9df44eSclaudio 			return -1;
755bd9df44eSclaudio 		}
756bd9df44eSclaudio 		RB_REMOVE(roa_tree, &rs->roa_set, r);
757bd9df44eSclaudio 		free(r);
758bd9df44eSclaudio 		free(roa);
759bd9df44eSclaudio 	}
760bd9df44eSclaudio 	return 0;
7619dad7388Sclaudio 
7629dad7388Sclaudio  badlen:
7639dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
7649dad7388Sclaudio 	    log_rtr_type(IPV6_PREFIX));
7659dad7388Sclaudio 	return -1;
766bd9df44eSclaudio }
767bd9df44eSclaudio 
768bd9df44eSclaudio static int
769b1793e8cSclaudio rtr_parse_aspa(struct rtr_session *rs, struct ibuf *pdu)
77083072fb6Sclaudio {
77183072fb6Sclaudio 	struct rtr_aspa rtr_aspa;
77283072fb6Sclaudio 	struct aspa_set *aspa, *a;
773228f6bd1Sclaudio 	uint32_t cnt, i;
774228f6bd1Sclaudio 	uint8_t flags;
77583072fb6Sclaudio 
776b0ea642aSclaudio 	if (ibuf_get(pdu, &rtr_aspa, sizeof(rtr_aspa)) == -1)
7779dad7388Sclaudio 		goto badlen;
7789dad7388Sclaudio 
779228f6bd1Sclaudio 	flags = rtr_aspa.hdr.flags;
780228f6bd1Sclaudio 	cnt = ibuf_size(pdu) / sizeof(uint32_t);
78183072fb6Sclaudio 
782d9facda5Sclaudio 	if ((flags & FLAG_ANNOUNCE) && cnt == 0) {
783d9facda5Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: "
784d9facda5Sclaudio 		    "announce with empty SPAS", log_rtr_type(ASPA));
785d9facda5Sclaudio 		return -1;
786d9facda5Sclaudio 	}
787d9facda5Sclaudio 	if ((flags & FLAG_ANNOUNCE) == 0 && cnt != 0) {
788d9facda5Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: "
789d9facda5Sclaudio 		    "withdraw with non-empty SPAS", log_rtr_type(ASPA));
790d9facda5Sclaudio 		return -1;
791d9facda5Sclaudio 	}
792d9facda5Sclaudio 
7930314fe8bSclaudio 	if (rs->state != RTR_STATE_EXCHANGE) {
7949dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context",
7959dad7388Sclaudio 		    log_rtr_type(ASPA));
79683072fb6Sclaudio 		return -1;
79783072fb6Sclaudio 	}
79883072fb6Sclaudio 
799623585daSclaudio 	/* treat ASPA records with too many SPAS like a withdraw */
800623585daSclaudio 	if (cnt > MAX_ASPA_SPAS_COUNT) {
801623585daSclaudio 		struct aspa_set needle = { 0 };
802623585daSclaudio 		needle.as = ntohl(rtr_aspa.cas);
803623585daSclaudio 
804623585daSclaudio 		log_warnx("rtr %s: oversized ASPA PDU: "
805623585daSclaudio 		    "imlicit withdraw of customerAS %s",
806623585daSclaudio 		    log_rtr(rs), log_as(needle.as));
807228f6bd1Sclaudio 		a = RB_FIND(aspa_tree, &rs->aspa, &needle);
808623585daSclaudio 		if (a != NULL) {
809228f6bd1Sclaudio 			RB_REMOVE(aspa_tree, &rs->aspa, a);
810623585daSclaudio 			free_aspa(a);
811623585daSclaudio 		}
812623585daSclaudio 		return 0;
813623585daSclaudio 	}
814623585daSclaudio 
81583072fb6Sclaudio 	/* create aspa_set entry from the rtr aspa pdu */
81683072fb6Sclaudio 	if ((aspa = calloc(1, sizeof(*aspa))) == NULL) {
8179dad7388Sclaudio 		rtr_send_error(rs, NULL, INTERNAL_ERROR, "out of memory");
81883072fb6Sclaudio 		return -1;
81983072fb6Sclaudio 	}
82083072fb6Sclaudio 	aspa->as = ntohl(rtr_aspa.cas);
82183072fb6Sclaudio 	aspa->num = cnt;
82283072fb6Sclaudio 	if (cnt > 0) {
823c0c9c169Sclaudio 		if ((aspa->tas = calloc(cnt, sizeof(uint32_t))) == NULL) {
82483072fb6Sclaudio 			free_aspa(aspa);
8259dad7388Sclaudio 			rtr_send_error(rs, NULL, INTERNAL_ERROR,
8269dad7388Sclaudio 			    "out of memory");
82783072fb6Sclaudio 			return -1;
82883072fb6Sclaudio 		}
82983072fb6Sclaudio 		for (i = 0; i < cnt; i++) {
830*1cd81e65Sclaudio 			if (ibuf_get_n32(pdu, &aspa->tas[i]) == -1) {
831*1cd81e65Sclaudio 				free_aspa(aspa);
8329dad7388Sclaudio 				goto badlen;
83383072fb6Sclaudio 			}
83483072fb6Sclaudio 		}
835*1cd81e65Sclaudio 	}
83683072fb6Sclaudio 
837228f6bd1Sclaudio 	if (flags & FLAG_ANNOUNCE) {
838228f6bd1Sclaudio 		a = RB_INSERT(aspa_tree, &rs->aspa, aspa);
83983072fb6Sclaudio 		if (a != NULL) {
840228f6bd1Sclaudio 			RB_REMOVE(aspa_tree, &rs->aspa, a);
84183072fb6Sclaudio 			free_aspa(a);
84283072fb6Sclaudio 
843228f6bd1Sclaudio 			if (RB_INSERT(aspa_tree, &rs->aspa, aspa) != NULL) {
8449dad7388Sclaudio 				rtr_send_error(rs, NULL, INTERNAL_ERROR,
8459dad7388Sclaudio 				    "corrupt aspa tree");
84683072fb6Sclaudio 				free_aspa(aspa);
84783072fb6Sclaudio 				return -1;
84883072fb6Sclaudio 			}
84983072fb6Sclaudio 		}
85083072fb6Sclaudio 	} else {
851228f6bd1Sclaudio 		a = RB_FIND(aspa_tree, &rs->aspa, aspa);
85283072fb6Sclaudio 		if (a == NULL) {
8539dad7388Sclaudio 			rtr_send_error(rs, pdu, UNK_REC_WDRAWL, "%s %s",
8549dad7388Sclaudio 			    log_rtr_type(ASPA), log_aspa(aspa));
85583072fb6Sclaudio 			free_aspa(aspa);
85683072fb6Sclaudio 			return -1;
85783072fb6Sclaudio 		}
858228f6bd1Sclaudio 		RB_REMOVE(aspa_tree, &rs->aspa, a);
85983072fb6Sclaudio 		free_aspa(a);
86083072fb6Sclaudio 		free_aspa(aspa);
86183072fb6Sclaudio 	}
86283072fb6Sclaudio 
86383072fb6Sclaudio 	return 0;
8649dad7388Sclaudio 
8659dad7388Sclaudio  badlen:
8669dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
8679dad7388Sclaudio 	    log_rtr_type(ASPA));
8689dad7388Sclaudio 	return -1;
86983072fb6Sclaudio }
87083072fb6Sclaudio 
87183072fb6Sclaudio static int
8720d6339a2Sclaudio rtr_parse_end_of_data_v0(struct rtr_session *rs, struct ibuf *pdu)
8730d6339a2Sclaudio {
8740d6339a2Sclaudio 	struct rtr_endofdata_v0 eod;
8750d6339a2Sclaudio 
8769dad7388Sclaudio 	if (ibuf_get(pdu, &eod, sizeof(eod)) == -1)
8779dad7388Sclaudio 		goto badlen;
8780d6339a2Sclaudio 
8790d6339a2Sclaudio 	if (rtr_check_session_id(rs, rs->session_id, &eod.hdr, pdu) == -1)
8800d6339a2Sclaudio 		return -1;
8810d6339a2Sclaudio 
8820d6339a2Sclaudio 	if (rs->state != RTR_STATE_EXCHANGE) {
8839dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context",
8849dad7388Sclaudio 		    log_rtr_type(END_OF_DATA));
8850d6339a2Sclaudio 		return -1;
8860d6339a2Sclaudio 	}
8870d6339a2Sclaudio 
8880d6339a2Sclaudio 	rs->serial = ntohl(eod.serial);
8890d6339a2Sclaudio 
8900d6339a2Sclaudio 	rtr_fsm(rs, RTR_EVNT_END_OF_DATA);
8910d6339a2Sclaudio 	return 0;
8929dad7388Sclaudio 
8939dad7388Sclaudio  badlen:
8949dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
8959dad7388Sclaudio 	    log_rtr_type(END_OF_DATA));
8969dad7388Sclaudio 	return -1;
8970d6339a2Sclaudio }
8980d6339a2Sclaudio 
8990d6339a2Sclaudio static int
900b1793e8cSclaudio rtr_parse_end_of_data(struct rtr_session *rs, struct ibuf *pdu)
901bd9df44eSclaudio {
902bd9df44eSclaudio 	struct rtr_endofdata eod;
903bd9df44eSclaudio 	uint32_t t;
904bd9df44eSclaudio 
9050d6339a2Sclaudio 	/* version 0 does not have the timing values */
9060d6339a2Sclaudio 	if (rs->version == 0)
9070d6339a2Sclaudio 		return rtr_parse_end_of_data_v0(rs, pdu);
9080d6339a2Sclaudio 
9099dad7388Sclaudio 	if (ibuf_get(pdu, &eod, sizeof(eod)) == -1)
9109dad7388Sclaudio 		goto badlen;
911bd9df44eSclaudio 
91237149e4fSclaudio 	if (rtr_check_session_id(rs, rs->session_id, &eod.hdr, pdu) == -1)
91337149e4fSclaudio 		return -1;
91437149e4fSclaudio 
9150314fe8bSclaudio 	if (rs->state != RTR_STATE_EXCHANGE) {
9169dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context",
9179dad7388Sclaudio 		    log_rtr_type(END_OF_DATA));
918bd9df44eSclaudio 		return -1;
919bd9df44eSclaudio 	}
920bd9df44eSclaudio 
921bd9df44eSclaudio 	rs->serial = ntohl(eod.serial);
922bd9df44eSclaudio 	/* validate timer values to be in the right range */
923bd9df44eSclaudio 	t = ntohl(eod.refresh);
924bd9df44eSclaudio 	if (t < 1 || t > 86400)
925bd9df44eSclaudio 		goto bad;
926bd9df44eSclaudio 	rs->refresh = t;
927bd9df44eSclaudio 	t = ntohl(eod.retry);
928bd9df44eSclaudio 	if (t < 1 || t > 7200)
929bd9df44eSclaudio 		goto bad;
930bd9df44eSclaudio 	rs->retry = t;
931bd9df44eSclaudio 	t = ntohl(eod.expire);
932bd9df44eSclaudio 	if (t < 600 || t > 172800)
933bd9df44eSclaudio 		goto bad;
934bd9df44eSclaudio 	if (t <= rs->retry || t <= rs->refresh)
935bd9df44eSclaudio 		goto bad;
936bd9df44eSclaudio 	rs->expire = t;
937bd9df44eSclaudio 
938bd9df44eSclaudio 	rtr_fsm(rs, RTR_EVNT_END_OF_DATA);
939bd9df44eSclaudio 	return 0;
940bd9df44eSclaudio 
941bd9df44eSclaudio bad:
9429dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad timeout values",
9439dad7388Sclaudio 	    log_rtr_type(END_OF_DATA));
9449dad7388Sclaudio 	return -1;
9459dad7388Sclaudio 
9469dad7388Sclaudio badlen:
9479dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
9489dad7388Sclaudio 	    log_rtr_type(END_OF_DATA));
949bd9df44eSclaudio 	return -1;
950bd9df44eSclaudio }
951bd9df44eSclaudio 
952bd9df44eSclaudio static int
953b1793e8cSclaudio rtr_parse_cache_reset(struct rtr_session *rs, struct ibuf *pdu)
954bd9df44eSclaudio {
95537149e4fSclaudio 	struct rtr_reset reset;
95637149e4fSclaudio 
9579dad7388Sclaudio 	if (ibuf_get(pdu, &reset, sizeof(reset)) == -1)
9589dad7388Sclaudio 		goto badlen;
95937149e4fSclaudio 
96037149e4fSclaudio 	if (rtr_check_session_id(rs, 0, &reset.hdr, pdu) == -1)
96137149e4fSclaudio 		return -1;
96237149e4fSclaudio 
9630314fe8bSclaudio 	if (rs->state != RTR_STATE_ESTABLISHED) {
9649dad7388Sclaudio 		rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context",
9659dad7388Sclaudio 		    log_rtr_type(CACHE_RESET));
966bd9df44eSclaudio 		return -1;
967bd9df44eSclaudio 	}
968bd9df44eSclaudio 
969bd9df44eSclaudio 	rtr_fsm(rs, RTR_EVNT_CACHE_RESET);
970bd9df44eSclaudio 	return 0;
9719dad7388Sclaudio 
9729dad7388Sclaudio  badlen:
9739dad7388Sclaudio 	rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length",
9749dad7388Sclaudio 	    log_rtr_type(CACHE_RESET));
9759dad7388Sclaudio 	return -1;
976bd9df44eSclaudio }
977bd9df44eSclaudio 
978bd9df44eSclaudio /*
979bd9df44eSclaudio  * Parse an Error Response message. This function behaves a bit different
980bd9df44eSclaudio  * from other parse functions since on error the connection needs to be
981bd9df44eSclaudio  * dropped without sending an error response back.
982bd9df44eSclaudio  */
983bd9df44eSclaudio static int
984b1793e8cSclaudio rtr_parse_error(struct rtr_session *rs, struct ibuf *pdu)
985bd9df44eSclaudio {
986bd9df44eSclaudio 	struct rtr_header rh;
987b1793e8cSclaudio 	struct ibuf err_pdu;
988bd9df44eSclaudio 	uint32_t pdu_len, msg_len;
989bd9df44eSclaudio 	char *str = NULL;
990bd9df44eSclaudio 	uint16_t errcode;
991929e486eSclaudio 	int rv = -1;
992bd9df44eSclaudio 
993b1793e8cSclaudio 	if (ibuf_get(pdu, &rh, sizeof(rh)) == -1)
994b1793e8cSclaudio 		goto fail;
995228f6bd1Sclaudio 	errcode = ntohs(rh.errcode);
996bd9df44eSclaudio 
997b1793e8cSclaudio 	if (ibuf_get_n32(pdu, &pdu_len) == -1)
998b1793e8cSclaudio 		goto fail;
999bd9df44eSclaudio 
1000bd9df44eSclaudio 	/* for now just ignore the embedded pdu */
1001b1793e8cSclaudio 	if (ibuf_get_ibuf(pdu, pdu_len, &err_pdu) == -1)
1002b1793e8cSclaudio 		goto fail;
1003bd9df44eSclaudio 
1004b1793e8cSclaudio 	if (ibuf_get_n32(pdu, &msg_len) == -1)
1005b1793e8cSclaudio 		goto fail;
1006bd9df44eSclaudio 
1007b1793e8cSclaudio 	/* optional error msg */
1008bd9df44eSclaudio 	if (msg_len != 0)
1009b1793e8cSclaudio 		if ((str = ibuf_get_string(pdu, msg_len)) == NULL)
1010b1793e8cSclaudio 			goto fail;
1011bd9df44eSclaudio 
1012bd9df44eSclaudio 	log_warnx("rtr %s: received error: %s%s%s", log_rtr(rs),
1013bd9df44eSclaudio 	    log_rtr_error(errcode), str ? ": " : "", str ? str : "");
1014bd9df44eSclaudio 
1015bd9df44eSclaudio 	if (errcode == NO_DATA_AVAILABLE) {
1016bd9df44eSclaudio 		rtr_fsm(rs, RTR_EVNT_NO_DATA);
1017929e486eSclaudio 		rv = 0;
1018125ef3d5Sclaudio 	} else if (errcode == UNSUPP_PROTOCOL_VERS) {
101983072fb6Sclaudio 		rtr_fsm(rs, RTR_EVNT_UNSUPP_PROTO_VERSION);
1020125ef3d5Sclaudio 		rv = 0;
1021125ef3d5Sclaudio 	} else
10222ced3cdbSclaudio 		rtr_fsm(rs, RTR_EVNT_RESET_AND_CLOSE);
1023929e486eSclaudio 
1024bd9df44eSclaudio 	rs->last_recv_error = errcode;
1025bd9df44eSclaudio 	if (str)
1026b1793e8cSclaudio 		strlcpy(rs->last_recv_msg, str, sizeof(rs->last_recv_msg));
1027bd9df44eSclaudio 	else
1028b1793e8cSclaudio 		memset(rs->last_recv_msg, 0, sizeof(rs->last_recv_msg));
1029bd9df44eSclaudio 
1030bd9df44eSclaudio 	free(str);
1031929e486eSclaudio 	return rv;
1032b1793e8cSclaudio 
1033b1793e8cSclaudio  fail:
1034b1793e8cSclaudio 	log_warnx("rtr %s: received %s: bad encoding", log_rtr(rs),
1035b1793e8cSclaudio 	    log_rtr_type(ERROR_REPORT));
1036b1793e8cSclaudio 	rtr_fsm(rs, RTR_EVNT_RESET_AND_CLOSE);
1037b1793e8cSclaudio 	return -1;
1038bd9df44eSclaudio }
1039bd9df44eSclaudio 
1040bd9df44eSclaudio /*
1041bd9df44eSclaudio  * Try to process received rtr message, it is possible that not a full
1042bd9df44eSclaudio  * message is in the buffer. In that case stop, once new data is available
1043bd9df44eSclaudio  * a retry will be done.
1044bd9df44eSclaudio  */
1045bd9df44eSclaudio static void
104622b46a1fSclaudio rtr_process_msg(struct rtr_session *rs, struct ibuf *msg)
1047bd9df44eSclaudio {
1048bd9df44eSclaudio 	enum rtr_pdu_type msgtype;
1049bd9df44eSclaudio 
105022b46a1fSclaudio 	/* parse and check header */
105122b46a1fSclaudio 	if (rtr_parse_header(rs, msg, &msgtype) == -1)
1052bd9df44eSclaudio 		return;
1053bd9df44eSclaudio 
1054bd9df44eSclaudio 	switch (msgtype) {
1055bd9df44eSclaudio 	case SERIAL_NOTIFY:
105622b46a1fSclaudio 		if (rtr_parse_notify(rs, msg) == -1)
1057bd9df44eSclaudio 			return;
1058bd9df44eSclaudio 		break;
1059bd9df44eSclaudio 	case CACHE_RESPONSE:
106022b46a1fSclaudio 		if (rtr_parse_cache_response(rs, msg) == -1)
1061bd9df44eSclaudio 			return;
1062bd9df44eSclaudio 		break;
1063bd9df44eSclaudio 	case IPV4_PREFIX:
106422b46a1fSclaudio 		if (rtr_parse_ipv4_prefix(rs, msg) == -1)
1065bd9df44eSclaudio 			return;
1066bd9df44eSclaudio 		break;
1067bd9df44eSclaudio 	case IPV6_PREFIX:
106822b46a1fSclaudio 		if (rtr_parse_ipv6_prefix(rs, msg) == -1)
1069bd9df44eSclaudio 			return;
1070bd9df44eSclaudio 		break;
1071bd9df44eSclaudio 	case END_OF_DATA:
107222b46a1fSclaudio 		if (rtr_parse_end_of_data(rs, msg) == -1)
1073bd9df44eSclaudio 			return;
1074bd9df44eSclaudio 		break;
1075bd9df44eSclaudio 	case CACHE_RESET:
107622b46a1fSclaudio 		if (rtr_parse_cache_reset(rs, msg) == -1)
1077bd9df44eSclaudio 			return;
1078bd9df44eSclaudio 		break;
1079bd9df44eSclaudio 	case ROUTER_KEY:
1080bd9df44eSclaudio 		/* silently ignore router key */
1081bd9df44eSclaudio 		break;
1082bd9df44eSclaudio 	case ERROR_REPORT:
108322b46a1fSclaudio 		if (rtr_parse_error(rs, msg) == -1) {
1084bd9df44eSclaudio 			/* no need to send back an error */
1085bd9df44eSclaudio 			return;
1086b1793e8cSclaudio 		}
1087bd9df44eSclaudio 		break;
108883072fb6Sclaudio 	case ASPA:
108922b46a1fSclaudio 		if (rtr_parse_aspa(rs, msg) == -1)
109083072fb6Sclaudio 			return;
109183072fb6Sclaudio 		break;
1092bd9df44eSclaudio 	default:
10939dad7388Sclaudio 		/* unreachable, checked in rtr_parse_header() */
109422b46a1fSclaudio 		rtr_send_error(rs, msg, UNSUPP_PDU_TYPE, "type %s",
10959dad7388Sclaudio 		    log_rtr_type(msgtype));
1096bd9df44eSclaudio 		return;
1097bd9df44eSclaudio 	}
1098bd9df44eSclaudio }
1099bd9df44eSclaudio 
1100bd9df44eSclaudio /*
1101bd9df44eSclaudio  * Simple FSM for RTR sessions
1102bd9df44eSclaudio  */
1103bd9df44eSclaudio static void
1104bd9df44eSclaudio rtr_fsm(struct rtr_session *rs, enum rtr_event event)
1105bd9df44eSclaudio {
1106bd9df44eSclaudio 	enum rtr_state prev_state = rs->state;
1107bd9df44eSclaudio 
1108bd9df44eSclaudio 	switch (event) {
110983072fb6Sclaudio 	case RTR_EVNT_UNSUPP_PROTO_VERSION:
1110d87cfbccSclaudio 		if (rs->prev_version == rs->version ||
1111d87cfbccSclaudio 		    rs->version < rs->min_version) {
111283072fb6Sclaudio 			/*
1113125ef3d5Sclaudio 			 * Can't downgrade anymore, fail connection.
1114125ef3d5Sclaudio 			 * RFC requires sending the error with the
1115125ef3d5Sclaudio 			 * highest supported version number.
111683072fb6Sclaudio 			 */
1117d87cfbccSclaudio 			rs->version = rtr_max_session_version(rs);
11189dad7388Sclaudio 			rtr_send_error(rs, NULL, UNSUPP_PROTOCOL_VERS,
11199dad7388Sclaudio 			    "negotiation failed");
112083072fb6Sclaudio 			return;
112183072fb6Sclaudio 		}
1122125ef3d5Sclaudio 		/* try again with new version */
1123125ef3d5Sclaudio 		if (rs->session_id == -1)
1124125ef3d5Sclaudio 			rtr_send_reset_query(rs);
1125125ef3d5Sclaudio 		else
1126125ef3d5Sclaudio 			rtr_send_serial_query(rs);
112783072fb6Sclaudio 		break;
11282ced3cdbSclaudio 	case RTR_EVNT_RESET_AND_CLOSE:
11295a04dc7fSclaudio 		rtr_reset_cache(rs);
11305a04dc7fSclaudio 		rtr_recalc();
11312ced3cdbSclaudio 		/* FALLTHROUGH */
11322ced3cdbSclaudio 	case RTR_EVNT_CON_CLOSE:
11335a04dc7fSclaudio 		if (rs->fd != -1) {
1134bd9df44eSclaudio 			/* flush buffers */
113505453d67Sclaudio 			msgbuf_clear(rs->w);
1136bd9df44eSclaudio 			close(rs->fd);
1137bd9df44eSclaudio 			rs->fd = -1;
1138cd16358eSclaudio 			rtr_imsg_compose(IMSG_SOCKET_TEARDOWN, rs->id, 0,
1139cd16358eSclaudio 			    NULL, 0);
1140bd9df44eSclaudio 		}
1141bd9df44eSclaudio 		/* try to reopen session */
1142d87cfbccSclaudio 		if (!rs->errored)
1143bd9df44eSclaudio 			timer_set(&rs->timers, Timer_Rtr_Retry,
1144bd9df44eSclaudio 			    arc4random_uniform(10));
1145d87cfbccSclaudio 		else
1146d87cfbccSclaudio 			timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry);
1147d87cfbccSclaudio 
1148d87cfbccSclaudio 		rs->errored = 1;
1149125ef3d5Sclaudio 		/*
1150125ef3d5Sclaudio 		 * A close event during version negotiation needs to remain
1151125ef3d5Sclaudio 		 * in the negotiation state else the same error will happen
1152125ef3d5Sclaudio 		 * over and over again. The RFC is utterly underspecified
1153125ef3d5Sclaudio 		 * and some RTR caches close the connection after sending
1154125ef3d5Sclaudio 		 * the error PDU.
1155125ef3d5Sclaudio 		 */
1156125ef3d5Sclaudio 		if (rs->state != RTR_STATE_NEGOTIATION)
1157125ef3d5Sclaudio 			rs->state = RTR_STATE_CLOSED;
1158bd9df44eSclaudio 		break;
1159bd9df44eSclaudio 	case RTR_EVNT_START:
1160bd9df44eSclaudio 	case RTR_EVNT_TIMER_RETRY:
1161bd9df44eSclaudio 		switch (rs->state) {
1162bd9df44eSclaudio 		case RTR_STATE_ERROR:
11632ced3cdbSclaudio 			rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1164125ef3d5Sclaudio 			break;
1165bd9df44eSclaudio 		case RTR_STATE_CLOSED:
1166125ef3d5Sclaudio 		case RTR_STATE_NEGOTIATION:
1167bd9df44eSclaudio 			timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry);
1168cd16358eSclaudio 			rtr_imsg_compose(IMSG_SOCKET_SETUP, rs->id, 0, NULL, 0);
1169125ef3d5Sclaudio 			break;
1170a62144a2Sclaudio 		case RTR_STATE_ESTABLISHED:
1171a62144a2Sclaudio 			if (rs->session_id == -1)
1172a62144a2Sclaudio 				rtr_send_reset_query(rs);
1173a62144a2Sclaudio 			else
1174a62144a2Sclaudio 				rtr_send_serial_query(rs);
1175bd9df44eSclaudio 		default:
1176bd9df44eSclaudio 			break;
1177bd9df44eSclaudio 		}
1178125ef3d5Sclaudio 		break;
1179bd9df44eSclaudio 	case RTR_EVNT_CON_OPEN:
1180bd9df44eSclaudio 		timer_stop(&rs->timers, Timer_Rtr_Retry);
1181125ef3d5Sclaudio 		rs->state = RTR_STATE_NEGOTIATION;
1182bd9df44eSclaudio 		if (rs->session_id == -1)
11835a04dc7fSclaudio 			rtr_send_reset_query(rs);
1184bd9df44eSclaudio 		else
11855a04dc7fSclaudio 			rtr_send_serial_query(rs);
1186bd9df44eSclaudio 		break;
1187bd9df44eSclaudio 	case RTR_EVNT_SERIAL_NOTIFY:
1188bd9df44eSclaudio 		/* schedule a refresh after a quick wait */
1189bd9df44eSclaudio 		timer_set(&rs->timers, Timer_Rtr_Refresh,
1190bd9df44eSclaudio 		    arc4random_uniform(10));
1191bd9df44eSclaudio 		break;
1192bd9df44eSclaudio 	case RTR_EVNT_TIMER_REFRESH:
11935a04dc7fSclaudio 		rtr_send_serial_query(rs);
1194bd9df44eSclaudio 		break;
1195bd9df44eSclaudio 	case RTR_EVNT_TIMER_EXPIRE:
11965a04dc7fSclaudio 		rtr_reset_cache(rs);
1197bd9df44eSclaudio 		rtr_recalc();
1198bd9df44eSclaudio 		break;
1199dfd27b08Sclaudio 	case RTR_EVNT_TIMER_ACTIVE:
1200dfd27b08Sclaudio 		log_warnx("rtr %s: activity timer fired", log_rtr(rs));
1201dfd27b08Sclaudio 		rtr_sem_release(rs->active_lock);
1202dfd27b08Sclaudio 		rtr_recalc();
1203dfd27b08Sclaudio 		rs->active_lock = 0;
1204dfd27b08Sclaudio 		break;
1205bd9df44eSclaudio 	case RTR_EVNT_CACHE_RESPONSE:
12060314fe8bSclaudio 		rs->state = RTR_STATE_EXCHANGE;
1207bd9df44eSclaudio 		timer_stop(&rs->timers, Timer_Rtr_Refresh);
1208bd9df44eSclaudio 		timer_stop(&rs->timers, Timer_Rtr_Retry);
1209dfd27b08Sclaudio 		timer_set(&rs->timers, Timer_Rtr_Active, rs->active);
1210dfd27b08Sclaudio 		/* prevent rtr_recalc from running while active */
1211dfd27b08Sclaudio 		rs->active_lock = 1;
1212dfd27b08Sclaudio 		rtr_sem_acquire(rs->active_lock);
1213bd9df44eSclaudio 		break;
1214bd9df44eSclaudio 	case RTR_EVNT_END_OF_DATA:
1215bd9df44eSclaudio 		/* start refresh and expire timers */
1216bd9df44eSclaudio 		timer_set(&rs->timers, Timer_Rtr_Refresh, rs->refresh);
1217bd9df44eSclaudio 		timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire);
1218dfd27b08Sclaudio 		timer_stop(&rs->timers, Timer_Rtr_Active);
12190314fe8bSclaudio 		rs->state = RTR_STATE_ESTABLISHED;
1220dfd27b08Sclaudio 		rtr_sem_release(rs->active_lock);
1221bd9df44eSclaudio 		rtr_recalc();
1222dfd27b08Sclaudio 		rs->active_lock = 0;
1223d87cfbccSclaudio 		rs->errored = 0;
1224047cb73cSclaudio 		/* clear the last errors */
1225047cb73cSclaudio 		rs->last_sent_error = NO_ERROR;
1226047cb73cSclaudio 		rs->last_recv_error = NO_ERROR;
1227047cb73cSclaudio 		rs->last_sent_msg[0] = '\0';
1228047cb73cSclaudio 		rs->last_recv_msg[0] = '\0';
1229bd9df44eSclaudio 		break;
1230bd9df44eSclaudio 	case RTR_EVNT_CACHE_RESET:
12315a04dc7fSclaudio 		rtr_reset_cache(rs);
12322ced3cdbSclaudio 		rtr_recalc();
12335a04dc7fSclaudio 		/* retry after a quick wait */
1234bd9df44eSclaudio 		timer_set(&rs->timers, Timer_Rtr_Retry,
1235bd9df44eSclaudio 		    arc4random_uniform(10));
1236bd9df44eSclaudio 		break;
1237bd9df44eSclaudio 	case RTR_EVNT_NO_DATA:
1238bd9df44eSclaudio 		/* start retry timer */
1239bd9df44eSclaudio 		timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry);
1240bd9df44eSclaudio 		/* stop refresh timer just to be sure */
1241bd9df44eSclaudio 		timer_stop(&rs->timers, Timer_Rtr_Refresh);
12420314fe8bSclaudio 		rs->state = RTR_STATE_ESTABLISHED;
1243bd9df44eSclaudio 		break;
1244bd9df44eSclaudio 	case RTR_EVNT_SEND_ERROR:
12455a04dc7fSclaudio 		rtr_reset_cache(rs);
12465a04dc7fSclaudio 		rtr_recalc();
1247bd9df44eSclaudio 		rs->state = RTR_STATE_ERROR;
1248bd9df44eSclaudio 		break;
124937149e4fSclaudio 	case RTR_EVNT_NEGOTIATION_DONE:
125037149e4fSclaudio 		rs->state = RTR_STATE_ESTABLISHED;
125137149e4fSclaudio 		break;
1252bd9df44eSclaudio 	}
1253bd9df44eSclaudio 
1254fba72c10Sclaudio 	log_debug("rtr %s: state change %s -> %s, reason: %s",
1255bd9df44eSclaudio 	    log_rtr(rs), rtr_statenames[prev_state], rtr_statenames[rs->state],
1256bd9df44eSclaudio 	    rtr_eventnames[event]);
1257bd9df44eSclaudio }
1258bd9df44eSclaudio 
1259bd9df44eSclaudio /*
1260bd9df44eSclaudio  * IO handler for RTR sessions
1261bd9df44eSclaudio  */
1262bd9df44eSclaudio static void
1263bd9df44eSclaudio rtr_dispatch_msg(struct pollfd *pfd, struct rtr_session *rs)
1264bd9df44eSclaudio {
126522b46a1fSclaudio 	struct ibuf *b;
1266bd9df44eSclaudio 
1267bd9df44eSclaudio 	if (pfd->revents & POLLHUP) {
1268bd9df44eSclaudio 		log_warnx("rtr %s: Connection closed, hangup", log_rtr(rs));
12692ced3cdbSclaudio 		rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1270bd9df44eSclaudio 		return;
1271bd9df44eSclaudio 	}
1272bd9df44eSclaudio 	if (pfd->revents & (POLLERR|POLLNVAL)) {
1273bd9df44eSclaudio 		log_warnx("rtr %s: Connection closed, error", log_rtr(rs));
12742ced3cdbSclaudio 		rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1275bd9df44eSclaudio 		return;
1276bd9df44eSclaudio 	}
127705453d67Sclaudio 	if (pfd->revents & POLLOUT && msgbuf_queuelen(rs->w) > 0) {
127805453d67Sclaudio 		if (ibuf_write(rs->fd, rs->w) == -1) {
1279bd9df44eSclaudio 			log_warn("rtr %s: write error", log_rtr(rs));
12802ced3cdbSclaudio 			rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
12814fb43511Sclaudio 			return;
128283072fb6Sclaudio 		}
1283db359c81Sclaudio 		if (rs->state == RTR_STATE_ERROR &&
128405453d67Sclaudio 		    msgbuf_queuelen(rs->w) == 0)
12852ced3cdbSclaudio 			rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1286bd9df44eSclaudio 	}
1287bd9df44eSclaudio 	if (pfd->revents & POLLIN) {
128822b46a1fSclaudio 		switch (ibuf_read(rs->fd, rs->w)) {
128922b46a1fSclaudio 		case -1:
129022b46a1fSclaudio 			/* if already in error state, ignore */
129122b46a1fSclaudio 			if (rs->state == RTR_STATE_ERROR)
129222b46a1fSclaudio 				return;
1293bd9df44eSclaudio 			log_warn("rtr %s: read error", log_rtr(rs));
12942ced3cdbSclaudio 			rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1295bd9df44eSclaudio 			return;
129622b46a1fSclaudio 		case 0:
12972ced3cdbSclaudio 			rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1298bd9df44eSclaudio 			return;
1299bd9df44eSclaudio 		}
1300bd9df44eSclaudio 		/* new data arrived, try to process it */
130122b46a1fSclaudio 		while ((b = msgbuf_get(rs->w)) != NULL) {
130222b46a1fSclaudio 			rtr_process_msg(rs, b);
130322b46a1fSclaudio 			ibuf_free(b);
1304bd9df44eSclaudio 		}
130522b46a1fSclaudio 	}
1306bd9df44eSclaudio }
1307bd9df44eSclaudio 
1308bd9df44eSclaudio void
1309bd9df44eSclaudio rtr_check_events(struct pollfd *pfds, size_t npfds)
1310bd9df44eSclaudio {
1311bd9df44eSclaudio 	struct rtr_session *rs;
1312bd9df44eSclaudio 	struct timer *t;
1313bd9df44eSclaudio 	time_t now;
1314bd9df44eSclaudio 	size_t i = 0;
1315bd9df44eSclaudio 
1316bd9df44eSclaudio 	for (i = 0; i < npfds; i++) {
1317bd9df44eSclaudio 		if (pfds[i].revents == 0)
1318bd9df44eSclaudio 			continue;
1319bd9df44eSclaudio 		TAILQ_FOREACH(rs, &rtrs, entry)
1320bd9df44eSclaudio 			if (rs->fd == pfds[i].fd) {
1321bd9df44eSclaudio 				rtr_dispatch_msg(&pfds[i], rs);
1322bd9df44eSclaudio 				break;
1323bd9df44eSclaudio 			}
1324bd9df44eSclaudio 		if (rs == NULL)
1325bd9df44eSclaudio 			log_warnx("%s: unknown fd in pollfds", __func__);
1326bd9df44eSclaudio 	}
1327bd9df44eSclaudio 
1328bd9df44eSclaudio 	/* run all timers */
1329bd9df44eSclaudio 	now = getmonotime();
1330bd9df44eSclaudio 	TAILQ_FOREACH(rs, &rtrs, entry)
1331bd9df44eSclaudio 		if ((t = timer_nextisdue(&rs->timers, now)) != NULL) {
1332bd9df44eSclaudio 			/* stop timer so it does not trigger again */
1333bd9df44eSclaudio 			timer_stop(&rs->timers, t->type);
1334bd9df44eSclaudio 			switch (t->type) {
1335bd9df44eSclaudio 			case Timer_Rtr_Refresh:
1336bd9df44eSclaudio 				rtr_fsm(rs, RTR_EVNT_TIMER_REFRESH);
1337bd9df44eSclaudio 				break;
1338bd9df44eSclaudio 			case Timer_Rtr_Retry:
1339bd9df44eSclaudio 				rtr_fsm(rs, RTR_EVNT_TIMER_RETRY);
1340bd9df44eSclaudio 				break;
1341bd9df44eSclaudio 			case Timer_Rtr_Expire:
1342bd9df44eSclaudio 				rtr_fsm(rs, RTR_EVNT_TIMER_EXPIRE);
1343bd9df44eSclaudio 				break;
1344dfd27b08Sclaudio 			case Timer_Rtr_Active:
1345dfd27b08Sclaudio 				rtr_fsm(rs, RTR_EVNT_TIMER_ACTIVE);
1346dfd27b08Sclaudio 				break;
1347bd9df44eSclaudio 			default:
1348bd9df44eSclaudio 				fatalx("King Bula lost in time");
1349bd9df44eSclaudio 			}
1350bd9df44eSclaudio 		}
1351bd9df44eSclaudio }
1352bd9df44eSclaudio 
1353bd9df44eSclaudio size_t
1354bd9df44eSclaudio rtr_count(void)
1355bd9df44eSclaudio {
1356bd9df44eSclaudio 	struct rtr_session *rs;
1357bd9df44eSclaudio 	size_t count = 0;
1358bd9df44eSclaudio 
1359bd9df44eSclaudio 	TAILQ_FOREACH(rs, &rtrs, entry)
1360bd9df44eSclaudio 		count++;
1361bd9df44eSclaudio 	return count;
1362bd9df44eSclaudio }
1363bd9df44eSclaudio 
1364bd9df44eSclaudio size_t
1365bd9df44eSclaudio rtr_poll_events(struct pollfd *pfds, size_t npfds, time_t *timeout)
1366bd9df44eSclaudio {
1367bd9df44eSclaudio 	struct rtr_session *rs;
1368bd9df44eSclaudio 	time_t now = getmonotime();
1369bd9df44eSclaudio 	size_t i = 0;
1370bd9df44eSclaudio 
1371bd9df44eSclaudio 	TAILQ_FOREACH(rs, &rtrs, entry) {
1372bd9df44eSclaudio 		time_t nextaction;
1373bd9df44eSclaudio 		struct pollfd *pfd = pfds + i++;
1374bd9df44eSclaudio 
1375bd9df44eSclaudio 		if (i > npfds)
1376bd9df44eSclaudio 			fatalx("%s: too many sessions for pollfd", __func__);
1377bd9df44eSclaudio 
1378bd9df44eSclaudio 		if ((nextaction = timer_nextduein(&rs->timers, now)) != -1 &&
1379bd9df44eSclaudio 		    nextaction < *timeout)
1380bd9df44eSclaudio 			*timeout = nextaction;
1381bd9df44eSclaudio 
1382bd9df44eSclaudio 		if (rs->state == RTR_STATE_CLOSED) {
1383bd9df44eSclaudio 			pfd->fd = -1;
1384bd9df44eSclaudio 			continue;
1385bd9df44eSclaudio 		}
1386bd9df44eSclaudio 
1387bd9df44eSclaudio 		pfd->fd = rs->fd;
1388bd9df44eSclaudio 		pfd->events = 0;
1389bd9df44eSclaudio 
139005453d67Sclaudio 		if (msgbuf_queuelen(rs->w) > 0)
1391bd9df44eSclaudio 			pfd->events |= POLLOUT;
13920314fe8bSclaudio 		if (rs->state >= RTR_STATE_ESTABLISHED)
1393bd9df44eSclaudio 			pfd->events |= POLLIN;
1394bd9df44eSclaudio 	}
1395bd9df44eSclaudio 
1396bd9df44eSclaudio 	return i;
1397bd9df44eSclaudio }
1398bd9df44eSclaudio 
1399bd9df44eSclaudio struct rtr_session *
1400d87cfbccSclaudio rtr_new(uint32_t id, struct rtr_config_msg *conf)
1401bd9df44eSclaudio {
1402bd9df44eSclaudio 	struct rtr_session *rs;
1403bd9df44eSclaudio 
1404bd9df44eSclaudio 	if ((rs = calloc(1, sizeof(*rs))) == NULL)
1405d87cfbccSclaudio 		fatal("RTR session %s", conf->descr);
140622b46a1fSclaudio 	if ((rs->w = msgbuf_new_reader(sizeof(struct rtr_header),
14079509a1e6Sclaudio 	    rtr_reader_callback, rs)) == NULL)
140805453d67Sclaudio 		fatal("RTR session %s", conf->descr);
1409bd9df44eSclaudio 
1410bd9df44eSclaudio 	RB_INIT(&rs->roa_set);
1411c0c9c169Sclaudio 	RB_INIT(&rs->aspa);
1412bd9df44eSclaudio 	TAILQ_INIT(&rs->timers);
1413bd9df44eSclaudio 
1414d87cfbccSclaudio 	strlcpy(rs->descr, conf->descr, sizeof(rs->descr));
1415bd9df44eSclaudio 	rs->id = id;
1416bd9df44eSclaudio 	rs->session_id = -1;
1417d87cfbccSclaudio 	rs->min_version = conf->min_version;	/* must be set before version */
1418d87cfbccSclaudio 	rs->version = rtr_max_session_version(rs);
1419d87cfbccSclaudio 	rs->prev_version = rtr_max_session_version(rs);
1420bd9df44eSclaudio 	rs->refresh = RTR_DEFAULT_REFRESH;
1421bd9df44eSclaudio 	rs->retry = RTR_DEFAULT_RETRY;
1422bd9df44eSclaudio 	rs->expire = RTR_DEFAULT_EXPIRE;
1423dfd27b08Sclaudio 	rs->active = RTR_DEFAULT_ACTIVE;
1424bd9df44eSclaudio 	rs->state = RTR_STATE_CLOSED;
1425bd9df44eSclaudio 	rs->reconf_action = RECONF_REINIT;
1426bd9df44eSclaudio 	rs->last_recv_error = NO_ERROR;
1427bd9df44eSclaudio 	rs->last_sent_error = NO_ERROR;
1428bd9df44eSclaudio 
1429bd9df44eSclaudio 	/* make sure that some timer is running to abort bad sessions */
1430bd9df44eSclaudio 	timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire);
1431bd9df44eSclaudio 
1432bd9df44eSclaudio 	log_debug("rtr %s: new session, start", log_rtr(rs));
1433bd9df44eSclaudio 	TAILQ_INSERT_TAIL(&rtrs, rs, entry);
1434bd9df44eSclaudio 	rtr_fsm(rs, RTR_EVNT_START);
1435bd9df44eSclaudio 
1436bd9df44eSclaudio 	return rs;
1437bd9df44eSclaudio }
1438bd9df44eSclaudio 
1439bd9df44eSclaudio struct rtr_session *
1440bd9df44eSclaudio rtr_get(uint32_t id)
1441bd9df44eSclaudio {
1442bd9df44eSclaudio 	struct rtr_session *rs;
1443bd9df44eSclaudio 
1444bd9df44eSclaudio 	TAILQ_FOREACH(rs, &rtrs, entry)
1445bd9df44eSclaudio 		if (rs->id == id)
1446bd9df44eSclaudio 			return rs;
1447bd9df44eSclaudio 	return NULL;
1448bd9df44eSclaudio }
1449bd9df44eSclaudio 
1450bd9df44eSclaudio void
1451bd9df44eSclaudio rtr_free(struct rtr_session *rs)
1452bd9df44eSclaudio {
1453bd9df44eSclaudio 	if (rs == NULL)
1454bd9df44eSclaudio 		return;
1455bd9df44eSclaudio 
14565a04dc7fSclaudio 	rtr_reset_cache(rs);
14572ced3cdbSclaudio 	rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1458bd9df44eSclaudio 	timer_remove_all(&rs->timers);
145905453d67Sclaudio 	msgbuf_free(rs->w);
1460bd9df44eSclaudio 	free(rs);
1461bd9df44eSclaudio }
1462bd9df44eSclaudio 
1463bd9df44eSclaudio void
1464bd9df44eSclaudio rtr_open(struct rtr_session *rs, int fd)
1465bd9df44eSclaudio {
146683072fb6Sclaudio 	if (rs->state != RTR_STATE_CLOSED &&
146783072fb6Sclaudio 	    rs->state != RTR_STATE_NEGOTIATION) {
1468bd9df44eSclaudio 		log_warnx("rtr %s: bad session state", log_rtr(rs));
14692ced3cdbSclaudio 		rtr_fsm(rs, RTR_EVNT_CON_CLOSE);
1470bd9df44eSclaudio 	}
1471bd9df44eSclaudio 
1472125ef3d5Sclaudio 	if (rs->state == RTR_STATE_CLOSED) {
1473d87cfbccSclaudio 		rs->version = rtr_max_session_version(rs);
1474d87cfbccSclaudio 		rs->prev_version = rtr_max_session_version(rs);
1475125ef3d5Sclaudio 	}
1476bd9df44eSclaudio 
1477bb561412Sclaudio 	rs->fd = fd;
1478bd9df44eSclaudio 	rtr_fsm(rs, RTR_EVNT_CON_OPEN);
1479bd9df44eSclaudio }
1480bd9df44eSclaudio 
1481bd9df44eSclaudio void
1482bd9df44eSclaudio rtr_config_prep(void)
1483bd9df44eSclaudio {
1484bd9df44eSclaudio 	struct rtr_session *rs;
1485bd9df44eSclaudio 
1486bd9df44eSclaudio 	TAILQ_FOREACH(rs, &rtrs, entry)
1487bd9df44eSclaudio 		rs->reconf_action = RECONF_DELETE;
1488bd9df44eSclaudio }
1489bd9df44eSclaudio 
1490bd9df44eSclaudio void
1491bd9df44eSclaudio rtr_config_merge(void)
1492bd9df44eSclaudio {
1493bd9df44eSclaudio 	struct rtr_session *rs, *nrs;
1494bd9df44eSclaudio 
1495bd9df44eSclaudio 	TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs)
1496bd9df44eSclaudio 		if (rs->reconf_action == RECONF_DELETE) {
1497bd9df44eSclaudio 			TAILQ_REMOVE(&rtrs, rs, entry);
1498bd9df44eSclaudio 			rtr_free(rs);
1499bd9df44eSclaudio 		}
1500bd9df44eSclaudio }
1501bd9df44eSclaudio 
1502bd9df44eSclaudio void
1503d87cfbccSclaudio rtr_config_keep(struct rtr_session *rs, struct rtr_config_msg *conf)
1504bd9df44eSclaudio {
1505d87cfbccSclaudio 	strlcpy(rs->descr, conf->descr, sizeof(rs->descr));
1506d87cfbccSclaudio 	rs->min_version = conf->min_version;
1507bd9df44eSclaudio 	rs->reconf_action = RECONF_KEEP;
1508bd9df44eSclaudio }
1509bd9df44eSclaudio 
1510bd9df44eSclaudio void
1511bd9df44eSclaudio rtr_roa_merge(struct roa_tree *rt)
1512bd9df44eSclaudio {
1513bd9df44eSclaudio 	struct rtr_session *rs;
1514bd9df44eSclaudio 	struct roa *roa;
1515bd9df44eSclaudio 
1516bd9df44eSclaudio 	TAILQ_FOREACH(rs, &rtrs, entry) {
1517bd9df44eSclaudio 		RB_FOREACH(roa, roa_tree, &rs->roa_set)
151883072fb6Sclaudio 			rtr_roa_insert(rt, roa);
151983072fb6Sclaudio 	}
152083072fb6Sclaudio }
152183072fb6Sclaudio 
152283072fb6Sclaudio void
152383072fb6Sclaudio rtr_aspa_merge(struct aspa_tree *at)
152483072fb6Sclaudio {
152583072fb6Sclaudio 	struct rtr_session *rs;
152683072fb6Sclaudio 	struct aspa_set *aspa;
152783072fb6Sclaudio 
152883072fb6Sclaudio 	TAILQ_FOREACH(rs, &rtrs, entry) {
1529c0c9c169Sclaudio 		RB_FOREACH(aspa, aspa_tree, &rs->aspa)
153083072fb6Sclaudio 			rtr_aspa_insert(at, aspa);
1531bd9df44eSclaudio 	}
1532bd9df44eSclaudio }
1533bd9df44eSclaudio 
1534bd9df44eSclaudio void
1535bd9df44eSclaudio rtr_shutdown(void)
1536bd9df44eSclaudio {
1537bd9df44eSclaudio 	struct rtr_session *rs, *nrs;
1538bd9df44eSclaudio 
1539bd9df44eSclaudio 	TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs)
1540bd9df44eSclaudio 		rtr_free(rs);
1541bd9df44eSclaudio }
1542bd9df44eSclaudio 
1543bd9df44eSclaudio void
1544bd9df44eSclaudio rtr_show(struct rtr_session *rs, pid_t pid)
1545bd9df44eSclaudio {
1546bd9df44eSclaudio 	struct ctl_show_rtr msg;
1547bd9df44eSclaudio 	struct ctl_timer ct;
1548bd9df44eSclaudio 	u_int i;
1549bd9df44eSclaudio 	time_t d;
1550bd9df44eSclaudio 
1551bd9df44eSclaudio 	memset(&msg, 0, sizeof(msg));
1552bd9df44eSclaudio 
1553bd9df44eSclaudio 	/* descr, remote_addr, local_addr and remote_port set by parent */
155483072fb6Sclaudio 	msg.version = rs->version;
1555d87cfbccSclaudio 	msg.min_version = rs->min_version;
1556bd9df44eSclaudio 	msg.serial = rs->serial;
1557bd9df44eSclaudio 	msg.refresh = rs->refresh;
1558bd9df44eSclaudio 	msg.retry = rs->retry;
1559bd9df44eSclaudio 	msg.expire = rs->expire;
1560bd9df44eSclaudio 	msg.session_id = rs->session_id;
1561bd9df44eSclaudio 	msg.last_sent_error = rs->last_sent_error;
1562bd9df44eSclaudio 	msg.last_recv_error = rs->last_recv_error;
156333c73471Sclaudio 	strlcpy(msg.state, rtr_statenames[rs->state], sizeof(msg.state));
1564bd9df44eSclaudio 	strlcpy(msg.last_sent_msg, rs->last_sent_msg,
1565bd9df44eSclaudio 	    sizeof(msg.last_sent_msg));
1566bd9df44eSclaudio 	strlcpy(msg.last_recv_msg, rs->last_recv_msg,
1567bd9df44eSclaudio 	    sizeof(msg.last_recv_msg));
1568bd9df44eSclaudio 
1569bd9df44eSclaudio 	/* send back imsg */
1570bd9df44eSclaudio 	rtr_imsg_compose(IMSG_CTL_SHOW_RTR, rs->id, pid, &msg, sizeof(msg));
1571bd9df44eSclaudio 
1572bd9df44eSclaudio 	/* send back timer imsgs */
1573bd9df44eSclaudio 	for (i = 1; i < Timer_Max; i++) {
1574bd9df44eSclaudio 		if (!timer_running(&rs->timers, i, &d))
1575bd9df44eSclaudio 			continue;
1576bd9df44eSclaudio 		ct.type = i;
1577bd9df44eSclaudio 		ct.val = d;
1578bd9df44eSclaudio 		rtr_imsg_compose(IMSG_CTL_SHOW_TIMER, rs->id, pid,
1579bd9df44eSclaudio 		    &ct, sizeof(ct));
1580bd9df44eSclaudio 	}
1581bd9df44eSclaudio }
1582