xref: /openbsd-src/usr.sbin/ldpd/packet.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: packet.c,v 1.35 2014/07/12 19:22:32 krw Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
5  * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
23 
24 #include <netinet/in.h>
25 #include <netinet/in_systm.h>
26 #include <netinet/ip.h>
27 #include <arpa/inet.h>
28 #include <net/if_dl.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 
32 #include <errno.h>
33 #include <event.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "ldpd.h"
38 #include "ldp.h"
39 #include "log.h"
40 #include "ldpe.h"
41 
42 extern struct ldpd_conf        *leconf;
43 
44 struct iface	*disc_find_iface(unsigned int, struct in_addr);
45 ssize_t		 session_get_pdu(struct ibuf_read *, char **);
46 
47 static int	 msgcnt = 0;
48 
49 int
50 gen_ldp_hdr(struct ibuf *buf, u_int16_t size)
51 {
52 	struct ldp_hdr	ldp_hdr;
53 
54 	bzero(&ldp_hdr, sizeof(ldp_hdr));
55 	ldp_hdr.version = htons(LDP_VERSION);
56 
57 	/* We want just the size of the value */
58 	size -= TLV_HDR_LEN;
59 
60 	ldp_hdr.length = htons(size);
61 	ldp_hdr.lsr_id = ldpe_router_id();
62 	ldp_hdr.lspace_id = 0;
63 
64 	return (ibuf_add(buf, &ldp_hdr, LDP_HDR_SIZE));
65 }
66 
67 int
68 gen_msg_tlv(struct ibuf *buf, u_int32_t type, u_int16_t size)
69 {
70 	struct ldp_msg	msg;
71 
72 	/* We want just the size of the value */
73 	size -= TLV_HDR_LEN;
74 
75 	bzero(&msg, sizeof(msg));
76 	msg.type = htons(type);
77 	msg.length = htons(size);
78 	if (type != MSG_TYPE_HELLO)
79 		msg.msgid = htonl(++msgcnt);
80 
81 	return (ibuf_add(buf, &msg, sizeof(msg)));
82 }
83 
84 /* send packets */
85 int
86 send_packet(int fd, struct iface *iface, void *pkt, size_t len,
87     struct sockaddr_in *dst)
88 {
89 	/* set outgoing interface for multicast traffic */
90 	if (iface && IN_MULTICAST(ntohl(dst->sin_addr.s_addr)))
91 		if (if_set_mcast(iface) == -1) {
92 			log_warn("send_packet: error setting multicast "
93 			    "interface, %s", iface->name);
94 			return (-1);
95 		}
96 
97 	if (sendto(fd, pkt, len, 0, (struct sockaddr *)dst,
98 	    sizeof(*dst)) == -1) {
99 		log_warn("send_packet: error sending packet");
100 		return (-1);
101 	}
102 
103 	return (0);
104 }
105 
106 /* Discovery functions */
107 void
108 disc_recv_packet(int fd, short event, void *bula)
109 {
110 	union {
111 		struct cmsghdr hdr;
112 		char	buf[CMSG_SPACE(sizeof(struct sockaddr_dl))];
113 	} cmsgbuf;
114 	struct sockaddr_in	 src;
115 	struct msghdr		 msg;
116 	struct iovec		 iov;
117 	struct ldp_hdr		 ldp_hdr;
118 	struct ldp_msg		 ldp_msg;
119 	struct iface		*iface = NULL;
120 	char			*buf;
121 	struct cmsghdr		*cmsg;
122 	ssize_t			 r;
123 	u_int16_t		 len;
124 	unsigned int		 ifindex = 0;
125 
126 	if (event != EV_READ)
127 		return;
128 
129 	/* setup buffer */
130 	bzero(&msg, sizeof(msg));
131 	iov.iov_base = buf = pkt_ptr;
132 	iov.iov_len = IBUF_READ_SIZE;
133 	msg.msg_name = &src;
134 	msg.msg_namelen = sizeof(src);
135 	msg.msg_iov = &iov;
136 	msg.msg_iovlen = 1;
137 	msg.msg_control = &cmsgbuf.buf;
138 	msg.msg_controllen = sizeof(cmsgbuf.buf);
139 
140 	if ((r = recvmsg(fd, &msg, 0)) == -1) {
141 		if (errno != EAGAIN && errno != EINTR)
142 			log_debug("disc_recv_packet: read error: %s",
143 			    strerror(errno));
144 		return;
145 	}
146 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
147 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
148 		if (cmsg->cmsg_level == IPPROTO_IP &&
149 		    cmsg->cmsg_type == IP_RECVIF) {
150 			ifindex = ((struct sockaddr_dl *)
151 			    CMSG_DATA(cmsg))->sdl_index;
152 			break;
153 		}
154 	}
155 
156 	len = (u_int16_t)r;
157 
158 	/* find a matching interface */
159 	if ((fd == leconf->ldp_discovery_socket) &&
160 	    (iface = disc_find_iface(ifindex, src.sin_addr)) == NULL) {
161 		log_debug("disc_recv_packet: cannot find a matching interface");
162 		return;
163 	}
164 
165 	/* LDP header sanity checks */
166 	if (len < LDP_HDR_SIZE || len > LDP_MAX_LEN) {
167 		log_debug("disc_recv_packet: bad packet size");
168 		return;
169 	}
170 	bcopy(buf, &ldp_hdr, sizeof(ldp_hdr));
171 
172 	if (ntohs(ldp_hdr.version) != LDP_VERSION) {
173 		log_debug("dsc_recv_packet: invalid LDP version %d",
174 		    ldp_hdr.version);
175 		return;
176 	}
177 
178 	if (ntohs(ldp_hdr.length) >
179 	    len - sizeof(ldp_hdr.version) - sizeof(ldp_hdr.length)) {
180 		log_debug("disc_recv_packet: invalid LDP packet length %u",
181 		    ntohs(ldp_hdr.length));
182 		return;
183 	}
184 
185 	if (len < LDP_HDR_SIZE + LDP_MSG_LEN) {
186 		log_debug("disc_recv_packet: invalid LDP packet length %d",
187 		    ntohs(ldp_hdr.length));
188 		return;
189 	}
190 
191 	bcopy(buf + LDP_HDR_SIZE, &ldp_msg, sizeof(ldp_msg));
192 
193 	/* switch LDP packet type */
194 	switch (ntohs(ldp_msg.type)) {
195 	case MSG_TYPE_HELLO:
196 		recv_hello(iface, src.sin_addr, buf, len);
197 		break;
198 	default:
199 		log_debug("recv_packet: unknown LDP packet type, source %s",
200 		    inet_ntoa(src.sin_addr));
201 	}
202 }
203 
204 struct iface *
205 disc_find_iface(unsigned int ifindex, struct in_addr src)
206 {
207 	struct iface	*iface;
208 	struct if_addr	*if_addr;
209 
210 	LIST_FOREACH(iface, &leconf->iface_list, entry)
211 		LIST_FOREACH(if_addr, &iface->addr_list, iface_entry)
212 			switch (iface->type) {
213 			case IF_TYPE_POINTOPOINT:
214 				if (ifindex == iface->ifindex &&
215 				    if_addr->dstbrd.s_addr == src.s_addr)
216 					return (iface);
217 				break;
218 			default:
219 				if (ifindex == iface->ifindex &&
220 				    (if_addr->addr.s_addr &
221 					if_addr->mask.s_addr) ==
222 				    (src.s_addr & if_addr->mask.s_addr))
223 					return (iface);
224 				break;
225 			}
226 
227 	return (NULL);
228 }
229 
230 struct tcp_conn *
231 tcp_new(int fd, struct nbr *nbr)
232 {
233 	struct tcp_conn *tcp;
234 
235 	if ((tcp = calloc(1, sizeof(*tcp))) == NULL)
236 		fatal("tcp_new");
237 	if ((tcp->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL)
238 		fatal("tcp_new");
239 
240 	if (nbr)
241 		tcp->nbr = nbr;
242 
243 	tcp->fd = fd;
244 	evbuf_init(&tcp->wbuf, tcp->fd, session_write, tcp);
245 	event_set(&tcp->rev, tcp->fd, EV_READ | EV_PERSIST, session_read, tcp);
246 	event_add(&tcp->rev, NULL);
247 
248 	return (tcp);
249 }
250 
251 void
252 tcp_close(struct tcp_conn *tcp)
253 {
254 	evbuf_clear(&tcp->wbuf);
255 	event_del(&tcp->rev);
256 	close(tcp->fd);
257 	free(tcp->rbuf);
258 	free(tcp);
259 }
260 
261 void
262 session_accept(int fd, short event, void *bula)
263 {
264 	struct sockaddr_in	 src;
265 	int			 newfd;
266 	socklen_t		 len = sizeof(src);
267 
268 	if (!(event & EV_READ))
269 		return;
270 
271 	newfd = accept(fd, (struct sockaddr *)&src, &len);
272 	if (newfd == -1) {
273 		/*
274 		 * Pause accept if we are out of file descriptors, or
275 		 * libevent will haunt us here too.
276 		 */
277 		if (errno == ENFILE || errno == EMFILE) {
278 			accept_pause();
279 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
280 		    errno != ECONNABORTED)
281 			log_debug("sess_recv_packet: accept error: %s",
282 			    strerror(errno));
283 		return;
284 	}
285 
286 	session_socket_blockmode(newfd, BM_NONBLOCK);
287 
288 	tcp_new(newfd, NULL);
289 }
290 
291 void
292 session_read(int fd, short event, void *arg)
293 {
294 	struct tcp_conn	*tcp = arg;
295 	struct nbr	*nbr = tcp->nbr;
296 	struct ldp_hdr	*ldp_hdr;
297 	struct ldp_msg	*ldp_msg;
298 	char		*buf, *pdu;
299 	ssize_t		 n, len;
300 	int		 msg_size;
301 	u_int16_t	 pdu_len;
302 
303 	if (event != EV_READ) {
304 		log_debug("session_read: spurious event");
305 		return;
306 	}
307 
308 	if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos,
309 	    sizeof(tcp->rbuf->buf) - tcp->rbuf->wpos)) == -1) {
310 		if (errno != EINTR && errno != EAGAIN) {
311 			log_warn("session_read: read error");
312 			if (nbr)
313 				nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
314 			else
315 				tcp_close(tcp);
316 			return;
317 		}
318 		/* retry read */
319 		return;
320 	}
321 	if (n == 0) {
322 		/* connection closed */
323 		log_debug("session_read: connection closed by remote end");
324 		if (nbr)
325 			nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
326 		else
327 			tcp_close(tcp);
328 		return;
329 	}
330 	tcp->rbuf->wpos += n;
331 
332 	while ((len = session_get_pdu(tcp->rbuf, &buf)) > 0) {
333 		pdu = buf;
334 		ldp_hdr = (struct ldp_hdr *)pdu;
335 		if (ntohs(ldp_hdr->version) != LDP_VERSION) {
336 			if (nbr)
337 				session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0);
338 			else {
339 				send_notification(S_BAD_PROTO_VER, tcp, 0, 0);
340 				msgbuf_write(&tcp->wbuf.wbuf);
341 				tcp_close(tcp);
342 			}
343 			free(buf);
344 			return;
345 		}
346 
347 		pdu_len = ntohs(ldp_hdr->length);
348 		if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_LEN) ||
349 		    pdu_len > LDP_MAX_LEN) {
350 			if (nbr)
351 				session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
352 			else {
353 				send_notification(S_BAD_PDU_LEN, tcp, 0, 0);
354 				msgbuf_write(&tcp->wbuf.wbuf);
355 				tcp_close(tcp);
356 			}
357 			free(buf);
358 			return;
359 		}
360 
361 		if (nbr) {
362 			if (ldp_hdr->lsr_id != nbr->id.s_addr ||
363 			    ldp_hdr->lspace_id != 0) {
364 				session_shutdown(nbr, S_BAD_LDP_ID, 0, 0);
365 				free(buf);
366 				return;
367 			}
368 		} else {
369 			nbr = nbr_find_ldpid(ldp_hdr->lsr_id);
370 			if (!nbr) {
371 				send_notification(S_NO_HELLO, tcp, 0, 0);
372 				msgbuf_write(&tcp->wbuf.wbuf);
373 				tcp_close(tcp);
374 				free(buf);
375 				return;
376 			}
377 			/* handle duplicate SYNs */
378 			if (nbr->tcp) {
379 				tcp_close(tcp);
380 				free(buf);
381 				return;
382 			}
383 
384 			nbr->tcp = tcp;
385 			tcp->nbr = nbr;
386 			nbr_fsm(nbr, NBR_EVT_MATCH_ADJ);
387 		}
388 
389 		pdu += LDP_HDR_SIZE;
390 		len -= LDP_HDR_SIZE;
391 
392 		if (nbr->state == NBR_STA_OPER)
393 			nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
394 
395 		while (len >= LDP_MSG_LEN) {
396 			u_int16_t type;
397 
398 			ldp_msg = (struct ldp_msg *)pdu;
399 			type = ntohs(ldp_msg->type);
400 
401 			pdu_len = ntohs(ldp_msg->length) + TLV_HDR_LEN;
402 			if (pdu_len > len ||
403 			    pdu_len < LDP_MSG_LEN - TLV_HDR_LEN) {
404 				session_shutdown(nbr, S_BAD_TLV_LEN,
405 				    ldp_msg->msgid, ldp_msg->type);
406 				free(buf);
407 				return;
408 			}
409 
410 			/* check for error conditions earlier */
411 			switch (type) {
412 			case MSG_TYPE_INIT:
413 				if ((nbr->state != NBR_STA_INITIAL) &&
414 				    (nbr->state != NBR_STA_OPENSENT)) {
415 					session_shutdown(nbr, S_SHUTDOWN,
416 					    ldp_msg->msgid, ldp_msg->type);
417 					free(buf);
418 					return;
419 				}
420 				break;
421 			case MSG_TYPE_KEEPALIVE:
422 				if ((nbr->state == NBR_STA_INITIAL) ||
423 				    (nbr->state == NBR_STA_OPENSENT)) {
424 					session_shutdown(nbr, S_SHUTDOWN,
425 					    ldp_msg->msgid, ldp_msg->type);
426 					free(buf);
427 					return;
428 				}
429 				break;
430 			case MSG_TYPE_NOTIFICATION:
431 			case MSG_TYPE_ADDR:
432 			case MSG_TYPE_ADDRWITHDRAW:
433 			case MSG_TYPE_LABELMAPPING:
434 			case MSG_TYPE_LABELREQUEST:
435 			case MSG_TYPE_LABELWITHDRAW:
436 			case MSG_TYPE_LABELRELEASE:
437 			case MSG_TYPE_LABELABORTREQ:
438 			default:
439 				if (nbr->state != NBR_STA_OPER) {
440 					session_shutdown(nbr, S_SHUTDOWN,
441 					    ldp_msg->msgid, ldp_msg->type);
442 					free(buf);
443 					return;
444 				}
445 				break;
446 			}
447 
448 			/* switch LDP packet type */
449 			switch (type) {
450 			case MSG_TYPE_NOTIFICATION:
451 				msg_size = recv_notification(nbr, pdu, pdu_len);
452 				break;
453 			case MSG_TYPE_INIT:
454 				msg_size = recv_init(nbr, pdu, pdu_len);
455 				break;
456 			case MSG_TYPE_KEEPALIVE:
457 				msg_size = recv_keepalive(nbr, pdu, pdu_len);
458 				break;
459 			case MSG_TYPE_ADDR:
460 			case MSG_TYPE_ADDRWITHDRAW:
461 				msg_size = recv_address(nbr, pdu, pdu_len);
462 				break;
463 			case MSG_TYPE_LABELMAPPING:
464 			case MSG_TYPE_LABELREQUEST:
465 			case MSG_TYPE_LABELWITHDRAW:
466 			case MSG_TYPE_LABELRELEASE:
467 			case MSG_TYPE_LABELABORTREQ:
468 				msg_size = recv_labelmessage(nbr, pdu,
469 				    pdu_len, type);
470 				break;
471 			default:
472 				log_debug("session_read: unknown LDP packet "
473 				    "from nbr %s", inet_ntoa(nbr->id));
474 				if (!(ntohs(ldp_msg->type) & UNKNOWN_FLAG)) {
475 					session_shutdown(nbr, S_UNKNOWN_MSG,
476 					    ldp_msg->msgid, ldp_msg->type);
477 					free(buf);
478 					return;
479 				}
480 				/* unknown flag is set, ignore the message */
481 				msg_size = ntohs(ldp_msg->length);
482 				break;
483 			}
484 
485 			if (msg_size == -1) {
486 				/* parser failed, giving up */
487 				free(buf);
488 				return;
489 			}
490 
491 			/* Analyse the next message */
492 			pdu += msg_size + TLV_HDR_LEN;
493 			len -= msg_size + TLV_HDR_LEN;
494 		}
495 		free(buf);
496 		if (len != 0) {
497 			session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
498 			return;
499 		}
500 	}
501 }
502 
503 void
504 session_write(int fd, short event, void *arg)
505 {
506 	struct tcp_conn *tcp = arg;
507 	struct nbr *nbr = tcp->nbr;
508 
509 	if (event & EV_WRITE) {
510 		if (msgbuf_write(&tcp->wbuf.wbuf) <= 0 && errno != EAGAIN) {
511 			if (nbr)
512 				nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
513 		}
514 	} else
515 		log_debug("session_write: spurious event");
516 
517 	evbuf_event_add(&tcp->wbuf);
518 }
519 
520 void
521 session_shutdown(struct nbr *nbr, u_int32_t status, u_int32_t msgid,
522     u_int32_t type)
523 {
524 	log_debug("session_shutdown: nbr ID %s", inet_ntoa(nbr->id));
525 
526 	send_notification_nbr(nbr, status, msgid, type);
527 
528 	/* try to flush write buffer, if it fails tough shit */
529 	msgbuf_write(&nbr->tcp->wbuf.wbuf);
530 
531 	nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
532 }
533 
534 void
535 session_close(struct nbr *nbr)
536 {
537 	log_debug("session_close: closing session with nbr ID %s",
538 	    inet_ntoa(nbr->id));
539 
540 	tcp_close(nbr->tcp);
541 	nbr->tcp = NULL;
542 
543 	nbr_stop_ktimer(nbr);
544 	nbr_stop_ktimeout(nbr);
545 
546 	accept_unpause();
547 }
548 
549 ssize_t
550 session_get_pdu(struct ibuf_read *r, char **b)
551 {
552 	struct ldp_hdr	l;
553 	size_t		av, dlen, left;
554 
555 	av = r->wpos;
556 	if (av < sizeof(l))
557 		return (0);
558 
559 	memcpy(&l, r->buf, sizeof(l));
560 	dlen = ntohs(l.length) + TLV_HDR_LEN;
561 	if (dlen > av)
562 		return (0);
563 
564 	if ((*b = malloc(dlen)) == NULL)
565 		return (-1);
566 
567 	memcpy(*b, r->buf, dlen);
568 	if (dlen < av) {
569 		left = av - dlen;
570 		memmove(r->buf, r->buf + dlen, left);
571 		r->wpos = left;
572 	} else
573 		r->wpos = 0;
574 
575 	return (dlen);
576 }
577