xref: /openbsd-src/usr.sbin/ldpd/packet.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: packet.c,v 1.16 2012/04/12 17:33:43 claudio 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 int		 ldp_hdr_sanity_check(struct ldp_hdr *, u_int16_t,
43 		    const struct iface *);
44 struct iface	*find_iface(struct ldpd_conf *, 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, struct iface *iface, 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 	if (iface)
63 		ldp_hdr.lspace_id = iface->lspace_id;
64 
65 	return (ibuf_add(buf, &ldp_hdr, LDP_HDR_SIZE));
66 }
67 
68 int
69 gen_msg_tlv(struct ibuf *buf, u_int32_t type, u_int16_t size)
70 {
71 	struct ldp_msg	msg;
72 
73 	/* We want just the size of the value */
74 	size -= TLV_HDR_LEN;
75 
76 	bzero(&msg, sizeof(msg));
77 	msg.type = htons(type);
78 	msg.length = htons(size);
79 	msg.msgid = htonl(++msgcnt);
80 
81 	return (ibuf_add(buf, &msg, sizeof(msg)));
82 }
83 
84 /* send and receive packets */
85 int
86 send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst)
87 {
88 	/* set outgoing interface for multicast traffic */
89 	if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr)))
90 		if (if_set_mcast(iface) == -1) {
91 			log_warn("send_packet: error setting multicast "
92 			    "interface, %s", iface->name);
93 			return (-1);
94 		}
95 
96 	if (sendto(iface->discovery_fd, pkt, len, 0,
97 	    (struct sockaddr *)dst, sizeof(*dst)) == -1) {
98 		log_warn("send_packet: error sending packet on interface %s",
99 		    iface->name);
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 ldpd_conf	*xconf = bula;
118 	struct ldp_hdr		 ldp_hdr;
119 	struct ldp_msg		 ldp_msg;
120 	struct iface		*iface;
121 	char			*buf;
122 	struct cmsghdr		*cmsg;
123 	ssize_t			 r;
124 	u_int16_t		 len;
125 	int			 l;
126 	unsigned int		 ifindex = 0;
127 
128 	if (event != EV_READ)
129 		return;
130 
131 	/* setup buffer */
132 	bzero(&msg, sizeof(msg));
133 	iov.iov_base = buf = pkt_ptr;
134 	iov.iov_len = IBUF_READ_SIZE;
135 	msg.msg_name = &src;
136 	msg.msg_namelen = sizeof(src);
137 	msg.msg_iov = &iov;
138 	msg.msg_iovlen = 1;
139 	msg.msg_control = &cmsgbuf.buf;
140 	msg.msg_controllen = sizeof(cmsgbuf.buf);
141 
142 	if ((r = recvmsg(fd, &msg, 0)) == -1) {
143 		if (errno != EAGAIN && errno != EINTR)
144 			log_debug("disc_recv_packet: read error: %s",
145 			    strerror(errno));
146 		return;
147 	}
148 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
149 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
150 		if (cmsg->cmsg_level == IPPROTO_IP &&
151 		    cmsg->cmsg_type == IP_RECVIF) {
152 			ifindex = ((struct sockaddr_dl *)
153 			    CMSG_DATA(cmsg))->sdl_index;
154 			break;
155 		}
156 	}
157 
158 	len = (u_int16_t)r;
159 
160 	/* find a matching interface */
161 	if ((iface = find_iface(xconf, ifindex, src.sin_addr)) == NULL) {
162 		log_debug("disc_recv_packet: cannot find a matching interface");
163 		return;
164 	}
165 
166 	/* LDP header sanity checks */
167 	if (len < LDP_HDR_SIZE || len > LDP_MAX_LEN) {
168 		log_debug("disc_recv_packet: bad packet size");
169 		return;
170 	}
171 	bcopy(buf, &ldp_hdr, sizeof(ldp_hdr));
172 
173 	if (ntohs(ldp_hdr.version) != LDP_VERSION) {
174 		log_debug("dsc_recv_packet: invalid LDP version %d",
175 		    ldp_hdr.version);
176 		return;
177 	}
178 
179 	if ((l = ldp_hdr_sanity_check(&ldp_hdr, len, iface)) == -1)
180 		return;
181 
182 	if (l > len) {
183 		log_debug("disc_recv_packet: invalid LDP packet length %d",
184 		    ntohs(ldp_hdr.length));
185 		return;
186 	}
187 
188 	if (len < LDP_HDR_SIZE + LDP_MSG_LEN) {
189 		log_debug("disc_recv_packet: invalid LDP packet length %d",
190 		    ntohs(ldp_hdr.length));
191 		return;
192 	}
193 
194 	bcopy(buf + LDP_HDR_SIZE, &ldp_msg, sizeof(ldp_msg));
195 
196 
197 	/* switch LDP packet type */
198 	switch (ntohs(ldp_msg.type)) {
199 	case MSG_TYPE_HELLO:
200 		recv_hello(iface, src.sin_addr, buf, len);
201 		break;
202 	default:
203 		log_debug("recv_packet: unknown LDP packet type, interface %s",
204 		    iface->name);
205 	}
206 }
207 
208 int
209 ldp_hdr_sanity_check(struct ldp_hdr *ldp_hdr, u_int16_t len,
210     const struct iface *iface)
211 {
212 	struct in_addr		 addr;
213 
214 	if (ldp_hdr->lspace_id != iface->lspace_id) {
215 		addr.s_addr = ldp_hdr->lspace_id;
216 		log_debug("ldp_hdr_sanity_check: invalid label space "
217 		    "ID %s, interface %s", inet_ntoa(addr),
218 		    iface->name);
219 		return (-1);
220 	}
221 
222 	return (ntohs(ldp_hdr->length));
223 }
224 
225 struct iface *
226 find_iface(struct ldpd_conf *xconf, unsigned int ifindex, struct in_addr src)
227 {
228 	struct iface	*iface = NULL;
229 
230 	/* returned interface needs to be active */
231 	LIST_FOREACH(iface, &xconf->iface_list, entry) {
232 		switch (iface->type) {
233 		case IF_TYPE_POINTOPOINT:
234 			if (ifindex == iface->ifindex &&
235 			    iface->dst.s_addr == src.s_addr &&
236 			    !iface->passive)
237 				return (iface);
238 			break;
239 		default:
240 			if (ifindex == iface->ifindex &&
241 			    (iface->addr.s_addr & iface->mask.s_addr) ==
242 			    (src.s_addr & iface->mask.s_addr) &&
243 			    !iface->passive)
244 				return (iface);
245 			break;
246 		}
247 	}
248 
249 	return (NULL);
250 }
251 
252 void
253 session_accept(int fd, short event, void *bula)
254 {
255 	struct sockaddr_in	 src;
256 	struct nbr		*nbr = NULL;
257 	int			 newfd;
258 	socklen_t		 len = sizeof(src);
259 
260 	if (!(event & EV_READ))
261 		return;
262 
263 	newfd = accept(fd, (struct sockaddr *)&src, &len);
264 	if (newfd == -1) {
265 		/*
266 		 * Pause accept if we are out of file descriptors, or
267 		 * libevent will haunt us here too.
268 		 */
269 		if (errno == ENFILE || errno == EMFILE) {
270 			accept_pause();
271 		} else if (errno != EWOULDBLOCK && errno != EINTR)
272 			log_debug("sess_recv_packet: accept error: %s",
273 			    strerror(errno));
274 		return;
275 	}
276 
277 	session_socket_blockmode(newfd, BM_NONBLOCK);
278 
279 	nbr = nbr_find_ip(src.sin_addr.s_addr);
280 	if (nbr == NULL) {
281 		struct ibuf	*buf;
282 		/* If there is no neighbor matching there is no
283 		   Hello adjacency: try to send notification */
284 		log_warnx("Connection attempt from unknown neighbor %s: %s",
285 		    inet_ntoa(src.sin_addr), "NO HELLO");
286 		buf = send_notification(S_NO_HELLO, NULL, 0, 0);
287 		write(newfd, buf->buf, buf->wpos);
288 		ibuf_free(buf);
289 		close(newfd);
290 		return;
291 	}
292 
293 	nbr->fd = newfd;
294 	nbr_fsm(nbr, NBR_EVT_SESSION_UP);
295 }
296 
297 void
298 session_read(int fd, short event, void *arg)
299 {
300 	struct nbr	*nbr = arg;
301 	struct iface	*iface = nbr->iface;
302 	struct ldp_hdr	*ldp_hdr;
303 	struct ldp_msg	*ldp_msg;
304 	char		*buf, *pdu;
305 	ssize_t		 n, len;
306 	int		 l, msg_size;
307 	u_int16_t	 pdu_len;
308 
309 	if (event != EV_READ) {
310 		log_debug("session_read: spurious event");
311 		return;
312 	}
313 
314 	if ((n = read(fd, nbr->rbuf->buf + nbr->rbuf->wpos,
315 	    sizeof(nbr->rbuf->buf) - nbr->rbuf->wpos)) == -1) {
316 		if (errno != EINTR && errno != EAGAIN) {
317 			session_shutdown(nbr, S_SHUTDOWN, 0, 0);
318 			return;
319 		}
320 		/* retry read */
321 		return;
322 	}
323 	if (n == 0) {
324 		/* connection closed */
325 		session_shutdown(nbr, S_SHUTDOWN, 0, 0);
326 		return;
327 	}
328 	nbr->rbuf->wpos += n;
329 
330 	while ((len = session_get_pdu(nbr->rbuf, &buf)) > 0) {
331 		pdu = buf;
332 		ldp_hdr = (struct ldp_hdr *)pdu;
333 		if (ntohs(ldp_hdr->version) != LDP_VERSION) {
334 			session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0);
335 			free(buf);
336 			return;
337 		}
338 
339 		pdu_len = ntohs(ldp_hdr->length);
340 		if (pdu_len < LDP_HDR_SIZE || pdu_len > LDP_MAX_LEN) {
341 			session_shutdown(nbr, S_BAD_MSG_LEN, 0, 0);
342 			free(buf);
343 			return;
344 		}
345 
346 		if ((l = ldp_hdr_sanity_check(ldp_hdr, len, iface)) == -1) {
347 			session_shutdown(nbr, S_BAD_LDP_ID, 0, 0);
348 			free(buf);
349 			return;
350 		}
351 
352 		pdu += LDP_HDR_SIZE;
353 		len -= LDP_HDR_SIZE;
354 
355 		while (len >= LDP_MSG_LEN) {
356 			ldp_msg = (struct ldp_msg *)pdu;
357 
358 			pdu_len = ntohs(ldp_msg->length) + TLV_HDR_LEN;
359 			if (pdu_len > len ||
360 			    pdu_len < LDP_MSG_LEN - TLV_HDR_LEN) {
361 				session_shutdown(nbr, S_BAD_TLV_LEN, 0, 0);
362 				free(buf);
363 				return;
364 			}
365 
366 			/* switch LDP packet type */
367 			switch (ntohs(ldp_msg->type)) {
368 			case MSG_TYPE_NOTIFICATION:
369 				msg_size = recv_notification(nbr, pdu, pdu_len);
370 				break;
371 			case MSG_TYPE_INIT:
372 				msg_size = recv_init(nbr, pdu, pdu_len);
373 				break;
374 			case MSG_TYPE_KEEPALIVE:
375 				msg_size = recv_keepalive(nbr, pdu, pdu_len);
376 				break;
377 			case MSG_TYPE_ADDR:
378 			case MSG_TYPE_ADDRWITHDRAW:
379 				msg_size = recv_address(nbr, pdu, pdu_len);
380 				break;
381 			case MSG_TYPE_LABELMAPPING:
382 				msg_size = recv_labelmapping(nbr, pdu, pdu_len);
383 				break;
384 			case MSG_TYPE_LABELREQUEST:
385 				msg_size = recv_labelrequest(nbr, pdu, pdu_len);
386 				break;
387 			case MSG_TYPE_LABELWITHDRAW:
388 				msg_size = recv_labelwithdraw(nbr, pdu, pdu_len);
389 				break;
390 			case MSG_TYPE_LABELRELEASE:
391 				msg_size = recv_labelrelease(nbr, pdu, pdu_len);
392 				break;
393 			case MSG_TYPE_LABELABORTREQ:
394 			case MSG_TYPE_HELLO:
395 			default:
396 				log_debug("session_read: unknown LDP packet "
397 				    "type interface %s", iface->name);
398 				free(buf);
399 				return;
400 			}
401 
402 			if (msg_size == -1) {
403 				/* parser failed, giving up */
404 				free(buf);
405 				return;
406 			}
407 
408 			/* Analyse the next message */
409 			pdu += msg_size + TLV_HDR_LEN;
410 			len -= msg_size + TLV_HDR_LEN;
411 		}
412 		free(buf);
413 		if (len != 0) {
414 			session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
415 			return;
416 		}
417 	}
418 }
419 
420 void
421 session_write(int fd, short event, void *arg)
422 {
423 	struct nbr *nbr = arg;
424 
425 	if (event & EV_WRITE) {
426 		if (msgbuf_write(&nbr->wbuf.wbuf) == -1)
427 			nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
428 	} else
429 		log_debug("session_write: spurious event");
430 
431 	evbuf_event_add(&nbr->wbuf);
432 }
433 
434 void
435 session_shutdown(struct nbr *nbr, u_int32_t status, u_int32_t msgid,
436     u_int32_t type)
437 {
438 	log_debug("session_shutdown: nbr ID %s, status %x",
439 	    inet_ntoa(nbr->id), status);
440 
441 	send_notification_nbr(nbr, status, msgid, type);
442 
443 	/* try to flush write buffer, if it fails tough shit */
444 	msgbuf_write(&nbr->wbuf.wbuf);
445 
446 	nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
447 }
448 
449 void
450 session_close(struct nbr *nbr)
451 {
452 	log_debug("session_close: closing session with nbr ID %s",
453 	    inet_ntoa(nbr->id));
454 
455 	evbuf_clear(&nbr->wbuf);
456 	event_del(&nbr->rev);
457 
458 	if (evtimer_pending(&nbr->keepalive_timer, NULL))
459 		evtimer_del(&nbr->keepalive_timer);
460 	if (evtimer_pending(&nbr->keepalive_timeout, NULL))
461 		evtimer_del(&nbr->keepalive_timeout);
462 
463 	close(nbr->fd);
464 	accept_unpause();
465 }
466 
467 ssize_t
468 session_get_pdu(struct ibuf_read *r, char **b)
469 {
470 	struct ldp_hdr	l;
471 	size_t		av, dlen, left;
472 
473 	av = r->wpos;
474 	if (av < sizeof(l))
475 		return (0);
476 
477 	memcpy(&l, r->buf, sizeof(l));
478 	dlen = ntohs(l.length) + TLV_HDR_LEN;
479 	if (dlen > av)
480 		return (0);
481 
482 	if ((*b = malloc(dlen)) == NULL)
483 		return (-1);
484 
485 	memcpy(*b, r->buf, dlen);
486 	if (dlen < av) {
487 		left = av - dlen;
488 		memmove(r->buf, r->buf + dlen, left);
489 		r->wpos = left;
490 	} else
491 		r->wpos = 0;
492 
493 	return (dlen);
494 }
495