xref: /openbsd-src/usr.sbin/ldpd/packet.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /*	$OpenBSD: packet.c,v 1.3 2009/11/01 11:09:58 michele 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 struct iface	*session_find_iface(struct ldpd_conf *, struct in_addr);
46 
47 static int	 msgcnt = 0;
48 
49 int
50 gen_ldp_hdr(struct buf *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 	ldp_hdr.lspace_id = iface->lspace_id;
63 
64 	return (buf_add(buf, &ldp_hdr, LDP_HDR_SIZE));
65 }
66 
67 int
68 gen_msg_tlv(struct buf *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 	msg.msgid = htonl(++msgcnt);
79 
80 	return (buf_add(buf, &msg, sizeof(msg)));
81 }
82 
83 /* send and receive packets */
84 int
85 send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst)
86 {
87 	/* set outgoing interface for multicast traffic */
88 	if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr)))
89 		if (if_set_mcast(iface) == -1) {
90 			log_warn("send_packet: error setting multicast "
91 			    "interface, %s", iface->name);
92 			return (-1);
93 		}
94 
95 	if (sendto(iface->discovery_fd, pkt, len, 0,
96 	    (struct sockaddr *)dst, sizeof(*dst)) == -1) {
97 		log_warn("send_packet: error sending packet on interface %s",
98 		    iface->name);
99 		return (-1);
100 	}
101 
102 	return (0);
103 }
104 
105 /* Discovery functions */
106 void
107 disc_recv_packet(int fd, short event, void *bula)
108 {
109 	union {
110 		struct cmsghdr hdr;
111 		char	buf[CMSG_SPACE(sizeof(struct sockaddr_dl))];
112 	} cmsgbuf;
113 	struct sockaddr_in	 src;
114 	struct msghdr		 msg;
115 	struct iovec		 iov;
116 	struct ldpd_conf	*xconf = bula;
117 	struct ldp_hdr		*ldp_hdr;
118 	struct ldp_msg		*ldp_msg;
119 	struct iface		*iface;
120 	char			*buf;
121 	struct cmsghdr		*cmsg;
122 	ssize_t			 r;
123 	u_int16_t		 len;
124 	int			 l;
125 	unsigned int		 ifindex = 0;
126 
127 	if (event != EV_READ)
128 		return;
129 
130 	/* setup buffer */
131 	bzero(&msg, sizeof(msg));
132 	iov.iov_base = buf = pkt_ptr;
133 	iov.iov_len = READ_BUF_SIZE;
134 	msg.msg_name = &src;
135 	msg.msg_namelen = sizeof(src);
136 	msg.msg_iov = &iov;
137 	msg.msg_iovlen = 1;
138 	msg.msg_control = &cmsgbuf.buf;
139 	msg.msg_controllen = sizeof(cmsgbuf.buf);
140 
141 	if ((r = recvmsg(fd, &msg, 0)) == -1) {
142 		if (errno != EAGAIN && errno != EINTR)
143 			log_debug("disc_recv_packet: read error: %s",
144 			    strerror(errno));
145 		return;
146 	}
147 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
148 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
149 		if (cmsg->cmsg_level == IPPROTO_IP &&
150 		    cmsg->cmsg_type == IP_RECVIF) {
151 			ifindex = ((struct sockaddr_dl *)
152 			    CMSG_DATA(cmsg))->sdl_index;
153 			break;
154 		}
155 	}
156 
157 	len = (u_int16_t)r;
158 
159 	/* find a matching interface */
160 	if ((iface = find_iface(xconf, 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 	ldp_hdr = (struct ldp_hdr *)buf;
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) > len ||
179 	    len <= sizeof(struct ldp_hdr)) {
180 		log_debug("disc_recv_packet: invalid LDP packet length %d",
181 		    ntohs(ldp_hdr->length));
182 		return;
183 	}
184 
185 	if ((l = ldp_hdr_sanity_check(ldp_hdr, len, iface)) == -1)
186 		return;
187 
188 	ldp_msg = (struct ldp_msg *)(buf + LDP_HDR_SIZE);
189 
190 	if (len < LDP_MSG_LEN) {
191 		log_debug("disc_recv_packet: invalid LDP packet length %d",
192 		    ntohs(ldp_hdr->length));
193 		return;
194 	}
195 
196 	/* switch LDP packet type */
197 	switch (ntohs(ldp_msg->type)) {
198 	case MSG_TYPE_HELLO:
199 		recv_hello(iface, src.sin_addr, buf, len);
200 		break;
201 	default:
202 		log_debug("recv_packet: unknown LDP packet type, interface %s",
203 		    iface->name);
204 	}
205 }
206 
207 int
208 ldp_hdr_sanity_check(struct ldp_hdr *ldp_hdr, u_int16_t len,
209     const struct iface *iface)
210 {
211 	struct in_addr		 addr;
212 
213 	if (iface->type != IF_TYPE_VIRTUALLINK) {
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 	} else {
222 		if (ldp_hdr->lspace_id != 0) {
223 			addr.s_addr = ldp_hdr->lspace_id;
224 			log_debug("ldp_hdr_sanity_check: invalid label space "
225 			    "ID %s, interface %s", inet_ntoa(addr),
226 			    iface->name);
227 			return (-1);
228 		}
229 	}
230 
231 	return (ntohs(ldp_hdr->length));
232 }
233 
234 struct iface *
235 find_iface(struct ldpd_conf *xconf, unsigned int ifindex, struct in_addr src)
236 {
237 	struct iface	*iface = NULL;
238 
239 	/* returned interface needs to be active */
240 	LIST_FOREACH(iface, &xconf->iface_list, entry) {
241 		switch (iface->type) {
242 		case IF_TYPE_VIRTUALLINK:
243 			if ((src.s_addr == iface->dst.s_addr) &&
244 			    !iface->passive)
245 				return (iface);
246 			break;
247 		case IF_TYPE_POINTOPOINT:
248 			if (ifindex == iface->ifindex &&
249 			    iface->dst.s_addr == src.s_addr &&
250 			    !iface->passive)
251 				return (iface);
252 			break;
253 		default:
254 			if (ifindex == iface->ifindex &&
255 			    (iface->addr.s_addr & iface->mask.s_addr) ==
256 			    (src.s_addr & iface->mask.s_addr) &&
257 			    !iface->passive)
258 				return (iface);
259 			break;
260 		}
261 	}
262 
263 	return (NULL);
264 }
265 
266 void
267 session_recv_packet(int fd, short event, void *bula)
268 {
269 	struct sockaddr_in	 src;
270 	struct ldpd_conf	*xconf = bula;
271 	struct iface		*iface;
272 	struct nbr		*nbr = NULL;
273 	int			 newfd;
274 	socklen_t		 len = sizeof(src);
275 
276 	if (event != EV_READ)
277 		return;
278 
279 	newfd = accept(fd, (struct sockaddr *)&src, &len);
280 	if (newfd == -1) {
281 		log_debug("sess_recv_packet: accept error: %s",
282 		    strerror(errno));
283 		return;
284 	}
285 
286 	if (fcntl(newfd, F_SETFL, O_NONBLOCK) == -1) {
287 		log_debug("sess_recv_packet: unable to set non blocking flag");
288 		return;
289 	}
290 
291 	if ((iface = session_find_iface(xconf, src.sin_addr)) == NULL) {
292 		log_debug("sess_recv_packet: cannot find a matching interface");
293 		return;
294 	}
295 
296 	/* XXX */
297 	nbr = nbr_find_ip(iface, src.sin_addr.s_addr);
298 	if (nbr == NULL) {
299 		/* If there is no neighbor matching there is no
300 		   Hello adjacency: send notification */
301 		send_notification(S_NO_HELLO, iface, newfd, 0, 0);
302 		close(newfd);
303 		return;
304 	}
305 
306 	nbr->fd = newfd;
307 	nbr_fsm(nbr, NBR_EVT_SESSION_UP);
308 }
309 
310 void
311 session_read(struct bufferevent *bev, void *arg)
312 {
313 	struct nbr	*nbr = (struct nbr *)arg;
314 	struct iface	*iface = nbr->iface;
315 	struct ldp_hdr	*ldp_hdr;
316 	struct ldp_msg	*ldp_msg;
317 	u_int16_t	 len = EVBUFFER_LENGTH(EVBUFFER_INPUT(bev));
318 	u_int16_t	 pdu_len;
319 	char		 buffer[LDP_MAX_LEN];
320 	char		*buf = buffer;
321 	int		 l, msg_size = 0;
322 
323 	bufferevent_read(bev, buf, len);
324 
325 another_packet:
326 	ldp_hdr = (struct ldp_hdr *)buf;
327 
328 	if (ntohs(ldp_hdr->version) != LDP_VERSION) {
329 		session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0);
330 		return;
331 	}
332 
333 	pdu_len = ntohs(ldp_hdr->length);
334 
335 	if (pdu_len < LDP_HDR_SIZE || pdu_len > LDP_MAX_LEN) {
336 		session_shutdown(nbr, S_BAD_MSG_LEN, 0, 0);
337 		return;
338 	}
339 
340 	if ((l = ldp_hdr_sanity_check(ldp_hdr, len, iface)) == -1)
341 		return;
342 
343 	buf += LDP_HDR_SIZE;
344 	len -= LDP_HDR_SIZE;
345 
346 	pdu_len -= LDP_HDR_SIZE - PDU_HDR_SIZE;
347 
348 	while (pdu_len > LDP_MSG_LEN) {
349 		ldp_msg = (struct ldp_msg *)buf;
350 
351 		/* switch LDP packet type */
352 		switch (ntohs(ldp_msg->type)) {
353 		case MSG_TYPE_NOTIFICATION:
354 			msg_size = recv_notification(nbr, buf, pdu_len);
355 			break;
356 		case MSG_TYPE_INIT:
357 			msg_size = recv_init(nbr, buf, pdu_len);
358 			break;
359 		case MSG_TYPE_KEEPALIVE:
360 			msg_size = recv_keepalive(nbr, buf, pdu_len);
361 			break;
362 		case MSG_TYPE_ADDR:
363 			msg_size = recv_address(nbr, buf, pdu_len);
364 			break;
365 		case MSG_TYPE_ADDRWITHDRAW:
366 			msg_size = recv_address_withdraw(nbr, buf, pdu_len);
367 			break;
368 		case MSG_TYPE_LABELMAPPING:
369 			msg_size = recv_labelmapping(nbr, buf, pdu_len);
370 			break;
371 		case MSG_TYPE_LABELREQUEST:
372 			msg_size = recv_labelrequest(nbr, buf, pdu_len);
373 			break;
374 		case MSG_TYPE_LABELWITHDRAW:
375 			msg_size = recv_labelwithdraw(nbr, buf, pdu_len);
376 			break;
377 		case MSG_TYPE_LABELRELEASE:
378 			msg_size = recv_labelrelease(nbr, buf, pdu_len);
379 			break;
380 		case MSG_TYPE_LABELABORTREQ:
381 		case MSG_TYPE_HELLO:
382 		default:
383 			log_debug("session_read: unknown LDP packet type "
384 			    "interface %s", iface->name);
385 			return;
386 		}
387 
388 		if (msg_size < 0)
389 			return;
390 
391 		/* Analyse the next message */
392 		buf += msg_size + TLV_HDR_LEN;
393 		len -= msg_size + TLV_HDR_LEN;
394 		pdu_len -= msg_size + TLV_HDR_LEN;
395 	}
396 
397 	if (len > LDP_HDR_SIZE)
398 		goto another_packet;
399 }
400 
401 void
402 session_error(struct bufferevent *bev, short what, void *arg)
403 {
404 	struct nbr *nbr = arg;
405 
406 	nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
407 }
408 
409 void
410 session_shutdown(struct nbr *nbr, u_int32_t status, u_int32_t msgid,
411     u_int32_t type)
412 {
413 	send_notification_nbr(nbr, status, msgid, type);
414 	send_notification_nbr(nbr, S_SHUTDOWN, msgid, type);
415 
416 	nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
417 }
418 
419 void
420 session_close(struct nbr *nbr)
421 {
422 	log_debug("session_close: closing session with nbr ID %s",
423 	    inet_ntoa(nbr->id));
424 
425 	if (evtimer_pending(&nbr->keepalive_timer, NULL))
426 		evtimer_del(&nbr->keepalive_timer);
427 	if (evtimer_pending(&nbr->keepalive_timeout, NULL))
428 		evtimer_del(&nbr->keepalive_timeout);
429 
430 	bufferevent_free(nbr->bev);
431 	close(nbr->fd);
432 }
433 
434 struct iface *
435 session_find_iface(struct ldpd_conf *xconf, struct in_addr src)
436 {
437 	struct iface	*iface = NULL;
438 
439 	/* returned interface needs to be active */
440 	LIST_FOREACH(iface, &xconf->iface_list, entry) {
441 		switch (iface->type) {
442 		case IF_TYPE_VIRTUALLINK:
443 			if ((src.s_addr == iface->dst.s_addr) &&
444 			    !iface->passive)
445 				return (iface);
446 			break;
447 		case IF_TYPE_POINTOPOINT:
448 			if (iface->dst.s_addr == src.s_addr &&
449 			    !iface->passive)
450 				return (iface);
451 			break;
452 		default:
453 			if ((iface->addr.s_addr & iface->mask.s_addr) ==
454 			    (src.s_addr & iface->mask.s_addr) &&
455 			    !iface->passive)
456 				return (iface);
457 			break;
458 		}
459 	}
460 
461 	return (NULL);
462 }
463