VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/ip_icmp.c@ 50980

Last change on this file since 50980 was 50980, checked in by vboxsync, 11 years ago

NAT: Rewrite icmp_error(). r91460 messed it up and all that jerking
around of m_data/m_len made it not only wrong but also practically
unreadable.

Always truncate the original datagram to ip header plus 8 bytes of
payload (as required by the RFC) to simplify things.

Don't append "message" to the datagram, whether DEBUG or not. This
might have been a useful communication channel for the original slirp,
but 90s are over and the host process is not far away from us
reachable only via 9600 link that is already engaged into doing slirp.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.3 KB
Line 
1/* $Id: ip_icmp.c 50980 2014-04-05 02:21:29Z vboxsync $ */
2/** @file
3 * NAT - IP/ICMP handling.
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*
19 * This code is based on:
20 *
21 * Copyright (c) 1982, 1986, 1988, 1993
22 * The Regents of the University of California. All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 * 3. All advertising materials mentioning features or use of this software
33 * must display the following acknowledgement:
34 * This product includes software developed by the University of
35 * California, Berkeley and its contributors.
36 * 4. Neither the name of the University nor the names of its contributors
37 * may be used to endorse or promote products derived from this software
38 * without specific prior written permission.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 *
52 * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
53 * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
54 */
55
56#include "slirp.h"
57#include "ip_icmp.h"
58#ifdef RT_OS_WINDOWS
59# include <Icmpapi.h>
60# include <Iphlpapi.h>
61# include <iprt/ldr.h>
62#endif
63
64/* The message sent when emulating PING */
65/* Be nice and tell them it's just a psuedo-ping packet */
66static const char icmp_ping_msg[] = "This is a psuedo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
67
68/* list of actions for icmp_error() on RX of an icmp message */
69static const int icmp_flush[19] =
70{
71/* ECHO REPLY (0) */ 0,
72 1,
73 1,
74/* DEST UNREACH (3) */ 1,
75/* SOURCE QUENCH (4)*/ 1,
76/* REDIRECT (5) */ 1,
77 1,
78 1,
79/* ECHO (8) */ 0,
80/* ROUTERADVERT (9) */ 1,
81/* ROUTERSOLICIT (10) */ 1,
82/* TIME EXCEEDED (11) */ 1,
83/* PARAMETER PROBLEM (12) */ 1,
84/* TIMESTAMP (13) */ 0,
85/* TIMESTAMP REPLY (14) */ 0,
86/* INFO (15) */ 0,
87/* INFO REPLY (16) */ 0,
88/* ADDR MASK (17) */ 0,
89/* ADDR MASK REPLY (18) */ 0
90};
91
92static void icmp_cache_clean(PNATState pData, int iEntries);
93
94int
95icmp_init(PNATState pData, int iIcmpCacheLimit)
96{
97 pData->icmp_socket.so_type = IPPROTO_ICMP;
98 pData->icmp_socket.so_state = SS_ISFCONNECTED;
99 if (iIcmpCacheLimit < 0)
100 {
101 LogRel(("NAT: iIcmpCacheLimit is invalid %d, will be alter to default value 100\n", iIcmpCacheLimit));
102 iIcmpCacheLimit = 100;
103 }
104 pData->iIcmpCacheLimit = iIcmpCacheLimit;
105#ifndef RT_OS_WINDOWS
106# ifndef RT_OS_DARWIN
107 pData->icmp_socket.s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
108# else /* !RT_OS_DARWIN */
109 pData->icmp_socket.s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
110# endif /* RT_OS_DARWIN */
111 if (pData->icmp_socket.s == -1)
112 {
113 int rc = RTErrConvertFromErrno(errno);
114 LogRel(("NAT: ICMP/ping not available (could not open ICMP socket, error %Rrc)\n", rc));
115 return 1;
116 }
117 fd_nonblock(pData->icmp_socket.s);
118 NSOCK_INC();
119
120#else /* RT_OS_WINDOWS */
121 /* Resolve symbols we need. */
122 {
123 RTLDRMOD hLdrMod;
124 int rc = RTLdrLoadSystem("Iphlpapi.dll", true /*fNoUnload*/, &hLdrMod);
125 if (RT_SUCCESS(rc))
126 {
127 pData->pfIcmpParseReplies = (long (WINAPI *)(void *, long))RTLdrGetFunction(hLdrMod, "IcmpParseReplies");
128 pData->pfIcmpCloseHandle = (BOOL (WINAPI *)(HANDLE))RTLdrGetFunction(hLdrMod, "IcmpCloseHandle");
129 rc = RTLdrGetSymbol(hLdrMod, "GetAdaptersAddresses", (void **)&pData->pfGetAdaptersAddresses);
130 if (RT_FAILURE(rc))
131 LogRel(("NAT: Can't find GetAdapterAddresses in Iphlpapi.dll\n"));
132 RTLdrClose(hLdrMod);
133 }
134
135 if (pData->pfIcmpParseReplies == NULL)
136 {
137 int rc = RTLdrLoadSystem("Icmp.dll", true /*fNoUnload*/, &hLdrMod);
138 if (RT_FAILURE(rc))
139 {
140 LogRel(("NAT: Icmp.dll could not be loaded: %Rrc\n", rc));
141 return 1;
142 }
143 pData->pfIcmpParseReplies = (long (WINAPI *)(void *, long))RTLdrGetFunction(hLdrMod, "IcmpParseReplies");
144 pData->pfIcmpCloseHandle = (BOOL (WINAPI *)(HANDLE))RTLdrGetFunction(hLdrMod, "IcmpCloseHandle");
145 RTLdrClose(hLdrMod);
146 }
147 }
148 if (pData->pfIcmpParseReplies == NULL)
149 {
150 LogRel(("NAT: Can't find IcmpParseReplies symbol\n"));
151 return 1;
152 }
153 if (pData->pfIcmpCloseHandle == NULL)
154 {
155 LogRel(("NAT: Can't find IcmpCloseHandle symbol\n"));
156 return 1;
157 }
158
159 pData->icmp_socket.sh = IcmpCreateFile();
160 pData->phEvents[VBOX_ICMP_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
161 pData->cbIcmpBuffer = sizeof(ICMP_ECHO_REPLY) * 10;
162 pData->pvIcmpBuffer = RTMemAlloc(pData->cbIcmpBuffer);
163#endif /* RT_OS_WINDOWS */
164
165 LIST_INIT(&pData->icmp_msg_head);
166 return 0;
167}
168
169/**
170 * Cleans ICMP cache.
171 */
172void
173icmp_finit(PNATState pData)
174{
175 icmp_cache_clean(pData, -1);
176#ifdef RT_OS_WINDOWS
177 pData->pfIcmpCloseHandle(pData->icmp_socket.sh);
178 RTMemFree(pData->pvIcmpBuffer);
179#else
180 closesocket(pData->icmp_socket.s);
181#endif
182}
183
184/*
185 * ip here is ip header + 64bytes readed from ICMP packet
186 */
187struct icmp_msg *
188icmp_find_original_mbuf(PNATState pData, struct ip *ip)
189{
190 struct mbuf *m0;
191 struct ip *ip0;
192 struct icmp *icp, *icp0;
193 struct icmp_msg *icm = NULL;
194 int found = 0;
195 struct udphdr *udp;
196 struct tcphdr *tcp;
197 struct socket *head_socket = NULL;
198 struct socket *last_socket = NULL;
199 struct socket *so = NULL;
200 struct in_addr faddr;
201 u_short lport, fport;
202
203 faddr.s_addr = ~0;
204
205 lport = ~0;
206 fport = ~0;
207
208
209 LogFlowFunc(("ENTER: ip->ip_p:%d\n", ip->ip_p));
210 switch (ip->ip_p)
211 {
212 case IPPROTO_ICMP:
213 icp = (struct icmp *)((char *)ip + (ip->ip_hl << 2));
214 LIST_FOREACH(icm, &pData->icmp_msg_head, im_list)
215 {
216 m0 = icm->im_m;
217 ip0 = mtod(m0, struct ip *);
218 if (ip0->ip_p != IPPROTO_ICMP)
219 {
220 /* try next item */
221 continue;
222 }
223 icp0 = (struct icmp *)((char *)ip0 + (ip0->ip_hl << 2));
224 /*
225 * IP could pointer to ICMP_REPLY datagram (1)
226 * or pointer IP header in ICMP payload in case of
227 * ICMP_TIMXCEED or ICMP_UNREACH (2)
228 *
229 * if (1) and then ICMP (type should be ICMP_ECHOREPLY) and we need check that
230 * IP.IP_SRC == IP0.IP_DST received datagramm comes from destination.
231 *
232 * if (2) then check that payload ICMP has got type ICMP_ECHO and
233 * IP.IP_DST == IP0.IP_DST destination of returned datagram is the same as
234 * one was sent.
235 */
236 if ( ( (icp->icmp_type != ICMP_ECHO && ip->ip_src.s_addr == ip0->ip_dst.s_addr)
237 || (icp->icmp_type == ICMP_ECHO && ip->ip_dst.s_addr == ip0->ip_dst.s_addr))
238 && icp->icmp_id == icp0->icmp_id
239 && icp->icmp_seq == icp0->icmp_seq)
240 {
241 found = 1;
242 Log(("Have found %R[natsock]\n", icm->im_so));
243 break;
244 }
245 Log(("Have found nothing\n"));
246 }
247 break;
248
249 /*
250 * for TCP and UDP logic little bit reverted, we try to find the HOST socket
251 * from which the IP package has been sent.
252 */
253 case IPPROTO_UDP:
254 head_socket = &udb;
255 udp = (struct udphdr *)((char *)ip + (ip->ip_hl << 2));
256 faddr.s_addr = ip->ip_dst.s_addr;
257 fport = udp->uh_dport;
258 lport = udp->uh_sport;
259 last_socket = udp_last_so;
260 /* fall through */
261
262 case IPPROTO_TCP:
263 if (head_socket == NULL)
264 {
265 tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
266 head_socket = &tcb; /* head_socket could be initialized with udb*/
267 faddr.s_addr = ip->ip_dst.s_addr;
268 fport = tcp->th_dport;
269 lport = tcp->th_sport;
270 last_socket = tcp_last_so;
271 }
272 /* check last socket first */
273 if ( last_socket->so_faddr.s_addr == faddr.s_addr
274 && last_socket->so_fport == fport
275 && last_socket->so_hlport == lport)
276 {
277 found = 1;
278 so = last_socket;
279 goto sofound;
280 }
281 for (so = head_socket->so_prev; so != head_socket; so = so->so_prev)
282 {
283 /* Should be reaplaced by hash here */
284 Log(("trying:%R[natsock] against %RTnaipv4:%d lport=%d hlport=%d\n", so, &faddr, fport, lport, so->so_hlport));
285 if ( so->so_faddr.s_addr == faddr.s_addr
286 && so->so_fport == fport
287 && so->so_hlport == lport)
288 {
289 found = 1;
290 break;
291 }
292 }
293 break;
294
295 default:
296 Log(("NAT:ICMP: unsupported protocol(%d)\n", ip->ip_p));
297 }
298 sofound:
299 if (found == 1 && icm == NULL)
300 {
301 if (so->so_state == SS_NOFDREF)
302 {
303 /* socket is shutdowning we've already sent ICMP on it.*/
304 Log(("NAT: Received icmp on shutdowning socket (probably corresponding ICMP socket has been already sent)\n"));
305 return NULL;
306 }
307 icm = RTMemAlloc(sizeof(struct icmp_msg));
308 icm->im_m = so->so_m;
309 icm->im_so = so;
310 found = 1;
311 Log(("hit:%R[natsock]\n", so));
312 /*XXX: this storage not very long,
313 * better add flag if it should removed from lis
314 */
315 LIST_INSERT_HEAD(&pData->icmp_msg_head, icm, im_list);
316 pData->cIcmpCacheSize++;
317 if (pData->cIcmpCacheSize > pData->iIcmpCacheLimit)
318 icmp_cache_clean(pData, pData->iIcmpCacheLimit/2);
319 LogFlowFunc(("LEAVE: icm:%p\n", icm));
320 return (icm);
321 }
322 if (found == 1)
323 {
324 LogFlowFunc(("LEAVE: icm:%p\n", icm));
325 return icm;
326 }
327
328 LogFlowFunc(("LEAVE: NULL\n"));
329 return NULL;
330}
331
332/**
333 * iEntries how many entries to leave, if iEntries < 0, clean all
334 */
335static void icmp_cache_clean(PNATState pData, int iEntries)
336{
337 int iIcmpCount = 0;
338 struct icmp_msg *icm = NULL;
339 LogFlowFunc(("iEntries:%d\n", iEntries));
340 if (iEntries > pData->cIcmpCacheSize)
341 {
342 LogFlowFuncLeave();
343 return;
344 }
345 while(!LIST_EMPTY(&pData->icmp_msg_head))
346 {
347 icm = LIST_FIRST(&pData->icmp_msg_head);
348 if ( iEntries > 0
349 && iIcmpCount < iEntries)
350 {
351 iIcmpCount++;
352 continue;
353 }
354
355 LIST_REMOVE(icm, im_list);
356 if (icm->im_m)
357 {
358 pData->cIcmpCacheSize--;
359 m_freem(pData, icm->im_m);
360 }
361 RTMemFree(icm);
362 }
363 LogFlowFuncLeave();
364}
365
366static int
367icmp_attach(PNATState pData, struct mbuf *m)
368{
369 struct icmp_msg *icm;
370 struct ip *ip;
371 ip = mtod(m, struct ip *);
372 Assert(ip->ip_p == IPPROTO_ICMP);
373 icm = RTMemAlloc(sizeof(struct icmp_msg));
374 icm->im_m = m;
375 icm->im_so = m->m_so;
376 LIST_INSERT_HEAD(&pData->icmp_msg_head, icm, im_list);
377 pData->cIcmpCacheSize++;
378 if (pData->cIcmpCacheSize > pData->iIcmpCacheLimit)
379 icmp_cache_clean(pData, pData->iIcmpCacheLimit/2);
380 return 0;
381}
382
383/*
384 * Process a received ICMP message.
385 */
386void
387icmp_input(PNATState pData, struct mbuf *m, int hlen)
388{
389 register struct icmp *icp;
390 void *icp_buf = NULL;
391 register struct ip *ip = mtod(m, struct ip *);
392 int icmplen = ip->ip_len;
393 int status;
394 uint32_t dst;
395#if !defined(RT_OS_WINDOWS)
396 int ttl;
397#endif
398
399 /* int code; */
400
401 LogFlowFunc(("ENTER: m = %lx, m_len = %d\n", (long)m, m ? m->m_len : 0));
402
403 icmpstat.icps_received++;
404
405 /*
406 * Locate icmp structure in mbuf, and check
407 * that its not corrupted and of at least minimum length.
408 */
409 if (icmplen < ICMP_MINLEN)
410 {
411 /* min 8 bytes payload */
412 icmpstat.icps_tooshort++;
413 goto end_error_free_m;
414 }
415
416 m->m_len -= hlen;
417 m->m_data += hlen;
418
419 if (cksum(m, icmplen))
420 {
421 icmpstat.icps_checksum++;
422 goto end_error_free_m;
423 }
424
425 if (m->m_next)
426 {
427 icp_buf = RTMemAlloc(icmplen);
428 if (!icp_buf)
429 {
430 Log(("NAT: not enought memory to allocate the buffer\n"));
431 goto end_error_free_m;
432 }
433 m_copydata(m, 0, icmplen, icp_buf);
434 icp = (struct icmp *)icp_buf;
435 }
436 else
437 icp = mtod(m, struct icmp *);
438
439 m->m_len += hlen;
440 m->m_data -= hlen;
441
442 /* icmpstat.icps_inhist[icp->icmp_type]++; */
443 /* code = icp->icmp_code; */
444
445 LogFlow(("icmp_type = %d\n", icp->icmp_type));
446 switch (icp->icmp_type)
447 {
448 case ICMP_ECHO:
449 ip->ip_len += hlen; /* since ip_input subtracts this */
450 dst = ip->ip_dst.s_addr;
451 if ( CTL_CHECK(dst, CTL_ALIAS)
452 || CTL_CHECK(dst, CTL_DNS)
453 || CTL_CHECK(dst, CTL_TFTP))
454 {
455 icp->icmp_type = ICMP_ECHOREPLY;
456 ip->ip_dst.s_addr = ip->ip_src.s_addr;
457 ip->ip_src.s_addr = dst;
458 icmp_reflect(pData, m);
459 goto done;
460 }
461 else
462 {
463 struct sockaddr_in addr;
464#ifdef RT_OS_WINDOWS
465 IP_OPTION_INFORMATION ipopt;
466 int error;
467#endif
468 addr.sin_family = AF_INET;
469 if ((ip->ip_dst.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
470 {
471 /* It's an alias */
472 switch (RT_N2H_U32(ip->ip_dst.s_addr) & ~pData->netmask)
473 {
474 case CTL_DNS:
475 case CTL_ALIAS:
476 default:
477 addr.sin_addr = loopback_addr;
478 break;
479 }
480 }
481 else
482 addr.sin_addr.s_addr = ip->ip_dst.s_addr;
483#ifndef RT_OS_WINDOWS
484 if (pData->icmp_socket.s != -1)
485 {
486 ssize_t rc;
487 static bool fIcmpSocketErrorReported;
488 ttl = ip->ip_ttl;
489 Log(("NAT/ICMP: try to set TTL(%d)\n", ttl));
490 status = setsockopt(pData->icmp_socket.s, IPPROTO_IP, IP_TTL,
491 (void *)&ttl, sizeof(ttl));
492 if (status < 0)
493 Log(("NAT: Error (%s) occurred while setting TTL attribute of IP packet\n",
494 strerror(errno)));
495 rc = sendto(pData->icmp_socket.s, icp, icmplen, 0,
496 (struct sockaddr *)&addr, sizeof(addr));
497 if (rc >= 0)
498 {
499 m->m_so = &pData->icmp_socket;
500 icmp_attach(pData, m);
501 /* don't let m_freem at the end free atached buffer */
502 goto done;
503 }
504
505
506 if (!fIcmpSocketErrorReported)
507 {
508 LogRel(("icmp_input udp sendto tx errno = %d (%s)\n",
509 errno, strerror(errno)));
510 fIcmpSocketErrorReported = true;
511 }
512 icmp_error(pData, m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
513 }
514#else /* RT_OS_WINDOWS */
515 pData->icmp_socket.so_laddr.s_addr = ip->ip_src.s_addr; /* XXX: hack*/
516 pData->icmp_socket.so_icmp_id = icp->icmp_id;
517 pData->icmp_socket.so_icmp_seq = icp->icmp_seq;
518 memset(&ipopt, 0, sizeof(IP_OPTION_INFORMATION));
519 ipopt.Ttl = ip->ip_ttl;
520 status = IcmpSendEcho2(pData->icmp_socket.sh /*=handle*/,
521 pData->phEvents[VBOX_ICMP_EVENT_INDEX] /*=Event*/,
522 NULL /*=ApcRoutine*/,
523 NULL /*=ApcContext*/,
524 addr.sin_addr.s_addr /*=DestinationAddress*/,
525 icp->icmp_data /*=RequestData*/,
526 icmplen - ICMP_MINLEN /*=RequestSize*/,
527 &ipopt /*=RequestOptions*/,
528 pData->pvIcmpBuffer /*=ReplyBuffer*/,
529 pData->cbIcmpBuffer /*=ReplySize*/,
530 1 /*=Timeout in ms*/);
531 error = GetLastError();
532 if ( status != 0
533 || error == ERROR_IO_PENDING)
534 {
535 /* no error! */
536 m->m_so = &pData->icmp_socket;
537 icmp_attach(pData, m);
538 /* don't let m_freem at the end free atached buffer */
539 goto done;
540 }
541 Log(("NAT: Error (%d) occurred while sending ICMP (", error));
542 switch (error)
543 {
544 case ERROR_INVALID_PARAMETER:
545 Log(("icmp_socket:%lx is invalid)\n", pData->icmp_socket.s));
546 break;
547 case ERROR_NOT_SUPPORTED:
548 Log(("operation is unsupported)\n"));
549 break;
550 case ERROR_NOT_ENOUGH_MEMORY:
551 Log(("OOM!!!)\n"));
552 break;
553 case IP_BUF_TOO_SMALL:
554 Log(("Buffer too small)\n"));
555 break;
556 default:
557 Log(("Other error!!!)\n"));
558 break;
559 }
560#endif /* RT_OS_WINDOWS */
561 } /* if ip->ip_dst.s_addr == alias_addr.s_addr */
562 break;
563 case ICMP_UNREACH:
564 case ICMP_TIMXCEED:
565 /* @todo(vvl): both up cases comes from guest,
566 * indeed right solution would be find the socket
567 * corresponding to ICMP data and close it.
568 */
569 case ICMP_PARAMPROB:
570 case ICMP_SOURCEQUENCH:
571 case ICMP_TSTAMP:
572 case ICMP_MASKREQ:
573 case ICMP_REDIRECT:
574 icmpstat.icps_notsupp++;
575 break;
576
577 default:
578 icmpstat.icps_badtype++;
579 } /* switch */
580
581end_error_free_m:
582 m_freem(pData, m);
583
584done:
585 if (icp_buf)
586 RTMemFree(icp_buf);
587}
588
589
590/**
591 * Send an ICMP message in response to a situation
592 *
593 * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
594 * MUST NOT change this header information.
595 * MUST NOT reply to a multicast/broadcast IP address.
596 * MUST NOT reply to a multicast/broadcast MAC address.
597 * MUST reply to only the first fragment.
598 *
599 * Send ICMP_UNREACH back to the source regarding msrc.
600 * It is reported as the bad ip packet. The header should
601 * be fully correct and in host byte order.
602 * ICMP fragmentation is illegal. All machines must accept 576 bytes in one
603 * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
604 * @note: implementation note: MSIZE is 256 bytes (minimal buffer), m_getjcl we allocate two mbufs on: clust_zone
605 * and mbuf_zone. the maximum payload 256 - 14 (Ethernet header) - 20 (IPv4 hdr) - 8 (ICMPv4 header) = 214
606 *
607 * @note This function will free msrc!
608 */
609
610void icmp_error(PNATState pData, struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message)
611{
612 unsigned ohlen, olen;
613 struct mbuf *m;
614 struct ip *oip, *ip;
615 struct icmp *icp;
616 void *payload;
617
618 LogFlow(("icmp_error: msrc = %p, msrc_len = %d\n",
619 (void *)msrc, msrc ? msrc->m_len : 0));
620
621 if (RT_UNLIKELY(msrc == NULL))
622 goto end_error;
623
624 M_ASSERTPKTHDR(msrc);
625
626 if ( type != ICMP_UNREACH
627 && type != ICMP_TIMXCEED
628 && type != ICMP_SOURCEQUENCH)
629 goto end_error;
630
631 oip = mtod(msrc, struct ip *);
632 LogFunc(("msrc: %RTnaipv4 -> %RTnaipv4\n", oip->ip_src, oip->ip_dst));
633
634 if (oip->ip_src.s_addr == INADDR_ANY)
635 goto end_error;
636
637 if (oip->ip_off & IP_OFFMASK)
638 goto end_error; /* Only reply to fragment 0 */
639
640 ohlen = oip->ip_hl * 4;
641 AssertStmt(ohlen >= sizeof(struct ip), goto end_error);
642
643 olen = oip->ip_len;
644 AssertStmt(olen >= ohlen, goto end_error);
645
646 if (oip->ip_p == IPPROTO_ICMP)
647 {
648 struct icmp *oicp = (struct icmp *)((char *)oip + ohlen);
649 /*
650 * Assume any unknown ICMP type is an error. This isn't
651 * specified by the RFC, but think about it..
652 */
653 if (oicp->icmp_type > ICMP_MAXTYPE || icmp_flush[oicp->icmp_type])
654 goto end_error;
655 }
656
657 /* undo byte order conversions done in ip_input() */
658 HTONS(oip->ip_len);
659 HTONS(oip->ip_id);
660 HTONS(oip->ip_off);
661
662 m = m_gethdr(pData, M_NOWAIT, MT_HEADER);
663 if (RT_UNLIKELY(m == NULL))
664 goto end_error;
665
666 m->m_flags |= M_SKIP_FIREWALL;
667 m->m_data += if_maxlinkhdr;
668
669 ip = mtod(m, struct ip *);
670 m->m_pkthdr.header = (void *)ip;
671
672 /* fill in ip (ip_output0() does the boilerplate for us) */
673 ip->ip_tos = ((oip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
674 /* ip->ip_len will be set later */
675 ip->ip_off = 0;
676 ip->ip_ttl = MAXTTL;
677 ip->ip_p = IPPROTO_ICMP;
678 ip->ip_src = alias_addr;
679 ip->ip_dst = oip->ip_src;
680
681 /* fill in icmp */
682 icp = (struct icmp *)((char *)ip + sizeof(*ip));
683 icp->icmp_type = type;
684 icp->icmp_code = code;
685 icp->icmp_id = 0;
686 icp->icmp_seq = 0;
687
688 /* fill in icmp payload: original ip header plus 8 bytes of its payload */
689 if (olen > ohlen + 8)
690 olen = ohlen + 8;
691 payload = (void *)((char *)icp + ICMP_MINLEN);
692 memcpy(payload, oip, olen);
693
694 /*
695 * Original code appended this message after the payload. This
696 * might have been a good idea for real slirp, as it provided a
697 * communication channel with the remote host. But 90s are over.
698 */
699 NOREF(message);
700
701 /* hide ip header for icmp checksum calculation */
702 m->m_data += sizeof(struct ip);
703 m->m_len = ICMP_MINLEN + /* truncated */ olen;
704
705 icp->icmp_cksum = 0;
706 icp->icmp_cksum = cksum(m, m->m_len);
707
708 /* reveal ip header */
709 m->m_data -= sizeof(struct ip);
710 m->m_len += sizeof(struct ip);
711 ip->ip_len = m->m_len;
712
713 (void) ip_output0(pData, (struct socket *)NULL, m, 1);
714
715 icmpstat.icps_reflect++;
716
717 /* clear source datagramm in positive branch */
718 m_freem(pData, msrc);
719 LogFlowFuncLeave();
720 return;
721
722end_error:
723
724 /*
725 * clear source datagramm in case if some of requirement haven't been met.
726 */
727 if (msrc)
728 m_freem(pData, msrc);
729
730 {
731 static bool fIcmpErrorReported;
732 if (!fIcmpErrorReported)
733 {
734 LogRel(("NAT: error occurred while sending ICMP error message\n"));
735 fIcmpErrorReported = true;
736 }
737 }
738 LogFlowFuncLeave();
739}
740
741/*
742 * Reflect the ip packet back to the source
743 * Note: m isn't duplicated by this method and more delivered to ip_output then.
744 */
745void
746icmp_reflect(PNATState pData, struct mbuf *m)
747{
748 register struct ip *ip = mtod(m, struct ip *);
749 int hlen = ip->ip_hl << 2;
750 register struct icmp *icp;
751 LogFlowFunc(("ENTER: m:%p\n", m));
752
753 /*
754 * Send an icmp packet back to the ip level,
755 * after supplying a checksum.
756 */
757 m->m_data += hlen;
758 m->m_len -= hlen;
759 icp = mtod(m, struct icmp *);
760
761 icp->icmp_cksum = 0;
762 icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
763
764 m->m_data -= hlen;
765 m->m_len += hlen;
766
767 (void) ip_output(pData, (struct socket *)NULL, m);
768
769 icmpstat.icps_reflect++;
770 LogFlowFuncLeave();
771}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette