Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / lwip / core / ipv4 / icmp.c
1 /**
2  * @file
3  * ICMP - Internet Control Message Protocol
4  *
5  */
6
7 /*
8  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without modification,
12  * are permitted provided that the following conditions are met:
13  *
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.
21  *
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
31  * OF SUCH DAMAGE.
32  *
33  * This file is part of the lwIP TCP/IP stack.
34  *
35  * Author: Adam Dunkels <adam@sics.se>
36  *
37  */
38
39 /* Some ICMP messages should be passed to the transport protocols. This
40    is not implemented. */
41
42 #include "lwip/opt.h"
43
44 #if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
45
46 #include "lwip/icmp.h"
47 #include "lwip/inet.h"
48 #include "lwip/inet_chksum.h"
49 #include "lwip/ip.h"
50 #include "lwip/def.h"
51 #include "lwip/stats.h"
52 #include "lwip/snmp.h"
53
54 #include <string.h>
55
56 /* The amount of data from the original packet to return in a dest-unreachable */
57 #define ICMP_DEST_UNREACH_DATASIZE 8
58
59 /**
60  * Processes ICMP input packets, called from ip_input().
61  *
62  * Currently only processes icmp echo requests and sends
63  * out the echo response.
64  *
65  * @param p the icmp echo request packet, p->payload pointing to the ip header
66  * @param inp the netif on which this packet was received
67  */
68 void
69 icmp_input(struct pbuf *p, struct netif *inp)
70 {
71   u8_t type;
72 #ifdef LWIP_DEBUG
73   u8_t code;
74 #endif /* LWIP_DEBUG */
75   struct icmp_echo_hdr *iecho;
76   struct ip_hdr *iphdr;
77   struct ip_addr tmpaddr;
78   s16_t hlen;
79
80   ICMP_STATS_INC(icmp.recv);
81   snmp_inc_icmpinmsgs();
82
83
84   iphdr = p->payload;
85   hlen = IPH_HL(iphdr) * 4;
86   if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {
87     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
88     goto lenerr;
89   }
90
91   type = *((u8_t *)p->payload);
92 #ifdef LWIP_DEBUG
93   code = *(((u8_t *)p->payload)+1);
94 #endif /* LWIP_DEBUG */
95   switch (type) {
96   case ICMP_ECHO:
97     /* broadcast or multicast destination address? */
98     if (ip_addr_isbroadcast(&iphdr->dest, inp) || ip_addr_ismulticast(&iphdr->dest)) {
99       LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
100       ICMP_STATS_INC(icmp.err);
101       pbuf_free(p);
102       return;
103     }
104     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
105     if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
106       LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
107       goto lenerr;
108     }
109     if (inet_chksum_pbuf(p) != 0) {
110       LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
111       pbuf_free(p);
112       ICMP_STATS_INC(icmp.chkerr);
113       snmp_inc_icmpinerrors();
114       return;
115     }
116     if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
117       /* p is not big enough to contain link headers
118        * allocate a new one and copy p into it
119        */
120       struct pbuf *r;
121       /* switch p->payload to ip header */
122       if (pbuf_header(p, hlen)) {
123         LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
124         goto memerr;
125       }
126       /* allocate new packet buffer with space for link headers */
127       r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
128       if (r == NULL) {
129         LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
130         goto memerr;
131       }
132       LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
133                   (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
134       /* copy the whole packet including ip header */
135       if (pbuf_copy(r, p) != ERR_OK) {
136         LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
137         goto memerr;
138       }
139       iphdr = r->payload;
140       /* switch r->payload back to icmp header */
141       if (pbuf_header(r, -hlen)) {
142         LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
143         goto memerr;
144       }
145       /* free the original p */
146       pbuf_free(p);
147       /* we now have an identical copy of p that has room for link headers */
148       p = r;
149     } else {
150       /* restore p->payload to point to icmp header */
151       if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
152         LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
153         goto memerr;
154       }
155     }
156     /* At this point, all checks are OK. */
157     /* We generate an answer by switching the dest and src ip addresses,
158      * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
159     iecho = p->payload;
160     tmpaddr.addr = iphdr->src.addr;
161     iphdr->src.addr = iphdr->dest.addr;
162     iphdr->dest.addr = tmpaddr.addr;
163     ICMPH_TYPE_SET(iecho, ICMP_ER);
164     /* adjust the checksum */
165     if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) {
166       iecho->chksum += htons(ICMP_ECHO << 8) + 1;
167     } else {
168       iecho->chksum += htons(ICMP_ECHO << 8);
169     }
170
171     /* Set the correct TTL and recalculate the header checksum. */
172     IPH_TTL_SET(iphdr, ICMP_TTL);
173     IPH_CHKSUM_SET(iphdr, 0);
174 #if CHECKSUM_GEN_IP
175     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
176 #endif /* CHECKSUM_GEN_IP */
177
178     ICMP_STATS_INC(icmp.xmit);
179     /* increase number of messages attempted to send */
180     snmp_inc_icmpoutmsgs();
181     /* increase number of echo replies attempted to send */
182     snmp_inc_icmpoutechoreps();
183
184     if(pbuf_header(p, hlen)) {
185       LWIP_ASSERT("Can't move over header in packet", 0);
186     } else {
187       err_t ret;
188       ret = ip_output_if(p, &(iphdr->src), IP_HDRINCL,
189                    ICMP_TTL, 0, IP_PROTO_ICMP, inp);
190       if (ret != ERR_OK) {
191         LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
192       }
193     }
194     break;
195   default:
196     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", 
197                 (s16_t)type, (s16_t)code));
198     ICMP_STATS_INC(icmp.proterr);
199     ICMP_STATS_INC(icmp.drop);
200   }
201   pbuf_free(p);
202   return;
203 lenerr:
204   pbuf_free(p);
205   ICMP_STATS_INC(icmp.lenerr);
206   snmp_inc_icmpinerrors();
207   return;
208 memerr:
209   pbuf_free(p);
210   ICMP_STATS_INC(icmp.err);
211   snmp_inc_icmpinerrors();
212   return;
213 }
214
215 /**
216  * Send an icmp 'destination unreachable' packet, called from ip_input() if
217  * the transport layer protocol is unknown and from udp_input() if the local
218  * port is not bound.
219  *
220  * @param p the input packet for which the 'unreachable' should be sent,
221  *          p->payload pointing to the IP header
222  * @param t type of the 'unreachable' packet
223  */
224 void
225 icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
226 {
227   struct pbuf *q;
228   struct ip_hdr *iphdr;
229   struct icmp_dur_hdr *idur;
230
231   /* ICMP header + IP header + 8 bytes of data */
232   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
233                  PBUF_RAM);
234   if (q == NULL) {
235     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n"));
236     return;
237   }
238   LWIP_ASSERT("check that first pbuf can hold icmp message",
239              (q->len >= (sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
240
241   iphdr = p->payload;
242
243   idur = q->payload;
244   ICMPH_TYPE_SET(idur, ICMP_DUR);
245   ICMPH_CODE_SET(idur, t);
246
247   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_dur_hdr), p->payload,
248           IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
249
250   /* calculate checksum */
251   idur->chksum = 0;
252   idur->chksum = inet_chksum(idur, q->len);
253   ICMP_STATS_INC(icmp.xmit);
254   /* increase number of messages attempted to send */
255   snmp_inc_icmpoutmsgs();
256   /* increase number of destination unreachable messages attempted to send */
257   snmp_inc_icmpoutdestunreachs();
258
259   ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP);
260   pbuf_free(q);
261 }
262
263 #if IP_FORWARD || IP_REASSEMBLY
264 /**
265  * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
266  *
267  * @param p the input packet for which the 'time exceeded' should be sent,
268  *          p->payload pointing to the IP header
269  * @param t type of the 'time exceeded' packet
270  */
271 void
272 icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
273 {
274   struct pbuf *q;
275   struct ip_hdr *iphdr;
276   struct icmp_te_hdr *tehdr;
277
278   /* ICMP header + IP header + 8 bytes of data */
279   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
280                  PBUF_RAM);
281   if (q == NULL) {
282     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
283     return;
284   }
285   LWIP_ASSERT("check that first pbuf can hold icmp message",
286              (q->len >= (sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
287
288   iphdr = p->payload;
289   LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
290   ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
291   LWIP_DEBUGF(ICMP_DEBUG, (" to "));
292   ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
293   LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
294
295   tehdr = q->payload;
296   ICMPH_TYPE_SET(tehdr, ICMP_TE);
297   ICMPH_CODE_SET(tehdr, t);
298
299   /* copy fields from original packet */
300   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_dur_hdr), (u8_t *)p->payload,
301           IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
302
303   /* calculate checksum */
304   tehdr->chksum = 0;
305   tehdr->chksum = inet_chksum(tehdr, q->len);
306   ICMP_STATS_INC(icmp.xmit);
307   /* increase number of messages attempted to send */
308   snmp_inc_icmpoutmsgs();
309   /* increase number of destination unreachable messages attempted to send */
310   snmp_inc_icmpouttimeexcds();
311   ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP);
312   pbuf_free(q);
313 }
314
315 #endif /* IP_FORWARD */
316
317 #endif /* LWIP_ICMP */