xref: /freebsd-src/sbin/dhclient/packet.c (revision 47c0859616bfefdd59ce6bbd8dd46882cef4527a)
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