1*2471dd62Sflorian /* $OpenBSD: packet.c,v 1.9 2024/08/21 09:18:47 florian Exp $ */ 2978e5cffSnorby 3978e5cffSnorby /* 4978e5cffSnorby * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org> 5978e5cffSnorby * 6978e5cffSnorby * Permission to use, copy, modify, and distribute this software for any 7978e5cffSnorby * purpose with or without fee is hereby granted, provided that the above 8978e5cffSnorby * copyright notice and this permission notice appear in all copies. 9978e5cffSnorby * 10978e5cffSnorby * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11978e5cffSnorby * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12978e5cffSnorby * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13978e5cffSnorby * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14978e5cffSnorby * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15978e5cffSnorby * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16978e5cffSnorby * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17978e5cffSnorby */ 18978e5cffSnorby 19978e5cffSnorby #include <sys/types.h> 20978e5cffSnorby #include <sys/socket.h> 21978e5cffSnorby #include <sys/time.h> 22978e5cffSnorby 23978e5cffSnorby #include <netinet/in.h> 24978e5cffSnorby #include <netinet/ip.h> 25978e5cffSnorby #include <netinet/ip_mroute.h> 26978e5cffSnorby #include <arpa/inet.h> 27978e5cffSnorby 28978e5cffSnorby #include <errno.h> 29978e5cffSnorby #include <event.h> 3035f7e862Sclaudio #include <stddef.h> 31978e5cffSnorby #include <stdlib.h> 32b53a5054Smmcc #include <string.h> 33978e5cffSnorby 34978e5cffSnorby #include "igmp.h" 35978e5cffSnorby #include "dvmrpd.h" 36978e5cffSnorby #include "dvmrp.h" 37978e5cffSnorby #include "log.h" 38978e5cffSnorby #include "dvmrpe.h" 39978e5cffSnorby 40978e5cffSnorby int ip_hdr_sanity_check(const struct ip *, u_int16_t); 41978e5cffSnorby int dvmrp_hdr_sanity_check(const struct ip *, struct dvmrp_hdr *, 42978e5cffSnorby u_int16_t, const struct iface *); 43978e5cffSnorby struct iface *find_iface(struct dvmrpd_conf *, struct in_addr); 44978e5cffSnorby 452d4baefbSclaudio static u_int8_t *recv_buf; 46978e5cffSnorby 47978e5cffSnorby int 48e39620e5Snicm gen_dvmrp_hdr(struct ibuf *buf, struct iface *iface, u_int8_t code) 49978e5cffSnorby { 50978e5cffSnorby struct dvmrp_hdr dvmrp_hdr; 51978e5cffSnorby 52b206500fSmmcc memset(&dvmrp_hdr, 0, sizeof(dvmrp_hdr)); 53978e5cffSnorby dvmrp_hdr.type = PKT_TYPE_DVMRP; 54978e5cffSnorby dvmrp_hdr.code = code; 55978e5cffSnorby dvmrp_hdr.chksum = 0; /* updated later */ 56978e5cffSnorby dvmrp_hdr.capabilities = DVMRP_CAP_DEFAULT; /* XXX update */ 57978e5cffSnorby dvmrp_hdr.minor_version = DVMRP_MINOR_VERSION; 58978e5cffSnorby dvmrp_hdr.major_version = DVMRP_MAJOR_VERSION; 59978e5cffSnorby 60e39620e5Snicm return (ibuf_add(buf, &dvmrp_hdr, sizeof(dvmrp_hdr))); 61978e5cffSnorby } 62978e5cffSnorby 63978e5cffSnorby /* send and receive packets */ 64978e5cffSnorby int 6535f7e862Sclaudio send_packet(struct iface *iface, struct ibuf *pkt, struct sockaddr_in *dst) 66978e5cffSnorby { 6735f7e862Sclaudio u_int16_t chksum; 6835f7e862Sclaudio 69978e5cffSnorby if (iface->passive) { 70978e5cffSnorby log_warnx("send_packet: cannot send packet on passive " 71978e5cffSnorby "interface %s", iface->name); 72978e5cffSnorby return (-1); 73978e5cffSnorby } 74978e5cffSnorby 75978e5cffSnorby /* set outgoing interface for multicast traffic */ 76978e5cffSnorby if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) 77978e5cffSnorby if (if_set_mcast(iface) == -1) { 78978e5cffSnorby log_warn("send_packet: error setting multicast " 79978e5cffSnorby "interface, %s", iface->name); 80978e5cffSnorby return (-1); 81978e5cffSnorby } 82978e5cffSnorby 8335f7e862Sclaudio /* update chksum */ 8435f7e862Sclaudio chksum = in_cksum(ibuf_data(pkt), ibuf_size(pkt)); 8535f7e862Sclaudio if (ibuf_set(pkt, offsetof(struct dvmrp_hdr, chksum), 8635f7e862Sclaudio &chksum, sizeof(chksum)) == -1) { 8735f7e862Sclaudio log_warn("send_packet: failed to update checksum"); 8835f7e862Sclaudio return (-1); 8935f7e862Sclaudio } 9035f7e862Sclaudio 9135f7e862Sclaudio if (sendto(iface->fd, ibuf_data(pkt), ibuf_size(pkt), 0, 92978e5cffSnorby (struct sockaddr *)dst, sizeof(*dst)) == -1 ) { 93978e5cffSnorby log_warn("send_packet: error sending packet on interface %s", 94978e5cffSnorby iface->name); 95978e5cffSnorby return (-1); 96978e5cffSnorby } 97978e5cffSnorby 98978e5cffSnorby return (0); 99978e5cffSnorby } 100978e5cffSnorby 101978e5cffSnorby void 102978e5cffSnorby recv_packet(int fd, short event, void *bula) 103978e5cffSnorby { 104978e5cffSnorby struct dvmrpd_conf *xconf = bula; 105978e5cffSnorby struct ip ip_hdr; 106978e5cffSnorby struct dvmrp_hdr *dvmrp_hdr; 107978e5cffSnorby struct iface *iface; 108978e5cffSnorby struct nbr *nbr = NULL; 109978e5cffSnorby struct in_addr addr; 110978e5cffSnorby char *buf; 111978e5cffSnorby ssize_t r; 112978e5cffSnorby u_int16_t len; 113978e5cffSnorby int l; 114978e5cffSnorby 115978e5cffSnorby if (event != EV_READ) 116978e5cffSnorby return; 117978e5cffSnorby 1182d4baefbSclaudio if (recv_buf == NULL) 1192d4baefbSclaudio if ((recv_buf = malloc(READ_BUF_SIZE)) == NULL) 1202d4baefbSclaudio fatal(__func__); 1212d4baefbSclaudio 1222d4baefbSclaudio buf = recv_buf; 1232d4baefbSclaudio if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) { 124978e5cffSnorby if (errno != EAGAIN && errno != EINTR) 125978e5cffSnorby log_debug("recv_packet: error receiving packet"); 126978e5cffSnorby return; 127978e5cffSnorby } 128978e5cffSnorby 129978e5cffSnorby len = (u_int16_t)r; 130978e5cffSnorby 131978e5cffSnorby /* IP header sanity checks */ 132978e5cffSnorby if (len < sizeof(ip_hdr)) { 133978e5cffSnorby log_warnx("recv_packet: bad packet size"); 134978e5cffSnorby return; 135978e5cffSnorby } 136978e5cffSnorby 137978e5cffSnorby memcpy(&ip_hdr, buf, sizeof(ip_hdr)); 138978e5cffSnorby if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1) 139978e5cffSnorby return; 140978e5cffSnorby buf += l; 141978e5cffSnorby len -= l; 142978e5cffSnorby 143978e5cffSnorby /* find a matching interface */ 144978e5cffSnorby if ((iface = find_iface(xconf, ip_hdr.ip_src)) == NULL) { 145978e5cffSnorby log_debug("recv_packet: cannot find valid interface, ip src %s", 146978e5cffSnorby inet_ntoa(ip_hdr.ip_src)); 147978e5cffSnorby return; 148978e5cffSnorby } 149978e5cffSnorby 150978e5cffSnorby /* header sanity checks */ 151978e5cffSnorby if (len < sizeof(*dvmrp_hdr)) { 152978e5cffSnorby log_warnx("recv_packet: bad packet size"); 153978e5cffSnorby return; 154978e5cffSnorby } 155978e5cffSnorby dvmrp_hdr = (struct dvmrp_hdr *)buf; 156978e5cffSnorby 157978e5cffSnorby switch (dvmrp_hdr->type) { 158978e5cffSnorby /* DVMRP */ 159978e5cffSnorby case PKT_TYPE_DVMRP: 160978e5cffSnorby if ((l = dvmrp_hdr_sanity_check(&ip_hdr, dvmrp_hdr, len, 161978e5cffSnorby iface)) == -1) 162978e5cffSnorby return; 163978e5cffSnorby 164978e5cffSnorby /* 165978e5cffSnorby * mrouted compat 166978e5cffSnorby * 167978e5cffSnorby * Old mrouted versions, send route reports before establishing 168978e5cffSnorby * 2-WAY neighbor relationships. 169978e5cffSnorby */ 170978e5cffSnorby if ((nbr_find_ip(iface, ip_hdr.ip_src.s_addr) == NULL) && 171978e5cffSnorby (dvmrp_hdr->code == DVMRP_CODE_REPORT)) { 172978e5cffSnorby log_debug("recv_packet: route report from neighbor" 173978e5cffSnorby " ID %s, compat", inet_ntoa(ip_hdr.ip_src)); 174978e5cffSnorby nbr = nbr_new(ip_hdr.ip_src.s_addr, iface, 0); 175978e5cffSnorby nbr_fsm(nbr, NBR_EVT_PROBE_RCVD); 176978e5cffSnorby nbr->compat = 1; 177978e5cffSnorby nbr->addr = ip_hdr.ip_src; 178978e5cffSnorby } 179978e5cffSnorby 180978e5cffSnorby if ((dvmrp_hdr->type == PKT_TYPE_DVMRP) && 181978e5cffSnorby (dvmrp_hdr->code != DVMRP_CODE_PROBE)) 182978e5cffSnorby /* find neighbor */ 183978e5cffSnorby if ((nbr = nbr_find_ip(iface, ip_hdr.ip_src.s_addr)) 184978e5cffSnorby == NULL) { 185978e5cffSnorby log_debug("recv_packet: unknown neighbor ID"); 186978e5cffSnorby return; 187978e5cffSnorby } 188978e5cffSnorby 189978e5cffSnorby buf += sizeof(*dvmrp_hdr); 190978e5cffSnorby len = l - sizeof(*dvmrp_hdr); 191978e5cffSnorby 192*2471dd62Sflorian inet_pton(AF_INET, AllDVMRPRouters, &addr); 193978e5cffSnorby if ((ip_hdr.ip_dst.s_addr != addr.s_addr) && 194978e5cffSnorby (ip_hdr.ip_dst.s_addr != iface->addr.s_addr)) { 195978e5cffSnorby log_debug("recv_packet: interface %s, invalid" 196978e5cffSnorby " destination IP address %s", iface->name, 197978e5cffSnorby inet_ntoa(ip_hdr.ip_dst)); 198978e5cffSnorby break; 199978e5cffSnorby } 200978e5cffSnorby 201978e5cffSnorby switch (dvmrp_hdr->code) { 202978e5cffSnorby case DVMRP_CODE_PROBE: 203978e5cffSnorby recv_probe(iface, ip_hdr.ip_src, ip_hdr.ip_src.s_addr, 204978e5cffSnorby dvmrp_hdr->capabilities, buf, len); 205978e5cffSnorby break; 206978e5cffSnorby case DVMRP_CODE_REPORT: 207978e5cffSnorby recv_report(nbr, buf, len); 208978e5cffSnorby break; 209978e5cffSnorby case DVMRP_CODE_ASK_NBRS2: 210978e5cffSnorby recv_ask_nbrs2(nbr, buf,len); 211978e5cffSnorby break; 212978e5cffSnorby case DVMRP_CODE_NBRS2: 213978e5cffSnorby recv_nbrs2(nbr, buf,len); 214978e5cffSnorby break; 215978e5cffSnorby case DVMRP_CODE_PRUNE: 216978e5cffSnorby recv_prune(nbr, buf, len); 217978e5cffSnorby break; 218978e5cffSnorby case DVMRP_CODE_GRAFT: 219978e5cffSnorby recv_graft(nbr, buf,len); 220978e5cffSnorby break; 221978e5cffSnorby case DVMRP_CODE_GRAFT_ACK: 222978e5cffSnorby recv_graft_ack(nbr, buf,len); 223978e5cffSnorby break; 224978e5cffSnorby default: 225978e5cffSnorby log_debug("recv_packet: unknown DVMRP packet type, " 226978e5cffSnorby "interface %s", iface->name); 227978e5cffSnorby } 228978e5cffSnorby break; 229978e5cffSnorby /* IGMP */ 230978e5cffSnorby case PKT_TYPE_MEMBER_QUERY: 231978e5cffSnorby recv_igmp_query(iface, ip_hdr.ip_src, buf, len); 232978e5cffSnorby break; 233978e5cffSnorby case PKT_TYPE_MEMBER_REPORTv1: 234978e5cffSnorby case PKT_TYPE_MEMBER_REPORTv2: 235978e5cffSnorby recv_igmp_report(iface, ip_hdr.ip_src, buf, len, 236978e5cffSnorby dvmrp_hdr->type); 237978e5cffSnorby break; 238978e5cffSnorby case PKT_TYPE_LEAVE_GROUPv2: 239978e5cffSnorby recv_igmp_leave(iface, ip_hdr.ip_src, buf, len); 240978e5cffSnorby break; 241978e5cffSnorby default: 242978e5cffSnorby log_debug("recv_packet: unknown IGMP packet type, interface %s", 243978e5cffSnorby iface->name); 244978e5cffSnorby } 245978e5cffSnorby } 246978e5cffSnorby 247978e5cffSnorby int 248978e5cffSnorby ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len) 249978e5cffSnorby { 250978e5cffSnorby if (ntohs(ip_hdr->ip_len) != len) { 251978e5cffSnorby log_debug("recv_packet: invalid IP packet length %u", 252978e5cffSnorby ntohs(ip_hdr->ip_len)); 253978e5cffSnorby return (-1); 254978e5cffSnorby } 255978e5cffSnorby 256978e5cffSnorby if (ip_hdr->ip_p != IPPROTO_IGMP) 257978e5cffSnorby /* this is enforced by the socket itself */ 258978e5cffSnorby fatalx("recv_packet: invalid IP proto"); 259978e5cffSnorby 260978e5cffSnorby return (ip_hdr->ip_hl << 2); 261978e5cffSnorby } 262978e5cffSnorby 263978e5cffSnorby int 264978e5cffSnorby dvmrp_hdr_sanity_check(const struct ip *ip_hdr, struct dvmrp_hdr *dvmrp_hdr, 265978e5cffSnorby u_int16_t len, const struct iface *iface) 266978e5cffSnorby { 267978e5cffSnorby /* we only support DVMRPv3 */ 268978e5cffSnorby if (dvmrp_hdr->major_version != DVMRP_MAJOR_VERSION) { 269978e5cffSnorby log_debug("recv_packet: invalid DVMRP version"); 270978e5cffSnorby return (-1); 271978e5cffSnorby } 272978e5cffSnorby 273978e5cffSnorby /* XXX enforce minor version as well, but not yet */ 274978e5cffSnorby 275978e5cffSnorby /* XXX chksum */ 276978e5cffSnorby 277978e5cffSnorby return (len); 278978e5cffSnorby } 279978e5cffSnorby 280978e5cffSnorby struct iface * 281978e5cffSnorby find_iface(struct dvmrpd_conf *xconf, struct in_addr src) 282978e5cffSnorby { 283978e5cffSnorby struct iface *iface = NULL; 284978e5cffSnorby 285978e5cffSnorby /* returned interface needs to be active */ 286978e5cffSnorby LIST_FOREACH(iface, &xconf->iface_list, entry) { 287978e5cffSnorby if (iface->fd > 0 && 288978e5cffSnorby (iface->type == IF_TYPE_POINTOPOINT) && 289978e5cffSnorby (iface->dst.s_addr == src.s_addr) && 290978e5cffSnorby !iface->passive) 291978e5cffSnorby return (iface); 292978e5cffSnorby 293978e5cffSnorby if (iface->fd > 0 && (iface->addr.s_addr & 294978e5cffSnorby iface->mask.s_addr) == (src.s_addr & 295978e5cffSnorby iface->mask.s_addr) && !iface->passive) 296978e5cffSnorby return (iface); 297978e5cffSnorby } 298978e5cffSnorby 299978e5cffSnorby return (NULL); 300978e5cffSnorby } 301