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