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