1 /* $OpenBSD: packet.c,v 1.14 2017/04/05 14:40:56 reyk Exp $ */ 2 3 /* Packet assembly code, originally contributed by Archie Cobbs. */ 4 5 /* 6 * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of The Internet Software Consortium nor the names 19 * of its contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * This software has been written for the Internet Software Consortium 37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 38 * Enterprises. To learn more about the Internet Software Consortium, 39 * see ``http://www.vix.com/isc''. To learn more about Vixie 40 * Enterprises, see ``http://www.vix.com''. 41 */ 42 43 #include <sys/types.h> 44 #include <sys/socket.h> 45 46 #include <arpa/inet.h> 47 48 #include <net/if.h> 49 #include <net/if_enc.h> 50 51 #include <netinet/in.h> 52 #include <netinet/ip.h> 53 #include <netinet/udp.h> 54 #include <netinet/if_ether.h> 55 56 #include <string.h> 57 58 #include "dhcp.h" 59 #include "dhcpd.h" 60 #include "log.h" 61 62 63 u_int32_t checksum(unsigned char *, u_int32_t, u_int32_t); 64 u_int32_t wrapsum(u_int32_t); 65 66 u_int32_t 67 checksum(unsigned char *buf, u_int32_t nbytes, u_int32_t sum) 68 { 69 u_int32_t i; 70 71 /* Checksum all the pairs of bytes first... */ 72 for (i = 0; i < (nbytes & ~1U); i += 2) { 73 sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i))); 74 if (sum > 0xFFFF) 75 sum -= 0xFFFF; 76 } 77 78 /* 79 * If there's a single byte left over, checksum it, too. 80 * Network byte order is big-endian, so the remaining byte is 81 * the high byte. 82 */ 83 if (i < nbytes) { 84 sum += buf[i] << 8; 85 if (sum > 0xFFFF) 86 sum -= 0xFFFF; 87 } 88 89 return (sum); 90 } 91 92 u_int32_t 93 wrapsum(u_int32_t sum) 94 { 95 sum = ~sum & 0xFFFF; 96 return (htons(sum)); 97 } 98 99 ssize_t 100 assemble_hw_header(unsigned char *buf, size_t buflen, 101 size_t offset, struct packet_ctx *pc, unsigned int intfhtype) 102 { 103 struct ether_header eh; 104 105 switch (intfhtype) { 106 case HTYPE_ETHER: 107 if (buflen < offset + ETHER_HDR_LEN) 108 return (-1); 109 110 /* Use the supplied address or let the kernel fill it. */ 111 memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN); 112 memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN); 113 114 eh.ether_type = htons(ETHERTYPE_IP); 115 116 memcpy(&buf[offset], &eh, ETHER_HDR_LEN); 117 offset += ETHER_HDR_LEN; 118 break; 119 default: 120 return (-1); 121 } 122 123 return (offset); 124 } 125 126 ssize_t 127 assemble_udp_ip_header(unsigned char *buf, size_t buflen, size_t offset, 128 struct packet_ctx *pc, unsigned char *data, size_t datalen) 129 { 130 struct ip ip; 131 struct udphdr udp; 132 133 if (buflen < offset + sizeof(ip) + sizeof(udp)) 134 return (-1); 135 136 ip.ip_v = 4; 137 ip.ip_hl = 5; 138 ip.ip_tos = IPTOS_LOWDELAY; 139 ip.ip_len = htons(sizeof(ip) + sizeof(udp) + datalen); 140 ip.ip_id = 0; 141 ip.ip_off = 0; 142 ip.ip_ttl = 16; 143 ip.ip_p = IPPROTO_UDP; 144 ip.ip_sum = 0; 145 ip.ip_src.s_addr = ss2sin(&pc->pc_src)->sin_addr.s_addr; 146 ip.ip_dst.s_addr = ss2sin(&pc->pc_dst)->sin_addr.s_addr; 147 148 ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 149 memcpy(&buf[offset], &ip, sizeof(ip)); 150 offset += sizeof(ip); 151 152 udp.uh_sport = ss2sin(&pc->pc_src)->sin_port; 153 udp.uh_dport = ss2sin(&pc->pc_dst)->sin_port; 154 udp.uh_ulen = htons(sizeof(udp) + datalen); 155 memset(&udp.uh_sum, 0, sizeof(udp.uh_sum)); 156 157 udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 158 checksum(data, datalen, checksum((unsigned char *)&ip.ip_src, 159 2 * sizeof(ip.ip_src), 160 IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen))))); 161 162 memcpy(&buf[offset], &udp, sizeof(udp)); 163 offset += sizeof(udp); 164 165 return (offset); 166 } 167 168 ssize_t 169 decode_hw_header(unsigned char *buf, size_t buflen, 170 size_t offset, struct packet_ctx *pc, unsigned int intfhtype) 171 { 172 u_int32_t ip_len; 173 struct ip *ip; 174 175 switch (intfhtype) { 176 case HTYPE_IPSEC_TUNNEL: 177 if (buflen < offset + ENC_HDRLEN + sizeof(*ip)) 178 return (-1); 179 offset += ENC_HDRLEN; 180 ip_len = (buf[offset] & 0xf) << 2; 181 if (buflen < offset + ip_len) 182 return (-1); 183 184 ip = (struct ip *)(buf + offset); 185 186 /* Encapsulated IP */ 187 if (ip->ip_p != IPPROTO_IPIP) 188 return (-1); 189 190 memset(pc->pc_dmac, 0xff, ETHER_ADDR_LEN); 191 offset += ip_len; 192 193 pc->pc_htype = ARPHRD_ETHER; 194 pc->pc_hlen = ETHER_ADDR_LEN; 195 break; 196 case HTYPE_ETHER: 197 if (buflen < offset + ETHER_HDR_LEN) 198 return (-1); 199 200 memcpy(pc->pc_dmac, buf + offset, ETHER_ADDR_LEN); 201 memcpy(pc->pc_smac, buf + offset + ETHER_ADDR_LEN, 202 ETHER_ADDR_LEN); 203 offset += ETHER_HDR_LEN; 204 205 pc->pc_htype = ARPHRD_ETHER; 206 pc->pc_hlen = ETHER_ADDR_LEN; 207 break; 208 default: 209 return (-1); 210 } 211 212 return (offset); 213 } 214 215 ssize_t 216 decode_udp_ip_header(unsigned char *buf, size_t buflen, 217 size_t offset, struct packet_ctx *pc) 218 { 219 struct ip *ip; 220 struct udphdr *udp; 221 unsigned char *data; 222 u_int32_t ip_len; 223 u_int32_t sum, usum; 224 static unsigned int ip_packets_seen; 225 static unsigned int ip_packets_bad_checksum; 226 static unsigned int udp_packets_seen; 227 static unsigned int udp_packets_bad_checksum; 228 static unsigned int udp_packets_length_checked; 229 static unsigned int udp_packets_length_overflow; 230 int len; 231 232 /* Assure that an entire IP header is within the buffer. */ 233 if (buflen < offset + sizeof(*ip)) 234 return (-1); 235 ip_len = (buf[offset] & 0xf) << 2; 236 if (buflen < offset + ip_len) 237 return (-1); 238 239 ip = (struct ip *)(buf + offset); 240 ip_packets_seen++; 241 242 /* Check the IP header checksum - it should be zero. */ 243 if (wrapsum(checksum(buf + offset, ip_len, 0)) != 0) { 244 ip_packets_bad_checksum++; 245 if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 && 246 (ip_packets_seen / ip_packets_bad_checksum) < 2) { 247 log_info("%u bad IP checksums seen in %u packets", 248 ip_packets_bad_checksum, ip_packets_seen); 249 ip_packets_seen = ip_packets_bad_checksum = 0; 250 } 251 return (-1); 252 } 253 254 pc->pc_src.ss_len = sizeof(struct sockaddr_in); 255 pc->pc_src.ss_family = AF_INET; 256 memcpy(&ss2sin(&pc->pc_src)->sin_addr, &ip->ip_src, 257 sizeof(ss2sin(&pc->pc_src)->sin_addr)); 258 259 pc->pc_dst.ss_len = sizeof(struct sockaddr_in); 260 pc->pc_dst.ss_family = AF_INET; 261 memcpy(&ss2sin(&pc->pc_dst)->sin_addr, &ip->ip_dst, 262 sizeof(ss2sin(&pc->pc_dst)->sin_addr)); 263 264 #ifdef DEBUG 265 if (buflen != offset + ntohs(ip->ip_len)) 266 log_debug("ip length %d disagrees with bytes received %zd.", 267 ntohs(ip->ip_len), buflen - offset); 268 #endif 269 270 /* Assure that the entire IP packet is within the buffer. */ 271 if (buflen < offset + ntohs(ip->ip_len)) 272 return (-1); 273 274 /* Assure that the UDP header is within the buffer. */ 275 if (buflen < offset + ip_len + sizeof(*udp)) 276 return (-1); 277 udp = (struct udphdr *)(buf + offset + ip_len); 278 udp_packets_seen++; 279 280 /* Assure that the entire UDP packet is within the buffer. */ 281 if (buflen < offset + ip_len + ntohs(udp->uh_ulen)) 282 return (-1); 283 data = buf + offset + ip_len + sizeof(*udp); 284 285 /* 286 * Compute UDP checksums, including the ``pseudo-header'', the 287 * UDP header and the data. If the UDP checksum field is zero, 288 * we're not supposed to do a checksum. 289 */ 290 udp_packets_length_checked++; 291 len = ntohs(udp->uh_ulen) - sizeof(*udp); 292 if ((len < 0) || (len + data > buf + buflen)) { 293 udp_packets_length_overflow++; 294 if (udp_packets_length_checked > 4 && 295 udp_packets_length_overflow != 0 && 296 (udp_packets_length_checked / 297 udp_packets_length_overflow) < 2) { 298 log_info("%u udp packets in %u too long - dropped", 299 udp_packets_length_overflow, 300 udp_packets_length_checked); 301 udp_packets_length_overflow = 302 udp_packets_length_checked = 0; 303 } 304 return (-1); 305 } 306 if (len + data != buf + buflen) 307 log_debug("accepting packet with data after udp payload."); 308 309 usum = udp->uh_sum; 310 udp->uh_sum = 0; 311 312 sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp), 313 checksum(data, len, checksum((unsigned char *)&ip->ip_src, 314 2 * sizeof(ip->ip_src), 315 IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen))))); 316 317 udp_packets_seen++; 318 if (usum && usum != sum) { 319 udp_packets_bad_checksum++; 320 if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 && 321 (udp_packets_seen / udp_packets_bad_checksum) < 2) { 322 log_info("%u bad udp checksums in %u packets", 323 udp_packets_bad_checksum, udp_packets_seen); 324 udp_packets_seen = udp_packets_bad_checksum = 0; 325 } 326 return (-1); 327 } 328 329 ss2sin(&pc->pc_src)->sin_port = udp->uh_sport; 330 ss2sin(&pc->pc_dst)->sin_port = udp->uh_dport; 331 332 return (offset + ip_len + sizeof(*udp)); 333 } 334