packet.c revision 149399
150769Sdfr/* $OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt Exp $ */ 250769Sdfr 350769Sdfr/* Packet assembly code, originally contributed by Archie Cobbs. */ 450769Sdfr 550769Sdfr/* 650769Sdfr * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium. 750769Sdfr * All rights reserved. 850769Sdfr * 950769Sdfr * Redistribution and use in source and binary forms, with or without 1050769Sdfr * modification, are permitted provided that the following conditions 1150769Sdfr * are met: 1250769Sdfr * 1350769Sdfr * 1. Redistributions of source code must retain the above copyright 1450769Sdfr * notice, this list of conditions and the following disclaimer. 1550769Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1650769Sdfr * notice, this list of conditions and the following disclaimer in the 1750769Sdfr * documentation and/or other materials provided with the distribution. 1850769Sdfr * 3. Neither the name of The Internet Software Consortium nor the names 1950769Sdfr * of its contributors may be used to endorse or promote products derived 2050769Sdfr * from this software without specific prior written permission. 2150769Sdfr * 2250769Sdfr * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 2350769Sdfr * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 2450769Sdfr * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2550769Sdfr * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2650769Sdfr * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 2750769Sdfr * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2850769Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2950769Sdfr * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 3050769Sdfr * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 3150769Sdfr * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3250769Sdfr * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3350769Sdfr * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3450769Sdfr * SUCH DAMAGE. 3550769Sdfr * 3650769Sdfr * This software has been written for the Internet Software Consortium 3750769Sdfr * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 3850769Sdfr * Enterprises. To learn more about the Internet Software Consortium, 3950769Sdfr * see ``http://www.vix.com/isc''. To learn more about Vixie 4050769Sdfr * Enterprises, see ``http://www.vix.com''. 4150769Sdfr */ 4250769Sdfr 4350769Sdfr#include <sys/cdefs.h> 4450769Sdfr__FBSDID("$FreeBSD: head/sbin/dhclient/packet.c 149399 2005-08-23 23:59:55Z brooks $"); 4550769Sdfr 4650769Sdfr#include "dhcpd.h" 4750769Sdfr 4850769Sdfr#include <netinet/in_systm.h> 4950769Sdfr#include <netinet/ip.h> 5050769Sdfr#include <netinet/udp.h> 5150769Sdfr#include <netinet/if_ether.h> 5250769Sdfr 5350769Sdfr#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t)) 5450769Sdfr 5550769Sdfru_int32_t checksum(unsigned char *, unsigned, u_int32_t); 5650769Sdfru_int32_t wrapsum(u_int32_t); 5762947Stanimura 5850769Sdfrvoid assemble_ethernet_header(struct interface_info *, unsigned char *, 5950769Sdfr int *, struct hardware *); 6050769Sdfrssize_t decode_ethernet_header(struct interface_info *, unsigned char *, 6150769Sdfr int bufix, struct hardware *); 6250769Sdfr 6350769Sdfru_int32_t 6450769Sdfrchecksum(unsigned char *buf, unsigned nbytes, u_int32_t sum) 6550769Sdfr{ 6650769Sdfr int i; 6750769Sdfr 6850769Sdfr /* Checksum all the pairs of bytes first... */ 6950769Sdfr for (i = 0; i < (nbytes & ~1U); i += 2) { 7062947Stanimura sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i))); 7162947Stanimura if (sum > 0xFFFF) 7262947Stanimura sum -= 0xFFFF; 7362947Stanimura } 7462947Stanimura 7562947Stanimura /* 7662947Stanimura * If there's a single byte left over, checksum it, too. 7762947Stanimura * Network byte order is big-endian, so the remaining byte is 7862947Stanimura * the high byte. 7962947Stanimura */ 8062947Stanimura if (i < nbytes) { 8162947Stanimura sum += buf[i] << 8; 8262947Stanimura if (sum > 0xFFFF) 8362947Stanimura sum -= 0xFFFF; 8462947Stanimura } 8562947Stanimura 8662947Stanimura return (sum); 8762947Stanimura} 8850769Sdfr 8950769Sdfru_int32_t 9050769Sdfrwrapsum(u_int32_t sum) 9150769Sdfr{ 9250769Sdfr sum = ~sum & 0xFFFF; 9350769Sdfr return (htons(sum)); 9450769Sdfr} 9550769Sdfr 9650769Sdfrvoid 9750769Sdfrassemble_hw_header(struct interface_info *interface, unsigned char *buf, 9850769Sdfr int *bufix, struct hardware *to) 9950769Sdfr{ 10050769Sdfr struct ether_header eh; 10150769Sdfr 10250769Sdfr if (to != NULL && to->hlen == 6) /* XXX */ 10350769Sdfr memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost)); 10450769Sdfr else 10550769Sdfr memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); 10650769Sdfr if (interface->hw_address.hlen == sizeof(eh.ether_shost)) 10750769Sdfr memcpy(eh.ether_shost, interface->hw_address.haddr, 10850769Sdfr sizeof(eh.ether_shost)); 10950769Sdfr else 11050769Sdfr memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost)); 11150769Sdfr 11252241Sdfr eh.ether_type = htons(ETHERTYPE_IP); 11352241Sdfr 11452241Sdfr memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE); 11552241Sdfr *bufix += ETHER_HEADER_SIZE; 11652241Sdfr} 11752241Sdfr 11852241Sdfrvoid 11952241Sdfrassemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from, 12052241Sdfr u_int32_t to, unsigned int port, unsigned char *data, int len) 12152241Sdfr{ 12252241Sdfr struct ip ip; 12352241Sdfr struct udphdr udp; 12452241Sdfr 12552241Sdfr ip.ip_v = 4; 12652241Sdfr ip.ip_hl = 5; 12752241Sdfr ip.ip_tos = IPTOS_LOWDELAY; 12852241Sdfr ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 12952241Sdfr ip.ip_id = 0; 13050769Sdfr ip.ip_off = 0; 13150769Sdfr ip.ip_ttl = 16; 13250769Sdfr ip.ip_p = IPPROTO_UDP; 13350769Sdfr ip.ip_sum = 0; 13450769Sdfr ip.ip_src.s_addr = from; 13550769Sdfr ip.ip_dst.s_addr = to; 13650769Sdfr 13750769Sdfr ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 13850769Sdfr memcpy(&buf[*bufix], &ip, sizeof(ip)); 13950769Sdfr *bufix += sizeof(ip); 14050769Sdfr 14150769Sdfr udp.uh_sport = htons(LOCAL_PORT); /* XXX */ 14250769Sdfr udp.uh_dport = port; /* XXX */ 14350769Sdfr udp.uh_ulen = htons(sizeof(udp) + len); 14450769Sdfr memset(&udp.uh_sum, 0, sizeof(udp.uh_sum)); 14550769Sdfr 14650769Sdfr udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 14750769Sdfr checksum(data, len, checksum((unsigned char *)&ip.ip_src, 14850769Sdfr 2 * sizeof(ip.ip_src), 14950769Sdfr IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen))))); 15050769Sdfr 15150769Sdfr memcpy(&buf[*bufix], &udp, sizeof(udp)); 15250769Sdfr *bufix += sizeof(udp); 15350769Sdfr} 15450769Sdfr 15550769Sdfrssize_t 15650769Sdfrdecode_hw_header(unsigned char *buf, int bufix, struct hardware *from) 15750769Sdfr{ 15850769Sdfr struct ether_header eh; 15950769Sdfr 16050769Sdfr memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE); 16150769Sdfr 16250769Sdfr memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost)); 16350769Sdfr from->htype = ARPHRD_ETHER; 16450769Sdfr from->hlen = sizeof(eh.ether_shost); 16550769Sdfr 16650769Sdfr return (sizeof(eh)); 16750769Sdfr} 16850769Sdfr 16950769Sdfrssize_t 17050769Sdfrdecode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from, 17150769Sdfr unsigned char *data, int buflen) 17250769Sdfr{ 17350769Sdfr struct ip *ip; 17450769Sdfr struct udphdr *udp; 17550769Sdfr u_int32_t ip_len = (buf[bufix] & 0xf) << 2; 17650769Sdfr u_int32_t sum, usum; 17750769Sdfr static int ip_packets_seen; 17850769Sdfr static int ip_packets_bad_checksum; 17950769Sdfr static int udp_packets_seen; 18050769Sdfr static int udp_packets_bad_checksum; 18150769Sdfr static int udp_packets_length_checked; 18250769Sdfr static int udp_packets_length_overflow; 18350769Sdfr int len = 0; 18450769Sdfr 18550769Sdfr ip = (struct ip *)(buf + bufix); 18650769Sdfr udp = (struct udphdr *)(buf + bufix + ip_len); 18750769Sdfr 18850769Sdfr /* Check the IP header checksum - it should be zero. */ 18950769Sdfr ip_packets_seen++; 19050769Sdfr if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) { 19150769Sdfr ip_packets_bad_checksum++; 19250769Sdfr if (ip_packets_seen > 4 && 19350769Sdfr (ip_packets_seen / ip_packets_bad_checksum) < 2) { 19450769Sdfr note("%d bad IP checksums seen in %d packets", 19550769Sdfr ip_packets_bad_checksum, ip_packets_seen); 19650769Sdfr ip_packets_seen = ip_packets_bad_checksum = 0; 19750769Sdfr } 19850769Sdfr return (-1); 19950769Sdfr } 20050769Sdfr 20150769Sdfr if (ntohs(ip->ip_len) != buflen) 20250769Sdfr debug("ip length %d disagrees with bytes received %d.", 20350769Sdfr ntohs(ip->ip_len), buflen); 20450769Sdfr 20550769Sdfr memcpy(&from->sin_addr, &ip->ip_src, 4); 20652059Sdfr 20750769Sdfr /* 20850769Sdfr * Compute UDP checksums, including the ``pseudo-header'', the 20950769Sdfr * UDP header and the data. If the UDP checksum field is zero, 21050769Sdfr * we're not supposed to do a checksum. 21152059Sdfr */ 21250769Sdfr if (!data) { 21350769Sdfr data = buf + bufix + ip_len + sizeof(*udp); 21452059Sdfr len = ntohs(udp->uh_ulen) - sizeof(*udp); 21550769Sdfr udp_packets_length_checked++; 21650769Sdfr if (len + data > buf + bufix + buflen) { 21750769Sdfr udp_packets_length_overflow++; 21850769Sdfr if (udp_packets_length_checked > 4 && 21950769Sdfr (udp_packets_length_checked / 22050769Sdfr udp_packets_length_overflow) < 2) { 22150769Sdfr note("%d udp packets in %d too long - dropped", 22250769Sdfr udp_packets_length_overflow, 22350769Sdfr udp_packets_length_checked); 22452059Sdfr udp_packets_length_overflow = 22550769Sdfr udp_packets_length_checked = 0; 22650769Sdfr } 22750769Sdfr return (-1); 22850769Sdfr } 22950769Sdfr if (len + data != buf + bufix + buflen) 23052059Sdfr debug("accepting packet with data after udp payload."); 23150769Sdfr } 23252059Sdfr 23350769Sdfr usum = udp->uh_sum; 23450769Sdfr udp->uh_sum = 0; 23550769Sdfr 23650769Sdfr sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp), 23750769Sdfr checksum(data, len, checksum((unsigned char *)&ip->ip_src, 23850769Sdfr 2 * sizeof(ip->ip_src), 23950769Sdfr IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen))))); 24050769Sdfr 24150769Sdfr udp_packets_seen++; 24250769Sdfr if (usum && usum != sum) { 24350769Sdfr udp_packets_bad_checksum++; 24450769Sdfr if (udp_packets_seen > 4 && 24550769Sdfr (udp_packets_seen / udp_packets_bad_checksum) < 2) { 24650769Sdfr note("%d bad udp checksums in %d packets", 24750769Sdfr udp_packets_bad_checksum, udp_packets_seen); 24850769Sdfr udp_packets_seen = udp_packets_bad_checksum = 0; 24950769Sdfr } 25050769Sdfr return (-1); 25150769Sdfr } 25250769Sdfr 25350769Sdfr memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport)); 25450769Sdfr 25550769Sdfr return (ip_len + sizeof(*udp)); 25650769Sdfr} 25750769Sdfr