1*22c60a6bSreyk /* $OpenBSD: packet.c,v 1.14 2017/04/05 14:40:56 reyk Exp $ */
248be18b4Shenning
348be18b4Shenning /* Packet assembly code, originally contributed by Archie Cobbs. */
448be18b4Shenning
548be18b4Shenning /*
648be18b4Shenning * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
748be18b4Shenning * All rights reserved.
848be18b4Shenning *
948be18b4Shenning * Redistribution and use in source and binary forms, with or without
1048be18b4Shenning * modification, are permitted provided that the following conditions
1148be18b4Shenning * are met:
1248be18b4Shenning *
1348be18b4Shenning * 1. Redistributions of source code must retain the above copyright
1448be18b4Shenning * notice, this list of conditions and the following disclaimer.
1548be18b4Shenning * 2. Redistributions in binary form must reproduce the above copyright
1648be18b4Shenning * notice, this list of conditions and the following disclaimer in the
1748be18b4Shenning * documentation and/or other materials provided with the distribution.
1848be18b4Shenning * 3. Neither the name of The Internet Software Consortium nor the names
1948be18b4Shenning * of its contributors may be used to endorse or promote products derived
2048be18b4Shenning * from this software without specific prior written permission.
2148be18b4Shenning *
2248be18b4Shenning * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
2348be18b4Shenning * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
2448be18b4Shenning * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2548be18b4Shenning * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2648be18b4Shenning * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
2748be18b4Shenning * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2848be18b4Shenning * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2948be18b4Shenning * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
3048be18b4Shenning * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3148be18b4Shenning * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3248be18b4Shenning * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3348be18b4Shenning * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3448be18b4Shenning * SUCH DAMAGE.
3548be18b4Shenning *
3648be18b4Shenning * This software has been written for the Internet Software Consortium
3748be18b4Shenning * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
3848be18b4Shenning * Enterprises. To learn more about the Internet Software Consortium,
3948be18b4Shenning * see ``http://www.vix.com/isc''. To learn more about Vixie
4048be18b4Shenning * Enterprises, see ``http://www.vix.com''.
4148be18b4Shenning */
4248be18b4Shenning
43f70ef60cSkrw #include <sys/types.h>
44f70ef60cSkrw #include <sys/socket.h>
4548be18b4Shenning
46f70ef60cSkrw #include <arpa/inet.h>
47f70ef60cSkrw
48f70ef60cSkrw #include <net/if.h>
494be048dcSreyk #include <net/if_enc.h>
50f70ef60cSkrw
51f70ef60cSkrw #include <netinet/in.h>
5248be18b4Shenning #include <netinet/ip.h>
5348be18b4Shenning #include <netinet/udp.h>
5448be18b4Shenning #include <netinet/if_ether.h>
5548be18b4Shenning
56f70ef60cSkrw #include <string.h>
57f70ef60cSkrw
58f70ef60cSkrw #include "dhcp.h"
59f70ef60cSkrw #include "dhcpd.h"
60986dbb4cSkrw #include "log.h"
61f70ef60cSkrw
62f70ef60cSkrw
63*22c60a6bSreyk u_int32_t checksum(unsigned char *, u_int32_t, u_int32_t);
6448be18b4Shenning u_int32_t wrapsum(u_int32_t);
6548be18b4Shenning
6648be18b4Shenning u_int32_t
checksum(unsigned char * buf,u_int32_t nbytes,u_int32_t sum)67*22c60a6bSreyk checksum(unsigned char *buf, u_int32_t nbytes, u_int32_t sum)
6848be18b4Shenning {
69*22c60a6bSreyk u_int32_t i;
7048be18b4Shenning
7148be18b4Shenning /* Checksum all the pairs of bytes first... */
7248be18b4Shenning for (i = 0; i < (nbytes & ~1U); i += 2) {
7348be18b4Shenning sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
7448be18b4Shenning if (sum > 0xFFFF)
7548be18b4Shenning sum -= 0xFFFF;
7648be18b4Shenning }
7748be18b4Shenning
7848be18b4Shenning /*
7948be18b4Shenning * If there's a single byte left over, checksum it, too.
8048be18b4Shenning * Network byte order is big-endian, so the remaining byte is
8148be18b4Shenning * the high byte.
8248be18b4Shenning */
8348be18b4Shenning if (i < nbytes) {
8448be18b4Shenning sum += buf[i] << 8;
8548be18b4Shenning if (sum > 0xFFFF)
8648be18b4Shenning sum -= 0xFFFF;
8748be18b4Shenning }
8848be18b4Shenning
8948be18b4Shenning return (sum);
9048be18b4Shenning }
9148be18b4Shenning
9248be18b4Shenning u_int32_t
wrapsum(u_int32_t sum)9348be18b4Shenning wrapsum(u_int32_t sum)
9448be18b4Shenning {
9548be18b4Shenning sum = ~sum & 0xFFFF;
9648be18b4Shenning return (htons(sum));
9748be18b4Shenning }
9848be18b4Shenning
99*22c60a6bSreyk ssize_t
assemble_hw_header(unsigned char * buf,size_t buflen,size_t offset,struct packet_ctx * pc,unsigned int intfhtype)100*22c60a6bSreyk assemble_hw_header(unsigned char *buf, size_t buflen,
101*22c60a6bSreyk size_t offset, struct packet_ctx *pc, unsigned int intfhtype)
10248be18b4Shenning {
10348be18b4Shenning struct ether_header eh;
10448be18b4Shenning
105*22c60a6bSreyk switch (intfhtype) {
106*22c60a6bSreyk case HTYPE_ETHER:
107*22c60a6bSreyk if (buflen < offset + ETHER_HDR_LEN)
108*22c60a6bSreyk return (-1);
109*22c60a6bSreyk
110fa3d4f89Srzalamena /* Use the supplied address or let the kernel fill it. */
111fa3d4f89Srzalamena memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN);
112fa3d4f89Srzalamena memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN);
11348be18b4Shenning
11448be18b4Shenning eh.ether_type = htons(ETHERTYPE_IP);
11548be18b4Shenning
116*22c60a6bSreyk memcpy(&buf[offset], &eh, ETHER_HDR_LEN);
117*22c60a6bSreyk offset += ETHER_HDR_LEN;
118*22c60a6bSreyk break;
119*22c60a6bSreyk default:
120*22c60a6bSreyk return (-1);
12148be18b4Shenning }
12248be18b4Shenning
123*22c60a6bSreyk return (offset);
124*22c60a6bSreyk }
125*22c60a6bSreyk
126*22c60a6bSreyk ssize_t
assemble_udp_ip_header(unsigned char * buf,size_t buflen,size_t offset,struct packet_ctx * pc,unsigned char * data,size_t datalen)127*22c60a6bSreyk assemble_udp_ip_header(unsigned char *buf, size_t buflen, size_t offset,
128*22c60a6bSreyk struct packet_ctx *pc, unsigned char *data, size_t datalen)
12948be18b4Shenning {
13048be18b4Shenning struct ip ip;
13148be18b4Shenning struct udphdr udp;
13248be18b4Shenning
133*22c60a6bSreyk if (buflen < offset + sizeof(ip) + sizeof(udp))
134*22c60a6bSreyk return (-1);
135*22c60a6bSreyk
13648be18b4Shenning ip.ip_v = 4;
13748be18b4Shenning ip.ip_hl = 5;
13848be18b4Shenning ip.ip_tos = IPTOS_LOWDELAY;
139*22c60a6bSreyk ip.ip_len = htons(sizeof(ip) + sizeof(udp) + datalen);
14048be18b4Shenning ip.ip_id = 0;
14148be18b4Shenning ip.ip_off = 0;
14248be18b4Shenning ip.ip_ttl = 16;
14348be18b4Shenning ip.ip_p = IPPROTO_UDP;
14448be18b4Shenning ip.ip_sum = 0;
145fa3d4f89Srzalamena ip.ip_src.s_addr = ss2sin(&pc->pc_src)->sin_addr.s_addr;
146fa3d4f89Srzalamena ip.ip_dst.s_addr = ss2sin(&pc->pc_dst)->sin_addr.s_addr;
14748be18b4Shenning
14848be18b4Shenning ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
149*22c60a6bSreyk memcpy(&buf[offset], &ip, sizeof(ip));
150*22c60a6bSreyk offset += sizeof(ip);
15148be18b4Shenning
152fa3d4f89Srzalamena udp.uh_sport = ss2sin(&pc->pc_src)->sin_port;
153fa3d4f89Srzalamena udp.uh_dport = ss2sin(&pc->pc_dst)->sin_port;
154*22c60a6bSreyk udp.uh_ulen = htons(sizeof(udp) + datalen);
15548be18b4Shenning memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
15648be18b4Shenning
15748be18b4Shenning udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
158*22c60a6bSreyk checksum(data, datalen, checksum((unsigned char *)&ip.ip_src,
15948be18b4Shenning 2 * sizeof(ip.ip_src),
16048be18b4Shenning IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
16148be18b4Shenning
162*22c60a6bSreyk memcpy(&buf[offset], &udp, sizeof(udp));
163*22c60a6bSreyk offset += sizeof(udp);
164*22c60a6bSreyk
165*22c60a6bSreyk return (offset);
16648be18b4Shenning }
16748be18b4Shenning
16848be18b4Shenning ssize_t
decode_hw_header(unsigned char * buf,size_t buflen,size_t offset,struct packet_ctx * pc,unsigned int intfhtype)169*22c60a6bSreyk decode_hw_header(unsigned char *buf, size_t buflen,
170*22c60a6bSreyk size_t offset, struct packet_ctx *pc, unsigned int intfhtype)
17148be18b4Shenning {
1724be048dcSreyk u_int32_t ip_len;
1734be048dcSreyk struct ip *ip;
1744be048dcSreyk
175*22c60a6bSreyk switch (intfhtype) {
176*22c60a6bSreyk case HTYPE_IPSEC_TUNNEL:
177*22c60a6bSreyk if (buflen < offset + ENC_HDRLEN + sizeof(*ip))
178*22c60a6bSreyk return (-1);
179*22c60a6bSreyk offset += ENC_HDRLEN;
180*22c60a6bSreyk ip_len = (buf[offset] & 0xf) << 2;
181*22c60a6bSreyk if (buflen < offset + ip_len)
182*22c60a6bSreyk return (-1);
183*22c60a6bSreyk
184*22c60a6bSreyk ip = (struct ip *)(buf + offset);
1854be048dcSreyk
1864be048dcSreyk /* Encapsulated IP */
1874be048dcSreyk if (ip->ip_p != IPPROTO_IPIP)
1884be048dcSreyk return (-1);
1894be048dcSreyk
190fa3d4f89Srzalamena memset(pc->pc_dmac, 0xff, ETHER_ADDR_LEN);
191*22c60a6bSreyk offset += ip_len;
19248be18b4Shenning
193fa3d4f89Srzalamena pc->pc_htype = ARPHRD_ETHER;
194fa3d4f89Srzalamena pc->pc_hlen = ETHER_ADDR_LEN;
195*22c60a6bSreyk break;
196*22c60a6bSreyk case HTYPE_ETHER:
197*22c60a6bSreyk if (buflen < offset + ETHER_HDR_LEN)
198*22c60a6bSreyk return (-1);
199*22c60a6bSreyk
200*22c60a6bSreyk memcpy(pc->pc_dmac, buf + offset, ETHER_ADDR_LEN);
201*22c60a6bSreyk memcpy(pc->pc_smac, buf + offset + ETHER_ADDR_LEN,
202*22c60a6bSreyk ETHER_ADDR_LEN);
203*22c60a6bSreyk offset += ETHER_HDR_LEN;
204*22c60a6bSreyk
205*22c60a6bSreyk pc->pc_htype = ARPHRD_ETHER;
206*22c60a6bSreyk pc->pc_hlen = ETHER_ADDR_LEN;
207*22c60a6bSreyk break;
208*22c60a6bSreyk default:
209*22c60a6bSreyk return (-1);
210*22c60a6bSreyk }
21148be18b4Shenning
2124be048dcSreyk return (offset);
21348be18b4Shenning }
21448be18b4Shenning
21548be18b4Shenning ssize_t
decode_udp_ip_header(unsigned char * buf,size_t buflen,size_t offset,struct packet_ctx * pc)216*22c60a6bSreyk decode_udp_ip_header(unsigned char *buf, size_t buflen,
217*22c60a6bSreyk size_t offset, struct packet_ctx *pc)
21848be18b4Shenning {
21948be18b4Shenning struct ip *ip;
22048be18b4Shenning struct udphdr *udp;
22123d823c3Ssthen unsigned char *data;
222b4e2b639Skrw u_int32_t ip_len;
22348be18b4Shenning u_int32_t sum, usum;
2248ae29202Ssthen static unsigned int ip_packets_seen;
2258ae29202Ssthen static unsigned int ip_packets_bad_checksum;
2268ae29202Ssthen static unsigned int udp_packets_seen;
2278ae29202Ssthen static unsigned int udp_packets_bad_checksum;
2288ae29202Ssthen static unsigned int udp_packets_length_checked;
2298ae29202Ssthen static unsigned int udp_packets_length_overflow;
23023d823c3Ssthen int len;
23148be18b4Shenning
232b4e2b639Skrw /* Assure that an entire IP header is within the buffer. */
233*22c60a6bSreyk if (buflen < offset + sizeof(*ip))
234b4e2b639Skrw return (-1);
235*22c60a6bSreyk ip_len = (buf[offset] & 0xf) << 2;
236*22c60a6bSreyk if (buflen < offset + ip_len)
237b4e2b639Skrw return (-1);
238*22c60a6bSreyk
239*22c60a6bSreyk ip = (struct ip *)(buf + offset);
240b4e2b639Skrw ip_packets_seen++;
24148be18b4Shenning
24248be18b4Shenning /* Check the IP header checksum - it should be zero. */
243*22c60a6bSreyk if (wrapsum(checksum(buf + offset, ip_len, 0)) != 0) {
24448be18b4Shenning ip_packets_bad_checksum++;
24564b20cadStobias if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 &&
24648be18b4Shenning (ip_packets_seen / ip_packets_bad_checksum) < 2) {
247986dbb4cSkrw log_info("%u bad IP checksums seen in %u packets",
24848be18b4Shenning ip_packets_bad_checksum, ip_packets_seen);
24948be18b4Shenning ip_packets_seen = ip_packets_bad_checksum = 0;
25048be18b4Shenning }
25148be18b4Shenning return (-1);
25248be18b4Shenning }
25348be18b4Shenning
254fa3d4f89Srzalamena pc->pc_src.ss_len = sizeof(struct sockaddr_in);
255fa3d4f89Srzalamena pc->pc_src.ss_family = AF_INET;
256fa3d4f89Srzalamena memcpy(&ss2sin(&pc->pc_src)->sin_addr, &ip->ip_src,
257fa3d4f89Srzalamena sizeof(ss2sin(&pc->pc_src)->sin_addr));
258fa3d4f89Srzalamena
259fa3d4f89Srzalamena pc->pc_dst.ss_len = sizeof(struct sockaddr_in);
260fa3d4f89Srzalamena pc->pc_dst.ss_family = AF_INET;
261fa3d4f89Srzalamena memcpy(&ss2sin(&pc->pc_dst)->sin_addr, &ip->ip_dst,
262fa3d4f89Srzalamena sizeof(ss2sin(&pc->pc_dst)->sin_addr));
263b4e2b639Skrw
264b4e2b639Skrw #ifdef DEBUG
265*22c60a6bSreyk if (buflen != offset + ntohs(ip->ip_len))
266*22c60a6bSreyk log_debug("ip length %d disagrees with bytes received %zd.",
267*22c60a6bSreyk ntohs(ip->ip_len), buflen - offset);
268b4e2b639Skrw #endif
26948be18b4Shenning
270b4e2b639Skrw /* Assure that the entire IP packet is within the buffer. */
271*22c60a6bSreyk if (buflen < offset + ntohs(ip->ip_len))
272b4e2b639Skrw return (-1);
273b4e2b639Skrw
274b4e2b639Skrw /* Assure that the UDP header is within the buffer. */
275*22c60a6bSreyk if (buflen < offset + ip_len + sizeof(*udp))
276b4e2b639Skrw return (-1);
277*22c60a6bSreyk udp = (struct udphdr *)(buf + offset + ip_len);
278b4e2b639Skrw udp_packets_seen++;
279b4e2b639Skrw
280b4e2b639Skrw /* Assure that the entire UDP packet is within the buffer. */
281*22c60a6bSreyk if (buflen < offset + ip_len + ntohs(udp->uh_ulen))
282b4e2b639Skrw return (-1);
283*22c60a6bSreyk data = buf + offset + ip_len + sizeof(*udp);
28448be18b4Shenning
28548be18b4Shenning /*
28648be18b4Shenning * Compute UDP checksums, including the ``pseudo-header'', the
28748be18b4Shenning * UDP header and the data. If the UDP checksum field is zero,
28848be18b4Shenning * we're not supposed to do a checksum.
28948be18b4Shenning */
29048be18b4Shenning udp_packets_length_checked++;
291b4e2b639Skrw len = ntohs(udp->uh_ulen) - sizeof(*udp);
292*22c60a6bSreyk if ((len < 0) || (len + data > buf + buflen)) {
29348be18b4Shenning udp_packets_length_overflow++;
29448be18b4Shenning if (udp_packets_length_checked > 4 &&
29564b20cadStobias udp_packets_length_overflow != 0 &&
29648be18b4Shenning (udp_packets_length_checked /
29748be18b4Shenning udp_packets_length_overflow) < 2) {
298986dbb4cSkrw log_info("%u udp packets in %u too long - dropped",
29948be18b4Shenning udp_packets_length_overflow,
30048be18b4Shenning udp_packets_length_checked);
30148be18b4Shenning udp_packets_length_overflow =
30248be18b4Shenning udp_packets_length_checked = 0;
30348be18b4Shenning }
30448be18b4Shenning return (-1);
30548be18b4Shenning }
306*22c60a6bSreyk if (len + data != buf + buflen)
307986dbb4cSkrw log_debug("accepting packet with data after udp payload.");
30848be18b4Shenning
30948be18b4Shenning usum = udp->uh_sum;
31048be18b4Shenning udp->uh_sum = 0;
31148be18b4Shenning
31248be18b4Shenning sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
31348be18b4Shenning checksum(data, len, checksum((unsigned char *)&ip->ip_src,
31448be18b4Shenning 2 * sizeof(ip->ip_src),
31548be18b4Shenning IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
31648be18b4Shenning
31748be18b4Shenning udp_packets_seen++;
31848be18b4Shenning if (usum && usum != sum) {
31948be18b4Shenning udp_packets_bad_checksum++;
32064b20cadStobias if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 &&
32148be18b4Shenning (udp_packets_seen / udp_packets_bad_checksum) < 2) {
322986dbb4cSkrw log_info("%u bad udp checksums in %u packets",
32348be18b4Shenning udp_packets_bad_checksum, udp_packets_seen);
32448be18b4Shenning udp_packets_seen = udp_packets_bad_checksum = 0;
32548be18b4Shenning }
32648be18b4Shenning return (-1);
32748be18b4Shenning }
32848be18b4Shenning
329fa3d4f89Srzalamena ss2sin(&pc->pc_src)->sin_port = udp->uh_sport;
330fa3d4f89Srzalamena ss2sin(&pc->pc_dst)->sin_port = udp->uh_dport;
33148be18b4Shenning
332*22c60a6bSreyk return (offset + ip_len + sizeof(*udp));
33348be18b4Shenning }
334