1 1.3 manu /* $NetBSD: packet.c,v 1.4 2022/04/03 01:10:58 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* packet.c 4 1.1 christos 5 1.1 christos Packet assembly code, originally contributed by Archie Cobbs. */ 6 1.1 christos 7 1.1 christos /* 8 1.4 christos * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") 9 1.1 christos * Copyright (c) 1996-2003 by Internet Software Consortium 10 1.1 christos * 11 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 12 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 13 1.1 christos * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 1.1 christos * 15 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21 1.1 christos * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 1.1 christos * 23 1.1 christos * Internet Systems Consortium, Inc. 24 1.4 christos * PO Box 360 25 1.4 christos * Newmarket, NH 03857 USA 26 1.1 christos * <info (at) isc.org> 27 1.1 christos * https://www.isc.org/ 28 1.1 christos * 29 1.1 christos * This code was originally contributed by Archie Cobbs, and is still 30 1.1 christos * very similar to that contribution, although the packet checksum code 31 1.1 christos * has been hacked significantly with the help of quite a few ISC DHCP 32 1.1 christos * users, without whose gracious and thorough help the checksum code would 33 1.1 christos * still be disabled. 34 1.1 christos */ 35 1.1 christos 36 1.1 christos #include <sys/cdefs.h> 37 1.3 manu __RCSID("$NetBSD: packet.c,v 1.4 2022/04/03 01:10:58 christos Exp $"); 38 1.1 christos 39 1.1 christos #include "dhcpd.h" 40 1.1 christos 41 1.1 christos #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) 42 1.1 christos #include "includes/netinet/ip.h" 43 1.1 christos #include "includes/netinet/udp.h" 44 1.1 christos #include "includes/netinet/if_ether.h" 45 1.1 christos #endif /* PACKET_ASSEMBLY || PACKET_DECODING */ 46 1.1 christos 47 1.1 christos /* Compute the easy part of the checksum on a range of bytes. */ 48 1.1 christos 49 1.1 christos u_int32_t checksum (buf, nbytes, sum) 50 1.1 christos unsigned char *buf; 51 1.1 christos unsigned nbytes; 52 1.1 christos u_int32_t sum; 53 1.1 christos { 54 1.1 christos unsigned i; 55 1.1 christos 56 1.1 christos #ifdef DEBUG_CHECKSUM 57 1.1 christos log_debug ("checksum (%x %d %x)", (unsigned)buf, nbytes, sum); 58 1.1 christos #endif 59 1.1 christos 60 1.1 christos /* Checksum all the pairs of bytes first... */ 61 1.1 christos for (i = 0; i < (nbytes & ~1U); i += 2) { 62 1.1 christos #ifdef DEBUG_CHECKSUM_VERBOSE 63 1.1 christos log_debug ("sum = %x", sum); 64 1.1 christos #endif 65 1.1 christos sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i))); 66 1.1 christos /* Add carry. */ 67 1.1 christos if (sum > 0xFFFF) 68 1.1 christos sum -= 0xFFFF; 69 1.4 christos } 70 1.1 christos 71 1.1 christos /* If there's a single byte left over, checksum it, too. Network 72 1.1 christos byte order is big-endian, so the remaining byte is the high byte. */ 73 1.1 christos if (i < nbytes) { 74 1.1 christos #ifdef DEBUG_CHECKSUM_VERBOSE 75 1.1 christos log_debug ("sum = %x", sum); 76 1.1 christos #endif 77 1.1 christos sum += buf [i] << 8; 78 1.1 christos /* Add carry. */ 79 1.1 christos if (sum > 0xFFFF) 80 1.1 christos sum -= 0xFFFF; 81 1.1 christos } 82 1.4 christos 83 1.1 christos return sum; 84 1.1 christos } 85 1.1 christos 86 1.1 christos /* Finish computing the checksum, and then put it into network byte order. */ 87 1.1 christos 88 1.1 christos u_int32_t wrapsum (sum) 89 1.1 christos u_int32_t sum; 90 1.1 christos { 91 1.1 christos #ifdef DEBUG_CHECKSUM 92 1.1 christos log_debug ("wrapsum (%x)", sum); 93 1.1 christos #endif 94 1.1 christos 95 1.1 christos sum = ~sum & 0xFFFF; 96 1.1 christos #ifdef DEBUG_CHECKSUM_VERBOSE 97 1.1 christos log_debug ("sum = %x", sum); 98 1.1 christos #endif 99 1.4 christos 100 1.1 christos #ifdef DEBUG_CHECKSUM 101 1.1 christos log_debug ("wrapsum returns %x", htons (sum)); 102 1.1 christos #endif 103 1.1 christos return htons(sum); 104 1.1 christos } 105 1.1 christos 106 1.1 christos #ifdef PACKET_ASSEMBLY 107 1.1 christos void assemble_hw_header (interface, buf, bufix, to) 108 1.1 christos struct interface_info *interface; 109 1.1 christos unsigned char *buf; 110 1.1 christos unsigned *bufix; 111 1.1 christos struct hardware *to; 112 1.1 christos { 113 1.1 christos switch (interface->hw_address.hbuf[0]) { 114 1.1 christos #if defined(HAVE_TR_SUPPORT) 115 1.1 christos case HTYPE_IEEE802: 116 1.1 christos assemble_tr_header(interface, buf, bufix, to); 117 1.1 christos break; 118 1.1 christos #endif 119 1.1 christos #if defined (DEC_FDDI) 120 1.1 christos case HTYPE_FDDI: 121 1.1 christos assemble_fddi_header(interface, buf, bufix, to); 122 1.1 christos break; 123 1.1 christos #endif 124 1.1 christos case HTYPE_INFINIBAND: 125 1.1 christos log_error("Attempt to assemble hw header for infiniband"); 126 1.1 christos break; 127 1.1 christos case HTYPE_ETHER: 128 1.1 christos default: 129 1.1 christos assemble_ethernet_header(interface, buf, bufix, to); 130 1.1 christos break; 131 1.1 christos } 132 1.1 christos } 133 1.1 christos 134 1.1 christos /* UDP header and IP header assembled together for convenience. */ 135 1.1 christos 136 1.1 christos void assemble_udp_ip_header (interface, buf, bufix, 137 1.1 christos from, to, port, data, len) 138 1.1 christos struct interface_info *interface; 139 1.1 christos unsigned char *buf; 140 1.1 christos unsigned *bufix; 141 1.1 christos u_int32_t from; 142 1.1 christos u_int32_t to; 143 1.1 christos u_int32_t port; 144 1.1 christos unsigned char *data; 145 1.1 christos unsigned len; 146 1.1 christos { 147 1.1 christos struct ip ip; 148 1.1 christos struct udphdr udp; 149 1.1 christos 150 1.1 christos memset (&ip, 0, sizeof ip); 151 1.1 christos 152 1.1 christos /* Fill out the IP header */ 153 1.1 christos IP_V_SET (&ip, 4); 154 1.1 christos IP_HL_SET (&ip, 20); 155 1.1 christos ip.ip_tos = IPTOS_LOWDELAY; 156 1.1 christos ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 157 1.1 christos ip.ip_id = 0; 158 1.1 christos ip.ip_off = 0; 159 1.1 christos ip.ip_ttl = 128; 160 1.1 christos ip.ip_p = IPPROTO_UDP; 161 1.1 christos ip.ip_sum = 0; 162 1.1 christos ip.ip_src.s_addr = from; 163 1.1 christos ip.ip_dst.s_addr = to; 164 1.4 christos 165 1.1 christos /* Checksum the IP header... */ 166 1.1 christos ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0)); 167 1.4 christos 168 1.1 christos /* Copy the ip header into the buffer... */ 169 1.1 christos memcpy (&buf [*bufix], &ip, sizeof ip); 170 1.1 christos *bufix += sizeof ip; 171 1.1 christos 172 1.1 christos /* Fill out the UDP header */ 173 1.2 christos udp.uh_sport = *libdhcp_callbacks.local_port; /* XXX */ 174 1.1 christos udp.uh_dport = port; /* XXX */ 175 1.1 christos #if defined(RELAY_PORT) 176 1.1 christos /* Change to relay port defined if sending to server */ 177 1.1 christos if (relay_port && (port == htons(67))) { 178 1.1 christos udp.uh_sport = relay_port; 179 1.1 christos } 180 1.1 christos #endif 181 1.1 christos udp.uh_ulen = htons(sizeof(udp) + len); 182 1.1 christos memset (&udp.uh_sum, 0, sizeof udp.uh_sum); 183 1.1 christos 184 1.1 christos /* Compute UDP checksums, including the ``pseudo-header'', the UDP 185 1.1 christos header and the data. */ 186 1.1 christos 187 1.1 christos udp.uh_sum = 188 1.1 christos wrapsum (checksum ((unsigned char *)&udp, sizeof udp, 189 1.4 christos checksum (data, len, 190 1.1 christos checksum ((unsigned char *) 191 1.1 christos &ip.ip_src, 192 1.1 christos 2 * sizeof ip.ip_src, 193 1.1 christos IPPROTO_UDP + 194 1.1 christos (u_int32_t) 195 1.1 christos ntohs (udp.uh_ulen))))); 196 1.1 christos 197 1.1 christos /* Copy the udp header into the buffer... */ 198 1.1 christos memcpy (&buf [*bufix], &udp, sizeof udp); 199 1.1 christos *bufix += sizeof udp; 200 1.1 christos } 201 1.1 christos #endif /* PACKET_ASSEMBLY */ 202 1.1 christos 203 1.1 christos #ifdef PACKET_DECODING 204 1.1 christos /* Decode a hardware header... */ 205 1.1 christos /* Support for ethernet, TR and FDDI 206 1.1 christos * Doesn't support infiniband yet as the supported oses shouldn't get here 207 1.1 christos */ 208 1.1 christos 209 1.1 christos ssize_t decode_hw_header (interface, buf, bufix, from) 210 1.1 christos struct interface_info *interface; 211 1.1 christos unsigned char *buf; 212 1.1 christos unsigned bufix; 213 1.1 christos struct hardware *from; 214 1.1 christos { 215 1.1 christos switch(interface->hw_address.hbuf[0]) { 216 1.1 christos #if defined (HAVE_TR_SUPPORT) 217 1.1 christos case HTYPE_IEEE802: 218 1.1 christos return (decode_tr_header(interface, buf, bufix, from)); 219 1.1 christos #endif 220 1.1 christos #if defined (DEC_FDDI) 221 1.1 christos case HTYPE_FDDI: 222 1.1 christos return (decode_fddi_header(interface, buf, bufix, from)); 223 1.1 christos #endif 224 1.1 christos case HTYPE_INFINIBAND: 225 1.1 christos log_error("Attempt to decode hw header for infiniband"); 226 1.1 christos return (0); 227 1.1 christos case HTYPE_ETHER: 228 1.1 christos default: 229 1.1 christos return (decode_ethernet_header(interface, buf, bufix, from)); 230 1.1 christos } 231 1.1 christos } 232 1.1 christos 233 1.1 christos /*! 234 1.1 christos * 235 1.1 christos * \brief UDP header and IP header decoded together for convenience. 236 1.1 christos * 237 1.1 christos * Attempt to decode the UDP and IP headers and, if necessary, checksum 238 1.1 christos * the packet. 239 1.1 christos * 240 1.1 christos * \param inteface - the interface on which the packet was recevied 241 1.1 christos * \param buf - a pointer to the buffer for the received packet 242 1.1 christos * \param bufix - where to start processing the buffer, previous 243 1.1 christos * routines may have processed parts of the buffer already 244 1.1 christos * \param from - space to return the address of the packet sender 245 1.1 christos * \param buflen - remaining length of the buffer, this will have been 246 1.1 christos * decremented by bufix by the caller 247 1.1 christos * \param rbuflen - space to return the length of the payload from the udp 248 1.1 christos * header 249 1.1 christos * \param csum_ready - indication if the checksum is valid for use 250 1.1 christos * non-zero indicates the checksum should be validated 251 1.1 christos * 252 1.1 christos * \return - the index to the first byte of the udp payload (that is the 253 1.1 christos * start of the DHCP packet 254 1.1 christos */ 255 1.1 christos 256 1.1 christos ssize_t 257 1.1 christos decode_udp_ip_header(struct interface_info *interface, 258 1.1 christos unsigned char *buf, unsigned bufix, 259 1.1 christos struct sockaddr_in *from, unsigned buflen, 260 1.1 christos unsigned *rbuflen, int csum_ready) 261 1.1 christos { 262 1.1 christos unsigned char *data; 263 1.1 christos struct ip ip; 264 1.1 christos struct udphdr udp; 265 1.1 christos unsigned char *upp; 266 1.1 christos u_int32_t ip_len, ulen, pkt_len; 267 1.1 christos static unsigned int ip_packets_seen = 0; 268 1.1 christos static unsigned int ip_packets_bad_checksum = 0; 269 1.1 christos static unsigned int udp_packets_seen = 0; 270 1.1 christos static unsigned int udp_packets_bad_checksum = 0; 271 1.1 christos static unsigned int udp_packets_length_checked = 0; 272 1.1 christos static unsigned int udp_packets_length_overflow = 0; 273 1.1 christos unsigned len; 274 1.1 christos 275 1.1 christos /* Assure there is at least an IP header there. */ 276 1.1 christos if (sizeof(ip) > buflen) 277 1.1 christos return -1; 278 1.1 christos 279 1.1 christos /* Copy the IP header into a stack aligned structure for inspection. 280 1.1 christos * There may be bits in the IP header that we're not decoding, so we 281 1.1 christos * copy out the bits we grok and skip ahead by ip.ip_hl * 4. 282 1.1 christos */ 283 1.1 christos upp = buf + bufix; 284 1.1 christos memcpy(&ip, upp, sizeof(ip)); 285 1.1 christos ip_len = (*upp & 0x0f) << 2; 286 1.1 christos upp += ip_len; 287 1.1 christos 288 1.1 christos /* Check packet lengths are within the buffer: 289 1.1 christos * first the ip header (ip_len) 290 1.1 christos * then the packet length from the ip header (pkt_len) 291 1.1 christos * then the udp header (ip_len + sizeof(udp) 292 1.1 christos * We are liberal in what we accept, the udp payload should fit within 293 1.1 christos * pkt_len, but we only check against the full buffer size. 294 1.1 christos */ 295 1.1 christos pkt_len = ntohs(ip.ip_len); 296 1.1 christos if ((ip_len > buflen) || 297 1.1 christos (pkt_len > buflen) || 298 1.1 christos ((ip_len + sizeof(udp)) > buflen)) 299 1.1 christos return -1; 300 1.1 christos 301 1.1 christos /* Copy the UDP header into a stack aligned structure for inspection. */ 302 1.1 christos memcpy(&udp, upp, sizeof(udp)); 303 1.1 christos 304 1.1 christos #ifdef USERLAND_FILTER 305 1.1 christos /* Is it a UDP packet? */ 306 1.1 christos if (ip.ip_p != IPPROTO_UDP) 307 1.1 christos return -1; 308 1.1 christos 309 1.1 christos /* Is it to the port we're serving? */ 310 1.1 christos #if defined(RELAY_PORT) 311 1.3 manu if ((udp.uh_dport != *libdhcp_callbacks.local_port) && 312 1.1 christos ((relay_port == 0) || (udp.uh_dport != relay_port))) 313 1.1 christos #else 314 1.3 manu if (udp.uh_dport != *libdhcp_callbacks.local_port) 315 1.1 christos #endif 316 1.1 christos return -1; 317 1.1 christos #endif /* USERLAND_FILTER */ 318 1.1 christos 319 1.1 christos ulen = ntohs(udp.uh_ulen); 320 1.1 christos if (ulen < sizeof(udp)) 321 1.1 christos return -1; 322 1.1 christos 323 1.1 christos udp_packets_length_checked++; 324 1.1 christos /* verify that the payload length from the udp packet fits in the buffer */ 325 1.1 christos if ((ip_len + ulen) > buflen) { 326 1.1 christos udp_packets_length_overflow++; 327 1.1 christos if (((udp_packets_length_checked > 4) && 328 1.1 christos (udp_packets_length_overflow != 0)) && 329 1.1 christos ((udp_packets_length_checked / udp_packets_length_overflow) < 2)) { 330 1.1 christos log_info("%u udp packets in %u too long - dropped", 331 1.1 christos udp_packets_length_overflow, 332 1.1 christos udp_packets_length_checked); 333 1.1 christos udp_packets_length_overflow = 0; 334 1.1 christos udp_packets_length_checked = 0; 335 1.1 christos } 336 1.1 christos return -1; 337 1.1 christos } 338 1.1 christos 339 1.1 christos /* If at least 5 with less than 50% bad, start over */ 340 1.1 christos if (udp_packets_length_checked > 4) { 341 1.1 christos udp_packets_length_overflow = 0; 342 1.1 christos udp_packets_length_checked = 0; 343 1.1 christos } 344 1.1 christos 345 1.1 christos /* Check the IP header checksum - it should be zero. */ 346 1.1 christos ip_packets_seen++; 347 1.1 christos if (wrapsum (checksum (buf + bufix, ip_len, 0))) { 348 1.1 christos ++ip_packets_bad_checksum; 349 1.1 christos if (((ip_packets_seen > 4) && (ip_packets_bad_checksum != 0)) && 350 1.1 christos ((ip_packets_seen / ip_packets_bad_checksum) < 2)) { 351 1.1 christos log_info ("%u bad IP checksums seen in %u packets", 352 1.1 christos ip_packets_bad_checksum, ip_packets_seen); 353 1.1 christos ip_packets_seen = ip_packets_bad_checksum = 0; 354 1.1 christos } 355 1.1 christos return -1; 356 1.1 christos } 357 1.1 christos 358 1.1 christos /* If at least 5 with less than 50% bad, start over */ 359 1.1 christos if (ip_packets_seen > 4) { 360 1.1 christos ip_packets_bad_checksum = 0; 361 1.1 christos ip_packets_seen = 0; 362 1.1 christos } 363 1.1 christos 364 1.1 christos /* Copy out the IP source address... */ 365 1.1 christos memcpy(&from->sin_addr, &ip.ip_src, 4); 366 1.1 christos 367 1.1 christos data = upp + sizeof(udp); 368 1.1 christos len = ulen - sizeof(udp); 369 1.1 christos 370 1.1 christos /* UDP check sum may be optional (udp.uh_sum == 0) or not ready if checksum 371 1.1 christos * offloading is in use */ 372 1.1 christos udp_packets_seen++; 373 1.1 christos if (udp.uh_sum && csum_ready) { 374 1.1 christos /* Check the UDP header checksum - since the received packet header 375 1.1 christos * contains the UDP checksum calculated by the transmitter, calculating 376 1.1 christos * it now should come out to zero. */ 377 1.1 christos if (wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 378 1.1 christos checksum(data, len, 379 1.1 christos checksum((unsigned char *)&ip.ip_src, 380 1.1 christos 8, IPPROTO_UDP + ulen))))) { 381 1.1 christos udp_packets_bad_checksum++; 382 1.1 christos if (((udp_packets_seen > 4) && (udp_packets_bad_checksum != 0)) 383 1.1 christos && ((udp_packets_seen / udp_packets_bad_checksum) < 2)) { 384 1.1 christos log_debug ("%u bad udp checksums in %u packets", 385 1.1 christos udp_packets_bad_checksum, udp_packets_seen); 386 1.1 christos udp_packets_seen = udp_packets_bad_checksum = 0; 387 1.1 christos } 388 1.1 christos 389 1.1 christos return -1; 390 1.1 christos } 391 1.1 christos } 392 1.1 christos 393 1.1 christos /* If at least 5 with less than 50% bad, start over */ 394 1.1 christos if (udp_packets_seen > 4) { 395 1.1 christos udp_packets_bad_checksum = 0; 396 1.1 christos udp_packets_seen = 0; 397 1.1 christos } 398 1.1 christos 399 1.1 christos /* Copy out the port... */ 400 1.1 christos memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport); 401 1.1 christos 402 1.1 christos /* Save the length of the UDP payload. */ 403 1.1 christos if (rbuflen != NULL) 404 1.1 christos *rbuflen = len; 405 1.1 christos 406 1.1 christos /* Return the index to the UDP payload. */ 407 1.1 christos return ip_len + sizeof udp; 408 1.1 christos } 409 1.1 christos #endif /* PACKET_DECODING */ 410