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