xref: /openbsd-src/usr.sbin/ldpd/packet.c (revision 3bba10cf949623023c76c1ca00cd0c4e229940f0)
1*3bba10cfSclaudio /*	$OpenBSD: packet.c,v 1.77 2024/11/21 13:29:28 claudio Exp $ */
2ab0c2486Smichele 
3ab0c2486Smichele /*
45dc9330aSrenato  * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
5ab0c2486Smichele  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
6ab0c2486Smichele  * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
7ab0c2486Smichele  *
8ab0c2486Smichele  * Permission to use, copy, modify, and distribute this software for any
9ab0c2486Smichele  * purpose with or without fee is hereby granted, provided that the above
10ab0c2486Smichele  * copyright notice and this permission notice appear in all copies.
11ab0c2486Smichele  *
12ab0c2486Smichele  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13ab0c2486Smichele  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14ab0c2486Smichele  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15ab0c2486Smichele  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16ab0c2486Smichele  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17ab0c2486Smichele  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18ab0c2486Smichele  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19ab0c2486Smichele  */
20ab0c2486Smichele 
21ab0c2486Smichele #include <sys/types.h>
22ab0c2486Smichele #include <netinet/in.h>
23627846caSrenato #include <netinet/tcp.h>
24ab0c2486Smichele #include <arpa/inet.h>
25ab0c2486Smichele #include <net/if_dl.h>
26ab0c2486Smichele #include <unistd.h>
27ab0c2486Smichele #include <errno.h>
28ab0c2486Smichele #include <stdlib.h>
29ab0c2486Smichele #include <string.h>
30ab0c2486Smichele 
31ab0c2486Smichele #include "ldpd.h"
32ab0c2486Smichele #include "ldpe.h"
335411bbb6Srenato #include "log.h"
34ab0c2486Smichele 
35c28a25a1Srenato static struct iface		*disc_find_iface(unsigned int, int,
36c28a25a1Srenato 				    union ldpd_addr *, int);
37c28a25a1Srenato static void			 session_read(int, short, void *);
38c28a25a1Srenato static void			 session_write(int, short, void *);
3926889e1dSclaudio static ssize_t			 session_get_pdu(struct buf_read *, char **);
40c28a25a1Srenato static void			 tcp_close(struct tcp_conn *);
41c28a25a1Srenato static struct pending_conn	*pending_conn_new(int, int, union ldpd_addr *);
42c28a25a1Srenato static void			 pending_conn_timeout(int, short, void *);
43ab0c2486Smichele 
444ff910d1Sclaudio static u_int8_t	*recv_buf;
454ff910d1Sclaudio 
46ab0c2486Smichele int
473de94509Srenato gen_ldp_hdr(struct ibuf *buf, uint16_t size)
48ab0c2486Smichele {
49ab0c2486Smichele 	struct ldp_hdr	ldp_hdr;
50ab0c2486Smichele 
513de94509Srenato 	memset(&ldp_hdr, 0, sizeof(ldp_hdr));
52ab0c2486Smichele 	ldp_hdr.version = htons(LDP_VERSION);
539277622bSrenato 	/* exclude the 'Version' and 'PDU Length' fields from the total */
549277622bSrenato 	ldp_hdr.length = htons(size - LDP_HDR_DEAD_LEN);
5586408800Srenato 	ldp_hdr.lsr_id = leconf->rtr_id.s_addr;
56122f143eSclaudio 	ldp_hdr.lspace_id = 0;
57ab0c2486Smichele 
58e39620e5Snicm 	return (ibuf_add(buf, &ldp_hdr, LDP_HDR_SIZE));
59ab0c2486Smichele }
60ab0c2486Smichele 
61ab0c2486Smichele int
62c31aa80eSrenato gen_msg_hdr(struct ibuf *buf, uint16_t type, uint16_t size)
63ab0c2486Smichele {
64c28a25a1Srenato 	static int	msgcnt = 0;
65ab0c2486Smichele 	struct ldp_msg	msg;
66ab0c2486Smichele 
673de94509Srenato 	memset(&msg, 0, sizeof(msg));
68ab0c2486Smichele 	msg.type = htons(type);
693de94509Srenato 	/* exclude the 'Type' and 'Length' fields from the total */
703de94509Srenato 	msg.length = htons(size - LDP_MSG_DEAD_LEN);
7160e1e0e7Srenato 	msg.id = htonl(++msgcnt);
72ab0c2486Smichele 
73e39620e5Snicm 	return (ibuf_add(buf, &msg, sizeof(msg)));
74ab0c2486Smichele }
75ab0c2486Smichele 
7683dcf737Sclaudio /* send packets */
77ab0c2486Smichele int
78a8c39dc0Srenato send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia,
79a8c39dc0Srenato     void *pkt, size_t len)
80ab0c2486Smichele {
81a8c39dc0Srenato 	struct sockaddr		*sa;
82a8c39dc0Srenato 
83a8c39dc0Srenato 	switch (af) {
84a8c39dc0Srenato 	case AF_INET:
85a8c39dc0Srenato 		if (ia && IN_MULTICAST(ntohl(dst->v4.s_addr))) {
86ab0c2486Smichele 			/* set outgoing interface for multicast traffic */
87a8c39dc0Srenato 			if (sock_set_ipv4_mcast(ia->iface) == -1) {
88a8c39dc0Srenato 				log_debug("%s: error setting multicast "
89a8c39dc0Srenato 				    "interface, %s", __func__, ia->iface->name);
90ab0c2486Smichele 				return (-1);
91ab0c2486Smichele 			}
92a8c39dc0Srenato 		}
93a8c39dc0Srenato 		break;
94a8c39dc0Srenato 	case AF_INET6:
95a8c39dc0Srenato 		if (ia && IN6_IS_ADDR_MULTICAST(&dst->v6)) {
96a8c39dc0Srenato 			/* set outgoing interface for multicast traffic */
97a8c39dc0Srenato 			if (sock_set_ipv6_mcast(ia->iface) == -1) {
98a8c39dc0Srenato 				log_debug("%s: error setting multicast "
99a8c39dc0Srenato 				    "interface, %s", __func__, ia->iface->name);
100a8c39dc0Srenato 				return (-1);
101a8c39dc0Srenato 			}
102a8c39dc0Srenato 		}
103a8c39dc0Srenato 		break;
104a8c39dc0Srenato 	default:
105a8c39dc0Srenato 		fatalx("send_packet: unknown af");
106a8c39dc0Srenato 	}
107ab0c2486Smichele 
108a8c39dc0Srenato 	sa = addr2sa(af, dst, LDP_PORT);
109a8c39dc0Srenato 	if (sendto(fd, pkt, len, 0, sa, sa->sa_len) == -1) {
110b7b4db73Srenato 		log_warn("%s: error sending packet to %s", __func__,
111a8c39dc0Srenato 		    log_sockaddr(sa));
112ab0c2486Smichele 		return (-1);
113ab0c2486Smichele 	}
114ab0c2486Smichele 
115ab0c2486Smichele 	return (0);
116ab0c2486Smichele }
117ab0c2486Smichele 
118ab0c2486Smichele /* Discovery functions */
119a8c39dc0Srenato #define CMSG_MAXLEN max(sizeof(struct sockaddr_dl), sizeof(struct in6_pktinfo))
120ab0c2486Smichele void
121ab0c2486Smichele disc_recv_packet(int fd, short event, void *bula)
122ab0c2486Smichele {
123ab0c2486Smichele 	union {
124ab0c2486Smichele 		struct	cmsghdr hdr;
125a8c39dc0Srenato 		char	buf[CMSG_SPACE(CMSG_MAXLEN)];
126ab0c2486Smichele 	} cmsgbuf;
12760e1e0e7Srenato 	struct msghdr		 m;
128a8c39dc0Srenato 	struct sockaddr_storage	 from;
129ab0c2486Smichele 	struct iovec		 iov;
130ab0c2486Smichele 	char			*buf;
131ab0c2486Smichele 	struct cmsghdr		*cmsg;
132ab0c2486Smichele 	ssize_t			 r;
1333de94509Srenato 	int			 multicast;
134a8c39dc0Srenato 	int			 af;
135a8c39dc0Srenato 	union ldpd_addr		 src;
136ab0c2486Smichele 	unsigned int		 ifindex = 0;
1373de94509Srenato 	struct iface		*iface;
1383de94509Srenato 	uint16_t		 len;
1393de94509Srenato 	struct ldp_hdr		 ldp_hdr;
1403de94509Srenato 	uint16_t		 pdu_len;
14160e1e0e7Srenato 	struct ldp_msg		 msg;
1423de94509Srenato 	uint16_t		 msg_len;
1433de94509Srenato 	struct in_addr		 lsr_id;
144ab0c2486Smichele 
145ab0c2486Smichele 	if (event != EV_READ)
146ab0c2486Smichele 		return;
147ab0c2486Smichele 
1484ff910d1Sclaudio 	if (recv_buf == NULL)
1494ff910d1Sclaudio 		if ((recv_buf = malloc(READ_BUF_SIZE)) == NULL)
1504ff910d1Sclaudio 			fatal(__func__);
1514ff910d1Sclaudio 
152ab0c2486Smichele 	/* setup buffer */
15360e1e0e7Srenato 	memset(&m, 0, sizeof(m));
1544ff910d1Sclaudio 	iov.iov_base = buf = recv_buf;
1554ff910d1Sclaudio 	iov.iov_len = READ_BUF_SIZE;
15660e1e0e7Srenato 	m.msg_name = &from;
15760e1e0e7Srenato 	m.msg_namelen = sizeof(from);
15860e1e0e7Srenato 	m.msg_iov = &iov;
15960e1e0e7Srenato 	m.msg_iovlen = 1;
16060e1e0e7Srenato 	m.msg_control = &cmsgbuf.buf;
16160e1e0e7Srenato 	m.msg_controllen = sizeof(cmsgbuf.buf);
162ab0c2486Smichele 
16360e1e0e7Srenato 	if ((r = recvmsg(fd, &m, 0)) == -1) {
164ab0c2486Smichele 		if (errno != EAGAIN && errno != EINTR)
165b7b4db73Srenato 			log_debug("%s: read error: %s", __func__,
166ab0c2486Smichele 			    strerror(errno));
167ab0c2486Smichele 		return;
168ab0c2486Smichele 	}
169ac5a809bSrenato 
17060e1e0e7Srenato 	multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0;
171a8c39dc0Srenato 	sa2addr((struct sockaddr *)&from, &af, &src);
172a8c39dc0Srenato 	if (bad_addr(af, &src)) {
173ac5a809bSrenato 		log_debug("%s: invalid source address: %s", __func__,
174a8c39dc0Srenato 		    log_addr(af, &src));
175ac5a809bSrenato 		return;
176ac5a809bSrenato 	}
177ac5a809bSrenato 
17860e1e0e7Srenato 	for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL;
17960e1e0e7Srenato 	    cmsg = CMSG_NXTHDR(&m, cmsg)) {
180a8c39dc0Srenato 		if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP &&
181ab0c2486Smichele 		    cmsg->cmsg_type == IP_RECVIF) {
182ab0c2486Smichele 			ifindex = ((struct sockaddr_dl *)
183ab0c2486Smichele 			    CMSG_DATA(cmsg))->sdl_index;
184ab0c2486Smichele 			break;
185ab0c2486Smichele 		}
186a8c39dc0Srenato 		if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 &&
187a8c39dc0Srenato 		    cmsg->cmsg_type == IPV6_PKTINFO) {
188a8c39dc0Srenato 			ifindex = ((struct in6_pktinfo *)
189a8c39dc0Srenato 			    CMSG_DATA(cmsg))->ipi6_ifindex;
190a8c39dc0Srenato 			break;
191a8c39dc0Srenato 		}
192ab0c2486Smichele 	}
193ab0c2486Smichele 
194ab0c2486Smichele 	/* find a matching interface */
195a8c39dc0Srenato 	iface = disc_find_iface(ifindex, af, &src, multicast);
1963de94509Srenato 	if (iface == NULL)
1973de94509Srenato 		return;
1983de94509Srenato 
1993de94509Srenato 	/* check packet size */
2003de94509Srenato 	len = (uint16_t)r;
2013de94509Srenato 	if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) {
2023de94509Srenato 		log_debug("%s: bad packet size, source %s", __func__,
203a8c39dc0Srenato 		    log_addr(af, &src));
204ab0c2486Smichele 		return;
205ab0c2486Smichele 	}
206ab0c2486Smichele 
207ab0c2486Smichele 	/* LDP header sanity checks */
2083de94509Srenato 	memcpy(&ldp_hdr, buf, sizeof(ldp_hdr));
20989f23408Sclaudio 	if (ntohs(ldp_hdr.version) != LDP_VERSION) {
2103de94509Srenato 		log_debug("%s: invalid LDP version %d, source %s", __func__,
211a8c39dc0Srenato 		    ntohs(ldp_hdr.version), log_addr(af, &src));
212ab0c2486Smichele 		return;
213ab0c2486Smichele 	}
2143de94509Srenato 	if (ntohs(ldp_hdr.lspace_id) != 0) {
2153de94509Srenato 		log_debug("%s: invalid label space %u, source %s", __func__,
216a8c39dc0Srenato 		    ntohs(ldp_hdr.lspace_id), log_addr(af, &src));
217ab0c2486Smichele 		return;
218ab0c2486Smichele 	}
2193de94509Srenato 	/* check "PDU Length" field */
2203de94509Srenato 	pdu_len = ntohs(ldp_hdr.length);
2213de94509Srenato 	if ((pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE)) ||
2223de94509Srenato 	    (pdu_len > (len - LDP_HDR_DEAD_LEN))) {
2233de94509Srenato 		log_debug("%s: invalid LDP packet length %u, source %s",
224a8c39dc0Srenato 		    __func__, ntohs(ldp_hdr.length), log_addr(af, &src));
225ab0c2486Smichele 		return;
226ab0c2486Smichele 	}
2273de94509Srenato 	buf += LDP_HDR_SIZE;
2283de94509Srenato 	len -= LDP_HDR_SIZE;
229ab0c2486Smichele 
2303de94509Srenato 	lsr_id.s_addr = ldp_hdr.lsr_id;
2313de94509Srenato 
2323de94509Srenato 	/*
2333de94509Srenato 	 * For UDP, we process only the first message of each packet. This does
2343de94509Srenato 	 * not impose any restrictions since LDP uses UDP only for sending Hello
2353de94509Srenato 	 * packets.
2363de94509Srenato 	 */
23760e1e0e7Srenato 	memcpy(&msg, buf, sizeof(msg));
2383de94509Srenato 
2393de94509Srenato 	/* check "Message Length" field */
24060e1e0e7Srenato 	msg_len = ntohs(msg.length);
2413de94509Srenato 	if (msg_len < LDP_MSG_LEN || ((msg_len + LDP_MSG_DEAD_LEN) > pdu_len)) {
2423de94509Srenato 		log_debug("%s: invalid LDP message length %u, source %s",
24360e1e0e7Srenato 		    __func__, ntohs(msg.length), log_addr(af, &src));
2443de94509Srenato 		return;
2453de94509Srenato 	}
2463de94509Srenato 	buf += LDP_MSG_SIZE;
2473de94509Srenato 	len -= LDP_MSG_SIZE;
24889f23408Sclaudio 
249ab0c2486Smichele 	/* switch LDP packet type */
25060e1e0e7Srenato 	switch (ntohs(msg.type)) {
251ab0c2486Smichele 	case MSG_TYPE_HELLO:
25260e1e0e7Srenato 		recv_hello(lsr_id, &msg, af, &src, iface, multicast, buf, len);
253ab0c2486Smichele 		break;
254ab0c2486Smichele 	default:
255b7b4db73Srenato 		log_debug("%s: unknown LDP packet type, source %s", __func__,
256a8c39dc0Srenato 		    log_addr(af, &src));
257ab0c2486Smichele 	}
258ab0c2486Smichele }
259ab0c2486Smichele 
260c28a25a1Srenato static struct iface *
261a8c39dc0Srenato disc_find_iface(unsigned int ifindex, int af, union ldpd_addr *src,
262ac5a809bSrenato     int multicast)
263ab0c2486Smichele {
264814e607dSclaudio 	struct iface	*iface;
265a8c39dc0Srenato 	struct iface_af	*ia;
266814e607dSclaudio 	struct if_addr	*if_addr;
267a8c39dc0Srenato 	in_addr_t	 mask;
268ab0c2486Smichele 
269ac5a809bSrenato 	iface = if_lookup(leconf, ifindex);
270ac5a809bSrenato 	if (iface == NULL)
271ac5a809bSrenato 		return (NULL);
272ac5a809bSrenato 
273a8c39dc0Srenato 	/*
274a8c39dc0Srenato 	 * For unicast packets, we just need to make sure that the interface
275a8c39dc0Srenato 	 * is enabled for the given address-family.
276a8c39dc0Srenato 	 */
277a8c39dc0Srenato 	if (!multicast) {
278a8c39dc0Srenato 		ia = iface_af_get(iface, af);
279a8c39dc0Srenato 		if (ia->enabled)
280ac5a809bSrenato 			return (iface);
281a8c39dc0Srenato 		return (NULL);
282a8c39dc0Srenato 	}
283ac5a809bSrenato 
284a8c39dc0Srenato 	switch (af) {
285a8c39dc0Srenato 	case AF_INET:
286ac5a809bSrenato 		LIST_FOREACH(if_addr, &iface->addr_list, entry) {
287a8c39dc0Srenato 			if (if_addr->af != AF_INET)
288a8c39dc0Srenato 				continue;
289a8c39dc0Srenato 
290ab0c2486Smichele 			switch (iface->type) {
291ab0c2486Smichele 			case IF_TYPE_POINTOPOINT:
292a8c39dc0Srenato 				if (if_addr->dstbrd.v4.s_addr == src->v4.s_addr)
293ab0c2486Smichele 					return (iface);
294ab0c2486Smichele 				break;
295ab0c2486Smichele 			default:
296a8c39dc0Srenato 				mask = prefixlen2mask(if_addr->prefixlen);
297a8c39dc0Srenato 				if ((if_addr->addr.v4.s_addr & mask) ==
298a8c39dc0Srenato 				    (src->v4.s_addr & mask))
299ab0c2486Smichele 					return (iface);
300ab0c2486Smichele 				break;
301ab0c2486Smichele 			}
302ac5a809bSrenato 		}
303a8c39dc0Srenato 		break;
304a8c39dc0Srenato 	case AF_INET6:
305a8c39dc0Srenato 		if (IN6_IS_ADDR_LINKLOCAL(&src->v6))
306a8c39dc0Srenato 			return (iface);
307a8c39dc0Srenato 		break;
308a8c39dc0Srenato 	default:
309a8c39dc0Srenato 		fatalx("disc_find_iface: unknown af");
310a8c39dc0Srenato 	}
311ab0c2486Smichele 
312ab0c2486Smichele 	return (NULL);
313ab0c2486Smichele }
314ab0c2486Smichele 
315ab0c2486Smichele void
3162196e980Sclaudio session_accept(int fd, short event, void *bula)
317ab0c2486Smichele {
318a8c39dc0Srenato 	struct sockaddr_storage	 src;
319dbadea95Smichele 	socklen_t		 len = sizeof(src);
32035ee709dSrenato 	int			 newfd;
321a8c39dc0Srenato 	int			 af;
322a8c39dc0Srenato 	union ldpd_addr		 addr;
32335ee709dSrenato 	struct nbr		*nbr;
32435ee709dSrenato 	struct pending_conn	*pconn;
325ab0c2486Smichele 
3264dda87d0Sclaudio 	if (!(event & EV_READ))
327ab0c2486Smichele 		return;
328ab0c2486Smichele 
3295f3d6e22Sclaudio 	newfd = accept4(fd, (struct sockaddr *)&src, &len,
3305f3d6e22Sclaudio 	    SOCK_NONBLOCK | SOCK_CLOEXEC);
331ab0c2486Smichele 	if (newfd == -1) {
3324dda87d0Sclaudio 		/*
3334dda87d0Sclaudio 		 * Pause accept if we are out of file descriptors, or
3344dda87d0Sclaudio 		 * libevent will haunt us here too.
3354dda87d0Sclaudio 		 */
3364dda87d0Sclaudio 		if (errno == ENFILE || errno == EMFILE) {
3374dda87d0Sclaudio 			accept_pause();
33862e3c252Sderaadt 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
33962e3c252Sderaadt 		    errno != ECONNABORTED)
340b7b4db73Srenato 			log_debug("%s: accept error: %s", __func__,
341ab0c2486Smichele 			    strerror(errno));
342ab0c2486Smichele 		return;
343ab0c2486Smichele 	}
344ab0c2486Smichele 
345a8c39dc0Srenato 	sa2addr((struct sockaddr *)&src, &af, &addr);
346a8c39dc0Srenato 
34735ee709dSrenato 	/*
34835ee709dSrenato 	 * Since we don't support label spaces, we can identify this neighbor
34935ee709dSrenato 	 * just by its source address. This way we don't need to wait for its
35035ee709dSrenato 	 * Initialization message to know who we are talking to.
35135ee709dSrenato 	 */
352a8c39dc0Srenato 	nbr = nbr_find_addr(af, &addr);
35335ee709dSrenato 	if (nbr == NULL) {
35435ee709dSrenato 		/*
35535ee709dSrenato 		 * According to RFC 5036, we would need to send a No Hello
35635ee709dSrenato 		 * Error Notification message and close this TCP connection
35735ee709dSrenato 		 * right now. But doing so would trigger the backoff exponential
35835ee709dSrenato 		 * timer in the remote peer, which would considerably slow down
35935ee709dSrenato 		 * the session establishment process. The trick here is to wait
36035ee709dSrenato 		 * five seconds before sending the Notification Message. There's
36135ee709dSrenato 		 * a good chance that the remote peer will send us a Hello
36235ee709dSrenato 		 * message within this interval, so it's worth waiting before
36335ee709dSrenato 		 * taking a more drastic measure.
36435ee709dSrenato 		 */
365a8c39dc0Srenato 		pconn = pending_conn_find(af, &addr);
36635ee709dSrenato 		if (pconn)
36735ee709dSrenato 			close(newfd);
36835ee709dSrenato 		else
369a8c39dc0Srenato 			pending_conn_new(newfd, af, &addr);
37035ee709dSrenato 		return;
37135ee709dSrenato 	}
37235ee709dSrenato 	/* protection against buggy implementations */
37335ee709dSrenato 	if (nbr_session_active_role(nbr)) {
37435ee709dSrenato 		close(newfd);
37535ee709dSrenato 		return;
37635ee709dSrenato 	}
37735ee709dSrenato 	if (nbr->state != NBR_STA_PRESENT) {
37835ee709dSrenato 		log_debug("%s: lsr-id %s: rejecting additional transport "
37935ee709dSrenato 		    "connection", __func__, inet_ntoa(nbr->id));
38035ee709dSrenato 		close(newfd);
38135ee709dSrenato 		return;
38235ee709dSrenato 	}
38335ee709dSrenato 
38435ee709dSrenato 	session_accept_nbr(nbr, newfd);
38535ee709dSrenato }
38635ee709dSrenato 
38735ee709dSrenato void
38835ee709dSrenato session_accept_nbr(struct nbr *nbr, int fd)
38935ee709dSrenato {
39035ee709dSrenato 	struct nbr_params	*nbrp;
39135ee709dSrenato 	int			 opt;
39235ee709dSrenato 	socklen_t		 len;
39335ee709dSrenato 
3941ce5acabSrenato 	nbrp = nbr_params_find(leconf, nbr->id);
3955ff72af8Srenato 	if (nbr_gtsm_check(fd, nbr, nbrp)) {
3965ff72af8Srenato 		close(fd);
3975ff72af8Srenato 		return;
3985ff72af8Srenato 	}
3995ff72af8Srenato 
4007ee91690Sdlg 	if (!LIST_EMPTY(&leconf->auth_list)) {
401627846caSrenato 		if (sysdep.no_pfkey || sysdep.no_md5sig) {
402627846caSrenato 			log_warnx("md5sig configured but not available");
40335ee709dSrenato 			close(fd);
404627846caSrenato 			return;
405627846caSrenato 		}
406627846caSrenato 
407627846caSrenato 		len = sizeof(opt);
40835ee709dSrenato 		if (getsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, &len) == -1)
409627846caSrenato 			fatal("getsockopt TCP_MD5SIG");
410627846caSrenato 		if (!opt) {	/* non-md5'd connection! */
41135ee709dSrenato 			log_warnx("connection attempt without md5 signature");
41235ee709dSrenato 			close(fd);
413627846caSrenato 			return;
414627846caSrenato 		}
415627846caSrenato 	}
416627846caSrenato 
41735ee709dSrenato 	nbr->tcp = tcp_new(fd, nbr);
41835ee709dSrenato 	nbr_fsm(nbr, NBR_EVT_MATCH_ADJ);
419ab0c2486Smichele }
420ab0c2486Smichele 
421c28a25a1Srenato static void
4222196e980Sclaudio session_read(int fd, short event, void *arg)
423ab0c2486Smichele {
42435ee709dSrenato 	struct nbr	*nbr = arg;
42535ee709dSrenato 	struct tcp_conn	*tcp = nbr->tcp;
426ab0c2486Smichele 	struct ldp_hdr	*ldp_hdr;
42760e1e0e7Srenato 	struct ldp_msg	*msg;
4282196e980Sclaudio 	char		*buf, *pdu;
4292196e980Sclaudio 	ssize_t		 n, len;
4303de94509Srenato 	uint16_t	 pdu_len, msg_len, msg_size, max_pdu_len;
4313de94509Srenato 	int		 ret;
432ab0c2486Smichele 
43335ee709dSrenato 	if (event != EV_READ)
4342196e980Sclaudio 		return;
435ab0c2486Smichele 
436699b7d06Sclaudio 	if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos,
437699b7d06Sclaudio 	    sizeof(tcp->rbuf->buf) - tcp->rbuf->wpos)) == -1) {
4382196e980Sclaudio 		if (errno != EINTR && errno != EAGAIN) {
439b7b4db73Srenato 			log_warn("%s: read error", __func__);
44053e24948Sclaudio 			nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
4412196e980Sclaudio 			return;
4422196e980Sclaudio 		}
4432196e980Sclaudio 		/* retry read */
4442196e980Sclaudio 		return;
4452196e980Sclaudio 	}
4462196e980Sclaudio 	if (n == 0) {
4472196e980Sclaudio 		/* connection closed */
448b7b4db73Srenato 		log_debug("%s: connection closed by remote end", __func__);
44953e24948Sclaudio 		nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
4502196e980Sclaudio 		return;
4512196e980Sclaudio 	}
452699b7d06Sclaudio 	tcp->rbuf->wpos += n;
453ab0c2486Smichele 
454699b7d06Sclaudio 	while ((len = session_get_pdu(tcp->rbuf, &buf)) > 0) {
455132e53f0Sjsg 		pdu = buf;
456132e53f0Sjsg 		ldp_hdr = (struct ldp_hdr *)pdu;
457ab0c2486Smichele 		if (ntohs(ldp_hdr->version) != LDP_VERSION) {
4587d508fe8Smichele 			session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0);
4592196e980Sclaudio 			free(buf);
460ab0c2486Smichele 			return;
461ab0c2486Smichele 		}
462ab0c2486Smichele 
463ab0c2486Smichele 		pdu_len = ntohs(ldp_hdr->length);
464feeedd8aSrenato 		/*
465feeedd8aSrenato 	 	 * RFC 5036 - Section 3.5.3:
466feeedd8aSrenato 		 * "Prior to completion of the negotiation, the maximum
467feeedd8aSrenato 		 * allowable length is 4096 bytes".
468feeedd8aSrenato 		 */
46935ee709dSrenato 		if (nbr->state == NBR_STA_OPER)
470feeedd8aSrenato 			max_pdu_len = nbr->max_pdu_len;
471feeedd8aSrenato 		else
472feeedd8aSrenato 			max_pdu_len = LDP_MAX_LEN;
4739277622bSrenato 		if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) ||
474feeedd8aSrenato 		    pdu_len > max_pdu_len) {
47535791d36Srenato 			session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
4762196e980Sclaudio 			free(buf);
477ab0c2486Smichele 			return;
478ab0c2486Smichele 		}
4799277622bSrenato 		pdu_len -= LDP_HDR_PDU_LEN;
48053e24948Sclaudio 		if (ldp_hdr->lsr_id != nbr->id.s_addr ||
48153e24948Sclaudio 		    ldp_hdr->lspace_id != 0) {
4822196e980Sclaudio 			session_shutdown(nbr, S_BAD_LDP_ID, 0, 0);
4832196e980Sclaudio 			free(buf);
484ab0c2486Smichele 			return;
4852196e980Sclaudio 		}
4862196e980Sclaudio 		pdu += LDP_HDR_SIZE;
487ab0c2486Smichele 		len -= LDP_HDR_SIZE;
488ab0c2486Smichele 
48923695a60Srenato 		nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
49023695a60Srenato 
4913de94509Srenato 		while (len >= LDP_MSG_SIZE) {
4923de94509Srenato 			uint16_t type;
493a9ad3d7dSmiod 
49460e1e0e7Srenato 			msg = (struct ldp_msg *)pdu;
49560e1e0e7Srenato 			type = ntohs(msg->type);
49660e1e0e7Srenato 			msg_len = ntohs(msg->length);
497c31aa80eSrenato 			if (msg_len < LDP_MSG_LEN ||
498c31aa80eSrenato 			    (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) {
49960e1e0e7Srenato 				session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
50060e1e0e7Srenato 				    msg->type);
5012196e980Sclaudio 				free(buf);
5022196e980Sclaudio 				return;
5032196e980Sclaudio 			}
504c31aa80eSrenato 			msg_size = msg_len + LDP_MSG_DEAD_LEN;
5053de94509Srenato 			pdu_len -= msg_size;
506ab0c2486Smichele 
50753e24948Sclaudio 			/* check for error conditions earlier */
50853e24948Sclaudio 			switch (type) {
50953e24948Sclaudio 			case MSG_TYPE_INIT:
51053e24948Sclaudio 				if ((nbr->state != NBR_STA_INITIAL) &&
51153e24948Sclaudio 				    (nbr->state != NBR_STA_OPENSENT)) {
51253e24948Sclaudio 					session_shutdown(nbr, S_SHUTDOWN,
51360e1e0e7Srenato 					    msg->id, msg->type);
51453e24948Sclaudio 					free(buf);
51553e24948Sclaudio 					return;
51653e24948Sclaudio 				}
51753e24948Sclaudio 				break;
51853e24948Sclaudio 			case MSG_TYPE_KEEPALIVE:
51953e24948Sclaudio 				if ((nbr->state == NBR_STA_INITIAL) ||
52053e24948Sclaudio 				    (nbr->state == NBR_STA_OPENSENT)) {
52153e24948Sclaudio 					session_shutdown(nbr, S_SHUTDOWN,
52260e1e0e7Srenato 					    msg->id, msg->type);
52353e24948Sclaudio 					free(buf);
52453e24948Sclaudio 					return;
52553e24948Sclaudio 				}
52653e24948Sclaudio 				break;
5273b4c1866Srenato 			default:
52853e24948Sclaudio 				if (nbr->state != NBR_STA_OPER) {
52953e24948Sclaudio 					session_shutdown(nbr, S_SHUTDOWN,
53060e1e0e7Srenato 					    msg->id, msg->type);
53153e24948Sclaudio 					free(buf);
53253e24948Sclaudio 					return;
53353e24948Sclaudio 				}
53453e24948Sclaudio 				break;
53553e24948Sclaudio 			}
53653e24948Sclaudio 
537ab0c2486Smichele 			/* switch LDP packet type */
53853e24948Sclaudio 			switch (type) {
539ab0c2486Smichele 			case MSG_TYPE_NOTIFICATION:
5409277622bSrenato 				ret = recv_notification(nbr, pdu, msg_size);
541ab0c2486Smichele 				break;
542ab0c2486Smichele 			case MSG_TYPE_INIT:
5439277622bSrenato 				ret = recv_init(nbr, pdu, msg_size);
544ab0c2486Smichele 				break;
545ab0c2486Smichele 			case MSG_TYPE_KEEPALIVE:
5469277622bSrenato 				ret = recv_keepalive(nbr, pdu, msg_size);
547ab0c2486Smichele 				break;
5483b4c1866Srenato 			case MSG_TYPE_CAPABILITY:
5493b4c1866Srenato 				ret = recv_capability(nbr, pdu, msg_size);
5503b4c1866Srenato 				break;
551ab0c2486Smichele 			case MSG_TYPE_ADDR:
552ab0c2486Smichele 			case MSG_TYPE_ADDRWITHDRAW:
5539277622bSrenato 				ret = recv_address(nbr, pdu, msg_size);
554ab0c2486Smichele 				break;
555ab0c2486Smichele 			case MSG_TYPE_LABELMAPPING:
556ab0c2486Smichele 			case MSG_TYPE_LABELREQUEST:
557ab0c2486Smichele 			case MSG_TYPE_LABELWITHDRAW:
558ab0c2486Smichele 			case MSG_TYPE_LABELRELEASE:
559ab0c2486Smichele 			case MSG_TYPE_LABELABORTREQ:
5609277622bSrenato 				ret = recv_labelmessage(nbr, pdu, msg_size,
5619277622bSrenato 				    type);
562cf650e8bSrenato 				break;
563ab0c2486Smichele 			default:
56405822608Srenato 				log_debug("%s: unknown LDP message from nbr %s",
565b7b4db73Srenato 				    __func__, inet_ntoa(nbr->id));
56660e1e0e7Srenato 				if (!(ntohs(msg->type) & UNKNOWN_FLAG))
5670101edf8Srenato 					send_notification(nbr->tcp,
56860e1e0e7Srenato 					    S_UNKNOWN_MSG, msg->id, msg->type);
56905822608Srenato 				/* ignore the message */
5709277622bSrenato 				ret = 0;
571da0a2a5cSrenato 				break;
572da0a2a5cSrenato 			}
573ab0c2486Smichele 
5749277622bSrenato 			if (ret == -1) {
5752196e980Sclaudio 				/* parser failed, giving up */
5762196e980Sclaudio 				free(buf);
577ab0c2486Smichele 				return;
5782196e980Sclaudio 			}
579ab0c2486Smichele 
580ab0c2486Smichele 			/* Analyse the next message */
5819277622bSrenato 			pdu += msg_size;
5829277622bSrenato 			len -= msg_size;
583ab0c2486Smichele 		}
5842196e980Sclaudio 		free(buf);
5852196e980Sclaudio 		if (len != 0) {
5862196e980Sclaudio 			session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
5872196e980Sclaudio 			return;
5882196e980Sclaudio 		}
5892196e980Sclaudio 	}
590ab0c2486Smichele }
591ab0c2486Smichele 
592c28a25a1Srenato static void
5932196e980Sclaudio session_write(int fd, short event, void *arg)
594ab0c2486Smichele {
595699b7d06Sclaudio 	struct tcp_conn *tcp = arg;
596699b7d06Sclaudio 	struct nbr	*nbr = tcp->nbr;
597ab0c2486Smichele 
59835ee709dSrenato 	if (!(event & EV_WRITE))
59935ee709dSrenato 		return;
60035ee709dSrenato 
601*3bba10cfSclaudio 	if (ibuf_write(fd, tcp->wbuf.wbuf) == -1)
602baf86509Sclaudio 		if (nbr)
603ab0c2486Smichele 			nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
60435ee709dSrenato 
605*3bba10cfSclaudio 	if (nbr == NULL && msgbuf_queuelen(tcp->wbuf.wbuf) == 0) {
60635ee709dSrenato 		/*
60735ee709dSrenato 		 * We are done sending the notification message, now we can
60835ee709dSrenato 		 * close the socket.
60935ee709dSrenato 		 */
61035ee709dSrenato 		tcp_close(tcp);
61135ee709dSrenato 		return;
612699b7d06Sclaudio 	}
6132196e980Sclaudio 
614699b7d06Sclaudio 	evbuf_event_add(&tcp->wbuf);
615ab0c2486Smichele }
616ab0c2486Smichele 
617ab0c2486Smichele void
61860e1e0e7Srenato session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id,
61960e1e0e7Srenato     uint32_t msg_type)
6207d508fe8Smichele {
62107f78510Srenato 	switch (nbr->state) {
62207f78510Srenato 	case NBR_STA_PRESENT:
62307f78510Srenato 		if (nbr_pending_connect(nbr))
62407f78510Srenato 			event_del(&nbr->ev_connect);
62507f78510Srenato 		break;
62607f78510Srenato 	case NBR_STA_INITIAL:
62707f78510Srenato 	case NBR_STA_OPENREC:
62807f78510Srenato 	case NBR_STA_OPENSENT:
62907f78510Srenato 	case NBR_STA_OPER:
630d99a8fc3Srenato 		log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
63147c7016aSclaudio 
6320101edf8Srenato 		send_notification(nbr->tcp, status, msg_id, msg_type);
633deb01a47Sclaudio 
6347d508fe8Smichele 		nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
63507f78510Srenato 		break;
63607f78510Srenato 	default:
63707f78510Srenato 		fatalx("session_shutdown: unknown neighbor state");
63807f78510Srenato 	}
6397d508fe8Smichele }
6407d508fe8Smichele 
6417d508fe8Smichele void
642ab0c2486Smichele session_close(struct nbr *nbr)
643ab0c2486Smichele {
644d99a8fc3Srenato 	log_debug("%s: closing session with lsr-id %s", __func__,
645ab0c2486Smichele 	    inet_ntoa(nbr->id));
646ab0c2486Smichele 
647699b7d06Sclaudio 	tcp_close(nbr->tcp);
648938c679bSclaudio 	nbr_stop_ktimer(nbr);
649938c679bSclaudio 	nbr_stop_ktimeout(nbr);
650c78471fcSrenato 	nbr_stop_itimeout(nbr);
651ab0c2486Smichele }
652ab0c2486Smichele 
653c28a25a1Srenato static ssize_t
65426889e1dSclaudio session_get_pdu(struct buf_read *r, char **b)
6552196e980Sclaudio {
6562196e980Sclaudio 	struct ldp_hdr	l;
6572196e980Sclaudio 	size_t		av, dlen, left;
6582196e980Sclaudio 
6592196e980Sclaudio 	av = r->wpos;
6602196e980Sclaudio 	if (av < sizeof(l))
6612196e980Sclaudio 		return (0);
6622196e980Sclaudio 
6632196e980Sclaudio 	memcpy(&l, r->buf, sizeof(l));
6649277622bSrenato 	dlen = ntohs(l.length) + LDP_HDR_DEAD_LEN;
6652196e980Sclaudio 	if (dlen > av)
6662196e980Sclaudio 		return (0);
6672196e980Sclaudio 
6682196e980Sclaudio 	if ((*b = malloc(dlen)) == NULL)
6692196e980Sclaudio 		return (-1);
6702196e980Sclaudio 
6712196e980Sclaudio 	memcpy(*b, r->buf, dlen);
6722196e980Sclaudio 	if (dlen < av) {
6732196e980Sclaudio 		left = av - dlen;
6742196e980Sclaudio 		memmove(r->buf, r->buf + dlen, left);
6752196e980Sclaudio 		r->wpos = left;
6762196e980Sclaudio 	} else
6772196e980Sclaudio 		r->wpos = 0;
6782196e980Sclaudio 
6792196e980Sclaudio 	return (dlen);
6802196e980Sclaudio }
68133f6ccfeSrenato 
68233f6ccfeSrenato struct tcp_conn *
68333f6ccfeSrenato tcp_new(int fd, struct nbr *nbr)
68433f6ccfeSrenato {
68533f6ccfeSrenato 	struct tcp_conn *tcp;
68633f6ccfeSrenato 
68733f6ccfeSrenato 	if ((tcp = calloc(1, sizeof(*tcp))) == NULL)
68833f6ccfeSrenato 		fatal(__func__);
68933f6ccfeSrenato 
69033f6ccfeSrenato 	tcp->fd = fd;
69133f6ccfeSrenato 	evbuf_init(&tcp->wbuf, tcp->fd, session_write, tcp);
69235ee709dSrenato 
69335ee709dSrenato 	if (nbr) {
69426889e1dSclaudio 		if ((tcp->rbuf = calloc(1, sizeof(struct buf_read))) == NULL)
69535ee709dSrenato 			fatal(__func__);
69635ee709dSrenato 
69735ee709dSrenato 		event_set(&tcp->rev, tcp->fd, EV_READ | EV_PERSIST,
69835ee709dSrenato 		    session_read, nbr);
69933f6ccfeSrenato 		event_add(&tcp->rev, NULL);
70035ee709dSrenato 		tcp->nbr = nbr;
70135ee709dSrenato 	}
70233f6ccfeSrenato 
70333f6ccfeSrenato 	return (tcp);
70433f6ccfeSrenato }
70533f6ccfeSrenato 
706c28a25a1Srenato static void
70733f6ccfeSrenato tcp_close(struct tcp_conn *tcp)
70833f6ccfeSrenato {
709e373a269Srenato 	/* try to flush write buffer */
710*3bba10cfSclaudio 	ibuf_write(tcp->fd, tcp->wbuf.wbuf);
71133f6ccfeSrenato 	evbuf_clear(&tcp->wbuf);
71235ee709dSrenato 
71335ee709dSrenato 	if (tcp->nbr) {
71433f6ccfeSrenato 		event_del(&tcp->rev);
71535ee709dSrenato 		free(tcp->rbuf);
71635ee709dSrenato 		tcp->nbr->tcp = NULL;
71735ee709dSrenato 	}
71835ee709dSrenato 
71933f6ccfeSrenato 	close(tcp->fd);
72033f6ccfeSrenato 	accept_unpause();
72133f6ccfeSrenato 	free(tcp);
72233f6ccfeSrenato }
72335ee709dSrenato 
724c28a25a1Srenato static struct pending_conn *
725a8c39dc0Srenato pending_conn_new(int fd, int af, union ldpd_addr *addr)
72635ee709dSrenato {
72735ee709dSrenato 	struct pending_conn	*pconn;
72835ee709dSrenato 	struct timeval		 tv;
72935ee709dSrenato 
73035ee709dSrenato 	if ((pconn = calloc(1, sizeof(*pconn))) == NULL)
73135ee709dSrenato 		fatal(__func__);
73235ee709dSrenato 
73335ee709dSrenato 	pconn->fd = fd;
734a8c39dc0Srenato 	pconn->af = af;
735a8c39dc0Srenato 	pconn->addr = *addr;
73635ee709dSrenato 	evtimer_set(&pconn->ev_timeout, pending_conn_timeout, pconn);
73735ee709dSrenato 	TAILQ_INSERT_TAIL(&global.pending_conns, pconn, entry);
73835ee709dSrenato 
73935ee709dSrenato 	timerclear(&tv);
74035ee709dSrenato 	tv.tv_sec = PENDING_CONN_TIMEOUT;
74135ee709dSrenato 	if (evtimer_add(&pconn->ev_timeout, &tv) == -1)
74235ee709dSrenato 		fatal(__func__);
74335ee709dSrenato 
74435ee709dSrenato 	return (pconn);
74535ee709dSrenato }
74635ee709dSrenato 
74735ee709dSrenato void
74835ee709dSrenato pending_conn_del(struct pending_conn *pconn)
74935ee709dSrenato {
75035ee709dSrenato 	if (evtimer_pending(&pconn->ev_timeout, NULL) &&
75135ee709dSrenato 	    evtimer_del(&pconn->ev_timeout) == -1)
75235ee709dSrenato 		fatal(__func__);
75335ee709dSrenato 
75435ee709dSrenato 	TAILQ_REMOVE(&global.pending_conns, pconn, entry);
75535ee709dSrenato 	free(pconn);
75635ee709dSrenato }
75735ee709dSrenato 
75835ee709dSrenato struct pending_conn *
759a8c39dc0Srenato pending_conn_find(int af, union ldpd_addr *addr)
76035ee709dSrenato {
76135ee709dSrenato 	struct pending_conn	*pconn;
76235ee709dSrenato 
76335ee709dSrenato 	TAILQ_FOREACH(pconn, &global.pending_conns, entry)
764a8c39dc0Srenato 		if (af == pconn->af &&
765a8c39dc0Srenato 		    ldp_addrcmp(af, addr, &pconn->addr) == 0)
76635ee709dSrenato 			return (pconn);
76735ee709dSrenato 
76835ee709dSrenato 	return (NULL);
76935ee709dSrenato }
77035ee709dSrenato 
771c28a25a1Srenato static void
77235ee709dSrenato pending_conn_timeout(int fd, short event, void *arg)
77335ee709dSrenato {
77435ee709dSrenato 	struct pending_conn	*pconn = arg;
77535ee709dSrenato 	struct tcp_conn		*tcp;
77635ee709dSrenato 
77735ee709dSrenato 	log_debug("%s: no adjacency with remote end: %s", __func__,
778a8c39dc0Srenato 	    log_addr(pconn->af, &pconn->addr));
77935ee709dSrenato 
78035ee709dSrenato 	/*
78135ee709dSrenato 	 * Create a write buffer detached from any neighbor to send a
78235ee709dSrenato 	 * notification message reliably.
78335ee709dSrenato 	 */
78435ee709dSrenato 	tcp = tcp_new(pconn->fd, NULL);
7850101edf8Srenato 	send_notification(tcp, S_NO_HELLO, 0, 0);
786*3bba10cfSclaudio 	ibuf_write(fd, tcp->wbuf.wbuf);
78735ee709dSrenato 
78835ee709dSrenato 	pending_conn_del(pconn);
78935ee709dSrenato }
790