VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use