1 /*
2 * Copyright (c) 2012, 2013
3 * Inferno Nettverk A/S, Norway. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. The above copyright notice, this list of conditions and the following
9 * disclaimer must appear in all copies of the software, derivative works
10 * or modified versions, and any portions thereof, aswell as in all
11 * supporting documentation.
12 * 2. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by
15 * Inferno Nettverk A/S, Norway.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Inferno Nettverk A/S requests users of this software to return to
31 *
32 * Software Distribution Coordinator or sdc@inet.no
33 * Inferno Nettverk A/S
34 * Oslo Research Park
35 * Gaustadall�en 21
36 * NO-0349 Oslo
37 * Norway
38 *
39 * any improvements or extensions that they make and grant Inferno Nettverk A/S
40 * the rights to redistribute these changes.
41 *
42 */
43
44 #include "common.h"
45
46 static const char rcsid[] =
47 "$Id: sockd_icmp.c,v 1.23 2013/10/27 15:24:42 karls Exp $";
48
49 extern int freefds;
50
51 void
send_icmperror(s,receivedonaddr,originalpacketsrc,packettarget,iplen,udplen,type,code)52 send_icmperror(s, receivedonaddr, originalpacketsrc, packettarget,
53 iplen, udplen, type, code)
54 int s;
55 const struct sockaddr_storage *receivedonaddr;
56 const struct sockaddr_storage *originalpacketsrc;
57 const struct sockaddr_storage *packettarget;
58 const int iplen;
59 const int udplen;
60 const int type;
61 const int code;
62 {
63 const char *function = "send_icmperror()";
64 socklen_t len;
65 struct ip *ip;
66 struct icmp *icmp;
67 struct udphdr *udp;
68 ssize_t rc;
69 char pstr[MAXSOCKADDRSTRING], lstr[MAXSOCKADDRSTRING],
70 tstr[MAXSOCKADDRSTRING];
71 union { /* for alignment reasons. */
72 char data[MIN_ICMPUNREACHLEN];
73 struct icmp icmp;
74 struct ip ip;
75 } packet;
76
77 slog(LOG_DEBUG,
78 "%s: should send icmp type/code %d/%d out on fd %d to %s, "
79 "concerning previously sent/received packet for %s having "
80 "iplen %d and udplen %d, originally received on %s. ",
81 function,
82 type,
83 code,
84 s,
85 originalpacketsrc == NULL ?
86 "0.0.0.0" : sockaddr2string(originalpacketsrc, pstr, sizeof(pstr)),
87 packettarget == NULL ?
88 "0.0.0.0" : sockaddr2string(packettarget, tstr, sizeof(tstr)),
89 iplen,
90 udplen,
91 receivedonaddr == NULL ?
92 "0.0.0.0" : sockaddr2string(receivedonaddr, lstr, sizeof(lstr)));
93
94 if (s == -1) {
95 slog(LOG_DEBUG,
96 "%s: can not send icmp error to %s: have no raw socket to send on",
97 function,
98 originalpacketsrc == NULL ?
99 "0.0.0.0" : sockaddr2string(originalpacketsrc, pstr, sizeof(pstr)));
100
101 return;
102 }
103
104 if (originalpacketsrc == NULL
105 || receivedonaddr == NULL
106 || packettarget == NULL)
107 return;
108
109 len = salen(originalpacketsrc->ss_family);
110
111 if (originalpacketsrc->ss_family != AF_INET) {
112 slog(LOG_DEBUG,
113 "%s: can not send icmp error to %s: no support for %s here yet",
114 function,
115 sockaddr2string(originalpacketsrc, NULL, 0),
116 safamily2string(originalpacketsrc->ss_family));
117
118 return;
119 }
120
121 icmp = (struct icmp *)&packet.icmp;
122 icmp->icmp_type = type;
123 icmp->icmp_code = code;
124 icmp->icmp_cksum = 0;
125
126 /* four unused bytes before ipheader; must be zero. */
127 bzero((char *)icmp + 4, 4);
128 ip = (struct ip *)icmp->icmp_data;
129 ip->ip_hl = MIN_IPHLEN >> 2; /* number of 32bit words. */
130 ip->ip_v = 4;
131 ip->ip_tos = 0;
132 ip->ip_len = htons(iplen == -1 ? MIN_IPHLEN : iplen);
133 ip->ip_id = 0;
134 ip->ip_off = 0;
135 ip->ip_ttl = 1;
136 ip->ip_p = IPPROTO_UDP;
137 ip->ip_sum = 0;
138 ip->ip_src = TOCIN(originalpacketsrc)->sin_addr;
139 ip->ip_dst = TOCIN(receivedonaddr)->sin_addr;
140 ip->ip_sum = in_cksum((uint16_t *)ip, sizeof(*ip));
141
142 udp = (struct udphdr *)((char *)icmp + 8 + (ip->ip_hl << 2));
143 *udphdr_uh_sport(udp) = TOCIN(originalpacketsrc)->sin_port;
144 *udphdr_uh_dport(udp) = TOCIN(receivedonaddr)->sin_port;
145 *udphdr_uh_ulen(udp) = htons(udplen == -1 ? MIN_UDPLEN : udplen);
146 *udphdr_uh_sum(udp) = 0; /* don't know, but once upon a time zero was ok */
147
148 icmp->icmp_cksum = in_cksum((uint16_t *)icmp, sizeof(packet));
149
150 if ((rc = sendto(s,
151 &packet.data,
152 sizeof(packet.data),
153 0,
154 TOCSA(originalpacketsrc),
155 len)) != sizeof(packet))
156 swarn("%s: could not send raw packet of length %lu to %s. Sent %ld",
157 function,
158 (unsigned long)sizeof(packet),
159 sockaddr2string(originalpacketsrc, NULL, 0),
160 (long)rc);
161 else
162 slog(LOG_DEBUG, "%s: sent raw packet of length %lu to %s",
163 function,
164 (unsigned long)sizeof(packet),
165 sockaddr2string(originalpacketsrc, NULL, 0));
166 }
167
168 #if BAREFOOTD
169 rawsocketstatus_t
rawsocket_recv(s,ioc,iov)170 rawsocket_recv(s, ioc, iov)
171 const int s;
172 const size_t ioc;
173 sockd_io_t iov[];
174 {
175 const char *function = "rawsocket_recv()";
176 const size_t maxiterations = 10; /* icmp errors are not the priority. */
177 rawsocketstatus_t rc;
178 union { /* for alignment-reasons. */
179 char data[MAX_IPHLEN + MAX_ICMPUNREACHLEN];
180 struct ip ip;
181 struct icmp icmp;
182 } packet;
183 udptarget_t *client;
184 struct icmp *icmp;
185 struct ip *ip;
186 struct udphdr *udp;
187 struct sockaddr_storage from, srcaddr, dstaddr;
188 socklen_t addrlen;
189 ssize_t r;
190 size_t ioi, iterations;
191 char fromstr[MAXSOCKADDRSTRING],
192 srcstr[MAXSOCKADDRSTRING], dststr[MAXSOCKADDRSTRING];
193 int send_icmp_to_client = 0, send_icmp_to_target = 0;
194
195 rc = RAWSOCKET_NOP;
196 for (iterations = 0; iterations < maxiterations; ++iterations) {
197 addrlen = sizeof(from);
198 if ((r = recvfrom(s,
199 &packet.data,
200 sizeof(packet.data),
201 0,
202 TOSA(&from),
203 &addrlen)) == -1) {
204 if (!ERRNOISTMP(errno))
205 swarn("%s: recvfrom() on raw socket (fd %d) failed", function, s);
206
207 break;
208 }
209
210 if (r < MIN_IPHLEN + MIN_ICMPUNREACHLEN) {
211 slog(LOG_DEBUG,
212 "%s: packet received from %s on raw socket is too short to be of "
213 "interest (%ld/%lu)",
214 function,
215 sockaddr2string(&from, NULL, 0),
216 (long)r,
217 (unsigned long)(MIN_IPHLEN + MIN_ICMPUNREACHLEN));
218
219 continue;
220 }
221
222 ip = &packet.ip;
223 if (r < (ip->ip_hl << 2)) {
224 swarn("%s: strange ... kernel says ip header length in the packet "
225 "from %s is %u bytes long, but read packet size is only %ld. "
226 "Are we reading too little? (Tried to read up to %lu bytes)",
227 function,
228 sockaddr2string(&from, NULL, 0),
229 (unsigned)(ip->ip_hl << 2),
230 (long)r,
231 (unsigned long)sizeof(packet));
232
233 continue;
234 }
235
236 icmp = (struct icmp *)(packet.data + (ip->ip_hl << 2));
237 if (r - (ip->ip_hl << 2) < MIN_ICMPUNREACHLEN) {
238 slog(LOG_DEBUG,
239 "%s: icmp data in packet received from %s on raw socket is too "
240 "short to be of interest (%ld/%lu)",
241 function,
242 sockaddr2string(&from, NULL, 0),
243 (long)(r - (ip->ip_hl << 2)),
244 (unsigned long)MIN_ICMPUNREACHLEN);
245
246 continue;
247 }
248
249 /* ip-packet the icmp error is in reply to. */
250 ip = (struct ip *)(icmp->icmp_data);
251
252 if (sockscf.option.debug >= DEBUG_VERBOSE)
253 slog(LOG_DEBUG,
254 "%s: received raw packet from %s, type/code %d/%d, ip proto %u, "
255 "total length %ld, ip_hl %lu, icmp len %lu",
256 function,
257 inet_ntop(from.ss_family,
258 GET_SOCKADDRADDR(&from),
259 fromstr,
260 sizeof(fromstr)),
261 icmp->icmp_type,
262 icmp->icmp_code,
263 (unsigned)ip->ip_p,
264 (long)r,
265 (unsigned long)(ip->ip_hl << 2),
266 (unsigned long)(r - (ip->ip_hl << 2)));
267
268 if (icmp->icmp_type != ICMP_UNREACH)
269 continue;
270
271 if (ip->ip_p != IPPROTO_UDP)
272 continue;
273
274 bzero(&srcaddr, sizeof(srcaddr));
275 SET_SOCKADDR(&srcaddr, AF_INET);
276 dstaddr = srcaddr;
277
278 udp = (struct udphdr *)((char *)ip + (ip->ip_hl << 2));
279 TOIN(&srcaddr)->sin_addr = ip->ip_src;
280 TOIN(&srcaddr)->sin_port = *udphdr_uh_sport(udp);
281 TOIN(&dstaddr)->sin_addr = ip->ip_dst;
282 TOIN(&dstaddr)->sin_port = *udphdr_uh_dport(udp);
283
284 slog(LOG_DEBUG, "%s: icmp packet is in relation to packet from %s to %s",
285 function,
286 sockaddr2string(&srcaddr, srcstr, sizeof(srcstr)),
287 sockaddr2string(&dstaddr, dststr, sizeof(dststr)));
288
289 /*
290 * Figure out is this icmp error is related to a packet we sent.
291 * Two possibilities if so:
292 * 1: Response to an udpreply forwarded by us from target to client:
293 * - Dstaddr in error packet should match a client addr.
294 * - Srcaddr should match a address used by the client at the given
295 * dstaddr.
296 *
297 * 2: Response to an udp packet forwarded by us from client to target:
298 * - Dstaddr in error packet should match a target address.
299 * - Srcaddr should match a laddr we use for forwarding packets
300 * to the given target address.
301 */
302
303 client = NULL;
304 for (ioi = 0; ioi < ioc; ++ioi) {
305 size_t dsti;
306
307 if (iov[ioi].state.protocol != SOCKS_UDP)
308 continue;
309
310 /*
311 * Check possibility 1 first.
312 */
313 if ((client = clientofclientaddr(&dstaddr,
314 iov[ioi].dst.dstc,
315 iov[ioi].dst.dstv)) != NULL) {
316 const int matches = sockaddrareeq(&srcaddr, &iov[ioi].src.laddr, 0);
317
318 slog(LOG_DEBUG,
319 "%s: dstaddr %s matches a client address, srcaddress %s %s an "
320 "address we listen on",
321 function,
322 sockaddr2string(&dstaddr, dststr, sizeof(dststr)),
323 sockaddr2string(&srcaddr, srcstr, sizeof(srcstr)),
324 matches ? "also matches" : "however does not");
325
326 if (matches) {
327 send_icmp_to_target = 1;
328 break;
329 }
330 else
331 client = NULL;
332 }
333
334 SASSERTX(client == NULL);
335
336 /*
337 * Nope. How about possibility 2)?
338 */
339 for (dsti = 0; dsti < iov[ioi].dst.dstc; ++dsti) {
340 SASSERTX(iov[ioi].dst.dstv[dsti].s != -1);
341
342 if (!sockaddrareeq(&iov[ioi].dst.dstv[dsti].raddr, &dstaddr, 0))
343 continue;
344
345 if (!sockaddrareeq(&srcaddr, &iov[ioi].dst.dstv[dsti].laddr, 0))
346 continue;
347
348 client = &iov[ioi].dst.dstv[dsti];
349 break;
350 }
351
352 if (client != NULL) { /* found our match. */
353 SASSERTX(dsti < iov[ioi].dst.dstc);
354 send_icmp_to_client = 1;
355 break;
356 }
357 }
358
359 if (client != NULL) {
360 struct sockaddr_storage *receivedonaddr, *icmptargetaddr;
361
362 SASSERTX(ioi < ioc);
363
364 SASSERTX(send_icmp_to_client || send_icmp_to_target);
365 SASSERTX(!(send_icmp_to_client && send_icmp_to_target));
366
367 io_syncudp(&iov[ioi], client);
368
369 if (send_icmp_to_target) {
370 /*
371 * error is related to case 1 (packet from target to client).
372 */
373 SASSERTX(sockaddrareeq(&srcaddr, &iov[ioi].src.laddr, 0));
374 SASSERTX(sockaddrareeq(&dstaddr, &client->client, 0));
375 receivedonaddr = &iov[ioi].dst.laddr;
376 icmptargetaddr = &iov[ioi].dst.raddr;
377 }
378 else {
379 /*
380 * error is related to case 2 (packet from client to target).
381 */
382 SASSERTX(send_icmp_to_client);
383
384 SASSERTX(sockaddrareeq(&srcaddr, &iov[ioi].dst.laddr, 0));
385 SASSERTX(sockaddrareeq(&dstaddr, &iov[ioi].dst.raddr, 0));
386 receivedonaddr = &iov[ioi].src.laddr;
387 icmptargetaddr = &iov[ioi].src.raddr;
388 }
389
390 slog(LOG_DEBUG, "%s: packet received from %s is related to client %s",
391 function,
392 sockaddr2string(&from, fromstr, sizeof(fromstr)),
393 sockaddr2string(&iov[ioi].src.raddr, NULL, 0));
394
395 send_icmperror(s,
396 receivedonaddr,
397 icmptargetaddr,
398 &dstaddr,
399 ntohs(ip->ip_len),
400 ntohs(*udphdr_uh_ulen(udp)),
401 icmp->icmp_type,
402 icmp->icmp_code);
403
404 slog(LOG_DEBUG, "%s: removing client %s from iov #%lu",
405 function,
406 sockaddr2string(&iov[ioi].src.raddr, NULL, 0),
407 (unsigned long)ioi);
408
409 io_delete(-1 /* nothing to ack */, &iov[ioi], client->s, IO_CLOSE);
410 rc = RAWSOCKET_IO_DELETED;
411
412 continue;
413 }
414
415 if (sockscf.option.debug >= DEBUG_VERBOSE)
416 slog(LOG_DEBUG,
417 "%s: icmp error received from %s refers to packet from %s to "
418 "%s. Not a session known to us",
419 function,
420 sockaddr2string(&from, fromstr, sizeof(fromstr)),
421 sockaddr2string(&srcaddr, srcstr, sizeof(srcstr)),
422 sockaddr2string(&dstaddr, dststr, sizeof(dststr)));
423
424 }
425
426 return rc;
427 }
428 #endif /* BAREFOOTD */
429