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