packet.c revision 149399
155682Smarkm/*	$OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt Exp $	*/
255682Smarkm
355682Smarkm/* Packet assembly code, originally contributed by Archie Cobbs. */
455682Smarkm
555682Smarkm/*
655682Smarkm * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
755682Smarkm * All rights reserved.
855682Smarkm *
955682Smarkm * Redistribution and use in source and binary forms, with or without
1055682Smarkm * modification, are permitted provided that the following conditions
1155682Smarkm * are met:
1255682Smarkm *
1355682Smarkm * 1. Redistributions of source code must retain the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer.
1555682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1655682Smarkm *    notice, this list of conditions and the following disclaimer in the
1755682Smarkm *    documentation and/or other materials provided with the distribution.
1855682Smarkm * 3. Neither the name of The Internet Software Consortium nor the names
1955682Smarkm *    of its contributors may be used to endorse or promote products derived
2055682Smarkm *    from this software without specific prior written permission.
2155682Smarkm *
2255682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
2355682Smarkm * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
2455682Smarkm * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2555682Smarkm * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2655682Smarkm * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
2755682Smarkm * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2855682Smarkm * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2955682Smarkm * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
3055682Smarkm * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3155682Smarkm * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3255682Smarkm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3355682Smarkm * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3455682Smarkm * SUCH DAMAGE.
3555682Smarkm *
3655682Smarkm * This software has been written for the Internet Software Consortium
3755682Smarkm * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
3855682Smarkm * Enterprises.  To learn more about the Internet Software Consortium,
3955682Smarkm * see ``http://www.vix.com/isc''.  To learn more about Vixie
40233294Sstas * Enterprises, see ``http://www.vix.com''.
4155682Smarkm */
4255682Smarkm
4355682Smarkm#include <sys/cdefs.h>
4455682Smarkm__FBSDID("$FreeBSD: head/sbin/dhclient/packet.c 149399 2005-08-23 23:59:55Z brooks $");
4555682Smarkm
4655682Smarkm#include "dhcpd.h"
4755682Smarkm
4855682Smarkm#include <netinet/in_systm.h>
4955682Smarkm#include <netinet/ip.h>
5055682Smarkm#include <netinet/udp.h>
5155682Smarkm#include <netinet/if_ether.h>
5255682Smarkm
5355682Smarkm#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t))
5455682Smarkm
5555682Smarkmu_int32_t	checksum(unsigned char *, unsigned, u_int32_t);
5655682Smarkmu_int32_t	wrapsum(u_int32_t);
5755682Smarkm
5855682Smarkmvoid	assemble_ethernet_header(struct interface_info *, unsigned char *,
5955682Smarkm	    int *, struct hardware *);
6055682Smarkmssize_t	decode_ethernet_header(struct interface_info *, unsigned char *,
6155682Smarkm	    int bufix, struct hardware *);
6255682Smarkm
6355682Smarkmu_int32_t
6472445Sassarchecksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
6555682Smarkm{
6655682Smarkm	int i;
6755682Smarkm
68233294Sstas	/* Checksum all the pairs of bytes first... */
6955682Smarkm	for (i = 0; i < (nbytes & ~1U); i += 2) {
7055682Smarkm		sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
7155682Smarkm		if (sum > 0xFFFF)
7255682Smarkm			sum -= 0xFFFF;
7355682Smarkm	}
7455682Smarkm
7555682Smarkm	/*
7655682Smarkm	 * If there's a single byte left over, checksum it, too.
7755682Smarkm	 * Network byte order is big-endian, so the remaining byte is
7855682Smarkm	 * the high byte.
7955682Smarkm	 */
8055682Smarkm	if (i < nbytes) {
8155682Smarkm		sum += buf[i] << 8;
8255682Smarkm		if (sum > 0xFFFF)
8355682Smarkm			sum -= 0xFFFF;
8455682Smarkm	}
8555682Smarkm
8655682Smarkm	return (sum);
8755682Smarkm}
8855682Smarkm
8955682Smarkmu_int32_t
9055682Smarkmwrapsum(u_int32_t sum)
9155682Smarkm{
9255682Smarkm	sum = ~sum & 0xFFFF;
9355682Smarkm	return (htons(sum));
9455682Smarkm}
9555682Smarkm
9655682Smarkmvoid
9772445Sassarassemble_hw_header(struct interface_info *interface, unsigned char *buf,
9872445Sassar    int *bufix, struct hardware *to)
9972445Sassar{
10055682Smarkm	struct ether_header eh;
10155682Smarkm
10255682Smarkm	if (to != NULL && to->hlen == 6) /* XXX */
10355682Smarkm		memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
10455682Smarkm	else
10555682Smarkm		memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
10672445Sassar	if (interface->hw_address.hlen == sizeof(eh.ether_shost))
10755682Smarkm		memcpy(eh.ether_shost, interface->hw_address.haddr,
10855682Smarkm		    sizeof(eh.ether_shost));
10955682Smarkm	else
11055682Smarkm		memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
11155682Smarkm
11255682Smarkm	eh.ether_type = htons(ETHERTYPE_IP);
11355682Smarkm
11455682Smarkm	memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE);
11555682Smarkm	*bufix += ETHER_HEADER_SIZE;
11655682Smarkm}
11755682Smarkm
11855682Smarkmvoid
11955682Smarkmassemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
12055682Smarkm    u_int32_t to, unsigned int port, unsigned char *data, int len)
12155682Smarkm{
12255682Smarkm	struct ip ip;
12355682Smarkm	struct udphdr udp;
12455682Smarkm
12555682Smarkm	ip.ip_v = 4;
12655682Smarkm	ip.ip_hl = 5;
12755682Smarkm	ip.ip_tos = IPTOS_LOWDELAY;
12855682Smarkm	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
12972445Sassar	ip.ip_id = 0;
13055682Smarkm	ip.ip_off = 0;
13155682Smarkm	ip.ip_ttl = 16;
13255682Smarkm	ip.ip_p = IPPROTO_UDP;
13372445Sassar	ip.ip_sum = 0;
13455682Smarkm	ip.ip_src.s_addr = from;
13555682Smarkm	ip.ip_dst.s_addr = to;
13655682Smarkm
13772445Sassar	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
13855682Smarkm	memcpy(&buf[*bufix], &ip, sizeof(ip));
13978527Sassar	*bufix += sizeof(ip);
14090926Snectar
14190926Snectar	udp.uh_sport = htons(LOCAL_PORT);	/* XXX */
14290926Snectar	udp.uh_dport = port;			/* XXX */
14390926Snectar	udp.uh_ulen = htons(sizeof(udp) + len);
14490926Snectar	memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
14590926Snectar
14690926Snectar	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
14755682Smarkm	    checksum(data, len, checksum((unsigned char *)&ip.ip_src,
14855682Smarkm	    2 * sizeof(ip.ip_src),
149233294Sstas	    IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
150233294Sstas
151102644Snectar	memcpy(&buf[*bufix], &udp, sizeof(udp));
15255682Smarkm	*bufix += sizeof(udp);
15355682Smarkm}
15472445Sassar
15572445Sassarssize_t
15672445Sassardecode_hw_header(unsigned char *buf, int bufix, struct hardware *from)
15755682Smarkm{
15855682Smarkm	struct ether_header eh;
15955682Smarkm
16055682Smarkm	memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE);
16155682Smarkm
16255682Smarkm	memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
16355682Smarkm	from->htype = ARPHRD_ETHER;
16455682Smarkm	from->hlen = sizeof(eh.ether_shost);
16555682Smarkm
16655682Smarkm	return (sizeof(eh));
16755682Smarkm}
16855682Smarkm
16955682Smarkmssize_t
17055682Smarkmdecode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
17155682Smarkm    unsigned char *data, int buflen)
17255682Smarkm{
17355682Smarkm	struct ip *ip;
17455682Smarkm	struct udphdr *udp;
17555682Smarkm	u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
17655682Smarkm	u_int32_t sum, usum;
17755682Smarkm	static int ip_packets_seen;
17855682Smarkm	static int ip_packets_bad_checksum;
17955682Smarkm	static int udp_packets_seen;
18055682Smarkm	static int udp_packets_bad_checksum;
18155682Smarkm	static int udp_packets_length_checked;
18255682Smarkm	static int udp_packets_length_overflow;
18355682Smarkm	int len = 0;
18455682Smarkm
18555682Smarkm	ip = (struct ip *)(buf + bufix);
18655682Smarkm	udp = (struct udphdr *)(buf + bufix + ip_len);
18755682Smarkm
18855682Smarkm	/* Check the IP header checksum - it should be zero. */
18955682Smarkm	ip_packets_seen++;
19055682Smarkm	if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
19155682Smarkm		ip_packets_bad_checksum++;
19255682Smarkm		if (ip_packets_seen > 4 &&
19355682Smarkm		    (ip_packets_seen / ip_packets_bad_checksum) < 2) {
19455682Smarkm			note("%d bad IP checksums seen in %d packets",
19555682Smarkm			    ip_packets_bad_checksum, ip_packets_seen);
19655682Smarkm			ip_packets_seen = ip_packets_bad_checksum = 0;
19755682Smarkm		}
19855682Smarkm		return (-1);
199233294Sstas	}
200233294Sstas
20155682Smarkm	if (ntohs(ip->ip_len) != buflen)
20255682Smarkm		debug("ip length %d disagrees with bytes received %d.",
20355682Smarkm		    ntohs(ip->ip_len), buflen);
20455682Smarkm
20555682Smarkm	memcpy(&from->sin_addr, &ip->ip_src, 4);
20655682Smarkm
20755682Smarkm	/*
20855682Smarkm	 * Compute UDP checksums, including the ``pseudo-header'', the
20955682Smarkm	 * UDP header and the data.   If the UDP checksum field is zero,
21055682Smarkm	 * we're not supposed to do a checksum.
21155682Smarkm	 */
21255682Smarkm	if (!data) {
21355682Smarkm		data = buf + bufix + ip_len + sizeof(*udp);
21455682Smarkm		len = ntohs(udp->uh_ulen) - sizeof(*udp);
21555682Smarkm		udp_packets_length_checked++;
21655682Smarkm		if (len + data > buf + bufix + buflen) {
21755682Smarkm			udp_packets_length_overflow++;
21855682Smarkm			if (udp_packets_length_checked > 4 &&
21955682Smarkm			    (udp_packets_length_checked /
22055682Smarkm			    udp_packets_length_overflow) < 2) {
22155682Smarkm				note("%d udp packets in %d too long - dropped",
22255682Smarkm				    udp_packets_length_overflow,
22355682Smarkm				    udp_packets_length_checked);
22455682Smarkm				udp_packets_length_overflow =
22555682Smarkm				    udp_packets_length_checked = 0;
22655682Smarkm			}
22755682Smarkm			return (-1);
22855682Smarkm		}
22955682Smarkm		if (len + data != buf + bufix + buflen)
23055682Smarkm			debug("accepting packet with data after udp payload.");
23155682Smarkm	}
23255682Smarkm
23355682Smarkm	usum = udp->uh_sum;
23455682Smarkm	udp->uh_sum = 0;
23555682Smarkm
23655682Smarkm	sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
237	    checksum(data, len, checksum((unsigned char *)&ip->ip_src,
238	    2 * sizeof(ip->ip_src),
239	    IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
240
241	udp_packets_seen++;
242	if (usum && usum != sum) {
243		udp_packets_bad_checksum++;
244		if (udp_packets_seen > 4 &&
245		    (udp_packets_seen / udp_packets_bad_checksum) < 2) {
246			note("%d bad udp checksums in %d packets",
247			    udp_packets_bad_checksum, udp_packets_seen);
248			udp_packets_seen = udp_packets_bad_checksum = 0;
249		}
250		return (-1);
251	}
252
253	memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
254
255	return (ip_len + sizeof(*udp));
256}
257