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