3 * ICMP - Internet Control Message Protocol
8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
33 * This file is part of the lwIP TCP/IP stack.
35 * Author: Adam Dunkels <adam@sics.se>
39 /* Some ICMP messages should be passed to the transport protocols. This
40 is not implemented. */
44 #if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
46 #include "lwip/icmp.h"
47 #include "lwip/inet.h"
48 #include "lwip/inet_chksum.h"
51 #include "lwip/stats.h"
52 #include "lwip/snmp.h"
56 /** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
57 * used to modify and send a response packet (and to 1 if this is not the case,
58 * e.g. when link header is stripped of when receiving) */
59 #ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
60 #define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
61 #endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
63 /* The amount of data from the original packet to return in a dest-unreachable */
64 #define ICMP_DEST_UNREACH_DATASIZE 8
66 static void icmp_send_response(struct pbuf
*p
, u8_t type
, u8_t code
);
69 * Processes ICMP input packets, called from ip_input().
71 * Currently only processes icmp echo requests and sends
72 * out the echo response.
74 * @param p the icmp echo request packet, p->payload pointing to the ip header
75 * @param inp the netif on which this packet was received
78 icmp_input(struct pbuf
*p
, struct netif
*inp
)
83 #endif /* LWIP_DEBUG */
84 struct icmp_echo_hdr
*iecho
;
86 struct ip_addr tmpaddr
;
89 ICMP_STATS_INC(icmp
.recv
);
90 snmp_inc_icmpinmsgs();
94 hlen
= IPH_HL(iphdr
) * 4;
95 if (pbuf_header(p
, -hlen
) || (p
->tot_len
< sizeof(u16_t
)*2)) {
96 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: short ICMP (%"U16_F
" bytes) received\n", p
->tot_len
));
100 type
= *((u8_t
*)p
->payload
);
102 code
= *(((u8_t
*)p
->payload
)+1);
103 #endif /* LWIP_DEBUG */
106 #if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
109 #if !LWIP_MULTICAST_PING
110 /* multicast destination address? */
111 if (ip_addr_ismulticast(&iphdr
->dest
)) {
114 #endif /* LWIP_MULTICAST_PING */
115 #if !LWIP_BROADCAST_PING
116 /* broadcast destination address? */
117 if (ip_addr_isbroadcast(&iphdr
->dest
, inp
)) {
120 #endif /* LWIP_BROADCAST_PING */
121 /* broadcast or multicast destination address not acceptd? */
123 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
124 ICMP_STATS_INC(icmp
.err
);
129 #endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
130 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: ping\n"));
131 if (p
->tot_len
< sizeof(struct icmp_echo_hdr
)) {
132 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: bad ICMP echo received\n"));
135 if (inet_chksum_pbuf(p
) != 0) {
136 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: checksum failed for received ICMP echo\n"));
138 ICMP_STATS_INC(icmp
.chkerr
);
139 snmp_inc_icmpinerrors();
142 #if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
143 if (pbuf_header(p
, (PBUF_IP_HLEN
+ PBUF_LINK_HLEN
))) {
144 /* p is not big enough to contain link headers
145 * allocate a new one and copy p into it
148 /* switch p->payload to ip header */
149 if (pbuf_header(p
, hlen
)) {
150 LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
153 /* allocate new packet buffer with space for link headers */
154 r
= pbuf_alloc(PBUF_LINK
, p
->tot_len
, PBUF_RAM
);
156 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: allocating new pbuf failed\n"));
159 LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
160 (r
->len
>= hlen
+ sizeof(struct icmp_echo_hdr
)));
161 /* copy the whole packet including ip header */
162 if (pbuf_copy(r
, p
) != ERR_OK
) {
163 LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
167 /* switch r->payload back to icmp header */
168 if (pbuf_header(r
, -hlen
)) {
169 LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
172 /* free the original p */
174 /* we now have an identical copy of p that has room for link headers */
177 /* restore p->payload to point to icmp header */
178 if (pbuf_header(p
, -(s16_t
)(PBUF_IP_HLEN
+ PBUF_LINK_HLEN
))) {
179 LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
183 #endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
184 /* At this point, all checks are OK. */
185 /* We generate an answer by switching the dest and src ip addresses,
186 * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
188 tmpaddr
.addr
= iphdr
->src
.addr
;
189 iphdr
->src
.addr
= iphdr
->dest
.addr
;
190 iphdr
->dest
.addr
= tmpaddr
.addr
;
191 ICMPH_TYPE_SET(iecho
, ICMP_ER
);
192 /* adjust the checksum */
193 if (iecho
->chksum
>= htons(0xffff - (ICMP_ECHO
<< 8))) {
194 iecho
->chksum
+= htons(ICMP_ECHO
<< 8) + 1;
196 iecho
->chksum
+= htons(ICMP_ECHO
<< 8);
199 /* Set the correct TTL and recalculate the header checksum. */
200 IPH_TTL_SET(iphdr
, ICMP_TTL
);
201 IPH_CHKSUM_SET(iphdr
, 0);
203 IPH_CHKSUM_SET(iphdr
, inet_chksum(iphdr
, IP_HLEN
));
204 #endif /* CHECKSUM_GEN_IP */
206 ICMP_STATS_INC(icmp
.xmit
);
207 /* increase number of messages attempted to send */
208 snmp_inc_icmpoutmsgs();
209 /* increase number of echo replies attempted to send */
210 snmp_inc_icmpoutechoreps();
212 if(pbuf_header(p
, hlen
)) {
213 LWIP_ASSERT("Can't move over header in packet", 0);
216 ret
= ip_output_if(p
, &(iphdr
->src
), IP_HDRINCL
,
217 ICMP_TTL
, 0, IP_PROTO_ICMP
, inp
);
219 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: ip_output_if returned an error: %c.\n", ret
));
224 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_input: ICMP type %"S16_F
" code %"S16_F
" not supported.\n",
225 (s16_t
)type
, (s16_t
)code
));
226 ICMP_STATS_INC(icmp
.proterr
);
227 ICMP_STATS_INC(icmp
.drop
);
233 ICMP_STATS_INC(icmp
.lenerr
);
234 snmp_inc_icmpinerrors();
236 #if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
239 ICMP_STATS_INC(icmp
.err
);
240 snmp_inc_icmpinerrors();
242 #endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
246 * Send an icmp 'destination unreachable' packet, called from ip_input() if
247 * the transport layer protocol is unknown and from udp_input() if the local
250 * @param p the input packet for which the 'unreachable' should be sent,
251 * p->payload pointing to the IP header
252 * @param t type of the 'unreachable' packet
255 icmp_dest_unreach(struct pbuf
*p
, enum icmp_dur_type t
)
257 icmp_send_response(p
, ICMP_DUR
, t
);
260 #if IP_FORWARD || IP_REASSEMBLY
262 * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
264 * @param p the input packet for which the 'time exceeded' should be sent,
265 * p->payload pointing to the IP header
266 * @param t type of the 'time exceeded' packet
269 icmp_time_exceeded(struct pbuf
*p
, enum icmp_te_type t
)
271 icmp_send_response(p
, ICMP_TE
, t
);
274 #endif /* IP_FORWARD || IP_REASSEMBLY */
277 * Send an icmp packet in response to an incoming packet.
279 * @param p the input packet for which the 'unreachable' should be sent,
280 * p->payload pointing to the IP header
281 * @param type Type of the ICMP header
282 * @param code Code of the ICMP header
285 icmp_send_response(struct pbuf
*p
, u8_t type
, u8_t code
)
288 struct ip_hdr
*iphdr
;
289 /* we can use the echo header here */
290 struct icmp_echo_hdr
*icmphdr
;
292 /* ICMP header + IP header + 8 bytes of data */
293 q
= pbuf_alloc(PBUF_IP
, sizeof(struct icmp_echo_hdr
) + IP_HLEN
+ ICMP_DEST_UNREACH_DATASIZE
,
296 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
299 LWIP_ASSERT("check that first pbuf can hold icmp message",
300 (q
->len
>= (sizeof(struct icmp_echo_hdr
) + IP_HLEN
+ ICMP_DEST_UNREACH_DATASIZE
)));
303 LWIP_DEBUGF(ICMP_DEBUG
, ("icmp_time_exceeded from "));
304 ip_addr_debug_print(ICMP_DEBUG
, &(iphdr
->src
));
305 LWIP_DEBUGF(ICMP_DEBUG
, (" to "));
306 ip_addr_debug_print(ICMP_DEBUG
, &(iphdr
->dest
));
307 LWIP_DEBUGF(ICMP_DEBUG
, ("\n"));
309 icmphdr
= q
->payload
;
310 icmphdr
->type
= type
;
311 icmphdr
->code
= code
;
315 /* copy fields from original packet */
316 SMEMCPY((u8_t
*)q
->payload
+ sizeof(struct icmp_echo_hdr
), (u8_t
*)p
->payload
,
317 IP_HLEN
+ ICMP_DEST_UNREACH_DATASIZE
);
319 /* calculate checksum */
321 icmphdr
->chksum
= inet_chksum(icmphdr
, q
->len
);
322 ICMP_STATS_INC(icmp
.xmit
);
323 /* increase number of messages attempted to send */
324 snmp_inc_icmpoutmsgs();
325 /* increase number of destination unreachable messages attempted to send */
326 snmp_inc_icmpouttimeexcds();
327 ip_output(q
, NULL
, &(iphdr
->src
), ICMP_TTL
, 0, IP_PROTO_ICMP
);
331 #endif /* LWIP_ICMP */