| 1 |  |  | /*	$OpenBSD: packet.c,v 1.41 2017/09/17 21:20:23 krw 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/queue.h> | 
    
    | 44 |  |  | #include <sys/socket.h> | 
    
    | 45 |  |  |  | 
    
    | 46 |  |  | #include <net/if.h> | 
    
    | 47 |  |  |  | 
    
    | 48 |  |  | #include <netinet/in.h> | 
    
    | 49 |  |  | #include <netinet/ip.h> | 
    
    | 50 |  |  | #include <netinet/udp.h> | 
    
    | 51 |  |  | #include <netinet/if_ether.h> | 
    
    | 52 |  |  |  | 
    
    | 53 |  |  | #include <signal.h> | 
    
    | 54 |  |  | #include <stdio.h> | 
    
    | 55 |  |  | #include <string.h> | 
    
    | 56 |  |  | #include <unistd.h> | 
    
    | 57 |  |  |  | 
    
    | 58 |  |  | #include "dhcp.h" | 
    
    | 59 |  |  | #include "dhcpd.h" | 
    
    | 60 |  |  | #include "log.h" | 
    
    | 61 |  |  |  | 
    
    | 62 |  |  | uint32_t | 
    
    | 63 |  |  | checksum(unsigned char *buf, uint32_t nbytes, uint32_t sum) | 
    
    | 64 |  |  | { | 
    
    | 65 |  |  | 	unsigned int	 i; | 
    
    | 66 |  |  |  | 
    
    | 67 |  |  | 	/* Checksum all the pairs of bytes first. */ | 
    
    | 68 |  |  | 	for (i = 0; i < (nbytes & ~1U); i += 2) { | 
    
    | 69 |  |  | 		sum += (uint16_t)ntohs(*((uint16_t *)(buf + i))); | 
    
    | 70 |  |  | 		if (sum > 0xFFFF) | 
    
    | 71 |  |  | 			sum -= 0xFFFF; | 
    
    | 72 |  |  | 	} | 
    
    | 73 |  |  |  | 
    
    | 74 |  |  | 	/* | 
    
    | 75 |  |  | 	 * If there's a single byte left over, checksum it, too. | 
    
    | 76 |  |  | 	 * Network byte order is big-endian, so the remaining byte is | 
    
    | 77 |  |  | 	 * the high byte. | 
    
    | 78 |  |  | 	 */ | 
    
    | 79 |  |  | 	if (i < nbytes) { | 
    
    | 80 |  |  | 		sum += buf[i] << 8; | 
    
    | 81 |  |  | 		if (sum > 0xFFFF) | 
    
    | 82 |  |  | 			sum -= 0xFFFF; | 
    
    | 83 |  |  | 	} | 
    
    | 84 |  |  |  | 
    
    | 85 |  |  | 	return sum; | 
    
    | 86 |  |  | } | 
    
    | 87 |  |  |  | 
    
    | 88 |  |  | uint32_t | 
    
    | 89 |  |  | wrapsum(uint32_t sum) | 
    
    | 90 |  |  | { | 
    
    | 91 |  |  | 	sum = ~sum & 0xFFFF; | 
    
    | 92 |  |  | 	return htons(sum); | 
    
    | 93 |  |  | } | 
    
    | 94 |  |  |  | 
    
    | 95 |  |  | void | 
    
    | 96 |  |  | assemble_eh_header(struct ether_addr shost, struct ether_header *eh) | 
    
    | 97 |  |  | { | 
    
    | 98 |  |  | 	memset(eh->ether_dhost, 0xff, sizeof(eh->ether_dhost)); | 
    
    | 99 |  |  |  | 
    
    | 100 |  |  | 	memcpy(eh->ether_shost, shost.ether_addr_octet, | 
    
    | 101 |  |  | 	    sizeof(eh->ether_shost)); | 
    
    | 102 |  |  |  | 
    
    | 103 |  |  | 	eh->ether_type = htons(ETHERTYPE_IP); | 
    
    | 104 |  |  | } | 
    
    | 105 |  |  |  | 
    
    | 106 |  |  | ssize_t | 
    
    | 107 |  |  | decode_hw_header(unsigned char *buf, uint32_t buflen, struct ether_addr *from) | 
    
    | 108 |  |  | { | 
    
    | 109 |  |  | 	struct ether_header	 eh; | 
    
    | 110 |  |  |  | 
    
    | 111 |  |  | 	if (buflen < sizeof(eh)) | 
    
    | 112 |  |  | 		return -1; | 
    
    | 113 |  |  |  | 
    
    | 114 |  |  | 	memcpy(&eh, buf, sizeof(eh)); | 
    
    | 115 |  |  |  | 
    
    | 116 |  |  | 	memcpy(from->ether_addr_octet, eh.ether_shost, ETHER_ADDR_LEN); | 
    
    | 117 |  |  |  | 
    
    | 118 |  |  | 	return sizeof(eh); | 
    
    | 119 |  |  | } | 
    
    | 120 |  |  |  | 
    
    | 121 |  |  | ssize_t | 
    
    | 122 |  |  | decode_udp_ip_header(unsigned char *buf, uint32_t buflen, | 
    
    | 123 |  |  |     struct sockaddr_in *from) | 
    
    | 124 |  |  | { | 
    
    | 125 |  |  | 	static int	 ip_packets_seen; | 
    
    | 126 |  |  | 	static int	 ip_packets_bad_checksum; | 
    
    | 127 |  |  | 	static int	 udp_packets_seen; | 
    
    | 128 |  |  | 	static int	 udp_packets_bad_checksum; | 
    
    | 129 |  |  | 	static int	 udp_packets_length_checked; | 
    
    | 130 |  |  | 	static int	 udp_packets_length_overflow; | 
    
    | 131 |  |  | 	struct ip	*ip; | 
    
    | 132 |  |  | 	struct udphdr	*udp; | 
    
    | 133 |  |  | 	unsigned char	*data; | 
    
    | 134 |  |  | 	int		 len; | 
    
    | 135 |  |  | 	uint32_t	 ip_len; | 
    
    | 136 |  |  | 	uint32_t	 sum, usum; | 
    
    | 137 |  |  |  | 
    
    | 138 |  |  | 	/* Assure that an entire IP header is within the buffer. */ | 
    
    | 139 |  |  | 	if (sizeof(*ip) > buflen) | 
    
    | 140 |  |  | 		return -1; | 
    
    | 141 |  |  | 	ip_len = (*buf & 0xf) << 2; | 
    
    | 142 |  |  | 	if (ip_len > buflen) | 
    
    | 143 |  |  | 		return -1; | 
    
    | 144 |  |  | 	ip = (struct ip *)(buf); | 
    
    | 145 |  |  | 	ip_packets_seen++; | 
    
    | 146 |  |  |  | 
    
    | 147 |  |  | 	/* Check the IP header checksum - it should be zero. */ | 
    
    | 148 |  |  | 	if (wrapsum(checksum((unsigned char *)ip, ip_len, 0)) != 0) { | 
    
    | 149 |  |  | 		ip_packets_bad_checksum++; | 
    
    | 150 |  |  | 		if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 && | 
    
    | 151 |  |  | 		    (ip_packets_seen / ip_packets_bad_checksum) < 2) { | 
    
    | 152 |  |  | 			log_info("%s: %d bad IP checksums seen in %d packets", | 
    
    | 153 |  |  | 			    log_procname, ip_packets_bad_checksum, | 
    
    | 154 |  |  | 			    ip_packets_seen); | 
    
    | 155 |  |  | 			ip_packets_seen = ip_packets_bad_checksum = 0; | 
    
    | 156 |  |  | 		} | 
    
    | 157 |  |  | 		return -1; | 
    
    | 158 |  |  | 	} | 
    
    | 159 |  |  |  | 
    
    | 160 |  |  | 	memcpy(&from->sin_addr, &ip->ip_src, sizeof(from->sin_addr)); | 
    
    | 161 |  |  |  | 
    
    | 162 |  |  | #ifdef DEBUG | 
    
    | 163 |  |  | 	if (ntohs(ip->ip_len) != buflen) | 
    
    | 164 |  |  | 		log_debug("%s: ip length %hu disagrees with bytes received %d", | 
    
    | 165 |  |  | 		    log_procname, ntohs(ip->ip_len), buflen); | 
    
    | 166 |  |  | #endif	/* DEBUG */ | 
    
    | 167 |  |  |  | 
    
    | 168 |  |  | 	/* Assure that the entire IP packet is within the buffer. */ | 
    
    | 169 |  |  | 	if (ntohs(ip->ip_len) > buflen) | 
    
    | 170 |  |  | 		return -1; | 
    
    | 171 |  |  |  | 
    
    | 172 |  |  | 	/* Assure that the UDP header is within the buffer. */ | 
    
    | 173 |  |  | 	if (ip_len + sizeof(*udp) > buflen) | 
    
    | 174 |  |  | 		return -1; | 
    
    | 175 |  |  | 	udp = (struct udphdr *)(buf + ip_len); | 
    
    | 176 |  |  | 	udp_packets_seen++; | 
    
    | 177 |  |  |  | 
    
    | 178 |  |  | 	/* Assure that the entire UDP packet is within the buffer. */ | 
    
    | 179 |  |  | 	if (ip_len + ntohs(udp->uh_ulen) > buflen) | 
    
    | 180 |  |  | 		return -1; | 
    
    | 181 |  |  | 	data = buf + ip_len + sizeof(*udp); | 
    
    | 182 |  |  |  | 
    
    | 183 |  |  | 	/* | 
    
    | 184 |  |  | 	 * Compute UDP checksums, including the ``pseudo-header'', the | 
    
    | 185 |  |  | 	 * UDP header and the data. If the UDP checksum field is zero, | 
    
    | 186 |  |  | 	 * we're not supposed to do a checksum. | 
    
    | 187 |  |  | 	 */ | 
    
    | 188 |  |  | 	udp_packets_length_checked++; | 
    
    | 189 |  |  | 	len = ntohs(udp->uh_ulen) - sizeof(*udp); | 
    
    | 190 |  |  | 	if ((len < 0) || (len + data > buf + buflen)) { | 
    
    | 191 |  |  | 		udp_packets_length_overflow++; | 
    
    | 192 |  |  | 		if (udp_packets_length_checked > 4 && | 
    
    | 193 |  |  | 		    udp_packets_length_overflow != 0 && | 
    
    | 194 |  |  | 		    (udp_packets_length_checked / | 
    
    | 195 |  |  | 		    udp_packets_length_overflow) < 2) { | 
    
    | 196 |  |  | 			log_info("%s: %d udp packets in %d too long - dropped", | 
    
    | 197 |  |  | 			    log_procname, udp_packets_length_overflow, | 
    
    | 198 |  |  | 			    udp_packets_length_checked); | 
    
    | 199 |  |  | 			udp_packets_length_overflow = | 
    
    | 200 |  |  | 			    udp_packets_length_checked = 0; | 
    
    | 201 |  |  | 		} | 
    
    | 202 |  |  | 		return -1; | 
    
    | 203 |  |  | 	} | 
    
    | 204 |  |  | #ifdef DEBUG | 
    
    | 205 |  |  | 	if (len + data != buf + buflen) | 
    
    | 206 |  |  | 		log_debug("%s: accepting packet with data after udp payload", | 
    
    | 207 |  |  | 		    log_procname); | 
    
    | 208 |  |  | #endif	/* DEBUG */ | 
    
    | 209 |  |  |  | 
    
    | 210 |  |  | 	usum = udp->uh_sum; | 
    
    | 211 |  |  | 	udp->uh_sum = 0; | 
    
    | 212 |  |  |  | 
    
    | 213 |  |  | 	sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp), | 
    
    | 214 |  |  | 	    checksum(data, len, checksum((unsigned char *)&ip->ip_src, | 
    
    | 215 |  |  | 	    2 * sizeof(ip->ip_src), | 
    
    | 216 |  |  | 	    IPPROTO_UDP + (uint32_t)ntohs(udp->uh_ulen))))); | 
    
    | 217 |  |  |  | 
    
    | 218 |  |  | 	udp_packets_seen++; | 
    
    | 219 |  |  | 	if (usum != 0 && usum != sum) { | 
    
    | 220 |  |  | 		udp_packets_bad_checksum++; | 
    
    | 221 |  |  | 		if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 && | 
    
    | 222 |  |  | 		    (udp_packets_seen / udp_packets_bad_checksum) < 2) { | 
    
    | 223 |  |  | 			log_info("%s: %d bad udp checksums in %d packets", | 
    
    | 224 |  |  | 			    log_procname, udp_packets_bad_checksum, | 
    
    | 225 |  |  | 			    udp_packets_seen); | 
    
    | 226 |  |  | 			udp_packets_seen = udp_packets_bad_checksum = 0; | 
    
    | 227 |  |  | 		} | 
    
    | 228 |  |  | 		return -1; | 
    
    | 229 |  |  | 	} | 
    
    | 230 |  |  |  | 
    
    | 231 |  |  | 	memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport)); | 
    
    | 232 |  |  |  | 
    
    | 233 |  |  | 	return ip_len + sizeof(*udp); | 
    
    | 234 |  |  | } |