summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Ip4Dxe/Ip4Icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg/Ip4Dxe/Ip4Icmp.c')
-rw-r--r--NetworkPkg/Ip4Dxe/Ip4Icmp.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/NetworkPkg/Ip4Dxe/Ip4Icmp.c b/NetworkPkg/Ip4Dxe/Ip4Icmp.c
new file mode 100644
index 0000000000..052d6b77f7
--- /dev/null
+++ b/NetworkPkg/Ip4Dxe/Ip4Icmp.c
@@ -0,0 +1,363 @@
+/** @file
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ip4Impl.h"
+
+IP4_ICMP_CLASS
+mIcmpClass[] = {
+ {ICMP_ECHO_REPLY, ICMP_QUERY_MESSAGE },
+ {1, ICMP_INVALID_MESSAGE},
+ {2, ICMP_INVALID_MESSAGE},
+ {ICMP_DEST_UNREACHABLE, ICMP_ERROR_MESSAGE },
+ {ICMP_SOURCE_QUENCH, ICMP_ERROR_MESSAGE },
+ {ICMP_REDIRECT, ICMP_ERROR_MESSAGE },
+ {6, ICMP_INVALID_MESSAGE},
+ {7, ICMP_INVALID_MESSAGE},
+ {ICMP_ECHO_REQUEST, ICMP_QUERY_MESSAGE },
+ {9, ICMP_INVALID_MESSAGE},
+ {10, ICMP_INVALID_MESSAGE},
+ {ICMP_TIME_EXCEEDED, ICMP_ERROR_MESSAGE },
+ {ICMP_PARAMETER_PROBLEM, ICMP_ERROR_MESSAGE },
+ {ICMP_TIMESTAMP , ICMP_QUERY_MESSAGE },
+ {14, ICMP_INVALID_MESSAGE},
+ {ICMP_INFO_REQUEST , ICMP_QUERY_MESSAGE },
+ {ICMP_INFO_REPLY , ICMP_QUERY_MESSAGE },
+};
+
+EFI_IP4_ICMP_TYPE
+mIp4SupportedIcmp[23] = {
+ {ICMP_ECHO_REPLY, ICMP_DEFAULT_CODE },
+
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_PROTO_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_PORT_UNREACHABLE },
+ {ICMP_DEST_UNREACHABLE, ICMP_FRAGMENT_FAILED },
+ {ICMP_DEST_UNREACHABLE, ICMP_SOURCEROUTE_FAILED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNKNOWN },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNKNOWN },
+ {ICMP_DEST_UNREACHABLE, ICMP_SOURCE_ISOLATED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_PROHIBITED },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_PROHIBITED },
+ {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE_TOS },
+ {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE_TOS},
+
+ {ICMP_SOURCE_QUENCH, ICMP_DEFAULT_CODE },
+
+ {ICMP_REDIRECT, ICMP_NET_REDIRECT },
+ {ICMP_REDIRECT, ICMP_HOST_REDIRECT },
+ {ICMP_REDIRECT, ICMP_NET_TOS_REDIRECT },
+ {ICMP_REDIRECT, ICMP_HOST_TOS_REDIRECT },
+
+ {ICMP_ECHO_REQUEST, ICMP_DEFAULT_CODE },
+
+ {ICMP_TIME_EXCEEDED, ICMP_TIMEOUT_IN_TRANSIT },
+ {ICMP_TIME_EXCEEDED, ICMP_TIMEOUT_REASSEMBLE },
+
+ {ICMP_PARAMETER_PROBLEM, ICMP_DEFAULT_CODE },
+};
+
+
+
+/**
+ Process the ICMP redirect. Find the instance then update
+ its route cache.
+
+ All kinds of redirect is treated as host redirect as
+ specified by RFC1122 3.3.1.2:
+ "Since the subnet mask appropriate to the destination
+ address is generally not known, a Network Redirect
+ message SHOULD be treated identically to a Host Redirect
+ message;"
+
+ @param[in] IpSb The IP4 service binding instance that received
+ the packet.
+ @param[in] Head The IP head of the received ICMPpacket.
+ @param[in] Packet The content of the ICMP redirect packet with IP
+ head removed.
+ @param[in] Icmp The buffer to store the ICMP error message if
+ something is wrong.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+ @retval EFI_SUCCESS Successfully updated the route caches
+
+**/
+EFI_STATUS
+Ip4ProcessIcmpRedirect (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet,
+ IN IP4_ICMP_ERROR_HEAD *Icmp
+ )
+{
+ LIST_ENTRY *Entry;
+ IP4_PROTOCOL *Ip4Instance;
+ IP4_ROUTE_CACHE_ENTRY *CacheEntry;
+ IP4_INTERFACE *IpIf;
+ IP4_ADDR Gateway;
+ IP4_ADDR Src;
+ IP4_ADDR Dst;
+
+ //
+ // Find the interface whose IP address is the source of the
+ // orgianl IP packet.
+ //
+ IpIf = Ip4FindInterface (IpSb, NTOHL (Icmp->IpHead.Src));
+ Gateway = NTOHL (Icmp->Fourth);
+
+ //
+ // discard the packet if the new gateway address it specifies
+ // is not on the same connected net through which the Redirect
+ // arrived. (RFC1122 3.2.2.2).
+ //
+ if ((IpIf == NULL) || !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Update each IP child's route cache on the interface.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
+
+ if (Ip4Instance->RouteTable == NULL) {
+ continue;
+ }
+
+ Dst = NTOHL (Icmp->IpHead.Dst);
+ Src = NTOHL (Icmp->IpHead.Src);
+ CacheEntry = Ip4FindRouteCache (Ip4Instance->RouteTable, Dst, Src);
+
+ //
+ // Only update the route cache's gateway if the source of the
+ // Redirect is the current first-hop gateway
+ //
+ if ((CacheEntry != NULL) && (NTOHL (Head->Src) == CacheEntry->NextHop)) {
+ CacheEntry->NextHop = Gateway;
+ }
+ }
+
+ NetbufFree (Packet);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Process the ICMP error packet. If it is an ICMP redirect packet,
+ update call Ip4ProcessIcmpRedirect to update the IP instance's
+ route cache, otherwise, deliver the packet to upper layer.
+
+ @param[in] IpSb The IP4 service that received the packet.
+ @param[in] Head The IP4 head of the ICMP error packet
+ @param[in] Packet The content of the ICMP error with IP4 head
+ removed.
+
+ @retval EFI_SUCCESS The ICMP error is processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip4ProcessIcmpError (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_ERROR_HEAD Icmp;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ //
+ // If it is an ICMP redirect error, update the route cache
+ // as RFC1122. Otherwise, demultiplex it to IP instances.
+ //
+ if (Icmp.Head.Type == ICMP_REDIRECT) {
+ return Ip4ProcessIcmpRedirect (IpSb, Head, Packet, &Icmp);
+ }
+
+ IP4_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
+ return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
+}
+
+
+/**
+ Replay an ICMP echo request.
+
+ @param[in] IpSb The IP4 service that receivd the packet
+ @param[in] Head The IP4 head of the ICMP error packet
+ @param[in] Packet The content of the ICMP error with IP4 head
+ removed.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+ @retval EFI_SUCCESS The ICMP Echo request is successfully answered.
+ @retval Others Failed to answer the ICMP echo request.
+
+**/
+EFI_STATUS
+Ip4IcmpReplyEcho (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_QUERY_HEAD *Icmp;
+ NET_BUF *Data;
+ EFI_STATUS Status;
+ IP4_HEAD ReplyHead;
+
+ //
+ // make a copy the packet, it is really a bad idea to
+ // send the MNP's buffer back to MNP.
+ //
+ Data = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);
+
+ if (Data == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Change the ICMP type to echo reply, exchange the source
+ // and destination, then send it. The source is updated to
+ // use specific destination. See RFC1122. SRR/RR option
+ // update is omitted.
+ //
+ Icmp = (IP4_ICMP_QUERY_HEAD *) NetbufGetByte (Data, 0, NULL);
+ ASSERT (Icmp != NULL);
+ Icmp->Head.Type = ICMP_ECHO_REPLY;
+ Icmp->Head.Checksum = 0;
+ Icmp->Head.Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize));
+
+ ReplyHead.Tos = 0;
+ ReplyHead.Fragment = 0;
+ ReplyHead.Ttl = 64;
+ ReplyHead.Protocol = EFI_IP_PROTO_ICMP;
+ ReplyHead.Src = 0;
+
+ //
+ // Ip4Output will select a source for us
+ //
+ ReplyHead.Dst = Head->Src;
+
+ Status = Ip4Output (
+ IpSb,
+ NULL,
+ Data,
+ &ReplyHead,
+ NULL,
+ 0,
+ IP4_ALLZERO_ADDRESS,
+ Ip4SysPacketSent,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Data);
+ }
+
+ON_EXIT:
+ NetbufFree (Packet);
+ return Status;
+}
+
+
+/**
+ Process the ICMP query message. If it is an ICMP echo
+ request, answer it. Otherwise deliver it to upper layer.
+
+ @param[in] IpSb The IP4 service that receivd the packet
+ @param[in] Head The IP4 head of the ICMP query packet
+ @param[in] Packet The content of the ICMP query with IP4 head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is invalid
+ @retval EFI_SUCCESS The ICMP query message is processed
+ @retval Others Failed to process ICMP query.
+
+**/
+EFI_STATUS
+Ip4ProcessIcmpQuery (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_QUERY_HEAD Icmp;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ if (Icmp.Head.Type == ICMP_ECHO_REQUEST) {
+ return Ip4IcmpReplyEcho (IpSb, Head, Packet);
+ }
+
+ return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
+}
+
+
+/**
+ Handle the ICMP packet. First validate the message format,
+ then according to the message types, process it as query or
+ error packet.
+
+ @param[in] IpSb The IP4 service that receivd the packet.
+ @param[in] Head The IP4 head of the ICMP query packet.
+ @param[in] Packet The content of the ICMP query with IP4 head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_SUCCESS The ICMP message is successfully processed.
+ @retval Others Failed to handle ICMP packet.
+
+**/
+EFI_STATUS
+Ip4IcmpHandle (
+ IN IP4_SERVICE *IpSb,
+ IN IP4_HEAD *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP4_ICMP_HEAD Icmp;
+ UINT16 Checksum;
+
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ goto DROP;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ if (Icmp.Type > ICMP_TYPE_MAX) {
+ goto DROP;
+ }
+
+ Checksum = (UINT16) (~NetbufChecksum (Packet));
+ if ((Icmp.Checksum != 0) && (Checksum != 0)) {
+ goto DROP;
+ }
+
+ if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
+ return Ip4ProcessIcmpError (IpSb, Head, Packet);
+
+ } else if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_QUERY_MESSAGE) {
+ return Ip4ProcessIcmpQuery (IpSb, Head, Packet);
+
+ }
+
+DROP:
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+}