1 /* $OpenBSD: packet.c,v 1.4 2021/06/16 16:55:02 dv 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 "vmd.h" 60 61 u_int32_t checksum(unsigned char *, u_int32_t, u_int32_t); 62 u_int32_t wrapsum(u_int32_t); 63 64 u_int32_t 65 checksum(unsigned char *buf, u_int32_t nbytes, u_int32_t sum) 66 { 67 u_int32_t i; 68 69 /* Checksum all the pairs of bytes first... */ 70 for (i = 0; i < (nbytes & ~1U); i += 2) { 71 sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i))); 72 if (sum > 0xFFFF) 73 sum -= 0xFFFF; 74 } 75 76 /* 77 * If there's a single byte left over, checksum it, too. 78 * Network byte order is big-endian, so the remaining byte is 79 * the high byte. 80 */ 81 if (i < nbytes) { 82 sum += buf[i] << 8; 83 if (sum > 0xFFFF) 84 sum -= 0xFFFF; 85 } 86 87 return (sum); 88 } 89 90 u_int32_t 91 wrapsum(u_int32_t sum) 92 { 93 sum = ~sum & 0xFFFF; 94 return (htons(sum)); 95 } 96 97 ssize_t 98 assemble_hw_header(unsigned char *buf, size_t buflen, 99 size_t offset, struct packet_ctx *pc, unsigned int intfhtype) 100 { 101 struct ether_header eh; 102 103 switch (intfhtype) { 104 case HTYPE_ETHER: 105 if (buflen < offset + ETHER_HDR_LEN) 106 return (-1); 107 108 /* Use the supplied address or let the kernel fill it. */ 109 memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN); 110 memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN); 111 112 eh.ether_type = htons(ETHERTYPE_IP); 113 114 memcpy(&buf[offset], &eh, ETHER_HDR_LEN); 115 offset += ETHER_HDR_LEN; 116 break; 117 default: 118 return (-1); 119 } 120 121 return (offset); 122 } 123 124 ssize_t 125 assemble_udp_ip_header(unsigned char *buf, size_t buflen, size_t offset, 126 struct packet_ctx *pc, unsigned char *data, size_t datalen) 127 { 128 struct ip ip; 129 struct udphdr udp; 130 131 if (buflen < offset + sizeof(ip) + sizeof(udp)) 132 return (-1); 133 134 ip.ip_v = 4; 135 ip.ip_hl = 5; 136 ip.ip_tos = IPTOS_LOWDELAY; 137 ip.ip_len = htons(sizeof(ip) + sizeof(udp) + datalen); 138 ip.ip_id = 0; 139 ip.ip_off = 0; 140 ip.ip_ttl = 16; 141 ip.ip_p = IPPROTO_UDP; 142 ip.ip_sum = 0; 143 ip.ip_src.s_addr = ss2sin(&pc->pc_src)->sin_addr.s_addr; 144 ip.ip_dst.s_addr = ss2sin(&pc->pc_dst)->sin_addr.s_addr; 145 146 ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 147 memcpy(&buf[offset], &ip, sizeof(ip)); 148 offset += sizeof(ip); 149 150 udp.uh_sport = ss2sin(&pc->pc_src)->sin_port; 151 udp.uh_dport = ss2sin(&pc->pc_dst)->sin_port; 152 udp.uh_ulen = htons(sizeof(udp) + datalen); 153 memset(&udp.uh_sum, 0, sizeof(udp.uh_sum)); 154 155 udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 156 checksum(data, datalen, checksum((unsigned char *)&ip.ip_src, 157 2 * sizeof(ip.ip_src), 158 IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen))))); 159 160 memcpy(&buf[offset], &udp, sizeof(udp)); 161 offset += sizeof(udp); 162 163 return (offset); 164 } 165 166 ssize_t 167 decode_hw_header(unsigned char *buf, size_t buflen, 168 size_t offset, struct packet_ctx *pc, unsigned int intfhtype) 169 { 170 u_int32_t ip_len; 171 u_int16_t ether_type; 172 struct ether_header *eh; 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 eh = (struct ether_header *)(buf + offset); 201 memcpy(pc->pc_dmac, eh->ether_dhost, ETHER_ADDR_LEN); 202 memcpy(pc->pc_smac, eh->ether_shost, ETHER_ADDR_LEN); 203 memcpy(ðer_type, &eh->ether_type, sizeof(ether_type)); 204 205 if (ether_type != htons(ETHERTYPE_IP)) 206 return (-1); 207 208 offset += ETHER_HDR_LEN; 209 210 pc->pc_htype = ARPHRD_ETHER; 211 pc->pc_hlen = ETHER_ADDR_LEN; 212 break; 213 default: 214 return (-1); 215 } 216 217 return (offset); 218 } 219 220 ssize_t 221 decode_udp_ip_header(unsigned char *buf, size_t buflen, 222 size_t offset, struct packet_ctx *pc) 223 { 224 struct ip *ip; 225 struct udphdr *udp; 226 unsigned char *data; 227 u_int32_t ip_len; 228 u_int32_t sum, usum; 229 int len; 230 231 /* Assure that an entire IP header is within the buffer. */ 232 if (buflen < offset + sizeof(*ip)) 233 return (-1); 234 ip = (struct ip *)(buf + offset); 235 if (ip->ip_v != IPVERSION) 236 return (-1); 237 ip_len = ip->ip_hl << 2; 238 if (ip_len < sizeof(struct ip) || 239 buflen < offset + ip_len) 240 return (-1); 241 242 if (ip->ip_p != IPPROTO_UDP) 243 return (-1); 244 245 /* Check the IP header checksum - it should be zero. */ 246 if (wrapsum(checksum(buf + offset, ip_len, 0)) != 0) 247 return (-1); 248 249 pc->pc_src.ss_len = sizeof(struct sockaddr_in); 250 pc->pc_src.ss_family = AF_INET; 251 memcpy(&ss2sin(&pc->pc_src)->sin_addr, &ip->ip_src, 252 sizeof(ss2sin(&pc->pc_src)->sin_addr)); 253 254 pc->pc_dst.ss_len = sizeof(struct sockaddr_in); 255 pc->pc_dst.ss_family = AF_INET; 256 memcpy(&ss2sin(&pc->pc_dst)->sin_addr, &ip->ip_dst, 257 sizeof(ss2sin(&pc->pc_dst)->sin_addr)); 258 259 #ifdef DEBUG 260 if (buflen != offset + ntohs(ip->ip_len)) 261 log_debug("ip length %d disagrees with bytes received %zd.", 262 ntohs(ip->ip_len), buflen - offset); 263 #endif 264 265 /* Assure that the entire IP packet is within the buffer. */ 266 if (buflen < offset + ntohs(ip->ip_len)) 267 return (-1); 268 269 /* Assure that the UDP header is within the buffer. */ 270 if (buflen < offset + ip_len + sizeof(*udp)) 271 return (-1); 272 udp = (struct udphdr *)(buf + offset + ip_len); 273 274 /* Assure that the entire UDP packet is within the buffer. */ 275 if (buflen < offset + ip_len + ntohs(udp->uh_ulen)) 276 return (-1); 277 data = buf + offset + ip_len + sizeof(*udp); 278 279 /* 280 * Compute UDP checksums, including the ``pseudo-header'', the 281 * UDP header and the data. If the UDP checksum field is zero, 282 * we're not supposed to do a checksum. 283 */ 284 len = ntohs(udp->uh_ulen) - sizeof(*udp); 285 if ((len < 0) || (len + data > buf + buflen)) { 286 return (-1); 287 } 288 if (len + data != buf + buflen) 289 log_debug("accepting packet with data after udp payload."); 290 291 usum = udp->uh_sum; 292 udp->uh_sum = 0; 293 294 sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp), 295 checksum(data, len, checksum((unsigned char *)&ip->ip_src, 296 2 * sizeof(ip->ip_src), 297 IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen))))); 298 299 if (usum && usum != sum) 300 return (-1); 301 302 ss2sin(&pc->pc_src)->sin_port = udp->uh_sport; 303 ss2sin(&pc->pc_dst)->sin_port = udp->uh_dport; 304 305 return (offset + ip_len + sizeof(*udp)); 306 } 307