xref: /openbsd-src/usr.sbin/ldpd/packet.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: packet.c,v 1.15 2011/03/12 01:52:04 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 		log_debug("sess_recv_packet: accept error: %s",
266 		    strerror(errno));
267 		return;
268 	}
269 
270 	session_socket_blockmode(newfd, BM_NONBLOCK);
271 
272 	nbr = nbr_find_ip(src.sin_addr.s_addr);
273 	if (nbr == NULL) {
274 		struct ibuf	*buf;
275 		/* If there is no neighbor matching there is no
276 		   Hello adjacency: try to send notification */
277 		log_warnx("Connection attempt from unknown neighbor %s: %s",
278 		    inet_ntoa(src.sin_addr), "NO HELLO");
279 		buf = send_notification(S_NO_HELLO, NULL, 0, 0);
280 		write(newfd, buf->buf, buf->wpos);
281 		ibuf_free(buf);
282 		close(newfd);
283 		return;
284 	}
285 
286 	nbr->fd = newfd;
287 	nbr_fsm(nbr, NBR_EVT_SESSION_UP);
288 }
289 
290 void
291 session_read(int fd, short event, void *arg)
292 {
293 	struct nbr	*nbr = arg;
294 	struct iface	*iface = nbr->iface;
295 	struct ldp_hdr	*ldp_hdr;
296 	struct ldp_msg	*ldp_msg;
297 	char		*buf, *pdu;
298 	ssize_t		 n, len;
299 	int		 l, msg_size;
300 	u_int16_t	 pdu_len;
301 
302 	if (event != EV_READ) {
303 		log_debug("session_read: spurious event");
304 		return;
305 	}
306 
307 	if ((n = read(fd, nbr->rbuf->buf + nbr->rbuf->wpos,
308 	    sizeof(nbr->rbuf->buf) - nbr->rbuf->wpos)) == -1) {
309 		if (errno != EINTR && errno != EAGAIN) {
310 			session_shutdown(nbr, S_SHUTDOWN, 0, 0);
311 			return;
312 		}
313 		/* retry read */
314 		return;
315 	}
316 	if (n == 0) {
317 		/* connection closed */
318 		session_shutdown(nbr, S_SHUTDOWN, 0, 0);
319 		return;
320 	}
321 	nbr->rbuf->wpos += n;
322 
323 	while ((len = session_get_pdu(nbr->rbuf, &buf)) > 0) {
324 		pdu = buf;
325 		ldp_hdr = (struct ldp_hdr *)pdu;
326 		if (ntohs(ldp_hdr->version) != LDP_VERSION) {
327 			session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0);
328 			free(buf);
329 			return;
330 		}
331 
332 		pdu_len = ntohs(ldp_hdr->length);
333 		if (pdu_len < LDP_HDR_SIZE || pdu_len > LDP_MAX_LEN) {
334 			session_shutdown(nbr, S_BAD_MSG_LEN, 0, 0);
335 			free(buf);
336 			return;
337 		}
338 
339 		if ((l = ldp_hdr_sanity_check(ldp_hdr, len, iface)) == -1) {
340 			session_shutdown(nbr, S_BAD_LDP_ID, 0, 0);
341 			free(buf);
342 			return;
343 		}
344 
345 		pdu += LDP_HDR_SIZE;
346 		len -= LDP_HDR_SIZE;
347 
348 		while (len >= LDP_MSG_LEN) {
349 			ldp_msg = (struct ldp_msg *)pdu;
350 
351 			pdu_len = ntohs(ldp_msg->length) + TLV_HDR_LEN;
352 			if (pdu_len > len ||
353 			    pdu_len < LDP_MSG_LEN - TLV_HDR_LEN) {
354 				session_shutdown(nbr, S_BAD_TLV_LEN, 0, 0);
355 				free(buf);
356 				return;
357 			}
358 
359 			/* switch LDP packet type */
360 			switch (ntohs(ldp_msg->type)) {
361 			case MSG_TYPE_NOTIFICATION:
362 				msg_size = recv_notification(nbr, pdu, pdu_len);
363 				break;
364 			case MSG_TYPE_INIT:
365 				msg_size = recv_init(nbr, pdu, pdu_len);
366 				break;
367 			case MSG_TYPE_KEEPALIVE:
368 				msg_size = recv_keepalive(nbr, pdu, pdu_len);
369 				break;
370 			case MSG_TYPE_ADDR:
371 			case MSG_TYPE_ADDRWITHDRAW:
372 				msg_size = recv_address(nbr, pdu, pdu_len);
373 				break;
374 			case MSG_TYPE_LABELMAPPING:
375 				msg_size = recv_labelmapping(nbr, pdu, pdu_len);
376 				break;
377 			case MSG_TYPE_LABELREQUEST:
378 				msg_size = recv_labelrequest(nbr, pdu, pdu_len);
379 				break;
380 			case MSG_TYPE_LABELWITHDRAW:
381 				msg_size = recv_labelwithdraw(nbr, pdu, pdu_len);
382 				break;
383 			case MSG_TYPE_LABELRELEASE:
384 				msg_size = recv_labelrelease(nbr, pdu, pdu_len);
385 				break;
386 			case MSG_TYPE_LABELABORTREQ:
387 			case MSG_TYPE_HELLO:
388 			default:
389 				log_debug("session_read: unknown LDP packet "
390 				    "type interface %s", iface->name);
391 				free(buf);
392 				return;
393 			}
394 
395 			if (msg_size == -1) {
396 				/* parser failed, giving up */
397 				free(buf);
398 				return;
399 			}
400 
401 			/* Analyse the next message */
402 			pdu += msg_size + TLV_HDR_LEN;
403 			len -= msg_size + TLV_HDR_LEN;
404 		}
405 		free(buf);
406 		if (len != 0) {
407 			session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
408 			return;
409 		}
410 	}
411 }
412 
413 void
414 session_write(int fd, short event, void *arg)
415 {
416 	struct nbr *nbr = arg;
417 
418 	if (event & EV_WRITE) {
419 		if (msgbuf_write(&nbr->wbuf.wbuf) == -1)
420 			nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
421 	} else
422 		log_debug("session_write: spurious event");
423 
424 	evbuf_event_add(&nbr->wbuf);
425 }
426 
427 void
428 session_shutdown(struct nbr *nbr, u_int32_t status, u_int32_t msgid,
429     u_int32_t type)
430 {
431 	log_debug("session_shutdown: nbr ID %s, status %x",
432 	    inet_ntoa(nbr->id), status);
433 
434 	send_notification_nbr(nbr, status, msgid, type);
435 
436 	/* try to flush write buffer, if it fails tough shit */
437 	msgbuf_write(&nbr->wbuf.wbuf);
438 
439 	nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
440 }
441 
442 void
443 session_close(struct nbr *nbr)
444 {
445 	log_debug("session_close: closing session with nbr ID %s",
446 	    inet_ntoa(nbr->id));
447 
448 	evbuf_clear(&nbr->wbuf);
449 	event_del(&nbr->rev);
450 
451 	if (evtimer_pending(&nbr->keepalive_timer, NULL))
452 		evtimer_del(&nbr->keepalive_timer);
453 	if (evtimer_pending(&nbr->keepalive_timeout, NULL))
454 		evtimer_del(&nbr->keepalive_timeout);
455 
456 	close(nbr->fd);
457 }
458 
459 ssize_t
460 session_get_pdu(struct ibuf_read *r, char **b)
461 {
462 	struct ldp_hdr	l;
463 	size_t		av, dlen, left;
464 
465 	av = r->wpos;
466 	if (av < sizeof(l))
467 		return (0);
468 
469 	memcpy(&l, r->buf, sizeof(l));
470 	dlen = ntohs(l.length) + TLV_HDR_LEN;
471 	if (dlen > av)
472 		return (0);
473 
474 	if ((*b = malloc(dlen)) == NULL)
475 		return (-1);
476 
477 	memcpy(*b, r->buf, dlen);
478 	if (dlen < av) {
479 		left = av - dlen;
480 		memmove(r->buf, r->buf + dlen, left);
481 		r->wpos = left;
482 	} else
483 		r->wpos = 0;
484 
485 	return (dlen);
486 }
487