1*4f4fe40bSflorian /* $OpenBSD: packet.c,v 1.38 2024/08/21 15:18:00 florian Exp $ */ 2204df0f8Sclaudio 3204df0f8Sclaudio /* 4367f601bSnorby * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 5204df0f8Sclaudio * 6204df0f8Sclaudio * Permission to use, copy, modify, and distribute this software for any 7204df0f8Sclaudio * purpose with or without fee is hereby granted, provided that the above 8204df0f8Sclaudio * copyright notice and this permission notice appear in all copies. 9204df0f8Sclaudio * 10204df0f8Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11204df0f8Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12204df0f8Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13204df0f8Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14204df0f8Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15204df0f8Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16204df0f8Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17204df0f8Sclaudio */ 18204df0f8Sclaudio 19204df0f8Sclaudio #include <sys/types.h> 20204df0f8Sclaudio #include <sys/socket.h> 21e29a452cSclaudio #include <sys/uio.h> 22204df0f8Sclaudio 23204df0f8Sclaudio #include <netinet/in.h> 24204df0f8Sclaudio #include <netinet/ip.h> 25204df0f8Sclaudio #include <arpa/inet.h> 26e29a452cSclaudio #include <net/if_dl.h> 27204df0f8Sclaudio 28204df0f8Sclaudio #include <errno.h> 29204df0f8Sclaudio #include <event.h> 30204df0f8Sclaudio #include <stdlib.h> 3153e1908aSstevesk #include <string.h> 32204df0f8Sclaudio 33204df0f8Sclaudio #include "ospfd.h" 34204df0f8Sclaudio #include "ospf.h" 35204df0f8Sclaudio #include "log.h" 36204df0f8Sclaudio #include "ospfe.h" 37204df0f8Sclaudio 38204df0f8Sclaudio int ip_hdr_sanity_check(const struct ip *, u_int16_t); 39204df0f8Sclaudio int ospf_hdr_sanity_check(const struct ip *, 40204df0f8Sclaudio struct ospf_hdr *, u_int16_t, const struct iface *); 41e29a452cSclaudio struct iface *find_iface(struct ospfd_conf *, unsigned int, struct in_addr); 42204df0f8Sclaudio 43713697e0Sclaudio static u_int8_t *recv_buf; 44713697e0Sclaudio 450491ce75Sclaudio int 46e39620e5Snicm gen_ospf_hdr(struct ibuf *buf, struct iface *iface, u_int8_t type) 47204df0f8Sclaudio { 480491ce75Sclaudio struct ospf_hdr ospf_hdr; 49204df0f8Sclaudio 500491ce75Sclaudio bzero(&ospf_hdr, sizeof(ospf_hdr)); 510491ce75Sclaudio ospf_hdr.version = OSPF_VERSION; 520491ce75Sclaudio ospf_hdr.type = type; 53f12637e5Smsf ospf_hdr.rtr_id = ospfe_router_id(); 5433ad38afSnorby if (iface->type != IF_TYPE_VIRTUALLINK) 550491ce75Sclaudio ospf_hdr.area_id = iface->area->id.s_addr; 560491ce75Sclaudio ospf_hdr.auth_type = htons(iface->auth_type); 570491ce75Sclaudio 58e39620e5Snicm return (ibuf_add(buf, &ospf_hdr, sizeof(ospf_hdr))); 59204df0f8Sclaudio } 60204df0f8Sclaudio 61204df0f8Sclaudio /* send and receive packets */ 62204df0f8Sclaudio int 63e39620e5Snicm send_packet(struct iface *iface, struct ibuf *buf, struct sockaddr_in *dst) 64204df0f8Sclaudio { 6566dd3991Sclaudio struct msghdr msg; 6666dd3991Sclaudio struct iovec iov[2]; 6766dd3991Sclaudio struct ip ip_hdr; 6866dd3991Sclaudio 6966dd3991Sclaudio /* setup IP hdr */ 7066dd3991Sclaudio bzero(&ip_hdr, sizeof(ip_hdr)); 7166dd3991Sclaudio ip_hdr.ip_v = IPVERSION; 7266dd3991Sclaudio ip_hdr.ip_hl = sizeof(ip_hdr) >> 2; 7366dd3991Sclaudio ip_hdr.ip_tos = IPTOS_PREC_INTERNETCONTROL; 74e39620e5Snicm ip_hdr.ip_len = htons(ibuf_size(buf) + sizeof(ip_hdr)); 7566dd3991Sclaudio ip_hdr.ip_id = 0; /* 0 means kernel set appropriate value */ 7666dd3991Sclaudio ip_hdr.ip_off = 0; 7766dd3991Sclaudio ip_hdr.ip_ttl = iface->type != IF_TYPE_VIRTUALLINK ? 7866dd3991Sclaudio IP_DEFAULT_MULTICAST_TTL : MAXTTL; 7966dd3991Sclaudio ip_hdr.ip_p = IPPROTO_OSPF; 8066dd3991Sclaudio ip_hdr.ip_sum = 0; 8166dd3991Sclaudio ip_hdr.ip_src = iface->addr; 8266dd3991Sclaudio ip_hdr.ip_dst = dst->sin_addr; 8366dd3991Sclaudio 8466dd3991Sclaudio /* setup buffer */ 8566dd3991Sclaudio bzero(&msg, sizeof(msg)); 8666dd3991Sclaudio iov[0].iov_base = &ip_hdr; 8766dd3991Sclaudio iov[0].iov_len = sizeof(ip_hdr); 88a50d52cbSclaudio iov[1].iov_base = ibuf_data(buf); 89e39620e5Snicm iov[1].iov_len = ibuf_size(buf); 9066dd3991Sclaudio msg.msg_name = dst; 9166dd3991Sclaudio msg.msg_namelen = sizeof(*dst); 9266dd3991Sclaudio msg.msg_iov = iov; 9366dd3991Sclaudio msg.msg_iovlen = 2; 9466dd3991Sclaudio 95204df0f8Sclaudio /* set outgoing interface for multicast traffic */ 96204df0f8Sclaudio if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) 974426c0e7Sclaudio if (if_set_mcast(iface) == -1) 98204df0f8Sclaudio return (-1); 99204df0f8Sclaudio 10066dd3991Sclaudio if (sendmsg(iface->fd, &msg, 0) == -1) { 10125a742ccSremi log_warn("%s: error sending packet to %s on interface %s", 10225a742ccSremi __func__, inet_ntoa(ip_hdr.ip_dst), iface->name); 103204df0f8Sclaudio return (-1); 104204df0f8Sclaudio } 105204df0f8Sclaudio 106204df0f8Sclaudio return (0); 107204df0f8Sclaudio } 108204df0f8Sclaudio 109204df0f8Sclaudio void 110204df0f8Sclaudio recv_packet(int fd, short event, void *bula) 111204df0f8Sclaudio { 1120827ab61Sderaadt union { 1130827ab61Sderaadt struct cmsghdr hdr; 1140827ab61Sderaadt char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; 1150827ab61Sderaadt } cmsgbuf; 116e29a452cSclaudio struct msghdr msg; 117e29a452cSclaudio struct iovec iov; 118204df0f8Sclaudio struct ip ip_hdr; 119e29a452cSclaudio struct in_addr addr; 120e29a452cSclaudio struct ospfd_conf *xconf = bula; 121204df0f8Sclaudio struct ospf_hdr *ospf_hdr; 122204df0f8Sclaudio struct iface *iface; 123204df0f8Sclaudio struct nbr *nbr = NULL; 124590eae4cSnorby char *buf; 125e29a452cSclaudio struct cmsghdr *cmsg; 126204df0f8Sclaudio ssize_t r; 127204df0f8Sclaudio u_int16_t len; 128204df0f8Sclaudio int l; 129e29a452cSclaudio unsigned int ifindex = 0; 130204df0f8Sclaudio 131204df0f8Sclaudio if (event != EV_READ) 132204df0f8Sclaudio return; 133204df0f8Sclaudio 134713697e0Sclaudio if (recv_buf == NULL) 135713697e0Sclaudio if ((recv_buf = malloc(READ_BUF_SIZE)) == NULL) 136713697e0Sclaudio fatal(__func__); 137713697e0Sclaudio 138590eae4cSnorby /* setup buffer */ 139e29a452cSclaudio bzero(&msg, sizeof(msg)); 140713697e0Sclaudio iov.iov_base = buf = recv_buf; 141e29a452cSclaudio iov.iov_len = READ_BUF_SIZE; 142e29a452cSclaudio msg.msg_iov = &iov; 143e29a452cSclaudio msg.msg_iovlen = 1; 1440827ab61Sderaadt msg.msg_control = &cmsgbuf.buf; 145da15c7b9Sderaadt msg.msg_controllen = sizeof(cmsgbuf.buf); 146204df0f8Sclaudio 147e29a452cSclaudio if ((r = recvmsg(fd, &msg, 0)) == -1) { 1484a862d7aShenning if (errno != EAGAIN && errno != EINTR) 149e29a452cSclaudio log_debug("recv_packet: read error: %s", 150e29a452cSclaudio strerror(errno)); 151590eae4cSnorby return; 152204df0f8Sclaudio } 153e29a452cSclaudio for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 154e29a452cSclaudio cmsg = CMSG_NXTHDR(&msg, cmsg)) { 155e29a452cSclaudio if (cmsg->cmsg_level == IPPROTO_IP && 156e29a452cSclaudio cmsg->cmsg_type == IP_RECVIF) { 157e29a452cSclaudio ifindex = ((struct sockaddr_dl *) 158e29a452cSclaudio CMSG_DATA(cmsg))->sdl_index; 159e29a452cSclaudio break; 160e29a452cSclaudio } 161e29a452cSclaudio } 162204df0f8Sclaudio 163204df0f8Sclaudio len = (u_int16_t)r; 164204df0f8Sclaudio 165204df0f8Sclaudio /* IP header sanity checks */ 166204df0f8Sclaudio if (len < sizeof(ip_hdr)) { 167204df0f8Sclaudio log_warnx("recv_packet: bad packet size"); 168590eae4cSnorby return; 169204df0f8Sclaudio } 170204df0f8Sclaudio memcpy(&ip_hdr, buf, sizeof(ip_hdr)); 171204df0f8Sclaudio if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1) 172590eae4cSnorby return; 173204df0f8Sclaudio buf += l; 174204df0f8Sclaudio len -= l; 175204df0f8Sclaudio 176204df0f8Sclaudio /* find a matching interface */ 177e29a452cSclaudio if ((iface = find_iface(xconf, ifindex, ip_hdr.ip_src)) == NULL) { 17866dd3991Sclaudio /* XXX add a counter here */ 179590eae4cSnorby return; 180204df0f8Sclaudio } 181204df0f8Sclaudio 182204df0f8Sclaudio /* 183204df0f8Sclaudio * Packet needs to be sent to AllSPFRouters or AllDRouters 184204df0f8Sclaudio * or to the address of the interface itself. 185204df0f8Sclaudio * AllDRouters is only valid for DR and BDR but this is checked later. 186204df0f8Sclaudio */ 187*4f4fe40bSflorian inet_pton(AF_INET, AllSPFRouters, &addr); 188204df0f8Sclaudio if (ip_hdr.ip_dst.s_addr != addr.s_addr) { 189*4f4fe40bSflorian inet_pton(AF_INET, AllDRouters, &addr); 190204df0f8Sclaudio if (ip_hdr.ip_dst.s_addr != addr.s_addr) { 191204df0f8Sclaudio if (ip_hdr.ip_dst.s_addr != iface->addr.s_addr) { 192204df0f8Sclaudio log_debug("recv_packet: packet sent to wrong " 193204df0f8Sclaudio "address %s, interface %s", 194204df0f8Sclaudio inet_ntoa(ip_hdr.ip_dst), iface->name); 195590eae4cSnorby return; 196204df0f8Sclaudio } 197204df0f8Sclaudio } 198204df0f8Sclaudio } 199204df0f8Sclaudio 200204df0f8Sclaudio /* OSPF header sanity checks */ 201204df0f8Sclaudio if (len < sizeof(*ospf_hdr)) { 202e29a452cSclaudio log_debug("recv_packet: bad packet size"); 203590eae4cSnorby return; 204204df0f8Sclaudio } 205204df0f8Sclaudio ospf_hdr = (struct ospf_hdr *)buf; 206204df0f8Sclaudio 207204df0f8Sclaudio if ((l = ospf_hdr_sanity_check(&ip_hdr, ospf_hdr, len, iface)) == -1) 208590eae4cSnorby return; 209204df0f8Sclaudio 21003431b74Snorby nbr = nbr_find_id(iface, ospf_hdr->rtr_id); 21103431b74Snorby if (ospf_hdr->type != PACKET_TYPE_HELLO && nbr == NULL) { 212204df0f8Sclaudio log_debug("recv_packet: unknown neighbor ID"); 213590eae4cSnorby return; 214204df0f8Sclaudio } 215204df0f8Sclaudio 21603431b74Snorby if (auth_validate(buf, len, iface, nbr)) { 2179cc19ef1Ssthen if (nbr == NULL) 21803431b74Snorby log_warnx("recv_packet: authentication error, " 21903431b74Snorby "interface %s", iface->name); 2209cc19ef1Ssthen else 2219cc19ef1Ssthen log_warnx("recv_packet: authentication error, " 2229cc19ef1Ssthen "neighbor ID %s interface %s", 2239cc19ef1Ssthen inet_ntoa(nbr->id), iface->name); 224590eae4cSnorby return; 22503431b74Snorby } 22603431b74Snorby 22703431b74Snorby buf += sizeof(*ospf_hdr); 22803431b74Snorby len = l - sizeof(*ospf_hdr); 22903431b74Snorby 230204df0f8Sclaudio /* switch OSPF packet type */ 231204df0f8Sclaudio switch (ospf_hdr->type) { 232204df0f8Sclaudio case PACKET_TYPE_HELLO: 233*4f4fe40bSflorian inet_pton(AF_INET, AllSPFRouters, &addr); 2348c9a8c5fSremi if (iface->type == IF_TYPE_BROADCAST || 2358c9a8c5fSremi iface->type == IF_TYPE_POINTOPOINT) 2368c9a8c5fSremi if (ip_hdr.ip_dst.s_addr != addr.s_addr) { 2378c9a8c5fSremi log_warnx("%s: hello ignored on interface %s, " 2388c9a8c5fSremi "invalid destination IP address %s", 2398c9a8c5fSremi __func__, iface->name, 2408c9a8c5fSremi inet_ntoa(ip_hdr.ip_dst)); 241204df0f8Sclaudio break; 242204df0f8Sclaudio } 243204df0f8Sclaudio 244204df0f8Sclaudio recv_hello(iface, ip_hdr.ip_src, ospf_hdr->rtr_id, buf, len); 245204df0f8Sclaudio break; 246204df0f8Sclaudio case PACKET_TYPE_DD: 247204df0f8Sclaudio recv_db_description(nbr, buf, len); 248204df0f8Sclaudio break; 249204df0f8Sclaudio case PACKET_TYPE_LS_REQUEST: 250204df0f8Sclaudio recv_ls_req(nbr, buf, len); 251204df0f8Sclaudio break; 252204df0f8Sclaudio case PACKET_TYPE_LS_UPDATE: 253204df0f8Sclaudio recv_ls_update(nbr, buf, len); 254204df0f8Sclaudio break; 255204df0f8Sclaudio case PACKET_TYPE_LS_ACK: 256204df0f8Sclaudio recv_ls_ack(nbr, buf, len); 257204df0f8Sclaudio break; 258204df0f8Sclaudio default: 259204df0f8Sclaudio log_debug("recv_packet: unknown OSPF packet type, interface %s", 260204df0f8Sclaudio iface->name); 261204df0f8Sclaudio } 262204df0f8Sclaudio } 263204df0f8Sclaudio 264204df0f8Sclaudio int 265204df0f8Sclaudio ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len) 266204df0f8Sclaudio { 267204df0f8Sclaudio if (ntohs(ip_hdr->ip_len) != len) { 268beeeb35cScloder log_debug("recv_packet: invalid IP packet length %u", 269204df0f8Sclaudio ntohs(ip_hdr->ip_len)); 270204df0f8Sclaudio return (-1); 271204df0f8Sclaudio } 272204df0f8Sclaudio 273204df0f8Sclaudio if (ip_hdr->ip_p != IPPROTO_OSPF) 274204df0f8Sclaudio /* this is enforced by the socket itself */ 275204df0f8Sclaudio fatalx("recv_packet: invalid IP proto"); 276204df0f8Sclaudio 277204df0f8Sclaudio return (ip_hdr->ip_hl << 2); 278204df0f8Sclaudio } 279204df0f8Sclaudio 280204df0f8Sclaudio int 281204df0f8Sclaudio ospf_hdr_sanity_check(const struct ip *ip_hdr, struct ospf_hdr *ospf_hdr, 282204df0f8Sclaudio u_int16_t len, const struct iface *iface) 283204df0f8Sclaudio { 284204df0f8Sclaudio struct in_addr addr; 285204df0f8Sclaudio 286204df0f8Sclaudio if (ospf_hdr->version != OSPF_VERSION) { 287204df0f8Sclaudio log_debug("recv_packet: invalid OSPF version %d", 288204df0f8Sclaudio ospf_hdr->version); 289204df0f8Sclaudio return (-1); 290204df0f8Sclaudio } 291204df0f8Sclaudio 292204df0f8Sclaudio if (ntohs(ospf_hdr->len) > len || 293204df0f8Sclaudio len <= sizeof(struct ospf_hdr)) { 294204df0f8Sclaudio log_debug("recv_packet: invalid OSPF packet length %d", 295204df0f8Sclaudio ntohs(ospf_hdr->len)); 296204df0f8Sclaudio return (-1); 297204df0f8Sclaudio } 298204df0f8Sclaudio 29933ad38afSnorby if (iface->type != IF_TYPE_VIRTUALLINK) { 300204df0f8Sclaudio if (ospf_hdr->area_id != iface->area->id.s_addr) { 301204df0f8Sclaudio addr.s_addr = ospf_hdr->area_id; 30233ad38afSnorby log_debug("recv_packet: invalid area ID %s, " 30333ad38afSnorby "interface %s", inet_ntoa(addr), iface->name); 304204df0f8Sclaudio return (-1); 305204df0f8Sclaudio } 30633ad38afSnorby } else { 30733ad38afSnorby if (ospf_hdr->area_id != 0) { 30865d2ce15Sclaudio addr.s_addr = ospf_hdr->area_id; 30933ad38afSnorby log_debug("recv_packet: invalid area ID %s, " 31065d2ce15Sclaudio "interface %s", inet_ntoa(addr), iface->name); 31133ad38afSnorby return (-1); 31233ad38afSnorby } 31333ad38afSnorby } 314204df0f8Sclaudio 315204df0f8Sclaudio if (iface->type == IF_TYPE_BROADCAST || iface->type == IF_TYPE_NBMA) { 316*4f4fe40bSflorian inet_pton(AF_INET, AllDRouters, &addr); 31752339221Sclaudio if (ip_hdr->ip_dst.s_addr == addr.s_addr && 31852339221Sclaudio (iface->state & IF_STA_DRORBDR) == 0) { 319204df0f8Sclaudio log_debug("recv_packet: invalid destination IP in " 320204df0f8Sclaudio "state %s, interface %s", 321204df0f8Sclaudio if_state_name(iface->state), iface->name); 322204df0f8Sclaudio return (-1); 323204df0f8Sclaudio } 324204df0f8Sclaudio } 325204df0f8Sclaudio 326204df0f8Sclaudio return (ntohs(ospf_hdr->len)); 327204df0f8Sclaudio } 328204df0f8Sclaudio 329204df0f8Sclaudio struct iface * 330e29a452cSclaudio find_iface(struct ospfd_conf *xconf, unsigned int ifindex, struct in_addr src) 331204df0f8Sclaudio { 332204df0f8Sclaudio struct area *area = NULL; 333204df0f8Sclaudio struct iface *iface = NULL; 334204df0f8Sclaudio 335204df0f8Sclaudio /* returned interface needs to be active */ 336204df0f8Sclaudio LIST_FOREACH(area, &xconf->area_list, entry) { 337204df0f8Sclaudio LIST_FOREACH(iface, &area->iface_list, entry) { 338e29a452cSclaudio switch (iface->type) { 339e29a452cSclaudio case IF_TYPE_VIRTUALLINK: 340e29a452cSclaudio if ((src.s_addr == iface->dst.s_addr) && 341feb9f399Snorby !iface->passive) 342feb9f399Snorby return (iface); 343e29a452cSclaudio break; 344e29a452cSclaudio case IF_TYPE_POINTOPOINT: 345e29a452cSclaudio if (ifindex == iface->ifindex && 346e29a452cSclaudio !iface->passive) 347204df0f8Sclaudio return (iface); 348e29a452cSclaudio break; 349e29a452cSclaudio default: 350e29a452cSclaudio if (ifindex == iface->ifindex && 351e29a452cSclaudio (iface->addr.s_addr & iface->mask.s_addr) == 352e29a452cSclaudio (src.s_addr & iface->mask.s_addr) && 353e29a452cSclaudio !iface->passive) 35433ad38afSnorby return (iface); 355e29a452cSclaudio break; 356e29a452cSclaudio } 357e29a452cSclaudio } 35833ad38afSnorby } 35933ad38afSnorby 360204df0f8Sclaudio return (NULL); 361204df0f8Sclaudio } 362