1*47c08596SBrooks Davis /* $OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt Exp $ */ 2*47c08596SBrooks Davis 3*47c08596SBrooks Davis /* Packet assembly code, originally contributed by Archie Cobbs. */ 4*47c08596SBrooks Davis 5*47c08596SBrooks Davis /* 6*47c08596SBrooks Davis * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium. 7*47c08596SBrooks Davis * All rights reserved. 8*47c08596SBrooks Davis * 9*47c08596SBrooks Davis * Redistribution and use in source and binary forms, with or without 10*47c08596SBrooks Davis * modification, are permitted provided that the following conditions 11*47c08596SBrooks Davis * are met: 12*47c08596SBrooks Davis * 13*47c08596SBrooks Davis * 1. Redistributions of source code must retain the above copyright 14*47c08596SBrooks Davis * notice, this list of conditions and the following disclaimer. 15*47c08596SBrooks Davis * 2. Redistributions in binary form must reproduce the above copyright 16*47c08596SBrooks Davis * notice, this list of conditions and the following disclaimer in the 17*47c08596SBrooks Davis * documentation and/or other materials provided with the distribution. 18*47c08596SBrooks Davis * 3. Neither the name of The Internet Software Consortium nor the names 19*47c08596SBrooks Davis * of its contributors may be used to endorse or promote products derived 20*47c08596SBrooks Davis * from this software without specific prior written permission. 21*47c08596SBrooks Davis * 22*47c08596SBrooks Davis * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 23*47c08596SBrooks Davis * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 24*47c08596SBrooks Davis * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25*47c08596SBrooks Davis * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26*47c08596SBrooks Davis * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 27*47c08596SBrooks Davis * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28*47c08596SBrooks Davis * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29*47c08596SBrooks Davis * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 30*47c08596SBrooks Davis * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31*47c08596SBrooks Davis * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32*47c08596SBrooks Davis * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33*47c08596SBrooks Davis * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34*47c08596SBrooks Davis * SUCH DAMAGE. 35*47c08596SBrooks Davis * 36*47c08596SBrooks Davis * This software has been written for the Internet Software Consortium 37*47c08596SBrooks Davis * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 38*47c08596SBrooks Davis * Enterprises. To learn more about the Internet Software Consortium, 39*47c08596SBrooks Davis * see ``http://www.vix.com/isc''. To learn more about Vixie 40*47c08596SBrooks Davis * Enterprises, see ``http://www.vix.com''. 41*47c08596SBrooks Davis */ 42*47c08596SBrooks Davis 43*47c08596SBrooks Davis #include "dhcpd.h" 44*47c08596SBrooks Davis 45*47c08596SBrooks Davis #include <netinet/in_systm.h> 46*47c08596SBrooks Davis #include <netinet/ip.h> 47*47c08596SBrooks Davis #include <netinet/udp.h> 48*47c08596SBrooks Davis #include <netinet/if_ether.h> 49*47c08596SBrooks Davis 50*47c08596SBrooks Davis #define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t)) 51*47c08596SBrooks Davis 52*47c08596SBrooks Davis u_int32_t checksum(unsigned char *, unsigned, u_int32_t); 53*47c08596SBrooks Davis u_int32_t wrapsum(u_int32_t); 54*47c08596SBrooks Davis 55*47c08596SBrooks Davis void assemble_ethernet_header(struct interface_info *, unsigned char *, 56*47c08596SBrooks Davis int *, struct hardware *); 57*47c08596SBrooks Davis ssize_t decode_ethernet_header(struct interface_info *, unsigned char *, 58*47c08596SBrooks Davis int bufix, struct hardware *); 59*47c08596SBrooks Davis 60*47c08596SBrooks Davis u_int32_t 61*47c08596SBrooks Davis checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum) 62*47c08596SBrooks Davis { 63*47c08596SBrooks Davis int i; 64*47c08596SBrooks Davis 65*47c08596SBrooks Davis /* Checksum all the pairs of bytes first... */ 66*47c08596SBrooks Davis for (i = 0; i < (nbytes & ~1U); i += 2) { 67*47c08596SBrooks Davis sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i))); 68*47c08596SBrooks Davis if (sum > 0xFFFF) 69*47c08596SBrooks Davis sum -= 0xFFFF; 70*47c08596SBrooks Davis } 71*47c08596SBrooks Davis 72*47c08596SBrooks Davis /* 73*47c08596SBrooks Davis * If there's a single byte left over, checksum it, too. 74*47c08596SBrooks Davis * Network byte order is big-endian, so the remaining byte is 75*47c08596SBrooks Davis * the high byte. 76*47c08596SBrooks Davis */ 77*47c08596SBrooks Davis if (i < nbytes) { 78*47c08596SBrooks Davis sum += buf[i] << 8; 79*47c08596SBrooks Davis if (sum > 0xFFFF) 80*47c08596SBrooks Davis sum -= 0xFFFF; 81*47c08596SBrooks Davis } 82*47c08596SBrooks Davis 83*47c08596SBrooks Davis return (sum); 84*47c08596SBrooks Davis } 85*47c08596SBrooks Davis 86*47c08596SBrooks Davis u_int32_t 87*47c08596SBrooks Davis wrapsum(u_int32_t sum) 88*47c08596SBrooks Davis { 89*47c08596SBrooks Davis sum = ~sum & 0xFFFF; 90*47c08596SBrooks Davis return (htons(sum)); 91*47c08596SBrooks Davis } 92*47c08596SBrooks Davis 93*47c08596SBrooks Davis void 94*47c08596SBrooks Davis assemble_hw_header(struct interface_info *interface, unsigned char *buf, 95*47c08596SBrooks Davis int *bufix, struct hardware *to) 96*47c08596SBrooks Davis { 97*47c08596SBrooks Davis struct ether_header eh; 98*47c08596SBrooks Davis 99*47c08596SBrooks Davis if (to != NULL && to->hlen == 6) /* XXX */ 100*47c08596SBrooks Davis memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost)); 101*47c08596SBrooks Davis else 102*47c08596SBrooks Davis memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); 103*47c08596SBrooks Davis if (interface->hw_address.hlen == sizeof(eh.ether_shost)) 104*47c08596SBrooks Davis memcpy(eh.ether_shost, interface->hw_address.haddr, 105*47c08596SBrooks Davis sizeof(eh.ether_shost)); 106*47c08596SBrooks Davis else 107*47c08596SBrooks Davis memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost)); 108*47c08596SBrooks Davis 109*47c08596SBrooks Davis eh.ether_type = htons(ETHERTYPE_IP); 110*47c08596SBrooks Davis 111*47c08596SBrooks Davis memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE); 112*47c08596SBrooks Davis *bufix += ETHER_HEADER_SIZE; 113*47c08596SBrooks Davis } 114*47c08596SBrooks Davis 115*47c08596SBrooks Davis void 116*47c08596SBrooks Davis assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from, 117*47c08596SBrooks Davis u_int32_t to, unsigned int port, unsigned char *data, int len) 118*47c08596SBrooks Davis { 119*47c08596SBrooks Davis struct ip ip; 120*47c08596SBrooks Davis struct udphdr udp; 121*47c08596SBrooks Davis 122*47c08596SBrooks Davis ip.ip_v = 4; 123*47c08596SBrooks Davis ip.ip_hl = 5; 124*47c08596SBrooks Davis ip.ip_tos = IPTOS_LOWDELAY; 125*47c08596SBrooks Davis ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 126*47c08596SBrooks Davis ip.ip_id = 0; 127*47c08596SBrooks Davis ip.ip_off = 0; 128*47c08596SBrooks Davis ip.ip_ttl = 16; 129*47c08596SBrooks Davis ip.ip_p = IPPROTO_UDP; 130*47c08596SBrooks Davis ip.ip_sum = 0; 131*47c08596SBrooks Davis ip.ip_src.s_addr = from; 132*47c08596SBrooks Davis ip.ip_dst.s_addr = to; 133*47c08596SBrooks Davis 134*47c08596SBrooks Davis ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 135*47c08596SBrooks Davis memcpy(&buf[*bufix], &ip, sizeof(ip)); 136*47c08596SBrooks Davis *bufix += sizeof(ip); 137*47c08596SBrooks Davis 138*47c08596SBrooks Davis udp.uh_sport = htons(LOCAL_PORT); /* XXX */ 139*47c08596SBrooks Davis udp.uh_dport = port; /* XXX */ 140*47c08596SBrooks Davis udp.uh_ulen = htons(sizeof(udp) + len); 141*47c08596SBrooks Davis memset(&udp.uh_sum, 0, sizeof(udp.uh_sum)); 142*47c08596SBrooks Davis 143*47c08596SBrooks Davis udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 144*47c08596SBrooks Davis checksum(data, len, checksum((unsigned char *)&ip.ip_src, 145*47c08596SBrooks Davis 2 * sizeof(ip.ip_src), 146*47c08596SBrooks Davis IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen))))); 147*47c08596SBrooks Davis 148*47c08596SBrooks Davis memcpy(&buf[*bufix], &udp, sizeof(udp)); 149*47c08596SBrooks Davis *bufix += sizeof(udp); 150*47c08596SBrooks Davis } 151*47c08596SBrooks Davis 152*47c08596SBrooks Davis ssize_t 153*47c08596SBrooks Davis decode_hw_header(unsigned char *buf, int bufix, struct hardware *from) 154*47c08596SBrooks Davis { 155*47c08596SBrooks Davis struct ether_header eh; 156*47c08596SBrooks Davis 157*47c08596SBrooks Davis memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE); 158*47c08596SBrooks Davis 159*47c08596SBrooks Davis memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost)); 160*47c08596SBrooks Davis from->htype = ARPHRD_ETHER; 161*47c08596SBrooks Davis from->hlen = sizeof(eh.ether_shost); 162*47c08596SBrooks Davis 163*47c08596SBrooks Davis return (sizeof(eh)); 164*47c08596SBrooks Davis } 165*47c08596SBrooks Davis 166*47c08596SBrooks Davis ssize_t 167*47c08596SBrooks Davis decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from, 168*47c08596SBrooks Davis unsigned char *data, int buflen) 169*47c08596SBrooks Davis { 170*47c08596SBrooks Davis struct ip *ip; 171*47c08596SBrooks Davis struct udphdr *udp; 172*47c08596SBrooks Davis u_int32_t ip_len = (buf[bufix] & 0xf) << 2; 173*47c08596SBrooks Davis u_int32_t sum, usum; 174*47c08596SBrooks Davis static int ip_packets_seen; 175*47c08596SBrooks Davis static int ip_packets_bad_checksum; 176*47c08596SBrooks Davis static int udp_packets_seen; 177*47c08596SBrooks Davis static int udp_packets_bad_checksum; 178*47c08596SBrooks Davis static int udp_packets_length_checked; 179*47c08596SBrooks Davis static int udp_packets_length_overflow; 180*47c08596SBrooks Davis int len = 0; 181*47c08596SBrooks Davis 182*47c08596SBrooks Davis ip = (struct ip *)(buf + bufix); 183*47c08596SBrooks Davis udp = (struct udphdr *)(buf + bufix + ip_len); 184*47c08596SBrooks Davis 185*47c08596SBrooks Davis /* Check the IP header checksum - it should be zero. */ 186*47c08596SBrooks Davis ip_packets_seen++; 187*47c08596SBrooks Davis if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) { 188*47c08596SBrooks Davis ip_packets_bad_checksum++; 189*47c08596SBrooks Davis if (ip_packets_seen > 4 && 190*47c08596SBrooks Davis (ip_packets_seen / ip_packets_bad_checksum) < 2) { 191*47c08596SBrooks Davis note("%d bad IP checksums seen in %d packets", 192*47c08596SBrooks Davis ip_packets_bad_checksum, ip_packets_seen); 193*47c08596SBrooks Davis ip_packets_seen = ip_packets_bad_checksum = 0; 194*47c08596SBrooks Davis } 195*47c08596SBrooks Davis return (-1); 196*47c08596SBrooks Davis } 197*47c08596SBrooks Davis 198*47c08596SBrooks Davis if (ntohs(ip->ip_len) != buflen) 199*47c08596SBrooks Davis debug("ip length %d disagrees with bytes received %d.", 200*47c08596SBrooks Davis ntohs(ip->ip_len), buflen); 201*47c08596SBrooks Davis 202*47c08596SBrooks Davis memcpy(&from->sin_addr, &ip->ip_src, 4); 203*47c08596SBrooks Davis 204*47c08596SBrooks Davis /* 205*47c08596SBrooks Davis * Compute UDP checksums, including the ``pseudo-header'', the 206*47c08596SBrooks Davis * UDP header and the data. If the UDP checksum field is zero, 207*47c08596SBrooks Davis * we're not supposed to do a checksum. 208*47c08596SBrooks Davis */ 209*47c08596SBrooks Davis if (!data) { 210*47c08596SBrooks Davis data = buf + bufix + ip_len + sizeof(*udp); 211*47c08596SBrooks Davis len = ntohs(udp->uh_ulen) - sizeof(*udp); 212*47c08596SBrooks Davis udp_packets_length_checked++; 213*47c08596SBrooks Davis if (len + data > buf + bufix + buflen) { 214*47c08596SBrooks Davis udp_packets_length_overflow++; 215*47c08596SBrooks Davis if (udp_packets_length_checked > 4 && 216*47c08596SBrooks Davis (udp_packets_length_checked / 217*47c08596SBrooks Davis udp_packets_length_overflow) < 2) { 218*47c08596SBrooks Davis note("%d udp packets in %d too long - dropped", 219*47c08596SBrooks Davis udp_packets_length_overflow, 220*47c08596SBrooks Davis udp_packets_length_checked); 221*47c08596SBrooks Davis udp_packets_length_overflow = 222*47c08596SBrooks Davis udp_packets_length_checked = 0; 223*47c08596SBrooks Davis } 224*47c08596SBrooks Davis return (-1); 225*47c08596SBrooks Davis } 226*47c08596SBrooks Davis if (len + data != buf + bufix + buflen) 227*47c08596SBrooks Davis debug("accepting packet with data after udp payload."); 228*47c08596SBrooks Davis } 229*47c08596SBrooks Davis 230*47c08596SBrooks Davis usum = udp->uh_sum; 231*47c08596SBrooks Davis udp->uh_sum = 0; 232*47c08596SBrooks Davis 233*47c08596SBrooks Davis sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp), 234*47c08596SBrooks Davis checksum(data, len, checksum((unsigned char *)&ip->ip_src, 235*47c08596SBrooks Davis 2 * sizeof(ip->ip_src), 236*47c08596SBrooks Davis IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen))))); 237*47c08596SBrooks Davis 238*47c08596SBrooks Davis udp_packets_seen++; 239*47c08596SBrooks Davis if (usum && usum != sum) { 240*47c08596SBrooks Davis udp_packets_bad_checksum++; 241*47c08596SBrooks Davis if (udp_packets_seen > 4 && 242*47c08596SBrooks Davis (udp_packets_seen / udp_packets_bad_checksum) < 2) { 243*47c08596SBrooks Davis note("%d bad udp checksums in %d packets", 244*47c08596SBrooks Davis udp_packets_bad_checksum, udp_packets_seen); 245*47c08596SBrooks Davis udp_packets_seen = udp_packets_bad_checksum = 0; 246*47c08596SBrooks Davis } 247*47c08596SBrooks Davis return (-1); 248*47c08596SBrooks Davis } 249*47c08596SBrooks Davis 250*47c08596SBrooks Davis memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport)); 251*47c08596SBrooks Davis 252*47c08596SBrooks Davis return (ip_len + sizeof(*udp)); 253*47c08596SBrooks Davis } 254