X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FLibrary%2FDxeNetLib%2FDxeNetLib.c;h=b42054f544777b46e144acc824eb7ee8a8c1194d;hp=fb296e5f595d20f65698dcfc08a08ef5d1a5df6d;hb=9ae650ef6ca9372199adb31fb3432ed8cde353ad;hpb=b45b45b2d248892930620c33a9d01d8457ae0e54 diff --git a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c index fb296e5f59..b42054f544 100644 --- a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c +++ b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c @@ -81,6 +81,452 @@ IP4_ADDR gIp4AllMasks[IP4_MASK_NUM] = { EFI_IPv4_ADDRESS mZeroIp4Addr = {{0, 0, 0, 0}}; +// +// Any error level digitally larger than mNetDebugLevelMax +// will be silently discarded. +// +UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR; +UINT32 mSyslogPacketSeq = 0xDEADBEEF; + +// +// You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp +// here to direct the syslog packets to the syslog deamon. The +// default is broadcast to both the ethernet and IP. +// +UINT8 mSyslogDstMac[NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +UINT32 mSyslogDstIp = 0xffffffff; +UINT32 mSyslogSrcIp = 0; + +CHAR8 * +mMonthName[] = { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +}; + +/** + Locate the handles that support SNP, then open one of them + to send the syslog packets. The caller isn't required to close + the SNP after use because the SNP is opened by HandleProtocol. + + @return The point to SNP if one is properly openned. Otherwise NULL + +**/ +EFI_SIMPLE_NETWORK_PROTOCOL * +SyslogLocateSnp ( + VOID + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN Index; + + // + // Locate the handles which has SNP installed. + // + Handles = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleNetworkProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + + if (EFI_ERROR (Status) || (HandleCount == 0)) { + return NULL; + } + + // + // Try to open one of the ethernet SNP protocol to send packet + // + Snp = NULL; + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Handles[Index], + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp + ); + + if ((Status == EFI_SUCCESS) && (Snp != NULL) && + (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) && + (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) { + + break; + } + + Snp = NULL; + } + + FreePool (Handles); + return Snp; +} + +/** + Transmit a syslog packet synchronously through SNP. The Packet + already has the ethernet header prepended. This function should + fill in the source MAC because it will try to locate a SNP each + time it is called to avoid the problem if SNP is unloaded. + This code snip is copied from MNP. + + @param[in] Packet - The Syslog packet + @param[in] Length - The length of the packet + + @retval EFI_DEVICE_ERROR - Failed to locate a usable SNP protocol + @retval EFI_TIMEOUT - Timeout happened to send the packet. + @retval EFI_SUCCESS - Packet is sent. + +**/ +EFI_STATUS +SyslogSendPacket ( + IN CHAR8 *Packet, + IN UINT32 Length + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + ETHER_HEAD *Ether; + EFI_STATUS Status; + EFI_EVENT TimeoutEvent; + UINT8 *TxBuf; + + Snp = SyslogLocateSnp (); + + if (Snp == NULL) { + return EFI_DEVICE_ERROR; + } + + Ether = (ETHER_HEAD *) Packet; + CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN); + + // + // Start the timeout event. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_NOTIFY, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + for (;;) { + // + // Transmit the packet through SNP. + // + Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL); + + if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) { + Status = EFI_DEVICE_ERROR; + break; + } + + // + // If Status is EFI_SUCCESS, the packet is put in the transmit queue. + // if Status is EFI_NOT_READY, the transmit engine of the network + // interface is busy. Both need to sync SNP. + // + TxBuf = NULL; + + do { + // + // Get the recycled transmit buffer status. + // + Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf); + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Status = EFI_TIMEOUT; + break; + } + + } while (TxBuf == NULL); + + if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) { + break; + } + + // + // Status is EFI_NOT_READY. Restart the timer event and + // call Snp->Transmit again. + // + gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT); + } + + gBS->SetTimer (TimeoutEvent, TimerCancel, 0); + +ON_EXIT: + gBS->CloseEvent (TimeoutEvent); + return Status; +} + +/** + Build a syslog packet, including the Ethernet/Ip/Udp headers + and user's message. + + @param[in] Level - Syslog servity level + @param[in] Module - The module that generates the log + @param[in] File - The file that contains the current log + @param[in] Line - The line of code in the File that contains the current log + @param[in] Message - The log message + @param[in] BufLen - The lenght of the Buf + @param[out] Buf - The buffer to put the packet data + +Returns: + + The length of the syslog packet built. + +**/ +UINT32 +SyslogBuildPacket ( + IN UINT32 Level, + IN UINT8 *Module, + IN UINT8 *File, + IN UINT32 Line, + IN UINT8 *Message, + IN UINT32 BufLen, + OUT CHAR8 *Buf + ) +{ + ETHER_HEAD *Ether; + IP4_HEAD *Ip4; + EFI_UDP_HEADER *Udp4; + EFI_TIME Time; + UINT32 Pri; + UINT32 Len; + + // + // Fill in the Ethernet header. Leave alone the source MAC. + // SyslogSendPacket will fill in the address for us. + // + Ether = (ETHER_HEAD *) Buf; + CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN); + ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN); + + Ether->EtherType = HTONS (0x0800); // IPv4 protocol + + Buf += sizeof (ETHER_HEAD); + BufLen -= sizeof (ETHER_HEAD); + + // + // Fill in the IP header + // + Ip4 = (IP4_HEAD *) Buf; + Ip4->HeadLen = 5; + Ip4->Ver = 4; + Ip4->Tos = 0; + Ip4->TotalLen = 0; + Ip4->Id = (UINT16) mSyslogPacketSeq; + Ip4->Fragment = 0; + Ip4->Ttl = 16; + Ip4->Protocol = 0x11; + Ip4->Checksum = 0; + Ip4->Src = mSyslogSrcIp; + Ip4->Dst = mSyslogDstIp; + + Buf += sizeof (IP4_HEAD); + BufLen -= sizeof (IP4_HEAD); + + // + // Fill in the UDP header, Udp checksum is optional. Leave it zero. + // + Udp4 = (EFI_UDP_HEADER *) Buf; + Udp4->SrcPort = HTONS (514); + Udp4->DstPort = HTONS (514); + Udp4->Length = 0; + Udp4->Checksum = 0; + + Buf += sizeof (EFI_UDP_HEADER); + BufLen -= sizeof (EFI_UDP_HEADER); + + // + // Build the syslog message body with Timestamp machine module Message + // + Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7); + gRT->GetTime (&Time, NULL); + + // + // Use %a to format the ASCII strings, %s to format UNICODE strings + // + Len = 0; + Len += (UINT32) AsciiSPrint ( + Buf, + BufLen, + "<%d> %a %d %d:%d:%d ", + Pri, + mMonthName [Time.Month-1], + Time.Day, + Time.Hour, + Time.Minute, + Time.Second + ); + Len--; + + Len += (UINT32) AsciiSPrint ( + Buf + Len, + BufLen - Len, + "Tiano %a: %a (Line: %d File: %a)", + Module, + Message, + Line, + File + ); + Len--; + + // + // OK, patch the IP length/checksum and UDP length fields. + // + Len += sizeof (EFI_UDP_HEADER); + Udp4->Length = HTONS ((UINT16) Len); + + Len += sizeof (IP4_HEAD); + Ip4->TotalLen = HTONS ((UINT16) Len); + Ip4->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD))); + + return Len + sizeof (ETHER_HEAD); +} + +/** + Allocate a buffer, then format the message to it. This is a + help function for the NET_DEBUG_XXX macros. The PrintArg of + these macros treats the variable length print parameters as a + single parameter, and pass it to the NetDebugASPrint. For + example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name)) + if extracted to: + + NetDebugOutput ( + NETDEBUG_LEVEL_TRACE, + "Tcp", + __FILE__, + __LINE__, + NetDebugASPrint ("State transit to %a\n", Name) + ) + + @param Format The ASCII format string. + @param ... The variable length parameter whose format is determined + by the Format string. + + @return The buffer containing the formatted message, + or NULL if failed to allocate memory. + +**/ +CHAR8 * +NetDebugASPrint ( + IN CHAR8 *Format, + ... + ) +{ + VA_LIST Marker; + CHAR8 *Buf; + + Buf = (CHAR8 *) AllocatePool (NET_DEBUG_MSG_LEN); + + if (Buf == NULL) { + return NULL; + } + + VA_START (Marker, Format); + AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker); + VA_END (Marker); + + return Buf; +} + +/** + Builds an UDP4 syslog packet and send it using SNP. + + This function will locate a instance of SNP then send the message through it. + Because it isn't open the SNP BY_DRIVER, apply caution when using it. + + @param Level The servity level of the message. + @param Module The Moudle that generates the log. + @param File The file that contains the log. + @param Line The exact line that contains the log. + @param Message The user message to log. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet + @retval EFI_SUCCESS The log is discard because that it is more verbose + than the mNetDebugLevelMax. Or, it has been sent out. +**/ +EFI_STATUS +NetDebugOutput ( + IN UINT32 Level, + IN UINT8 *Module, + IN UINT8 *File, + IN UINT32 Line, + IN UINT8 *Message + ) +{ + CHAR8 *Packet; + UINT32 Len; + EFI_STATUS Status; + + // + // Check whether the message should be sent out + // + if (Message == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Level > mNetDebugLevelMax) { + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // Allocate a maxium of 1024 bytes, the caller should ensure + // that the message plus the ethernet/ip/udp header is shorter + // than this + // + Packet = (CHAR8 *) AllocatePool (NET_SYSLOG_PACKET_LEN); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Build the message: Ethernet header + IP header + Udp Header + user data + // + Len = SyslogBuildPacket ( + Level, + Module, + File, + Line, + Message, + NET_SYSLOG_PACKET_LEN, + Packet + ); + + mSyslogPacketSeq++; + Status = SyslogSendPacket (Packet, Len); + FreePool (Packet); + +ON_EXIT: + FreePool (Message); + return Status; +} /** Return the length of the mask. @@ -179,7 +625,7 @@ NetGetIpClass ( **/ BOOLEAN EFIAPI -Ip4IsUnicast ( +NetIp4IsUnicast ( IN IP4_ADDR Ip, IN IP4_ADDR NetMask ) @@ -218,7 +664,7 @@ Ip4IsUnicast ( **/ BOOLEAN -Ip6IsValidUnicast ( +NetIp6IsValidUnicast ( IN EFI_IPv6_ADDRESS *Ip6 ) { @@ -244,6 +690,113 @@ Ip6IsValidUnicast ( return TRUE; } +/** + Check whether the incoming Ipv6 address is the unspecified address or not. + + @param[in] Ip6 - Ip6 address, in network order. + + @retval TRUE - Yes, unspecified + @retval FALSE - No + +**/ +BOOLEAN +NetIp6IsUnspecifiedAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ) +{ + UINT8 Index; + + for (Index = 0; Index < 16; Index++) { + if (Ip6->Addr[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Check whether the incoming Ipv6 address is a link-local address. + + @param[in] Ip6 - Ip6 address, in network order. + + @retval TRUE - Yes, link-local address + @retval FALSE - No + +**/ +BOOLEAN +NetIp6IsLinkLocalAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ) +{ + UINT8 Index; + + ASSERT (Ip6 != NULL); + + if (Ip6->Addr[0] != 0xFE) { + return FALSE; + } + + if (Ip6->Addr[1] != 0x80) { + return FALSE; + } + + for (Index = 2; Index < 8; Index++) { + if (Ip6->Addr[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Check whether the Ipv6 address1 and address2 are on the connected network. + + @param[in] Ip1 - Ip6 address1, in network order. + @param[in] Ip2 - Ip6 address2, in network order. + @param[in] PrefixLength - The prefix length of the checking net. + + @retval TRUE - Yes, connected. + @retval FALSE - No. + +**/ +BOOLEAN +NetIp6IsNetEqual ( + EFI_IPv6_ADDRESS *Ip1, + EFI_IPv6_ADDRESS *Ip2, + UINT8 PrefixLength + ) +{ + UINT8 Byte; + UINT8 Bit; + UINT8 Mask; + + ASSERT (Ip1 != NULL && Ip2 != NULL); + + if (PrefixLength == 0) { + return TRUE; + } + + Byte = (UINT8) (PrefixLength / 8); + Bit = (UINT8) (PrefixLength % 8); + + if (CompareMem (Ip1, Ip2, Byte) != 0) { + return FALSE; + } + + if (Bit > 0) { + Mask = (UINT8) (0xFF << (8 - Bit)); + + if ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) { + return FALSE; + } + } + + return TRUE; +} + + /** Switches the endianess of an IPv6 address @@ -1432,7 +1985,7 @@ ON_EXIT: Get other info from parameters to make up the whole IPv4 device path node. @param[in, out] Node Pointer to the IPv4 device path node. - @param[in] Controller The handle where the NIC IP4 config protocol resides. + @param[in] Controller The controller handle. @param[in] LocalIp The local IPv4 address. @param[in] LocalPort The local port. @param[in] RemoteIp The remote IPv4 address. @@ -1473,6 +2026,47 @@ NetLibCreateIPv4DPathNode ( } } +/** + Create an IPv6 device path node. + + The header type of IPv6 device path node is MESSAGING_DEVICE_PATH. + The header subtype of IPv6 device path node is MSG_IPv6_DP. + Get other info from parameters to make up the whole IPv6 device path node. + + @param[in, out] Node Pointer to the IPv6 device path node. + @param[in] Controller The controller handle. + @param[in] LocalIp The local IPv6 address. + @param[in] LocalPort The local port. + @param[in] RemoteIp The remote IPv6 address. + @param[in] RemotePort The remote port. + @param[in] Protocol The protocol type in the IP header. + +**/ +VOID +EFIAPI +NetLibCreateIPv6DPathNode ( + IN OUT IPv6_DEVICE_PATH *Node, + IN EFI_HANDLE Controller, + IN EFI_IPv6_ADDRESS *LocalIp, + IN UINT16 LocalPort, + IN EFI_IPv6_ADDRESS *RemoteIp, + IN UINT16 RemotePort, + IN UINT16 Protocol + ) +{ + Node->Header.Type = MESSAGING_DEVICE_PATH; + Node->Header.SubType = MSG_IPv6_DP; + SetDevicePathNodeLength (&Node->Header, sizeof (IPv6_DEVICE_PATH)); + + CopyMem (&Node->LocalIpAddress, LocalIp, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Node->RemoteIpAddress, RemoteIp, sizeof (EFI_IPv6_ADDRESS)); + + Node->LocalPort = LocalPort; + Node->RemotePort = RemotePort; + + Node->Protocol = Protocol; + Node->StaticIpAddress = FALSE; +} /** Find the UNDI/SNP handle from controller and protocol GUID.