VirtualBox

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

Last change on this file since 38927 was 38927, checked in by vboxsync, 13 years ago

NAT: Don't let ICMP cache to grow without limitations.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.7 KB
Line 
1/* $Id: ip_icmp.c 38927 2011-10-01 03:28:49Z vboxsync $ */
2/** @file
3 * NAT - IP/ICMP handling.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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#endif
62
63/* The message sent when emulating PING */
64/* Be nice and tell them it's just a psuedo-ping packet */
65static const char icmp_ping_msg[] = "This is a psuedo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
66
67/* list of actions for icmp_error() on RX of an icmp message */
68static const int icmp_flush[19] =
69{
70/* ECHO REPLY (0) */ 0,
71 1,
72 1,
73/* DEST UNREACH (3) */ 1,
74/* SOURCE QUENCH (4)*/ 1,
75/* REDIRECT (5) */ 1,
76 1,
77 1,
78/* ECHO (8) */ 0,
79/* ROUTERADVERT (9) */ 1,
80/* ROUTERSOLICIT (10) */ 1,
81/* TIME EXCEEDED (11) */ 1,
82/* PARAMETER PROBLEM (12) */ 1,
83/* TIMESTAMP (13) */ 0,
84/* TIMESTAMP REPLY (14) */ 0,
85/* INFO (15) */ 0,
86/* INFO REPLY (16) */ 0,
87/* ADDR MASK (17) */ 0,
88/* ADDR MASK REPLY (18) */ 0
89};
90
91static int icmp_cache_count(PNATState pData);
92static void icmp_cache_clean(PNATState pData, int iEntries);
93
94int
95icmp_init(PNATState pData)
96{
97 pData->icmp_socket.so_type = IPPROTO_ICMP;
98 pData->icmp_socket.so_state = SS_ISFCONNECTED;
99#ifndef RT_OS_WINDOWS
100# ifndef RT_OS_DARWIN
101 pData->icmp_socket.s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
102# else /* !RT_OS_DARWIN */
103 pData->icmp_socket.s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
104# endif /* RT_OS_DARWIN */
105 if (pData->icmp_socket.s == -1)
106 {
107 int rc = RTErrConvertFromErrno(errno);
108 LogRel(("NAT: ICMP/ping not available (could not open ICMP socket, error %Rrc)\n", rc));
109 return 1;
110 }
111 fd_nonblock(pData->icmp_socket.s);
112 NSOCK_INC();
113#else /* RT_OS_WINDOWS */
114 pData->hmIcmpLibrary = LoadLibrary("Iphlpapi.dll");
115 if (pData->hmIcmpLibrary != NULL)
116 {
117 pData->pfIcmpParseReplies = (long (WINAPI *)(void *, long))
118 GetProcAddress(pData->hmIcmpLibrary, "IcmpParseReplies");
119 pData->pfIcmpCloseHandle = (BOOL (WINAPI *)(HANDLE))
120 GetProcAddress(pData->hmIcmpLibrary, "IcmpCloseHandle");
121 pData->pfGetAdaptersAddresses = (ULONG (WINAPI *)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG))
122 GetProcAddress(pData->hmIcmpLibrary, "GetAdaptersAddresses");
123 if (pData->pfGetAdaptersAddresses == NULL)
124 {
125 LogRel(("NAT: Can't find GetAdapterAddresses in Iphlpapi.dll\n"));
126 }
127 }
128
129 if (pData->pfIcmpParseReplies == NULL)
130 {
131 if(pData->pfGetAdaptersAddresses == NULL)
132 FreeLibrary(pData->hmIcmpLibrary);
133 pData->hmIcmpLibrary = LoadLibrary("Icmp.dll");
134 if (pData->hmIcmpLibrary == NULL)
135 {
136 LogRel(("NAT: Icmp.dll could not be loaded\n"));
137 return 1;
138 }
139 pData->pfIcmpParseReplies = (long (WINAPI *)(void *, long))
140 GetProcAddress(pData->hmIcmpLibrary, "IcmpParseReplies");
141 pData->pfIcmpCloseHandle = (BOOL (WINAPI *)(HANDLE))
142 GetProcAddress(pData->hmIcmpLibrary, "IcmpCloseHandle");
143 }
144 if (pData->pfIcmpParseReplies == NULL)
145 {
146 LogRel(("NAT: Can't find IcmpParseReplies symbol\n"));
147 FreeLibrary(pData->hmIcmpLibrary);
148 return 1;
149 }
150 if (pData->pfIcmpCloseHandle == NULL)
151 {
152 LogRel(("NAT: Can't find IcmpCloseHandle symbol\n"));
153 FreeLibrary(pData->hmIcmpLibrary);
154 return 1;
155 }
156 pData->icmp_socket.sh = IcmpCreateFile();
157 pData->phEvents[VBOX_ICMP_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
158 pData->szIcmpBuffer = sizeof(ICMP_ECHO_REPLY) * 10;
159 pData->pvIcmpBuffer = RTMemAlloc(pData->szIcmpBuffer);
160#endif /* RT_OS_WINDOWS */
161 LIST_INIT(&pData->icmp_msg_head);
162 return 0;
163}
164
165/*
166 * ip here is ip header + 64bytes readed from ICMP packet
167 */
168struct icmp_msg *
169icmp_find_original_mbuf(PNATState pData, struct ip *ip)
170{
171 struct mbuf *m0;
172 struct ip *ip0;
173 struct icmp *icp, *icp0;
174 struct icmp_msg *icm = NULL;
175 int found = 0;
176 struct udphdr *udp;
177 struct tcphdr *tcp;
178 struct socket *head_socket = NULL;
179 struct socket *last_socket = NULL;
180 struct socket *so = NULL;
181 struct in_addr laddr, faddr;
182 u_short lport, fport;
183
184 laddr.s_addr = ~0;
185 faddr.s_addr = ~0;
186
187 lport = ~0;
188 fport = ~0;
189
190
191 LogFlowFunc(("ENTER: ip->ip_p:%d\n", ip->ip_p));
192 switch (ip->ip_p)
193 {
194 case IPPROTO_ICMP:
195 icp = (struct icmp *)((char *)ip + (ip->ip_hl << 2));
196 LIST_FOREACH(icm, &pData->icmp_msg_head, im_list)
197 {
198 m0 = icm->im_m;
199 ip0 = mtod(m0, struct ip *);
200 if (ip0->ip_p != IPPROTO_ICMP)
201 {
202 /* try next item */
203 continue;
204 }
205 icp0 = (struct icmp *)((char *)ip0 + (ip0->ip_hl << 2));
206 /*
207 * IP could pointer to ICMP_REPLY datagram (1)
208 * or pointer IP header in ICMP payload in case of
209 * ICMP_TIMXCEED or ICMP_UNREACH (2)
210 *
211 * if (1) and then ICMP (type should be ICMP_ECHOREPLY) and we need check that
212 * IP.IP_SRC == IP0.IP_DST received datagramm comes from destination.
213 *
214 * if (2) then check that payload ICMP has got type ICMP_ECHO and
215 * IP.IP_DST == IP0.IP_DST destination of returned datagram is the same as
216 * one was sent.
217 */
218 if ( ( (icp->icmp_type != ICMP_ECHO && ip->ip_src.s_addr == ip0->ip_dst.s_addr)
219 || (icp->icmp_type == ICMP_ECHO && ip->ip_dst.s_addr == ip0->ip_dst.s_addr))
220 && icp->icmp_id == icp0->icmp_id
221 && icp->icmp_seq == icp0->icmp_seq)
222 {
223 found = 1;
224 Log(("Have found %R[natsock]\n", icm->im_so));
225 break;
226 }
227 Log(("Have found nothing\n"));
228 }
229 break;
230
231 /*
232 * for TCP and UDP logic little bit reverted, we try to find the HOST socket
233 * from which the IP package has been sent.
234 */
235 case IPPROTO_UDP:
236 head_socket = &udb;
237 udp = (struct udphdr *)((char *)ip + (ip->ip_hl << 2));
238 faddr.s_addr = ip->ip_dst.s_addr;
239 fport = udp->uh_dport;
240 laddr.s_addr = ip->ip_src.s_addr;
241 lport = udp->uh_sport;
242 last_socket = udp_last_so;
243 /* fall through */
244
245 case IPPROTO_TCP:
246 if (head_socket == NULL)
247 {
248 tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
249 head_socket = &tcb; /* head_socket could be initialized with udb*/
250 faddr.s_addr = ip->ip_dst.s_addr;
251 fport = tcp->th_dport;
252 laddr.s_addr = ip->ip_src.s_addr;
253 lport = tcp->th_sport;
254 last_socket = tcp_last_so;
255 }
256 /* check last socket first */
257 if ( last_socket->so_faddr.s_addr == faddr.s_addr
258 && last_socket->so_fport == fport
259 && last_socket->so_hlport == lport)
260 {
261 found = 1;
262 so = last_socket;
263 goto sofound;
264 }
265 for (so = head_socket->so_prev; so != head_socket; so = so->so_prev)
266 {
267 /* Should be reaplaced by hash here */
268 Log(("trying:%R[natsock] against %RTnaipv4:%d lport=%d hlport=%d\n", so, &faddr, fport, lport, so->so_hlport));
269 if ( so->so_faddr.s_addr == faddr.s_addr
270 && so->so_fport == fport
271 && so->so_hlport == lport)
272 {
273 found = 1;
274 break;
275 }
276 }
277 break;
278
279 default:
280 Log(("NAT:ICMP: unsupported protocol(%d)\n", ip->ip_p));
281 }
282 sofound:
283 if (found == 1 && icm == NULL)
284 {
285 if (so->so_state == SS_NOFDREF)
286 {
287 /* socket is shutdowning we've already sent ICMP on it.*/
288 Log(("NAT: Received icmp on shutdowning socket (probably corresponding ICMP socket has been already sent)\n"));
289 return NULL;
290 }
291 icm = RTMemAlloc(sizeof(struct icmp_msg));
292 icm->im_m = so->so_m;
293 icm->im_so = so;
294 found = 1;
295 Log(("hit:%R[natsock]\n", so));
296 /*XXX: this storage not very long,
297 * better add flag if it should removed from lis
298 */
299 LIST_INSERT_HEAD(&pData->icmp_msg_head, icm, im_list);
300 LogFlowFunc(("LEAVE: icm:%p\n", icm));
301 return (icm);
302 }
303 if (found == 1)
304 {
305 LogFlowFunc(("LEAVE: icm:%p\n", icm));
306 return icm;
307 }
308
309 LogFlowFunc(("LEAVE: NULL\n"));
310 return NULL;
311}
312
313static int icmp_cache_count(PNATState pData)
314{
315 int iIcmpCount = 0;
316 struct icmp_msg *icm = NULL;
317 LogFlowFuncEnter();
318 LIST_FOREACH(icm, &pData->icmp_msg_head, im_list)
319 iIcmpCount++;
320 LogFlowFunc(("LEAVE: %d\n", iIcmpCount));
321 return iIcmpCount;
322}
323/**
324 * iEntries how many entries to leave, if iEntries < 0, clean all
325 */
326static void icmp_cache_clean(PNATState pData, int iEntries)
327{
328 int iIcmpCount = 0;
329 struct icmp_msg *icm = NULL;
330 LogFlowFunc(("iEntries:%d\n", iEntries));
331 if (iEntries > icmp_cache_count(pData))
332 {
333 LogFlowFuncLeave();
334 return;
335 }
336 while(!LIST_EMPTY(&pData->icmp_msg_head))
337 {
338 icm = LIST_FIRST(&pData->icmp_msg_head);
339 if ( iEntries > 0
340 && iIcmpCount < iEntries)
341 {
342 iIcmpCount++;
343 continue;
344 }
345
346 LIST_REMOVE(icm, im_list);
347 if (icm->im_m)
348 m_freem(pData, icm->im_m);
349 RTMemFree(icm);
350 }
351 LogFlowFuncLeave();
352}
353
354static int
355icmp_attach(PNATState pData, struct mbuf *m)
356{
357 struct icmp_msg *icm;
358 struct ip *ip;
359 ip = mtod(m, struct ip *);
360 Assert(ip->ip_p == IPPROTO_ICMP);
361 icm = RTMemAlloc(sizeof(struct icmp_msg));
362 icm->im_m = m;
363 icm->im_so = m->m_so;
364 LIST_INSERT_HEAD(&pData->icmp_msg_head, icm, im_list);
365 if (icmp_cache_count(pData) > 100)
366 icmp_cache_clean(pData, 50);
367 return 0;
368}
369
370/*
371 * Process a received ICMP message.
372 */
373void
374icmp_input(PNATState pData, struct mbuf *m, int hlen)
375{
376 register struct icmp *icp;
377 void *icp_buf = NULL;
378 register struct ip *ip = mtod(m, struct ip *);
379 int icmplen = ip->ip_len;
380 int status;
381 uint32_t dst;
382#if !defined(RT_OS_WINDOWS)
383 int ttl;
384#endif
385
386 /* int code; */
387
388 LogFlowFunc(("ENTER: m = %lx, m_len = %d\n", (long)m, m ? m->m_len : 0));
389
390 icmpstat.icps_received++;
391
392 /*
393 * Locate icmp structure in mbuf, and check
394 * that its not corrupted and of at least minimum length.
395 */
396 if (icmplen < ICMP_MINLEN)
397 {
398 /* min 8 bytes payload */
399 icmpstat.icps_tooshort++;
400 goto end_error_free_m;
401 }
402
403 m->m_len -= hlen;
404 m->m_data += hlen;
405
406 if (cksum(m, icmplen))
407 {
408 icmpstat.icps_checksum++;
409 goto end_error_free_m;
410 }
411
412 if (m->m_next)
413 {
414 icp_buf = RTMemAlloc(icmplen);
415 if (!icp_buf)
416 {
417 Log(("NAT: not enought memory to allocate the buffer\n"));
418 goto end_error_free_m;
419 }
420 m_copydata(m, 0, icmplen, icp_buf);
421 icp = (struct icmp *)icp_buf;
422 }
423 else
424 icp = mtod(m, struct icmp *);
425
426 m->m_len += hlen;
427 m->m_data -= hlen;
428
429 /* icmpstat.icps_inhist[icp->icmp_type]++; */
430 /* code = icp->icmp_code; */
431
432 LogFlow(("icmp_type = %d\n", icp->icmp_type));
433 switch (icp->icmp_type)
434 {
435 case ICMP_ECHO:
436 ip->ip_len += hlen; /* since ip_input subtracts this */
437 dst = ip->ip_dst.s_addr;
438 if (dst == alias_addr.s_addr)
439 {
440 icp->icmp_type = ICMP_ECHOREPLY;
441 ip->ip_dst.s_addr = ip->ip_src.s_addr;
442 ip->ip_src.s_addr = dst;
443 icmp_reflect(pData, m);
444 goto done;
445 }
446 else
447 {
448 struct sockaddr_in addr;
449#ifdef RT_OS_WINDOWS
450 IP_OPTION_INFORMATION ipopt;
451 int error;
452#endif
453 addr.sin_family = AF_INET;
454 if ((ip->ip_dst.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
455 {
456 /* It's an alias */
457 switch (RT_N2H_U32(ip->ip_dst.s_addr) & ~pData->netmask)
458 {
459 case CTL_DNS:
460 case CTL_ALIAS:
461 default:
462 addr.sin_addr = loopback_addr;
463 break;
464 }
465 }
466 else
467 addr.sin_addr.s_addr = ip->ip_dst.s_addr;
468#ifndef RT_OS_WINDOWS
469 if (pData->icmp_socket.s != -1)
470 {
471 ssize_t rc;
472 static bool fIcmpSocketErrorReported;
473 ttl = ip->ip_ttl;
474 Log(("NAT/ICMP: try to set TTL(%d)\n", ttl));
475 status = setsockopt(pData->icmp_socket.s, IPPROTO_IP, IP_TTL,
476 (void *)&ttl, sizeof(ttl));
477 if (status < 0)
478 Log(("NAT: Error (%s) occurred while setting TTL attribute of IP packet\n",
479 strerror(errno)));
480 rc = sendto(pData->icmp_socket.s, icp, icmplen, 0,
481 (struct sockaddr *)&addr, sizeof(addr));
482 if (rc >= 0)
483 {
484 m->m_so = &pData->icmp_socket;
485 icmp_attach(pData, m);
486 /* don't let m_freem at the end free atached buffer */
487 goto done;
488 }
489
490
491 if (!fIcmpSocketErrorReported)
492 {
493 LogRel(("icmp_input udp sendto tx errno = %d (%s)\n",
494 errno, strerror(errno)));
495 fIcmpSocketErrorReported = true;
496 }
497 icmp_error(pData, m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
498 }
499#else /* RT_OS_WINDOWS */
500 pData->icmp_socket.so_laddr.s_addr = ip->ip_src.s_addr; /* XXX: hack*/
501 pData->icmp_socket.so_icmp_id = icp->icmp_id;
502 pData->icmp_socket.so_icmp_seq = icp->icmp_seq;
503 memset(&ipopt, 0, sizeof(IP_OPTION_INFORMATION));
504 ipopt.Ttl = ip->ip_ttl;
505 status = IcmpSendEcho2(pData->icmp_socket.sh /*=handle*/,
506 pData->phEvents[VBOX_ICMP_EVENT_INDEX] /*=Event*/,
507 NULL /*=ApcRoutine*/,
508 NULL /*=ApcContext*/,
509 addr.sin_addr.s_addr /*=DestinationAddress*/,
510 icp->icmp_data /*=RequestData*/,
511 icmplen - ICMP_MINLEN /*=RequestSize*/,
512 &ipopt /*=RequestOptions*/,
513 pData->pvIcmpBuffer /*=ReplyBuffer*/,
514 pData->szIcmpBuffer /*=ReplySize*/,
515 1 /*=Timeout in ms*/);
516 error = GetLastError();
517 if ( status != 0
518 || error == ERROR_IO_PENDING)
519 {
520 /* no error! */
521 m->m_so = &pData->icmp_socket;
522 icmp_attach(pData, m);
523 /* don't let m_freem at the end free atached buffer */
524 goto done;
525 }
526 Log(("NAT: Error (%d) occurred while sending ICMP (", error));
527 switch (error)
528 {
529 case ERROR_INVALID_PARAMETER:
530 Log(("icmp_socket:%lx is invalid)\n", pData->icmp_socket.s));
531 break;
532 case ERROR_NOT_SUPPORTED:
533 Log(("operation is unsupported)\n"));
534 break;
535 case ERROR_NOT_ENOUGH_MEMORY:
536 Log(("OOM!!!)\n"));
537 break;
538 case IP_BUF_TOO_SMALL:
539 Log(("Buffer too small)\n"));
540 break;
541 default:
542 Log(("Other error!!!)\n"));
543 break;
544 }
545#endif /* RT_OS_WINDOWS */
546 } /* if ip->ip_dst.s_addr == alias_addr.s_addr */
547 break;
548 case ICMP_UNREACH:
549 case ICMP_TIMXCEED:
550 /* @todo(vvl): both up cases comes from guest,
551 * indeed right solution would be find the socket
552 * corresponding to ICMP data and close it.
553 */
554 case ICMP_PARAMPROB:
555 case ICMP_SOURCEQUENCH:
556 case ICMP_TSTAMP:
557 case ICMP_MASKREQ:
558 case ICMP_REDIRECT:
559 icmpstat.icps_notsupp++;
560 break;
561
562 default:
563 icmpstat.icps_badtype++;
564 } /* switch */
565
566end_error_free_m:
567 m_freem(pData, m);
568
569done:
570 if (icp_buf)
571 RTMemFree(icp_buf);
572}
573
574
575/**
576 * Send an ICMP message in response to a situation
577 *
578 * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
579 * MUST NOT change this header information.
580 * MUST NOT reply to a multicast/broadcast IP address.
581 * MUST NOT reply to a multicast/broadcast MAC address.
582 * MUST reply to only the first fragment.
583 *
584 * Send ICMP_UNREACH back to the source regarding msrc.
585 * It is reported as the bad ip packet. The header should
586 * be fully correct and in host byte order.
587 * ICMP fragmentation is illegal. All machines must accept 576 bytes in one
588 * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
589 *
590 * @note This function will free msrc!
591 */
592
593#define ICMP_MAXDATALEN (IP_MSS-28)
594void icmp_error(PNATState pData, struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message)
595{
596 unsigned hlen, shlen, s_ip_len;
597 register struct ip *ip;
598 register struct icmp *icp;
599 register struct mbuf *m;
600 int new_m_size = 0;
601 int size = 0;
602
603 LogFlow(("icmp_error: msrc = %lx, msrc_len = %d\n", (long)msrc, msrc ? msrc->m_len : 0));
604 if (msrc != NULL)
605 M_ASSERTPKTHDR(msrc);
606
607 if ( type != ICMP_UNREACH
608 && type != ICMP_TIMXCEED
609 && type != ICMP_SOURCEQUENCH)
610 goto end_error;
611
612 /* check msrc */
613 if (!msrc)
614 goto end_error;
615
616 ip = mtod(msrc, struct ip *);
617#if DEBUG
618 {
619 char bufa[20], bufb[20];
620 strcpy(bufa, inet_ntoa(ip->ip_src));
621 strcpy(bufb, inet_ntoa(ip->ip_dst));
622 Log2((" %.16s to %.16s\n", bufa, bufb));
623 }
624#endif
625 if ( ip->ip_off & IP_OFFMASK
626 && type != ICMP_SOURCEQUENCH)
627 goto end_error; /* Only reply to fragment 0 */
628
629 shlen = ip->ip_hl << 2;
630 s_ip_len = ip->ip_len;
631 if (ip->ip_p == IPPROTO_ICMP)
632 {
633 icp = (struct icmp *)((char *)ip + shlen);
634 /*
635 * Assume any unknown ICMP type is an error. This isn't
636 * specified by the RFC, but think about it..
637 */
638 if (icp->icmp_type>18 || icmp_flush[icp->icmp_type])
639 goto end_error;
640 }
641
642 new_m_size = sizeof(struct ip) + ICMP_MINLEN + msrc->m_len + ICMP_MAXDATALEN;
643 if (new_m_size < MSIZE)
644 size = MCLBYTES;
645 else if (new_m_size < MCLBYTES)
646 size = MCLBYTES;
647 else if(new_m_size < MJUM9BYTES)
648 size = MJUM9BYTES;
649 else if (new_m_size < MJUM16BYTES)
650 size = MJUM16BYTES;
651 else
652 AssertMsgFailed(("Unsupported size"));
653 m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, size);
654 if (!m)
655 goto end_error;
656
657 m->m_data += if_maxlinkhdr;
658 m->m_pkthdr.header = mtod(m, void *);
659
660 memcpy(m->m_data, msrc->m_data, msrc->m_len);
661 m->m_len = msrc->m_len; /* copy msrc to m */
662
663 /* make the header of the reply packet */
664 ip = mtod(m, struct ip *);
665 hlen = sizeof(struct ip); /* no options in reply */
666
667 /* fill in icmp */
668 m->m_data += hlen;
669 m->m_len -= hlen;
670
671 icp = mtod(m, struct icmp *);
672
673 if (minsize)
674 s_ip_len = shlen+ICMP_MINLEN; /* return header+8b only */
675 else if (s_ip_len > ICMP_MAXDATALEN) /* maximum size */
676 s_ip_len = ICMP_MAXDATALEN;
677
678 m->m_len = ICMP_MINLEN + s_ip_len; /* 8 bytes ICMP header */
679
680 /* min. size = 8+sizeof(struct ip)+8 */
681
682 icp->icmp_type = type;
683 icp->icmp_code = code;
684 icp->icmp_id = 0;
685 icp->icmp_seq = 0;
686
687 memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */
688
689 HTONS(icp->icmp_ip.ip_len);
690 HTONS(icp->icmp_ip.ip_id);
691 HTONS(icp->icmp_ip.ip_off);
692
693#if DEBUG
694 if (message)
695 {
696 /* DEBUG : append message to ICMP packet */
697 int message_len;
698 char *cpnt;
699 message_len = strlen(message);
700 if (message_len > ICMP_MAXDATALEN)
701 message_len = ICMP_MAXDATALEN;
702 cpnt = (char *)m->m_data+m->m_len;
703 m_append(pData, m, message_len, message);
704 }
705#endif
706
707 icp->icmp_cksum = 0;
708 icp->icmp_cksum = cksum(m, m->m_len);
709
710 /* fill in ip */
711 ip->ip_hl = hlen >> 2;
712 ip->ip_len = m->m_len;
713
714 ip->ip_tos = ((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
715
716 ip->ip_ttl = MAXTTL;
717 ip->ip_p = IPPROTO_ICMP;
718 ip->ip_dst = ip->ip_src; /* ip adresses */
719 ip->ip_src = alias_addr;
720
721 /* returns pointer back. */
722 m->m_data -= hlen;
723 m->m_len += hlen;
724 (void) ip_output0(pData, (struct socket *)NULL, m, 1);
725
726 icmpstat.icps_reflect++;
727
728 /* clear source datagramm in positive branch */
729 m_freem(pData, msrc);
730 LogFlowFuncLeave();
731 return;
732
733end_error_free_m:
734 m_freem(pData, m);
735
736end_error:
737
738 /*
739 * clear source datagramm in case if some of requirement haven't been met.
740 */
741 if (!msrc)
742 m_freem(pData, msrc);
743
744 {
745 static bool fIcmpErrorReported;
746 if (!fIcmpErrorReported)
747 {
748 LogRel(("NAT: error occurred while sending ICMP error message\n"));
749 fIcmpErrorReported = true;
750 }
751 }
752 LogFlowFuncLeave();
753}
754#undef ICMP_MAXDATALEN
755
756/*
757 * Reflect the ip packet back to the source
758 * Note: m isn't duplicated by this method and more delivered to ip_output then.
759 */
760void
761icmp_reflect(PNATState pData, struct mbuf *m)
762{
763 register struct ip *ip = mtod(m, struct ip *);
764 int hlen = ip->ip_hl << 2;
765 int optlen = hlen - sizeof(struct ip);
766 register struct icmp *icp;
767 LogFlowFunc(("ENTER: m:%p\n", m));
768
769 /*
770 * Send an icmp packet back to the ip level,
771 * after supplying a checksum.
772 */
773 m->m_data += hlen;
774 m->m_len -= hlen;
775 icp = mtod(m, struct icmp *);
776
777 icp->icmp_cksum = 0;
778 icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
779
780 m->m_data -= hlen;
781 m->m_len += hlen;
782
783 (void) ip_output(pData, (struct socket *)NULL, m);
784
785 icmpstat.icps_reflect++;
786 LogFlowFuncLeave();
787}
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