xref: /freebsd/sbin/dhclient/packet.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
147c08596SBrooks Davis /*	$OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt Exp $	*/
247c08596SBrooks Davis 
347c08596SBrooks Davis /* Packet assembly code, originally contributed by Archie Cobbs. */
447c08596SBrooks Davis 
58a16b7a1SPedro F. Giffuni /*-
68a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
78a16b7a1SPedro F. Giffuni  *
847c08596SBrooks Davis  * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
947c08596SBrooks Davis  * All rights reserved.
1047c08596SBrooks Davis  *
1147c08596SBrooks Davis  * Redistribution and use in source and binary forms, with or without
1247c08596SBrooks Davis  * modification, are permitted provided that the following conditions
1347c08596SBrooks Davis  * are met:
1447c08596SBrooks Davis  *
1547c08596SBrooks Davis  * 1. Redistributions of source code must retain the above copyright
1647c08596SBrooks Davis  *    notice, this list of conditions and the following disclaimer.
1747c08596SBrooks Davis  * 2. Redistributions in binary form must reproduce the above copyright
1847c08596SBrooks Davis  *    notice, this list of conditions and the following disclaimer in the
1947c08596SBrooks Davis  *    documentation and/or other materials provided with the distribution.
2047c08596SBrooks Davis  * 3. Neither the name of The Internet Software Consortium nor the names
2147c08596SBrooks Davis  *    of its contributors may be used to endorse or promote products derived
2247c08596SBrooks Davis  *    from this software without specific prior written permission.
2347c08596SBrooks Davis  *
2447c08596SBrooks Davis  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
2547c08596SBrooks Davis  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
2647c08596SBrooks Davis  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2747c08596SBrooks Davis  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2847c08596SBrooks Davis  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
2947c08596SBrooks Davis  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3047c08596SBrooks Davis  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3147c08596SBrooks Davis  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
3247c08596SBrooks Davis  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3347c08596SBrooks Davis  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3447c08596SBrooks Davis  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3547c08596SBrooks Davis  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3647c08596SBrooks Davis  * SUCH DAMAGE.
3747c08596SBrooks Davis  *
3847c08596SBrooks Davis  * This software has been written for the Internet Software Consortium
3947c08596SBrooks Davis  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
4047c08596SBrooks Davis  * Enterprises.  To learn more about the Internet Software Consortium,
4147c08596SBrooks Davis  * see ``http://www.vix.com/isc''.  To learn more about Vixie
4247c08596SBrooks Davis  * Enterprises, see ``http://www.vix.com''.
4347c08596SBrooks Davis  */
4447c08596SBrooks Davis 
458794fdbbSBrooks Davis #include <sys/cdefs.h>
4647c08596SBrooks Davis #include "dhcpd.h"
4747c08596SBrooks Davis 
4847c08596SBrooks Davis #include <netinet/in_systm.h>
4947c08596SBrooks Davis #include <netinet/ip.h>
5047c08596SBrooks Davis #include <netinet/udp.h>
5147c08596SBrooks Davis #include <netinet/if_ether.h>
5247c08596SBrooks Davis 
5347c08596SBrooks Davis #define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t))
5447c08596SBrooks Davis 
5547c08596SBrooks Davis u_int32_t	checksum(unsigned char *, unsigned, u_int32_t);
5647c08596SBrooks Davis u_int32_t	wrapsum(u_int32_t);
5747c08596SBrooks Davis 
5847c08596SBrooks Davis u_int32_t
checksum(unsigned char * buf,unsigned nbytes,u_int32_t sum)5947c08596SBrooks Davis checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
6047c08596SBrooks Davis {
61afe6f835SAlan Somers 	unsigned i;
6247c08596SBrooks Davis 
6347c08596SBrooks Davis 	/* Checksum all the pairs of bytes first... */
6447c08596SBrooks Davis 	for (i = 0; i < (nbytes & ~1U); i += 2) {
6547c08596SBrooks Davis 		sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
6647c08596SBrooks Davis 		if (sum > 0xFFFF)
6747c08596SBrooks Davis 			sum -= 0xFFFF;
6847c08596SBrooks Davis 	}
6947c08596SBrooks Davis 
7047c08596SBrooks Davis 	/*
7147c08596SBrooks Davis 	 * If there's a single byte left over, checksum it, too.
7247c08596SBrooks Davis 	 * Network byte order is big-endian, so the remaining byte is
7347c08596SBrooks Davis 	 * the high byte.
7447c08596SBrooks Davis 	 */
7547c08596SBrooks Davis 	if (i < nbytes) {
7647c08596SBrooks Davis 		sum += buf[i] << 8;
7747c08596SBrooks Davis 		if (sum > 0xFFFF)
7847c08596SBrooks Davis 			sum -= 0xFFFF;
7947c08596SBrooks Davis 	}
8047c08596SBrooks Davis 
8147c08596SBrooks Davis 	return (sum);
8247c08596SBrooks Davis }
8347c08596SBrooks Davis 
8447c08596SBrooks Davis u_int32_t
wrapsum(u_int32_t sum)8547c08596SBrooks Davis wrapsum(u_int32_t sum)
8647c08596SBrooks Davis {
8747c08596SBrooks Davis 	sum = ~sum & 0xFFFF;
8847c08596SBrooks Davis 	return (htons(sum));
8947c08596SBrooks Davis }
9047c08596SBrooks Davis 
9147c08596SBrooks Davis void
assemble_hw_header(struct interface_info * interface,unsigned char * buf,int * bufix)9247c08596SBrooks Davis assemble_hw_header(struct interface_info *interface, unsigned char *buf,
93d1f4d854SPawel Jakub Dawidek     int *bufix)
9447c08596SBrooks Davis {
9547c08596SBrooks Davis 	struct ether_header eh;
9647c08596SBrooks Davis 
9747c08596SBrooks Davis 	memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
9847c08596SBrooks Davis 	if (interface->hw_address.hlen == sizeof(eh.ether_shost))
9947c08596SBrooks Davis 		memcpy(eh.ether_shost, interface->hw_address.haddr,
10047c08596SBrooks Davis 		    sizeof(eh.ether_shost));
10147c08596SBrooks Davis 	else
10247c08596SBrooks Davis 		memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
10347c08596SBrooks Davis 
10447c08596SBrooks Davis 	eh.ether_type = htons(ETHERTYPE_IP);
10547c08596SBrooks Davis 
10647c08596SBrooks Davis 	memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE);
10747c08596SBrooks Davis 	*bufix += ETHER_HEADER_SIZE;
10847c08596SBrooks Davis }
10947c08596SBrooks Davis 
11047c08596SBrooks Davis void
assemble_udp_ip_header(unsigned char * buf,int * bufix,u_int32_t from,u_int32_t to,unsigned int port,unsigned char * data,int len)11147c08596SBrooks Davis assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
11247c08596SBrooks Davis     u_int32_t to, unsigned int port, unsigned char *data, int len)
11347c08596SBrooks Davis {
11447c08596SBrooks Davis 	struct ip ip;
11547c08596SBrooks Davis 	struct udphdr udp;
11647c08596SBrooks Davis 
11747c08596SBrooks Davis 	ip.ip_v = 4;
11847c08596SBrooks Davis 	ip.ip_hl = 5;
11947c08596SBrooks Davis 	ip.ip_tos = IPTOS_LOWDELAY;
12047c08596SBrooks Davis 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
12147c08596SBrooks Davis 	ip.ip_id = 0;
12247c08596SBrooks Davis 	ip.ip_off = 0;
1239291a1cdSJohn Baldwin 	ip.ip_ttl = 128;
12447c08596SBrooks Davis 	ip.ip_p = IPPROTO_UDP;
12547c08596SBrooks Davis 	ip.ip_sum = 0;
12647c08596SBrooks Davis 	ip.ip_src.s_addr = from;
12747c08596SBrooks Davis 	ip.ip_dst.s_addr = to;
12847c08596SBrooks Davis 
12947c08596SBrooks Davis 	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
13047c08596SBrooks Davis 	memcpy(&buf[*bufix], &ip, sizeof(ip));
13147c08596SBrooks Davis 	*bufix += sizeof(ip);
13247c08596SBrooks Davis 
13347c08596SBrooks Davis 	udp.uh_sport = htons(LOCAL_PORT);	/* XXX */
13447c08596SBrooks Davis 	udp.uh_dport = port;			/* XXX */
13547c08596SBrooks Davis 	udp.uh_ulen = htons(sizeof(udp) + len);
13647c08596SBrooks Davis 	memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
13747c08596SBrooks Davis 
13847c08596SBrooks Davis 	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
13947c08596SBrooks Davis 	    checksum(data, len, checksum((unsigned char *)&ip.ip_src,
14047c08596SBrooks Davis 	    2 * sizeof(ip.ip_src),
14147c08596SBrooks Davis 	    IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
14247c08596SBrooks Davis 
14347c08596SBrooks Davis 	memcpy(&buf[*bufix], &udp, sizeof(udp));
14447c08596SBrooks Davis 	*bufix += sizeof(udp);
14547c08596SBrooks Davis }
14647c08596SBrooks Davis 
14747c08596SBrooks Davis ssize_t
decode_hw_header(unsigned char * buf,int bufix,struct hardware * from)14847c08596SBrooks Davis decode_hw_header(unsigned char *buf, int bufix, struct hardware *from)
14947c08596SBrooks Davis {
15047c08596SBrooks Davis 	struct ether_header eh;
15147c08596SBrooks Davis 
15247c08596SBrooks Davis 	memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE);
15347c08596SBrooks Davis 
15447c08596SBrooks Davis 	memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
15547c08596SBrooks Davis 	from->htype = ARPHRD_ETHER;
15647c08596SBrooks Davis 	from->hlen = sizeof(eh.ether_shost);
15747c08596SBrooks Davis 
158*abf5bff7SFranco Fichtner 	return (sizeof(eh) + (ntohs(eh.ether_type) == ETHERTYPE_VLAN ?
159*abf5bff7SFranco Fichtner 	    ETHER_VLAN_ENCAP_LEN : 0));
16047c08596SBrooks Davis }
16147c08596SBrooks Davis 
16247c08596SBrooks Davis ssize_t
decode_udp_ip_header(unsigned char * buf,int bufix,struct sockaddr_in * from,unsigned char * data,int buflen)16347c08596SBrooks Davis decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
16447c08596SBrooks Davis     unsigned char *data, int buflen)
16547c08596SBrooks Davis {
16647c08596SBrooks Davis 	struct ip *ip;
16747c08596SBrooks Davis 	struct udphdr *udp;
16847c08596SBrooks Davis 	u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
16947c08596SBrooks Davis 	u_int32_t sum, usum;
17047c08596SBrooks Davis 	static int ip_packets_seen;
17147c08596SBrooks Davis 	static int ip_packets_bad_checksum;
17247c08596SBrooks Davis 	static int udp_packets_seen;
17347c08596SBrooks Davis 	static int udp_packets_bad_checksum;
17447c08596SBrooks Davis 	static int udp_packets_length_checked;
17547c08596SBrooks Davis 	static int udp_packets_length_overflow;
17647c08596SBrooks Davis 	int len = 0;
17747c08596SBrooks Davis 
17847c08596SBrooks Davis 	ip = (struct ip *)(buf + bufix);
17947c08596SBrooks Davis 	udp = (struct udphdr *)(buf + bufix + ip_len);
18047c08596SBrooks Davis 
18147c08596SBrooks Davis 	/* Check the IP header checksum - it should be zero. */
18247c08596SBrooks Davis 	ip_packets_seen++;
18347c08596SBrooks Davis 	if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
18447c08596SBrooks Davis 		ip_packets_bad_checksum++;
185a3ae40c7SMark Johnston 		if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 &&
18647c08596SBrooks Davis 		    (ip_packets_seen / ip_packets_bad_checksum) < 2) {
18747c08596SBrooks Davis 			note("%d bad IP checksums seen in %d packets",
18847c08596SBrooks Davis 			    ip_packets_bad_checksum, ip_packets_seen);
18947c08596SBrooks Davis 			ip_packets_seen = ip_packets_bad_checksum = 0;
19047c08596SBrooks Davis 		}
19147c08596SBrooks Davis 		return (-1);
19247c08596SBrooks Davis 	}
19347c08596SBrooks Davis 
19447c08596SBrooks Davis 	if (ntohs(ip->ip_len) != buflen)
19547c08596SBrooks Davis 		debug("ip length %d disagrees with bytes received %d.",
19647c08596SBrooks Davis 		    ntohs(ip->ip_len), buflen);
19747c08596SBrooks Davis 
19847c08596SBrooks Davis 	memcpy(&from->sin_addr, &ip->ip_src, 4);
19947c08596SBrooks Davis 
20047c08596SBrooks Davis 	/*
20147c08596SBrooks Davis 	 * Compute UDP checksums, including the ``pseudo-header'', the
20247c08596SBrooks Davis 	 * UDP header and the data.   If the UDP checksum field is zero,
20347c08596SBrooks Davis 	 * we're not supposed to do a checksum.
20447c08596SBrooks Davis 	 */
20547c08596SBrooks Davis 	if (!data) {
20647c08596SBrooks Davis 		data = buf + bufix + ip_len + sizeof(*udp);
20747c08596SBrooks Davis 		len = ntohs(udp->uh_ulen) - sizeof(*udp);
20847c08596SBrooks Davis 		udp_packets_length_checked++;
20947c08596SBrooks Davis 		if (len + data > buf + bufix + buflen) {
21047c08596SBrooks Davis 			udp_packets_length_overflow++;
21147c08596SBrooks Davis 			if (udp_packets_length_checked > 4 &&
21247c08596SBrooks Davis 			    (udp_packets_length_checked /
21347c08596SBrooks Davis 			    udp_packets_length_overflow) < 2) {
21447c08596SBrooks Davis 				note("%d udp packets in %d too long - dropped",
21547c08596SBrooks Davis 				    udp_packets_length_overflow,
21647c08596SBrooks Davis 				    udp_packets_length_checked);
21747c08596SBrooks Davis 				udp_packets_length_overflow =
21847c08596SBrooks Davis 				    udp_packets_length_checked = 0;
21947c08596SBrooks Davis 			}
22047c08596SBrooks Davis 			return (-1);
22147c08596SBrooks Davis 		}
22247c08596SBrooks Davis 		if (len + data != buf + bufix + buflen)
22347c08596SBrooks Davis 			debug("accepting packet with data after udp payload.");
22447c08596SBrooks Davis 	}
22547c08596SBrooks Davis 
22647c08596SBrooks Davis 	usum = udp->uh_sum;
22747c08596SBrooks Davis 	udp->uh_sum = 0;
22847c08596SBrooks Davis 
22947c08596SBrooks Davis 	sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
23047c08596SBrooks Davis 	    checksum(data, len, checksum((unsigned char *)&ip->ip_src,
23147c08596SBrooks Davis 	    2 * sizeof(ip->ip_src),
23247c08596SBrooks Davis 	    IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
23347c08596SBrooks Davis 
23447c08596SBrooks Davis 	udp_packets_seen++;
23547c08596SBrooks Davis 	if (usum && usum != sum) {
23647c08596SBrooks Davis 		udp_packets_bad_checksum++;
237a3ae40c7SMark Johnston 		if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 &&
23847c08596SBrooks Davis 		    (udp_packets_seen / udp_packets_bad_checksum) < 2) {
23947c08596SBrooks Davis 			note("%d bad udp checksums in %d packets",
24047c08596SBrooks Davis 			    udp_packets_bad_checksum, udp_packets_seen);
24147c08596SBrooks Davis 			udp_packets_seen = udp_packets_bad_checksum = 0;
24247c08596SBrooks Davis 		}
24347c08596SBrooks Davis 		return (-1);
24447c08596SBrooks Davis 	}
24547c08596SBrooks Davis 
24647c08596SBrooks Davis 	memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
24747c08596SBrooks Davis 
24847c08596SBrooks Davis 	return (ip_len + sizeof(*udp));
24947c08596SBrooks Davis }
250