1147072Sbrooks/*	$OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt Exp $	*/
2147072Sbrooks
3147072Sbrooks/* Packet assembly code, originally contributed by Archie Cobbs. */
4147072Sbrooks
5147072Sbrooks/*
6147072Sbrooks * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
7147072Sbrooks * All rights reserved.
8147072Sbrooks *
9147072Sbrooks * Redistribution and use in source and binary forms, with or without
10147072Sbrooks * modification, are permitted provided that the following conditions
11147072Sbrooks * are met:
12147072Sbrooks *
13147072Sbrooks * 1. Redistributions of source code must retain the above copyright
14147072Sbrooks *    notice, this list of conditions and the following disclaimer.
15147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
16147072Sbrooks *    notice, this list of conditions and the following disclaimer in the
17147072Sbrooks *    documentation and/or other materials provided with the distribution.
18147072Sbrooks * 3. Neither the name of The Internet Software Consortium nor the names
19147072Sbrooks *    of its contributors may be used to endorse or promote products derived
20147072Sbrooks *    from this software without specific prior written permission.
21147072Sbrooks *
22147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23147072Sbrooks * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24147072Sbrooks * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25147072Sbrooks * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26147072Sbrooks * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27147072Sbrooks * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28147072Sbrooks * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29147072Sbrooks * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30147072Sbrooks * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31147072Sbrooks * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32147072Sbrooks * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33147072Sbrooks * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34147072Sbrooks * SUCH DAMAGE.
35147072Sbrooks *
36147072Sbrooks * This software has been written for the Internet Software Consortium
37147072Sbrooks * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38147072Sbrooks * Enterprises.  To learn more about the Internet Software Consortium,
39147072Sbrooks * see ``http://www.vix.com/isc''.  To learn more about Vixie
40147072Sbrooks * Enterprises, see ``http://www.vix.com''.
41147072Sbrooks */
42147072Sbrooks
43149399Sbrooks#include <sys/cdefs.h>
44149399Sbrooks__FBSDID("$FreeBSD$");
45149399Sbrooks
46147072Sbrooks#include "dhcpd.h"
47147072Sbrooks
48147072Sbrooks#include <netinet/in_systm.h>
49147072Sbrooks#include <netinet/ip.h>
50147072Sbrooks#include <netinet/udp.h>
51147072Sbrooks#include <netinet/if_ether.h>
52147072Sbrooks
53147072Sbrooks#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t))
54147072Sbrooks
55147072Sbrooksu_int32_t	checksum(unsigned char *, unsigned, u_int32_t);
56147072Sbrooksu_int32_t	wrapsum(u_int32_t);
57147072Sbrooks
58147072Sbrooksu_int32_t
59147072Sbrookschecksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
60147072Sbrooks{
61147072Sbrooks	int i;
62147072Sbrooks
63147072Sbrooks	/* Checksum all the pairs of bytes first... */
64147072Sbrooks	for (i = 0; i < (nbytes & ~1U); i += 2) {
65147072Sbrooks		sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
66147072Sbrooks		if (sum > 0xFFFF)
67147072Sbrooks			sum -= 0xFFFF;
68147072Sbrooks	}
69147072Sbrooks
70147072Sbrooks	/*
71147072Sbrooks	 * If there's a single byte left over, checksum it, too.
72147072Sbrooks	 * Network byte order is big-endian, so the remaining byte is
73147072Sbrooks	 * the high byte.
74147072Sbrooks	 */
75147072Sbrooks	if (i < nbytes) {
76147072Sbrooks		sum += buf[i] << 8;
77147072Sbrooks		if (sum > 0xFFFF)
78147072Sbrooks			sum -= 0xFFFF;
79147072Sbrooks	}
80147072Sbrooks
81147072Sbrooks	return (sum);
82147072Sbrooks}
83147072Sbrooks
84147072Sbrooksu_int32_t
85147072Sbrookswrapsum(u_int32_t sum)
86147072Sbrooks{
87147072Sbrooks	sum = ~sum & 0xFFFF;
88147072Sbrooks	return (htons(sum));
89147072Sbrooks}
90147072Sbrooks
91147072Sbrooksvoid
92147072Sbrooksassemble_hw_header(struct interface_info *interface, unsigned char *buf,
93252615Spjd    int *bufix)
94147072Sbrooks{
95147072Sbrooks	struct ether_header eh;
96147072Sbrooks
97252615Spjd	memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
98147072Sbrooks	if (interface->hw_address.hlen == sizeof(eh.ether_shost))
99147072Sbrooks		memcpy(eh.ether_shost, interface->hw_address.haddr,
100147072Sbrooks		    sizeof(eh.ether_shost));
101147072Sbrooks	else
102147072Sbrooks		memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
103147072Sbrooks
104147072Sbrooks	eh.ether_type = htons(ETHERTYPE_IP);
105147072Sbrooks
106147072Sbrooks	memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE);
107147072Sbrooks	*bufix += ETHER_HEADER_SIZE;
108147072Sbrooks}
109147072Sbrooks
110147072Sbrooksvoid
111147072Sbrooksassemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
112147072Sbrooks    u_int32_t to, unsigned int port, unsigned char *data, int len)
113147072Sbrooks{
114147072Sbrooks	struct ip ip;
115147072Sbrooks	struct udphdr udp;
116147072Sbrooks
117147072Sbrooks	ip.ip_v = 4;
118147072Sbrooks	ip.ip_hl = 5;
119147072Sbrooks	ip.ip_tos = IPTOS_LOWDELAY;
120147072Sbrooks	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
121147072Sbrooks	ip.ip_id = 0;
122147072Sbrooks	ip.ip_off = 0;
123249766Sjhb	ip.ip_ttl = 128;
124147072Sbrooks	ip.ip_p = IPPROTO_UDP;
125147072Sbrooks	ip.ip_sum = 0;
126147072Sbrooks	ip.ip_src.s_addr = from;
127147072Sbrooks	ip.ip_dst.s_addr = to;
128147072Sbrooks
129147072Sbrooks	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
130198352Sphilip
131198352Sphilip	/*
132198352Sphilip	 * While the BPF -- used for broadcasts -- expects a "true" IP header
133198352Sphilip	 * with all the bytes in network byte order, the raw socket interface
134198352Sphilip	 * which is used for unicasts expects the ip_len field to be in host
135198352Sphilip	 * byte order.  In both cases, the checksum has to be correct, so this
136198352Sphilip	 * is as good a place as any to turn the bytes around again.
137198352Sphilip	 */
138198352Sphilip	if (to != INADDR_BROADCAST)
139198352Sphilip		ip.ip_len = ntohs(ip.ip_len);
140198352Sphilip
141147072Sbrooks	memcpy(&buf[*bufix], &ip, sizeof(ip));
142147072Sbrooks	*bufix += sizeof(ip);
143147072Sbrooks
144147072Sbrooks	udp.uh_sport = htons(LOCAL_PORT);	/* XXX */
145147072Sbrooks	udp.uh_dport = port;			/* XXX */
146147072Sbrooks	udp.uh_ulen = htons(sizeof(udp) + len);
147147072Sbrooks	memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
148147072Sbrooks
149147072Sbrooks	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
150147072Sbrooks	    checksum(data, len, checksum((unsigned char *)&ip.ip_src,
151147072Sbrooks	    2 * sizeof(ip.ip_src),
152147072Sbrooks	    IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
153147072Sbrooks
154147072Sbrooks	memcpy(&buf[*bufix], &udp, sizeof(udp));
155147072Sbrooks	*bufix += sizeof(udp);
156147072Sbrooks}
157147072Sbrooks
158147072Sbrooksssize_t
159147072Sbrooksdecode_hw_header(unsigned char *buf, int bufix, struct hardware *from)
160147072Sbrooks{
161147072Sbrooks	struct ether_header eh;
162147072Sbrooks
163147072Sbrooks	memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE);
164147072Sbrooks
165147072Sbrooks	memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
166147072Sbrooks	from->htype = ARPHRD_ETHER;
167147072Sbrooks	from->hlen = sizeof(eh.ether_shost);
168147072Sbrooks
169147072Sbrooks	return (sizeof(eh));
170147072Sbrooks}
171147072Sbrooks
172147072Sbrooksssize_t
173147072Sbrooksdecode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
174147072Sbrooks    unsigned char *data, int buflen)
175147072Sbrooks{
176147072Sbrooks	struct ip *ip;
177147072Sbrooks	struct udphdr *udp;
178147072Sbrooks	u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
179147072Sbrooks	u_int32_t sum, usum;
180147072Sbrooks	static int ip_packets_seen;
181147072Sbrooks	static int ip_packets_bad_checksum;
182147072Sbrooks	static int udp_packets_seen;
183147072Sbrooks	static int udp_packets_bad_checksum;
184147072Sbrooks	static int udp_packets_length_checked;
185147072Sbrooks	static int udp_packets_length_overflow;
186147072Sbrooks	int len = 0;
187147072Sbrooks
188147072Sbrooks	ip = (struct ip *)(buf + bufix);
189147072Sbrooks	udp = (struct udphdr *)(buf + bufix + ip_len);
190147072Sbrooks
191147072Sbrooks	/* Check the IP header checksum - it should be zero. */
192147072Sbrooks	ip_packets_seen++;
193147072Sbrooks	if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
194147072Sbrooks		ip_packets_bad_checksum++;
195147072Sbrooks		if (ip_packets_seen > 4 &&
196147072Sbrooks		    (ip_packets_seen / ip_packets_bad_checksum) < 2) {
197147072Sbrooks			note("%d bad IP checksums seen in %d packets",
198147072Sbrooks			    ip_packets_bad_checksum, ip_packets_seen);
199147072Sbrooks			ip_packets_seen = ip_packets_bad_checksum = 0;
200147072Sbrooks		}
201147072Sbrooks		return (-1);
202147072Sbrooks	}
203147072Sbrooks
204147072Sbrooks	if (ntohs(ip->ip_len) != buflen)
205147072Sbrooks		debug("ip length %d disagrees with bytes received %d.",
206147072Sbrooks		    ntohs(ip->ip_len), buflen);
207147072Sbrooks
208147072Sbrooks	memcpy(&from->sin_addr, &ip->ip_src, 4);
209147072Sbrooks
210147072Sbrooks	/*
211147072Sbrooks	 * Compute UDP checksums, including the ``pseudo-header'', the
212147072Sbrooks	 * UDP header and the data.   If the UDP checksum field is zero,
213147072Sbrooks	 * we're not supposed to do a checksum.
214147072Sbrooks	 */
215147072Sbrooks	if (!data) {
216147072Sbrooks		data = buf + bufix + ip_len + sizeof(*udp);
217147072Sbrooks		len = ntohs(udp->uh_ulen) - sizeof(*udp);
218147072Sbrooks		udp_packets_length_checked++;
219147072Sbrooks		if (len + data > buf + bufix + buflen) {
220147072Sbrooks			udp_packets_length_overflow++;
221147072Sbrooks			if (udp_packets_length_checked > 4 &&
222147072Sbrooks			    (udp_packets_length_checked /
223147072Sbrooks			    udp_packets_length_overflow) < 2) {
224147072Sbrooks				note("%d udp packets in %d too long - dropped",
225147072Sbrooks				    udp_packets_length_overflow,
226147072Sbrooks				    udp_packets_length_checked);
227147072Sbrooks				udp_packets_length_overflow =
228147072Sbrooks				    udp_packets_length_checked = 0;
229147072Sbrooks			}
230147072Sbrooks			return (-1);
231147072Sbrooks		}
232147072Sbrooks		if (len + data != buf + bufix + buflen)
233147072Sbrooks			debug("accepting packet with data after udp payload.");
234147072Sbrooks	}
235147072Sbrooks
236147072Sbrooks	usum = udp->uh_sum;
237147072Sbrooks	udp->uh_sum = 0;
238147072Sbrooks
239147072Sbrooks	sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
240147072Sbrooks	    checksum(data, len, checksum((unsigned char *)&ip->ip_src,
241147072Sbrooks	    2 * sizeof(ip->ip_src),
242147072Sbrooks	    IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
243147072Sbrooks
244147072Sbrooks	udp_packets_seen++;
245147072Sbrooks	if (usum && usum != sum) {
246147072Sbrooks		udp_packets_bad_checksum++;
247147072Sbrooks		if (udp_packets_seen > 4 &&
248147072Sbrooks		    (udp_packets_seen / udp_packets_bad_checksum) < 2) {
249147072Sbrooks			note("%d bad udp checksums in %d packets",
250147072Sbrooks			    udp_packets_bad_checksum, udp_packets_seen);
251147072Sbrooks			udp_packets_seen = udp_packets_bad_checksum = 0;
252147072Sbrooks		}
253147072Sbrooks		return (-1);
254147072Sbrooks	}
255147072Sbrooks
256147072Sbrooks	memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
257147072Sbrooks
258147072Sbrooks	return (ip_len + sizeof(*udp));
259147072Sbrooks}
260