1/* packet.c 2 3 Packet assembly code, originally contributed by Archie Cobbs. */ 4 5/* 6 * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC") 7 * Copyright (c) 1996-2003 by Internet Software Consortium 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 * 21 * Internet Systems Consortium, Inc. 22 * 950 Charter Street 23 * Redwood City, CA 94063 24 * <info@isc.org> 25 * http://www.isc.org/ 26 * 27 * This code was originally contributed by Archie Cobbs, and is still 28 * very similar to that contribution, although the packet checksum code 29 * has been hacked significantly with the help of quite a few ISC DHCP 30 * users, without whose gracious and thorough help the checksum code would 31 * still be disabled. 32 */ 33 34#ifndef lint 35static char copyright[] = 36"$Id: packet.c,v 1.7 2005/08/11 17:13:21 drochner Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n"; 37#endif /* not lint */ 38 39#include "dhcpd.h" 40 41#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) 42#include "includes/netinet/ip.h" 43#include "includes/netinet/udp.h" 44#include "includes/netinet/if_ether.h" 45#endif /* PACKET_ASSEMBLY || PACKET_DECODING */ 46 47/* Compute the easy part of the checksum on a range of bytes. */ 48 49u_int32_t checksum (buf, nbytes, sum) 50 unsigned char *buf; 51 unsigned nbytes; 52 u_int32_t sum; 53{ 54 unsigned i; 55 56#ifdef DEBUG_CHECKSUM 57 log_debug ("checksum (%x %d %x)", buf, nbytes, sum); 58#endif 59 60 /* Checksum all the pairs of bytes first... */ 61 for (i = 0; i < (nbytes & ~1U); i += 2) { 62#ifdef DEBUG_CHECKSUM_VERBOSE 63 log_debug ("sum = %x", sum); 64#endif 65 sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i))); 66 /* Add carry. */ 67 if (sum > 0xFFFF) 68 sum -= 0xFFFF; 69 } 70 71 /* If there's a single byte left over, checksum it, too. Network 72 byte order is big-endian, so the remaining byte is the high byte. */ 73 if (i < nbytes) { 74#ifdef DEBUG_CHECKSUM_VERBOSE 75 log_debug ("sum = %x", sum); 76#endif 77 sum += buf [i] << 8; 78 /* Add carry. */ 79 if (sum > 0xFFFF) 80 sum -= 0xFFFF; 81 } 82 83 return sum; 84} 85 86/* Finish computing the checksum, and then put it into network byte order. */ 87 88u_int32_t wrapsum (sum) 89 u_int32_t sum; 90{ 91#ifdef DEBUG_CHECKSUM 92 log_debug ("wrapsum (%x)", sum); 93#endif 94 95 sum = ~sum & 0xFFFF; 96#ifdef DEBUG_CHECKSUM_VERBOSE 97 log_debug ("sum = %x", sum); 98#endif 99 100#ifdef DEBUG_CHECKSUM 101 log_debug ("wrapsum returns %x", htons (sum)); 102#endif 103 return htons(sum); 104} 105 106#ifdef PACKET_ASSEMBLY 107void assemble_hw_header (interface, buf, bufix, to) 108 struct interface_info *interface; 109 unsigned char *buf; 110 unsigned *bufix; 111 struct hardware *to; 112{ 113#if defined (HAVE_TR_SUPPORT) 114 if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802) 115 assemble_tr_header (interface, buf, bufix, to); 116 else 117#endif 118#if defined (DEC_FDDI) || defined (NETBSD_FDDI) 119 if (interface -> hw_address.hbuf [0] == HTYPE_FDDI) 120 assemble_fddi_header (interface, buf, bufix, to); 121 else 122#endif 123 assemble_ethernet_header (interface, buf, bufix, to); 124 125} 126 127/* UDP header and IP header assembled together for convenience. */ 128 129void assemble_udp_ip_header (interface, buf, bufix, 130 from, to, port, data, len) 131 struct interface_info *interface; 132 unsigned char *buf; 133 unsigned *bufix; 134 u_int32_t from; 135 u_int32_t to; 136 u_int32_t port; 137 unsigned char *data; 138 unsigned len; 139{ 140 struct ip ip; 141 struct udphdr udp; 142 143 /* Fill out the IP header */ 144 IP_V_SET (&ip, 4); 145 IP_HL_SET (&ip, 20); 146 ip.ip_tos = IPTOS_LOWDELAY; 147 ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 148 ip.ip_id = 0; 149 ip.ip_off = 0; 150 ip.ip_ttl = 32; 151 ip.ip_p = IPPROTO_UDP; 152 ip.ip_sum = 0; 153 ip.ip_src.s_addr = from; 154 ip.ip_dst.s_addr = to; 155 156 /* Checksum the IP header... */ 157 ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0)); 158 159 /* Copy the ip header into the buffer... */ 160 memcpy (&buf [*bufix], &ip, sizeof ip); 161 *bufix += sizeof ip; 162 163 /* Fill out the UDP header */ 164 udp.uh_sport = local_port; /* XXX */ 165 udp.uh_dport = port; /* XXX */ 166 udp.uh_ulen = htons(sizeof(udp) + len); 167 memset (&udp.uh_sum, 0, sizeof udp.uh_sum); 168 169 /* Compute UDP checksums, including the ``pseudo-header'', the UDP 170 header and the data. */ 171 172 udp.uh_sum = 173 wrapsum (checksum ((unsigned char *)&udp, sizeof udp, 174 checksum (data, len, 175 checksum ((unsigned char *) 176 &ip.ip_src, 177 2 * sizeof ip.ip_src, 178 IPPROTO_UDP + 179 (u_int32_t) 180 ntohs (udp.uh_ulen))))); 181 182 /* Copy the udp header into the buffer... */ 183 memcpy (&buf [*bufix], &udp, sizeof udp); 184 *bufix += sizeof udp; 185} 186#endif /* PACKET_ASSEMBLY */ 187 188#ifdef PACKET_DECODING 189/* Decode a hardware header... */ 190/* XXX currently only supports ethernet; doesn't check for other types. */ 191 192ssize_t decode_hw_header (interface, buf, bufix, from) 193 struct interface_info *interface; 194 unsigned char *buf; 195 unsigned bufix; 196 struct hardware *from; 197{ 198#if defined (HAVE_TR_SUPPORT) 199 if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802) 200 return decode_tr_header (interface, buf, bufix, from); 201 else 202#endif 203#if defined (DEC_FDDI) || defined (NETBSD_FDDI) 204 if (interface -> hw_address.hbuf [0] == HTYPE_FDDI) 205 return decode_fddi_header (interface, buf, bufix, from); 206 else 207#endif 208 return decode_ethernet_header (interface, buf, bufix, from); 209} 210 211/* UDP header and IP header decoded together for convenience. */ 212 213ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen, rbuflen) 214 struct interface_info *interface; 215 unsigned char *buf; 216 unsigned bufix; 217 struct sockaddr_in *from; 218 unsigned buflen; 219 unsigned *rbuflen; 220{ 221 unsigned char *data; 222 struct ip ip; 223 struct udphdr *udp; 224 u_int32_t ip_len = (buf [bufix] & 0xf) << 2; 225 u_int32_t sum, usum; 226 static int ip_packets_seen; 227 static int ip_packets_bad_checksum; 228 static int udp_packets_seen; 229 static int udp_packets_bad_checksum; 230 static int udp_packets_length_checked; 231 static int udp_packets_length_overflow; 232 unsigned len; 233 unsigned ulen; 234 int ignore = 0; 235 236 memcpy(&ip, buf + bufix, sizeof (struct ip)); 237 udp = (struct udphdr *)(buf + bufix + ip_len); 238 len = 0; /* XXXGCC -Wuninitialized */ 239 240#ifdef USERLAND_FILTER 241 /* Is it a UDP packet? */ 242 if (ip.ip_p != IPPROTO_UDP) 243 return -1; 244 245 /* Is it to the port we're serving? */ 246 if (udp -> uh_dport != local_port) 247 return -1; 248#endif /* USERLAND_FILTER */ 249 250 ulen = ntohs (udp -> uh_ulen); 251 if (ulen < sizeof *udp || 252 ((unsigned char *)udp) + ulen > buf + bufix + buflen) { 253 log_info ("bogus UDP packet length: %d", ulen); 254 return -1; 255 } 256 257 /* Check the IP header checksum - it should be zero. */ 258 ++ip_packets_seen; 259 if (wrapsum (checksum (buf + bufix, ip_len, 0))) { 260 ++ip_packets_bad_checksum; 261 if (ip_packets_seen > 4 && 262 (ip_packets_seen / ip_packets_bad_checksum) < 2) { 263 log_info ("%d bad IP checksums seen in %d packets", 264 ip_packets_bad_checksum, ip_packets_seen); 265 ip_packets_seen = ip_packets_bad_checksum = 0; 266 } 267 return -1; 268 } 269 270 /* Check the IP packet length. */ 271 if (ntohs (ip.ip_len) != buflen) { 272 if ((ntohs (ip.ip_len + 2) & ~1) == buflen) 273 ignore = 1; 274 else 275 log_debug ("ip length %d disagrees with bytes received %d.", 276 ntohs (ip.ip_len), buflen); 277 } 278 279 /* Copy out the IP source address... */ 280 memcpy (&from -> sin_addr, &ip.ip_src, 4); 281 282 /* Compute UDP checksums, including the ``pseudo-header'', the UDP 283 header and the data. If the UDP checksum field is zero, we're 284 not supposed to do a checksum. */ 285 286 data = buf + bufix + ip_len + sizeof *udp; 287 len = ulen - sizeof *udp; 288 ++udp_packets_length_checked; 289 if (len + data > buf + bufix + buflen) { 290 ++udp_packets_length_overflow; 291 if (udp_packets_length_checked > 4 && 292 (udp_packets_length_checked / 293 udp_packets_length_overflow) < 2) { 294 log_info ("%d udp packets in %d too long - dropped", 295 udp_packets_length_overflow, 296 udp_packets_length_checked); 297 udp_packets_length_overflow = 298 udp_packets_length_checked = 0; 299 } 300 return -1; 301 } 302 if (len + data < buf + bufix + buflen && 303 len + data != buf + bufix + buflen && !ignore) 304 log_debug ("accepting packet with data after udp payload."); 305 if (len + data > buf + bufix + buflen) { 306 log_debug ("dropping packet with bogus uh_ulen %ld", 307 (long)(len + sizeof *udp)); 308 return -1; 309 } 310 311 usum = udp -> uh_sum; 312 udp -> uh_sum = 0; 313 314 sum = wrapsum (checksum ((unsigned char *)udp, sizeof *udp, 315 checksum (data, len, 316 checksum ((unsigned char *) 317 &ip.ip_src, 318 2 * sizeof ip.ip_src, 319 IPPROTO_UDP + 320 (u_int32_t)ulen)))); 321 322 udp_packets_seen++; 323 if (usum && usum != sum) { 324 udp_packets_bad_checksum++; 325 if (udp_packets_seen > 4 && 326 (udp_packets_seen / udp_packets_bad_checksum) < 2) { 327 log_info ("%d bad udp checksums in %d packets", 328 udp_packets_bad_checksum, udp_packets_seen); 329 udp_packets_seen = udp_packets_bad_checksum = 0; 330 } 331 return -1; 332 } 333 334 /* Copy out the port... */ 335 memcpy (&from -> sin_port, &udp -> uh_sport, sizeof udp -> uh_sport); 336 337 *rbuflen = ntohs (ip.ip_len) - ip_len - sizeof *udp; 338 return ip_len + sizeof *udp; 339} 340#endif /* PACKET_DECODING */ 341