1 /* $OpenBSD: packet.c,v 1.14 2017/04/05 14:40:56 reyk 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 <arpa/inet.h>
47
48 #include <net/if.h>
49 #include <net/if_enc.h>
50
51 #include <netinet/in.h>
52 #include <netinet/ip.h>
53 #include <netinet/udp.h>
54 #include <netinet/if_ether.h>
55
56 #include <string.h>
57
58 #include "dhcp.h"
59 #include "dhcpd.h"
60 #include "log.h"
61
62
63 u_int32_t checksum(unsigned char *, u_int32_t, u_int32_t);
64 u_int32_t wrapsum(u_int32_t);
65
66 u_int32_t
checksum(unsigned char * buf,u_int32_t nbytes,u_int32_t sum)67 checksum(unsigned char *buf, u_int32_t nbytes, u_int32_t sum)
68 {
69 u_int32_t i;
70
71 /* Checksum all the pairs of bytes first... */
72 for (i = 0; i < (nbytes & ~1U); i += 2) {
73 sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
74 if (sum > 0xFFFF)
75 sum -= 0xFFFF;
76 }
77
78 /*
79 * If there's a single byte left over, checksum it, too.
80 * Network byte order is big-endian, so the remaining byte is
81 * the high byte.
82 */
83 if (i < nbytes) {
84 sum += buf[i] << 8;
85 if (sum > 0xFFFF)
86 sum -= 0xFFFF;
87 }
88
89 return (sum);
90 }
91
92 u_int32_t
wrapsum(u_int32_t sum)93 wrapsum(u_int32_t sum)
94 {
95 sum = ~sum & 0xFFFF;
96 return (htons(sum));
97 }
98
99 ssize_t
assemble_hw_header(unsigned char * buf,size_t buflen,size_t offset,struct packet_ctx * pc,unsigned int intfhtype)100 assemble_hw_header(unsigned char *buf, size_t buflen,
101 size_t offset, struct packet_ctx *pc, unsigned int intfhtype)
102 {
103 struct ether_header eh;
104
105 switch (intfhtype) {
106 case HTYPE_ETHER:
107 if (buflen < offset + ETHER_HDR_LEN)
108 return (-1);
109
110 /* Use the supplied address or let the kernel fill it. */
111 memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN);
112 memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN);
113
114 eh.ether_type = htons(ETHERTYPE_IP);
115
116 memcpy(&buf[offset], &eh, ETHER_HDR_LEN);
117 offset += ETHER_HDR_LEN;
118 break;
119 default:
120 return (-1);
121 }
122
123 return (offset);
124 }
125
126 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 assemble_udp_ip_header(unsigned char *buf, size_t buflen, size_t offset,
128 struct packet_ctx *pc, unsigned char *data, size_t datalen)
129 {
130 struct ip ip;
131 struct udphdr udp;
132
133 if (buflen < offset + sizeof(ip) + sizeof(udp))
134 return (-1);
135
136 ip.ip_v = 4;
137 ip.ip_hl = 5;
138 ip.ip_tos = IPTOS_LOWDELAY;
139 ip.ip_len = htons(sizeof(ip) + sizeof(udp) + datalen);
140 ip.ip_id = 0;
141 ip.ip_off = 0;
142 ip.ip_ttl = 16;
143 ip.ip_p = IPPROTO_UDP;
144 ip.ip_sum = 0;
145 ip.ip_src.s_addr = ss2sin(&pc->pc_src)->sin_addr.s_addr;
146 ip.ip_dst.s_addr = ss2sin(&pc->pc_dst)->sin_addr.s_addr;
147
148 ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
149 memcpy(&buf[offset], &ip, sizeof(ip));
150 offset += sizeof(ip);
151
152 udp.uh_sport = ss2sin(&pc->pc_src)->sin_port;
153 udp.uh_dport = ss2sin(&pc->pc_dst)->sin_port;
154 udp.uh_ulen = htons(sizeof(udp) + datalen);
155 memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
156
157 udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
158 checksum(data, datalen, checksum((unsigned char *)&ip.ip_src,
159 2 * sizeof(ip.ip_src),
160 IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
161
162 memcpy(&buf[offset], &udp, sizeof(udp));
163 offset += sizeof(udp);
164
165 return (offset);
166 }
167
168 ssize_t
decode_hw_header(unsigned char * buf,size_t buflen,size_t offset,struct packet_ctx * pc,unsigned int intfhtype)169 decode_hw_header(unsigned char *buf, size_t buflen,
170 size_t offset, struct packet_ctx *pc, unsigned int intfhtype)
171 {
172 u_int32_t ip_len;
173 struct ip *ip;
174
175 switch (intfhtype) {
176 case HTYPE_IPSEC_TUNNEL:
177 if (buflen < offset + ENC_HDRLEN + sizeof(*ip))
178 return (-1);
179 offset += ENC_HDRLEN;
180 ip_len = (buf[offset] & 0xf) << 2;
181 if (buflen < offset + ip_len)
182 return (-1);
183
184 ip = (struct ip *)(buf + offset);
185
186 /* Encapsulated IP */
187 if (ip->ip_p != IPPROTO_IPIP)
188 return (-1);
189
190 memset(pc->pc_dmac, 0xff, ETHER_ADDR_LEN);
191 offset += ip_len;
192
193 pc->pc_htype = ARPHRD_ETHER;
194 pc->pc_hlen = ETHER_ADDR_LEN;
195 break;
196 case HTYPE_ETHER:
197 if (buflen < offset + ETHER_HDR_LEN)
198 return (-1);
199
200 memcpy(pc->pc_dmac, buf + offset, ETHER_ADDR_LEN);
201 memcpy(pc->pc_smac, buf + offset + ETHER_ADDR_LEN,
202 ETHER_ADDR_LEN);
203 offset += ETHER_HDR_LEN;
204
205 pc->pc_htype = ARPHRD_ETHER;
206 pc->pc_hlen = ETHER_ADDR_LEN;
207 break;
208 default:
209 return (-1);
210 }
211
212 return (offset);
213 }
214
215 ssize_t
decode_udp_ip_header(unsigned char * buf,size_t buflen,size_t offset,struct packet_ctx * pc)216 decode_udp_ip_header(unsigned char *buf, size_t buflen,
217 size_t offset, struct packet_ctx *pc)
218 {
219 struct ip *ip;
220 struct udphdr *udp;
221 unsigned char *data;
222 u_int32_t ip_len;
223 u_int32_t sum, usum;
224 static unsigned int ip_packets_seen;
225 static unsigned int ip_packets_bad_checksum;
226 static unsigned int udp_packets_seen;
227 static unsigned int udp_packets_bad_checksum;
228 static unsigned int udp_packets_length_checked;
229 static unsigned int udp_packets_length_overflow;
230 int len;
231
232 /* Assure that an entire IP header is within the buffer. */
233 if (buflen < offset + sizeof(*ip))
234 return (-1);
235 ip_len = (buf[offset] & 0xf) << 2;
236 if (buflen < offset + ip_len)
237 return (-1);
238
239 ip = (struct ip *)(buf + offset);
240 ip_packets_seen++;
241
242 /* Check the IP header checksum - it should be zero. */
243 if (wrapsum(checksum(buf + offset, ip_len, 0)) != 0) {
244 ip_packets_bad_checksum++;
245 if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 &&
246 (ip_packets_seen / ip_packets_bad_checksum) < 2) {
247 log_info("%u bad IP checksums seen in %u packets",
248 ip_packets_bad_checksum, ip_packets_seen);
249 ip_packets_seen = ip_packets_bad_checksum = 0;
250 }
251 return (-1);
252 }
253
254 pc->pc_src.ss_len = sizeof(struct sockaddr_in);
255 pc->pc_src.ss_family = AF_INET;
256 memcpy(&ss2sin(&pc->pc_src)->sin_addr, &ip->ip_src,
257 sizeof(ss2sin(&pc->pc_src)->sin_addr));
258
259 pc->pc_dst.ss_len = sizeof(struct sockaddr_in);
260 pc->pc_dst.ss_family = AF_INET;
261 memcpy(&ss2sin(&pc->pc_dst)->sin_addr, &ip->ip_dst,
262 sizeof(ss2sin(&pc->pc_dst)->sin_addr));
263
264 #ifdef DEBUG
265 if (buflen != offset + ntohs(ip->ip_len))
266 log_debug("ip length %d disagrees with bytes received %zd.",
267 ntohs(ip->ip_len), buflen - offset);
268 #endif
269
270 /* Assure that the entire IP packet is within the buffer. */
271 if (buflen < offset + ntohs(ip->ip_len))
272 return (-1);
273
274 /* Assure that the UDP header is within the buffer. */
275 if (buflen < offset + ip_len + sizeof(*udp))
276 return (-1);
277 udp = (struct udphdr *)(buf + offset + ip_len);
278 udp_packets_seen++;
279
280 /* Assure that the entire UDP packet is within the buffer. */
281 if (buflen < offset + ip_len + ntohs(udp->uh_ulen))
282 return (-1);
283 data = buf + offset + ip_len + sizeof(*udp);
284
285 /*
286 * Compute UDP checksums, including the ``pseudo-header'', the
287 * UDP header and the data. If the UDP checksum field is zero,
288 * we're not supposed to do a checksum.
289 */
290 udp_packets_length_checked++;
291 len = ntohs(udp->uh_ulen) - sizeof(*udp);
292 if ((len < 0) || (len + data > buf + buflen)) {
293 udp_packets_length_overflow++;
294 if (udp_packets_length_checked > 4 &&
295 udp_packets_length_overflow != 0 &&
296 (udp_packets_length_checked /
297 udp_packets_length_overflow) < 2) {
298 log_info("%u udp packets in %u too long - dropped",
299 udp_packets_length_overflow,
300 udp_packets_length_checked);
301 udp_packets_length_overflow =
302 udp_packets_length_checked = 0;
303 }
304 return (-1);
305 }
306 if (len + data != buf + buflen)
307 log_debug("accepting packet with data after udp payload.");
308
309 usum = udp->uh_sum;
310 udp->uh_sum = 0;
311
312 sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
313 checksum(data, len, checksum((unsigned char *)&ip->ip_src,
314 2 * sizeof(ip->ip_src),
315 IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
316
317 udp_packets_seen++;
318 if (usum && usum != sum) {
319 udp_packets_bad_checksum++;
320 if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 &&
321 (udp_packets_seen / udp_packets_bad_checksum) < 2) {
322 log_info("%u bad udp checksums in %u packets",
323 udp_packets_bad_checksum, udp_packets_seen);
324 udp_packets_seen = udp_packets_bad_checksum = 0;
325 }
326 return (-1);
327 }
328
329 ss2sin(&pc->pc_src)->sin_port = udp->uh_sport;
330 ss2sin(&pc->pc_dst)->sin_port = udp->uh_dport;
331
332 return (offset + ip_len + sizeof(*udp));
333 }
334