1 /** @file
2 
3 Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 **/
13 
14 #include "Ip4Impl.h"
15 
16 IP4_ICMP_CLASS
17 mIcmpClass[] = {
18   {ICMP_ECHO_REPLY,         ICMP_QUERY_MESSAGE  },
19   {1,                       ICMP_INVALID_MESSAGE},
20   {2,                       ICMP_INVALID_MESSAGE},
21   {ICMP_DEST_UNREACHABLE,   ICMP_ERROR_MESSAGE  },
22   {ICMP_SOURCE_QUENCH,      ICMP_ERROR_MESSAGE  },
23   {ICMP_REDIRECT,           ICMP_ERROR_MESSAGE  },
24   {6,                       ICMP_INVALID_MESSAGE},
25   {7,                       ICMP_INVALID_MESSAGE},
26   {ICMP_ECHO_REQUEST,       ICMP_QUERY_MESSAGE  },
27   {9,                       ICMP_INVALID_MESSAGE},
28   {10,                      ICMP_INVALID_MESSAGE},
29   {ICMP_TIME_EXCEEDED,      ICMP_ERROR_MESSAGE  },
30   {ICMP_PARAMETER_PROBLEM,  ICMP_ERROR_MESSAGE  },
31   {ICMP_TIMESTAMP ,         ICMP_QUERY_MESSAGE  },
32   {14,                      ICMP_INVALID_MESSAGE},
33   {ICMP_INFO_REQUEST ,      ICMP_QUERY_MESSAGE  },
34   {ICMP_INFO_REPLY ,        ICMP_QUERY_MESSAGE  },
35 };
36 
37 EFI_IP4_ICMP_TYPE
38 mIp4SupportedIcmp[23] = {
39   {ICMP_ECHO_REPLY,        ICMP_DEFAULT_CODE        },
40 
41   {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNREACHABLE     },
42   {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNREACHABLE    },
43   {ICMP_DEST_UNREACHABLE,  ICMP_PROTO_UNREACHABLE   },
44   {ICMP_DEST_UNREACHABLE,  ICMP_PORT_UNREACHABLE    },
45   {ICMP_DEST_UNREACHABLE,  ICMP_FRAGMENT_FAILED     },
46   {ICMP_DEST_UNREACHABLE,  ICMP_SOURCEROUTE_FAILED  },
47   {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNKNOWN         },
48   {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNKNOWN        },
49   {ICMP_DEST_UNREACHABLE,  ICMP_SOURCE_ISOLATED     },
50   {ICMP_DEST_UNREACHABLE,  ICMP_NET_PROHIBITED      },
51   {ICMP_DEST_UNREACHABLE,  ICMP_HOST_PROHIBITED     },
52   {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNREACHABLE_TOS },
53   {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNREACHABLE_TOS},
54 
55   {ICMP_SOURCE_QUENCH,     ICMP_DEFAULT_CODE        },
56 
57   {ICMP_REDIRECT,          ICMP_NET_REDIRECT        },
58   {ICMP_REDIRECT,          ICMP_HOST_REDIRECT       },
59   {ICMP_REDIRECT,          ICMP_NET_TOS_REDIRECT    },
60   {ICMP_REDIRECT,          ICMP_HOST_TOS_REDIRECT   },
61 
62   {ICMP_ECHO_REQUEST,      ICMP_DEFAULT_CODE        },
63 
64   {ICMP_TIME_EXCEEDED,     ICMP_TIMEOUT_IN_TRANSIT  },
65   {ICMP_TIME_EXCEEDED,     ICMP_TIMEOUT_REASSEMBLE  },
66 
67   {ICMP_PARAMETER_PROBLEM, ICMP_DEFAULT_CODE        },
68 };
69 
70 
71 
72 /**
73   Process the ICMP redirect. Find the instance then update
74   its route cache.
75 
76   All kinds of redirect is treated as host redirect as
77   specified by RFC1122 3.3.1.2:
78   "Since the subnet mask appropriate to the destination
79   address is generally not known, a Network Redirect
80   message SHOULD be treated identically to a Host Redirect
81   message;"
82 
83   @param[in]  IpSb               The IP4 service binding instance that received
84                                  the packet.
85   @param[in]  Head               The IP head of the received ICMPpacket.
86   @param[in]  Packet             The content of the ICMP redirect packet with IP
87                                  head removed.
88   @param[in]  Icmp               The buffer to store the ICMP error message if
89                                  something is wrong.
90 
91   @retval EFI_INVALID_PARAMETER  The parameter is invalid
92   @retval EFI_SUCCESS            Successfully updated the route caches
93 
94 **/
95 EFI_STATUS
Ip4ProcessIcmpRedirect(IN IP4_SERVICE * IpSb,IN IP4_HEAD * Head,IN NET_BUF * Packet,IN IP4_ICMP_ERROR_HEAD * Icmp)96 Ip4ProcessIcmpRedirect (
97   IN IP4_SERVICE            *IpSb,
98   IN IP4_HEAD               *Head,
99   IN NET_BUF                *Packet,
100   IN IP4_ICMP_ERROR_HEAD    *Icmp
101   )
102 {
103   LIST_ENTRY                *Entry;
104   IP4_PROTOCOL              *Ip4Instance;
105   IP4_ROUTE_CACHE_ENTRY     *CacheEntry;
106   IP4_INTERFACE             *IpIf;
107   IP4_ADDR                  Gateway;
108   IP4_ADDR                  Src;
109   IP4_ADDR                  Dst;
110 
111   //
112   // Find the interface whose IP address is the source of the
113   // orgianl IP packet.
114   //
115   IpIf    = Ip4FindInterface (IpSb, NTOHL (Icmp->IpHead.Src));
116   Gateway = NTOHL (Icmp->Fourth);
117 
118   //
119   // discard the packet if the new gateway address it specifies
120   // is not on the same connected net through which the Redirect
121   // arrived. (RFC1122 3.2.2.2).
122   //
123   if ((IpIf == NULL) || !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask)) {
124     NetbufFree (Packet);
125     return EFI_INVALID_PARAMETER;
126   }
127 
128   //
129   // Update each IP child's route cache on the interface.
130   //
131   NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
132     Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
133 
134     if (Ip4Instance->RouteTable == NULL) {
135       continue;
136     }
137 
138     Dst = NTOHL (Icmp->IpHead.Dst);
139     Src = NTOHL (Icmp->IpHead.Src);
140     CacheEntry = Ip4FindRouteCache (Ip4Instance->RouteTable, Dst, Src);
141 
142     //
143     // Only update the route cache's gateway if the source of the
144     // Redirect is the current first-hop gateway
145     //
146     if ((CacheEntry != NULL) && (NTOHL (Head->Src) == CacheEntry->NextHop)) {
147       CacheEntry->NextHop = Gateway;
148     }
149   }
150 
151   NetbufFree (Packet);
152   return EFI_SUCCESS;
153 }
154 
155 
156 /**
157   Process the ICMP error packet. If it is an ICMP redirect packet,
158   update call Ip4ProcessIcmpRedirect to update the IP instance's
159   route cache, otherwise, deliver the packet to upper layer.
160 
161   @param[in]  IpSb               The IP4 service that received the packet.
162   @param[in]  Head               The IP4 head of the ICMP error packet
163   @param[in]  Packet             The content of the ICMP error with IP4 head
164                                  removed.
165 
166   @retval EFI_SUCCESS            The ICMP error is processed successfully.
167   @retval EFI_INVALID_PARAMETER  The packet is invalid
168   @retval Others                 Failed to process the packet.
169 
170 **/
171 EFI_STATUS
Ip4ProcessIcmpError(IN IP4_SERVICE * IpSb,IN IP4_HEAD * Head,IN NET_BUF * Packet)172 Ip4ProcessIcmpError (
173   IN IP4_SERVICE            *IpSb,
174   IN IP4_HEAD               *Head,
175   IN NET_BUF                *Packet
176   )
177 {
178   IP4_ICMP_ERROR_HEAD       Icmp;
179 
180   if (Packet->TotalSize < sizeof (Icmp)) {
181     NetbufFree (Packet);
182     return EFI_INVALID_PARAMETER;
183   }
184 
185   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
186 
187   //
188   // If it is an ICMP redirect error, update the route cache
189   // as RFC1122. Otherwise, demultiplex it to IP instances.
190   //
191   if (Icmp.Head.Type == ICMP_REDIRECT) {
192     return Ip4ProcessIcmpRedirect (IpSb, Head, Packet, &Icmp);
193   }
194 
195   IP4_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
196   return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
197 }
198 
199 
200 /**
201   Replay an ICMP echo request.
202 
203   @param[in]  IpSb               The IP4 service that receivd the packet
204   @param[in]  Head               The IP4 head of the ICMP error packet
205   @param[in]  Packet             The content of the ICMP error with IP4 head
206                                  removed.
207 
208   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource.
209   @retval EFI_SUCCESS            The ICMP Echo request is successfully answered.
210   @retval Others                 Failed to answer the ICMP echo request.
211 
212 **/
213 EFI_STATUS
Ip4IcmpReplyEcho(IN IP4_SERVICE * IpSb,IN IP4_HEAD * Head,IN NET_BUF * Packet)214 Ip4IcmpReplyEcho (
215   IN IP4_SERVICE            *IpSb,
216   IN IP4_HEAD               *Head,
217   IN NET_BUF                *Packet
218   )
219 {
220   IP4_ICMP_QUERY_HEAD       *Icmp;
221   NET_BUF                   *Data;
222   EFI_STATUS                Status;
223   IP4_HEAD                  ReplyHead;
224 
225   //
226   // make a copy the packet, it is really a bad idea to
227   // send the MNP's buffer back to MNP.
228   //
229   Data = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);
230 
231   if (Data == NULL) {
232     Status = EFI_OUT_OF_RESOURCES;
233     goto ON_EXIT;
234   }
235 
236   //
237   // Change the ICMP type to echo reply, exchange the source
238   // and destination, then send it. The source is updated to
239   // use specific destination. See RFC1122. SRR/RR option
240   // update is omitted.
241   //
242   Icmp                = (IP4_ICMP_QUERY_HEAD *) NetbufGetByte (Data, 0, NULL);
243   ASSERT (Icmp != NULL);
244   Icmp->Head.Type     = ICMP_ECHO_REPLY;
245   Icmp->Head.Checksum = 0;
246   Icmp->Head.Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize));
247 
248   ReplyHead.Tos       = 0;
249   ReplyHead.Fragment  = 0;
250   ReplyHead.Ttl       = 64;
251   ReplyHead.Protocol  = EFI_IP_PROTO_ICMP;
252   ReplyHead.Src       = 0;
253 
254   //
255   // Ip4Output will select a source for us
256   //
257   ReplyHead.Dst = Head->Src;
258 
259   Status = Ip4Output (
260              IpSb,
261              NULL,
262              Data,
263              &ReplyHead,
264              NULL,
265              0,
266              IP4_ALLZERO_ADDRESS,
267              Ip4SysPacketSent,
268              NULL
269              );
270 
271 ON_EXIT:
272   NetbufFree (Packet);
273   return Status;
274 }
275 
276 
277 /**
278   Process the ICMP query message. If it is an ICMP echo
279   request, answer it. Otherwise deliver it to upper layer.
280 
281   @param[in]  IpSb               The IP4 service that receivd the packet
282   @param[in]  Head               The IP4 head of the ICMP query packet
283   @param[in]  Packet             The content of the ICMP query with IP4 head
284                                  removed.
285 
286   @retval EFI_INVALID_PARAMETER  The packet is invalid
287   @retval EFI_SUCCESS            The ICMP query message is processed
288   @retval Others                 Failed to process ICMP query.
289 
290 **/
291 EFI_STATUS
Ip4ProcessIcmpQuery(IN IP4_SERVICE * IpSb,IN IP4_HEAD * Head,IN NET_BUF * Packet)292 Ip4ProcessIcmpQuery (
293   IN IP4_SERVICE            *IpSb,
294   IN IP4_HEAD               *Head,
295   IN NET_BUF                *Packet
296   )
297 {
298   IP4_ICMP_QUERY_HEAD       Icmp;
299 
300   if (Packet->TotalSize < sizeof (Icmp)) {
301     NetbufFree (Packet);
302     return EFI_INVALID_PARAMETER;
303   }
304 
305   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
306 
307   if (Icmp.Head.Type == ICMP_ECHO_REQUEST) {
308     return Ip4IcmpReplyEcho (IpSb, Head, Packet);
309   }
310 
311   return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
312 }
313 
314 
315 /**
316   Handle the ICMP packet. First validate the message format,
317   then according to the message types, process it as query or
318   error packet.
319 
320   @param[in]  IpSb               The IP4 service that receivd the packet.
321   @param[in]  Head               The IP4 head of the ICMP query packet.
322   @param[in]  Packet             The content of the ICMP query with IP4 head
323                                  removed.
324 
325   @retval EFI_INVALID_PARAMETER  The packet is malformated.
326   @retval EFI_SUCCESS            The ICMP message is successfully processed.
327   @retval Others                 Failed to handle ICMP packet.
328 
329 **/
330 EFI_STATUS
Ip4IcmpHandle(IN IP4_SERVICE * IpSb,IN IP4_HEAD * Head,IN NET_BUF * Packet)331 Ip4IcmpHandle (
332   IN IP4_SERVICE            *IpSb,
333   IN IP4_HEAD               *Head,
334   IN NET_BUF                *Packet
335   )
336 {
337   IP4_ICMP_HEAD             Icmp;
338   UINT16                    Checksum;
339 
340   if (Packet->TotalSize < sizeof (Icmp)) {
341     goto DROP;
342   }
343 
344   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
345 
346   if (Icmp.Type > ICMP_TYPE_MAX) {
347     goto DROP;
348   }
349 
350   Checksum = (UINT16) (~NetbufChecksum (Packet));
351   if ((Icmp.Checksum != 0) && (Checksum != 0)) {
352     goto DROP;
353   }
354 
355   if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
356     return Ip4ProcessIcmpError (IpSb, Head, Packet);
357 
358   } else if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_QUERY_MESSAGE) {
359     return Ip4ProcessIcmpQuery (IpSb, Head, Packet);
360 
361   }
362 
363 DROP:
364   NetbufFree (Packet);
365   return EFI_INVALID_PARAMETER;
366 }
367