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