From 772db4bb33ae66fa20e39f786b5f80d107d450a5 Mon Sep 17 00:00:00 2001 From: vanjeff Date: Mon, 30 Jul 2007 02:37:10 +0000 Subject: [PATCH] Import ArpDxe, Dhcp4Dxe, Ip4Dxe, Mtftp4Dxe, PxeBcDxe and PxeDhcp4Dxe. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3492 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Include/Library/IpIoLib.h | 5 - MdeModulePkg/Include/Library/NetLib.h | 360 +- MdeModulePkg/Include/Protocol/PxeDhcp4.h | 350 ++ .../Include/Protocol/PxeDhcp4CallBack.h | 85 + MdeModulePkg/Include/Protocol/Tcp.h | 108 + MdeModulePkg/Library/DxeNetLib/DxeNetLib.c | 1 + MdeModulePkg/MdeModulePkg.dec | 4 + MdeModulePkg/MdeModulePkg.dsc | 8 +- .../Universal/Network/ArpDxe/ArpDebug.h | 30 + .../Universal/Network/ArpDxe/ArpDriver.c | 763 ++++ .../Universal/Network/ArpDxe/ArpDriver.h | 84 + .../Universal/Network/ArpDxe/ArpDxe.inf | 59 + .../Universal/Network/ArpDxe/ArpDxe.msa | 72 + .../Universal/Network/ArpDxe/ArpImpl.c | 1628 ++++++++ .../Universal/Network/ArpDxe/ArpImpl.h | 341 ++ .../Universal/Network/ArpDxe/ArpMain.c | 727 ++++ .../Universal/Network/ArpDxe/ComponentName.c | 156 + .../Network/Dhcp4Dxe/ComponentName.c | 162 + .../Universal/Network/Dhcp4Dxe/Dhcp4Driver.c | 665 ++++ .../Universal/Network/Dhcp4Dxe/Dhcp4Driver.h | 67 + .../Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf | 65 + .../Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa | 77 + .../Universal/Network/Dhcp4Dxe/Dhcp4Impl.c | 914 +++++ .../Universal/Network/Dhcp4Dxe/Dhcp4Impl.h | 159 + .../Universal/Network/Dhcp4Dxe/Dhcp4Io.c | 1631 ++++++++ .../Universal/Network/Dhcp4Dxe/Dhcp4Io.h | 115 + .../Universal/Network/Dhcp4Dxe/Dhcp4Option.c | 906 +++++ .../Universal/Network/Dhcp4Dxe/Dhcp4Option.h | 266 ++ .../Network/Ip4ConfigDxe/Ip4Config.c | 24 +- .../Network/Ip4ConfigDxe/Ip4ConfigDriver.c | 1 - .../Universal/Network/Ip4Dxe/ComponentName.c | 166 + .../Universal/Network/Ip4Dxe/Ip4Common.c | 421 +++ .../Universal/Network/Ip4Dxe/Ip4Common.h | 143 + .../Universal/Network/Ip4Dxe/Ip4Driver.c | 932 +++++ .../Universal/Network/Ip4Dxe/Ip4Driver.h | 84 + .../Universal/Network/Ip4Dxe/Ip4Dxe.inf | 81 + .../Universal/Network/Ip4Dxe/Ip4Dxe.msa | 101 + .../Universal/Network/Ip4Dxe/Ip4Icmp.c | 372 ++ .../Universal/Network/Ip4Dxe/Ip4Icmp.h | 99 + MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c | 1146 ++++++ MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h | 238 ++ .../Universal/Network/Ip4Dxe/Ip4Igmp.c | 629 ++++ .../Universal/Network/Ip4Dxe/Ip4Igmp.h | 120 + .../Universal/Network/Ip4Dxe/Ip4Impl.c | 2022 ++++++++++ .../Universal/Network/Ip4Dxe/Ip4Impl.h | 258 ++ .../Universal/Network/Ip4Dxe/Ip4Input.c | 1235 +++++++ .../Universal/Network/Ip4Dxe/Ip4Input.h | 138 + .../Universal/Network/Ip4Dxe/Ip4Option.c | 231 ++ .../Universal/Network/Ip4Dxe/Ip4Option.h | 52 + .../Universal/Network/Ip4Dxe/Ip4Output.c | 457 +++ .../Universal/Network/Ip4Dxe/Ip4Output.h | 54 + .../Universal/Network/Ip4Dxe/Ip4Route.c | 690 ++++ .../Universal/Network/Ip4Dxe/Ip4Route.h | 151 + .../Universal/Network/MnpDxe/MnpConfig.c | 16 +- .../Universal/Network/MnpDxe/MnpDriver.c | 1 - .../Universal/Network/MnpDxe/MnpDriver.h | 1 + MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c | 5 +- .../Network/Mtftp4Dxe/ComponentName.c | 161 + .../Network/Mtftp4Dxe/Mtftp4Driver.c | 643 ++++ .../Network/Mtftp4Dxe/Mtftp4Driver.h | 69 + .../Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf | 68 + .../Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa | 79 + .../Universal/Network/Mtftp4Dxe/Mtftp4Impl.c | 892 +++++ .../Universal/Network/Mtftp4Dxe/Mtftp4Impl.h | 176 + .../Network/Mtftp4Dxe/Mtftp4Option.c | 542 +++ .../Network/Mtftp4Dxe/Mtftp4Option.h | 73 + .../Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c | 735 ++++ .../Network/Mtftp4Dxe/Mtftp4Support.c | 591 +++ .../Network/Mtftp4Dxe/Mtftp4Support.h | 96 + .../Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c | 522 +++ MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c | 2401 ++++++++++++ MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h | 564 +++ .../Network/PxeBcDxe/ComponentName.c | 160 + .../Universal/Network/PxeBcDxe/Dhcp.h | 632 ++++ .../Universal/Network/PxeBcDxe/Ebc/PxeArch.c | 46 + .../Universal/Network/PxeBcDxe/Ebc/PxeArch.h | 36 + .../Universal/Network/PxeBcDxe/Hton.h | 43 + .../Universal/Network/PxeBcDxe/Ia32/PxeArch.h | 26 + MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h | 736 ++++ .../Universal/Network/PxeBcDxe/Ipf/PxeArch.h | 26 + .../Universal/Network/PxeBcDxe/PxeBcDxe.inf | 92 + .../Universal/Network/PxeBcDxe/PxeBcDxe.msa | 106 + .../Universal/Network/PxeBcDxe/Pxe_bc_arp.c | 583 +++ .../Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c | 3284 +++++++++++++++++ .../Universal/Network/PxeBcDxe/Pxe_bc_igmp.c | 421 +++ .../Universal/Network/PxeBcDxe/Pxe_bc_ip.c | 846 +++++ .../Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c | 2193 +++++++++++ .../Universal/Network/PxeBcDxe/Pxe_bc_udp.c | 517 +++ .../Universal/Network/PxeBcDxe/Pxe_loadfile.c | 1614 ++++++++ .../Universal/Network/PxeBcDxe/Tftp.h | 154 + .../Universal/Network/PxeBcDxe/X64/PxeArch.h | 26 + .../Network/PxeDhcp4Dxe/ComponentName.c | 169 + .../Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c | 355 ++ .../Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h | 353 ++ .../Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf | 64 + .../Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa | 78 + .../Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c | 784 ++++ .../Network/PxeDhcp4Dxe/PxeDhcp4Release.c | 247 ++ .../Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c | 408 ++ .../Network/PxeDhcp4Dxe/PxeDhcp4Run.c | 191 + .../Network/PxeDhcp4Dxe/PxeDhcp4Setup.c | 258 ++ .../Universal/Network/PxeDhcp4Dxe/Support.c | 1086 ++++++ MdeModulePkg/Universal/Network/SnpDxe/snp.h | 14 - MdeModulePkg/Universal/Network/SnpDxe/start.c | 2 +- .../Network/Tcp4Dxe/Tcp4Dispatcher.c | 8 +- .../Universal/Network/Tcp4Dxe/Tcp4Driver.c | 1 - .../Universal/Network/Tcp4Dxe/Tcp4Io.c | 4 +- .../Universal/Network/Tcp4Dxe/Tcp4Main.c | 24 +- .../Universal/Network/Tcp4Dxe/Tcp4Main.h | 1 - .../Universal/Network/Tcp4Dxe/Tcp4Misc.c | 12 +- .../Universal/Network/Udp4Dxe/Udp4Driver.c | 1 - .../Universal/Network/Udp4Dxe/Udp4Impl.c | 84 +- .../Universal/Network/Udp4Dxe/Udp4Main.c | 64 +- 113 files changed, 42735 insertions(+), 302 deletions(-) create mode 100644 MdeModulePkg/Include/Protocol/PxeDhcp4.h create mode 100644 MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h create mode 100644 MdeModulePkg/Include/Protocol/Tcp.h create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c create mode 100644 MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c create mode 100644 MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c create mode 100644 MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h create mode 100644 MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ebc/PxeArch.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Hton.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h create mode 100644 MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c create mode 100644 MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c diff --git a/MdeModulePkg/Include/Library/IpIoLib.h b/MdeModulePkg/Include/Library/IpIoLib.h index 894e07bed4..e2c9f67559 100644 --- a/MdeModulePkg/Include/Library/IpIoLib.h +++ b/MdeModulePkg/Include/Library/IpIoLib.h @@ -73,11 +73,6 @@ typedef struct _ICMP_ERROR_INFO { BOOLEAN Notify; } ICMP_ERROR_INFO; -// -// Driver Consumed Protocol Prototypes -// -//@MT:#include EFI_PROTOCOL_CONSUMER (Ip4) -//@MT:#include EFI_PROTOCOL_CONSUMER (Udp4) #define EFI_IP4_HEADER_LEN(HdrPtr) ((HdrPtr)->HeaderLength << 2) diff --git a/MdeModulePkg/Include/Library/NetLib.h b/MdeModulePkg/Include/Library/NetLib.h index 150b97936e..fe240442ae 100644 --- a/MdeModulePkg/Include/Library/NetLib.h +++ b/MdeModulePkg/Include/Library/NetLib.h @@ -29,189 +29,189 @@ Abstract: #include #include -#define EFI_NET_LITTLE_ENDIAN - -typedef UINT32 IP4_ADDR; -typedef UINT32 TCP_SEQNO; -typedef UINT16 TCP_PORTNO; - -enum { - NET_ETHER_ADDR_LEN = 6, - NET_IFTYPE_ETHERNET = 0x01, - - EFI_IP_PROTO_UDP = 0x11, - EFI_IP_PROTO_TCP = 0x06, - EFI_IP_PROTO_ICMP = 0x01, - - // - // The address classfication - // - IP4_ADDR_CLASSA = 1, - IP4_ADDR_CLASSB, - IP4_ADDR_CLASSC, - IP4_ADDR_CLASSD, - IP4_ADDR_CLASSE, - - IP4_MASK_NUM = 33, -}; - -#pragma pack(1) - -// -// Ethernet head definition -// -typedef struct { - UINT8 DstMac [NET_ETHER_ADDR_LEN]; - UINT8 SrcMac [NET_ETHER_ADDR_LEN]; - UINT16 EtherType; -} ETHER_HEAD; - - -// -// The EFI_IP4_HEADER is hard to use because the source and -// destination address are defined as EFI_IPv4_ADDRESS, which -// is a structure. Two structures can't be compared or masked -// directly. This is why there is an internal representation. -// -typedef struct { -#ifdef EFI_NET_LITTLE_ENDIAN - UINT8 HeadLen : 4; - UINT8 Ver : 4; -#else - UINT8 Ver : 4; - UINT8 HeadLen : 4; -#endif - UINT8 Tos; - UINT16 TotalLen; - UINT16 Id; - UINT16 Fragment; - UINT8 Ttl; - UINT8 Protocol; - UINT16 Checksum; - IP4_ADDR Src; - IP4_ADDR Dst; -} IP4_HEAD; - - -// -// ICMP head definition. ICMP message is categoried as either an error -// message or query message. Two message types have their own head format. -// -typedef struct { - UINT8 Type; - UINT8 Code; - UINT16 Checksum; -} IP4_ICMP_HEAD; - -typedef struct { - IP4_ICMP_HEAD Head; - UINT32 Fourth; // 4th filed of the head, it depends on Type. - IP4_HEAD IpHead; -} IP4_ICMP_ERROR_HEAD; - -typedef struct { - IP4_ICMP_HEAD Head; - UINT16 Id; - UINT16 Seq; -} IP4_ICMP_QUERY_HEAD; - - -// -// UDP header definition -// -typedef struct { - UINT16 SrcPort; - UINT16 DstPort; - UINT16 Length; - UINT16 Checksum; -} EFI_UDP4_HEADER; - - -// -// TCP header definition -// -typedef struct { - TCP_PORTNO SrcPort; - TCP_PORTNO DstPort; - TCP_SEQNO Seq; - TCP_SEQNO Ack; -#ifdef EFI_NET_LITTLE_ENDIAN - UINT8 Res : 4; - UINT8 HeadLen : 4; -#else - UINT8 HeadLen : 4; - UINT8 Res : 4; -#endif - UINT8 Flag; - UINT16 Wnd; - UINT16 Checksum; - UINT16 Urg; -} TCP_HEAD; - -#pragma pack() - -#define NET_MAC_EQUAL(pMac1, pMac2, Len) \ - (NetCompareMem ((pMac1), (pMac2), Len) == 0) - -#define NET_MAC_IS_MULTICAST(Mac, BMac, Len) \ - (((*((UINT8 *) Mac) & 0x01) == 0x01) && (!NET_MAC_EQUAL (Mac, BMac, Len))) - -#ifdef EFI_NET_LITTLE_ENDIAN -#define NTOHL(x) (UINT32)((((UINT32) (x) & 0xff) << 24) | \ - (((UINT32) (x) & 0xff00) << 8) | \ - (((UINT32) (x) & 0xff0000) >> 8) | \ - (((UINT32) (x) & 0xff000000) >> 24)) - -#define HTONL(x) NTOHL(x) - -#define NTOHS(x) (UINT16)((((UINT16) (x) & 0xff) << 8) | \ - (((UINT16) (x) & 0xff00) >> 8)) - -#define HTONS(x) NTOHS(x) -#else -#define NTOHL(x) (UINT32)(x) -#define HTONL(x) (UINT32)(x) -#define NTOHS(x) (UINT16)(x) -#define HTONS(x) (UINT16)(x) -#endif - -// -// Test the IP's attribute, All the IPs are in host byte order. -// -#define IP4_IS_MULTICAST(Ip) (((Ip) & 0xF0000000) == 0xE0000000) -#define IP4_IS_LOCAL_BROADCAST(Ip) ((Ip) == 0xFFFFFFFF) -#define IP4_NET_EQUAL(Ip1, Ip2, NetMask) (((Ip1) & (NetMask)) == ((Ip2) & (NetMask))) -#define IP4_IS_VALID_NETMASK(Ip) (NetGetMaskLength (Ip) != IP4_MASK_NUM) - -// -// Convert the EFI_IP4_ADDRESS to plain UINT32 IP4 address. -// -#define EFI_IP4(EfiIpAddr) (*(IP4_ADDR *) ((EfiIpAddr).Addr)) -#define EFI_NTOHL(EfiIp) (NTOHL (EFI_IP4 ((EfiIp)))) -#define EFI_IP_EQUAL(Ip1, Ip2) (EFI_IP4 (Ip1) == EFI_IP4 (Ip2)) - -INTN -NetGetMaskLength ( - IN IP4_ADDR Mask - ); - -INTN -NetGetIpClass ( - IN IP4_ADDR Addr - ); - -BOOLEAN -Ip4IsUnicast ( - IN IP4_ADDR Ip, - IN IP4_ADDR NetMask - ); - +#define EFI_NET_LITTLE_ENDIAN + +typedef UINT32 IP4_ADDR; +typedef UINT32 TCP_SEQNO; +typedef UINT16 TCP_PORTNO; + +enum { + NET_ETHER_ADDR_LEN = 6, + NET_IFTYPE_ETHERNET = 0x01, + + EFI_IP_PROTO_UDP = 0x11, + EFI_IP_PROTO_TCP = 0x06, + EFI_IP_PROTO_ICMP = 0x01, + + // + // The address classfication + // + IP4_ADDR_CLASSA = 1, + IP4_ADDR_CLASSB, + IP4_ADDR_CLASSC, + IP4_ADDR_CLASSD, + IP4_ADDR_CLASSE, + + IP4_MASK_NUM = 33, +}; + +#pragma pack(1) + +// +// Ethernet head definition +// +typedef struct { + UINT8 DstMac [NET_ETHER_ADDR_LEN]; + UINT8 SrcMac [NET_ETHER_ADDR_LEN]; + UINT16 EtherType; +} ETHER_HEAD; + + +// +// The EFI_IP4_HEADER is hard to use because the source and +// destination address are defined as EFI_IPv4_ADDRESS, which +// is a structure. Two structures can't be compared or masked +// directly. This is why there is an internal representation. +// +typedef struct { +#ifdef EFI_NET_LITTLE_ENDIAN + UINT8 HeadLen : 4; + UINT8 Ver : 4; +#else + UINT8 Ver : 4; + UINT8 HeadLen : 4; +#endif + UINT8 Tos; + UINT16 TotalLen; + UINT16 Id; + UINT16 Fragment; + UINT8 Ttl; + UINT8 Protocol; + UINT16 Checksum; + IP4_ADDR Src; + IP4_ADDR Dst; +} IP4_HEAD; + + +// +// ICMP head definition. ICMP message is categoried as either an error +// message or query message. Two message types have their own head format. +// +typedef struct { + UINT8 Type; + UINT8 Code; + UINT16 Checksum; +} IP4_ICMP_HEAD; + +typedef struct { + IP4_ICMP_HEAD Head; + UINT32 Fourth; // 4th filed of the head, it depends on Type. + IP4_HEAD IpHead; +} IP4_ICMP_ERROR_HEAD; + +typedef struct { + IP4_ICMP_HEAD Head; + UINT16 Id; + UINT16 Seq; +} IP4_ICMP_QUERY_HEAD; + + +// +// UDP header definition +// +typedef struct { + UINT16 SrcPort; + UINT16 DstPort; + UINT16 Length; + UINT16 Checksum; +} EFI_UDP4_HEADER; + + +// +// TCP header definition +// +typedef struct { + TCP_PORTNO SrcPort; + TCP_PORTNO DstPort; + TCP_SEQNO Seq; + TCP_SEQNO Ack; +#ifdef EFI_NET_LITTLE_ENDIAN + UINT8 Res : 4; + UINT8 HeadLen : 4; +#else + UINT8 HeadLen : 4; + UINT8 Res : 4; +#endif + UINT8 Flag; + UINT16 Wnd; + UINT16 Checksum; + UINT16 Urg; +} TCP_HEAD; + +#pragma pack() + +#define NET_MAC_EQUAL(pMac1, pMac2, Len) \ + (NetCompareMem ((pMac1), (pMac2), Len) == 0) + +#define NET_MAC_IS_MULTICAST(Mac, BMac, Len) \ + (((*((UINT8 *) Mac) & 0x01) == 0x01) && (!NET_MAC_EQUAL (Mac, BMac, Len))) + +#ifdef EFI_NET_LITTLE_ENDIAN +#define NTOHL(x) (UINT32)((((UINT32) (x) & 0xff) << 24) | \ + (((UINT32) (x) & 0xff00) << 8) | \ + (((UINT32) (x) & 0xff0000) >> 8) | \ + (((UINT32) (x) & 0xff000000) >> 24)) + +#define HTONL(x) NTOHL(x) + +#define NTOHS(x) (UINT16)((((UINT16) (x) & 0xff) << 8) | \ + (((UINT16) (x) & 0xff00) >> 8)) + +#define HTONS(x) NTOHS(x) +#else +#define NTOHL(x) (UINT32)(x) +#define HTONL(x) (UINT32)(x) +#define NTOHS(x) (UINT16)(x) +#define HTONS(x) (UINT16)(x) +#endif + +// +// Test the IP's attribute, All the IPs are in host byte order. +// +#define IP4_IS_MULTICAST(Ip) (((Ip) & 0xF0000000) == 0xE0000000) +#define IP4_IS_LOCAL_BROADCAST(Ip) ((Ip) == 0xFFFFFFFF) +#define IP4_NET_EQUAL(Ip1, Ip2, NetMask) (((Ip1) & (NetMask)) == ((Ip2) & (NetMask))) +#define IP4_IS_VALID_NETMASK(Ip) (NetGetMaskLength (Ip) != IP4_MASK_NUM) + +// +// Convert the EFI_IP4_ADDRESS to plain UINT32 IP4 address. +// +#define EFI_IP4(EfiIpAddr) (*(IP4_ADDR *) ((EfiIpAddr).Addr)) +#define EFI_NTOHL(EfiIp) (NTOHL (EFI_IP4 ((EfiIp)))) +#define EFI_IP4_EQUAL(Ip1, Ip2) (NetCompareMem (&(Ip1), &(Ip2), sizeof (EFI_IPv4_ADDRESS)) == 0) + +INTN +NetGetMaskLength ( + IN IP4_ADDR Mask + ); + +INTN +NetGetIpClass ( + IN IP4_ADDR Addr + ); + +BOOLEAN +Ip4IsUnicast ( + IN IP4_ADDR Ip, + IN IP4_ADDR NetMask + ); + extern IP4_ADDR mIp4AllMasks [IP4_MASK_NUM]; -//@MT:#include EFI_PROTOCOL_CONSUMER (LoadedImage) -//@MT:#include EFI_PROTOCOL_CONSUMER (ServiceBinding) -//@MT:#include EFI_PROTOCOL_CONSUMER (SimpleNetwork) +extern EFI_IPv4_ADDRESS mZeroIp4Addr; + +#define NET_IS_DIGIT(Ch) (('0' <= (Ch)) && ((Ch) <= '9')) // // Wrap functions to ease the impact of EFI library changes. // diff --git a/MdeModulePkg/Include/Protocol/PxeDhcp4.h b/MdeModulePkg/Include/Protocol/PxeDhcp4.h new file mode 100644 index 0000000000..53fe743f55 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/PxeDhcp4.h @@ -0,0 +1,350 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4.h + +Abstract: + EFI PXE DHCPv4 protocol definition + +--*/ + +#ifndef _PXEDHCP4_H_ +#define _PXEDHCP4_H_ + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// +// PXE DHCPv4 GUID definition +// + +#define EFI_PXE_DHCP4_PROTOCOL_GUID \ + { 0x03c4e624, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x29, 0x3f, 0xc1, 0x4d } } + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// +// Interface definition +// + +typedef struct _EFI_PXE_DHCP4_PROTOCOL EFI_PXE_DHCP4_PROTOCOL; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// +// Descriptions of the DHCP version 4 header and options can be found +// in RFC-2131 and RFC-2132 at www.ietf.org +// + +#pragma pack(1) +typedef struct { + + UINT8 op; +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + + UINT8 htype; + + UINT8 hlen; + + UINT8 hops; + + UINT32 xid; + + UINT16 secs; +#define DHCP4_INITIAL_SECONDS 4 + + UINT16 flags; +#define DHCP4_BROADCAST_FLAG 0x8000 + + UINT32 ciaddr; + + UINT32 yiaddr; + + UINT32 siaddr; + + UINT32 giaddr; + + UINT8 chaddr[16]; + + UINT8 sname[64]; + + UINT8 fname[128]; + +// +// This is the minimum option length as specified in RFC-2131. +// The packet must be padded out this far with DHCP4_PAD. +// DHCPv4 packets are usually 576 bytes in length. This length +// includes the IPv4 and UDPv4 headers but not the media header. +// Note: Not all DHCP relay agents will forward DHCPv4 packets +// if they are less than 384 bytes or exceed 576 bytes. Even if +// the underlying hardware can handle smaller and larger packets, +// many older relay agents will not accept them. +// + UINT32 magik; +#define DHCP4_MAGIK_NUMBER 0x63825363 + + UINT8 options[308]; + +} DHCP4_HEADER; +#pragma pack() + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// +// DHCPv4 packet definition. Room for 576 bytes including IP and +// UDP header. +// + +#define DHCP4_MAX_PACKET_SIZE 576 +#define DHCP4_UDP_HEADER_SIZE 8 +#define DHCP4_IP_HEADER_SIZE 20 + +#pragma pack(1) +typedef union _DHCP4_PACKET { + UINT32 _force_data_alignment; + + UINT8 raw[1500]; + + DHCP4_HEADER dhcp4; +} DHCP4_PACKET; +#pragma pack() + +#define DHCP4_SERVER_PORT 67 +#define DHCP4_CLIENT_PORT 68 + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// +// DHCPv4 and PXE option numbers. +// + +#define DHCP4_PAD 0 +#define DHCP4_END 255 +#define DHCP4_SUBNET_MASK 1 +#define DHCP4_TIME_OFFSET 2 +#define DHCP4_ROUTER_LIST 3 +#define DHCP4_TIME_SERVERS 4 +#define DHCP4_NAME_SERVERS 5 +#define DHCP4_DNS_SERVERS 6 +#define DHCP4_LOG_SERVERS 7 +#define DHCP4_COOKIE_SERVERS 8 +#define DHCP4_LPR_SREVERS 9 +#define DHCP4_IMPRESS_SERVERS 10 +#define DHCP4_RESOURCE_LOCATION_SERVERS 11 +#define DHCP4_HOST_NAME 12 +#define DHCP4_BOOT_FILE_SIZE 13 +#define DHCP4_DUMP_FILE 14 +#define DHCP4_DOMAIN_NAME 15 +#define DHCP4_SWAP_SERVER 16 +#define DHCP4_ROOT_PATH 17 +#define DHCP4_EXTENSION_PATH 18 +#define DHCP4_IP_FORWARDING 19 +#define DHCP4_NON_LOCAL_SOURCE_ROUTE 20 +#define DHCP4_POLICY_FILTER 21 +#define DHCP4_MAX_DATAGRAM_SIZE 22 +#define DHCP4_DEFAULT_TTL 23 +#define DHCP4_MTU_AGING_TIMEOUT 24 +#define DHCP4_MTU_SIZES 25 +#define DHCP4_MTU_TO_USE 26 +#define DHCP4_ALL_SUBNETS_LOCAL 27 +#define DHCP4_BROADCAST_ADDRESS 28 +#define DHCP4_PERFORM_MASK_DISCOVERY 29 +#define DHCP4_RESPOND_TO_MASK_REQ 30 +#define DHCP4_PERFORM_ROUTER_DISCOVERY 31 +#define DHCP4_ROUTER_SOLICIT_ADDRESS 32 +#define DHCP4_STATIC_ROUTER_LIST 33 +#define DHCP4_USE_ARP_TRAILERS 34 +#define DHCP4_ARP_CACHE_TIMEOUT 35 +#define DHCP4_ETHERNET_ENCAPSULATION 36 +#define DHCP4_TCP_DEFAULT_TTL 37 +#define DHCP4_TCP_KEEP_ALIVE_INT 38 +#define DHCP4_KEEP_ALIVE_GARBAGE 39 +#define DHCP4_NIS_DOMAIN_NAME 40 +#define DHCP4_NIS_SERVERS 41 +#define DHCP4_NTP_SERVERS 42 +#define DHCP4_VENDOR_SPECIFIC 43 +# define PXE_MTFTP_IP 1 +# define PXE_MTFTP_CPORT 2 +# define PXE_MTFTP_SPORT 3 +# define PXE_MTFTP_TMOUT 4 +# define PXE_MTFTP_DELAY 5 +# define PXE_DISCOVERY_CONTROL 6 +# define PXE_DISABLE_BROADCAST_DISCOVERY 0x01 +# define PXE_DISABLE_MULTICAST_DISCOVERY 0x02 +# define PXE_ACCEPT_ONLY_PXE_BOOT_SERVERS 0x04 +# define PXE_DO_NOT_PROMPT 0x08 +# define PXE_DISCOVERY_MCAST_ADDR 7 +# define PXE_BOOT_SERVERS 8 +# define PXE_BOOT_MENU 9 +# define PXE_BOOT_PROMPT 10 +# define PXE_MCAST_ADDRS_ALLOC 11 +# define PXE_CREDENTIAL_TYPES 12 +# define PXE_BOOT_ITEM 71 +#define DHCP4_NBNS_SERVERS 44 +#define DHCP4_NBDD_SERVERS 45 +#define DHCP4_NETBIOS_NODE_TYPE 46 +#define DHCP4_NETBIOS_SCOPE 47 +#define DHCP4_XWINDOW_SYSTEM_FONT_SERVERS 48 +#define DHCP4_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49 +#define DHCP4_REQUESTED_IP_ADDRESS 50 +#define DHCP4_LEASE_TIME 51 +#define DHCP4_OPTION_OVERLOAD 52 +# define DHCP4_OVERLOAD_FNAME 1 +# define DHCP4_OVERLOAD_SNAME 2 +# define DHCP4_OVERLOAD_FNAME_AND_SNAME 3 +#define DHCP4_MESSAGE_TYPE 53 +# define DHCP4_MESSAGE_TYPE_DISCOVER 1 +# define DHCP4_MESSAGE_TYPE_OFFER 2 +# define DHCP4_MESSAGE_TYPE_REQUEST 3 +# define DHCP4_MESSAGE_TYPE_DECLINE 4 +# define DHCP4_MESSAGE_TYPE_ACK 5 +# define DHCP4_MESSAGE_TYPE_NAK 6 +# define DHCP4_MESSAGE_TYPE_RELEASE 7 +# define DHCP4_MESSAGE_TYPE_INFORM 8 +#define DHCP4_SERVER_IDENTIFIER 54 +#define DHCP4_PARAMETER_REQUEST_LIST 55 +#define DHCP4_ERROR_MESSAGE 56 +#define DHCP4_MAX_MESSAGE_SIZE 57 +# define DHCP4_DEFAULT_MAX_MESSAGE_SIZE 576 +#define DHCP4_RENEWAL_TIME 58 +#define DHCP4_REBINDING_TIME 59 +#define DHCP4_CLASS_IDENTIFIER 60 +#define DHCP4_CLIENT_IDENTIFIER 61 +#define DHCP4_NISPLUS_DOMAIN_NAME 64 +#define DHCP4_NISPLUS_SERVERS 65 +#define DHCP4_TFTP_SERVER_NAME 66 +#define DHCP4_BOOTFILE 67 +#define DHCP4_MOBILE_IP_HOME_AGENTS 68 +#define DHCP4_SMPT_SERVERS 69 +#define DHCP4_POP3_SERVERS 70 +#define DHCP4_NNTP_SERVERS 71 +#define DHCP4_WWW_SERVERS 72 +#define DHCP4_FINGER_SERVERS 73 +#define DHCP4_IRC_SERVERS 74 +#define DHCP4_STREET_TALK_SERVERS 75 +#define DHCP4_STREET_TALK_DIR_ASSIST_SERVERS 76 +#define DHCP4_NDS_SERVERS 85 +#define DHCP4_NDS_TREE_NAME 86 +#define DHCP4_NDS_CONTEXT 87 +#define DHCP4_SYSTEM_ARCHITECTURE 93 +#define DHCP4_NETWORK_ARCHITECTURE 94 +#define DHCP4_PLATFORM_ID 97 + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// +// DHCP4 option format. +// + +#pragma pack(1) +typedef struct { + UINT8 op; + UINT8 len; + UINT8 data[1]; +} DHCP4_OP; +#pragma pack() + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +typedef struct { + DHCP4_PACKET Discover; + DHCP4_PACKET Offer; + DHCP4_PACKET Request; + DHCP4_PACKET AckNak; + BOOLEAN SetupCompleted; + BOOLEAN InitCompleted; + BOOLEAN SelectCompleted; + BOOLEAN IsBootp; + BOOLEAN IsAck; +} EFI_PXE_DHCP4_DATA; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_DHCP4_RUN) ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN OPTIONAL UINTN OpLen, + IN OPTIONAL VOID *OpList + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_DHCP4_SETUP) ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN OPTIONAL EFI_PXE_DHCP4_DATA * NewData + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_DHCP4_INIT) ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN SecondsTimeout, + OUT UINTN *Offers, + OUT DHCP4_PACKET **OfferList + ); + +#define DHCP4_MIN_SECONDS 1 +#define DHCP4_MAX_SECONDS 60 + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_DHCP4_SELECT) ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN SecondsTimeout, + IN DHCP4_PACKET * offer + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_DHCP4_RENEW) ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + UINTN seconds_timeout + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_DHCP4_REBIND) ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + UINTN seconds_timeout + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_DHCP4_RELEASE) ( + IN EFI_PXE_DHCP4_PROTOCOL * This + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define EFI_PXE_DHCP4_PROTOCOL_REVISION 0x00010000 + +struct _EFI_PXE_DHCP4_PROTOCOL { + UINT64 Revision; + EFI_PXE_DHCP4_RUN Run; + EFI_PXE_DHCP4_SETUP Setup; + EFI_PXE_DHCP4_INIT Init; + EFI_PXE_DHCP4_SELECT Select; + EFI_PXE_DHCP4_RENEW Renew; + EFI_PXE_DHCP4_REBIND Rebind; + EFI_PXE_DHCP4_RELEASE Release; + EFI_PXE_DHCP4_DATA *Data; +}; + +// +// +// + +extern EFI_GUID gEfiPxeDhcp4ProtocolGuid; + +#endif /* _PXEDHCP4_H_ */ +/* EOF - PxeDhcp4.h */ diff --git a/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h b/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h new file mode 100644 index 0000000000..cfba38042a --- /dev/null +++ b/MdeModulePkg/Include/Protocol/PxeDhcp4CallBack.h @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4Callback.h + +Abstract: + EFI PXE DHCP4 Callback protocol definition. + +--*/ + +#ifndef _PXE_DHCP4CALLBACK_H +#define _PXE_DHCP4CALLBACK_H + +#include +// +// GUID definition +// + +#define EFI_PXE_DHCP4_CALLBACK_PROTOCOL_GUID \ +{ 0xc1544c01, 0x92a4, 0x4198, {0x8a, 0x84, 0x77, 0x85, 0x83, 0xc2, 0x36, 0x21 } } + + +// +// Revision number +// + +#define EFI_PXE_DHCP4_CALLBACK_INTERFACE_REVISION 0x00010000 + +// +// Interface definition +// + +typedef struct _EFI_PXE_DHCP4_CALLBACK_PROTOCOL EFI_PXE_DHCP4_CALLBACK_PROTOCOL; + +typedef enum { + EFI_PXE_DHCP4_FUNCTION_FIRST, + EFI_PXE_DHCP4_FUNCTION_INIT, + EFI_PXE_DHCP4_FUNCTION_SELECT, + EFI_PXE_DHCP4_FUNCTION_RENEW, + EFI_PXE_DHCP4_FUNCTION_REBIND, + EFI_PXE_DHCP4_FUNCTION_LAST +} EFI_PXE_DHCP4_FUNCTION; + +typedef enum { + EFI_PXE_DHCP4_CALLBACK_STATUS_FIRST, + EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT, + EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT, + EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT, + EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE, + EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE, + EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE, + EFI_PXE_DHCP4_CALLBACK_STATUS_LAST +} EFI_PXE_DHCP4_CALLBACK_STATUS; + +typedef +EFI_PXE_DHCP4_CALLBACK_STATUS +(EFIAPI *EFI_PXE_DHCP4_CALLBACK) ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN EFI_PXE_DHCP4_FUNCTION Function, + IN UINT32 PacketLen, + IN DHCP4_PACKET *Packet OPTIONAL + ); + +struct _EFI_PXE_DHCP4_CALLBACK_PROTOCOL { + UINT64 Revision; + EFI_PXE_DHCP4_CALLBACK Callback; +}; + +// +// GUID declaration +// + +extern EFI_GUID gEfiPxeDhcp4CallbackProtocolGuid; + +#endif /* _PXE_DHCP4CALLBACK_H */ +/* EOF - PxeDhcp4Callback.h */ diff --git a/MdeModulePkg/Include/Protocol/Tcp.h b/MdeModulePkg/Include/Protocol/Tcp.h new file mode 100644 index 0000000000..f49915cd93 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/Tcp.h @@ -0,0 +1,108 @@ +/*++ + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + tcp.h + +Abstract: + + EFI Transmission Control Protocol + + + +Revision History + +--*/ + + +#ifndef _EFITCP_H +#define _EFITCP_H + + +#include + +// +// PXE Base Code protocol +// + +#define EFI_TCP_PROTOCOL_GUID \ + { 0x02b3d5f2, 0xac28, 0x11d3, { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }} + + +typedef UINT16 EFI_PXE_BASE_CODE_TCP_PORT; + +// +// Port Receive Filter definitions +// +#define EFI_PXE_BASE_CODE_MAX_PORTCNT 8 +typedef struct { + UINT8 Filters; + UINT8 IpCnt; + UINT16 reserved; + EFI_IP_ADDRESS IpList[EFI_PXE_BASE_CODE_MAX_PORTCNT]; +} EFI_TCP_PORT_FILTER; + +typedef +EFI_STATUS +(EFIAPI *EFI_TCP_WRITE) ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN UINT16 *UrgentPointer, + IN UINT32 *SequenceNumber, + IN UINT32 *AckNumber, + IN UINT16 *HlenResCode, + IN UINT16 *Window, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 *DestPort, + IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL + IN EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN UINT16 *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_TCP_READ) ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT UINT16 *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT UINT16 *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_TCP_SET_PORT_FILTER) ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_TCP_PORT_FILTER *NewFilter + ); + +// +// TCP Protocol structure +// +typedef struct _EFI_TCP_PROTOCOL { + EFI_TCP_WRITE TcpWrite; + EFI_TCP_READ TcpRead; + EFI_TCP_SET_PORT_FILTER SetPortFilter; +} EFI_TCP_PROTOCOL; + +extern EFI_GUID gEfiTcpProtocolGuid; + +#endif /* _EFITCP_H */ diff --git a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c index dcf2309c71..9057a03a67 100644 --- a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c +++ b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c @@ -77,6 +77,7 @@ IP4_ADDR mIp4AllMasks[IP4_MASK_NUM] = { 0xFFFFFFFF, }; +EFI_IPv4_ADDRESS mZeroIp4Addr = {0, 0, 0, 0}; /** Converts the low nibble of a byte to hex unicode character. diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 72f58a1fb6..e6a9347969 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -80,6 +80,10 @@ gEfiNicIp4ConfigProtocolGuid = {0xdca3d4d, 0x12da, 0x4728, { 0xbf, 0x7e, 0x86, 0xce, 0xb9, 0x28, 0xd0, 0x67 }} gEfiNicIp4ConfigVariableGuid = {0xd8944553, 0xc4dd, 0x41f4, { 0x9b, 0x30, 0xe1, 0x39, 0x7c, 0xfb, 0x26, 0x7b }} + gEfiTcpProtocolGuid = { 0x02b3d5f2, 0xac28, 0x11d3, { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }} + gEfiPxeDhcp4CallbackProtocolGuid = { 0xc1544c01, 0x92a4, 0x4198, {0x8a, 0x84, 0x77, 0x85, 0x83, 0xc2, 0x36, 0x21 } } + gEfiPxeDhcp4ProtocolGuid = { 0x03c4e624, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x29, 0x3f, 0xc1, 0x4d } } + [Ppis.common] gPeiBaseMemoryTestPpiGuid = { 0xB6EC423C, 0x21D2, 0x490D, { 0x85, 0xC6, 0xDD, 0x58, 0x64, 0xEA, 0xA6, 0x74 }} diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index be390f443f..90a97c18a6 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -55,7 +55,7 @@ [LibraryClasses.IPF] IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf -[LibraryClasses.EBC.DXE_RUNTIME_DRIVER] +[LibraryClasses.EBC] IoLib|IntelFrameworkPkg/Library/DxeIoLibCpuIo/DxeIoLibCpuIo.inf [LibraryClasses.common.PEI_CORE] @@ -112,6 +112,7 @@ UsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf + UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf [LibraryClasses.common.DXE_RUNTIME_DRIVER] HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf @@ -368,8 +369,13 @@ MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf + MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf + MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDxe.inf + MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf + MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf + MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h new file mode 100644 index 0000000000..05f542d086 --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDebug.h @@ -0,0 +1,30 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ArpDebug.h + +Abstract: + + +**/ + +#ifndef _ARP_DEBUG_H_ +#define _ARP_DEBUG_H_ + + +#define ARP_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE ("Arp", PrintArg) +#define ARP_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Arp", PrintArg) +#define ARP_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Arp", PrintArg) + +#endif + diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c new file mode 100644 index 0000000000..2c108574e8 --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.c @@ -0,0 +1,763 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ArpDriver.c + +Abstract: + + +**/ + + +#include "ArpDriver.h" +#include "ArpImpl.h" + +EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding = { + ArpDriverBindingSupported, + ArpDriverBindingStart, + ArpDriverBindingStop, + 0xa, + NULL, + NULL +}; + + +/** + Create and initialize the arp service context data. + + @param ImageHandle The image handle representing the loaded driver + image. + @param ControllerHandle The controller handle the driver binds to. + @param ArpService Pointer to the buffer containing the arp service + context data. + + @retval EFI_SUCCESS The arp service context is initialized. + @retval other Failed to initialize the arp service context. + +**/ +STATIC +EFI_STATUS +ArpCreateService ( + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle, + IN ARP_SERVICE_DATA *ArpService + ) +{ + EFI_STATUS Status; + + ASSERT (ArpService != NULL); + + ArpService->Signature = ARP_SERVICE_DATA_SIGNATURE; + + // + // Init the servicebinding protocol members. + // + ArpService->ServiceBinding.CreateChild = ArpServiceBindingCreateChild; + ArpService->ServiceBinding.DestroyChild = ArpServiceBindingDestroyChild; + + // + // Save the handles. + // + ArpService->ImageHandle = ImageHandle; + ArpService->ControllerHandle = ControllerHandle; + + // + // Create a MNP child instance. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + ImageHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &ArpService->MnpChildHandle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the MNP protocol. + // + Status = gBS->OpenProtocol ( + ArpService->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **)&ArpService->Mnp, + ImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ERROR_EXIT; + } + + // + // Get the underlayer Snp mode data. + // + Status = ArpService->Mnp->GetModeData (ArpService->Mnp, NULL, &ArpService->SnpMode); + if ((Status != EFI_NOT_STARTED) && EFI_ERROR (Status)) { + goto ERROR_EXIT; + } + + if (ArpService->SnpMode.IfType != NET_IFTYPE_ETHERNET) { + // + // Only support the ethernet. + // + Status = EFI_UNSUPPORTED; + goto ERROR_EXIT; + } + + // + // Set the Mnp config parameters. + // + ArpService->MnpConfigData.ReceivedQueueTimeoutValue = 0; + ArpService->MnpConfigData.TransmitQueueTimeoutValue = 0; + ArpService->MnpConfigData.ProtocolTypeFilter = ARP_ETHER_PROTO_TYPE; + ArpService->MnpConfigData.EnableUnicastReceive = TRUE; + ArpService->MnpConfigData.EnableMulticastReceive = FALSE; + ArpService->MnpConfigData.EnableBroadcastReceive = TRUE; + ArpService->MnpConfigData.EnablePromiscuousReceive = FALSE; + ArpService->MnpConfigData.FlushQueuesOnReset = TRUE; + ArpService->MnpConfigData.EnableReceiveTimestamps = FALSE; + ArpService->MnpConfigData.DisableBackgroundPolling = FALSE; + + // + // Configure the Mnp child. + // + Status = ArpService->Mnp->Configure (ArpService->Mnp, &ArpService->MnpConfigData); + if (EFI_ERROR (Status)) { + goto ERROR_EXIT; + } + + // + // Create the event used in the RxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NET_TPL_EVENT, + ArpOnFrameRcvd, + ArpService, + &ArpService->RxToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR_EXIT; + } + + // + // Create the Arp heartbeat timer. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + NET_TPL_TIMER, + ArpTimerHandler, + ArpService, + &ArpService->PeriodicTimer + ); + if (EFI_ERROR (Status)) { + goto ERROR_EXIT; + } + + // + // Start the heartbeat timer. + // + Status = gBS->SetTimer ( + ArpService->PeriodicTimer, + TimerPeriodic, + ARP_PERIODIC_TIMER_INTERVAL + ); + if (EFI_ERROR (Status)) { + goto ERROR_EXIT; + } + + // + // Init the lock. + // + NET_LOCK_INIT (&ArpService->Lock); + + // + // Init the lists. + // + NetListInit (&ArpService->ChildrenList); + NetListInit (&ArpService->PendingRequestTable); + NetListInit (&ArpService->DeniedCacheTable); + NetListInit (&ArpService->ResolvedCacheTable); + +ERROR_EXIT: + + return Status; +} + + +/** + Clean the arp service context data. + + @param ArpService Pointer to the buffer containing the arp service + context data. + + @return None. + +**/ +STATIC +VOID +ArpCleanService ( + IN ARP_SERVICE_DATA *ArpService + ) +{ + NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE); + + if (ArpService->PeriodicTimer != NULL) { + // + // Cancle and close the PeriodicTimer. + // + gBS->SetTimer (ArpService->PeriodicTimer, TimerCancel, 0); + gBS->CloseEvent (ArpService->PeriodicTimer); + } + + if (ArpService->RxToken.Event != NULL) { + // + // Cancle the RxToken and close the event in the RxToken. + // + ArpService->Mnp->Cancel (ArpService->Mnp, NULL); + gBS->CloseEvent (ArpService->RxToken.Event); + } + + if (ArpService->Mnp != NULL) { + // + // Reset the Mnp child and close the Mnp protocol. + // + ArpService->Mnp->Configure (ArpService->Mnp, NULL); + gBS->CloseProtocol ( + ArpService->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + ArpService->ImageHandle, + ArpService->ControllerHandle + ); + } + + if (ArpService->MnpChildHandle != NULL) { + // + // Destroy the mnp child. + // + NetLibDestroyServiceChild( + ArpService->ControllerHandle, + ArpService->ImageHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + ArpService->MnpChildHandle + ); + } +} + + +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ArpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test to see if Arp SB is already installed. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiArpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + // + // Test to see if MNP SB is installed. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +ArpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + ARP_SERVICE_DATA *ArpService; + + // + // Allocate a zero pool for ArpService. + // + ArpService = NetAllocateZeroPool (sizeof(ARP_SERVICE_DATA)); + if (ArpService == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the arp service context data. + // + Status = ArpCreateService (This->DriverBindingHandle, ControllerHandle, ArpService); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Install the ARP service binding protocol. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiArpServiceBindingProtocolGuid, + &ArpService->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // OK, start to receive arp packets from Mnp. + // + Status = ArpService->Mnp->Receive (ArpService->Mnp, &ArpService->RxToken); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + return Status; + +ERROR: + + // + // On error, clean the arp service context data, and free the memory allocated. + // + ArpCleanService (ArpService); + NetFreePool (ArpService); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ArpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + ARP_SERVICE_DATA *ArpService; + ARP_INSTANCE_DATA *Instance; + + // + // Get the NicHandle which the arp servicebinding is installed on. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Try to get the arp servicebinding protocol on the NicHandle. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiArpServiceBindingProtocolGuid, + (VOID **)&ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + ARP_DEBUG_ERROR (("ArpDriverBindingStop: Open ArpSb failed, %r.\n", Status)); + return Status; + } + + ArpService = ARP_SERVICE_DATA_FROM_THIS (ServiceBinding); + + while (!NetListIsEmpty (&ArpService->ChildrenList)) { + // + // Iterate all the instances. + // + Instance = NET_LIST_HEAD (&ArpService->ChildrenList, ARP_INSTANCE_DATA, List); + + // + // Destroy this arp child. + // + ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); + } + + ASSERT (NetListIsEmpty (&ArpService->PendingRequestTable)); + ASSERT (NetListIsEmpty (&ArpService->DeniedCacheTable)); + ASSERT (NetListIsEmpty (&ArpService->ResolvedCacheTable)); + + // + // Uninstall the ARP ServiceBinding protocol. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiArpServiceBindingProtocolGuid, + &ArpService->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + ARP_DEBUG_ERROR (("ArpDriverBindingStop: Failed to uninstall ArpSb, %r.\n", Status)); + return Status; + } + + // + // Clean the arp servicebinding context data and free the memory allocated. + // + ArpCleanService (ArpService); + NetFreePool (ArpService); + + return Status; +} + + +/** + Creates a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it is + not NULL, then the I/O services are added to the + existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O + services. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +ArpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + EFI_STATUS Status; + ARP_SERVICE_DATA *ArpService; + ARP_INSTANCE_DATA *Instance; + VOID *Mnp; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ArpService = ARP_SERVICE_DATA_FROM_THIS (This); + + // + // Allocate memory for the instance context data. + // + Instance = NetAllocateZeroPool (sizeof(ARP_INSTANCE_DATA)); + if (Instance == NULL) { + ARP_DEBUG_ERROR (("ArpSBCreateChild: Failed to allocate memory for Instance.\n")); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Init the instance context data. + // + ArpInitInstance (ArpService, Instance); + + // + // Install the ARP protocol onto the ChildHandle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiArpProtocolGuid, + (VOID *)&Instance->ArpProto, + NULL + ); + if (EFI_ERROR (Status)) { + ARP_DEBUG_ERROR (("ArpSBCreateChild: faild to install ARP protocol, %r.\n", Status)); + + NetFreePool (Instance); + return Status; + } + + // + // Save the ChildHandle. + // + Instance->Handle = *ChildHandle; + + // + // Open the Managed Network protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + ArpService->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &Mnp, + gArpDriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + + Status = EFI_ACCESS_DENIED; + goto ERROR; + } + + // + // Insert the instance into children list managed by the arp service context data. + // + NetListInsertTail (&ArpService->ChildrenList, &Instance->List); + ArpService->ChildrenNumber++; + + NET_UNLOCK (&ArpService->Lock); + +ERROR: + + if (EFI_ERROR (Status)) { + + gBS->CloseProtocol ( + ArpService->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + gArpDriverBinding.DriverBindingHandle, + Instance->Handle + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiArpProtocolGuid, + &Instance->ArpProto, + NULL + ); + + // + // Free the allocated memory. + // + NetFreePool (Instance); + } + + return Status; +} + + +/** + Destroys a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +ArpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + ARP_SERVICE_DATA *ArpService; + ARP_INSTANCE_DATA *Instance; + EFI_ARP_PROTOCOL *Arp; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ArpService = ARP_SERVICE_DATA_FROM_THIS (This); + + // + // Get the arp protocol. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiArpProtocolGuid, + (VOID **)&Arp, + ArpService->ImageHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (Arp); + + if (Instance->Destroyed) { + return EFI_SUCCESS; + } + + // + // Use the Destroyed as a flag to avoid re-entrance. + // + Instance->Destroyed = TRUE; + + // + // Close the Managed Network protocol. + // + gBS->CloseProtocol ( + ArpService->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + gArpDriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the ARP protocol. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiArpProtocolGuid, + &Instance->ArpProto, + NULL + ); + if (EFI_ERROR (Status)) { + ARP_DEBUG_ERROR (("ArpSBDestroyChild: Failed to uninstall the arp protocol, %r.\n", + Status)); + + Instance->Destroyed = FALSE; + return Status; + } + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + Instance->Destroyed = FALSE; + return EFI_ACCESS_DENIED; + } + + if (Instance->Configured) { + // + // Delete the related cache entry. + // + ArpDeleteCacheEntry (Instance, FALSE, NULL, TRUE); + + // + // Reset the instance configuration. + // + ArpConfigureInstance (Instance, NULL); + } + + // + // Remove this instance from the ChildrenList. + // + NetListRemoveEntry (&Instance->List); + ArpService->ChildrenNumber--; + + NET_UNLOCK (&ArpService->Lock); + + NetFreePool (Instance); + + return Status; +} + +//@MT: EFI_DRIVER_ENTRY_POINT (ArpDriverEntryPoint) + +EFI_STATUS +EFIAPI +ArpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The entry point for Arp driver which installs the driver binding and component name + protocol on its ImageHandle. + +Arguments: + + ImageHandle - The image handle of the driver. + SystemTable - The system table. + +Returns: + + EFI_SUCCESS - if the driver binding and component name protocols are successfully + installed, otherwise if failed. + +--*/ +{ + return NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gArpDriverBinding, + ImageHandle, + &gArpComponentName, + NULL, + NULL + ); +} + diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h new file mode 100644 index 0000000000..93526c777f --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDriver.h @@ -0,0 +1,84 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ArpDriver.c + +Abstract: + + +**/ + +#ifndef _ARP_DRIVER_H_ +#define _ARP_DRIVER_H_ + + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "ArpDebug.h" + +// +// Global variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gArpDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gArpComponentName; + +EFI_STATUS +EFIAPI +ArpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +ArpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +ArpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +EFI_STATUS +EFIAPI +ArpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +EFIAPI +ArpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif + diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf new file mode 100644 index 0000000000..4cc9b2e533 --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf @@ -0,0 +1,59 @@ +#/** @file +# Component description file for ARP module +# +# Copyright (c) 2006, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArpDxe + FILE_GUID = 529D3F93-E8E9-4e73-B1E1-BDF6A9D50113 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = ArpDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + ArpMain.c + ArpDriver.h + ComponentName.c + ArpImpl.h + ArpImpl.c + ArpDebug.h + ArpDriver.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + NetLib + + +[Protocols] + gEfiManagedNetworkServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiArpServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiManagedNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiArpProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa new file mode 100644 index 0000000000..89178fa286 --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.msa @@ -0,0 +1,72 @@ + + + Arp + DXE_DRIVER + 529D3F93-E8E9-4e73-B1E1-BDF6A9D50113 + 1.0 + Component name for module Arp + FIX ME! + Copyright (c) 2007, Intel Corporation. All rights reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + Arp + + + + DebugLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + + ArpDriver.c + ArpDebug.h + ArpImpl.c + ArpImpl.h + ComponentName.c + ArpDriver.h + ArpMain.c + + + + + + + + gEfiArpProtocolGuid + + + gEfiManagedNetworkProtocolGuid + + + gEfiArpServiceBindingProtocolGuid + + + gEfiManagedNetworkServiceBindingProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + ArpDriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c new file mode 100644 index 0000000000..93b4c10200 --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.c @@ -0,0 +1,1628 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ArpImpl.c + +Abstract: + + +**/ + + +#include "ArpImpl.h" +#include "ArpDebug.h" + +EFI_ARP_PROTOCOL mEfiArpProtocolTemplate = { + ArpConfigure, + ArpAdd, + ArpFind, + ArpDelete, + ArpFlush, + ArpRequest, + ArpCancel +}; + + +/** + Initialize the instance context data. + + @param ArpService Pointer to the arp service context data this + instance belongs to. + @param Instance Pointer to the instance context data. + + @return None. + +**/ +VOID +ArpInitInstance ( + IN ARP_SERVICE_DATA *ArpService, + IN ARP_INSTANCE_DATA *Instance + ) +{ + NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE); + + Instance->Signature = ARP_INSTANCE_DATA_SIGNATURE; + Instance->ArpService = ArpService; + + CopyMem (&Instance->ArpProto, &mEfiArpProtocolTemplate, sizeof (ARP_SERVICE_DATA)); + + Instance->Configured = FALSE; + Instance->Destroyed = FALSE; + + NetListInit (&Instance->List); +} + + +/** + Process the Arp packets received from Mnp, the procedure conforms to RFC826. + + @param Event The Event this notify function registered to. + @param Context Pointer to the context data registerd to the + Event. + + @return None. + +**/ +VOID +EFIAPI +ArpOnFrameRcvd ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + ARP_SERVICE_DATA *ArpService; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *RxToken; + EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData; + ARP_HEAD *Head; + ARP_ADDRESS ArpAddress; + ARP_CACHE_ENTRY *CacheEntry; + NET_LIST_ENTRY *Entry; + ARP_INSTANCE_DATA *Instance; + EFI_ARP_CONFIG_DATA *ConfigData; + NET_ARP_ADDRESS SenderAddress[2]; + BOOLEAN ProtoMatched; + BOOLEAN IsTarget; + BOOLEAN MergeFlag; + + ArpService = (ARP_SERVICE_DATA *)Context; + NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE); + + RxToken = &ArpService->RxToken; + + if (RxToken->Status == EFI_ABORTED) { + // + // The Token is aborted, possibly by arp itself, just return and the receiving + // process is stopped. + // + return; + } + + if (EFI_ERROR (RxToken->Status)) { + // + // Restart the receiving if any other error Status occurs. + // + goto RESTART_RECEIVE; + } + + // + // Status is EFI_SUCCESS, process the received frame. + // + RxData = RxToken->Packet.RxData; + Head = (ARP_HEAD *) RxData->PacketData; + + // + // Convert the byte order of the multi-byte fields. + // + Head->HwType = NTOHS (Head->HwType); + Head->ProtoType = NTOHS (Head->ProtoType); + Head->OpCode = NTOHS (Head->OpCode); + + if ((Head->HwType != ArpService->SnpMode.IfType) || + (Head->HwAddrLen != ArpService->SnpMode.HwAddressSize) || + (RxData->ProtocolType != ARP_ETHER_PROTO_TYPE)) { + // + // The hardware type or the hardware address length doesn't match. + // There is a sanity check for the protocol type too. + // + goto RECYCLE_RXDATA; + } + + // + // Set the pointers to the addresses contained in the arp packet. + // + ArpAddress.SenderHwAddr = (UINT8 *)(Head + 1); + ArpAddress.SenderProtoAddr = ArpAddress.SenderHwAddr + Head->HwAddrLen; + ArpAddress.TargetHwAddr = ArpAddress.SenderProtoAddr + Head->ProtoAddrLen; + ArpAddress.TargetProtoAddr = ArpAddress.TargetHwAddr + Head->HwAddrLen; + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + ARP_DEBUG_ERROR (("ArpOnFrameRcvd: Faild to acquire the CacheTableLock.\n")); + goto RECYCLE_RXDATA; + } + + SenderAddress[Hardware].Type = Head->HwType; + SenderAddress[Hardware].Length = Head->HwAddrLen; + SenderAddress[Hardware].AddressPtr = ArpAddress.SenderHwAddr; + + SenderAddress[Protocol].Type = Head->ProtoType; + SenderAddress[Protocol].Length = Head->ProtoAddrLen; + SenderAddress[Protocol].AddressPtr = ArpAddress.SenderProtoAddr; + + // + // First, check the denied cache table. + // + CacheEntry = ArpFindDeniedCacheEntry ( + ArpService, + &SenderAddress[Protocol], + &SenderAddress[Hardware] + ); + if (CacheEntry != NULL) { + // + // This address (either hardware or protocol address, or both) is configured to + // be a deny entry, silently skip the normal process. + // + goto UNLOCK_EXIT; + } + + ProtoMatched = FALSE; + IsTarget = FALSE; + Instance = NULL; + NET_LIST_FOR_EACH (Entry, &ArpService->ChildrenList) { + // + // Iterate all the children. + // + Instance = NET_LIST_USER_STRUCT (Entry, ARP_INSTANCE_DATA, List); + NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE); + ConfigData = &Instance->ConfigData; + + if ((Instance->Configured) && + (Head->ProtoType == ConfigData->SwAddressType) && + (Head->ProtoAddrLen == ConfigData->SwAddressLength)) { + // + // The protocol type is matched for the received arp packet. + // + ProtoMatched = TRUE; + if (0 == NetCompareMem ( + (VOID *)ArpAddress.TargetProtoAddr, + ConfigData->StationAddress, + ConfigData->SwAddressLength + )) { + // + // The arp driver has the target address required by the received arp packet. + // + IsTarget = TRUE; + break; + } + } + } + + if (!ProtoMatched) { + // + // Protocol type unmatchable, skip. + // + goto UNLOCK_EXIT; + } + + // + // Check whether the sender's address information is already in the cache. + // + MergeFlag = FALSE; + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->ResolvedCacheTable, + NULL, + ByProtoAddress, + &SenderAddress[Protocol], + NULL + ); + if (CacheEntry != NULL) { + // + // Update the entry with the new information. + // + ArpFillAddressInCacheEntry (CacheEntry, &SenderAddress[Hardware], NULL); + CacheEntry->DecayTime = CacheEntry->DefaultDecayTime; + MergeFlag = TRUE; + } + + if (!IsTarget) { + // + // This arp packet isn't targeted to us, skip now. + // + goto UNLOCK_EXIT; + } + + if (!MergeFlag) { + // + // Add the triplet + // to the translation table. + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->PendingRequestTable, + NULL, + ByProtoAddress, + &SenderAddress[Protocol], + NULL + ); + if (CacheEntry == NULL) { + // + // Allocate a new CacheEntry. + // + CacheEntry = ArpAllocCacheEntry (NULL); + if (CacheEntry == NULL) { + goto UNLOCK_EXIT; + } + } + + NetListRemoveEntry (&CacheEntry->List); + + // + // Fill the addresses into the CacheEntry. + // + ArpFillAddressInCacheEntry ( + CacheEntry, + &SenderAddress[Hardware], + &SenderAddress[Protocol] + ); + + // + // Inform the user. + // + ArpAddressResolved (CacheEntry, NULL, NULL); + + // + // Add this entry into the ResolvedCacheTable + // + NetListInsertHead (&ArpService->ResolvedCacheTable, &CacheEntry->List); + } + + if (Head->OpCode == ARP_OPCODE_REQUEST) { + // + // Send back the ARP Reply. If we reach here, Instance is not NULL and CacheEntry + // is not NULL. + // + ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REPLY); + } + +UNLOCK_EXIT: + + NET_UNLOCK (&ArpService->Lock); + +RECYCLE_RXDATA: + + // + // Signal Mnp to recycle the RxData. + // + gBS->SignalEvent (RxData->RecycleEvent); + +RESTART_RECEIVE: + + // + // Continue to receive packets from Mnp. + // + Status = ArpService->Mnp->Receive (ArpService->Mnp, RxToken); + + DEBUG_CODE ( + if (EFI_ERROR (Status)) { + ARP_DEBUG_ERROR (("ArpOnFrameRcvd: ArpService->Mnp->Receive " + "failed, %r\n.", Status)); + } + ); +} + + +/** + Process the already sent arp packets. + + @param Event The Event this notify function registered to. + @param Context Pointer to the context data registerd to the + Event. + + @return None. + +**/ +VOID +EFIAPI +ArpOnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData; + + ASSERT (Context != NULL); + + TxToken = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *)Context; + TxData = TxToken->Packet.TxData; + + DEBUG_CODE ( + if (EFI_ERROR (TxToken->Status)) { + ARP_DEBUG_ERROR (("ArpOnFrameSent: TxToken->Status, %r.\n", TxToken->Status)); + } + ); + + // + // Free the allocated memory and close the event. + // + NetFreePool (TxData->FragmentTable[0].FragmentBuffer); + NetFreePool (TxData); + gBS->CloseEvent (TxToken->Event); + NetFreePool (TxToken); +} + + +/** + Process the arp cache olding and drive the retrying arp requests. + + @param Event The Event this notify function registered to. + @param Context Pointer to the context data registerd to the + Event. + + @return None. + +**/ +VOID +EFIAPI +ArpTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ARP_SERVICE_DATA *ArpService; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *NextEntry; + NET_LIST_ENTRY *ContextEntry; + ARP_CACHE_ENTRY *CacheEntry; + USER_REQUEST_CONTEXT *RequestContext; + + ASSERT (Context != NULL); + ArpService = (ARP_SERVICE_DATA *)Context; + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + return; + } + + // + // Iterate all the pending requests to see whether a retry is needed to send out + // or the request finally fails because the retry time reaches the limitation. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) { + CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); + + if (CacheEntry->NextRetryTime <= ARP_PERIODIC_TIMER_INTERVAL) { + // + // Timeout, if we can retry more, send out the request again, otherwise abort + // this request. + // + if (CacheEntry->RetryCount == 0) { + // + // Abort this request. + // + ArpAddressResolved (CacheEntry, NULL, NULL); + ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList)); + + NetListRemoveEntry (&CacheEntry->List); + NetFreePool (CacheEntry); + } else { + // + // resend the ARP request. + // + ASSERT (!NetListIsEmpty(&CacheEntry->UserRequestList)); + + ContextEntry = CacheEntry->UserRequestList.ForwardLink; + RequestContext = NET_LIST_USER_STRUCT (ContextEntry, USER_REQUEST_CONTEXT, List); + + ArpSendFrame (RequestContext->Instance, CacheEntry, ARP_OPCODE_REQUEST); + + CacheEntry->RetryCount--; + CacheEntry->NextRetryTime = RequestContext->Instance->ConfigData.RetryTimeOut; + } + } else { + // + // Update the NextRetryTime. + // + CacheEntry->NextRetryTime -= ARP_PERIODIC_TIMER_INTERVAL; + } + } + + // + // Check the timeouts for the DeniedCacheTable. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->DeniedCacheTable) { + CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); + ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList)); + + if (CacheEntry->DefaultDecayTime == 0) { + // + // It's a static entry, skip it. + // + continue; + } + + if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) { + // + // Time out, remove it. + // + NetListRemoveEntry (&CacheEntry->List); + NetFreePool (CacheEntry); + } else { + // + // Update the DecayTime. + // + CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL; + } + } + + // + // Check the timeouts for the ResolvedCacheTable. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->ResolvedCacheTable) { + CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); + ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList)); + + if (CacheEntry->DefaultDecayTime == 0) { + // + // It's a static entry, skip it. + // + continue; + } + + if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) { + // + // Time out, remove it. + // + NetListRemoveEntry (&CacheEntry->List); + NetFreePool (CacheEntry); + } else { + // + // Update the DecayTime. + // + CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL; + } + } + + NET_UNLOCK (&ArpService->Lock); +} + + +/** + Match the two NET_ARP_ADDRESSes. + + @param AddressOne Pointer to the first address to match. + @param AddressTwo Pointer to the second address to match. + + @return The two addresses match or not. + +**/ +STATIC +BOOLEAN +ArpMatchAddress ( + IN NET_ARP_ADDRESS *AddressOne, + IN NET_ARP_ADDRESS *AddressTwo + ) +{ + if ((AddressOne->Type != AddressTwo->Type) || + (AddressOne->Length != AddressTwo->Length)) { + // + // Either Type or Length doesn't match. + // + return FALSE; + } + + if ((AddressOne->AddressPtr != NULL) && + (NetCompareMem ( + AddressOne->AddressPtr, + AddressTwo->AddressPtr, + AddressOne->Length + ) != 0)) { + // + // The address is not the same. + // + return FALSE; + } + + return TRUE; +} + + +/** + Find the CacheEntry which matches the requirements in the specified CacheTable. + + @param CacheTable Pointer to the arp cache table. + @param StartEntry Pointer to the start entry this search begins with + in the cache table. + @param FindOpType The search type. + @param ProtocolAddress Pointer to the protocol address to match. + @param HardwareAddress Pointer to the hardware address to match. + + @return Pointer to the matched arp cache entry, if NULL, no match is found. + +**/ +ARP_CACHE_ENTRY * +ArpFindNextCacheEntryInTable ( + IN NET_LIST_ENTRY *CacheTable, + IN NET_LIST_ENTRY *StartEntry, + IN FIND_OPTYPE FindOpType, + IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL, + IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL + ) +{ + NET_LIST_ENTRY *Entry; + ARP_CACHE_ENTRY *CacheEntry; + + if (StartEntry == NULL) { + // + // Start from the beginning of the table if no StartEntry is specified. + // + StartEntry = CacheTable; + } + + for (Entry = StartEntry->ForwardLink; Entry != CacheTable; Entry = Entry->ForwardLink) { + CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); + + if (FindOpType & MATCH_SW_ADDRESS) { + // + // Find by the software address. + // + if (!ArpMatchAddress (ProtocolAddress, &CacheEntry->Addresses[Protocol])) { + // + // The ProtocolAddress doesn't match, continue to the next cache entry. + // + continue; + } + } + + if (FindOpType & MATCH_HW_ADDRESS) { + // + // Find by the hardware address. + // + if (!ArpMatchAddress (HardwareAddress, &CacheEntry->Addresses[Hardware])) { + // + // The HardwareAddress doesn't match, continue to the next cache entry. + // + continue; + } + } + + // + // The CacheEntry meets the requirements now, return this entry. + // + return CacheEntry; + } + + // + // No matching. + // + return NULL; +} + + +/** + Find the CacheEntry, using ProtocolAddress or HardwareAddress or both, as the keyword, + in the DeniedCacheTable. + + @param ArpService Pointer to the arp service context data. + @param ProtocolAddress Pointer to the protocol address. + @param HardwareAddress Pointer to the hardware address. + + @return Pointer to the matched cache entry, if NULL no match is found. + +**/ +ARP_CACHE_ENTRY * +ArpFindDeniedCacheEntry ( + IN ARP_SERVICE_DATA *ArpService, + IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL, + IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL + ) +{ + ARP_CACHE_ENTRY *CacheEntry; + + ASSERT ((ProtocolAddress != NULL) || (HardwareAddress != NULL)); + NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE); + + CacheEntry = NULL; + + if ((ProtocolAddress != NULL) && (ProtocolAddress->AddressPtr != NULL)) { + // + // Find the cache entry in the DeniedCacheTable by the protocol address. + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->DeniedCacheTable, + NULL, + ByProtoAddress, + ProtocolAddress, + NULL + ); + if (CacheEntry != NULL) { + // + // There is a match. + // + return CacheEntry; + } + } + + if ((HardwareAddress != NULL) && (HardwareAddress->AddressPtr != NULL)) { + // + // Find the cache entry in the DeniedCacheTable by the hardware address. + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->DeniedCacheTable, + NULL, + ByHwAddress, + NULL, + HardwareAddress + ); + } + + return CacheEntry; +} + + +/** + Allocate a cache entry and initialize it. + + @param Instance Pointer to the instance context data. + + @return Pointer to the new created cache entry. + +**/ +ARP_CACHE_ENTRY * +ArpAllocCacheEntry ( + IN ARP_INSTANCE_DATA *Instance + ) +{ + ARP_CACHE_ENTRY *CacheEntry; + NET_ARP_ADDRESS *Address; + UINT16 Index; + + // + // Allocate memory for the cache entry. + // + CacheEntry = NetAllocatePool (sizeof (ARP_CACHE_ENTRY)); + if (CacheEntry == NULL) { + return NULL; + } + + // + // Init the lists. + // + NetListInit (&CacheEntry->List); + NetListInit (&CacheEntry->UserRequestList); + + for (Index = 0; Index < 2; Index++) { + // + // Init the address pointers to point to the concrete buffer. + // + Address = &CacheEntry->Addresses[Index]; + Address->AddressPtr = Address->Buffer.ProtoAddress; + } + + // + // Zero the hardware address first. + // + NetZeroMem (CacheEntry->Addresses[Hardware].AddressPtr, ARP_MAX_HARDWARE_ADDRESS_LEN); + + if (Instance != NULL) { + // + // Inherit the parameters from the instance configuration. + // + CacheEntry->RetryCount = Instance->ConfigData.RetryCount; + CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut; + CacheEntry->DefaultDecayTime = Instance->ConfigData.EntryTimeOut; + CacheEntry->DecayTime = Instance->ConfigData.EntryTimeOut; + } else { + // + // Use the default parameters if this cache entry isn't allocate in a + // instance's scope. + // + CacheEntry->RetryCount = ARP_DEFAULT_RETRY_COUNT; + CacheEntry->NextRetryTime = ARP_DEFAULT_RETRY_INTERVAL; + CacheEntry->DefaultDecayTime = ARP_DEFAULT_TIMEOUT_VALUE; + CacheEntry->DecayTime = ARP_DEFAULT_TIMEOUT_VALUE; + } + + return CacheEntry; +} + + +/** + Turn the CacheEntry into the resolved status. + + @param CacheEntry Pointer to the resolved cache entry. + @param Instance Pointer to the instance context data. + @param UserEvent Pointer to the UserEvent to notify. + + @return The count of notifications sent to the instance. + +**/ +UINTN +ArpAddressResolved ( + IN ARP_CACHE_ENTRY *CacheEntry, + IN ARP_INSTANCE_DATA *Instance OPTIONAL, + IN EFI_EVENT UserEvent OPTIONAL + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *NextEntry; + USER_REQUEST_CONTEXT *Context; + UINTN Count; + + Count = 0; + + // + // Iterate all the linked user requests to notify them. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &CacheEntry->UserRequestList) { + Context = NET_LIST_USER_STRUCT (Entry, USER_REQUEST_CONTEXT, List); + + if (((Instance == NULL) || (Context->Instance == Instance)) && + ((UserEvent == NULL) || (Context->UserRequestEvent == UserEvent))) { + // + // Copy the address to the user-provided buffer and notify the user. + // + NetCopyMem ( + Context->UserHwAddrBuffer, + CacheEntry->Addresses[Hardware].AddressPtr, + CacheEntry->Addresses[Hardware].Length + ); + gBS->SignalEvent (Context->UserRequestEvent); + + // + // Remove this user request and free the context data. + // + NetListRemoveEntry (&Context->List); + NetFreePool (Context); + + Count++; + } + } + + return Count; +} + + +/** + Fill the addresses in the CacheEntry using the information passed in by + HwAddr and SwAddr. + + @param CacheEntry Pointer to the cache entry. + @param HwAddr Pointer to the software address. + @param SwAddr Pointer to the hardware address. + + @return None. + +**/ +VOID +ArpFillAddressInCacheEntry ( + IN ARP_CACHE_ENTRY *CacheEntry, + IN NET_ARP_ADDRESS *HwAddr OPTIONAL, + IN NET_ARP_ADDRESS *SwAddr OPTIONAL + ) +{ + NET_ARP_ADDRESS *Address[2]; + NET_ARP_ADDRESS *CacheAddress; + UINT32 Index; + + Address[Hardware] = HwAddr; + Address[Protocol] = SwAddr; + + for (Index = 0; Index < 2; Index++) { + if (Address[Index] != NULL) { + // + // Fill the address if the passed in pointer is not NULL. + // + CacheAddress = &CacheEntry->Addresses[Index]; + + CacheAddress->Type = Address[Index]->Type; + CacheAddress->Length = Address[Index]->Length; + + if (Address[Index]->AddressPtr != NULL) { + // + // Copy it if the AddressPtr points to some buffer. + // + NetCopyMem ( + CacheAddress->AddressPtr, + Address[Index]->AddressPtr, + CacheAddress->Length + ); + } else { + // + // Zero the corresponding address buffer in the CacheEntry. + // + NetZeroMem (CacheAddress->AddressPtr, CacheAddress->Length); + } + } + } +} + + +/** + Configure the instance using the ConfigData. ConfigData is already validated. + + @param Instance Pointer to the instance context data to be + configured. + @param ConfigData Pointer to the configuration data used to + configure the instance. + + @retval EFI_SUCCESS The instance is configured with the ConfigData. + @retval EFI_ACCESS_DENIED The instance is already configured and the + ConfigData tries to reset some unchangeable + fields. + @retval EFI_INVALID_PARAMETER The ConfigData provides a non-unicast IPv4 address + when the SwAddressType is IPv4. + @retval EFI_OUT_OF_RESOURCES The instance fails to configure due to memory + limitation. + +**/ +EFI_STATUS +ArpConfigureInstance ( + IN ARP_INSTANCE_DATA *Instance, + IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL + ) +{ + EFI_ARP_CONFIG_DATA *OldConfigData; + IP4_ADDR Ip; + + OldConfigData = &Instance->ConfigData; + + if (ConfigData != NULL) { + + if (Instance->Configured) { + // + // The instance is configured, check the unchangeable fields. + // + if ((OldConfigData->SwAddressType != ConfigData->SwAddressType) || + (OldConfigData->SwAddressLength != ConfigData->SwAddressLength) || + (NetCompareMem ( + OldConfigData->StationAddress, + ConfigData->StationAddress, + OldConfigData->SwAddressLength + ) != 0)) { + // + // Deny the unallowed changes. + // + return EFI_ACCESS_DENIED; + } + } else { + // + // The instance is not configured. + // + + if (ConfigData->SwAddressType == IPv4_ETHER_PROTO_TYPE) { + NetCopyMem (&Ip, ConfigData->StationAddress, sizeof (IP4_ADDR)); + + if (!Ip4IsUnicast (NTOHL (Ip), 0)) { + // + // The station address is not a valid IPv4 unicast address. + // + return EFI_INVALID_PARAMETER; + } + } + + // + // Save the configuration. + // + CopyMem (OldConfigData, ConfigData, sizeof (EFI_ARP_CONFIG_DATA)); + + OldConfigData->StationAddress = NetAllocatePool (OldConfigData->SwAddressLength); + if (OldConfigData->StationAddress == NULL) { + ARP_DEBUG_ERROR (("ArpConfigInstance: NetAllocatePool for the StationAddress " + "failed.\n")); + return EFI_OUT_OF_RESOURCES; + } + + // + // Save the StationAddress. + // + NetCopyMem ( + OldConfigData->StationAddress, + ConfigData->StationAddress, + OldConfigData->SwAddressLength + ); + + // + // Set the state to configured. + // + Instance->Configured = TRUE; + } + + // + // Use the implementation specific values if the following field is zero. + // + OldConfigData->EntryTimeOut = (ConfigData->EntryTimeOut == 0) ? + ARP_DEFAULT_TIMEOUT_VALUE : ConfigData->EntryTimeOut; + + OldConfigData->RetryCount = (ConfigData->RetryCount == 0) ? + ARP_DEFAULT_RETRY_COUNT : ConfigData->RetryCount; + + OldConfigData->RetryTimeOut = (ConfigData->RetryTimeOut == 0) ? + ARP_DEFAULT_RETRY_INTERVAL : ConfigData->RetryTimeOut; + } else { + // + // Reset the configuration. + // + + if (Instance->Configured) { + // + // Cancel the arp requests issued by this instance. + // + ArpCancelRequest (Instance, NULL, NULL); + + // + // Free the buffer previously allocated to hold the station address. + // + NetFreePool (OldConfigData->StationAddress); + } + + Instance->Configured = FALSE; + } + + return EFI_SUCCESS; +} + + +/** + Send out an arp frame using the CachEntry and the ArpOpCode. + + @param Instance Pointer to the instance context data. + @param CacheEntry Pointer to the configuration data used to + configure the instance. + @param ArpOpCode The opcode used to send out this Arp frame, either + request or reply. + + @return None. + +**/ +VOID +ArpSendFrame ( + IN ARP_INSTANCE_DATA *Instance, + IN ARP_CACHE_ENTRY *CacheEntry, + IN UINT16 ArpOpCode + ) +{ + EFI_STATUS Status; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData; + UINT32 TotalLength; + UINT8 *Packet; + ARP_SERVICE_DATA *ArpService; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_ARP_CONFIG_DATA *ConfigData; + UINT8 *TmpPtr; + ARP_HEAD *ArpHead; + + ASSERT ((Instance != NULL) && (CacheEntry != NULL)); + + // + // Allocate memory for the TxToken. + // + TxToken = NetAllocatePool (sizeof(EFI_MANAGED_NETWORK_COMPLETION_TOKEN)); + if (TxToken == NULL) { + ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for TxToken failed.\n")); + return; + } + + TxToken->Event = NULL; + TxData = NULL; + Packet = NULL; + + // + // Create the event for this TxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NET_TPL_EVENT, + ArpOnFrameSent, + (VOID *)TxToken, + &TxToken->Event + ); + if (EFI_ERROR (Status)) { + ARP_DEBUG_ERROR (("ArpSendFrame: CreateEvent failed for TxToken->Event.\n")); + goto CLEAN_EXIT; + } + + // + // Allocate memory for the TxData used in the TxToken. + // + TxData = NetAllocatePool (sizeof(EFI_MANAGED_NETWORK_TRANSMIT_DATA)); + if (TxData == NULL) { + ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for TxData failed.\n")); + goto CLEAN_EXIT; + } + + ArpService = Instance->ArpService; + SnpMode = &ArpService->SnpMode; + ConfigData = &Instance->ConfigData; + + // + // Calculate the buffer length for this arp frame. + // + TotalLength = SnpMode->MediaHeaderSize + sizeof (ARP_HEAD) + + 2 * (ConfigData->SwAddressLength + SnpMode->HwAddressSize); + + // + // Allocate buffer for the arp frame. + // + Packet = NetAllocatePool (TotalLength); + if (Packet == NULL) { + ARP_DEBUG_ERROR (("ArpSendFrame: Allocate memory for Packet failed.\n")); + } + + TmpPtr = Packet; + + // + // The destination MAC address. + // + if (ArpOpCode == ARP_OPCODE_REQUEST) { + NetCopyMem (TmpPtr, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize); + } else { + NetCopyMem ( + TmpPtr, + CacheEntry->Addresses[Hardware].AddressPtr, + SnpMode->HwAddressSize + ); + } + TmpPtr += SnpMode->HwAddressSize; + + // + // The source MAC address. + // + NetCopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize); + TmpPtr += SnpMode->HwAddressSize; + + // + // The ethernet protocol type. + // + *(UINT16 *)TmpPtr = HTONS (ARP_ETHER_PROTO_TYPE); + TmpPtr += 2; + + // + // The ARP Head. + // + ArpHead = (ARP_HEAD *) TmpPtr; + ArpHead->HwType = HTONS ((UINT16)SnpMode->IfType); + ArpHead->ProtoType = HTONS (ConfigData->SwAddressType); + ArpHead->HwAddrLen = (UINT8)SnpMode->HwAddressSize; + ArpHead->ProtoAddrLen = ConfigData->SwAddressLength; + ArpHead->OpCode = HTONS (ArpOpCode); + TmpPtr += sizeof (ARP_HEAD); + + // + // The sender hardware address. + // + NetCopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize); + TmpPtr += SnpMode->HwAddressSize; + + // + // The sender protocol address. + // + NetCopyMem (TmpPtr, ConfigData->StationAddress, ConfigData->SwAddressLength); + TmpPtr += ConfigData->SwAddressLength; + + // + // The target hardware address. + // + NetCopyMem ( + TmpPtr, + CacheEntry->Addresses[Hardware].AddressPtr, + SnpMode->HwAddressSize + ); + TmpPtr += SnpMode->HwAddressSize; + + // + // The target protocol address. + // + NetCopyMem ( + TmpPtr, + CacheEntry->Addresses[Protocol].AddressPtr, + ConfigData->SwAddressLength + ); + + // + // Set all the fields of the TxData. + // + TxData->DestinationAddress = NULL; + TxData->SourceAddress = NULL; + TxData->ProtocolType = 0; + TxData->DataLength = TotalLength - SnpMode->MediaHeaderSize; + TxData->HeaderLength = (UINT16) SnpMode->MediaHeaderSize; + TxData->FragmentCount = 1; + + TxData->FragmentTable[0].FragmentBuffer = Packet; + TxData->FragmentTable[0].FragmentLength = TotalLength; + + // + // Associate the TxData with the TxToken. + // + TxToken->Packet.TxData = TxData; + TxToken->Status = EFI_NOT_READY; + + // + // Send out this arp packet by Mnp. + // + Status = ArpService->Mnp->Transmit (ArpService->Mnp, TxToken); + if (EFI_ERROR (Status)) { + ARP_DEBUG_ERROR (("Mnp->Transmit failed, %r.\n", Status)); + goto CLEAN_EXIT; + } + + return; + +CLEAN_EXIT: + + if (Packet != NULL) { + NetFreePool (Packet); + } + + if (TxData != NULL) { + NetFreePool (TxData); + } + + if (TxToken->Event != NULL) { + gBS->CloseEvent (TxToken->Event); + } + + NetFreePool (TxToken); +} + + +/** + Delete the cache entries in the specified CacheTable, using the BySwAddress, + SwAddressType, AddressBuffer combination as the matching key, if Force is TRUE, + the cache is deleted event it's a static entry. + + @param CacheTable Pointer to the cache table to do the deletion. + @param BySwAddress Delete the cache entry by software address or by + hardware address. + @param SwAddressType The software address used to do the deletion. + @param AddressBuffer Pointer to the buffer containing the address to + match for the deletion. + @param Force This deletion is forced or not. + + @return The count of the deleted cache entries. + +**/ +STATIC +UINTN +ArpDeleteCacheEntryInTable ( + IN NET_LIST_ENTRY *CacheTable, + IN BOOLEAN BySwAddress, + IN UINT16 SwAddressType, + IN UINT8 *AddressBuffer OPTIONAL, + IN BOOLEAN Force + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *NextEntry; + ARP_CACHE_ENTRY *CacheEntry; + UINTN Count; + + Count = 0; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, CacheTable) { + CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); + + if ((CacheEntry->DefaultDecayTime == 0) && !Force) { + // + // It's a static entry and we are not forced to delete it, skip. + // + continue; + } + + if (BySwAddress) { + if (SwAddressType == CacheEntry->Addresses[Protocol].Type) { + // + // Protocol address type matched. Check the address. + // + if ((AddressBuffer == NULL) || + (NetCompareMem ( + AddressBuffer, + CacheEntry->Addresses[Protocol].AddressPtr, + CacheEntry->Addresses[Protocol].Length + ) == 0)) { + // + // Address matched. + // + goto MATCHED; + } + } + } else { + if ((AddressBuffer == NULL) || + (NetCompareMem ( + AddressBuffer, + CacheEntry->Addresses[Hardware].AddressPtr, + CacheEntry->Addresses[Hardware].Length + ) == 0)) { + // + // Address matched. + // + goto MATCHED; + } + } + + continue; + +MATCHED: + + // + // Delete this entry. + // + NetListRemoveEntry (&CacheEntry->List); + ASSERT (NetListIsEmpty (&CacheEntry->UserRequestList)); + NetFreePool (CacheEntry); + + Count++; + } + + return Count; +} + + +/** + Delete cache entries in all the cache tables. + + @param Instance Pointer to the instance context data. + @param BySwAddress Delete the cache entry by software address or by + hardware address. + @param AddressBuffer Pointer to the buffer containing the address to + match for the deletion. + @param Force This deletion is forced or not. + + @return The count of the deleted cache entries. + +**/ +UINTN +ArpDeleteCacheEntry ( + IN ARP_INSTANCE_DATA *Instance, + IN BOOLEAN BySwAddress, + IN UINT8 *AddressBuffer OPTIONAL, + IN BOOLEAN Force + ) +{ + ARP_SERVICE_DATA *ArpService; + UINTN Count; + + NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE); + + ArpService = Instance->ArpService; + + // + // Delete the cache entries in the DeniedCacheTable. + // + Count = ArpDeleteCacheEntryInTable ( + &ArpService->DeniedCacheTable, + BySwAddress, + Instance->ConfigData.SwAddressType, + AddressBuffer, + Force + ); + + // + // Delete the cache entries inthe ResolvedCacheTable. + // + Count += ArpDeleteCacheEntryInTable ( + &ArpService->ResolvedCacheTable, + BySwAddress, + Instance->ConfigData.SwAddressType, + AddressBuffer, + Force + ); + + return Count; +} + + +/** + Cancel the arp request. + + @param Instance Pointer to the instance context data. + @param TargetSwAddress Pointer to the buffer containing the target + software address to match the arp request. + @param UserEvent The user event used to notify this request + cancellation. + + @return The count of the cancelled requests. + +**/ +UINTN +ArpCancelRequest ( + IN ARP_INSTANCE_DATA *Instance, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT UserEvent OPTIONAL + ) +{ + ARP_SERVICE_DATA *ArpService; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *NextEntry; + ARP_CACHE_ENTRY *CacheEntry; + UINTN Count; + + NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE); + + ArpService = Instance->ArpService; + + Count = 0; + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) { + CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); + + if ((TargetSwAddress == NULL) || + (NetCompareMem ( + TargetSwAddress, + CacheEntry->Addresses[Protocol].AddressPtr, + CacheEntry->Addresses[Protocol].Length + ) == 0)) { + // + // This request entry matches the TargetSwAddress or all requests are to be + // cancelled as TargetSwAddress is NULL. + // + Count += ArpAddressResolved (CacheEntry, Instance, UserEvent); + + if (NetListIsEmpty (&CacheEntry->UserRequestList)) { + // + // No user requests any more, remove this request cache entry. + // + NetListRemoveEntry (&CacheEntry->List); + NetFreePool (CacheEntry); + } + } + } + + return Count; +} + + +/** + Find the cache entry in the cache table. + + @param Instance Pointer to the instance context data. + @param BySwAddress Set to TRUE to look for matching software protocol + addresses. Set to FALSE to look for matching + hardware protocol addresses. + @param AddressBuffer Pointer to address buffer. Set to NULL to match + all addresses. + @param EntryLength The size of an entry in the entries buffer. + @param EntryCount The number of ARP cache entries that are found by + the specified criteria. + @param Entries Pointer to the buffer that will receive the ARP + cache entries. + @param Refresh Set to TRUE to refresh the timeout value of the + matching ARP cache entry. + + @retval EFI_SUCCESS The requested ARP cache entries are copied into + the buffer. + @retval EFI_NOT_FOUND No matching entries found. + @retval EFI_OUT_OF_RESOURCE There is a memory allocation failure. + +**/ +EFI_STATUS +ArpFindCacheEntry ( + IN ARP_INSTANCE_DATA *Instance, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL, + OUT UINT32 *EntryLength OPTIONAL, + OUT UINT32 *EntryCount OPTIONAL, + OUT EFI_ARP_FIND_DATA **Entries OPTIONAL, + IN BOOLEAN Refresh + ) +{ + EFI_STATUS Status; + ARP_SERVICE_DATA *ArpService; + NET_ARP_ADDRESS MatchAddress; + FIND_OPTYPE FindOpType; + NET_LIST_ENTRY *StartEntry; + ARP_CACHE_ENTRY *CacheEntry; + NET_MAP FoundEntries; + UINT32 FoundCount; + EFI_ARP_FIND_DATA *FindData; + NET_LIST_ENTRY *CacheTable; + + ArpService = Instance->ArpService; + + // + // Init the FounEntries used to hold the found cache entries. + // + NetMapInit (&FoundEntries); + + // + // Set the MatchAddress. + // + if (BySwAddress) { + MatchAddress.Type = Instance->ConfigData.SwAddressType; + MatchAddress.Length = Instance->ConfigData.SwAddressLength; + FindOpType = ByProtoAddress; + } else { + MatchAddress.Type = ArpService->SnpMode.IfType; + MatchAddress.Length = (UINT8)ArpService->SnpMode.HwAddressSize; + FindOpType = ByHwAddress; + } + + MatchAddress.AddressPtr = AddressBuffer; + + // + // Search the DeniedCacheTable + // + StartEntry = NULL; + while (TRUE) { + // + // Try to find the matched entries in the DeniedCacheTable. + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->DeniedCacheTable, + StartEntry, + FindOpType, + &MatchAddress, + &MatchAddress + ); + if (CacheEntry == NULL) { + // + // Once the CacheEntry is NULL, there are no more matches. + // + break; + } + + // + // Insert the found entry into the map. + // + NetMapInsertTail ( + &FoundEntries, + (VOID *)CacheEntry, + (VOID *)&ArpService->DeniedCacheTable + ); + + // + // Let the next search start from this cache entry. + // + StartEntry = &CacheEntry->List; + + if (Refresh) { + // + // Refresh the DecayTime if needed. + // + CacheEntry->DecayTime = CacheEntry->DefaultDecayTime; + } + } + + // + // Search the ResolvedCacheTable + // + StartEntry = NULL; + while (TRUE) { + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->ResolvedCacheTable, + StartEntry, + FindOpType, + &MatchAddress, + &MatchAddress + ); + if (CacheEntry == NULL) { + // + // Once the CacheEntry is NULL, there are no more matches. + // + break; + } + + // + // Insert the found entry into the map. + // + NetMapInsertTail ( + &FoundEntries, + (VOID *)CacheEntry, + (VOID *)&ArpService->ResolvedCacheTable + ); + + // + // Let the next search start from this cache entry. + // + StartEntry = &CacheEntry->List; + + if (Refresh) { + // + // Refresh the DecayTime if needed. + // + CacheEntry->DecayTime = CacheEntry->DefaultDecayTime; + } + } + + Status = EFI_SUCCESS; + + FoundCount = (UINT32) NetMapGetCount (&FoundEntries); + if (FoundCount == 0) { + Status = EFI_NOT_FOUND; + goto CLEAN_EXIT; + } + + if (EntryLength != NULL) { + // + // Return the entry length. + // + *EntryLength = sizeof (EFI_ARP_FIND_DATA) + Instance->ConfigData.SwAddressLength + + ArpService->SnpMode.HwAddressSize; + } + + if (EntryCount != NULL) { + // + // Return the found entry count. + // + *EntryCount = FoundCount; + } + + if (Entries == NULL) { + goto CLEAN_EXIT; + } + + // + // Allocate buffer to copy the found entries. + // + FindData = NetAllocatePool (FoundCount * (*EntryLength)); + if (FindData == NULL) { + ARP_DEBUG_ERROR (("ArpFindCacheEntry: Failed to allocate memory.\n")); + Status = EFI_OUT_OF_RESOURCES; + goto CLEAN_EXIT; + } + + // + // Return the address to the user. + // + *Entries = FindData; + + // + // Dump the entries. + // + while (!NetMapIsEmpty (&FoundEntries)) { + // + // Get a cache entry from the map. + // + CacheEntry = NetMapRemoveHead (&FoundEntries, (VOID **)&CacheTable); + + // + // Set the fields in FindData. + // + FindData->Size = *EntryLength; + FindData->DenyFlag = (BOOLEAN)(CacheTable == &ArpService->DeniedCacheTable); + FindData->StaticFlag = (BOOLEAN)(CacheEntry->DefaultDecayTime == 0); + FindData->HwAddressType = ArpService->SnpMode.IfType; + FindData->SwAddressType = Instance->ConfigData.SwAddressType; + FindData->HwAddressLength = (UINT8)ArpService->SnpMode.HwAddressSize; + FindData->SwAddressLength = Instance->ConfigData.SwAddressLength; + + // + // Copy the software address. + // + NetCopyMem ( + FindData + 1, + CacheEntry->Addresses[Protocol].AddressPtr, + FindData->SwAddressLength + ); + + // + // Copy the hardware address. + // + NetCopyMem ( + (UINT8 *)(FindData + 1) + FindData->SwAddressLength, + CacheEntry->Addresses[Hardware].AddressPtr, + FindData->HwAddressLength + ); + + // + // Slip to the next FindData. + // + FindData = (EFI_ARP_FIND_DATA *)((UINT8 *)FindData + *EntryLength); + } + +CLEAN_EXIT: + + NetMapClean (&FoundEntries); + + return Status; +} + diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h new file mode 100644 index 0000000000..607443a99e --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpImpl.h @@ -0,0 +1,341 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ArpImpl.h + +Abstract: + + +**/ + +#ifndef _ARP_IMPL_H_ +#define _ARP_IMPL_H_ + + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ArpDebug.h" + +#define ARP_ETHER_PROTO_TYPE 0x0806 +#define IPv4_ETHER_PROTO_TYPE 0x0800 +#define IPv6_ETHER_PROTO_TYPE 0x86DD + +#define ARP_OPCODE_REQUEST 0x0001 +#define ARP_OPCODE_REPLY 0x0002 + +#define ARP_DEFAULT_TIMEOUT_VALUE (400 * TICKS_PER_SECOND) +#define ARP_DEFAULT_RETRY_COUNT 2 +#define ARP_DEFAULT_RETRY_INTERVAL (5 * TICKS_PER_MS) +#define ARP_PERIODIC_TIMER_INTERVAL (500 * TICKS_PER_MS) + +#pragma pack(1) +typedef struct _ARP_HEAD { + UINT16 HwType; + UINT16 ProtoType; + UINT8 HwAddrLen; + UINT8 ProtoAddrLen; + UINT16 OpCode; +} ARP_HEAD; +#pragma pack() + +typedef struct _ARP_ADDRESS { + UINT8 *SenderHwAddr; + UINT8 *SenderProtoAddr; + UINT8 *TargetHwAddr; + UINT8 *TargetProtoAddr; +} ARP_ADDRESS; + +#define MATCH_SW_ADDRESS 0x1 +#define MATCH_HW_ADDRESS 0x2 + +typedef enum { + ByNone = 0, + ByProtoAddress = MATCH_SW_ADDRESS, + ByHwAddress = MATCH_HW_ADDRESS, + ByBoth = MATCH_SW_ADDRESS | MATCH_HW_ADDRESS +} FIND_OPTYPE; + +#define ARP_INSTANCE_DATA_SIGNATURE EFI_SIGNATURE_32('A', 'R', 'P', 'I') + +#define ARP_INSTANCE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + ARP_INSTANCE_DATA, \ + ArpProto, \ + ARP_INSTANCE_DATA_SIGNATURE \ + ) + +typedef struct _ARP_SERVICE_DATA ARP_SERVICE_DATA; + +typedef struct _ARP_INSTANCE_DATA { + UINT32 Signature; + ARP_SERVICE_DATA *ArpService; + EFI_HANDLE Handle; + EFI_ARP_PROTOCOL ArpProto; + NET_LIST_ENTRY List; + EFI_ARP_CONFIG_DATA ConfigData; + BOOLEAN Configured; + BOOLEAN Destroyed; +} ARP_INSTANCE_DATA; + +#define ARP_SERVICE_DATA_SIGNATURE EFI_SIGNATURE_32('A', 'R', 'P', 'S') + +#define ARP_SERVICE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + ARP_SERVICE_DATA, \ + ServiceBinding, \ + ARP_SERVICE_DATA_SIGNATURE \ + ) + +struct _ARP_SERVICE_DATA { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + + EFI_HANDLE MnpChildHandle; + EFI_HANDLE ImageHandle; + EFI_HANDLE ControllerHandle; + + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN RxToken; + + EFI_SIMPLE_NETWORK_MODE SnpMode; + + NET_LOCK Lock; + + UINTN ChildrenNumber; + NET_LIST_ENTRY ChildrenList; + + NET_LIST_ENTRY PendingRequestTable; + NET_LIST_ENTRY DeniedCacheTable; + NET_LIST_ENTRY ResolvedCacheTable; + + EFI_EVENT PeriodicTimer; +}; + +typedef struct _USER_REQUEST_CONTEXT { + NET_LIST_ENTRY List; + ARP_INSTANCE_DATA *Instance; + EFI_EVENT UserRequestEvent; + VOID *UserHwAddrBuffer; +} USER_REQUEST_CONTEXT; + +#define ARP_MAX_PROTOCOL_ADDRESS_LEN sizeof(EFI_IP_ADDRESS) +#define ARP_MAX_HARDWARE_ADDRESS_LEN sizeof(EFI_MAC_ADDRESS) + +typedef struct _NET_ARP_ADDRESS { + UINT16 Type; + UINT8 Length; + UINT8 *AddressPtr; + union { + UINT8 ProtoAddress[ARP_MAX_PROTOCOL_ADDRESS_LEN]; + UINT8 HwAddress[ARP_MAX_HARDWARE_ADDRESS_LEN]; + } Buffer; +} NET_ARP_ADDRESS; + +typedef enum { + Hardware, + Protocol +} ARP_ADDRESS_TYPE; + +typedef struct _ARP_CACHE_ENTRY { + NET_LIST_ENTRY List; + + UINT32 RetryCount; + UINT32 DefaultDecayTime; + UINT32 DecayTime; + UINT32 NextRetryTime; + + NET_ARP_ADDRESS Addresses[2]; + + NET_LIST_ENTRY UserRequestList; +} ARP_CACHE_ENTRY; + +EFI_STATUS +EFIAPI +ArpConfigure ( + IN EFI_ARP_PROTOCOL *This, + IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL + ); + +EFI_STATUS +EFIAPI +ArpAdd ( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN DenyFlag, + IN VOID *TargetSwAddress OPTIONAL, + IN VOID *TargetHwAddress OPTIONAL, + IN UINT32 TimeoutValue, + IN BOOLEAN Overwrite + ); + +EFI_STATUS +EFIAPI +ArpFind ( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL, + OUT UINT32 *EntryLength OPTIONAL, + OUT UINT32 *EntryCount OPTIONAL, + OUT EFI_ARP_FIND_DATA **Entries OPTIONAL, + IN BOOLEAN Refresh + ); + +EFI_STATUS +EFIAPI +ArpDelete ( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL + ); + +EFI_STATUS +EFIAPI +ArpFlush ( + IN EFI_ARP_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +ArpRequest ( + IN EFI_ARP_PROTOCOL *This, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT ResolvedEvent OPTIONAL, + OUT VOID *TargetHwAddress + ); + +EFI_STATUS +EFIAPI +ArpCancel ( + IN EFI_ARP_PROTOCOL *This, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT ResolvedEvent OPTIONAL + ); + +EFI_STATUS +ArpConfigureInstance ( + IN ARP_INSTANCE_DATA *Instance, + IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL + ); + +ARP_CACHE_ENTRY * +ArpFindDeniedCacheEntry ( + IN ARP_SERVICE_DATA *ArpService, + IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL, + IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL + ); + +ARP_CACHE_ENTRY * +ArpFindNextCacheEntryInTable ( + IN NET_LIST_ENTRY *CacheTable, + IN NET_LIST_ENTRY *StartEntry, + IN FIND_OPTYPE FindOpType, + IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL, + IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL + ); + +ARP_CACHE_ENTRY * +ArpAllocCacheEntry ( + IN ARP_INSTANCE_DATA *Instance + ); + +VOID +ArpFillAddressInCacheEntry ( + IN ARP_CACHE_ENTRY *CacheEntry, + IN NET_ARP_ADDRESS *HwAddr OPTIONAL, + IN NET_ARP_ADDRESS *SwAddr OPTIONAL + ); + +UINTN +ArpAddressResolved ( + IN ARP_CACHE_ENTRY *CacheEntry, + IN ARP_INSTANCE_DATA *Instance OPTIONAL, + IN EFI_EVENT UserEvent OPTIONAL + ); + +UINTN +ArpDeleteCacheEntry ( + IN ARP_INSTANCE_DATA *Instance, + IN BOOLEAN BySwAddress, + IN UINT8 *AddressBuffer OPTIONAL, + IN BOOLEAN Force + ); + +VOID +ArpSendFrame ( + IN ARP_INSTANCE_DATA *Instance, + IN ARP_CACHE_ENTRY *CacheEntry, + IN UINT16 ArpOpCode + ); + +VOID +ArpInitInstance ( + IN ARP_SERVICE_DATA *ArpService, + IN ARP_INSTANCE_DATA *Instance + ); + +VOID +EFIAPI +ArpOnFrameRcvd ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +EFIAPI +ArpOnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +EFIAPI +ArpTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +UINTN +ArpCancelRequest ( + IN ARP_INSTANCE_DATA *Instance, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT UserEvent OPTIONAL + ); + +EFI_STATUS +ArpFindCacheEntry ( + IN ARP_INSTANCE_DATA *Instance, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL, + OUT UINT32 *EntryLength OPTIONAL, + OUT UINT32 *EntryCount OPTIONAL, + OUT EFI_ARP_FIND_DATA **Entries OPTIONAL, + IN BOOLEAN Refresh + ); + +#endif + diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c b/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c new file mode 100644 index 0000000000..eb1b082a21 --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ArpMain.c @@ -0,0 +1,727 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ArpMain.c + +Abstract: + + +**/ + +#include "ArpImpl.h" + + +/** + This function is used to assign a station address to the ARP cache for this instance + of the ARP driver. A call to this function with the ConfigData field set to NULL + will reset this ARP instance. + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + @param ConfigData Pointer to the EFI_ARP_CONFIG_DATA structure. + + @retval EFI_SUCCESS The new station address was successfully + registered. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. SwAddressLength is zero when + ConfigData is not NULL. StationAddress is NULL + when ConfigData is not NULL. + @retval EFI_ACCESS_DENIED The SwAddressType, SwAddressLength, or + StationAddress is different from the one that is + already registered. + @retval EFI_OUT_OF_RESOURCES Storage for the new StationAddress could not be + allocated. + +**/ +EFI_STATUS +EFIAPI +ArpConfigure ( + IN EFI_ARP_PROTOCOL *This, + IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL + ) +{ + EFI_STATUS Status; + ARP_INSTANCE_DATA *Instance; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((ConfigData != NULL) && + ((ConfigData->SwAddressLength == 0) || + (ConfigData->StationAddress == NULL) || + (ConfigData->SwAddressType <= 1500))) { + return EFI_INVALID_PARAMETER; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (This); + + if (EFI_ERROR (NET_TRYLOCK (&Instance->ArpService->Lock))) { + return EFI_ACCESS_DENIED; + } + + // + // Configure this instance, the ConfigData has already passed the basic checks. + // + Status = ArpConfigureInstance (Instance, ConfigData); + + NET_UNLOCK (&Instance->ArpService->Lock); + + return Status; +} + + +/** + This function is used to insert entries into the ARP cache. + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + @param DenyFlag Set to TRUE if this entry is a deny entry. Set to + FALSE if this entry is a normal entry. + @param TargetSwAddress Pointer to a protocol address to add (or deny). + May be set to NULL if DenyFlag is TRUE. + @param TargetHwAddress Pointer to a hardware address to add (or deny). + May be set to NULL if DenyFlag is TRUE. + @param TimeoutValue Time in 100-ns units that this entry will remain + in the ARP cache. A value of zero means that the + entry is permanent. A nonzero value will override + the one given by Configure() if the entry to be + added is a dynamic entry. + @param Overwrite If TRUE, the matching cache entry will be + overwritten with the supplied parameters. If + FALSE, EFI_ACCESS_DENIED is returned if the + corresponding cache entry already exists. + + @retval EFI_SUCCESS The entry has been added or updated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. DenyFlag is FALSE and + TargetHwAddress is NULL. DenyFlag is FALSE and + TargetSwAddress is NULL. TargetHwAddress is NULL + and TargetSwAddress is NULL. Both TargetSwAddress + and TargetHwAddress are not NULL when DenyFlag is + TRUE. + @retval EFI_OUT_OF_RESOURCES The new ARP cache entry could not be allocated. + @retval EFI_ACCESS_DENIED The ARP cache entry already exists and Overwrite + is not true. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +EFI_STATUS +EFIAPI +ArpAdd ( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN DenyFlag, + IN VOID *TargetSwAddress OPTIONAL, + IN VOID *TargetHwAddress OPTIONAL, + IN UINT32 TimeoutValue, + IN BOOLEAN Overwrite + ) +{ + EFI_STATUS Status; + ARP_INSTANCE_DATA *Instance; + ARP_SERVICE_DATA *ArpService; + ARP_CACHE_ENTRY *CacheEntry; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + NET_ARP_ADDRESS MatchAddress[2]; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (((!DenyFlag) && ((TargetHwAddress == NULL) || (TargetSwAddress == NULL))) || + (DenyFlag && (TargetHwAddress != NULL) && (TargetSwAddress != NULL)) || + ((TargetHwAddress == NULL) && (TargetSwAddress == NULL))) { + return EFI_INVALID_PARAMETER; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + Status = EFI_SUCCESS; + ArpService = Instance->ArpService; + SnpMode = &Instance->ArpService->SnpMode; + + // + // Fill the hardware address part in the MatchAddress. + // + MatchAddress[Hardware].Type = SnpMode->IfType; + MatchAddress[Hardware].Length = (UINT8) SnpMode->HwAddressSize; + MatchAddress[Hardware].AddressPtr = TargetHwAddress; + + // + // Fill the software address part in the MatchAddress. + // + MatchAddress[Protocol].Type = Instance->ConfigData.SwAddressType; + MatchAddress[Protocol].Length = Instance->ConfigData.SwAddressLength; + MatchAddress[Protocol].AddressPtr = TargetSwAddress; + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + return EFI_ACCESS_DENIED; + } + + // + // See whether the entry to add exists. Check the DeinedCacheTable first. + // + CacheEntry = ArpFindDeniedCacheEntry ( + ArpService, + &MatchAddress[Protocol], + &MatchAddress[Hardware] + ); + + if (CacheEntry == NULL) { + // + // Check the ResolvedCacheTable + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->ResolvedCacheTable, + NULL, + ByBoth, + &MatchAddress[Protocol], + &MatchAddress[Hardware] + ); + } + + if ((CacheEntry != NULL) && !Overwrite) { + // + // The entry to add exists, if not Overwirte, deny this add request. + // + Status = EFI_ACCESS_DENIED; + goto UNLOCK_EXIT; + } + + if ((CacheEntry == NULL) && (TargetSwAddress != NULL)) { + // + // Check whether there are pending requests matching the entry to be added. + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->PendingRequestTable, + NULL, + ByProtoAddress, + &MatchAddress[Protocol], + NULL + ); + } + + if (CacheEntry != NULL) { + // + // Remove it from the Table. + // + NetListRemoveEntry (&CacheEntry->List); + } else { + // + // It's a new entry, allocate memory for the entry. + // + CacheEntry = ArpAllocCacheEntry (Instance); + + if (CacheEntry == NULL) { + ARP_DEBUG_ERROR (("ArpAdd: Failed to allocate pool for CacheEntry.\n")); + Status = EFI_OUT_OF_RESOURCES; + goto UNLOCK_EXIT; + } + } + + // + // Overwrite these parameters. + // + CacheEntry->DefaultDecayTime = TimeoutValue; + CacheEntry->DecayTime = TimeoutValue; + + // + // Fill in the addresses. + // + ArpFillAddressInCacheEntry ( + CacheEntry, + &MatchAddress[Hardware], + &MatchAddress[Protocol] + ); + + // + // Inform the user if there is any. + // + ArpAddressResolved (CacheEntry, NULL, NULL); + + // + // Add this CacheEntry to the corresponding CacheTable. + // + if (DenyFlag) { + NetListInsertHead (&ArpService->DeniedCacheTable, &CacheEntry->List); + } else { + NetListInsertHead (&ArpService->ResolvedCacheTable, &CacheEntry->List); + } + +UNLOCK_EXIT: + + NET_UNLOCK (&ArpService->Lock); + + return Status; +} + + +/** + This function searches the ARP cache for matching entries and allocates a buffer into + which those entries are copied. + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + @param BySwAddress Set to TRUE to look for matching software protocol + addresses. Set to FALSE to look for matching + hardware protocol addresses. + @param AddressBuffer Pointer to address buffer. Set to NULL to match + all addresses. + @param EntryLength The size of an entry in the entries buffer. + @param EntryCount The number of ARP cache entries that are found by + the specified criteria. + @param Entries Pointer to the buffer that will receive the ARP + cache entries. + @param Refresh Set to TRUE to refresh the timeout value of the + matching ARP cache entry. + + @retval EFI_SUCCESS The requested ARP cache entries were copied into + the buffer. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. Both EntryCount and EntryLength are + NULL, when Refresh is FALSE. + @retval EFI_NOT_FOUND No matching entries were found. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +EFI_STATUS +EFIAPI +ArpFind ( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL, + OUT UINT32 *EntryLength OPTIONAL, + OUT UINT32 *EntryCount OPTIONAL, + OUT EFI_ARP_FIND_DATA **Entries OPTIONAL, + IN BOOLEAN Refresh + ) +{ + EFI_STATUS Status; + ARP_INSTANCE_DATA *Instance; + ARP_SERVICE_DATA *ArpService; + + if ((This == NULL) || + (!Refresh && (EntryCount == NULL) && (EntryLength == NULL)) || + ((Entries != NULL) && ((EntryLength == NULL) || (EntryCount == NULL)))) { + return EFI_INVALID_PARAMETER; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (This); + ArpService = Instance->ArpService; + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + return EFI_ACCESS_DENIED; + } + + // + // All the check passed, find the cache entries now. + // + Status = ArpFindCacheEntry ( + Instance, + BySwAddress, + AddressBuffer, + EntryLength, + EntryCount, + Entries, + Refresh + ); + + NET_UNLOCK (&ArpService->Lock); + + return Status; +} + + +/** + This function removes specified ARP cache entries. + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + @param BySwAddress Set to TRUE to delete matching protocol addresses. + Set to FALSE to delete matching hardware + addresses. + @param AddressBuffer Pointer to the address buffer that is used as a + key to look for the cache entry. Set to NULL to + delete all entries. + + @retval EFI_SUCCESS The entry was removed from the ARP cache. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_FOUND The specified deletion key was not found. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +EFI_STATUS +EFIAPI +ArpDelete ( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL + ) +{ + ARP_INSTANCE_DATA *Instance; + ARP_SERVICE_DATA *ArpService; + UINTN Count; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + ArpService = Instance->ArpService; + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + return EFI_ACCESS_DENIED; + } + + // + // Delete the specified cache entries. + // + Count = ArpDeleteCacheEntry (Instance, BySwAddress, AddressBuffer, TRUE); + + NET_UNLOCK (&ArpService->Lock); + + return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS; +} + + +/** + This function delete all dynamic entries from the ARP cache that match the specified + software protocol type. + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + + @retval EFI_SUCCESS The cache has been flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_FOUND There are no matching dynamic cache entries. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +EFI_STATUS +EFIAPI +ArpFlush ( + IN EFI_ARP_PROTOCOL *This + ) +{ + ARP_INSTANCE_DATA *Instance; + ARP_SERVICE_DATA *ArpService; + UINTN Count; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + ArpService = Instance->ArpService; + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + return EFI_ACCESS_DENIED; + } + + // + // Delete the dynamic entries from the cache table. + // + Count = ArpDeleteCacheEntry (Instance, FALSE, NULL, FALSE); + + NET_UNLOCK (&ArpService->Lock); + + return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS; +} + + +/** + This function tries to resolve the TargetSwAddress and optionally returns a + TargetHwAddress if it already exists in the ARP cache. + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + @param TargetSwAddress Pointer to the protocol address to resolve. + @param ResolvedEvent Pointer to the event that will be signaled when + the address is resolved or some error occurs. + @param TargetHwAddress Pointer to the buffer for the resolved hardware + address in network byte order. + + @retval EFI_SUCCESS The data is copied from the ARP cache into the + TargetHwAddress buffer. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. TargetHwAddress is NULL. + @retval EFI_ACCESS_DENIED The requested address is not present in the normal + ARP cache but is present in the deny address list. + Outgoing traffic to that address is forbidden. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + @retval EFI_NOT_READY The request has been started and is not finished. + +**/ +EFI_STATUS +EFIAPI +ArpRequest ( + IN EFI_ARP_PROTOCOL *This, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT ResolvedEvent OPTIONAL, + OUT VOID *TargetHwAddress + ) +{ + EFI_STATUS Status; + ARP_INSTANCE_DATA *Instance; + ARP_SERVICE_DATA *ArpService; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + ARP_CACHE_ENTRY *CacheEntry; + NET_ARP_ADDRESS HardwareAddress; + NET_ARP_ADDRESS ProtocolAddress; + USER_REQUEST_CONTEXT *RequestContext; + + if ((This == NULL) || (TargetHwAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + Status = EFI_SUCCESS; + ArpService = Instance->ArpService; + SnpMode = &ArpService->SnpMode; + + if ((TargetSwAddress == NULL) || + ((Instance->ConfigData.SwAddressType == IPv4_ETHER_PROTO_TYPE) && + IP4_IS_LOCAL_BROADCAST (*((UINT32 *)TargetSwAddress)))) { + // + // Return the hardware broadcast address. + // + NetCopyMem (TargetHwAddress, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize); + + goto SIGNAL_USER; + } + + if ((Instance->ConfigData.SwAddressType == IPv4_ETHER_PROTO_TYPE) && + IP4_IS_MULTICAST (NTOHL (*((UINT32 *)TargetSwAddress)))) { + // + // If the software address is an IPv4 multicast address, invoke Mnp to + // resolve the address. + // + Status = ArpService->Mnp->McastIpToMac ( + ArpService->Mnp, + FALSE, + TargetSwAddress, + TargetHwAddress + ); + goto SIGNAL_USER; + } + + HardwareAddress.Type = SnpMode->IfType; + HardwareAddress.Length = (UINT8)SnpMode->HwAddressSize; + HardwareAddress.AddressPtr = NULL; + + ProtocolAddress.Type = Instance->ConfigData.SwAddressType; + ProtocolAddress.Length = Instance->ConfigData.SwAddressLength; + ProtocolAddress.AddressPtr = TargetSwAddress; + + // + // Initialize the TargetHwAddrss to a zero address. + // + NetZeroMem (TargetHwAddress, SnpMode->HwAddressSize); + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + return EFI_ACCESS_DENIED; + } + + // + // Check whether the software address is in the denied table. + // + CacheEntry = ArpFindDeniedCacheEntry (ArpService, &ProtocolAddress, NULL); + if (CacheEntry != NULL) { + Status = EFI_ACCESS_DENIED; + goto UNLOCK_EXIT; + } + + // + // Check whether the software address is already resolved. + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->ResolvedCacheTable, + NULL, + ByProtoAddress, + &ProtocolAddress, + NULL + ); + if (CacheEntry != NULL) { + // + // Resolved, copy the address into the user buffer. + // + NetCopyMem ( + TargetHwAddress, + CacheEntry->Addresses[Hardware].AddressPtr, + CacheEntry->Addresses[Hardware].Length + ); + + goto UNLOCK_EXIT; + } + + if (ResolvedEvent == NULL) { + Status = EFI_NOT_READY; + goto UNLOCK_EXIT; + } + + // + // Create a request context for this arp request. + // + RequestContext = NetAllocatePool (sizeof(USER_REQUEST_CONTEXT)); + if (RequestContext == NULL) { + ARP_DEBUG_ERROR (("ArpRequest: Allocate memory for RequestContext failed.\n")); + + Status = EFI_OUT_OF_RESOURCES; + goto UNLOCK_EXIT; + } + + RequestContext->Instance = Instance; + RequestContext->UserRequestEvent = ResolvedEvent; + RequestContext->UserHwAddrBuffer = TargetHwAddress; + NetListInit (&RequestContext->List); + + // + // Check whether there is a same request. + // + CacheEntry = ArpFindNextCacheEntryInTable ( + &ArpService->PendingRequestTable, + NULL, + ByProtoAddress, + &ProtocolAddress, + NULL + ); + if (CacheEntry != NULL) { + + CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut; + CacheEntry->RetryCount = Instance->ConfigData.RetryCount; + } else { + // + // Allocate a cache entry for this request. + // + CacheEntry = ArpAllocCacheEntry (Instance); + if (CacheEntry == NULL) { + ARP_DEBUG_ERROR (("ArpRequest: Allocate memory for CacheEntry failed.\n")); + NetFreePool (RequestContext); + + Status = EFI_OUT_OF_RESOURCES; + goto UNLOCK_EXIT; + } + + // + // Fill the software address. + // + ArpFillAddressInCacheEntry (CacheEntry, &HardwareAddress, &ProtocolAddress); + + // + // Add this entry into the PendingRequestTable. + // + NetListInsertTail (&ArpService->PendingRequestTable, &CacheEntry->List); + } + + // + // Link this request context into the cache entry. + // + NetListInsertHead (&CacheEntry->UserRequestList, &RequestContext->List); + + // + // Send out the ARP Request frame. + // + ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REQUEST); + Status = EFI_NOT_READY; + +UNLOCK_EXIT: + + NET_UNLOCK (&ArpService->Lock); + +SIGNAL_USER: + + if ((ResolvedEvent != NULL) && (Status == EFI_SUCCESS)) { + gBS->SignalEvent (ResolvedEvent); + } + + return Status; +} + + +/** + This function aborts the previous ARP request (identified by This, TargetSwAddress + and ResolvedEvent) that is issued by EFI_ARP_PROTOCOL.Request(). + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + @param TargetSwAddress Pointer to the protocol address in previous + request session. + @param ResolvedEvent Pointer to the event that is used as the + notification event in previous request session. + + @retval EFI_SUCCESS The pending request session(s) is/are aborted and + corresponding event(s) is/are signaled. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. TargetSwAddress is not NULL and + ResolvedEvent is NULL. TargetSwAddress is NULL and + ResolvedEvent is not NULL. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + @retval EFI_NOT_FOUND The request is not issued by + EFI_ARP_PROTOCOL.Request(). + +**/ +EFI_STATUS +EFIAPI +ArpCancel ( + IN EFI_ARP_PROTOCOL *This, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT ResolvedEvent OPTIONAL + ) +{ + ARP_INSTANCE_DATA *Instance; + ARP_SERVICE_DATA *ArpService; + UINTN Count; + + if ((This == NULL) || + ((TargetSwAddress != NULL) && (ResolvedEvent == NULL)) || + ((TargetSwAddress == NULL) && (ResolvedEvent != NULL))) { + return EFI_INVALID_PARAMETER; + } + + Instance = ARP_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + ArpService = Instance->ArpService; + + if (EFI_ERROR (NET_TRYLOCK (&ArpService->Lock))) { + return EFI_ACCESS_DENIED; + } + + // + // Cancel the specified request. + // + Count = ArpCancelRequest (Instance, TargetSwAddress, ResolvedEvent); + + NET_UNLOCK (&ArpService->Lock); + + return (Count == 0) ? EFI_NOT_FOUND : EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c b/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c new file mode 100644 index 0000000000..ad92f3cc31 --- /dev/null +++ b/MdeModulePkg/Universal/Network/ArpDxe/ComponentName.c @@ -0,0 +1,156 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + +#include "ArpDriver.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +ArpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +ArpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gArpComponentName = { + ArpComponentNameGetDriverName, + ArpComponentNameGetControllerName, + "eng" +}; + +STATIC EFI_UNICODE_STRING_TABLE mArpDriverNameTable[] = { + { "eng", L"ARP Network Service Driver" }, + { NULL, NULL } +}; + +EFI_STATUS +EFIAPI +ArpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCES - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gArpComponentName.SupportedLanguages, + mArpDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +ArpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} + diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c new file mode 100644 index 0000000000..6808e771ac --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/ComponentName.c @@ -0,0 +1,162 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + + +#include "Dhcp4Impl.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +DhcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +DhcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName = { + DhcpComponentNameGetDriverName, + DhcpComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mDhcpDriverNameTable[] = { + { + "eng", + L"DHCP Protocol Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +DhcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCES - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gDhcp4ComponentName.SupportedLanguages, + mDhcpDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +DhcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language + specified by Language from the point of view of the + driver specified by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle isn't NULL and isn't a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c new file mode 100644 index 0000000000..08d6076bda --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.c @@ -0,0 +1,665 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Dhcp4Driver.c + +Abstract: + + +**/ + +#include "Dhcp4Impl.h" +#include "Dhcp4Driver.h" + +EFI_DRIVER_BINDING_PROTOCOL gDhcp4DriverBinding = { + Dhcp4DriverBindingSupported, + Dhcp4DriverBindingStart, + Dhcp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL mDhcp4ServiceBindingTemplete = { + Dhcp4ServiceBindingCreateChild, + Dhcp4ServiceBindingDestroyChild +}; + +//@MT: EFI_DRIVER_ENTRY_POINT (Dhcp4DriverEntryPoint) + +EFI_STATUS +EFIAPI +Dhcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + Entry point of the DHCP driver to install various protocols. + +Arguments: + + ImageHandle - The driver's image handle + SystemTable - The system table + +Returns: + + EFI_SUCCESS - All the related protocols are installed. + Others - Failed to install the protocols. + +--*/ +{ + return NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gDhcp4DriverBinding, + ImageHandle, + &gDhcp4ComponentName, + NULL, + NULL + ); +} + + +/** + Test to see if DHCP driver supports the ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +Dhcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + + +/** + Configure the default UDP child to receive all the DHCP traffics + on this network interface. + + @param UdpIo The UDP IO port to configure + @param Context The context to the function + + @retval EFI_SUCCESS The UDP IO port is successfully configured. + @retval Others Failed to configure the UDP child. + +**/ +EFI_STATUS +DhcpConfigUdpIo ( + IN UDP_IO_PORT *UdpIo, + IN VOID *Context + ) +{ + EFI_UDP4_CONFIG_DATA UdpConfigData; + + UdpConfigData.AcceptBroadcast = TRUE; + UdpConfigData.AcceptPromiscuous = FALSE; + UdpConfigData.AcceptAnyPort = FALSE; + UdpConfigData.AllowDuplicatePort = TRUE; + UdpConfigData.TypeOfService = 0; + UdpConfigData.TimeToLive = 64; + UdpConfigData.DoNotFragment = FALSE; + UdpConfigData.ReceiveTimeout = 0; + UdpConfigData.TransmitTimeout = 0; + + UdpConfigData.UseDefaultAddress = FALSE; + UdpConfigData.StationPort = DHCP_CLIENT_PORT; + UdpConfigData.RemotePort = DHCP_SERVER_PORT; + + NetZeroMem (&UdpConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + NetZeroMem (&UdpConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + NetZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS)); + + return UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);; +} + + + +/** + Destory the DHCP service. The Dhcp4 service may be partly initialized, + or partly destoried. If a resource is destoried, it is marked as so in + case the destory failed and being called again later. + + @param DhcpSb The DHCP service instance to destory. + + @retval EFI_SUCCESS The DHCP service is successfully closed. + +**/ +EFI_STATUS +Dhcp4CloseService ( + IN DHCP_SERVICE *DhcpSb + ) +{ + DhcpCleanLease (DhcpSb); + + if (DhcpSb->UdpIo != NULL) { + UdpIoFreePort (DhcpSb->UdpIo); + DhcpSb->UdpIo = NULL; + } + + if (DhcpSb->Timer != NULL) { + gBS->SetTimer (DhcpSb->Timer, TimerCancel, 0); + gBS->CloseEvent (DhcpSb->Timer); + + DhcpSb->Timer = NULL; + } + + return EFI_SUCCESS; +} + + + +/** + Create a new DHCP service binding instance for the controller. + + @param Controller The controller to install DHCP service binding + protocol onto + @param ImageHandle The driver's image handle + @param Service The variable to receive the created DHCP service + instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource . + @retval EFI_SUCCESS The DHCP service instance is created. + +**/ +EFI_STATUS +Dhcp4CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT DHCP_SERVICE **Service + ) +{ + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + + *Service = NULL; + DhcpSb = NetAllocateZeroPool (sizeof (DHCP_SERVICE)); + + if (DhcpSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DhcpSb->Signature = DHCP_SERVICE_SIGNATURE; + DhcpSb->ServiceBinding = mDhcp4ServiceBindingTemplete; + DhcpSb->ServiceState = DHCP_UNCONFIGED; + DhcpSb->InDestory = FALSE; + DhcpSb->Controller = Controller; + DhcpSb->Image = ImageHandle; + NetListInit (&DhcpSb->Children); + DhcpSb->DhcpState = Dhcp4Stopped; + DhcpSb->Xid = NET_RANDOM (NetRandomInitSeed ()); + + // + // Create various resources, UdpIo, Timer, and get Mac address + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + DhcpOnTimerTick, + DhcpSb, + &DhcpSb->Timer + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + DhcpSb->UdpIo = UdpIoCreatePort (Controller, ImageHandle, DhcpConfigUdpIo, NULL); + + if (DhcpSb->UdpIo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + DhcpSb->HwLen = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize; + DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType; + CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (EFI_MAC_ADDRESS)); + + *Service = DhcpSb; + return EFI_SUCCESS; + +ON_ERROR: + Dhcp4CloseService (DhcpSb); + NetFreePool (DhcpSb); + + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +Dhcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + + // + // First: test for the DHCP4 Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the Dhcp4ServiceBinding Protocol onto ControlerHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + &DhcpSb->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return Status; + +ON_ERROR: + Dhcp4CloseService (DhcpSb); + NetFreePool (DhcpSb); + return Status; +} + + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +Dhcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + DHCP_SERVICE *DhcpSb; + DHCP_PROTOCOL *Instance; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // DHCP driver opens UDP child, So, the ControllerHandle is the + // UDP child handle. locate the Nic handle first. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding); + + if (DhcpSb->InDestory) { + return EFI_SUCCESS; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + DhcpSb->InDestory = TRUE; + + // + // Don't use NET_LIST_FOR_EACH_SAFE here, Dhcp4ServiceBindingDestoryChild + // may cause other child to be deleted. + // + while (!NetListIsEmpty (&DhcpSb->Children)) { + Instance = NET_LIST_HEAD (&DhcpSb->Children, DHCP_PROTOCOL, Link); + Dhcp4ServiceBindingDestroyChild (ServiceBinding, Instance->Handle); + } + + if (DhcpSb->NumChildren != 0) { + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + DhcpSb->ServiceState = DHCP_DESTORY; + + Status = gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + ServiceBinding + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Dhcp4CloseService (DhcpSb); + NET_RESTORE_TPL (OldTpl); + + NetFreePool (DhcpSb); + return EFI_SUCCESS; + +ON_ERROR: + DhcpSb->InDestory = FALSE; + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Initialize a new DHCP child + + @param DhcpSb The dhcp service instance + @param Instance The dhcp instance to initialize + + @return None + +**/ +VOID +DhcpInitProtocol ( + IN DHCP_SERVICE *DhcpSb, + IN DHCP_PROTOCOL *Instance + ) +{ + Instance->Signature = DHCP_PROTOCOL_SIGNATURE; + CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (EFI_DHCP4_PROTOCOL)); + NetListInit (&Instance->Link); + Instance->Handle = NULL; + Instance->Service = DhcpSb; + Instance->InDestory = FALSE; + Instance->CompletionEvent = NULL; + Instance->RenewRebindEvent = NULL; + Instance->Token = NULL; +} + + +/** + Creates a child handle with a set of DHCP4 services. + + @param This Protocol instance pointer. + @param ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the DHCP4 services are added to + the existing child handle. + + @retval EFI_SUCCES The child handle was created with the DHCP4 + services + @retval EFI_OUT_OF_RESOURCES There are not enough resources to create the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +Dhcp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + DHCP_SERVICE *DhcpSb; + DHCP_PROTOCOL *Instance; + EFI_STATUS Status; + EFI_TPL OldTpl; + VOID *Udp4; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = NetAllocatePool (sizeof (*Instance)); + + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DhcpSb = DHCP_SERVICE_FROM_THIS (This); + DhcpInitProtocol (DhcpSb, Instance); + + // + // Install DHCP4 onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiDhcp4ProtocolGuid, + &Instance->Dhcp4Protocol, + NULL + ); + + if (EFI_ERROR (Status)) { + NetFreePool (Instance); + return Status; + } + + Instance->Handle = *ChildHandle; + + // + // Open the Udp4 protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + DhcpSb->UdpIo->UdpHandle, + &gEfiUdp4ProtocolGuid, + (VOID **) &Udp4, + gDhcp4DriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiDhcp4ProtocolGuid, + &Instance->Dhcp4Protocol, + NULL + ); + + NetFreePool (Instance); + return Status; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + NetListInsertTail (&DhcpSb->Children, &Instance->Link); + DhcpSb->NumChildren++; + + NET_RESTORE_TPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Destroys a child handle with a set of DHCP4 services. + + @param This Protocol instance pointer. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The DHCP4 service is removed from the child handle + @retval EFI_UNSUPPORTED The child handle does not support the DHCP4 + service + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its DHCP4 services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dhcp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + DHCP_SERVICE *DhcpSb; + DHCP_PROTOCOL *Instance; + EFI_DHCP4_PROTOCOL *Dhcp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Dhcp, + gDhcp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = DHCP_INSTANCE_FROM_THIS (Dhcp); + DhcpSb = DHCP_SERVICE_FROM_THIS (This); + + if (Instance->Service != DhcpSb) { + return EFI_INVALID_PARAMETER; + } + + // + // A child can be destoried more than once. For example, + // Dhcp4DriverBindingStop will destory all of its children. + // when caller driver is being stopped, it will destory the + // dhcp child it opens. + // + if (Instance->InDestory) { + return EFI_SUCCESS; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + Instance->InDestory = TRUE; + + // + // Close the Udp4 protocol. + // + gBS->CloseProtocol ( + DhcpSb->UdpIo->UdpHandle, + &gEfiUdp4ProtocolGuid, + gDhcp4DriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the DHCP4 protocol first to enable a top down destruction. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiDhcp4ProtocolGuid, + Dhcp + ); + + if (EFI_ERROR (Status)) { + Instance->InDestory = FALSE; + + NET_RESTORE_TPL (OldTpl); + return Status; + } + + if (DhcpSb->ActiveChild == Instance) { + DhcpYieldControl (DhcpSb); + } + + NetListRemoveEntry (&Instance->Link); + DhcpSb->NumChildren--; + + NET_RESTORE_TPL (OldTpl); + + NetFreePool (Instance); + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h new file mode 100644 index 0000000000..100bf56bb2 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Driver.h @@ -0,0 +1,67 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Dhcp4Driver.h + +Abstract: + + Header for the DHCP4 driver + + +**/ + +#ifndef __EFI_DHCP4_DRIVER_H__ +#define __EFI_DHCP4_DRIVER_H__ + +extern EFI_COMPONENT_NAME_PROTOCOL gDhcp4ComponentName; + +EFI_STATUS +EFIAPI +Dhcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Dhcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Dhcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +EFI_STATUS +EFIAPI +Dhcp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +EFIAPI +Dhcp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf new file mode 100644 index 0000000000..2c35be1f0e --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf @@ -0,0 +1,65 @@ +#/** @file +# Component name for module Dhcp4 +# +# Copyright (c) 2007, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Dhcp4Dxe + FILE_GUID = 94734718-0BBC-47fb-96A5-EE7A5AE6A2AD + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = Dhcp4DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Dhcp4Impl.c + Dhcp4Io.c + Dhcp4Io.h + ComponentName.c + Dhcp4Driver.h + Dhcp4Driver.c + Dhcp4Option.c + Dhcp4Option.h + Dhcp4Impl.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + NetLib + UdpIoLib + + +[Protocols] + gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDhcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDhcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa new file mode 100644 index 0000000000..6bde71ad29 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.msa @@ -0,0 +1,77 @@ + + + Dhcp4Dxe + DXE_DRIVER + 94734718-0BBC-47fb-96A5-EE7A5AE6A2AD + 1.0 + Component name for module Dhcp4 + FIX ME! + Copyright (c) 2007, Intel Corporation. All rights reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + Dhcp4Dxe + + + + DebugLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + BaseLib + + + + Dhcp4Impl.h + Dhcp4Option.h + Dhcp4Option.c + Dhcp4Driver.c + Dhcp4Driver.h + ComponentName.c + Dhcp4Io.h + Dhcp4Io.c + Dhcp4Impl.c + + + + + + + + gEfiDhcp4ProtocolGuid + + + gEfiUdp4ProtocolGuid + + + gEfiDhcp4ServiceBindingProtocolGuid + + + gEfiUdp4ServiceBindingProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + Dhcp4DriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c new file mode 100644 index 0000000000..68da959466 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c @@ -0,0 +1,914 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Dhcp4Impl.c + +Abstract: + + This file implement the EFI_DHCP4_PROTOCOL interface. + + +**/ + + +#include "Dhcp4Impl.h" + + +/** + Get the current operation parameter and lease for the network interface. + + @param This The DHCP protocol instance + @param Dhcp4ModeData The variable to save the DHCP mode data. + + @retval EFI_INVALID_PARAMETER The parameter is invalid + @retval EFI_SUCCESS The Dhcp4ModeData is updated with the current + operation parameter. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4GetModeData ( + IN EFI_DHCP4_PROTOCOL *This, + OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData + ) +{ + DHCP_PROTOCOL *Instance; + DHCP_SERVICE *DhcpSb; + DHCP_PARAMETER *Para; + EFI_TPL OldTpl; + IP4_ADDR Ip; + + // + // First validate the parameters. + // + if ((This == NULL) || (Dhcp4ModeData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP_INSTANCE_FROM_THIS (This); + + if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + DhcpSb = Instance->Service; + + // + // Caller can use GetModeData to retrieve current DHCP states + // no matter whether it is the active child or not. + // + Dhcp4ModeData->State = DhcpSb->DhcpState; + CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (EFI_DHCP4_CONFIG_DATA)); + CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (EFI_MAC_ADDRESS)); + + Ip = HTONL (DhcpSb->ClientAddr); + NetCopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Ip = HTONL (DhcpSb->Netmask); + NetCopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Ip = HTONL (DhcpSb->ServerAddr); + NetCopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Para = DhcpSb->Para; + + if (Para != NULL) { + Ip = HTONL (Para->Router); + NetCopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + Dhcp4ModeData->LeaseTime = Para->Lease; + } else { + NetZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS)); + Dhcp4ModeData->LeaseTime = 0xffffffff; + } + + Dhcp4ModeData->ReplyPacket = DhcpSb->Selected; + + NET_RESTORE_TPL (OldTpl); + return EFI_SUCCESS; +} + + +/** + Free the resource related to the configure parameters. + DHCP driver will make a copy of the user's configure + such as the time out value. + + @param Config The DHCP configure data + + @return None + +**/ +VOID +DhcpCleanConfigure ( + IN EFI_DHCP4_CONFIG_DATA *Config + ) +{ + UINT32 Index; + + if (Config->DiscoverTimeout != NULL) { + NetFreePool (Config->DiscoverTimeout); + } + + if (Config->RequestTimeout != NULL) { + NetFreePool (Config->RequestTimeout); + } + + if (Config->OptionList != NULL) { + for (Index = 0; Index < Config->OptionCount; Index++) { + if (Config->OptionList[Index] != NULL) { + NetFreePool (Config->OptionList[Index]); + } + } + + NetFreePool (Config->OptionList); + } + + NetZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA)); +} + + +/** + Allocate memory for configure parameter such as timeout value for Dst, + then copy the configure parameter from Src to Dst. + + @param Dst The destination DHCP configure data. + @param Src The source DHCP configure data. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_SUCCESS The configure is copied. + +**/ +EFI_STATUS +DhcpCopyConfigure ( + IN EFI_DHCP4_CONFIG_DATA *Dst, + IN EFI_DHCP4_CONFIG_DATA *Src + ) +{ + EFI_DHCP4_PACKET_OPTION **DstOptions; + EFI_DHCP4_PACKET_OPTION **SrcOptions; + INTN Len; + UINT32 Index; + + CopyMem (Dst, Src, sizeof (EFI_DHCP4_CONFIG_DATA)); + Dst->DiscoverTimeout = NULL; + Dst->RequestTimeout = NULL; + Dst->OptionList = NULL; + + // + // Allocate a memory then copy DiscoverTimeout to it + // + if (Src->DiscoverTimeout != NULL) { + Len = Src->DiscoverTryCount * sizeof (UINT32); + Dst->DiscoverTimeout = NetAllocatePool (Len); + + if (Dst->DiscoverTimeout == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < Src->DiscoverTryCount; Index++) { + Dst->DiscoverTimeout[Index] = NET_MAX (Src->DiscoverTimeout[Index], 1); + } + } + + // + // Allocate a memory then copy RequestTimeout to it + // + if (Src->RequestTimeout != NULL) { + Len = Src->RequestTryCount * sizeof (UINT32); + Dst->RequestTimeout = NetAllocatePool (Len); + + if (Dst->RequestTimeout == NULL) { + goto ON_ERROR; + } + + for (Index = 0; Index < Src->RequestTryCount; Index++) { + Dst->RequestTimeout[Index] = NET_MAX (Src->RequestTimeout[Index], 1); + } + } + + // + // Allocate an array of dhcp option point, then allocate memory + // for each option and copy the source option to it + // + if (Src->OptionList != NULL) { + Len = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *); + Dst->OptionList = NetAllocateZeroPool (Len); + + if (Dst->OptionList == NULL) { + goto ON_ERROR; + } + + DstOptions = Dst->OptionList; + SrcOptions = Src->OptionList; + + for (Index = 0; Index < Src->OptionCount; Index++) { + Len = sizeof (EFI_DHCP4_PACKET_OPTION) + NET_MAX (SrcOptions[Index]->Length - 1, 0); + + DstOptions[Index] = NetAllocatePool (Len); + + if (DstOptions[Index] == NULL) { + goto ON_ERROR; + } + + NetCopyMem (DstOptions[Index], SrcOptions[Index], Len); + } + } + + return EFI_SUCCESS; + +ON_ERROR: + DhcpCleanConfigure (Dst); + return EFI_OUT_OF_RESOURCES; +} + + +/** + Give up the control of the DHCP service to let other child + resume. Don't change the service's DHCP state and the Client + address and option list configure as required by RFC2131. + + @param DhcpSb The DHCP service instance. + + @return None + +**/ +VOID +DhcpYieldControl ( + IN DHCP_SERVICE *DhcpSb + ) +{ + EFI_DHCP4_CONFIG_DATA *Config; + DHCP_PROTOCOL *Instance; + + Instance = DhcpSb->ActiveChild; + Config = &DhcpSb->ActiveConfig; + + DhcpSb->ServiceState = DHCP_UNCONFIGED; + DhcpSb->ActiveChild = NULL; + + if (Config->DiscoverTimeout != NULL) { + NetFreePool (Config->DiscoverTimeout); + + Config->DiscoverTryCount = 0; + Config->DiscoverTimeout = NULL; + } + + if (Config->RequestTimeout != NULL) { + NetFreePool (Config->RequestTimeout); + + Config->RequestTryCount = 0; + Config->RequestTimeout = NULL; + } + + Config->Dhcp4Callback = NULL; + Config->CallbackContext = NULL; +} + + +/** + Configure the DHCP protocol instance and its underlying DHCP service + for operation. If Dhcp4CfgData is NULL and the child is currently + controlling the DHCP service, release the control. + + @param This The DHCP protocol instance + @param Dhcp4CfgData The DHCP configure data. + + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_ACCESS_DENIED The service isn't in one of configurable states, + or there is already an active child. + @retval EFI_OUT_OF_RESOURCE Failed to allocate some resources. + @retval EFI_SUCCESS The child is configured. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4Configure ( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL + ) +{ + EFI_DHCP4_CONFIG_DATA *Config; + DHCP_PROTOCOL *Instance; + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + EFI_TPL OldTpl; + UINT32 Index; + IP4_ADDR Ip; + + // + // First validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Dhcp4CfgData != NULL) { + if (Dhcp4CfgData->DiscoverTryCount && (Dhcp4CfgData->DiscoverTimeout == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (Dhcp4CfgData->RequestTryCount && (Dhcp4CfgData->RequestTimeout == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (Dhcp4CfgData->OptionCount && (Dhcp4CfgData->OptionList == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NetCopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR)); + + if ((Ip != 0) && !Ip4IsUnicast (NTOHL (Ip), 0)) { + + return EFI_INVALID_PARAMETER; + } + } + + Instance = DHCP_INSTANCE_FROM_THIS (This); + + if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + DhcpSb = Instance->Service; + Config = &DhcpSb->ActiveConfig; + + Status = EFI_ACCESS_DENIED; + + if ((DhcpSb->DhcpState != Dhcp4Stopped) && + (DhcpSb->DhcpState != Dhcp4Init) && + (DhcpSb->DhcpState != Dhcp4InitReboot) && + (DhcpSb->DhcpState != Dhcp4Bound)) { + + goto ON_EXIT; + } + + if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) { + goto ON_EXIT; + } + + if (Dhcp4CfgData != NULL) { + Status = EFI_OUT_OF_RESOURCES; + DhcpCleanConfigure (Config); + + if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) { + goto ON_EXIT; + } + + DhcpSb->UserOptionLen = 0; + + for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) { + DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2; + } + + DhcpSb->ActiveChild = Instance; + + if (DhcpSb->DhcpState == Dhcp4Stopped) { + DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress); + + if (DhcpSb->ClientAddr != 0) { + DhcpSb->DhcpState = Dhcp4InitReboot; + } else { + DhcpSb->DhcpState = Dhcp4Init; + } + } + + DhcpSb->ServiceState = DHCP_CONFIGED; + Status = EFI_SUCCESS; + + } else if (DhcpSb->ActiveChild == Instance) { + Status = EFI_SUCCESS; + DhcpYieldControl (DhcpSb); + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Start the DHCP process. + + @param This The DHCP protocol instance + @param CompletionEvent The event to signal is address is acquired. + + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_NOT_STARTED The protocol hasn't been configured. + @retval EFI_ALREADY_STARTED The DHCP process has already been started. + @retval EFI_SUCCESS The DHCP process is started. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4Start ( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_EVENT CompletionEvent OPTIONAL + ) +{ + DHCP_PROTOCOL *Instance; + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // First validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP_INSTANCE_FROM_THIS (This); + + if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + DhcpSb = Instance->Service; + + if (DhcpSb->DhcpState == Dhcp4Stopped) { + Status = EFI_NOT_STARTED; + goto ON_ERROR; + } + + if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) { + Status = EFI_ALREADY_STARTED; + goto ON_ERROR; + } + + DhcpSb->IoStatus = EFI_ALREADY_STARTED; + + if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) { + goto ON_ERROR; + } + + // + // Start/Restart the receiving. + // + Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + Instance->CompletionEvent = CompletionEvent; + + // + // Restore the TPL now, don't call poll function at NET_TPL_LOCK. + // + NET_RESTORE_TPL (OldTpl); + + if (CompletionEvent == NULL) { + while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) { + DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp); + } + + return DhcpSb->IoStatus; + } + + return EFI_SUCCESS; + +ON_ERROR: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Request an extra manual renew/rebind. + + @param This The DHCP protocol instance + @param RebindRequest TRUE if request a rebind, otherwise renew it + @param CompletionEvent Event to signal when complete + + @retval EFI_INVALID_PARAMETER The parameters are invalid + @retval EFI_NOT_STARTED The DHCP protocol hasn't been started. + @retval EFI_ACCESS_DENIED The DHCP protocol isn't in Bound state. + @retval EFI_SUCCESS The DHCP is renewed/rebound. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4RenewRebind ( + IN EFI_DHCP4_PROTOCOL *This, + IN BOOLEAN RebindRequest, + IN EFI_EVENT CompletionEvent OPTIONAL + ) +{ + DHCP_PROTOCOL *Instance; + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // First validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP_INSTANCE_FROM_THIS (This); + + if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + DhcpSb = Instance->Service; + + if (DhcpSb->DhcpState == Dhcp4Stopped) { + Status = EFI_NOT_STARTED; + goto ON_ERROR; + } + + if (DhcpSb->DhcpState != Dhcp4Bound) { + Status = EFI_ACCESS_DENIED; + goto ON_ERROR; + } + + if (DHCP_IS_BOOTP (DhcpSb->Para)) { + return EFI_SUCCESS; + } + + // + // Transit the states then send a extra DHCP request + // + if (!RebindRequest) { + DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE); + } else { + DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE); + } + + Status = DhcpSendMessage ( + DhcpSb, + DhcpSb->Selected, + DhcpSb->Para, + DHCP_MSG_REQUEST, + "Extra renew/rebind by the application" + ); + + if (EFI_ERROR (Status)) { + DhcpSetState (DhcpSb, Dhcp4Bound, FALSE); + goto ON_ERROR; + } + + DhcpSb->ExtraRefresh = TRUE; + DhcpSb->IoStatus = EFI_ALREADY_STARTED; + Instance->RenewRebindEvent = CompletionEvent; + + NET_RESTORE_TPL (OldTpl); + + if (CompletionEvent == NULL) { + while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) { + DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp); + } + + return DhcpSb->IoStatus; + } + + return EFI_SUCCESS; + +ON_ERROR: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Release the current acquired lease. + + @param This The DHCP protocol instance + + @retval EFI_INVALID_PARAMETER The parameter is invalid + @retval EFI_DEVICE_ERROR Failed to transmit the DHCP release packet + @retval EFI_ACCESS_DENIED The DHCP service isn't in one of the connected + state. + @retval EFI_SUCCESS The lease is released. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4Release ( + IN EFI_DHCP4_PROTOCOL *This + ) +{ + DHCP_PROTOCOL *Instance; + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // First validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP_INSTANCE_FROM_THIS (This); + + if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + DhcpSb = Instance->Service; + + if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) { + Status = DhcpSendMessage ( + DhcpSb, + DhcpSb->Selected, + DhcpSb->Para, + DHCP_MSG_RELEASE, + NULL + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + } + + DhcpCleanLease (DhcpSb); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Stop the current DHCP process. After this, other DHCP child + can gain control of the service, configure and use it. + + @param This The DHCP protocol instance + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_SUCCESS The DHCP process is stopped. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4Stop ( + IN EFI_DHCP4_PROTOCOL *This + ) +{ + DHCP_PROTOCOL *Instance; + DHCP_SERVICE *DhcpSb; + EFI_TPL OldTpl; + + // + // First validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP_INSTANCE_FROM_THIS (This); + + if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + DhcpSb = Instance->Service; + + DhcpCleanLease (DhcpSb); + + DhcpSb->DhcpState = Dhcp4Stopped; + DhcpSb->ServiceState = DHCP_UNCONFIGED; + + NET_RESTORE_TPL (OldTpl); + return EFI_SUCCESS; +} + + +/** + Build a new DHCP packet from the seed packet. Options may be deleted or + appended. The caller should free the NewPacket when finished using it. + + @param This The DHCP protocol instance. + @param SeedPacket The seed packet to start with + @param DeleteCount The number of options to delete + @param DeleteList The options to delete from the packet + @param AppendCount The number of options to append + @param AppendList The options to append to the packet + @param NewPacket The new packet, allocated and built by this + function. + + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory + @retval EFI_SUCCESS The packet is build. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4Build ( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_PACKET *SeedPacket, + IN UINT32 DeleteCount, + IN UINT8 *DeleteList OPTIONAL, + IN UINT32 AppendCount, + IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket + ) +{ + // + // First validate the parameters + // + if ((This == NULL) || (NewPacket == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) || + EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) { + + return EFI_INVALID_PARAMETER; + } + + if (((DeleteCount == 0) && (AppendCount == 0)) || + ((DeleteCount != 0) && (DeleteList == NULL)) || + ((AppendCount != 0) && (AppendList == NULL))) { + + return EFI_INVALID_PARAMETER; + } + + return DhcpBuild ( + SeedPacket, + DeleteCount, + DeleteList, + AppendCount, + AppendList, + NewPacket + ); +} + + +/** + Transmit and receive a packet through this DHCP service. + This is unsupported. + + @param This The DHCP protocol instance + @param Token The transmit and receive instance + + @retval EFI_UNSUPPORTED It always returns unsupported. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4TransmitReceive ( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token + ) +{ + // + // This function is for PXE, leave it for now + // + return EFI_UNSUPPORTED; +} + + +/** + Callback function for DhcpIterateOptions. This callback sets the + EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point + the individual DHCP option in the packet. + + @param Tag The DHCP option type + @param Len length of the DHCP option data + @param Data The DHCP option data + @param Context The context, to pass several parameters in. + + @retval EFI_SUCCESS It always returns EFI_SUCCESS + +**/ +STATIC +EFI_STATUS +Dhcp4ParseCheckOption ( + IN UINT8 Tag, + IN UINT8 Len, + IN UINT8 *Data, + IN VOID *Context + ) +{ + DHCP_PARSE_CONTEXT *Parse; + + Parse = (DHCP_PARSE_CONTEXT *) Context; + Parse->Index++; + + if (Parse->Index < Parse->OptionCount) { + // + // Use _CR to get the memory position of EFI_DHCP4_PACKET_OPTION for + // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only + // pass in the point to option data. + // + Parse->Option[Parse->Index - 1] = _CR (Data, EFI_DHCP4_PACKET_OPTION, Data); + } + + return EFI_SUCCESS; +} + + +/** + Parse the DHCP options in the Packet into the PacketOptionList. + User should allocate this array of EFI_DHCP4_PACKET_OPTION points. + + @param This The DHCP protocol instance + @param Packet The DHCP packet to parse + @param OptionCount On input, the size of the PacketOptionList; On + output, the actual number of options processed. + @param PacketOptionList The array of EFI_DHCP4_PACKET_OPTION points + + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_BUFFER_TOO_SMALL A bigger array of points is needed. + @retval EFI_SUCCESS The options are parsed. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiDhcp4Parse ( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_PACKET *Packet, + IN OUT UINT32 *OptionCount, + OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL + ) +{ + DHCP_PARSE_CONTEXT Context; + EFI_STATUS Status; + + // + // First validate the parameters + // + if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) || + (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) || + EFI_ERROR (DhcpValidateOptions (Packet, NULL))) { + + return EFI_INVALID_PARAMETER; + } + + if ((*OptionCount != 0) && (PacketOptionList == NULL)) { + return EFI_BUFFER_TOO_SMALL; + } + + NetZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); + + Context.Option = PacketOptionList; + Context.OptionCount = *OptionCount; + Context.Index = 0; + + Status = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context); + + if (EFI_ERROR (Status)) { + return Status; + } + + *OptionCount = Context.Index; + + if (Context.Index > Context.OptionCount) { + return EFI_BUFFER_TOO_SMALL; + } + + return EFI_SUCCESS; +} + +EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate = { + EfiDhcp4GetModeData, + EfiDhcp4Configure, + EfiDhcp4Start, + EfiDhcp4RenewRebind, + EfiDhcp4Release, + EfiDhcp4Stop, + EfiDhcp4Build, + EfiDhcp4TransmitReceive, + EfiDhcp4Parse +}; diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h new file mode 100644 index 0000000000..7f449415d2 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.h @@ -0,0 +1,159 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Dhcp4Impl.h + +Abstract: + + EFI DHCP protocol implementation + RFCs supported are: + RFC 2131: Dynamic Host Configuration Protocol + RFC 2132: DHCP Options and BOOTP Vendor Extensions + RFC 1534: Interoperation Between DHCP and BOOTP + RFC 3396: Encoding Long Options in DHCP + + +**/ + +#ifndef __EFI_DHCP4_IMPL_H__ +#define __EFI_DHCP4_IMPL_H__ + + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct _DHCP_SERVICE DHCP_SERVICE; +typedef struct _DHCP_PROTOCOL DHCP_PROTOCOL; + +#include "Dhcp4Option.h" +#include "Dhcp4Io.h" + +enum { + DHCP_SERVICE_SIGNATURE = EFI_SIGNATURE_32 ('D', 'H', 'C', 'P'), + DHCP_PROTOCOL_SIGNATURE = EFI_SIGNATURE_32 ('d', 'h', 'c', 'p'), + + // + // The state of the DHCP service. It starts as UNCONFIGED. If + // and active child configures the service successfully, it + // goes to CONFIGED. If the active child configures NULL, it + // goes back to UNCONFIGED. It becomes DESTORY if it is (partly) + // destoried. + // + DHCP_UNCONFIGED = 0, + DHCP_CONFIGED, + DHCP_DESTORY, +}; + +typedef struct _DHCP_PROTOCOL { + UINT32 Signature; + EFI_DHCP4_PROTOCOL Dhcp4Protocol; + NET_LIST_ENTRY Link; + EFI_HANDLE Handle; + DHCP_SERVICE *Service; + + BOOLEAN InDestory; + + EFI_EVENT CompletionEvent; + EFI_EVENT RenewRebindEvent; + + EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token; +}; + +// +// DHCP driver is specical in that it is a singleton. Although it +// has a service binding, there can be only one active child. +// +typedef struct _DHCP_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + + INTN ServiceState; // CONFIGED, UNCONFIGED, and DESTORY + BOOLEAN InDestory; + + EFI_HANDLE Controller; + EFI_HANDLE Image; + + NET_LIST_ENTRY Children; + UINTN NumChildren; + + INTN DhcpState; + EFI_STATUS IoStatus; // the result of last user operation + UINT32 Xid; + + IP4_ADDR ClientAddr; // lease IP or configured client address + IP4_ADDR Netmask; + IP4_ADDR ServerAddr; + + EFI_DHCP4_PACKET *LastOffer; // The last received offer + EFI_DHCP4_PACKET *Selected; + DHCP_PARAMETER *Para; + + UINT32 Lease; + UINT32 T1; + UINT32 T2; + INTN ExtraRefresh; // This refresh is reqested by user + + UDP_IO_PORT *UdpIo; // Udp child receiving all DHCP message + UDP_IO_PORT *LeaseIoPort; // Udp child with lease IP + NET_BUF *LastPacket; // The last sent packet for retransmission + EFI_MAC_ADDRESS Mac; + UINT8 HwType; + UINT8 HwLen; + + DHCP_PROTOCOL *ActiveChild; + EFI_DHCP4_CONFIG_DATA ActiveConfig; + UINT32 UserOptionLen; + + // + // Timer event and various timer + // + EFI_EVENT Timer; + + UINT32 PacketToLive; // Retransmission timer for our packets + INTN CurRetry; + INTN MaxRetries; + + UINT32 WaitOffer; // Time to collect the offers + UINT32 LeaseLife; +}; + +typedef struct { + EFI_DHCP4_PACKET_OPTION **Option; + UINT32 OptionCount; + UINT32 Index; +} DHCP_PARSE_CONTEXT; + +#define DHCP_INSTANCE_FROM_THIS(Proto) \ + CR ((Proto), DHCP_PROTOCOL, Dhcp4Protocol, DHCP_PROTOCOL_SIGNATURE) + +#define DHCP_SERVICE_FROM_THIS(Sb) \ + CR ((Sb), DHCP_SERVICE, ServiceBinding, DHCP_SERVICE_SIGNATURE) + +extern EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate; + +VOID +DhcpYieldControl ( + IN DHCP_SERVICE *DhcpSb + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c new file mode 100644 index 0000000000..e0fdcd382c --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c @@ -0,0 +1,1631 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Dhcp4Io.c + +Abstract: + + EFI DHCP protocol implementation + + +**/ + + +#include "Dhcp4Impl.h" + +UINT32 mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 }; + + +/** + Send an initial DISCOVER or REQUEST message according to the + DHCP service's current state. + + @param DhcpSb The DHCP service instance + + @retval EFI_SUCCESS The request has been sent + +**/ +EFI_STATUS +DhcpInitRequest ( + IN DHCP_SERVICE *DhcpSb + ) +{ + EFI_STATUS Status; + + ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot)); + + if (DhcpSb->DhcpState == Dhcp4Init) { + DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE); + Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL); + + if (EFI_ERROR (Status)) { + DhcpSb->DhcpState = Dhcp4Init; + return Status; + } + + DhcpSb->WaitOffer = DHCP_WAIT_OFFER; + } else { + DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE); + Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL); + + if (EFI_ERROR (Status)) { + DhcpSb->DhcpState = Dhcp4InitReboot; + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Call user provided callback function, and return the value the + function returns. If the user doesn't provide a callback, a + proper return value is selected to let the caller continue the + normal process. + + @param DhcpSb The DHCP service instance + @param Event The event as defined in the spec + @param Packet The current packet trigger the event + @param NewPacket The user's return new packet + + @retval EFI_NOT_READY Direct the caller to continue collecting the offer. + @retval EFI_SUCCESS The user function returns success. + @retval EFI_ABORTED The user function ask it to abort. + +**/ +STATIC +EFI_STATUS +DhcpCallUser ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_DHCP4_EVENT Event, + IN EFI_DHCP4_PACKET *Packet, OPTIONAL + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ) +{ + EFI_DHCP4_CONFIG_DATA *Config; + EFI_STATUS Status; + + if (NewPacket != NULL) { + *NewPacket = NULL; + } + + // + // If user doesn't provide the call back function, return the value + // that directs the client to continue the normal process. + // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting + // the offers and select a offer, EFI_NOT_READY tells the client to + // collect more offers. + // + Config = &DhcpSb->ActiveConfig; + + if (Config->Dhcp4Callback == NULL) { + if (Event == Dhcp4RcvdOffer) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; + } + + Status = Config->Dhcp4Callback ( + &DhcpSb->ActiveChild->Dhcp4Protocol, + Config->CallbackContext, + DhcpSb->DhcpState, + Event, + Packet, + NewPacket + ); + + // + // User's callback should only return EFI_SUCCESS, EFI_NOT_READY, + // and EFI_ABORTED. If it returns values other than those, assume + // it to be EFI_ABORTED. + // + if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) { + return Status; + } + + return EFI_ABORTED; +} + + +/** + Notify the user about the operation result. + + @param DhcpSb DHCP service instance + @param Which which notify function to signal + + @return None + +**/ +VOID +DhcpNotifyUser ( + IN DHCP_SERVICE *DhcpSb, + IN INTN Which + ) +{ + DHCP_PROTOCOL *Child; + + if ((Child = DhcpSb->ActiveChild) == NULL) { + return ; + } + + if ((Child->CompletionEvent != NULL) && + ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))) { + + gBS->SignalEvent (Child->CompletionEvent); + Child->CompletionEvent = NULL; + } + + if ((Child->RenewRebindEvent != NULL) && + ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))) { + + gBS->SignalEvent (Child->RenewRebindEvent); + Child->RenewRebindEvent = NULL; + } +} + + + +/** + Set the DHCP state. If CallUser is true, it will try to notify + the user before change the state by DhcpNotifyUser. It returns + EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns + EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test + the return value of this function. + + @param DhcpSb The DHCP service instance + @param State The new DHCP state to change to + @param CallUser Whether we need to call user + + @retval EFI_SUCCESS The state is changed + @retval EFI_ABORTED The user asks to abort the DHCP process. + +**/ +EFI_STATUS +DhcpSetState ( + IN DHCP_SERVICE *DhcpSb, + IN INTN State, + IN BOOLEAN CallUser + ) +{ + EFI_STATUS Status; + + if (CallUser) { + Status = EFI_SUCCESS; + + if (State == Dhcp4Renewing) { + Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL); + + } else if (State == Dhcp4Rebinding) { + Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL); + + } else if (State == Dhcp4Bound) { + Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL); + + } + + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Update the retransmission timer during the state transition. + // This will clear the retry count. This is also why the rule + // first transit the state, then send packets. + // + if (State == Dhcp4Init) { + DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount; + } else { + DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount; + } + + if (DhcpSb->MaxRetries == 0) { + DhcpSb->MaxRetries = 4; + } + + DhcpSb->CurRetry = 0; + DhcpSb->PacketToLive = 0; + + DhcpSb->DhcpState = State; + return EFI_SUCCESS; +} + + +/** + Set the retransmit timer for the packet. It will select from either + the discover timeouts/request timeouts or the default timeout values. + + @param DhcpSb The DHCP service instance. + + @return None + +**/ +STATIC +VOID +DhcpSetTransmitTimer ( + IN DHCP_SERVICE *DhcpSb + ) +{ + UINT32 *Times; + + ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry); + + if (DhcpSb->DhcpState == Dhcp4Init) { + Times = DhcpSb->ActiveConfig.DiscoverTimeout; + } else { + Times = DhcpSb->ActiveConfig.RequestTimeout; + } + + if (Times == NULL) { + Times = mDhcp4DefaultTimeout; + } + + DhcpSb->PacketToLive = Times[DhcpSb->CurRetry]; +} + + +/** + Compute the lease. If the server grants a permanent lease, just + process it as a normal timeout value since the lease will last + more than 100 years. + + @param DhcpSb The DHCP service instance + @param Para The DHCP parameter extracted from the server's + response. + + @return None + +**/ +STATIC +VOID +DhcpComputeLease ( + IN DHCP_SERVICE *DhcpSb, + IN DHCP_PARAMETER *Para + ) +{ + ASSERT (Para != NULL); + + DhcpSb->Lease = Para->Lease; + DhcpSb->T2 = Para->T2; + DhcpSb->T1 = Para->T1; + + if (DhcpSb->Lease == 0) { + DhcpSb->Lease = DHCP_DEFAULT_LEASE; + } + + if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) { + DhcpSb->T2 = Para->Lease - (Para->Lease >> 3); + } + + if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) { + DhcpSb->T1 = DhcpSb->Lease >> 1; + } +} + + +/** + Configure a UDP IO port to use the acquired lease address. + DHCP driver needs this port to unicast packet to the server + such as DHCP release. + + @param UdpIo The UDP IO port to configure + @param Context The opaque parameter to the function. + + @retval EFI_SUCCESS The UDP IO port is successfully configured. + @retval Others It failed to configure the port. + +**/ +EFI_STATUS +DhcpConfigLeaseIoPort ( + IN UDP_IO_PORT *UdpIo, + IN VOID *Context + ) +{ + EFI_UDP4_CONFIG_DATA UdpConfigData; + EFI_IPv4_ADDRESS Subnet; + EFI_IPv4_ADDRESS Gateway; + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + IP4_ADDR Ip; + + DhcpSb = (DHCP_SERVICE *) Context; + + UdpConfigData.AcceptBroadcast = FALSE; + UdpConfigData.AcceptPromiscuous = FALSE; + UdpConfigData.AcceptAnyPort = FALSE; + UdpConfigData.AllowDuplicatePort = TRUE; + UdpConfigData.TypeOfService = 0; + UdpConfigData.TimeToLive = 64; + UdpConfigData.DoNotFragment = FALSE; + UdpConfigData.ReceiveTimeout = 1; + UdpConfigData.TransmitTimeout = 0; + + UdpConfigData.UseDefaultAddress = FALSE; + UdpConfigData.StationPort = DHCP_CLIENT_PORT; + UdpConfigData.RemotePort = DHCP_SERVER_PORT; + + Ip = HTONL (DhcpSb->ClientAddr); + NetCopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Ip = HTONL (DhcpSb->Netmask); + NetCopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + NetZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS)); + + Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Add a default route if received from the server. + // + if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) { + NetZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS)); + + Ip = HTONL (DhcpSb->Para->Router); + NetCopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + UdpIo->Udp->Routes (UdpIo->Udp, FALSE, &Subnet, &Subnet, &Gateway); + } + + return EFI_SUCCESS; +} + + +/** + Update the lease states when a new lease is acquired. It will not only + save the acquired the address and lease time, it will also create a UDP + child to provide address resolution for the address. + + @param DhcpSb The DHCP service instance + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The lease is recorded. + +**/ +STATIC +EFI_STATUS +DhcpLeaseAcquired ( + IN DHCP_SERVICE *DhcpSb + ) +{ + INTN Class; + + DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr); + + if (DhcpSb->Para != NULL) { + DhcpSb->Netmask = DhcpSb->Para->NetMask; + DhcpSb->ServerAddr = DhcpSb->Para->ServerId; + } + + if (DhcpSb->Netmask == 0) { + Class = NetGetIpClass (DhcpSb->ClientAddr); + DhcpSb->Netmask = mIp4AllMasks[Class << 3]; + } + + if (DhcpSb->LeaseIoPort != NULL) { + UdpIoFreePort (DhcpSb->LeaseIoPort); + } + + // + // Create a UDP/IP child to provide ARP service for the Leased IP, + // and transmit unicast packet with it as source address. Don't + // start receive on this port, the queued packet will be timeout. + // + DhcpSb->LeaseIoPort = UdpIoCreatePort ( + DhcpSb->Controller, + DhcpSb->Image, + DhcpConfigLeaseIoPort, + DhcpSb + ); + + if (DhcpSb->LeaseIoPort == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (!DHCP_IS_BOOTP (DhcpSb->Para)) { + DhcpComputeLease (DhcpSb, DhcpSb->Para); + } + + return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE); +} + + +/** + Clean up the DHCP related states, IoStatus isn't reset. + + @param DhcpSb The DHCP instance service. + + @return None + +**/ +VOID +DhcpCleanLease ( + IN DHCP_SERVICE *DhcpSb + ) +{ + DhcpSb->DhcpState = Dhcp4Init; + DhcpSb->Xid = DhcpSb->Xid + 1; + DhcpSb->ClientAddr = 0; + DhcpSb->ServerAddr = 0; + + if (DhcpSb->LastOffer != NULL) { + NetFreePool (DhcpSb->LastOffer); + DhcpSb->LastOffer = NULL; + } + + if (DhcpSb->Selected != NULL) { + NetFreePool (DhcpSb->Selected); + DhcpSb->Selected = NULL; + } + + if (DhcpSb->Para != NULL) { + NetFreePool (DhcpSb->Para); + DhcpSb->Para = NULL; + } + + DhcpSb->Lease = 0; + DhcpSb->T1 = 0; + DhcpSb->T2 = 0; + DhcpSb->ExtraRefresh = FALSE; + + if (DhcpSb->LeaseIoPort != NULL) { + UdpIoFreePort (DhcpSb->LeaseIoPort); + DhcpSb->LeaseIoPort = NULL; + } + + if (DhcpSb->LastPacket != NULL) { + NetbufFree (DhcpSb->LastPacket); + DhcpSb->LastPacket = NULL; + } + + DhcpSb->PacketToLive = 0; + DhcpSb->CurRetry = 0; + DhcpSb->MaxRetries = 0; + DhcpSb->WaitOffer = 0; + DhcpSb->LeaseLife = 0; +} + + +/** + Select a offer among all the offers collected. If the offer selected is + of BOOTP, the lease is recorded and user notified. If the offer is of + DHCP, it will request the offer from the server. + + @param DhcpSb The DHCP service instance. + + @retval EFI_SUCCESS One of the offer is selected. + +**/ +STATIC +EFI_STATUS +DhcpChooseOffer ( + IN DHCP_SERVICE *DhcpSb + ) +{ + EFI_DHCP4_PACKET *Selected; + EFI_DHCP4_PACKET *NewPacket; + EFI_STATUS Status; + + ASSERT (DhcpSb->LastOffer != NULL); + + // + // Stop waiting more offers + // + DhcpSb->WaitOffer = 0; + + // + // User will cache previous offers if he wants to select + // from multiple offers. If user provides an invalid packet, + // use the last offer, otherwise use the provided packet. + // + NewPacket = NULL; + Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket); + + if (EFI_ERROR (Status)) { + return Status; + } + + Selected = DhcpSb->LastOffer; + + if (NewPacket != NULL) { + if (EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) { + NetFreePool (NewPacket); + } else { + NetFreePool (Selected); + Selected = NewPacket; + } + } + + DhcpSb->Selected = Selected; + DhcpSb->LastOffer = NULL; + DhcpSb->Para = NULL; + DhcpValidateOptions (Selected, &DhcpSb->Para); + + // + // A bootp offer has been selected, save the lease status, + // enter bound state then notify the user. + // + if (DHCP_IS_BOOTP (DhcpSb->Para)) { + Status = DhcpLeaseAcquired (DhcpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + DhcpSb->IoStatus = EFI_SUCCESS; + DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL); + return EFI_SUCCESS; + } + + // + // Send a DHCP requests + // + Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE); + + if (EFI_ERROR (Status)) { + return Status; + } + + return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL); +} + + +/** + Terminate the current address acquire. All the allocated resources + are released. Be careful when calling this function. A rule related + to this is: only call DhcpEndSession at the highest level, such as + DhcpInput, DhcpOnTimerTick...At the other level, just return error. + + @param DhcpSb The DHCP service instance + @param Status The result of the DHCP process. + + @return None + +**/ +STATIC +VOID +DhcpEndSession ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_STATUS Status + ) +{ + if (DHCP_CONNECTED (DhcpSb->DhcpState)) { + DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL); + } else { + DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL); + } + + DhcpCleanLease (DhcpSb); + + DhcpSb->IoStatus = Status; + DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL); +} + + +/** + Handle packets in DHCP select state. + + @param DhcpSb The DHCP service instance + @param Packet The DHCP packet received + @param Para The DHCP parameter extracted from the packet. That + is, all the option value that we care. + + @retval EFI_SUCCESS The packet is successfully processed. + @retval Others Some error occured. + +**/ +STATIC +EFI_STATUS +DhcpHandleSelect ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_DHCP4_PACKET *Packet, + IN DHCP_PARAMETER *Para + ) +{ + EFI_DHCP4_HEADER *Head; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + // + // First validate the message: + // 1. the offer is a unicast + // 2. if it is a DHCP message, it must contains a server ID. + // Don't return a error for these two case otherwise the session is ended. + // + Head = &Packet->Dhcp4.Header; + + if (!Ip4IsUnicast (EFI_NTOHL (Head->YourAddr), (Para == NULL ? 0 : Para->NetMask))) { + goto ON_EXIT; + } + + if (!DHCP_IS_BOOTP (Para) && + ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))) { + goto ON_EXIT; + } + + // + // Call the user's callback. The action according to the return is as: + // 1. EFI_SUCESS: stop waiting for more offers, select the offer now + // 2. EFI_NOT_READY: wait for more offers + // 3. EFI_ABORTED: abort the address acquiring. + // + Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL); + + if (Status == EFI_SUCCESS) { + if (DhcpSb->LastOffer != NULL) { + NetFreePool (DhcpSb->LastOffer); + } + + DhcpSb->LastOffer = Packet; + + return DhcpChooseOffer (DhcpSb); + + } else if (Status == EFI_NOT_READY) { + if (DhcpSb->LastOffer != NULL) { + NetFreePool (DhcpSb->LastOffer); + } + + DhcpSb->LastOffer = Packet; + + } else if (Status == EFI_ABORTED) { + // + // DhcpInput will end the session upon error return. Remember + // only to call DhcpEndSession at the top level call. + // + goto ON_EXIT; + } + + return EFI_SUCCESS; + +ON_EXIT: + NetFreePool (Packet); + return Status; +} + + +/** + Handle packets in DHCP request state. + + @param DhcpSb The DHCP service instance + @param Packet The DHCP packet received + @param Para The DHCP parameter extracted from the packet. That + is, all the option value that we care. + + @retval EFI_SUCCESS The packet is successfully processed. + @retval Others Some error occured. + +**/ +STATIC +EFI_STATUS +DhcpHandleRequest ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_DHCP4_PACKET *Packet, + IN DHCP_PARAMETER *Para + ) +{ + EFI_DHCP4_HEADER *Head; + EFI_DHCP4_HEADER *Selected; + EFI_STATUS Status; + UINT8 *Message; + + ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para)); + + Head = &Packet->Dhcp4.Header; + Selected = &DhcpSb->Selected->Dhcp4.Header; + + // + // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK. + // + if (DHCP_IS_BOOTP (Para) || + (Para->ServerId != DhcpSb->Para->ServerId) || + ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) { + + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // Received a NAK, end the session no matter what the user returns + // + Status = EFI_DEVICE_ERROR; + + if (Para->DhcpType == DHCP_MSG_NAK) { + DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL); + goto ON_EXIT; + } + + // + // Check whether the ACK matches the selected offer + // + Message = NULL; + + if (!EFI_IP4_EQUAL (Head->YourAddr, Selected->YourAddr)) { + Message = "Lease confirmed isn't the same as that in the offer"; + goto REJECT; + } + + Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL); + + if (EFI_ERROR (Status)) { + Message = "Lease is denied upon received ACK"; + goto REJECT; + } + + // + // Record the lease, transit to BOUND state, then notify the user + // + Status = DhcpLeaseAcquired (DhcpSb); + + if (EFI_ERROR (Status)) { + Message = "Lease is denied upon entering bound"; + goto REJECT; + } + + DhcpSb->IoStatus = EFI_SUCCESS; + DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION); + + NetFreePool (Packet); + return EFI_SUCCESS; + +REJECT: + DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message); + +ON_EXIT: + NetFreePool (Packet); + return Status; +} + + +/** + Handle packets in DHCP renew/rebound state. + + @param DhcpSb The DHCP service instance + @param Packet The DHCP packet received + @param Para The DHCP parameter extracted from the packet. That + is, all the option value that we care. + + @retval EFI_SUCCESS The packet is successfully processed. + @retval Others Some error occured. + +**/ +STATIC +EFI_STATUS +DhcpHandleRenewRebind ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_DHCP4_PACKET *Packet, + IN DHCP_PARAMETER *Para + ) +{ + EFI_DHCP4_HEADER *Head; + EFI_DHCP4_HEADER *Selected; + EFI_STATUS Status; + + ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para)); + + Head = &Packet->Dhcp4.Header; + Selected = &DhcpSb->Selected->Dhcp4.Header; + + // + // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK + // + if (DHCP_IS_BOOTP (Para) || + (Para->ServerId != DhcpSb->Para->ServerId) || + ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) { + + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // Received a NAK, ignore the user's return then terminate the process + // + Status = EFI_DEVICE_ERROR; + + if (Para->DhcpType == DHCP_MSG_NAK) { + DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL); + goto ON_EXIT; + } + + // + // The lease is different from the selected. Don't send a DECLINE + // since it isn't existed in the client's FSM. + // + if (!EFI_IP4_EQUAL (Head->YourAddr, Selected->YourAddr)) { + goto ON_EXIT; + } + + Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Record the lease, start timer for T1 and T2, + // + DhcpComputeLease (DhcpSb, Para); + DhcpSb->LeaseLife = 0; + DhcpSetState (DhcpSb, Dhcp4Bound, TRUE); + + if (DhcpSb->ExtraRefresh) { + DhcpSb->ExtraRefresh = FALSE; + + DhcpSb->IoStatus = EFI_SUCCESS; + DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND); + } + +ON_EXIT: + NetFreePool (Packet); + return Status; +} + + +/** + Handle packets in DHCP reboot state. + + @param DhcpSb The DHCP service instance + @param Packet The DHCP packet received + @param Para The DHCP parameter extracted from the packet. That + is, all the option value that we care. + + @retval EFI_SUCCESS The packet is successfully processed. + @retval Others Some error occured. + +**/ +STATIC +EFI_STATUS +DhcpHandleReboot ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_DHCP4_PACKET *Packet, + IN DHCP_PARAMETER *Para + ) +{ + EFI_DHCP4_HEADER *Head; + EFI_STATUS Status; + + Head = &Packet->Dhcp4.Header; + + // + // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK + // + if (DHCP_IS_BOOTP (Para) || + ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) { + + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // If a NAK is received, transit to INIT and try again. + // + if (Para->DhcpType == DHCP_MSG_NAK) { + DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL); + + DhcpSb->ClientAddr = 0; + DhcpSb->DhcpState = Dhcp4Init; + + Status = DhcpInitRequest (DhcpSb); + goto ON_EXIT; + } + + // + // Check whether the ACK matches the selected offer + // + if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // OK, get the parameter from server, record the lease + // + DhcpSb->Para = NetAllocatePool (sizeof (DHCP_PARAMETER)); + + if (DhcpSb->Para == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + DhcpSb->Selected = Packet; + CopyMem (DhcpSb->Para, Para, sizeof (DHCP_PARAMETER)); + + Status = DhcpLeaseAcquired (DhcpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + DhcpSb->IoStatus = EFI_SUCCESS; + DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION); + return EFI_SUCCESS; + +ON_EXIT: + NetFreePool (Packet); + return Status; +} + + +/** + Handle the received DHCP packets. This function drivers the DHCP + state machine. + + @param UdpPacket The UDP packets received. + @param Points The local/remote UDP access points + @param IoStatus The status of the UDP receive + @param Context The opaque parameter to the function. + + @return None + +**/ +VOID +DhcpInput ( + NET_BUF *UdpPacket, + UDP_POINTS *Points, + EFI_STATUS IoStatus, + VOID *Context + ) +{ + DHCP_SERVICE *DhcpSb; + EFI_DHCP4_HEADER *Head; + EFI_DHCP4_PACKET *Packet; + DHCP_PARAMETER *Para; + EFI_STATUS Status; + UINT32 Len; + + Packet = NULL; + DhcpSb = (DHCP_SERVICE *) Context; + + // + // Don't restart receive if error occurs or DHCP is destoried. + // + if (EFI_ERROR (IoStatus)) { + return ; + } else if (DhcpSb->ServiceState == DHCP_DESTORY) { + NetbufFree (UdpPacket); + return ; + } + + ASSERT (UdpPacket != NULL); + + if (DhcpSb->DhcpState == Dhcp4Stopped) { + goto RESTART; + } + + // + // Validate the packet received + // + if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) { + goto RESTART; + } + + // + // Copy the DHCP message to a continuous memory block + // + Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER); + Packet = (EFI_DHCP4_PACKET *) NetAllocatePool (Len); + + if (Packet == NULL) { + goto RESTART; + } + + Packet->Size = Len; + Head = &Packet->Dhcp4.Header; + Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head); + + if (Packet->Length != UdpPacket->TotalSize) { + goto RESTART; + } + + // + // Is this packet the answer to our packet? + // + if ((Head->OpCode != BOOTP_REPLY) || + (NTOHL (Head->Xid) != DhcpSb->Xid) || + !NET_MAC_EQUAL (&DhcpSb->Mac, Head->ClientHwAddr, DhcpSb->HwLen)) { + goto RESTART; + } + + // + // Validate the options and retrieve the interested options + // + Para = NULL; + if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) && + (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) && + EFI_ERROR (DhcpValidateOptions (Packet, &Para))) { + + goto RESTART; + } + + // + // Call the handler for each state. The handler should return + // EFI_SUCCESS if the process can go on no matter whether the + // packet is ignored or not. If the return is EFI_ERROR, the + // session will be terminated. Packet's ownership is handled + // over to the handlers. If operation succeeds, the handler + // must notify the user. It isn't necessary to do if EFI_ERROR + // is returned because the DhcpEndSession will notify the user. + // + Status = EFI_SUCCESS; + + switch (DhcpSb->DhcpState) { + case Dhcp4Selecting: + Status = DhcpHandleSelect (DhcpSb, Packet, Para); + break; + + case Dhcp4Requesting: + Status = DhcpHandleRequest (DhcpSb, Packet, Para); + break; + + case Dhcp4InitReboot: + case Dhcp4Init: + case Dhcp4Bound: + // + // Ignore the packet in INITREBOOT, INIT and BOUND states + // + NetFreePool (Packet); + Status = EFI_SUCCESS; + break; + + case Dhcp4Renewing: + case Dhcp4Rebinding: + Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para); + break; + + case Dhcp4Rebooting: + Status = DhcpHandleReboot (DhcpSb, Packet, Para); + break; + } + + if (Para != NULL) { + NetFreePool (Para); + } + + Packet = NULL; + + if (EFI_ERROR (Status)) { + NetbufFree (UdpPacket); + DhcpEndSession (DhcpSb, Status); + return ; + } + +RESTART: + NetbufFree (UdpPacket); + + if (Packet != NULL) { + NetFreePool (Packet); + } + + Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0); + + if (EFI_ERROR (Status)) { + DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR); + } +} + + +/** + Release the packet. + + @param Arg The packet to release + + @return None + +**/ +VOID +DhcpReleasePacket ( + IN VOID *Arg + ) +{ + NetFreePool (Arg); +} + + +/** + Release the net buffer when packet is sent. + + @param UdpPacket The UDP packets received. + @param Points The local/remote UDP access points + @param IoStatus The status of the UDP receive + @param Context The opaque parameter to the function. + + @return None + +**/ +VOID +DhcpOnPacketSent ( + NET_BUF *Packet, + UDP_POINTS *Points, + EFI_STATUS IoStatus, + VOID *Context + ) +{ + NetbufFree (Packet); +} + + + +/** + Build and transmit a DHCP message according to the current states. + This function implement the Table 5. of RFC 2131. Always transits + the state (as defined in Figure 5. of the same RFC) before sending + a DHCP message. The table is adjusted accordingly. + + @param DhcpSb The DHCP service instance + @param Seed The seed packet which the new packet is based on + @param Para The DHCP parameter of the Seed packet + @param Type The message type to send + @param Msg The human readable message to include in the packet + sent. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet + @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP + @retval EFI_SUCCESS The message is sent + +**/ +EFI_STATUS +DhcpSendMessage ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_DHCP4_PACKET *Seed, + IN DHCP_PARAMETER *Para, + IN UINT8 Type, + IN UINT8 *Msg + ) +{ + EFI_DHCP4_CONFIG_DATA *Config; + EFI_DHCP4_PACKET *Packet; + EFI_DHCP4_PACKET *NewPacket; + EFI_DHCP4_HEADER *Head; + EFI_DHCP4_HEADER *SeedHead; + UDP_IO_PORT *UdpIo; + UDP_POINTS EndPoint; + NET_BUF *Wrap; + NET_FRAGMENT Frag; + EFI_STATUS Status; + IP4_ADDR IpAddr; + UINT8 *Buf; + UINT16 MaxMsg; + UINT32 Len; + UINT32 Index; + + // + // Allocate a big enough memory block to hold the DHCP packet + // + Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen; + + if (Msg != NULL) { + Len += (UINT32)AsciiStrLen (Msg); + } + + Packet = NetAllocatePool (Len); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = Len; + Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32); + + // + // Fill in the DHCP header fields + // + Config = &DhcpSb->ActiveConfig; + SeedHead = NULL; + + if (Seed != NULL) { + SeedHead = &Seed->Dhcp4.Header; + } + + Head = &Packet->Dhcp4.Header; + NetZeroMem (Head, sizeof (EFI_DHCP4_HEADER)); + + Head->OpCode = BOOTP_REQUEST; + Head->HwType = DhcpSb->HwType; + Head->HwAddrLen = DhcpSb->HwLen; + Head->Xid = HTONL (DhcpSb->Xid); + Head->Reserved = HTONS (0x8000); //Server, broadcast the message please. + + EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr); + NetCopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen); + + // + // Append the DHCP message type + // + Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC; + Buf = Packet->Dhcp4.Option; + Buf = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type); + + // + // Append the serverid option if necessary: + // 1. DHCP decline message + // 2. DHCP release message + // 3. DHCP request to confirm one lease. + // + if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) || + ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))) { + + ASSERT ((Para != NULL) && (Para->ServerId != 0)); + + IpAddr = HTONL (Para->ServerId); + Buf = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr); + } + + // + // Append the requested IP option if necessary: + // 1. DHCP request to use the previously allocated address + // 2. DHCP request to confirm one lease + // 3. DHCP decline to decline one lease + // + IpAddr = 0; + + if (Type == DHCP_MSG_REQUEST) { + if (DhcpSb->DhcpState == Dhcp4Rebooting) { + IpAddr = EFI_IP4 (Config->ClientAddress); + + } else if (DhcpSb->DhcpState == Dhcp4Requesting) { + ASSERT (SeedHead != NULL); + IpAddr = EFI_IP4 (SeedHead->YourAddr); + } + + } else if (Type == DHCP_MSG_DECLINE) { + ASSERT (SeedHead != NULL); + IpAddr = EFI_IP4 (SeedHead->YourAddr); + } + + if (IpAddr != 0) { + Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr); + } + + // + // Append the Max Message Length option if it isn't a DECLINE + // or RELEASE to direct the server use large messages instead of + // override the BOOTFILE and SERVER fields in the message head. + // + if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) { + MaxMsg = HTONS (0xFF00); + Buf = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg); + } + + // + // Append the user's message if it isn't NULL + // + if (Msg != NULL) { + Len = NET_MIN ((UINT32) AsciiStrLen (Msg), 255); + Buf = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg); + } + + // + // Append the user configured options + // + if (DhcpSb->UserOptionLen != 0) { + for (Index = 0; Index < Config->OptionCount; Index++) { + // + // We can't use any option other than the client ID from user + // if it is a DHCP decline or DHCP release . + // + if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) && + (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) { + continue; + } + + Buf = DhcpAppendOption ( + Buf, + Config->OptionList[Index]->OpCode, + Config->OptionList[Index]->Length, + Config->OptionList[Index]->Data + ); + } + } + + *(Buf++) = DHCP_TAG_EOP; + Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option); + + // + // OK, the message is built, call the user to override it. + // + Status = EFI_SUCCESS; + NewPacket = NULL; + + if (Type == DHCP_MSG_DISCOVER) { + Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket); + + } else if (Type == DHCP_MSG_REQUEST) { + Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket); + + } else if (Type == DHCP_MSG_DECLINE) { + Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket); + } + + if (EFI_ERROR (Status)) { + NetFreePool (Packet); + return Status; + } + + if (NewPacket != NULL) { + NetFreePool (Packet); + Packet = NewPacket; + } + + // + // Wrap it into a netbuf then send it. + // + Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header; + Frag.Len = Packet->Length; + Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet); + + if (Wrap == NULL) { + NetFreePool (Packet); + return EFI_OUT_OF_RESOURCES; + } + + // + // Save it as the last sent packet for retransmission + // + if (DhcpSb->LastPacket != NULL) { + NetbufFree (DhcpSb->LastPacket); + } + + NET_GET_REF (Wrap); + DhcpSb->LastPacket = Wrap; + DhcpSetTransmitTimer (DhcpSb); + + // + // Broadcast the message, unless we know the server address. + // Use the lease UdpIo port to send the unicast packet. + // + EndPoint.RemoteAddr = 0xffffffff; + EndPoint.LocalAddr = 0; + EndPoint.RemotePort = DHCP_SERVER_PORT; + EndPoint.LocalPort = DHCP_CLIENT_PORT; + UdpIo = DhcpSb->UdpIo; + + if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) { + EndPoint.RemoteAddr = DhcpSb->ServerAddr; + EndPoint.LocalAddr = DhcpSb->ClientAddr; + UdpIo = DhcpSb->LeaseIoPort; + } + + ASSERT (UdpIo != NULL); + Status = UdpIoSendDatagram (UdpIo, Wrap, &EndPoint, 0, DhcpOnPacketSent, DhcpSb); + + if (EFI_ERROR (Status)) { + NetbufFree (Wrap); + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + Retransmit a saved packet. Only DISCOVER and REQUEST messages + will be retransmitted. + + @param DhcpSb The DHCP service instance + + @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port + @retval EFI_SUCCESS The packet is retransmitted. + +**/ +EFI_STATUS +DhcpRetransmit ( + IN DHCP_SERVICE *DhcpSb + ) +{ + UDP_IO_PORT *UdpIo; + UDP_POINTS EndPoint; + EFI_STATUS Status; + + ASSERT (DhcpSb->LastPacket != NULL); + + // + // Broadcast the message, unless we know the server address. + // + EndPoint.RemotePort = DHCP_SERVER_PORT; + EndPoint.LocalPort = DHCP_CLIENT_PORT; + EndPoint.RemoteAddr = 0xffffffff; + EndPoint.LocalAddr = 0; + UdpIo = DhcpSb->UdpIo; + + if (DhcpSb->DhcpState == Dhcp4Renewing) { + EndPoint.RemoteAddr = DhcpSb->ServerAddr; + EndPoint.LocalAddr = DhcpSb->ClientAddr; + UdpIo = DhcpSb->LeaseIoPort; + } + + ASSERT (UdpIo != NULL); + + NET_GET_REF (DhcpSb->LastPacket); + Status = UdpIoSendDatagram ( + UdpIo, + DhcpSb->LastPacket, + &EndPoint, + 0, + DhcpOnPacketSent, + DhcpSb + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (DhcpSb->LastPacket); + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + Each DHCP service has three timer. Two of them are count down timer. + One for the packet retransmission. The other is to collect the offers. + The third timer increaments the lease life which is compared to T1, T2, + and lease to determine the time to renew and rebind the lease. + DhcpOnTimerTick will be called once every second. + + @param Event The timer event + @param Context The context, which is the DHCP service instance. + + @return None + +**/ +VOID +EFIAPI +DhcpOnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + DHCP_SERVICE *DhcpSb; + EFI_STATUS Status; + + DhcpSb = (DHCP_SERVICE *) Context; + + // + // Check the retransmit timer first + // + if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) { + + if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) { + // + // Still has another try + // + DhcpRetransmit (DhcpSb); + DhcpSetTransmitTimer (DhcpSb); + + } else { + if (!DHCP_CONNECTED (DhcpSb->DhcpState)) { + goto END_SESSION; + } + + // + // Retransmission failed, if the DHCP request is initiated by + // user, adjust the current state according to the lease life. + // Otherwise do nothing to wait the lease to timeout + // + if (DhcpSb->ExtraRefresh) { + Status = EFI_SUCCESS; + + if (DhcpSb->LeaseLife < DhcpSb->T1) { + Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE); + + } else if (DhcpSb->LeaseLife < DhcpSb->T2) { + Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE); + + } else if (DhcpSb->LeaseLife < DhcpSb->Lease) { + Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE); + + } else { + goto END_SESSION; + + } + + DhcpSb->IoStatus = EFI_TIMEOUT; + DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND); + } + } + } + + if ((DhcpSb->WaitOffer > 0) && (--DhcpSb->WaitOffer == 0)) { + // + // OK, offer collection finished, select a offer + // + ASSERT (DhcpSb->DhcpState == Dhcp4Selecting); + + if (DhcpSb->LastOffer == NULL) { + goto END_SESSION; + } + + if (EFI_ERROR (DhcpChooseOffer (DhcpSb))) { + goto END_SESSION; + } + } + + // + // If an address has been acquired, check whether need to + // refresh or whether it has expired. + // + if (DHCP_CONNECTED (DhcpSb->DhcpState)) { + DhcpSb->LeaseLife++; + + // + // Don't timeout the lease, only count the life if user is + // requesting extra renew/rebind. Adjust the state after that. + // + if (DhcpSb->ExtraRefresh) { + return ; + } + + if (DhcpSb->LeaseLife == DhcpSb->Lease) { + // + // Lease expires, end the session + // + goto END_SESSION; + + } else if (DhcpSb->LeaseLife == DhcpSb->T2) { + // + // T2 expires, transit to rebinding then send a REQUEST to any server + // + if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) { + goto END_SESSION; + } + + Status = DhcpSendMessage ( + DhcpSb, + DhcpSb->Selected, + DhcpSb->Para, + DHCP_MSG_REQUEST, + NULL + ); + + if (EFI_ERROR (Status)) { + goto END_SESSION; + } + + } else if (DhcpSb->LeaseLife == DhcpSb->T1) { + // + // T1 expires, transit to renewing, then send a REQUEST to the server + // + if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) { + goto END_SESSION; + } + + Status = DhcpSendMessage ( + DhcpSb, + DhcpSb->Selected, + DhcpSb->Para, + DHCP_MSG_REQUEST, + NULL + ); + + if (EFI_ERROR (Status)) { + goto END_SESSION; + } + } + } + + return ; + +END_SESSION: + DhcpEndSession (DhcpSb, EFI_TIMEOUT); + + return ; +} diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h new file mode 100644 index 0000000000..1da95f94a7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.h @@ -0,0 +1,115 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Dhcp4Io.h + +Abstract: + + The DHCP4 protocol implementation. + + +**/ + +#ifndef __EFI_DHCP4_IO_H__ +#define __EFI_DHCP4_IO_H__ + +#include + +#include + +#include +#include +#include +#include + + +enum { + DHCP_WAIT_OFFER = 3, // Time to wait the offers + DHCP_DEFAULT_LEASE = 7 *24 *60 *60, // Seven days as default. + DHCP_SERVER_PORT = 67, + DHCP_CLIENT_PORT = 68, + + // + // BOOTP header "op" field + // + BOOTP_REQUEST = 1, + BOOTP_REPLY = 2, + + // + // DHCP message types + // + DHCP_MSG_DISCOVER = 1, + DHCP_MSG_OFFER = 2, + DHCP_MSG_REQUEST = 3, + DHCP_MSG_DECLINE = 4, + DHCP_MSG_ACK = 5, + DHCP_MSG_NAK = 6, + DHCP_MSG_RELEASE = 7, + DHCP_MSG_INFORM = 8, + + // + // DHCP notify user type + // + DHCP_NOTIFY_COMPLETION = 1, + DHCP_NOTIFY_RENEWREBIND, + DHCP_NOTIFY_ALL, +}; + +#define DHCP_IS_BOOTP(Parameter) (((Parameter) == NULL) || ((Parameter)->DhcpType == 0)) + +#define DHCP_CONNECTED(State) \ + (((State) == Dhcp4Bound) || ((State) == (Dhcp4Renewing)) || ((State) == Dhcp4Rebinding)) + +EFI_STATUS +DhcpSetState ( + IN DHCP_SERVICE *DhcpSb, + IN INTN State, + IN BOOLEAN CallUser + ); + +EFI_STATUS +DhcpSendMessage ( + IN DHCP_SERVICE *DhcpSb, + IN EFI_DHCP4_PACKET *Seed, + IN DHCP_PARAMETER *Para, + IN UINT8 Type, + IN UINT8 *Msg + ); + +VOID +EFIAPI +DhcpOnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +DhcpInput ( + NET_BUF *UdpPacket, + UDP_POINTS *Points, + EFI_STATUS IoStatus, + VOID *Context + ); + +EFI_STATUS +DhcpInitRequest ( + IN DHCP_SERVICE *DhcpSb + ); + +VOID +DhcpCleanLease ( + IN DHCP_SERVICE *DhcpSb + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c new file mode 100644 index 0000000000..b16f4693d7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c @@ -0,0 +1,906 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Dhcp4Option.c + +Abstract: + + Function to validate, parse, process the DHCP options + + +**/ + +#include "Dhcp4Impl.h" + +// +// A list of the format of DHCP Options sorted by option tag +// to validate a dhcp message. Refere the comments of the +// DHCP_OPTION_FORMAT structure. +// +STATIC +DHCP_OPTION_FORMAT +DhcpOptionFormats [] = { + {DHCP_TAG_NETMASK, DHCP_OPTION_IP, 1, 1 , TRUE}, + {DHCP_TAG_TIME_OFFSET, DHCP_OPTION_INT32, 1, 1 , FALSE}, + {DHCP_TAG_ROUTER, DHCP_OPTION_IP, 1, -1 , TRUE}, + {DHCP_TAG_TIME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_NAME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_DNS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_LOG_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_COOKIE_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_LPR_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_IMPRESS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_RL_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_HOSTNAME, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_BOOTFILE_LEN, DHCP_OPTION_INT16, 1, 1 , FALSE}, + {DHCP_TAG_DUMP, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_DOMAINNAME, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_SWAP_SERVER, DHCP_OPTION_IP, 1, 1 , FALSE}, + {DHCP_TAG_ROOTPATH, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_EXTEND_PATH, DHCP_OPTION_INT8, 1, -1 , FALSE}, + + {DHCP_TAG_IPFORWARD, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + {DHCP_TAG_NONLOCAL_SRR, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + {DHCP_TAG_POLICY_SRR, DHCP_OPTION_IPPAIR, 1, -1 , FALSE}, + {DHCP_TAG_EMTU, DHCP_OPTION_INT16, 1, 1 , FALSE}, + {DHCP_TAG_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE}, + {DHCP_TAG_PATHMTU_AGE, DHCP_OPTION_INT32, 1, 1 , FALSE}, + {DHCP_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16, 1, -1 , FALSE}, + + {DHCP_TAG_IFMTU, DHCP_OPTION_INT16, 1, 1 , FALSE}, + {DHCP_TAG_SUBNET_LOCAL, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + {DHCP_TAG_BROADCAST, DHCP_OPTION_IP, 1, 1 , FALSE}, + {DHCP_TAG_DISCOVER_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + {DHCP_TAG_SUPPLY_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + {DHCP_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + {DHCP_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP, 1, 1 , FALSE}, + {DHCP_TAG_STATIC_ROUTE, DHCP_OPTION_IPPAIR, 1, -1 , FALSE}, + + {DHCP_TAG_TRAILER, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + {DHCP_TAG_ARPAGE, DHCP_OPTION_INT32, 1, 1 , FALSE}, + {DHCP_TAG_ETHER_ENCAP, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + + {DHCP_TAG_TCP_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE}, + {DHCP_TAG_KEEP_INTERVAL, DHCP_OPTION_INT32, 1, 1 , FALSE}, + {DHCP_TAG_KEEP_GARBAGE, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, + + {DHCP_TAG_NIS_DOMAIN, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_NIS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_NTP_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_VENDOR, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_NBNS, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_NBDD, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_NBTYPE, DHCP_OPTION_INT8, 1, 1 , FALSE}, + {DHCP_TAG_NBSCOPE, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_XFONT, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_XDM, DHCP_OPTION_IP, 1, -1 , FALSE}, + + {DHCP_TAG_REQUEST_IP, DHCP_OPTION_IP, 1, 1 , FALSE}, + {DHCP_TAG_LEASE, DHCP_OPTION_INT32, 1, 1 , TRUE}, + {DHCP_TAG_OVERLOAD, DHCP_OPTION_INT8, 1, 1 , TRUE}, + {DHCP_TAG_TYPE, DHCP_OPTION_INT8, 1, 1 , TRUE}, + {DHCP_TAG_SERVER_ID, DHCP_OPTION_IP, 1, 1 , TRUE}, + {DHCP_TAG_PARA_LIST, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_MESSAGE, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_MAXMSG, DHCP_OPTION_INT16, 1, 1 , FALSE}, + {DHCP_TAG_T1, DHCP_OPTION_INT32, 1, 1 , TRUE}, + {DHCP_TAG_T2, DHCP_OPTION_INT32, 1, 1 , TRUE}, + {DHCP_TAG_VENDOR_CLASS, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_CLIENT_ID, DHCP_OPTION_INT8, 2, -1 , FALSE}, + + {DHCP_TAG_NISPLUS, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_NISPLUS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, + + {DHCP_TAG_TFTP, DHCP_OPTION_INT8, 1, -1 , FALSE}, + {DHCP_TAG_BOOTFILE, DHCP_OPTION_INT8, 1, -1 , FALSE}, + + {DHCP_TAG_MOBILEIP, DHCP_OPTION_IP, 0, -1 , FALSE}, + {DHCP_TAG_SMTP, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_POP3, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_NNTP, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_WWW, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_FINGER, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_IRC, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_STTALK, DHCP_OPTION_IP, 1, -1 , FALSE}, + {DHCP_TAG_STDA, DHCP_OPTION_IP, 1, -1 , FALSE}, + + {DHCP_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8, 5, -1 , FALSE}, +}; + + +/** + Binary search the DhcpOptionFormats array to find the format + information about a specific option. + + @param Tag The option's tag. + + @return The point to the option's format, NULL if not found. + +**/ +STATIC +DHCP_OPTION_FORMAT * +DhcpFindOptionFormat ( + IN UINT8 Tag + ) +{ + INTN Left; + INTN Right; + INTN Middle; + + Left = 0; + Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1; + + while (Right >= Left) { + Middle = (Left + Right) / 2; + + if (Tag == DhcpOptionFormats[Middle].Tag) { + return &DhcpOptionFormats[Middle]; + } + + if (Tag < DhcpOptionFormats[Middle].Tag) { + Right = Middle - 1; + } else { + Left = Middle + 1; + } + } + + return NULL; +} + + +/** + Validate whether a single DHCP option is valid according to its format. + + @param Format The option's format + @param OptValue The value of the option + @param Len The length of the option value + + @return TRUE is the option is valid, otherwise FALSE. + +**/ +STATIC +BOOLEAN +DhcpOptionIsValid ( + IN DHCP_OPTION_FORMAT *Format, + IN UINT8 *OptValue, + IN INTN Len + ) +{ + INTN Unit; + INTN Occur; + INTN Index; + + Unit = 0; + + switch (Format->Type) { + case DHCP_OPTION_SWITCH: + case DHCP_OPTION_INT8: + Unit = 1; + break; + + case DHCP_OPTION_INT16: + Unit = 2; + break; + + case DHCP_OPTION_INT32: + case DHCP_OPTION_IP: + Unit = 4; + break; + + case DHCP_OPTION_IPPAIR: + Unit = 8; + break; + } + + ASSERT (Unit != 0); + + // + // Validate that the option appears in the full units. + // + if ((Len % Unit) != 0) { + return FALSE; + } + + // + // Validate the occurance of the option unit is with in [MinOccur, MaxOccur] + // + Occur = Len / Unit; + + if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) || + ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))) { + return FALSE; + } + + // + // If the option is of type switch, only 0/1 are valid values. + // + if (Format->Type == DHCP_OPTION_SWITCH) { + for (Index = 0; Index < Occur; Index++) { + if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) { + return FALSE; + } + } + } + + return TRUE; +} + + +/** + Extract the client interested options, all the parameters are + converted to host byte order. + + @param Tag The DHCP option tag + @param Len The length of the option + @param Data The value of the DHCP option + @param Para The variable to save the interested parameter + + @retval EFI_SUCCESS The DHCP option is successfully extracted. + @retval EFI_INVALID_PARAMETER The DHCP option is mal-formated + +**/ +STATIC +EFI_STATUS +DhcpGetParameter ( + IN UINT8 Tag, + IN INTN Len, + IN UINT8 *Data, + IN DHCP_PARAMETER *Para + ) +{ + switch (Tag) { + case DHCP_TAG_NETMASK: + Para->NetMask = NetGetUint32 (Data); + break; + + case DHCP_TAG_ROUTER: + // + // Return the first router to consumer which is the preferred one + // + Para->Router = NetGetUint32 (Data); + break; + + case DHCP_TAG_LEASE: + Para->Lease = NetGetUint32 (Data); + break; + + case DHCP_TAG_OVERLOAD: + Para->Overload = *Data; + + if ((Para->Overload < 1) || (Para->Overload > 3)) { + return EFI_INVALID_PARAMETER; + } + break; + + case DHCP_TAG_TYPE: + Para->DhcpType = *Data; + + if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) { + return EFI_INVALID_PARAMETER; + } + break; + + case DHCP_TAG_SERVER_ID: + Para->ServerId = NetGetUint32 (Data); + break; + + case DHCP_TAG_T1: + Para->T1 = NetGetUint32 (Data); + break; + + case DHCP_TAG_T2: + Para->T2 = NetGetUint32 (Data); + break; + } + + return EFI_SUCCESS; +} + + +/** + Inspect all the options in a single buffer. DHCP options may be contained + in several buffers, such as the BOOTP options filed, boot file or server + name. Each option buffer is required to end with DHCP_TAG_EOP. + + @param Buffer The buffer which contains DHCP options + @param BufLen The length of the buffer + @param Check The callback function for each option found + @param Context The opaque parameter for the Check + @param Overload variable to save the value of DHCP_TAG_OVERLOAD + option. + + @retval EFI_SUCCESS All the options are valid + @retval EFI_INVALID_PARAMETER The options are mal-formated. + +**/ +STATIC +EFI_STATUS +DhcpIterateBufferOptions ( + IN UINT8 *Buffer, + IN INTN BufLen, + IN DHCP_CHECK_OPTION Check, OPTIONAL + IN VOID *Context, + OUT UINT8 *Overload OPTIONAL + ) +{ + INTN Cur; + UINT8 Tag; + UINT8 Len; + + Cur = 0; + + while (Cur < BufLen) { + Tag = Buffer[Cur]; + + if (Tag == DHCP_TAG_PAD) { + Cur++; + continue; + } else if (Tag == DHCP_TAG_EOP) { + return EFI_SUCCESS; + } + + Cur++; + + if (Cur == BufLen) { + return EFI_INVALID_PARAMETER; + } + + Len = Buffer[Cur++]; + + if (Cur + Len > BufLen) { + return EFI_INVALID_PARAMETER; + } + + if ((Tag == DHCP_TAG_OVERLOAD) && (Overload != NULL)) { + if (Len != 1) { + return EFI_INVALID_PARAMETER; + } + + *Overload = Buffer[Cur]; + } + + if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) { + return EFI_INVALID_PARAMETER; + } + + Cur += Len; + } + + // + // Each option buffer is expected to end with an EOP + // + return EFI_INVALID_PARAMETER; +} + + +/** + Iterate through a DHCP message to visit each option. First inspect + all the options in the OPTION field. Then if overloaded, inspect + the options in FILENAME and SERVERNAME fields. One option may be + encoded in several places. See RFC 3396 Encoding Long Options in DHCP + + @param Packet The DHCP packet to check the options for + @param Check The callback function to be called for each option + found + @param Context The opaque parameter for Check + + @retval EFI_SUCCESS The DHCP packet's options are well formated + @retval Others The DHCP packet's options are not well formated + +**/ +EFI_STATUS +DhcpIterateOptions ( + IN EFI_DHCP4_PACKET *Packet, + IN DHCP_CHECK_OPTION Check, OPTIONAL + IN VOID *Context + ) +{ + EFI_STATUS Status; + UINT8 Overload; + + Overload = 0; + + Status = DhcpIterateBufferOptions ( + Packet->Dhcp4.Option, + Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32), + Check, + Context, + &Overload + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) { + Status = DhcpIterateBufferOptions ( + Packet->Dhcp4.Header.BootFileName, + 128, + Check, + Context, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) { + Status = DhcpIterateBufferOptions ( + Packet->Dhcp4.Header.ServerName, + 64, + Check, + Context, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Call back function to DhcpiterateOptions to compute each option's + length. It just adds the data length of all the occurances of this + Tag. Context is an array of 256 DHCP_OPTION_COUNT. + + @param Tag The current option to check + @param Len The length of the option data + @param Data The option data + @param Context The context, which is a array of 256 + DHCP_OPTION_COUNT. + + @retval EFI_SUCCESS It always returns EFI_SUCCESS. + +**/ +STATIC +EFI_STATUS +DhcpGetOptionLen ( + IN UINT8 Tag, + IN UINT8 Len, + IN UINT8 *Data, + IN VOID *Context + ) +{ + DHCP_OPTION_COUNT *OpCount; + + OpCount = (DHCP_OPTION_COUNT *) Context; + OpCount[Tag].Offset = OpCount[Tag].Offset + Len; + + return EFI_SUCCESS; +} + + +/** + Call back function to DhcpiterateOptions to consolidate each option's + data. There are maybe several occurance of the same option. + + @param Tag The option to consolidate its data + @param Len The length of option data + @param Data The data of the option's current occurance + @param Context The context, which is DHCP_OPTION_CONTEXT. This + array is just a wrap to pass THREE parameters. + + @retval EFI_SUCCESS It always returns EFI_SUCCESS + +**/ +STATIC +EFI_STATUS +DhcpFillOption ( + IN UINT8 Tag, + IN UINT8 Len, + IN UINT8 *Data, + IN VOID *Context + ) +{ + DHCP_OPTION_CONTEXT *OptContext; + DHCP_OPTION_COUNT *OptCount; + DHCP_OPTION *Options; + UINT8 *Buf; + UINT8 Index; + + OptContext = (DHCP_OPTION_CONTEXT *) Context; + + OptCount = OptContext->OpCount; + Index = OptCount[Tag].Index; + Options = OptContext->Options; + Buf = OptContext->Buf; + + if (Options[Index].Data == NULL) { + Options[Index].Tag = Tag; + Options[Index].Data = Buf + OptCount[Tag].Offset; + } + + NetCopyMem (Buf + OptCount[Tag].Offset, Data, Len); + + OptCount[Tag].Offset = OptCount[Tag].Offset + Len; + Options[Index].Len = Options[Index].Len + Len; + return EFI_SUCCESS; +} + + +/** + Parse the options of a DHCP packet. It supports RFC 3396: Encoding + Long Options in DHCP. That is, it will combine all the option value + of all the occurances of each option. + A little bit of implemenation: + It adopts the "Key indexed counting" algorithm. First, it allocates + an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded + as a UINT8. It then iterates the DHCP packet to get data length of + each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it + knows the number of present options and their length. It allocates a + array of DHCP_OPTION and a continous buffer after the array to put + all the options' data. Each option's data is pointed to by the Data + field in DHCP_OPTION structure. At last, it call DhcpIterateOptions + with DhcpFillOption to fill each option's data to its position in the + buffer. + + @param Packet The DHCP packet to parse the options + @param Count The number of valid dhcp options present in the + packet + @param OptionPoint The array that contains the DHCP options. Caller + should free it. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet. + @retval EFI_INVALID_PARAMETER The options are mal-formated + @retval EFI_SUCCESS The options are parsed into OptionPoint + +**/ +EFI_STATUS +DhcpParseOption ( + IN EFI_DHCP4_PACKET *Packet, + OUT INTN *Count, + OUT DHCP_OPTION **OptionPoint + ) +{ + DHCP_OPTION_CONTEXT Context; + DHCP_OPTION *Options; + DHCP_OPTION_COUNT *OptCount; + EFI_STATUS Status; + UINT16 TotalLen; + INTN OptNum; + INTN Index; + + ASSERT ((Count != NULL) && (OptionPoint != NULL)); + + // + // First compute how many options and how long each option is + // with the "Key indexed counting" algorithms. + // + OptCount = NetAllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT)); + + if (OptCount == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Before the loop, Offset is the length of the option. After loop, + // OptCount[Index].Offset specifies the offset into the continuous + // option value buffer to put the data. + // + TotalLen = 0; + OptNum = 0; + + for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { + if (OptCount[Index].Offset != 0) { + OptCount[Index].Index = (UINT8) OptNum; + + TotalLen = TotalLen + OptCount[Index].Offset; + OptCount[Index].Offset = TotalLen - OptCount[Index].Offset; + + OptNum++; + } + } + + *Count = OptNum; + *OptionPoint = NULL; + + if (OptNum == 0) { + goto ON_EXIT; + } + + // + // Allocate a buffer to hold the DHCP options, and after that, a + // continuous buffer to put all the options' data. + // + Options = NetAllocateZeroPool (OptNum * sizeof (DHCP_OPTION) + TotalLen); + + if (Options == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Context.OpCount = OptCount; + Context.Options = Options; + Context.Buf = (UINT8 *) (Options + OptNum); + + Status = DhcpIterateOptions (Packet, DhcpFillOption, &Context); + + if (EFI_ERROR (Status)) { + NetFreePool (Options); + goto ON_EXIT; + } + + *OptionPoint = Options; + +ON_EXIT: + NetFreePool (OptCount); + return Status; +} + + +/** + Validate the packet's options. If necessary, allocate + and fill in the interested parameters. + + @param Packet The packet to validate the options + @param Para The variable to save the DHCP parameters. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet. + @retval EFI_INVALID_PARAMETER The options are mal-formated + @retval EFI_SUCCESS The options are parsed into OptionPoint + +**/ +EFI_STATUS +DhcpValidateOptions ( + IN EFI_DHCP4_PACKET *Packet, + OUT DHCP_PARAMETER **Para OPTIONAL + ) +{ + DHCP_PARAMETER Parameter; + DHCP_OPTION_FORMAT *Format; + DHCP_OPTION *AllOption; + DHCP_OPTION *Option; + EFI_STATUS Status; + BOOLEAN Updated; + INTN Count; + INTN Index; + + if (Para != NULL) { + *Para = NULL; + } + + AllOption = NULL; + Status = DhcpParseOption (Packet, &Count, &AllOption); + + if (EFI_ERROR (Status) || (Count == 0)) { + return Status; + } + + Updated = FALSE; + NetZeroMem (&Parameter, sizeof (Parameter)); + + for (Index = 0; Index < Count; Index++) { + Option = &AllOption[Index]; + + // + // Find the format of the option then validate it. + // + Format = DhcpFindOptionFormat (Option->Tag); + + if (Format == NULL) { + continue; + } + + if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + // + // Get the client interested parameters + // + if (Format->Alert && (Para != NULL)) { + Updated = TRUE; + Status = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + } + + if (Updated && (Para != NULL)) { + if ((*Para = NetAllocatePool (sizeof (DHCP_PARAMETER))) == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (*Para, &Parameter, sizeof (DHCP_PARAMETER)); + } + +ON_EXIT: + NetFreePool (AllOption); + return Status; +} + + + +/** + Append an option to the memory, if the option is longer than + 255 bytes, splits it into several options. + + @param Buf The buffer to append the option to + @param Tag The option's tag + @param DataLen The length of the option's data + @param Data The option's data + + @return The position to append the next option + +**/ +UINT8 * +DhcpAppendOption ( + IN UINT8 *Buf, + IN UINT8 Tag, + IN UINT16 DataLen, + IN UINT8 *Data + ) +{ + INTN Index; + INTN Len; + + ASSERT (DataLen != 0); + + for (Index = 0; Index < (DataLen + 254) / 255; Index++) { + Len = NET_MIN (255, DataLen - Index * 255); + + *(Buf++) = Tag; + *(Buf++) = (UINT8) Len; + NetCopyMem (Buf, Data + Index * 255, Len); + + Buf += Len; + } + + return Buf; +} + + +/** + Build a new DHCP packet from a seed packet. Options may be deleted or + appended. The caller should free the NewPacket when finished using it. + + @param SeedPacket The seed packet to start with + @param DeleteCount The number of options to delete + @param DeleteList The options to delete from the packet + @param AppendCount The number of options to append + @param AppendList The options to append to the packet + @param NewPacket The new packet, allocated and built by this + function. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory + @retval EFI_SUCCESS The packet is build. + +**/ +EFI_STATUS +DhcpBuild ( + IN EFI_DHCP4_PACKET *SeedPacket, + IN UINT32 DeleteCount, + IN UINT8 *DeleteList OPTIONAL, + IN UINT32 AppendCount, + IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket + ) +{ + DHCP_OPTION *Mark; + DHCP_OPTION *SeedOptions; + EFI_DHCP4_PACKET *Packet; + EFI_STATUS Status; + INTN Count; + UINT32 Index; + UINT32 Len; + UINT8 *Buf; + + // + // Use an array of DHCP_OPTION to mark the existance + // and position of each valid options. + // + Mark = NetAllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS); + + if (Mark == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { + Mark[Index].Tag = (UINT8) Index; + Mark[Index].Len = 0; + } + + // + // Get list of the options from the seed packet, then put + // them to the mark array according to their tags. + // + SeedOptions = NULL; + Status = DhcpParseOption (SeedPacket, &Count, &SeedOptions); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + for (Index = 0; Index < (UINT32) Count; Index++) { + Mark[SeedOptions[Index].Tag] = SeedOptions[Index]; + } + + // + // Mark the option's length is zero if it is in the DeleteList. + // + for (Index = 0; Index < DeleteCount; Index++) { + Mark[DeleteList[Index]].Len = 0; + } + + // + // Add or replace the option if it is in the append list. + // + for (Index = 0; Index < AppendCount; Index++) { + Mark[AppendList[Index]->OpCode].Len = AppendList[Index]->Length; + Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data; + } + + // + // compute the new packet length. No need to add 1 byte for + // EOP option since EFI_DHCP4_PACKET includes one extra byte + // for option. It is necessary to split the option if it is + // longer than 255 bytes. + // + Len = sizeof (EFI_DHCP4_PACKET); + + for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { + if (Mark[Index].Len != 0) { + Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len; + } + } + + Status = EFI_OUT_OF_RESOURCES; + Packet = (EFI_DHCP4_PACKET *) NetAllocatePool (Len); + + if (Packet == NULL) { + goto ON_ERROR; + } + + Packet->Size = Len; + Packet->Length = 0; + CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (EFI_DHCP4_HEADER)); + Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC; + Buf = Packet->Dhcp4.Option; + + for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { + if (Mark[Index].Len != 0) { + Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data); + } + } + + *(Buf++) = DHCP_TAG_EOP; + Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32) + + (UINT32) (Buf - Packet->Dhcp4.Option); + + *NewPacket = Packet; + Status = EFI_SUCCESS; + +ON_ERROR: + if (SeedOptions != NULL) { + NetFreePool (SeedOptions); + } + + NetFreePool (Mark); + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h new file mode 100644 index 0000000000..a6a485bece --- /dev/null +++ b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.h @@ -0,0 +1,266 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Dhcp4Option.h + +Abstract: + + To validate, parse and process the DHCP options + + +**/ + +#ifndef __EFI_DHCP4_OPTION_H__ +#define __EFI_DHCP4_OPTION_H__ + +// +// DHCP option tags (types) +// +enum { + // + // RFC1497 vendor extensions + // + DHCP_TAG_PAD = 0, // Pad Option + DHCP_TAG_EOP = 255, // End Option + DHCP_TAG_NETMASK = 1, // Subnet Mask + DHCP_TAG_TIME_OFFSET = 2, // Time Offset from UTC + DHCP_TAG_ROUTER = 3, // Router option, + DHCP_TAG_TIME_SERVER = 4, // Time Server + DHCP_TAG_NAME_SERVER = 5, // Name Server + DHCP_TAG_DNS_SERVER = 6, // Domain Name Server + DHCP_TAG_LOG_SERVER = 7, // Log Server + DHCP_TAG_COOKIE_SERVER = 8, // Cookie Server + DHCP_TAG_LPR_SERVER = 9, // LPR Print Server + DHCP_TAG_IMPRESS_SERVER = 10, // Impress Server + DHCP_TAG_RL_SERVER = 11, // Resource Location Server + DHCP_TAG_HOSTNAME = 12, // Host Name + DHCP_TAG_BOOTFILE_LEN = 13, // Boot File Size + DHCP_TAG_DUMP = 14, // Merit Dump File + DHCP_TAG_DOMAINNAME = 15, // Domain Name + DHCP_TAG_SWAP_SERVER = 16, // Swap Server + DHCP_TAG_ROOTPATH = 17, // Root path + DHCP_TAG_EXTEND_PATH = 18, // Extensions Path + + // + // IP Layer Parameters per Host + // + DHCP_TAG_IPFORWARD = 19, // IP Forwarding Enable/Disable + DHCP_TAG_NONLOCAL_SRR = 20, // on-Local Source Routing Enable/Disable + DHCP_TAG_POLICY_SRR = 21, // Policy Filter + DHCP_TAG_EMTU = 22, // Maximum Datagram Reassembly Size + DHCP_TAG_TTL = 23, // Default IP Time-to-live + DHCP_TAG_PATHMTU_AGE = 24, // Path MTU Aging Timeout + DHCP_TAG_PATHMTU_PLATEAU = 25, // Path MTU Plateau Table + + // + // IP Layer Parameters per Interface + // + DHCP_TAG_IFMTU = 26, // Interface MTU + DHCP_TAG_SUBNET_LOCAL = 27, // All Subnets are Local + DHCP_TAG_BROADCAST = 28, // Broadcast Address + DHCP_TAG_DISCOVER_MASK = 29, // Perform Mask Discovery + DHCP_TAG_SUPPLY_MASK = 30, // Mask Supplier + DHCP_TAG_DISCOVER_ROUTE = 31, // Perform Router Discovery + DHCP_TAG_ROUTER_SOLICIT = 32, // Router Solicitation Address + DHCP_TAG_STATIC_ROUTE = 33, // Static Route + + // + // Link Layer Parameters per Interface + // + DHCP_TAG_TRAILER = 34, // Trailer Encapsulation + DHCP_TAG_ARPAGE = 35, // ARP Cache Timeout + DHCP_TAG_ETHER_ENCAP = 36, // Ethernet Encapsulation + + // + // TCP Parameters + // + DHCP_TAG_TCP_TTL = 37, // TCP Default TTL + DHCP_TAG_KEEP_INTERVAL = 38, // TCP Keepalive Interval + DHCP_TAG_KEEP_GARBAGE = 39, // TCP Keepalive Garbage + + // + // Application and Service Parameters + // + DHCP_TAG_NIS_DOMAIN = 40, // Network Information Service Domain + DHCP_TAG_NIS_SERVER = 41, // Network Information Servers + DHCP_TAG_NTP_SERVER = 42, // Network Time Protocol Servers + DHCP_TAG_VENDOR = 43, // Vendor Specific Information + DHCP_TAG_NBNS = 44, // NetBIOS over TCP/IP Name Server + DHCP_TAG_NBDD = 45, // NetBIOS Datagram Distribution Server + DHCP_TAG_NBTYPE = 46, // NetBIOS over TCP/IP Node Type + DHCP_TAG_NBSCOPE = 47, // NetBIOS over TCP/IP Scope + DHCP_TAG_XFONT = 48, // X Window System Font Server + DHCP_TAG_XDM = 49, // X Window System Display Manager + DHCP_TAG_NISPLUS = 64, // Network Information Service+ Domain + DHCP_TAG_NISPLUS_SERVER = 65, // Network Information Service+ Servers + DHCP_TAG_MOBILEIP = 68, // Mobile IP Home Agent + DHCP_TAG_SMTP = 69, // Simple Mail Transport Protocol Server + DHCP_TAG_POP3 = 70, // Post Office Protocol (POP3) Server + DHCP_TAG_NNTP = 71, // Network News Transport Protocol Server + DHCP_TAG_WWW = 72, // Default World Wide Web (WWW) Server + DHCP_TAG_FINGER = 73, // Default Finger Server + DHCP_TAG_IRC = 74, // Default Internet Relay Chat (IRC) Server + DHCP_TAG_STTALK = 75, // StreetTalk Server + DHCP_TAG_STDA = 76, // StreetTalk Directory Assistance Server + DHCP_TAG_CLASSLESS_ROUTE = 121, // Classless Route + + // + // DHCP Extensions + // + DHCP_TAG_REQUEST_IP = 50, // Requested IP Address + DHCP_TAG_LEASE = 51, // IP Address Lease Time + DHCP_TAG_OVERLOAD = 52, // Option Overload + DHCP_TAG_TFTP = 66, // TFTP server name + DHCP_TAG_BOOTFILE = 67, // Bootfile name + DHCP_TAG_TYPE = 53, // DHCP Message Type + DHCP_TAG_SERVER_ID = 54, // Server Identifier + DHCP_TAG_PARA_LIST = 55, // Parameter Request List + DHCP_TAG_MESSAGE = 56, // Message + DHCP_TAG_MAXMSG = 57, // Maximum DHCP Message Size + DHCP_TAG_T1 = 58, // Renewal (T1) Time Value + DHCP_TAG_T2 = 59, // Rebinding (T2) Time Value + DHCP_TAG_VENDOR_CLASS = 60, // Vendor class identifier + DHCP_TAG_CLIENT_ID = 61, // Client-identifier +}; + +enum { + DHCP_OPTION_MAGIC = 0x63538263, // Network byte order + DHCP_MAX_OPTIONS = 256, + + // + // DHCP option types, this is used to validate the DHCP options. + // + DHCP_OPTION_SWITCH = 1, + DHCP_OPTION_INT8, + DHCP_OPTION_INT16, + DHCP_OPTION_INT32, + DHCP_OPTION_IP, + DHCP_OPTION_IPPAIR, + + // + // Value of DHCP overload option + // + DHCP_OVERLOAD_FILENAME = 1, + DHCP_OVERLOAD_SVRNAME = 2, + DHCP_OVERLOAD_BOTH = 3, +}; + +// +// The DHCP option structure. This structure extends the EFI_DHCP_OPTION +// structure to support options longer than 255 bytes, such as classless route. +// +typedef struct { + UINT8 Tag; + UINT16 Len; + UINT8 *Data; +} DHCP_OPTION; + +// +// Structures used to parse the DHCP options with RFC3396 support. +// +typedef struct { + UINT8 Index; + UINT16 Offset; +} DHCP_OPTION_COUNT; + +typedef struct { + DHCP_OPTION_COUNT *OpCount; + DHCP_OPTION *Options; + UINT8 *Buf; +} DHCP_OPTION_CONTEXT; + +// +// The options that matters to DHCP driver itself. The user of +// DHCP clients may be interested in other options, such as +// classless route, who can parse the DHCP offer to get them. +// +typedef struct { + IP4_ADDR NetMask; // DHCP_TAG_NETMASK + IP4_ADDR Router; // DHCP_TAG_ROUTER, only the first router is used + + // + // DHCP specific options + // + UINT8 DhcpType; // DHCP_TAG_TYPE + UINT8 Overload; // DHCP_TAG_OVERLOAD + IP4_ADDR ServerId; // DHCP_TAG_SERVER_ID + UINT32 Lease; // DHCP_TAG_LEASE + UINT32 T1; // DHCP_TAG_T1 + UINT32 T2; // DHCP_TAG_T2 +} DHCP_PARAMETER; + +// +// Structure used to describe and validate the format of DHCP options. +// Type is the options' data type, such as DHCP_OPTION_INT8. MinOccur +// is the minium occurance of this data type. MaxOccur is defined +// similarly. If MaxOccur is -1, it means that there is no limit on the +// maximum occurance. Alert tells whether DHCP client should further +// inspect the option to parse DHCP_PARAMETER. +// +typedef struct { + UINT8 Tag; + INTN Type; + INTN MinOccur; + INTN MaxOccur; + BOOLEAN Alert; +} DHCP_OPTION_FORMAT; + +typedef +EFI_STATUS +(*DHCP_CHECK_OPTION) ( + IN UINT8 Tag, + IN UINT8 Len, + IN UINT8 *Data, + IN VOID *Context + ); + +EFI_STATUS +DhcpIterateOptions ( + IN EFI_DHCP4_PACKET *Packet, + IN DHCP_CHECK_OPTION Check, OPTIONAL + IN VOID *Context + ); + +EFI_STATUS +DhcpValidateOptions ( + IN EFI_DHCP4_PACKET *Packet, + OUT DHCP_PARAMETER **Para OPTIONAL + ); + +EFI_STATUS +DhcpParseOption ( + IN EFI_DHCP4_PACKET *Packet, + OUT INTN *Count, + OUT DHCP_OPTION **OptionPoint + ); + +UINT8 * +DhcpAppendOption ( + IN UINT8 *Buf, + IN UINT8 Tag, + IN UINT16 DataLen, + IN UINT8 *Data + ); + +EFI_STATUS +DhcpBuild ( + IN EFI_DHCP4_PACKET *SeedPacket, + IN UINT32 DeleteCount, + IN UINT8 *DeleteList OPTIONAL, + IN UINT32 AppendCount, + IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c index 326fb4dc33..a1bae0f29d 100644 --- a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c +++ b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4Config.c @@ -590,6 +590,8 @@ Ip4ConfigOnDhcp4Complete ( EFI_STATUS Status; BOOLEAN Perment; IP4_ADDR Subnet; + IP4_ADDR Ip1; + IP4_ADDR Ip2; Instance = (IP4_CONFIG_INSTANCE *) Context; ASSERT (Instance->Dhcp4 != NULL); @@ -641,20 +643,24 @@ Ip4ConfigOnDhcp4Complete ( // Ip4Config->RouteTableSize = 1; - Subnet = EFI_NTOHL (Dhcp4Mode.ClientAddress) & EFI_NTOHL (Dhcp4Mode.SubnetMask); + NetCopyMem (&Ip1, &Dhcp4Mode.ClientAddress, sizeof (IP4_ADDR)); + NetCopyMem (&Ip2, &Dhcp4Mode.SubnetMask, sizeof (IP4_ADDR)); + + Subnet = Ip1 & Ip2; - EFI_IP4 (Ip4Config->RouteTable[0].SubnetAddress) = HTONL (Subnet); - Ip4Config->RouteTable[0].SubnetMask = Dhcp4Mode.SubnetMask; - EFI_IP4 (Ip4Config->RouteTable[0].GatewayAddress) = 0; + NetCopyMem (&Ip4Config->RouteTable[0].SubnetAddress, &Subnet, sizeof (EFI_IPv4_ADDRESS)); + NetCopyMem (&Ip4Config->RouteTable[0].SubnetMask, &Dhcp4Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + NetZeroMem (&Ip4Config->RouteTable[0].GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); // // Create a route if there is a default router. // - if (EFI_IP4 (Dhcp4Mode.RouterAddress) != 0) { - Ip4Config->RouteTableSize = 2; - EFI_IP4 (Ip4Config->RouteTable[1].SubnetAddress) = 0; - EFI_IP4 (Ip4Config->RouteTable[1].SubnetMask) = 0; - Ip4Config->RouteTable[1].GatewayAddress = Dhcp4Mode.RouterAddress; + if (!EFI_IP4_EQUAL (Dhcp4Mode.RouterAddress, mZeroIp4Addr)) { + Ip4Config->RouteTableSize = 2; + + NetZeroMem (&Ip4Config->RouteTable[1].SubnetAddress, sizeof (EFI_IPv4_ADDRESS)); + NetZeroMem (&Ip4Config->RouteTable[1].SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + NetCopyMem (&Ip4Config->RouteTable[1].GatewayAddress, &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); } Instance->Result = EFI_SUCCESS; diff --git a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c index 265135a117..3d857b047e 100644 --- a/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c +++ b/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDriver.c @@ -60,7 +60,6 @@ EfiIp4ConfigUnload ( return NetLibDefaultUnload (ImageHandle); } -//@MT: EFI_DRIVER_ENTRY_POINT (Ip4ConfigDriverEntryPoint) EFI_STATUS Ip4ConfigDriverEntryPoint ( diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c new file mode 100644 index 0000000000..728887587e --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/ComponentName.c @@ -0,0 +1,166 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + +#include "Ip4Impl.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +Ip4ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +Ip4ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gIp4ComponentName = { + Ip4ComponentNameGetDriverName, + Ip4ComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mIp4DriverNameTable[] = { + { + "eng", + L"IP4 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +Ip4ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable + name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the driver name + that that the caller is requesting, and it must match + one of the languages specified in SupportedLanguages. + The number of languages supported by a driver is up to + the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode + string is the name of the driver specified by This in the + language specified by Language. + + Returns: + EFI_SUCCES - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gIp4ComponentName.SupportedLanguages, + mIp4DriverNameTable, + DriverName + ); + +} + +EFI_STATUS +EFIAPI +Ip4ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of + the controller that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language + specified by Language from the point of view of the + driver specified by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not + a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c new file mode 100644 index 0000000000..ac339bee52 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.c @@ -0,0 +1,421 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Common.c + +Abstract: + + +**/ + +#include "Ip4Impl.h" + + +/** + Return the cast type (Unicast/Boradcast) specific to a + interface. All the addresses are host byte ordered. + + @param IpAddr The IP address to classify in host byte order + @param IpIf The interface that IpAddr received from + + @return The cast type of this IP address specific to the interface. + @retval IP4_LOCAL_HOST The IpAddr equals to the interface's address + @retval IP4_SUBNET_BROADCAST The IpAddr is a directed subnet boradcast to the + interface + @retval IP4_NET_BROADCAST The IpAddr is a network broadcast to the interface + +**/ +INTN +Ip4GetNetCast ( + IN IP4_ADDR IpAddr, + IN IP4_INTERFACE *IpIf + ) +{ + if (IpAddr == IpIf->Ip) { + return IP4_LOCAL_HOST; + + } else if (IpAddr == IpIf->SubnetBrdcast) { + return IP4_SUBNET_BROADCAST; + + } else if (IpAddr == IpIf->NetBrdcast) { + return IP4_NET_BROADCAST; + + } + + return 0; +} + + +/** + Find the cast type of the packet related to the local host. + This isn't the same as link layer cast type. For example, DHCP + server may send local broadcast to the local unicast MAC. + + @param IpSb The IP4 service binding instance that received the + packet + @param Dst The destination address in the packet (host byte + order) + @param Src The source address in the packet (host byte order) + + @return The cast type for the Dst, it will return on the first non-promiscuous + @return cast type to a configured interface. If the packet doesn't match any of + @return the interface, multicast address and local broadcast address are checked. + +**/ +INTN +Ip4GetHostCast ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN IP4_ADDR Src + ) +{ + NET_LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + INTN Type; + INTN Class; + + Type = 0; + + if (IpSb->MnpConfigData.EnablePromiscuousReceive) { + Type = IP4_PROMISCUOUS; + } + + // + // Go through the interface list of the IP service, most likely. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + // + // Skip the unconfigured interface and invalid source address: + // source address can't be broadcast. + // + if (!IpIf->Configured || IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) { + continue; + } + + if ((Class = Ip4GetNetCast (Dst, IpIf)) > Type) { + return Class; + } + } + + // + // If it is local broadcast address. The source address must + // be a unicast address on one of the direct connected network. + // If it is a multicast address, accept it only if we are in + // the group. + // + if (Dst == IP4_ALLONE_ADDRESS) { + IpIf = Ip4FindNet (IpSb, Src); + + if (IpIf && !IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) { + return IP4_LOCAL_BROADCAST; + } + + } else if (IP4_IS_MULTICAST (Dst) && Ip4FindGroup (&IpSb->IgmpCtrl, Dst)) { + return IP4_MULTICAST; + } + + return Type; +} + + +/** + Find an interface whose configured IP address is Ip + + @param IpSb The IP4 service binding instance + @param Ip The Ip address (host byte order) to find + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindInterface ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip + ) +{ + NET_LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && (IpIf->Ip == Ip)) { + return IpIf; + } + } + + return NULL; +} + + +/** + Find an interface that Ip is on that connected network. + + @param IpSb The IP4 service binding instance + @param Ip The Ip address (host byte order) to find + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindNet ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip + ) +{ + NET_LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && IP4_NET_EQUAL (Ip, IpIf->Ip, IpIf->SubnetMask)) { + return IpIf; + } + } + + return NULL; +} + + +/** + Find an interface of the service with the same Ip/Netmask pair. + + @param IpSb Ip4 service binding instance + @param Ip The Ip adress to find (host byte order) + @param Netmask The network to find (host byte order) + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindStationAddress ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip, + IN IP4_ADDR Netmask + ) +{ + NET_LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && (IpIf->Ip == Ip) && (IpIf->SubnetMask == Netmask)) { + return IpIf; + } + } + + return NULL; +} + + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address in stead of + hard code the NIC to be Ethernet. + + @param Mnp The Mnp instance to get the MAC address. + @param Multicast The multicast IP address to translate. + @param Mac The buffer to hold the translated address. + + @return Returns EFI_SUCCESS if the multicast IP is successfully + @return translated to a multicast MAC address. Otherwise some error. + +**/ +EFI_STATUS +Ip4GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN IP4_ADDR Multicast, + OUT EFI_MAC_ADDRESS *Mac + ) +{ + EFI_IP_ADDRESS EfiIp; + + EFI_IP4 (EfiIp.v4) = HTONL (Multicast); + return Mnp->McastIpToMac (Mnp, FALSE, &EfiIp, Mac); +} + + +/** + Convert the multibyte field in IP header's byter order. + In spite of its name, it can also be used to convert from + host to network byte order. + + @param Head The IP head to convert + + @return Point to the converted IP head + +**/ +IP4_HEAD * +Ip4NtohHead ( + IN IP4_HEAD *Head + ) +{ + Head->TotalLen = NTOHS (Head->TotalLen); + Head->Id = NTOHS (Head->Id); + Head->Fragment = NTOHS (Head->Fragment); + Head->Src = NTOHL (Head->Src); + Head->Dst = NTOHL (Head->Dst); + + return Head; +} + + +/** + Set the Ip4 variable data. + + @param IpSb Ip4 service binding instance + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +Ip4SetVariableData ( + IN IP4_SERVICE *IpSb + ) +{ + UINT32 NumConfiguredInstance; + NET_LIST_ENTRY *Entry; + UINTN VariableDataSize; + EFI_IP4_VARIABLE_DATA *Ip4VariableData; + EFI_IP4_ADDRESS_PAIR *Ip4AddressPair; + IP4_PROTOCOL *IpInstance; + CHAR16 *NewMacString; + EFI_STATUS Status; + + NumConfiguredInstance = 0; + + // + // Go through the children list to count the configured children. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE); + + if (IpInstance->State == IP4_STATE_CONFIGED) { + NumConfiguredInstance++; + } + } + + // + // Calculate the size of the Ip4VariableData. As there may be no IP child, + // we should add extra buffer for the address paris only if the number of configured + // children is more than 1. + // + VariableDataSize = sizeof (EFI_IP4_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_IP4_ADDRESS_PAIR) * (NumConfiguredInstance - 1); + } + + Ip4VariableData = NetAllocatePool (VariableDataSize); + if (Ip4VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip4VariableData->DriverHandle = IpSb->Image; + Ip4VariableData->AddressCount = NumConfiguredInstance; + + Ip4AddressPair = &Ip4VariableData->AddressPairs[0]; + + // + // Go through the children list to fill the configured children's address pairs. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE); + + if (IpInstance->State == IP4_STATE_CONFIGED) { + Ip4AddressPair->InstanceHandle = IpInstance->Handle; + EFI_IP4 (Ip4AddressPair->Ip4Address) = NTOHL (IpInstance->Interface->Ip); + EFI_IP4 (Ip4AddressPair->SubnetMask) = NTOHL (IpInstance->Interface->SubnetMask); + + Ip4AddressPair++; + } + } + + // + // Get the mac string. + // + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (IpSb->MacString != NULL) { + // + // The variable is set already, we're going to update it. + // + if (StrCmp (IpSb->MacString, NewMacString) != 0) { + // + // The mac address is changed, delete the previous variable first. + // + gRT->SetVariable ( + IpSb->MacString, + &gEfiIp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + } + + NetFreePool (IpSb->MacString); + } + + IpSb->MacString = NewMacString; + + Status = gRT->SetVariable ( + IpSb->MacString, + &gEfiIp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableDataSize, + (VOID *) Ip4VariableData + ); + +ON_ERROR: + + NetFreePool (Ip4VariableData); + + return Status; +} + + +/** + Clear the variable and free the resource. + + @param IpSb Ip4 service binding instance + + @return None. + +**/ +VOID +Ip4ClearVariableData ( + IN IP4_SERVICE *IpSb + ) +{ + ASSERT (IpSb->MacString != NULL); + + gRT->SetVariable ( + IpSb->MacString, + &gEfiIp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + + NetFreePool (IpSb->MacString); + IpSb->MacString = NULL; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h new file mode 100644 index 0000000000..59e5c11786 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Common.h @@ -0,0 +1,143 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Common.h + +Abstract: + + Common definition for IP4. + + +**/ + +#ifndef __EFI_IP4_COMMON_H__ +#define __EFI_IP4_COMMON_H__ + +typedef struct _IP4_INTERFACE IP4_INTERFACE; +typedef struct _IP4_PROTOCOL IP4_PROTOCOL; +typedef struct _IP4_SERVICE IP4_SERVICE; + + +enum { + IP4_ETHER_PROTO = 0x0800, + + IP4_PROTO_ICMP = 0x01, + IP4_PROTO_IGMP = 0x02, + + // + // The packet is received as link level broadcast/multicast/promiscuous. + // + IP4_LINK_BROADCAST = 0x00000001, + IP4_LINK_MULTICAST = 0x00000002, + IP4_LINK_PROMISC = 0x00000004, + + // + // IP4 address cast type classfication. Keep it true that any + // type bigger than or equal to LOCAL_BROADCAST is broadcast. + // + IP4_PROMISCUOUS = 1, + IP4_LOCAL_HOST, + IP4_MULTICAST, + IP4_LOCAL_BROADCAST, // Destination is 255.255.255.255 + IP4_SUBNET_BROADCAST, + IP4_NET_BROADCAST, + + // + // IP4 header flags + // + IP4_HEAD_DF_MASK = 0x4000, + IP4_HEAD_MF_MASK = 0x2000, + IP4_HEAD_OFFSET_MASK = 0x1fff, +}; + +#define IP4_ALLZERO_ADDRESS 0x00000000u +#define IP4_ALLONE_ADDRESS 0xFFFFFFFFu +#define IP4_ALLSYSTEM_ADDRESS 0xE0000001u +#define IP4_ALLROUTER_ADDRESS 0xE0000002u + +// +// Compose the fragment field to be used in the IP4 header. +// +#define IP4_HEAD_FRAGMENT_FIELD(Df, Mf, Offset) \ + ((UINT16)(((Df) ? 0x4000 : 0) | ((Mf) ? 0x2000 : 0) | (((Offset) >> 3) & 0x1fff))) + +#define IP4_LAST_FRAGMENT(FragmentField) \ + (((FragmentField) & IP4_HEAD_MF_MASK) == 0) + +#define IP4_FIRST_FRAGMENT(FragmentField) \ + ((BOOLEAN)(((FragmentField) & IP4_HEAD_OFFSET_MASK) == 0)) + +#define IP4_IS_BROADCAST(CastType) ((CastType) >= IP4_LOCAL_BROADCAST) + +// +// Conver the Microsecond to second. IP transmit/receive time is +// in the unit of microsecond. IP ticks once per second. +// +#define IP4_US_TO_SEC(Us) (((Us) + 999999) / 1000000) + +INTN +Ip4GetNetCast ( + IN IP4_ADDR IpAddr, + IN IP4_INTERFACE *IpIf + ); + +INTN +Ip4GetHostCast ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN IP4_ADDR Src + ); + +IP4_INTERFACE * +Ip4FindInterface ( + IN IP4_SERVICE *IpService, + IN IP4_ADDR Addr + ); + +IP4_INTERFACE * +Ip4FindNet ( + IN IP4_SERVICE *IpService, + IN IP4_ADDR Addr + ); + +IP4_INTERFACE * +Ip4FindStationAddress ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip, + IN IP4_ADDR Netmask + ); + +EFI_STATUS +Ip4GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN IP4_ADDR Multicast, + OUT EFI_MAC_ADDRESS *Mac + ); + +IP4_HEAD * +Ip4NtohHead ( + IN IP4_HEAD *Head + ); + +EFI_STATUS +Ip4SetVariableData ( + IN IP4_SERVICE *IpSb + ); + +VOID +Ip4ClearVariableData ( + IN IP4_SERVICE *IpSb + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c new file mode 100644 index 0000000000..eec6d69bd3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.c @@ -0,0 +1,932 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Ip4Driver.c + +Abstract: + + The driver binding and service binding protocol for IP4 driver. + + +**/ + +#include "Ip4Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding = { + Ip4DriverBindingSupported, + Ip4DriverBindingStart, + Ip4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +//@MT: EFI_DRIVER_ENTRY_POINT (Ip4DriverEntryPoint) + +EFI_STATUS +EFIAPI +Ip4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The entry point for IP4 driver which install the driver + binding and component name protocol on its image. + +Arguments: + + ImageHandle - The image handle of the driver + SystemTable - The system table + +Returns: + + EFI_SUCCESS if the driver binding and component name protocols + are successfully installed, otherwise if failed. + +--*/ +{ + return NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gIp4DriverBinding, + ImageHandle, + &gIp4ComponentName, + NULL, + NULL + ); +} + + +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +Ip4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the MNP service binding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Test for the Arp service binding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiArpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +STATIC +EFI_STATUS +Ip4CleanService ( + IN IP4_SERVICE *IpSb + ); + + +/** + Create a new IP4 driver service binding protocol + + @param Controller The controller that has MNP service binding + installed + @param ImageHandle The IP4 driver's image handle + @param Service The variable to receive the newly created IP4 + service. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource + @retval EFI_SUCCESS A new IP4 service binding private is created. + +**/ +STATIC +EFI_STATUS +Ip4CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT IP4_SERVICE **Service + ) +{ + IP4_SERVICE *IpSb; + EFI_STATUS Status; + + ASSERT (Service != NULL); + + *Service = NULL; + + // + // allocate a service private data then initialize all the filed to + // empty resources, so if any thing goes wrong when allocating + // resources, Ip4CleanService can be called to clean it up. + // + IpSb = NetAllocatePool (sizeof (IP4_SERVICE)); + + if (IpSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IpSb->Signature = IP4_SERVICE_SIGNATURE; + IpSb->ServiceBinding.CreateChild = Ip4ServiceBindingCreateChild; + IpSb->ServiceBinding.DestroyChild = Ip4ServiceBindingDestroyChild; + IpSb->State = IP4_SERVICE_UNSTARTED; + IpSb->InDestory = FALSE; + + IpSb->NumChildren = 0; + NetListInit (&IpSb->Children); + + NetListInit (&IpSb->Interfaces); + IpSb->DefaultInterface = NULL; + IpSb->DefaultRouteTable = NULL; + + Ip4InitAssembleTable (&IpSb->Assemble); + + IpSb->IgmpCtrl.Igmpv1QuerySeen = 0; + NetListInit (&IpSb->IgmpCtrl.Groups); + + IpSb->Image = ImageHandle; + IpSb->Controller = Controller; + + IpSb->MnpChildHandle = NULL; + IpSb->Mnp = NULL; + + IpSb->MnpConfigData.ReceivedQueueTimeoutValue = 0; + IpSb->MnpConfigData.TransmitQueueTimeoutValue = 0; + IpSb->MnpConfigData.ProtocolTypeFilter = IP4_ETHER_PROTO; + IpSb->MnpConfigData.EnableUnicastReceive = TRUE; + IpSb->MnpConfigData.EnableMulticastReceive = TRUE; + IpSb->MnpConfigData.EnableBroadcastReceive = TRUE; + IpSb->MnpConfigData.EnablePromiscuousReceive = FALSE; + IpSb->MnpConfigData.FlushQueuesOnReset = TRUE; + IpSb->MnpConfigData.EnableReceiveTimestamps = FALSE; + IpSb->MnpConfigData.DisableBackgroundPolling = FALSE; + + NetZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE)); + + IpSb->Timer = NULL; + IpSb->Ip4Config = NULL; + IpSb->DoneEvent = NULL; + IpSb->ReconfigEvent = NULL; + + // + // Create various resources. First create the route table, timer + // event and MNP child. IGMP, interface's initialization depend + // on the MNP child. + // + IpSb->DefaultRouteTable = Ip4CreateRouteTable (); + + if (IpSb->DefaultRouteTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip4TimerTicking, + IpSb, + &IpSb->Timer + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = NetLibCreateServiceChild ( + Controller, + ImageHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &IpSb->MnpChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &IpSb->Mnp, + ImageHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip4ServiceConfigMnp (IpSb, TRUE); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip4InitIgmp (IpSb); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->DefaultInterface = Ip4CreateInterface (IpSb->Mnp, Controller, ImageHandle); + + if (IpSb->DefaultInterface == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + NetListInsertHead (&IpSb->Interfaces, &IpSb->DefaultInterface->Link); + + IpSb->MacString = NULL; + + *Service = IpSb; + return EFI_SUCCESS; + +ON_ERROR: + Ip4CleanService (IpSb); + NetFreePool (IpSb); + + return Status; +} + + +/** + Clean up a IP4 service binding instance. It will release all + the resource allocated by the instance. The instance may be + partly initialized, or partly destoried. If a resource is + destoried, it is marked as that in case the destory failed and + being called again later. + + @param IpSb The IP4 serviceing binding instance to clean up + + @retval EFI_SUCCESS The resource used by the instance are cleaned up + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip4CleanService ( + IN IP4_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + + if (IpSb->DefaultInterface != NULL) { + Status = Ip4FreeInterface (IpSb->DefaultInterface, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + IpSb->DefaultInterface = NULL; + } + + if (IpSb->DefaultRouteTable != NULL) { + Ip4FreeRouteTable (IpSb->DefaultRouteTable); + IpSb->DefaultRouteTable = NULL; + } + + Ip4CleanAssembleTable (&IpSb->Assemble); + + if (IpSb->MnpChildHandle != NULL) { + if (IpSb->Mnp) { + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + IpSb->Mnp = NULL; + } + + NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiManagedNetworkServiceBindingProtocolGuid, + IpSb->MnpChildHandle + ); + + IpSb->MnpChildHandle = NULL; + } + + if (IpSb->Timer != NULL) { + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->CloseEvent (IpSb->Timer); + + IpSb->Timer = NULL; + } + + if (IpSb->Ip4Config != NULL) { + IpSb->Ip4Config->Stop (IpSb->Ip4Config); + + gBS->CloseProtocol ( + IpSb->Controller, + &gEfiIp4ConfigProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + gBS->CloseEvent (IpSb->DoneEvent); + gBS->CloseEvent (IpSb->ReconfigEvent); + IpSb->Ip4Config = NULL; + } + + return EFI_SUCCESS; +} + + +/** + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +Ip4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + IP4_SERVICE *IpSb; + EFI_STATUS Status; + + // + // Test for the Ip4 service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = Ip4CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Install the Ip4ServiceBinding Protocol onto ControlerHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + &IpSb->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto FREE_SERVICE; + } + + // + // ready to go: start the receiving and timer + // + Status = Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb); + + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_SECOND); + + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + // + // Initialize the IP4 ID + // + mIp4Id = (UINT16)NET_RANDOM (NetRandomInitSeed ()); + + Ip4SetVariableData (IpSb); + + return Status; + +UNINSTALL_PROTOCOL: + gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + &IpSb->ServiceBinding + ); + +FREE_SERVICE: + Ip4CleanService (IpSb); + NetFreePool (IpSb); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +Ip4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + IP4_SERVICE *IpSb; + IP4_PROTOCOL *IpInstance; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_TPL OldTpl; + INTN State; + + // + // IP4 driver opens the MNP child, ARP children or the IP4_CONFIG protocol + // by driver. So the ControllerHandle may be the MNP child handle, ARP child + // handle, or the NIC (UNDI) handle because IP4_CONFIG protocol is installed + // in the NIC handle. + // + // + // First, check whether it is the IP4_CONFIG protocol being uninstalled. + // IP4_CONFIG protocol is installed on the NIC handle. It isn't necessary + // to clean up the default configuration if IP4_CONFIG is being stopped. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ConfigProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + // + // Retrieve the IP4 service binding protocol. If failed, it is + // likely that Ip4 ServiceBinding is uninstalled already. In this + // case, return immediately. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + IpSb = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (IpSb->Ip4Config && (IpSb->State != IP4_SERVICE_DESTORY)) { + + IpSb->Ip4Config->Stop (IpSb->Ip4Config); + + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiIp4ConfigProtocolGuid, + IpSb->Image, + ControllerHandle + ); + + if (EFI_ERROR (Status)) { + NET_RESTORE_TPL (OldTpl); + return Status; + } + + // + // If the auto configure hasn't complete, mark it as not started. + // + if (IpSb->State == IP4_SERVICE_STARTED) { + IpSb->State = IP4_SERVICE_UNSTARTED; + } + + IpSb->Ip4Config = NULL; + gBS->CloseEvent (IpSb->DoneEvent); + gBS->CloseEvent (IpSb->ReconfigEvent); + } + + NET_RESTORE_TPL (OldTpl); + return EFI_SUCCESS; + } + + // + // Either MNP or ARP protocol is being uninstalled. The controller + // handle is either the MNP child or ARP child. But, the IP4's + // service binding is installed on the NIC handle. So, need to open + // the protocol info to find the NIC handle. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid); + + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid); + } + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Retrieve the IP4 service binding protocol + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiIp4ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + IpSb = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (IpSb->InDestory) { + NET_RESTORE_TPL (OldTpl); + return EFI_SUCCESS; + } + + IpSb->InDestory = TRUE; + + State = IpSb->State; + IpSb->State = IP4_SERVICE_DESTORY; + + // + // Destory all the children first. If not all children are destoried, + // the IP driver can operate correctly, so restore it state. Don't + // use NET_LIST_FOR_EACH_SAFE here, because it will cache the next + // pointer, which may point to the child that has already been destoried. + // For example, if there are two child in the list, the first is UDP + // listen child, the send is the MTFTP's child. When Udp child is + // destoried, it will destory the MTFTP's child. Then Next point to + // a invalid child. + // + while (!NetListIsEmpty (&IpSb->Children)) { + IpInstance = NET_LIST_HEAD (&IpSb->Children, IP4_PROTOCOL, Link); + Ip4ServiceBindingDestroyChild (ServiceBinding, IpInstance->Handle); + } + + if (IpSb->NumChildren != 0) { + IpSb->State = State; + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + // + // Clear the variable data. + // + Ip4ClearVariableData (IpSb); + + // + // OK, clean other resources then uninstall the service binding protocol. + // + Status = Ip4CleanService (IpSb); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiIp4ServiceBindingProtocolGuid, + ServiceBinding + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + NET_RESTORE_TPL (OldTpl); + NetFreePool (IpSb); + return EFI_SUCCESS; + +ON_ERROR: + IpSb->InDestory = FALSE; + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Creates a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O services + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +Ip4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + IP4_SERVICE *IpSb; + IP4_PROTOCOL *IpInstance; + EFI_TPL OldTpl; + EFI_STATUS Status; + VOID *Mnp; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpSb = IP4_SERVICE_FROM_PROTOCOL (This); + IpInstance = NetAllocatePool (sizeof (IP4_PROTOCOL)); + + if (IpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip4InitProtocol (IpSb, IpInstance); + + // + // Install Ip4 onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp4ProtocolGuid, + &IpInstance->Ip4Proto, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpInstance->Handle = *ChildHandle; + + // + // Open the Managed Network protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &Mnp, + gIp4DriverBinding.DriverBindingHandle, + IpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp4ProtocolGuid, + &IpInstance->Ip4Proto, + NULL + ); + + goto ON_ERROR; + } + + // + // Insert it into the service binding instance. + // + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + NetListInsertTail (&IpSb->Children, &IpInstance->Link); + IpSb->NumChildren++; + + NET_RESTORE_TPL (OldTpl); + +ON_ERROR: + + if (EFI_ERROR (Status)) { + + Ip4CleanProtocol (IpInstance); + + NetFreePool (IpInstance); + } + + return Status; +} + + +/** + Destroys a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The I/O services were removed from the child + handle + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Ip4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + IP4_SERVICE *IpSb; + IP4_PROTOCOL *IpInstance; + EFI_IP4_PROTOCOL *Ip4; + EFI_TPL OldTpl; + INTN State; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + IpSb = IP4_SERVICE_FROM_PROTOCOL (This); + + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiIp4ProtocolGuid, + (VOID **) &Ip4, + gIp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (Ip4); + + if (IpInstance->Service != IpSb) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // A child can be destoried more than once. For example, + // Ip4DriverBindingStop will destory all of its children. + // when UDP driver is being stopped, it will destory all + // the IP child it opens. + // + if (IpInstance->State == IP4_STATE_DESTORY) { + NET_RESTORE_TPL (OldTpl); + return EFI_SUCCESS; + } + + State = IpInstance->State; + IpInstance->State = IP4_STATE_DESTORY; + + // + // Close the Managed Network protocol. + // + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + gIp4DriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the IP4 protocol first. Many thing happens during + // this: + // 1. The consumer of the IP4 protocol will be stopped if it + // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is + // stopped, IP driver's stop function will be called, and uninstall + // EFI_IP4_PROTOCOL will trigger the UDP's stop function. This + // makes it possible to create the network stack bottom up, and + // stop it top down. + // 2. the upper layer will recycle the received packet. The recycle + // event's TPL is higher than this function. The recycle events + // will be called back before preceeding. If any packets not recycled, + // that means there is a resource leak. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiIp4ProtocolGuid, + &IpInstance->Ip4Proto + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip4CleanProtocol (IpInstance); + + Ip4SetVariableData (IpSb); + + if (EFI_ERROR (Status)) { + gBS->InstallMultipleProtocolInterfaces ( + &ChildHandle, + &gEfiIp4ProtocolGuid, + Ip4, + NULL + ); + + goto ON_ERROR; + } + + NetListRemoveEntry (&IpInstance->Link); + IpSb->NumChildren--; + + NET_RESTORE_TPL (OldTpl); + + NetFreePool (IpInstance); + return EFI_SUCCESS; + +ON_ERROR: + IpInstance->State = State; + NET_RESTORE_TPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h new file mode 100644 index 0000000000..5421c90c3b --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Driver.h @@ -0,0 +1,84 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Driver.h + +Abstract: + + +**/ + +#ifndef __EFI_IP4_DRIVER_H__ +#define __EFI_IP4_DRIVER_H__ + +#include + +extern EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gIp4ComponentName; + +// +// Function prototype for the driver's entry point +// +EFI_STATUS +EFIAPI +Ip4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Drivr Binding Protocol +// +EFI_STATUS +EFIAPI +Ip4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Ip4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Ip4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// +EFI_STATUS +EFIAPI +Ip4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +EFIAPI +Ip4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf new file mode 100644 index 0000000000..f45cc3f609 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf @@ -0,0 +1,81 @@ +#/** @file +# Component name for module Ip4 +# +# Copyright (c) 2007, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Ip4Dxe + FILE_GUID = 9FB1A1F3-3B71-4324-B39A-745CBB015FFF + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = Ip4DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Ip4Driver.c + Ip4Option.h + Ip4Route.h + Ip4If.c + Ip4Igmp.h + Ip4Output.c + Ip4Icmp.c + Ip4Igmp.c + Ip4Impl.c + Ip4Common.h + Ip4Impl.h + Ip4Driver.h + Ip4Common.c + Ip4If.h + Ip4Option.c + Ip4Output.h + ComponentName.c + Ip4Input.h + Ip4Route.c + Ip4Icmp.h + Ip4Input.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DebugLib + NetLib + + +[Protocols] + gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiManagedNetworkServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ConfigProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiArpServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiManagedNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiArpProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa new file mode 100644 index 0000000000..c2059dd116 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.msa @@ -0,0 +1,101 @@ + + + Ip4Dxe + DXE_DRIVER + 9FB1A1F3-3B71-4324-B39A-745CBB015FFF + 1.0 + Component name for module Ip4 + FIX ME! + Copyright (c) 2007, Intel Corporation. All rights reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + Ip4Dxe + + + + DebugLib + + + UefiRuntimeServicesTableLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + BaseLib + + + UefiLib + + + + Ip4Input.c + Ip4Icmp.h + Ip4Route.c + Ip4Input.h + ComponentName.c + Ip4Output.h + Ip4Option.c + Ip4If.h + Ip4Common.c + Ip4Driver.h + Ip4Impl.h + Ip4Common.h + Ip4Impl.c + Ip4Igmp.c + Ip4Icmp.c + Ip4Output.c + Ip4Igmp.h + Ip4If.c + Ip4Route.h + Ip4Option.h + Ip4Driver.c + + + + + + + + gEfiArpProtocolGuid + + + gEfiManagedNetworkProtocolGuid + + + gEfiIp4ServiceBindingProtocolGuid + + + gEfiArpServiceBindingProtocolGuid + + + gEfiIp4ConfigProtocolGuid + + + gEfiManagedNetworkServiceBindingProtocolGuid + + + gEfiIp4ProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + Ip4DriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c new file mode 100644 index 0000000000..7f29c90869 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.c @@ -0,0 +1,372 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Icmp.c + +Abstract: + + +**/ + +#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 IpSb The IP4 service binding instance that received the + packet + @param Head The IP head of the received ICMPpacket. + @param Packet The content of the ICMP redirect packet with IP + head removed. + @param 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 + +**/ +STATIC +EFI_STATUS +Ip4ProcessIcmpRedirect ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN IP4_ICMP_ERROR_HEAD *Icmp + ) +{ + NET_LIST_ENTRY *Entry; + IP4_PROTOCOL *Ip4Instance; + IP4_ROUTE_CACHE_ENTRY *CacheEntry; + IP4_INTERFACE *IpIf; + IP4_ADDR Gateway; + + // + // 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; + } + + CacheEntry = Ip4FindRouteCache ( + Ip4Instance->RouteTable, + NTOHL (Icmp->IpHead.Dst), + NTOHL (Icmp->IpHead.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 IpSb The IP service that received the packet. + @param Head The IP head of the ICMP error packet + @param Packet The content of the ICMP error with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is invalid + @retval Others Failed to process the packet. + @retval EFI_SUCCESS The ICMP error is processed successfully. + +**/ +STATIC +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); +} + + +/** + Replay an ICMP echo request. + + @param IpSb The IP service that receivd the packet + @param Head The IP head of the ICMP error packet + @param Packet The content of the ICMP error with IP 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); + Icmp->Head.Type = ICMP_ECHO_REPLY; + Icmp->Head.Checksum = 0; + Icmp->Head.Checksum = ~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize); + + ReplyHead.Tos = 0; + ReplyHead.Fragment = 0; + ReplyHead.Ttl = 64; + ReplyHead.Protocol = IP4_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 + ); + +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 IpSb The IP service that receivd the packet + @param Head The IP head of the ICMP query packet + @param Packet The content of the ICMP query with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is invalid + @retval EFI_SUCCESS The ICMP query message is processed + +**/ +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); +} + + +/** + Handle the ICMP packet. First validate the message format, + then according to the message types, process it as query or + error packet. + + @param IpSb The IP service that receivd the packet + @param Head The IP head of the ICMP query packet + @param Packet The content of the ICMP query with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_SUCCESS The ICMP message is successfully processed. + +**/ +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 = ~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; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h new file mode 100644 index 0000000000..f4641e2ef6 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Icmp.h @@ -0,0 +1,99 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Icmp.h + +Abstract: + + Header file for ICMP protocol. + + +**/ + +#ifndef __EFI_IP4_ICMP_H__ +#define __EFI_IP4_ICMP_H__ + +enum { + // + // ICMP type definations + // + ICMP_ECHO_REPLY = 0, + ICMP_DEST_UNREACHABLE = 3, + ICMP_SOURCE_QUENCH = 4, + ICMP_REDIRECT = 5, + ICMP_ECHO_REQUEST = 8, + ICMP_TIME_EXCEEDED = 11, + ICMP_PARAMETER_PROBLEM = 12, + ICMP_TIMESTAMP = 13, + ICMP_INFO_REQUEST = 15, + ICMP_INFO_REPLY = 16, + ICMP_TYPE_MAX = ICMP_INFO_REPLY, + + ICMP_DEFAULT_CODE = 0, + + // + // ICMP code definations for ICMP_DEST_UNREACHABLE + // + ICMP_NET_UNREACHABLE = 0, + ICMP_HOST_UNREACHABLE = 1, + ICMP_PROTO_UNREACHABLE = 2, // Host may generate + ICMP_PORT_UNREACHABLE = 3, // Host may generate + ICMP_FRAGMENT_FAILED = 4, + ICMP_SOURCEROUTE_FAILED = 5, // Host may generate + ICMP_NET_UNKNOWN = 6, + ICMP_HOST_UNKNOWN = 7, + ICMP_SOURCE_ISOLATED = 8, + ICMP_NET_PROHIBITED = 9, + ICMP_HOST_PROHIBITED = 10, + ICMP_NET_UNREACHABLE_TOS = 11, + ICMP_HOST_UNREACHABLE_TOS = 12, + + // + // ICMP code definations for ICMP_TIME_EXCEEDED + // + ICMP_TIMEOUT_IN_TRANSIT = 0, + ICMp_TIMEOUT_REASSEMBLE = 1, // Host may generate + + // + // ICMP code definations for ICMP_TIME_EXCEEDED + // + ICMP_NET_REDIRECT = 0, + ICMP_HOST_REDIRECT = 1, + ICMP_NET_TOS_REDIRECT = 2, + ICMP_HOST_TOS_REDIRECT = 3, + + // + // ICMP message classes, each class of ICMP message shares + // a common message format. INVALID_MESSAGE is only a flag. + // + ICMP_INVALID_MESSAGE = 0, + ICMP_ERROR_MESSAGE = 1, + ICMP_QUERY_MESSAGE = 2, +}; + +typedef struct { + UINT8 IcmpType; + UINT8 IcmpClass; +} IP4_ICMP_CLASS; + +extern IP4_ICMP_CLASS mIcmpClass[]; +extern EFI_IP4_ICMP_TYPE mIp4SupportedIcmp[]; + +EFI_STATUS +Ip4IcmpHandle ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Header, + IN NET_BUF *Packet + ); +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c new file mode 100644 index 0000000000..952eb98ccc --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.c @@ -0,0 +1,1146 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4If.c + +Abstract: + + Implement IP4 pesudo interface. + + +**/ + +#include "Ip4Impl.h" + +// +// Mac address with all zero, used to determine whethter the ARP +// resolve succeeded. Failed ARP requests zero the MAC address buffer. +// +STATIC EFI_MAC_ADDRESS mZeroMacAddress; + +STATIC +VOID +EFIAPI +Ip4OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +STATIC +VOID +EFIAPI +Ip4OnArpResolved ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +STATIC +VOID +EFIAPI +Ip4OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +STATIC +VOID +Ip4CancelFrameArp ( + IN IP4_ARP_QUE *ArpQue, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL + IN VOID *Context + ); + + +/** + Wrap a transmit request into a newly allocated IP4_LINK_TX_TOKEN. + + @param Interface The interface to send out from + @param IpInstance The IpInstance that transmit the packet. NULL if + the packet is sent by the IP4 driver itself. + @param Packet The packet to transmit + @param CallBack Call back function to execute if transmission + finished. + @param Context Opaque parameter to the call back. + + @return The wrapped token if succeed or NULL + +**/ +STATIC +IP4_LINK_TX_TOKEN * +Ip4WrapLinkTxToken ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance, OPTIONAL + IN NET_BUF *Packet, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData; + IP4_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + UINT32 Count; + + Token = NetAllocatePool (sizeof (IP4_LINK_TX_TOKEN) + \ + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA)); + + if (Token == NULL) { + return NULL; + } + + Token->Signature = IP4_FRAME_TX_SIGNATURE; + NetListInit (&Token->Link); + + Token->Interface = Interface; + Token->IpInstance = IpInstance; + Token->CallBack = CallBack; + Token->Packet = Packet; + Token->Context = Context; + CopyMem (&Token->DstMac, &mZeroMacAddress, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&Token->SrcMac, &Interface->Mac, sizeof (EFI_MAC_ADDRESS)); + + MnpToken = &(Token->MnpToken); + MnpToken->Status = EFI_NOT_READY; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip4OnFrameSent, + Token, + &MnpToken->Event + ); + + if (EFI_ERROR (Status)) { + NetFreePool (Token); + return NULL; + } + + MnpTxData = &Token->MnpTxData; + MnpToken->Packet.TxData = MnpTxData; + + MnpTxData->DestinationAddress = &Token->DstMac; + MnpTxData->SourceAddress = &Token->SrcMac; + MnpTxData->ProtocolType = IP4_ETHER_PROTO; + MnpTxData->DataLength = Packet->TotalSize; + MnpTxData->HeaderLength = 0; + + Count = Packet->BlockOpNum; + + NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count); + MnpTxData->FragmentCount = (UINT16)Count; + + return Token; +} + + +/** + Free the link layer transmit token. It will close the event + then free the memory used. + + @param Token Token to free + + @return NONE + +**/ +STATIC +VOID +Ip4FreeLinkTxToken ( + IN IP4_LINK_TX_TOKEN *Token + ) +{ + NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE); + + gBS->CloseEvent (Token->MnpToken.Event); + NetFreePool (Token); +} + + +/** + Create an IP_ARP_QUE structure to request ARP service. + + @param Interface The interface to send ARP from. + @param DestIp The destination IP (host byte order) to request MAC + for + + @return Point to newly created IP4_ARP_QUE if succeed, otherwise NULL. + +**/ +STATIC +IP4_ARP_QUE * +Ip4CreateArpQue ( + IN IP4_INTERFACE *Interface, + IN IP4_ADDR DestIp + ) +{ + IP4_ARP_QUE *ArpQue; + EFI_STATUS Status; + + ArpQue = NetAllocatePool (sizeof (IP4_ARP_QUE)); + + if (ArpQue == NULL) { + return NULL; + } + + ArpQue->Signature = IP4_FRAME_ARP_SIGNATURE; + NetListInit (&ArpQue->Link); + + NetListInit (&ArpQue->Frames); + ArpQue->Interface = Interface; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip4OnArpResolved, + ArpQue, + &ArpQue->OnResolved + ); + + if (EFI_ERROR (Status)) { + NetFreePool (ArpQue); + return NULL; + } + + ArpQue->Ip = DestIp; + CopyMem (&ArpQue->Mac, &mZeroMacAddress, sizeof (EFI_MAC_ADDRESS)); + + return ArpQue; +} + + +/** + Remove all the transmit requests queued on the ARP queue, then free it. + + @param ArpQue Arp queue to free + @param IoStatus The transmit status returned to transmit requests' + callback. + + @return NONE + +**/ +STATIC +VOID +Ip4FreeArpQue ( + IN IP4_ARP_QUE *ArpQue, + IN EFI_STATUS IoStatus + ) +{ + NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE); + + // + // Remove all the frame waiting the ARP response + // + Ip4CancelFrameArp (ArpQue, IoStatus, NULL, NULL); + + gBS->CloseEvent (ArpQue->OnResolved); + NetFreePool (ArpQue); +} + + +/** + Create a link layer receive token to wrap the receive request + + @param Interface The interface to receive from + @param IpInstance The instance that request the receive (NULL for IP4 + driver itself) + @param CallBack Call back function to execute when finished. + @param Context Opaque parameters to the callback + + @return Point to created IP4_LINK_RX_TOKEN if succeed, otherwise NULL. + +**/ +STATIC +IP4_LINK_RX_TOKEN * +Ip4CreateLinkRxToken ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + IP4_LINK_RX_TOKEN *Token; + EFI_STATUS Status; + + Token = NetAllocatePool (sizeof (IP4_LINK_RX_TOKEN)); + if (Token == NULL) { + return NULL; + } + + Token->Signature = IP4_FRAME_RX_SIGNATURE; + Token->Interface = Interface; + Token->IpInstance = IpInstance; + Token->CallBack = CallBack; + Token->Context = Context; + + MnpToken = &Token->MnpToken; + MnpToken->Status = EFI_NOT_READY; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip4OnFrameReceived, + Token, + &MnpToken->Event + ); + + if (EFI_ERROR (Status)) { + NetFreePool (Token); + return NULL; + } + + MnpToken->Packet.RxData = NULL; + return Token; +} + + +/** + Free the link layer request token. It will close the event + then free the memory used. + + @param Token Request token to free + + @return NONE + +**/ +STATIC +VOID +Ip4FreeFrameRxToken ( + IN IP4_LINK_RX_TOKEN *Token + ) +{ + + NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE); + + gBS->CloseEvent (Token->MnpToken.Event); + NetFreePool (Token); +} + + +/** + Remove all the frames on the ARP queue that pass the FrameToCancel, + that is, either FrameToCancel is NULL or it returns true for the frame. + + @param ArpQue ARP frame to remove the frames from. + @param IoStatus The status returned to the cancelled frames' + callback function. + @param FrameToCancel Function to select which frame to cancel. + @param Context Opaque parameter to the FrameToCancel. + + @return NONE + +**/ +STATIC +VOID +Ip4CancelFrameArp ( + IN IP4_ARP_QUE *ArpQue, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL + IN VOID *Context + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_LINK_TX_TOKEN *Token; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link); + + if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { + NetListRemoveEntry (Entry); + + Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context); + Ip4FreeLinkTxToken (Token); + } + } +} + + +/** + Remove all the frames on the interface that pass the FrameToCancel, + either queued on ARP queues or that have already been delivered to + MNP and not yet recycled. + + @param Interface Interface to remove the frames from + @param IoStatus The transmit status returned to the frames' + callback + @param FrameToCancel Function to select the frame to cancel, NULL to + select all + @param Context Opaque parameters passed to FrameToCancel + + @return NONE + +**/ +VOID +Ip4CancelFrames ( + IN IP4_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL + IN VOID *Context + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ARP_QUE *ArpQue; + IP4_LINK_TX_TOKEN *Token; + + // + // Cancel all the pending frames on ARP requests + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link); + + Ip4CancelFrameArp (ArpQue, IoStatus, FrameToCancel, Context); + + if (NetListIsEmpty (&ArpQue->Frames)) { + NetListRemoveEntry (Entry); + + Interface->Arp->Cancel (Interface->Arp, &ArpQue->Ip, ArpQue->OnResolved); + Ip4FreeArpQue (ArpQue, EFI_ABORTED); + } + } + + // + // Cancel all the frames that have been delivered to MNP + // but not yet recycled. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) { + Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link); + + if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { + NetListRemoveEntry (Entry); + + Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken); + Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context); + Ip4FreeLinkTxToken (Token); + } + } +} + + +/** + Create an IP4_INTERFACE. Delay the creation of ARP instance until + the interface is configured. + + @param Mnp The shared MNP child of this IP4 service binding + instance + @param Controller The controller this IP4 service binding instance + is installed. Most like the UNDI handle. + @param ImageHandle This driver's image handle + + @return Point to the created IP4_INTERFACE, otherwise NULL. + +**/ +IP4_INTERFACE * +Ip4CreateInterface ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ) +{ + IP4_INTERFACE *Interface; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + Interface = NetAllocatePool (sizeof (IP4_INTERFACE)); + + if ((Interface == NULL) || (Mnp == NULL)) { + return NULL; + } + + Interface->Signature = IP4_INTERFACE_SIGNATURE; + NetListInit (&Interface->Link); + Interface->RefCnt = 1; + + Interface->Ip = IP4_ALLZERO_ADDRESS; + Interface->SubnetMask = IP4_ALLZERO_ADDRESS; + Interface->Configured = FALSE; + + Interface->Controller = Controller; + Interface->Image = ImageHandle; + Interface->Mnp = Mnp; + Interface->Arp = NULL; + Interface->ArpHandle = NULL; + + NetListInit (&Interface->ArpQues); + NetListInit (&Interface->SentFrames); + + Interface->RecvRequest = NULL; + + // + // Get the interface's Mac address and broadcast mac address from SNP + // + if (EFI_ERROR (Mnp->GetModeData (Mnp, NULL, &SnpMode))) { + NetFreePool (Interface); + return NULL; + } + + CopyMem (&Interface->Mac, &SnpMode.CurrentAddress, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&Interface->BroadcastMac, &SnpMode.BroadcastAddress, sizeof (EFI_MAC_ADDRESS)); + Interface->HwaddrLen = SnpMode.HwAddressSize; + + NetListInit (&Interface->IpInstances); + Interface->PromiscRecv = FALSE; + + return Interface; +} + + +/** + Set the interface's address, create and configure + the ARP child if necessary. + + @param Interface The interface to set the address + @param IpAddr The interface's IP address + @param SubnetMask The interface's netmask + + @retval EFI_SUCCESS The interface is configured with Ip/netmask pair, + and a ARP is created for it. + @retval Others Failed to set the interface's address. + +**/ +EFI_STATUS +Ip4SetAddress ( + IN IP4_INTERFACE *Interface, + IN IP4_ADDR IpAddr, + IN IP4_ADDR SubnetMask + ) +{ + EFI_ARP_CONFIG_DATA ArpConfig; + EFI_STATUS Status; + INTN Type; + INTN Len; + IP4_ADDR Netmask; + + NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE); + + ASSERT (!Interface->Configured); + + // + // Set the ip/netmask, then compute the subnet broadcast + // and network broadcast for easy access. When computing + // nework broadcast, the subnet mask is most like longer + // than the default netmask (not subneted) as defined in + // RFC793. If that isn't the case, we are aggregating the + // networks, use the subnet's mask instead. + // + Interface->Ip = IpAddr; + Interface->SubnetMask = SubnetMask; + Interface->SubnetBrdcast = (IpAddr | ~SubnetMask); + + Type = NetGetIpClass (IpAddr); + Len = NetGetMaskLength (SubnetMask); + Netmask = mIp4AllMasks[NET_MIN (Len, Type << 3)]; + Interface->NetBrdcast = (IpAddr | ~Netmask); + + // + // If the address is NOT all zero, create then configure an ARP child. + // Pay attention: DHCP configures its station address as 0.0.0.0/0 + // + Interface->Arp = NULL; + Interface->ArpHandle = NULL; + + if (IpAddr != IP4_ALLZERO_ADDRESS) { + Status = NetLibCreateServiceChild ( + Interface->Controller, + Interface->Image, + &gEfiArpServiceBindingProtocolGuid, + &Interface->ArpHandle + ); + + if (EFI_ERROR (Status)) { + return Status;; + } + + Status = gBS->OpenProtocol ( + Interface->ArpHandle, + &gEfiArpProtocolGuid, + (VOID **) &Interface->Arp, + Interface->Image, + Interface->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpAddr = HTONL (IpAddr); + ArpConfig.SwAddressType = IP4_ETHER_PROTO; + ArpConfig.SwAddressLength = 4; + ArpConfig.StationAddress = &IpAddr; + ArpConfig.EntryTimeOut = 0; + ArpConfig.RetryCount = 0; + ArpConfig.RetryTimeOut = 0; + + Status = Interface->Arp->Configure (Interface->Arp, &ArpConfig); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Interface->ArpHandle, + &gEfiArpProtocolGuid, + Interface->Image, + Interface->Controller + ); + + goto ON_ERROR; + } + } + + Interface->Configured = TRUE; + return EFI_SUCCESS; + +ON_ERROR: + NetLibDestroyServiceChild ( + Interface->Controller, + Interface->Image, + &gEfiArpServiceBindingProtocolGuid, + &Interface->ArpHandle + ); + + return Status; +} + + +/** + Fileter function to cancel all the frame related to an IP instance. + + @param Frame The transmit request to test whether to cancel + @param Context The context which is the Ip instance that issued + the transmit. + + @retval TRUE The frame belongs to this instance and is to be + removed + @retval FALSE The frame doesn't belong to this instance. + +**/ +STATIC +BOOLEAN +Ip4CancelInstanceFrame ( + IN IP4_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if (Frame->IpInstance == (IP4_PROTOCOL *) Context) { + return TRUE; + } + + return FALSE; +} + + + +/** + If there is a pending receive request, cancel it. Don't call + the receive request's callback because this function can be only + called if the instance or driver is tearing itself down. It + doesn't make sense to call it back. But it is necessary to call + the transmit token's callback to give it a chance to free the + packet and update the upper layer's transmit request status, say + that from the UDP. + + @param Interface The interface used by the IpInstance + + @return None + +**/ +VOID +Ip4CancelReceive ( + IN IP4_INTERFACE *Interface + ) +{ + EFI_TPL OldTpl; + IP4_LINK_RX_TOKEN *Token; + + if ((Token = Interface->RecvRequest) != NULL) { + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + Interface->RecvRequest = NULL; + Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken); + Ip4FreeFrameRxToken (Token); + + NET_RESTORE_TPL (OldTpl); + } +} + + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/Netmask pair share the same interface. It is reference + counted. All the frames haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list itself. + + @param Interface The interface used by the IpInstance + @param IpInstance The Ip instance that free the interface. NULL if + the Ip driver is releasing the default interface. + + @retval EFI_SUCCESS The interface use IpInstance is freed. + +**/ +EFI_STATUS +Ip4FreeInterface ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL + ) +{ + NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE); + ASSERT (Interface->RefCnt > 0); + + // + // Remove all the pending transmit token related to this IP instance. + // + Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, IpInstance); + + if (--Interface->RefCnt > 0) { + return EFI_SUCCESS; + } + + // + // Destory the interface if this is the last IP instance that + // has the address. Remove all the system transmitted packets + // from this interface, cancel the receive request if there is + // one, and destory the ARP requests. + // + Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, NULL); + Ip4CancelReceive (Interface); + + ASSERT (NetListIsEmpty (&Interface->IpInstances)); + ASSERT (NetListIsEmpty (&Interface->ArpQues)); + ASSERT (NetListIsEmpty (&Interface->SentFrames)); + + if (Interface->Arp != NULL) { + gBS->CloseProtocol ( + Interface->ArpHandle, + &gEfiArpProtocolGuid, + Interface->Image, + Interface->Controller + ); + + NetLibDestroyServiceChild ( + Interface->Controller, + Interface->Image, + &gEfiArpServiceBindingProtocolGuid, + Interface->ArpHandle + ); + } + + NetListRemoveEntry (&Interface->Link); + NetFreePool (Interface); + + return EFI_SUCCESS; +} + + +/** + Callback function when ARP request are finished. It will cancelled + all the queued frame if the ARP requests failed. Or transmit them + if the request succeed. + + @param Event The Arp request event + @param Context The context of the callback, a point to the ARP + queue + + @return None + +**/ +STATIC +VOID +EFIAPI +Ip4OnArpResolved ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ARP_QUE *ArpQue; + IP4_INTERFACE *Interface; + IP4_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + + ArpQue = (IP4_ARP_QUE *) Context; + NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE); + + NetListRemoveEntry (&ArpQue->Link); + + // + // ARP resolve failed for some reason. Release all the frame + // and ARP queue itself. Ip4FreeArpQue will call the frame's + // owner back. + // + if (NET_MAC_EQUAL (&ArpQue->Mac, &mZeroMacAddress, ArpQue->Interface->HwaddrLen)) { + Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING); + + return ; + } + + // + // ARP resolve succeeded, Transmit all the frame. Release the ARP + // queue. It isn't necessary for us to cache the ARP binding because + // we always check the ARP cache first before transmit. + // + Interface = ArpQue->Interface; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + NetListRemoveEntry (Entry); + + Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link); + CopyMem (&Token->DstMac, &ArpQue->Mac, sizeof (EFI_MAC_ADDRESS)); + + Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken); + + if (EFI_ERROR (Status)) { + Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context); + + Ip4FreeLinkTxToken (Token); + continue; + } + + NetListInsertTail (&Interface->SentFrames, &Token->Link); + } + + Ip4FreeArpQue (ArpQue, EFI_SUCCESS); +} + + +/** + Callback funtion when frame transmission is finished. It will + call the frame owner's callback function to tell it the result. + + @param Event The transmit token's event + @param Context Context which is point to the token. + + @return None. + +**/ +STATIC +VOID +EFIAPI +Ip4OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_LINK_TX_TOKEN *Token; + + Token = (IP4_LINK_TX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE); + + NetListRemoveEntry (&Token->Link); + + Token->CallBack ( + Token->IpInstance, + Token->Packet, + Token->MnpToken.Status, + 0, + Token->Context + ); + + Ip4FreeLinkTxToken (Token); +} + + + +/** + Send a frame from the interface. If the next hop is broadcast or + multicast address, it is transmitted immediately. If the next hop + is a unicast, it will consult ARP to resolve the NextHop's MAC. + If some error happened, the CallBack won't be called. So, the caller + must test the return value, and take action when there is an error. + + @param Interface The interface to send the frame from + @param IpInstance The IP child that request the transmission. NULL + if it is the IP4 driver itself. + @param Packet The packet to transmit. + @param NextHop The immediate destination to transmit the packet + to. + @param CallBack Function to call back when transmit finished. + @param Context Opaque parameter to the call back. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame + @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop + @retval EFI_SUCCESS The packet is successfully transmitted. + +**/ +EFI_STATUS +Ip4SendFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance, OPTIONAL + IN NET_BUF *Packet, + IN IP4_ADDR NextHop, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + IP4_LINK_TX_TOKEN *Token; + NET_LIST_ENTRY *Entry; + IP4_ARP_QUE *ArpQue; + EFI_ARP_PROTOCOL *Arp; + EFI_STATUS Status; + + ASSERT (Interface->Configured); + + Token = Ip4WrapLinkTxToken (Interface, IpInstance, Packet, CallBack, Context); + + if (Token == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get the destination MAC address for multicast and broadcasts. + // Don't depend on ARP to solve the address since there maybe no + // ARP at all. Ip4Output has set NextHop to 255.255.255.255 for + // all the broadcasts. + // + if (NextHop == IP4_ALLONE_ADDRESS) { + CopyMem (&Token->DstMac, &Interface->BroadcastMac, sizeof (EFI_MAC_ADDRESS)); + goto SEND_NOW; + + } else if (IP4_IS_MULTICAST (NextHop)) { + + Status = Ip4GetMulticastMac (Interface->Mnp, NextHop, &Token->DstMac); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + goto SEND_NOW; + } + + // + // Can only send out multicast/broadcast if the IP address is zero + // + if ((Arp = Interface->Arp) == NULL) { + Status = EFI_NO_MAPPING; + goto ON_ERROR; + } + + // + // First check whether this binding is in the ARP cache. + // + NextHop = HTONL (NextHop); + Status = Arp->Request (Arp, &NextHop, NULL, &Token->DstMac); + + if (Status == EFI_SUCCESS) { + goto SEND_NOW; + + } else if (Status != EFI_NOT_READY) { + goto ON_ERROR; + } + + // + // Have to do asynchronous ARP resolution. First check + // whether there is already a pending request. + // + ArpQue = NULL; + + NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link); + + if (ArpQue->Ip == NextHop) { + break; + } + } + + // + // Found a pending ARP request, enqueue the frame then return + // + if (Entry != &Interface->ArpQues) { + NetListInsertTail (&ArpQue->Frames, &Token->Link); + return EFI_SUCCESS; + } + + // + // First frame to NextHop, issue an asynchronous ARP requests + // + ArpQue = Ip4CreateArpQue (Interface, NextHop); + + if (ArpQue == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = Arp->Request (Arp, &ArpQue->Ip, ArpQue->OnResolved, ArpQue->Mac.Addr); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) { + Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING); + goto ON_ERROR; + } + + NetListInsertHead (&ArpQue->Frames, &Token->Link); + NetListInsertHead (&Interface->ArpQues, &ArpQue->Link); + return EFI_SUCCESS; + +SEND_NOW: + Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + NetListInsertTail (&Interface->SentFrames, &Token->Link); + return EFI_SUCCESS; + +ON_ERROR: + Ip4FreeLinkTxToken (Token); + return Status; +} + + +/** + Call back function when the received packet is freed. + Check Ip4OnFrameReceived for information. + + @param Context Context, which is the IP4_LINK_RX_TOKEN. + + @return None. + +**/ +STATIC +VOID +Ip4RecycleFrame ( + IN VOID *Context + ) +{ + IP4_LINK_RX_TOKEN *Frame; + + Frame = (IP4_LINK_RX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Frame, IP4_FRAME_RX_SIGNATURE); + + gBS->SignalEvent (Frame->MnpToken.Packet.RxData->RecycleEvent); + Ip4FreeFrameRxToken (Frame); +} + + +/** + Received a frame from MNP, wrap it in net buffer then deliver + it to IP's input function. The ownship of the packet also + transferred to IP. When Ip is finished with this packet, it + will call NetbufFree to release the packet, NetbufFree will + again call the Ip4RecycleFrame to signal MNP's event and free + the token used. + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + + @return None. + +**/ +STATIC +VOID +EFIAPI +Ip4OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData; + IP4_LINK_RX_TOKEN *Token; + NET_FRAGMENT Netfrag; + NET_BUF *Packet; + UINT32 Flag; + + Token = (IP4_LINK_RX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE); + + // + // First clear the interface's receive request in case the + // caller wants to call Ip4ReceiveFrame in the callback. + // + Token->Interface->RecvRequest = NULL; + + MnpToken = &Token->MnpToken; + MnpRxData = MnpToken->Packet.RxData; + + if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) { + Token->CallBack (Token->IpInstance, NULL, MnpToken->Status, 0, Token->Context); + Ip4FreeFrameRxToken (Token); + + return ; + } + + // + // Wrap the frame in a net buffer then deliever it to IP input. + // IP will reassemble the packet, and deliver it to upper layer + // + Netfrag.Len = MnpRxData->DataLength; + Netfrag.Bulk = MnpRxData->PacketData; + + Packet = NetbufFromExt (&Netfrag, 1, 0, IP4_MAX_HEADLEN, Ip4RecycleFrame, Token); + + if (Packet == NULL) { + gBS->SignalEvent (MnpRxData->RecycleEvent); + + Token->CallBack (Token->IpInstance, NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context); + Ip4FreeFrameRxToken (Token); + + return ; + } + + Flag = (MnpRxData->BroadcastFlag ? IP4_LINK_BROADCAST : 0); + Flag |= (MnpRxData->MulticastFlag ? IP4_LINK_MULTICAST : 0); + Flag |= (MnpRxData->PromiscuousFlag ? IP4_LINK_PROMISC : 0); + + Token->CallBack (Token->IpInstance, Packet, EFI_SUCCESS, Flag, Token->Context); +} + + +/** + Request to receive the packet from the interface. + + @param Interface The interface to receive the frames from + @param IpInstance The instance that requests the receive. NULL for + the driver itself. + @param CallBack Function to call when receive finished. + @param Context Opaque parameter to the callback + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive + @retval EFI_SUCCESS The recieve request has been started. + +**/ +EFI_STATUS +Ip4ReceiveFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance, OPTIONAL + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + IP4_LINK_RX_TOKEN *Token; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE); + + if (Interface->RecvRequest != NULL) { + return EFI_ALREADY_STARTED; + } + + Token = Ip4CreateLinkRxToken (Interface, IpInstance, CallBack, Context); + + if (Token == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Interface->Mnp->Receive (Interface->Mnp, &Token->MnpToken); + + if (EFI_ERROR (Status)) { + Ip4FreeFrameRxToken (Token); + return Status; + } + + Interface->RecvRequest = Token; + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h new file mode 100644 index 0000000000..a324c82430 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4If.h @@ -0,0 +1,238 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4If.h + +Abstract: + + Definition for IP4 pesudo interface structure. + + +**/ + +#ifndef __EFI_IP4_IF_H__ +#define __EFI_IP4_IF_H__ + +enum { + IP4_FRAME_RX_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'F', 'R'), + IP4_FRAME_TX_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'F', 'T'), + IP4_FRAME_ARP_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'F', 'A'), + IP4_INTERFACE_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', 'I', 'F'), +}; + +// +// This prototype is used by both receive and transmission. +// When receiving Netbuf is allocated by IP4_INTERFACE, and +// released by IP4. Flag shows whether the frame is received +// as link broadcast/multicast... +// +// When transmitting, the Netbuf is from IP4, and provided +// to the callback as a reference. Flag isn't used. +// +// IpInstance can be NULL which means that it is the IP4 driver +// itself sending the packets. IP4 driver may send packets that +// don't belong to any instance, such as ICMP errors, ICMP echo +// responses, or IGMP packets. IpInstance is used as a tag in +// this module. +// +typedef +VOID +(*IP4_FRAME_CALLBACK) ( + IP4_PROTOCOL *IpInstance, OPTIONAL + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +// +// Each receive request is wrapped in an IP4_LINK_RX_TOKEN. +// Upon completion, the Callback will be called. Only one +// receive request is send to MNP. IpInstance is always NULL. +// Reference MNP's spec for information. +// +typedef struct { + UINT32 Signature; + IP4_INTERFACE *Interface; + + IP4_PROTOCOL *IpInstance; + IP4_FRAME_CALLBACK CallBack; + VOID *Context; + + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; +} IP4_LINK_RX_TOKEN; + +// +// Each transmit request is wrapped in an IP4_LINK_TX_TOKEN. +// Upon completion, the Callback will be called. +// +typedef struct { + UINT32 Signature; + NET_LIST_ENTRY Link; + + IP4_INTERFACE *Interface; + + IP4_PROTOCOL *IpInstance; + IP4_FRAME_CALLBACK CallBack; + NET_BUF *Packet; + VOID *Context; + + EFI_MAC_ADDRESS DstMac; + EFI_MAC_ADDRESS SrcMac; + + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData; +} IP4_LINK_TX_TOKEN; + +// +// Only one ARP request is requested for all the frames in +// a time. It is started for the first frames to the Ip. Any +// subsequent transmission frame will be linked to Frames, and +// be sent all at once the ARP requests succeed. +// +typedef struct { + UINT32 Signature; + NET_LIST_ENTRY Link; + + NET_LIST_ENTRY Frames; + IP4_INTERFACE *Interface; + + // + // ARP requesting staffs + // + EFI_EVENT OnResolved; + IP4_ADDR Ip; + EFI_MAC_ADDRESS Mac; +} IP4_ARP_QUE; + +// +// Callback to select which frame to cancel. Caller can cancel a +// single frame, or all the frame from an IP instance. +// +typedef +BOOLEAN +(*IP4_FRAME_TO_CANCEL) ( + IP4_LINK_TX_TOKEN *Frame, + VOID *Context + ); + +// +// Each IP4 instance has its own station address. All the instances +// with the same station address share a single interface structure. +// Each interface has its own ARP child, and shares one MNP child. +// Notice the special cases that DHCP can configure the interface +// with 0.0.0.0/0.0.0.0. +// +typedef struct _IP4_INTERFACE { + UINT32 Signature; + NET_LIST_ENTRY Link; + INTN RefCnt; + + // + // IP address and subnet mask of the interface. It also contains + // the subnet/net broadcast address for quick access. The fileds + // are invalid if (Configured == FALSE) + // + IP4_ADDR Ip; + IP4_ADDR SubnetMask; + IP4_ADDR SubnetBrdcast; + IP4_ADDR NetBrdcast; + BOOLEAN Configured; + + // + // Handle used to create/destory ARP child. All the IP children + // share one MNP which is owned by IP service binding. + // + EFI_HANDLE Controller; + EFI_HANDLE Image; + + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + EFI_ARP_PROTOCOL *Arp; + EFI_HANDLE ArpHandle; + + // + // Queues to keep the frames sent and waiting ARP request. + // + NET_LIST_ENTRY ArpQues; + NET_LIST_ENTRY SentFrames; + IP4_LINK_RX_TOKEN *RecvRequest; + + // + // The interface's MAC and broadcast MAC address. + // + EFI_MAC_ADDRESS Mac; + EFI_MAC_ADDRESS BroadcastMac; + UINT32 HwaddrLen; + + // + // All the IP instances that have the same IP/SubnetMask are linked + // together through IpInstances. If any of the instance enables + // promiscuous receive, PromiscRecv is true. + // + NET_LIST_ENTRY IpInstances; + BOOLEAN PromiscRecv; +} IP4_INTERFACE; + +IP4_INTERFACE * +Ip4CreateInterface ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ); + +EFI_STATUS +Ip4SetAddress ( + IN IP4_INTERFACE *Interface, + IN IP4_ADDR IpAddr, + IN IP4_ADDR SubnetMask + ); + +EFI_STATUS +Ip4FreeInterface ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL + ); + +EFI_STATUS +Ip4SendFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance, OPTIONAL + IN NET_BUF *Packet, + IN IP4_ADDR NextHop, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ); + +VOID +Ip4CancelFrames ( + IN IP4_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel, OPTIONAL + IN VOID *Context + ); + +VOID +Ip4CancelReceive ( + IN IP4_INTERFACE *Interface + ); + +EFI_STATUS +Ip4ReceiveFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance, OPTIONAL + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c new file mode 100644 index 0000000000..135efa62ce --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c @@ -0,0 +1,629 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Igmp.c + +Abstract: + + This file implements the RFC2236: IGMP v2 + + +**/ + +#include "Ip4Impl.h" + +// +// Route Alert option in IGMP report to direct routers to +// examine the packet more closely. +// +UINT32 mRouteAlertOption = 0x00000494; + + +/** + Init the IGMP control data of the IP4 service instance, configure + MNP to receive ALL SYSTEM multicast. + + @param IpSb The IP4 service whose IGMP is to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP. + @retval Others Failed to initialize the IGMP of IpSb. + @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized. + +**/ +EFI_STATUS +Ip4InitIgmp ( + IN IP4_SERVICE *IpSb + ) +{ + IGMP_SERVICE_DATA *IgmpCtrl; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + IGMP_GROUP *Group; + EFI_STATUS Status; + + IgmpCtrl = &IpSb->IgmpCtrl; + + // + // Configure MNP to receive ALL_SYSTEM multicast + // + Group = NetAllocatePool (sizeof (IGMP_GROUP)); + + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mnp = IpSb->Mnp; + + Group->Address = IP4_ALLSYSTEM_ADDRESS; + Group->RefCnt = 1; + Group->DelayTime = 0; + Group->ReportByUs = FALSE; + + Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Mnp->Groups (Mnp, TRUE, &Group->Mac); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + NetListInsertHead (&IgmpCtrl->Groups, &Group->Link); + return EFI_SUCCESS; + +ON_ERROR: + NetFreePool (Group); + return Status; +} + + +/** + Find the IGMP_GROUP structure which contains the status of multicast + group Address in this IGMP control block + + @param IgmpCtrl The IGMP control block to search from + @param Address The multicast address to search + + @return NULL if the multicast address isn't in the IGMP control block. Otherwise + @return the point to the IGMP_GROUP which contains the status of multicast group + @return for Address. + +**/ +IGMP_GROUP * +Ip4FindGroup ( + IN IGMP_SERVICE_DATA *IgmpCtrl, + IN IP4_ADDR Address + ) +{ + NET_LIST_ENTRY *Entry; + IGMP_GROUP *Group; + + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + + if (Group->Address == Address) { + return Group; + } + } + + return NULL; +} + + +/** + Count the number of IP4 multicast groups that are mapped to the + same MAC address. Several IP4 multicast address may be mapped to + the same MAC address. + + @param IgmpCtrl The IGMP control block to search in + @param Mac The MAC address to search + + @return The number of the IP4 multicast group that mapped to the same + @return multicast group Mac. + +**/ +INTN +Ip4FindMac ( + IN IGMP_SERVICE_DATA *IgmpCtrl, + IN EFI_MAC_ADDRESS *Mac + ) +{ + NET_LIST_ENTRY *Entry; + IGMP_GROUP *Group; + INTN Count; + + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + + if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) { + Count++; + } + } + + return Count; +} + + +/** + Send an IGMP protocol message to the Dst, such as IGMP v1 membership report. + + @param IpSb The IP4 service instance that requests the + transmission + @param Dst The destinaton to send to + @param Type The IGMP message type, such as IGMP v2 membership + report + @param Group The group address in the IGMP message head. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message + @retval EFI_SUCCESS The IGMP message is successfully send + @retval Others Failed to send the IGMP message. + +**/ +EFI_STATUS +Ip4SendIgmpMessage ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN UINT8 Type, + IN IP4_ADDR Group + ) +{ + IP4_HEAD Head; + NET_BUF *Packet; + IGMP_HEAD *Igmp; + + // + // Allocate a net buffer to hold the message + // + Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD)); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill in the IGMP and IP header, then transmit the message + // + NetbufReserve (Packet, IP4_MAX_HEADLEN); + + Igmp = (IGMP_HEAD *) NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE); + + Igmp->Type = Type; + Igmp->MaxRespTime = 0; + Igmp->Checksum = 0; + Igmp->Group = HTONL (Group); + Igmp->Checksum = ~NetblockChecksum ((UINT8 *) Igmp, sizeof (IGMP_HEAD)); + + Head.Tos = 0; + Head.Protocol = IP4_PROTO_IGMP; + Head.Ttl = 1; + Head.Fragment = 0; + Head.Dst = Dst; + Head.Src = IP4_ALLZERO_ADDRESS; + + return Ip4Output ( + IpSb, + NULL, + Packet, + &Head, + (UINT8 *) &mRouteAlertOption, + sizeof (UINT32), + IP4_ALLZERO_ADDRESS, + Ip4SysPacketSent, + NULL + ); +} + + +/** + Send an IGMP membership report. Depends on whether the server is + v1 or v2, it will send either a V1 or V2 membership report. + + @param IpSb The IP4 service instance that requests the + transmission. + @param Group The group address to report + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message + @retval EFI_SUCCESS The IGMP report message is successfully send + @retval Others Failed to send the report. + +**/ +EFI_STATUS +Ip4SendIgmpReport ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Group + ) +{ + if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) { + return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group); + } else { + return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group); + } +} + + +/** + Join the multicast group on behavior of this IP4 child + + @param IpInstance The IP4 child that wants to join the group + @param Address The group to join + + @retval EFI_SUCCESS Successfully join the multicast group + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip4JoinGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ) +{ + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + IP4_SERVICE *IpSb; + IGMP_SERVICE_DATA *IgmpCtrl; + IGMP_GROUP *Group; + EFI_STATUS Status; + + IpSb = IpInstance->Service; + IgmpCtrl = &IpSb->IgmpCtrl; + Mnp = IpSb->Mnp; + + // + // If the IP service already is a member in the group, just + // increase the refernce count and return. + // + Group = Ip4FindGroup (IgmpCtrl, Address); + + if (Group != NULL) { + Group->RefCnt++; + return EFI_SUCCESS; + } + + // + // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address, + // send a report, then direct MNP to receive the multicast. + // + Group = NetAllocatePool (sizeof (IGMP_GROUP)); + + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Group->Address = Address; + Group->RefCnt = 1; + Group->DelayTime = IGMP_UNSOLICIATED_REPORT; + Group->ReportByUs = TRUE; + + Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip4SendIgmpReport (IpSb, Address); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Mnp->Groups (Mnp, TRUE, &Group->Mac); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + NetListInsertHead (&IgmpCtrl->Groups, &Group->Link); + return EFI_SUCCESS; + +ON_ERROR: + NetFreePool (Group); + return Status; +} + + +/** + Leave the IP4 multicast group on behavior of IpInstance. + + @param IpInstance The IP4 child that wants to leave the group + address + @param Address The group address to leave + + @retval EFI_NOT_FOUND The IP4 service instance isn't in the group + @retval EFI_SUCCESS Successfully leave the multicast group. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip4LeaveGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ) +{ + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + IP4_SERVICE *IpSb; + IGMP_SERVICE_DATA *IgmpCtrl; + IGMP_GROUP *Group; + EFI_STATUS Status; + + IpSb = IpInstance->Service; + IgmpCtrl = &IpSb->IgmpCtrl; + Mnp = IpSb->Mnp; + + Group = Ip4FindGroup (IgmpCtrl, Address); + + if (Group == NULL) { + return EFI_NOT_FOUND; + } + + // + // If more than one instance is in the group, decrease + // the RefCnt then return. + // + if (--Group->RefCnt > 0) { + return EFI_SUCCESS; + } + + // + // If multiple IP4 group addresses are mapped to the same + // multicast MAC address, don't configure the MNP to leave + // the MAC. + // + if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) { + Status = Mnp->Groups (Mnp, FALSE, &Group->Mac); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + return Status; + } + } + + // + // Send a leave report if the membership is reported by us + // and we are talking IGMPv2. + // + if (Group->ReportByUs && !IgmpCtrl->Igmpv1QuerySeen) { + Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address); + } + + NetListRemoveEntry (&Group->Link); + NetFreePool (Group); + + return EFI_SUCCESS; +} + + +/** + Handle the received IGMP message for the IP4 service instance. + + @param IpSb The IP4 service instance that received the message + @param Head The IP4 header of the received message + @param Packet The IGMP message, without IP4 header + + @retval EFI_INVALID_PARAMETER The IGMP message is malformated. + @retval EFI_SUCCESS The IGMP message is successfully processed. + +**/ +EFI_STATUS +Ip4IgmpHandle ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IGMP_SERVICE_DATA *IgmpCtrl; + IGMP_HEAD Igmp; + IGMP_GROUP *Group; + IP4_ADDR Address; + NET_LIST_ENTRY *Entry; + + IgmpCtrl = &IpSb->IgmpCtrl; + + // + // Must checksum over the whole packet, later IGMP version + // may employ message longer than 8 bytes. IP's header has + // already been trimmed off. + // + if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) { + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; + } + + // + // Copy the packet in case it is fragmented + // + NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp); + + switch (Igmp.Type) { + case IGMP_MEMBERSHIP_QUERY: + // + // If MaxRespTIme is zero, it is most likely that we are + // talking to a V1 router + // + if (Igmp.MaxRespTime == 0) { + IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT; + Igmp.MaxRespTime = 100; + } + + // + // Igmp is ticking once per second but MaxRespTime is in + // the unit of 100ms. + // + Igmp.MaxRespTime /= 10; + Address = NTOHL (Igmp.Group); + + if (Address == IP4_ALLSYSTEM_ADDRESS) { + break; + } + + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + + // + // If address is all zero, all the memberships will be reported. + // otherwise only one is reported. + // + if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) { + // + // If the timer is pending, only update it if the time left + // is longer than the MaxRespTime. TODO: randomize the DelayTime. + // + if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) { + Group->DelayTime = NET_MAX (1, Igmp.MaxRespTime); + } + } + } + + break; + + case IGMP_V1_MEMBERSHIP_REPORT: + case IGMP_V2_MEMBERSHIP_REPORT: + Address = NTOHL (Igmp.Group); + Group = Ip4FindGroup (IgmpCtrl, Address); + + if ((Group != NULL) && (Group->DelayTime > 0)) { + Group->DelayTime = 0; + Group->ReportByUs = FALSE; + } + + break; + } + + NetbufFree (Packet); + return EFI_SUCCESS; +} + + +/** + The periodical timer function for IGMP. It does the following + things: + 1. Decrease the Igmpv1QuerySeen to make it possible to refresh + the IGMP server type. + 2. Decrease the report timer for each IGMP group in "delaying + member" state. + + @param IpSb The IP4 service instance that is ticking + + @return None + +**/ +VOID +Ip4IgmpTicking ( + IN IP4_SERVICE *IpSb + ) +{ + IGMP_SERVICE_DATA *IgmpCtrl; + NET_LIST_ENTRY *Entry; + IGMP_GROUP *Group; + + IgmpCtrl = &IpSb->IgmpCtrl; + + if (IgmpCtrl->Igmpv1QuerySeen > 0) { + IgmpCtrl->Igmpv1QuerySeen--; + } + + // + // Decrease the report timer for each IGMP group in "delaying member" + // + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + ASSERT (Group->DelayTime >= 0); + + if (Group->DelayTime > 0) { + Group->DelayTime--; + + if (Group->DelayTime == 0) { + Ip4SendIgmpReport (IpSb, Group->Address); + Group->ReportByUs = TRUE; + } + } + } +} + + +/** + Add a group address to the array of group addresses. + The caller should make sure that no duplicated address + existed in the array. Although the function doesn't + assume the byte order of the both Source and Addr, the + network byte order is used by the caller. + + @param Source The array of group addresses to add to + @param Count The number of group addresses in the Source + @param Addr The IP4 multicast address to add + + @return NULL if failed to allocate memory for the new groups, + @return otherwise the new combined group addresses. + +**/ +IP4_ADDR * +Ip4CombineGroups ( + IN IP4_ADDR *Source, + IN UINT32 Count, + IN IP4_ADDR Addr + ) +{ + IP4_ADDR *Groups; + + Groups = NetAllocatePool (sizeof (IP4_ADDR) * (Count + 1)); + + if (Groups == NULL) { + return NULL; + } + + NetCopyMem (Groups, Source, Count * sizeof (IP4_ADDR)); + Groups[Count] = Addr; + + return Groups; +} + + +/** + Remove a group address frome the array of group addresses. + Although the function doesn't assume the byte order of the + both Groups and Addr, the network byte order is used by + the caller. + + @param Groups The array of group addresses to remove from + @param Count The number of group addresses in the Groups + @param Addr The IP4 multicast address to remove + + @return The nubmer of group addresses in the Groups after remove. + @return It is Count if the Addr isn't in the Groups. + +**/ +INTN +Ip4RemoveGroupAddr ( + IN IP4_ADDR *Groups, + IN UINT32 Count, + IN IP4_ADDR Addr + ) +{ + UINT32 Index; + + for (Index = 0; Index < Count; Index++) { + if (Groups[Index] == Addr) { + break; + } + } + + while (Index < Count - 1) { + Groups[Index] = Groups[Index + 1]; + Index++; + } + + return Index; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h new file mode 100644 index 0000000000..f7cb92c23f --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.h @@ -0,0 +1,120 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Igmp.h + +Abstract: + + +**/ + +#ifndef __EFI_IP4_IGMP_H__ +#define __EFI_IP4_IGMP_H__ + +#pragma pack(1) +typedef struct { + UINT8 Type; + UINT8 MaxRespTime; + UINT16 Checksum; + IP4_ADDR Group; +} IGMP_HEAD; +#pragma pack() + +// +// The status of multicast group. It isn't necessary to maintain +// explicit state of host state diagram. A group with non-zero +// DelayTime is in "delaying member" state. otherwise, it is in +// "idle member" state. +// +typedef struct { + NET_LIST_ENTRY Link; + INTN RefCnt; + IP4_ADDR Address; + INTN DelayTime; + BOOLEAN ReportByUs; + EFI_MAC_ADDRESS Mac; +} IGMP_GROUP; + +// +// The IGMP status. Each IP4 service instance has a IGMP_SERVICE_DATA +// attached. The Igmpv1QuerySeen remember whether the server on this +// connected network is v1 or v2. +// +typedef struct { + INTN Igmpv1QuerySeen; + NET_LIST_ENTRY Groups; +} IGMP_SERVICE_DATA; + +enum { + // + // IGMP message type + // + IGMP_MEMBERSHIP_QUERY = 0x11, + IGMP_V1_MEMBERSHIP_REPORT = 0x12, + IGMP_V2_MEMBERSHIP_REPORT = 0x16, + IGMP_LEAVE_GROUP = 0x17, + + IGMP_V1ROUTER_PRESENT = 400, + IGMP_UNSOLICIATED_REPORT = 10, +}; + +EFI_STATUS +Ip4InitIgmp ( + IN IP4_SERVICE *IpService + ); + +EFI_STATUS +Ip4JoinGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ); + +EFI_STATUS +Ip4LeaveGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ); + +EFI_STATUS +Ip4IgmpHandle ( + IN IP4_SERVICE *IpService, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ); + +VOID +Ip4IgmpTicking ( + IN IP4_SERVICE *IpService + ); + +IP4_ADDR * +Ip4CombineGroups ( + IN IP4_ADDR *SourceGroups, + IN UINT32 Count, + IN IP4_ADDR Addr + ); + +INTN +Ip4RemoveGroupAddr ( + IN IP4_ADDR *Group, + IN UINT32 GroupCnt, + IN IP4_ADDR Addr + ); + +IGMP_GROUP * +Ip4FindGroup ( + IN IGMP_SERVICE_DATA *IgmpCtrl, + IN IP4_ADDR Address + ); +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c new file mode 100644 index 0000000000..824524d508 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.c @@ -0,0 +1,2022 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Impl.c + +Abstract: + + +**/ + +#include "Ip4Impl.h" + + +/** + Get the IP child's current operational data. This can + all be used to get the underlying MNP and SNP data. + + @param This The IP4 protocol instance + @param Ip4ModeData The IP4 operation data + @param MnpConfigData The MNP configure data + @param SnpModeData The SNP operation data + + @retval EFI_INVALID_PARAMETER The parameter is invalid because This == NULL + @retval EFI_SUCCESS The operational parameter is returned. + @retval Others Failed to retrieve the IP4 route table. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4GetModeData ( + IN CONST EFI_IP4_PROTOCOL *This, + OUT EFI_IP4_MODE_DATA *Ip4ModeData, OPTIONAL + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData, OPTIONAL + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + IP4_SERVICE *IpSb; + EFI_IP4_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP4_ADDR Ip; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (Ip4ModeData != NULL) { + // + // IsStarted is "whether the EfiIp4Configure has been called". + // IsConfigured is "whether the station address has been configured" + // + Ip4ModeData->IsStarted = (BOOLEAN)(IpInstance->State == IP4_STATE_CONFIGED); + CopyMem (&Ip4ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP4_CONFIG_DATA)); + Ip4ModeData->IsConfigured = FALSE; + + Ip4ModeData->GroupCount = IpInstance->GroupCount; + Ip4ModeData->GroupTable = (EFI_IPv4_ADDRESS *) IpInstance->Groups; + + Ip4ModeData->IcmpTypeCount = 23; + Ip4ModeData->IcmpTypeList = mIp4SupportedIcmp; + + Ip4ModeData->RouteTable = NULL; + Ip4ModeData->RouteCount = 0; + + // + // return the current station address for this IP child. So, + // the user can get the default address through this. Some + // application wants to know it station address even it is + // using the default one, such as a ftp server. + // + if (Ip4ModeData->IsStarted) { + Config = &Ip4ModeData->ConfigData; + + Ip = HTONL (IpInstance->Interface->Ip); + NetCopyMem (&Config->StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Ip = HTONL (IpInstance->Interface->SubnetMask); + NetCopyMem (&Config->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Ip4ModeData->IsConfigured = IpInstance->Interface->Configured; + + // + // Build a EFI route table for user from the internal route table. + // + Status = Ip4BuildEfiRouteTable (IpInstance); + + if (EFI_ERROR (Status)) { + NET_RESTORE_TPL (OldTpl); + return Status; + } + + Ip4ModeData->RouteTable = IpInstance->EfiRouteTable; + Ip4ModeData->RouteCount = IpInstance->EfiRouteCount; + } + } + + if (MnpConfigData != NULL) { + CopyMem (MnpConfigData, &IpSb->MnpConfigData, sizeof (EFI_MANAGED_NETWORK_CONFIG_DATA)); + } + + if (SnpModeData != NULL) { + CopyMem (SnpModeData, &IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE)); + } + + NET_RESTORE_TPL (OldTpl); + return EFI_SUCCESS; +} + + +/** + Config the MNP parameter used by IP. The IP driver use one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. And it will enable/disable + the promiscous receive according to whether there is IP child + enable that or not. If Force isn't false, it will iterate through + all the IP children to check whether the promiscuous receive + setting has been changed. If it hasn't been changed, it won't + reconfigure the MNP. If Force is true, the MNP is configured no + matter whether that is changed or not. + + @param IpSb The IP4 service instance that is to be changed. + @param Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP is successfully configured/reconfigured. + @retval Others Configuration failed. + +**/ +EFI_STATUS +Ip4ServiceConfigMnp ( + IN IP4_SERVICE *IpSb, + IN BOOLEAN Force + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *ProtoEntry; + IP4_INTERFACE *IpIf; + IP4_PROTOCOL *IpInstance; + BOOLEAN Reconfig; + BOOLEAN PromiscReceive; + EFI_STATUS Status; + + Reconfig = FALSE; + PromiscReceive = FALSE; + + if (!Force) { + // + // Iterate through the IP children to check whether promiscuous + // receive setting has been changed. Update the interface's receive + // filter also. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + IpIf->PromiscRecv = FALSE; + + NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP4_PROTOCOL, AddrLink); + + if (IpInstance->ConfigData.AcceptPromiscuous) { + IpIf->PromiscRecv = TRUE; + PromiscReceive = TRUE; + } + } + } + + // + // If promiscuous receive isn't changed, it isn't necessary to reconfigure. + // + if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) { + return EFI_SUCCESS; + } + + Reconfig = TRUE; + IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive; + } + + Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData); + + // + // recover the original configuration if failed to set the configure. + // + if (EFI_ERROR (Status) && Reconfig) { + IpSb->MnpConfigData.EnablePromiscuousReceive = !PromiscReceive; + } + + return Status; +} + + +/** + The event handle for IP4 auto configuration. If IP is asked + to reconfigure the default address. The original default + interface and route table are removed as the default. If there + is active IP children using the default address, the interface + will remain valid until all the children have freed their + references. If IP is signalled when auto configuration is done, + it will configure the default interface and default route table + with the configuration information retrieved by IP4_CONFIGURE. + + @param Event The event that is signalled. + @param Context The IP4 service binding instance. + + @return None + +**/ +VOID +EFIAPI +Ip4AutoConfigCallBack ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_IP4_CONFIG_PROTOCOL *Ip4Config; + EFI_IP4_IPCONFIG_DATA *Data; + EFI_IP4_ROUTE_TABLE *RouteEntry; + IP4_SERVICE *IpSb; + IP4_ROUTE_TABLE *RouteTable; + IP4_INTERFACE *IpIf; + EFI_STATUS Status; + UINTN Len; + UINT32 Index; + + IpSb = (IP4_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE); + + Ip4Config = IpSb->Ip4Config; + + // + // IP is asked to do the reconfiguration. If the default interface + // has been configured, release the default interface and route + // table, then create a new one. If there are some IP children + // using it, the interface won't be physically freed until all the + // children have released their reference to it. Also remember to + // restart the receive on the default address. IP4 driver only receive + // frames on the default address, and when the default interface is + // freed, Ip4AcceptFrame won't be informed. + // + if (Event == IpSb->ReconfigEvent) { + + if (IpSb->DefaultInterface->Configured) { + IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image); + + if (IpIf == NULL) { + return; + } + + RouteTable = Ip4CreateRouteTable (); + + if (RouteTable == NULL) { + Ip4FreeInterface (IpIf, NULL); + return; + } + + Ip4CancelReceive (IpSb->DefaultInterface); + Ip4FreeInterface (IpSb->DefaultInterface, NULL); + Ip4FreeRouteTable (IpSb->DefaultRouteTable); + + IpSb->DefaultInterface = IpIf; + NetListInsertHead (&IpSb->Interfaces, &IpIf->Link); + + IpSb->DefaultRouteTable = RouteTable; + Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb); + } + + Ip4Config->Stop (Ip4Config); + Ip4Config->Start (Ip4Config, IpSb->DoneEvent, IpSb->ReconfigEvent); + return ; + } + + // + // Get the configure data in two steps: get the length then the data. + // + Len = 0; + + if (Ip4Config->GetData (Ip4Config, &Len, NULL) != EFI_BUFFER_TOO_SMALL) { + return ; + } + + Data = NetAllocatePool (Len); + + if (Data == NULL) { + return ; + } + + Status = Ip4Config->GetData (Ip4Config, &Len, Data); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + IpIf = IpSb->DefaultInterface; + + // + // If the default address has been configured don't change it. + // This is unlikely to happen if EFI_IP4_CONFIG protocol has + // informed us to reconfigure each time it wants to change the + // configuration parameters. + // + if (IpIf->Configured) { + goto ON_EXIT; + } + + // + // Set the default interface's address, then add a directed + // route for it, that is, the route whose nexthop is zero. + // + Status = Ip4SetAddress ( + IpIf, + EFI_NTOHL (Data->StationAddress), + EFI_NTOHL (Data->SubnetMask) + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Ip4AddRoute ( + IpSb->DefaultRouteTable, + EFI_NTOHL (Data->StationAddress), + EFI_NTOHL (Data->SubnetMask), + IP4_ALLZERO_ADDRESS + ); + + // + // Add routes returned by EFI_IP4_CONFIG protocol. + // + for (Index = 0; Index < Data->RouteTableSize; Index++) { + RouteEntry = &Data->RouteTable[Index]; + + Ip4AddRoute ( + IpSb->DefaultRouteTable, + EFI_NTOHL (RouteEntry->SubnetAddress), + EFI_NTOHL (RouteEntry->SubnetMask), + EFI_NTOHL (RouteEntry->GatewayAddress) + ); + } + + IpSb->State = IP4_SERVICE_CONFIGED; + + Ip4SetVariableData (IpSb); + +ON_EXIT: + NetFreePool (Data); +} + + +/** + Start the auto configuration for this IP service instance. + It will locates the EFI_IP4_CONFIG_PROTOCOL, then start the + auto configuration. + + @param IpSb The IP4 service instance to configure + + @retval EFI_SUCCESS The auto configuration is successfull started + @retval Others Failed to start auto configuration. + +**/ +EFI_STATUS +Ip4StartAutoConfig ( + IN IP4_SERVICE *IpSb + ) +{ + EFI_IP4_CONFIG_PROTOCOL *Ip4Config; + EFI_STATUS Status; + + if (IpSb->State > IP4_SERVICE_UNSTARTED) { + return EFI_SUCCESS; + } + + // + // Create the DoneEvent and ReconfigEvent to call EFI_IP4_CONFIG + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NET_TPL_LOCK, + Ip4AutoConfigCallBack, + IpSb, + &IpSb->DoneEvent + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip4AutoConfigCallBack, + IpSb, + &IpSb->ReconfigEvent + ); + + if (EFI_ERROR (Status)) { + goto CLOSE_DONE_EVENT; + } + + // + // Open the EFI_IP4_CONFIG protocol then start auto configure + // + Status = gBS->OpenProtocol ( + IpSb->Controller, + &gEfiIp4ConfigProtocolGuid, + (VOID **) &Ip4Config, + IpSb->Image, + IpSb->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE + ); + + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto CLOSE_RECONFIG_EVENT; + } + + Status = Ip4Config->Start (Ip4Config, IpSb->DoneEvent, IpSb->ReconfigEvent); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + IpSb->Controller, + &gEfiIp4ConfigProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + goto CLOSE_RECONFIG_EVENT; + } + + IpSb->Ip4Config = Ip4Config; + IpSb->State = IP4_SERVICE_STARTED; + return Status; + +CLOSE_RECONFIG_EVENT: + gBS->CloseEvent (IpSb->ReconfigEvent); + IpSb->ReconfigEvent = NULL; + +CLOSE_DONE_EVENT: + gBS->CloseEvent (IpSb->DoneEvent); + IpSb->DoneEvent = NULL; + + return Status; +} + + +/** + Intiialize the IP4_PROTOCOL structure to the unconfigured states. + + @param IpSb The IP4 service instance. + @param IpInstance The IP4 child instance. + + @return None + +**/ +VOID +Ip4InitProtocol ( + IN IP4_SERVICE *IpSb, + IN IP4_PROTOCOL *IpInstance + ) +{ + ASSERT ((IpSb != NULL) && (IpInstance != NULL)); + + NetZeroMem (IpInstance, sizeof (IP4_PROTOCOL)); + + IpInstance->Signature = IP4_PROTOCOL_SIGNATURE; + CopyMem (&IpInstance->Ip4Proto, &mEfiIp4ProtocolTemplete, sizeof (EFI_IP4_PROTOCOL)); + IpInstance->State = IP4_STATE_UNCONFIGED; + IpInstance->Service = IpSb; + + NetListInit (&IpInstance->Link); + NetMapInit (&IpInstance->RxTokens); + NetMapInit (&IpInstance->TxTokens); + NetListInit (&IpInstance->Received); + NetListInit (&IpInstance->Delivered); + NetListInit (&IpInstance->AddrLink); + + NET_RECYCLE_LOCK_INIT (&IpInstance->RecycleLock); +} + + +/** + Configure the IP4 child. If the child is already configured, + change the configuration parameter. Otherwise configure it + for the first time. The caller should validate the configuration + before deliver them to it. It also don't do configure NULL. + + @param IpInstance The IP4 child to configure. + @param Config The configure data. + + @retval EFI_SUCCESS The IP4 child is successfully configured. + @retval EFI_DEVICE_ERROR Failed to free the pending transive or to + configure underlying MNP or other errors. + @retval EFI_NO_MAPPING The IP4 child is configured to use default + address, but the default address hasn't been + configured. The IP4 child doesn't need to be + reconfigured when default address is configured. + +**/ +EFI_STATUS +Ip4ConfigProtocol ( + IN IP4_PROTOCOL *IpInstance, + IN EFI_IP4_CONFIG_DATA *Config + ) +{ + IP4_SERVICE *IpSb; + IP4_INTERFACE *IpIf; + EFI_STATUS Status; + IP4_ADDR Ip; + IP4_ADDR Netmask; + + IpSb = IpInstance->Service; + + // + // User is changing packet filters. It must be stopped + // before the station address can be changed. + // + if (IpInstance->State == IP4_STATE_CONFIGED) { + // + // Cancel all the pending transmit/receive from upper layer + // + Status = Ip4Cancel (IpInstance, NULL); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CopyMem (&IpInstance->ConfigData, Config, sizeof (EFI_IP4_CONFIG_DATA)); + return EFI_SUCCESS; + } + + // + // Configure a fresh IP4 protocol instance. Create a route table. + // Each IP child has its own route table, which may point to the + // default table if it is using default address. + // + Status = EFI_OUT_OF_RESOURCES; + IpInstance->RouteTable = Ip4CreateRouteTable (); + + if (IpInstance->RouteTable == NULL) { + return Status; + } + + // + // Set up the interface. + // + NetCopyMem (&Ip, &Config->StationAddress, sizeof (IP4_ADDR)); + NetCopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR)); + + Ip = NTOHL (Ip); + Netmask = NTOHL (Netmask); + + if (!Config->UseDefaultAddress) { + // + // Find whether there is already an interface with the same + // station address. All the instances with the same station + // address shares one interface. + // + IpIf = Ip4FindStationAddress (IpSb, Ip, Netmask); + + if (IpIf != NULL) { + NET_GET_REF (IpIf); + + } else { + IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image); + + if (IpIf == NULL) { + goto ON_ERROR; + } + + Status = Ip4SetAddress (IpIf, Ip, Netmask); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + Ip4FreeInterface (IpIf, IpInstance); + goto ON_ERROR; + } + + NetListInsertTail (&IpSb->Interfaces, &IpIf->Link); + } + + // + // Add a route to this connected network in the route table + // + Ip4AddRoute (IpInstance->RouteTable, Ip, Netmask, IP4_ALLZERO_ADDRESS); + + } else { + // + // Use the default address. If the default configuration hasn't + // been started, start it. + // + if (IpSb->State == IP4_SERVICE_UNSTARTED) { + Status = Ip4StartAutoConfig (IpSb); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + IpIf = IpSb->DefaultInterface; + NET_GET_REF (IpSb->DefaultInterface); + + // + // If default address is used, so is the default route table. + // Any route set by the instance has the precedence over the + // routes in the default route table. Link the default table + // after the instance's table. Routing will search the local + // table first. + // + NET_GET_REF (IpSb->DefaultRouteTable); + IpInstance->RouteTable->Next = IpSb->DefaultRouteTable; + } + + IpInstance->Interface = IpIf; + NetListInsertTail (&IpIf->IpInstances, &IpInstance->AddrLink); + + CopyMem (&IpInstance->ConfigData, Config, sizeof (EFI_IP4_CONFIG_DATA)); + IpInstance->State = IP4_STATE_CONFIGED; + + // + // Although EFI_NO_MAPPING is an error code, the IP child has been + // successfully configured and doesn't need reconfiguration when + // default address is acquired. + // + if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + return EFI_NO_MAPPING; + } + + return EFI_SUCCESS; + +ON_ERROR: + Ip4FreeRouteTable (IpInstance->RouteTable); + IpInstance->RouteTable = NULL; + return Status; +} + + +/** + Clean up the IP4 child, release all the resources used by it. + + @param IpInstance The IP4 child to clean up. + + @retval EFI_SUCCESS The IP4 child is cleaned up + @retval EFI_DEVICE_ERROR Some resources failed to be released + +**/ +EFI_STATUS +Ip4CleanProtocol ( + IN IP4_PROTOCOL *IpInstance + ) +{ + if (EFI_ERROR (Ip4Cancel (IpInstance, NULL))) { + return EFI_DEVICE_ERROR; + } + + if (EFI_ERROR (Ip4Groups (IpInstance, FALSE, NULL))) { + return EFI_DEVICE_ERROR; + } + + // + // Some packets haven't been recycled. It is because either the + // user forgets to recycle the packets, or because the callback + // hasn't been called. Just leave it alone. + // + if (!NetListIsEmpty (&IpInstance->Delivered)) { + ; + } + + if (IpInstance->Interface != NULL) { + NetListRemoveEntry (&IpInstance->AddrLink); + Ip4FreeInterface (IpInstance->Interface, IpInstance); + IpInstance->Interface = NULL; + } + + if (IpInstance->RouteTable != NULL) { + if (IpInstance->RouteTable->Next != NULL) { + Ip4FreeRouteTable (IpInstance->RouteTable->Next); + } + + Ip4FreeRouteTable (IpInstance->RouteTable); + IpInstance->RouteTable = NULL; + } + + if (IpInstance->EfiRouteTable != NULL) { + NetFreePool (IpInstance->EfiRouteTable); + IpInstance->EfiRouteTable = NULL; + IpInstance->EfiRouteCount = 0; + } + + if (IpInstance->Groups != NULL) { + NetFreePool (IpInstance->Groups); + IpInstance->Groups = NULL; + IpInstance->GroupCount = 0; + } + + NetMapClean (&IpInstance->TxTokens); + + NetMapClean (&IpInstance->RxTokens); + + return EFI_SUCCESS; +} + + +/** + Validate that Ip/Netmask pair is OK to be used as station + address. Only continuous netmasks are supported. and check + that StationAddress is a unicast address on the newtwork. + + @param Ip The IP address to validate + @param Netmask The netmaks of the IP + + @retval TRUE The Ip/Netmask pair is valid + @retval FALSE The + +**/ +BOOLEAN +Ip4StationAddressValid ( + IN IP4_ADDR Ip, + IN IP4_ADDR Netmask + ) +{ + IP4_ADDR NetBrdcastMask; + INTN Len; + INTN Type; + + // + // Only support the station address with 0.0.0.0/0 to enable DHCP client. + // + if (Netmask == IP4_ALLZERO_ADDRESS) { + return (BOOLEAN) (Ip == IP4_ALLZERO_ADDRESS); + } + + // + // Only support the continuous net masks + // + if ((Len = NetGetMaskLength (Netmask)) == IP4_MASK_NUM) { + return FALSE; + } + + // + // Station address can't be class D or class E address + // + if ((Type = NetGetIpClass (Ip)) > IP4_ADDR_CLASSC) { + return FALSE; + } + + // + // Station address can't be subnet broadcast/net broadcast address + // + if ((Ip == (Ip & Netmask)) || (Ip == (Ip | ~Netmask))) { + return FALSE; + } + + NetBrdcastMask = mIp4AllMasks[NET_MIN (Len, Type << 3)]; + + if (Ip == (Ip | ~NetBrdcastMask)) { + return FALSE; + } + + return TRUE; +} + + +/** + Configure the EFI_IP4_PROTOCOL instance. If IpConfigData is NULL, + the instance is cleaned up. If the instance hasn't been configure + before, it will be initialized. Otherwise, the filter setting of + the instance is updated. + + @param This The IP4 child to configure + @param IpConfigData The configuration to apply. If NULL, clean it up. + + @retval EFI_INVALID_PARAMETER The parameter is invalid + @retval EFI_NO_MAPPING The default address hasn't been configured and the + instance wants to use it. + @retval EFI_SUCCESS The instance is configured. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4Configure ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_CONFIG_DATA *IpConfigData OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_IP4_CONFIG_DATA *Current; + EFI_TPL OldTpl; + EFI_STATUS Status; + BOOLEAN AddrOk; + IP4_ADDR IpAddress; + IP4_ADDR SubnetMask; + + // + // First, validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // Validate the configuration first. + // + if (IpConfigData != NULL) { + // + // This implementation doesn't support RawData + // + if (IpConfigData->RawData) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + + NetCopyMem (&IpAddress, &IpConfigData->StationAddress, sizeof (IP4_ADDR)); + NetCopyMem (&SubnetMask, &IpConfigData->SubnetMask, sizeof (IP4_ADDR)); + + IpAddress = NTOHL (IpAddress); + SubnetMask = NTOHL (SubnetMask); + + // + // Check whether the station address is a valid unicast address + // + if (!IpConfigData->UseDefaultAddress) { + AddrOk = Ip4StationAddressValid (IpAddress, SubnetMask); + + if (!AddrOk) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + + // + // User can only update packet filters when already configured. + // If it wants to change the station address, it must configure(NULL) + // the instance first. + // + if (IpInstance->State == IP4_STATE_CONFIGED) { + Current = &IpInstance->ConfigData; + + if (Current->UseDefaultAddress != IpConfigData->UseDefaultAddress) { + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + if (!Current->UseDefaultAddress && + (!EFI_IP4_EQUAL (Current->StationAddress, IpConfigData->StationAddress) || + !EFI_IP4_EQUAL (Current->SubnetMask, IpConfigData->SubnetMask))) { + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + if (Current->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + return EFI_NO_MAPPING; + } + } + } + + // + // Configure the instance or clean it up. + // + if (IpConfigData != NULL) { + Status = Ip4ConfigProtocol (IpInstance, IpConfigData); + } else { + Status = Ip4CleanProtocol (IpInstance); + + // + // Don't change the state if it is DESTORY, consider the following + // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped, + // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED, + // the unload fails miserably. + // + if (IpInstance->State == IP4_STATE_CONFIGED) { + IpInstance->State = IP4_STATE_UNCONFIGED; + } + } + + // + // Update the MNP's configure data. Ip4ServiceConfigMnp will check + // whether it is necessary to reconfigure the MNP. + // + Ip4ServiceConfigMnp (IpInstance->Service, FALSE); + + // + // Update the variable data. + // + Ip4SetVariableData (IpInstance->Service); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; + +} + + +/** + Change the IP4 child's multicast setting. The caller + should make sure that the parameters is valid. + + @param IpInstance The IP4 child to change the setting. + @param JoinFlag TRUE to join the group, otherwise leave it + @param GroupAddress The target group address + + @retval EFI_ALREADY_STARTED Want to join the group, but already a member of it + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_DEVICE_ERROR Failed to set the group configuraton + @retval EFI_SUCCESS Successfully updated the group setting. + @retval EFI_NOT_FOUND Try to leave the group which it isn't a member. + +**/ +EFI_STATUS +Ip4Groups ( + IN IP4_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL + ) +{ + IP4_ADDR *Members; + IP4_ADDR Group; + UINT32 Index; + + // + // Add it to the instance's Groups, and join the group by IGMP. + // IpInstance->Groups is in network byte order. IGMP operates in + // host byte order + // + if (JoinFlag) { + NetCopyMem (&Group, GroupAddress, sizeof (IP4_ADDR)); + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (IpInstance->Groups[Index] == Group) { + return EFI_ALREADY_STARTED; + } + } + + Members = Ip4CombineGroups (IpInstance->Groups, IpInstance->GroupCount, Group); + + if (Members == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (EFI_ERROR (Ip4JoinGroup (IpInstance, NTOHL (Group)))) { + NetFreePool (Members); + return EFI_DEVICE_ERROR; + } + + if (IpInstance->Groups != NULL) { + NetFreePool (IpInstance->Groups); + } + + IpInstance->Groups = Members; + IpInstance->GroupCount++; + + return EFI_SUCCESS; + } + + // + // Leave the group. Leave all the groups if GroupAddress is NULL. + // Must iterate from the end to the beginning because the GroupCount + // is decreamented each time an address is removed.. + // + for (Index = IpInstance->GroupCount; Index > 0 ; Index--) { + Group = IpInstance->Groups[Index - 1]; + + if ((GroupAddress == NULL) || EFI_IP4_EQUAL (Group, *GroupAddress)) { + if (EFI_ERROR (Ip4LeaveGroup (IpInstance, NTOHL (Group)))) { + return EFI_DEVICE_ERROR; + } + + Ip4RemoveGroupAddr (IpInstance->Groups, IpInstance->GroupCount, Group); + IpInstance->GroupCount--; + + if (IpInstance->GroupCount == 0) { + ASSERT (Index == 1); + + NetFreePool (IpInstance->Groups); + IpInstance->Groups = NULL; + } + + if (GroupAddress != NULL) { + return EFI_SUCCESS; + } + } + } + + return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS); +} + + +/** + Change the IP4 child's multicast setting. If JoinFlag is true, + the child wants to join the group. Otherwise it wants to leave + the group. If JoinFlag is false, and GroupAddress is NULL, + it will leave all the groups which is a member. + + @param This The IP4 child to change the setting. + @param JoinFlag TRUE to join the group, otherwise leave it. + @param GroupAddress The target group address + + @retval EFI_INVALID_PARAMETER The parameters are invalid + @retval EFI_SUCCESS The group setting has been changed. + @retval Otherwise It failed to change the setting. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4Groups ( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP4_ADDR McastIp; + + if ((This == NULL) || (JoinFlag && (GroupAddress == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (GroupAddress != NULL) { + NetCopyMem (&McastIp, GroupAddress, sizeof (IP4_ADDR)); + + if (!IP4_IS_MULTICAST (NTOHL (McastIp))) { + return EFI_INVALID_PARAMETER; + } + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + Status = Ip4Groups (IpInstance, JoinFlag, GroupAddress); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Modify the IP child's route table. Each instance has its own + route table. + + @param This The IP4 child to modify the route + @param DeleteRoute TRUE to delete the route, otherwise add it + @param SubnetAddress The destination network + @param SubnetMask The destination network's mask + @param GatewayAddress The next hop address. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_SUCCESS The route table is successfully modified. + @retval Others Failed to modify the route table + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4Routes ( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ) +{ + IP4_PROTOCOL *IpInstance; + IP4_INTERFACE *IpIf; + IP4_ADDR Dest; + IP4_ADDR Netmask; + IP4_ADDR Nexthop; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // First, validate the parameters + // + if ((This == NULL) || (SubnetAddress == NULL) || + (SubnetMask == NULL) || (GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + NetCopyMem (&Dest, SubnetAddress, sizeof (IP4_ADDR)); + NetCopyMem (&Netmask, SubnetMask, sizeof (IP4_ADDR)); + NetCopyMem (&Nexthop, GatewayAddress, sizeof (IP4_ADDR)); + + Dest = NTOHL (Dest); + Netmask = NTOHL (Netmask); + Nexthop = NTOHL (Nexthop); + + IpIf = IpInstance->Interface; + + if (!IP4_IS_VALID_NETMASK (Netmask)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + // + // the gateway address must be a unicast on the connected network if not zero. + // + if ((Nexthop != IP4_ALLZERO_ADDRESS) && + (!IP4_NET_EQUAL (Nexthop, IpIf->Ip, IpIf->SubnetMask) || + IP4_IS_BROADCAST (Ip4GetNetCast (Nexthop, IpIf)))) { + + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (DeleteRoute) { + Status = Ip4DelRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop); + } else { + Status = Ip4AddRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop); + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Check whether the user's token or event has already + been enqueue on IP4's list. + + @param Map The container of either user's transmit or receive + token. + @param Item Current item to check against + @param Context The Token to check againist. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +STATIC +EFI_STATUS +Ip4TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + EFI_IP4_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_IP4_COMPLETION_TOKEN *) Context; + TokenInItem = (EFI_IP4_COMPLETION_TOKEN *) Item->Key; + + if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + Validate the user's token against current station address. + + @param Token User's token to validate + @param IpIf The IP4 child's interface. + + @retval EFI_INVALID_PARAMETER Some parameters are invalid + @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long. + @retval EFI_SUCCESS The token is OK + +**/ +STATIC +EFI_STATUS +Ip4TxTokenValid ( + IN EFI_IP4_COMPLETION_TOKEN *Token, + IN IP4_INTERFACE *IpIf + ) +{ + EFI_IP4_TRANSMIT_DATA *TxData; + EFI_IP4_OVERRIDE_DATA *Override; + IP4_ADDR Src; + IP4_ADDR Gateway; + UINT32 Offset; + UINT32 Index; + UINT32 HeadLen; + + if ((Token == NULL) || (Token->Event == NULL) || (Token->Packet.TxData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + TxData = Token->Packet.TxData; + + // + // Check the IP options: no more than 40 bytes and format is OK + // + if (TxData->OptionsLength != 0) { + if ((TxData->OptionsLength > 40) || (TxData->OptionsBuffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Ip4OptionIsValid (TxData->OptionsBuffer, TxData->OptionsLength, FALSE)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Check the fragment table: no empty fragment, and length isn't bogus + // + if ((TxData->TotalDataLength == 0) || (TxData->FragmentCount == 0)) { + return EFI_INVALID_PARAMETER; + } + + Offset = TxData->TotalDataLength; + + for (Index = 0; Index < TxData->FragmentCount; Index++) { + if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) || + (TxData->FragmentTable[Index].FragmentLength == 0)) { + + return EFI_INVALID_PARAMETER; + } + + Offset -= TxData->FragmentTable[Index].FragmentLength; + } + + if (Offset != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Check the source and gateway: they must be a valid unicast. + // Gateway must also be on the connected network. + // + if (TxData->OverrideData) { + Override = TxData->OverrideData; + + NetCopyMem (&Src, &Override->SourceAddress, sizeof (IP4_ADDR)); + NetCopyMem (&Gateway, &Override->GatewayAddress, sizeof (IP4_ADDR)); + + Src = NTOHL (Src); + Gateway = NTOHL (Gateway); + + if ((NetGetIpClass (Src) > IP4_ADDR_CLASSC) || + (Src == IP4_ALLONE_ADDRESS) || + IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) { + + return EFI_INVALID_PARAMETER; + } + + // + // If gateway isn't zero, it must be a unicast address, and + // on the connected network. + // + if ((Gateway != IP4_ALLZERO_ADDRESS) && + ((NetGetIpClass (Gateway) > IP4_ADDR_CLASSC) || + !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask) || + IP4_IS_BROADCAST (Ip4GetNetCast (Gateway, IpIf)))) { + + return EFI_INVALID_PARAMETER; + } + } + + // + // Check the packet length: Head length and packet length all has a limit + // + HeadLen = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03); + + if ((HeadLen > IP4_MAX_HEADLEN) || + (TxData->TotalDataLength + HeadLen > IP4_MAX_PACKET_SIZE)) { + + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although it seems this function is pretty simple, + there are some subtle things. + When user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP4_TXTOKEN_WRAP and the data + is wrapped in an net buffer. the net buffer's Free function is + set to Ip4FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip4Output for + transmission. If something error happened before that, the buffer + is freed, which in turn will free the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be fired because we are still in the EfiIp4Transmit. If + the buffer has been sent by Ip4Output, it should be removed from + the TxToken map and user's event signaled. The token wrap and buffer + are bound together. Check the comments in Ip4Output for information + about IP fragmentation. + + @param Context The token's wrap + + @return None + +**/ +STATIC +VOID +Ip4FreeTxToken ( + IN VOID *Context + ) +{ + IP4_TXTOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + + Wrap = (IP4_TXTOKEN_WRAP *) Context; + + // + // Find the token in the instance's map. EfiIp4Transmit put the + // token to the map. If that failed, NetMapFindKey will return NULL. + // + Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token); + + if (Item != NULL) { + NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL); + } + + if (Wrap->Sent) { + gBS->SignalEvent (Wrap->Token->Event); + } + + NetFreePool (Wrap); +} + + +/** + The callback function to Ip4Output to update the transmit status. + + @param Ip4Instance The Ip4Instance that request the transmit. + @param Packet The user's transmit request + @param IoStatus The result of the transmission + @param Flag Not used during transmission + @param Context The token's wrap. + + @return None + +**/ +STATIC +VOID +Ip4OnPacketSent ( + IP4_PROTOCOL *Ip4Instance, + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 Flag, + VOID *Context + ) +{ + IP4_TXTOKEN_WRAP *Wrap; + + // + // This is the transmission request from upper layer, + // not the IP4 driver itself. + // + ASSERT (Ip4Instance != NULL); + + // + // The first fragment of the packet has been sent. Update + // the token's status. That is, if fragmented, the transmit's + // status is the first fragment's status. The Wrap will be + // release when all the fragments are release. Check the comments + // in Ip4FreeTxToken and Ip4Output for information. + // + Wrap = (IP4_TXTOKEN_WRAP *) Context; + Wrap->Token->Status = IoStatus; + + NetbufFree (Wrap->Packet); +} + + +/** + Transmit the user's data asynchronously. When transmission + completed,the Token's status is updated and its event signalled. + + @param This The IP4 child instance + @param Token The user's transmit token, which contains user's + data, the result and an event to signal when + completed. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_NOT_STARTED The IP4 child hasn't been started. + @retval EFI_SUCCESS The user's data has been successfully enqueued + for transmission. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4Transmit ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ) +{ + IP4_SERVICE *IpSb; + IP4_PROTOCOL *IpInstance; + IP4_INTERFACE *IpIf; + IP4_TXTOKEN_WRAP *Wrap; + EFI_IP4_TRANSMIT_DATA *TxData; + EFI_IP4_CONFIG_DATA *Config; + EFI_IP4_OVERRIDE_DATA *Override; + IP4_HEAD Head; + IP4_ADDR GateWay; + EFI_STATUS Status; + EFI_TPL OldTpl; + BOOLEAN DontFragment; + UINT32 HeadLen; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + IpSb = IpInstance->Service; + IpIf = IpInstance->Interface; + Config = &IpInstance->ConfigData; + + if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + // + // make sure that token is properly formated + // + Status = Ip4TxTokenValid (Token, IpIf); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check whether the token or signal already existed. + // + if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip4TokenExist, Token))) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Build the IP header, need to fill in the Tos, TotalLen, Id, + // fragment, Ttl, protocol, Src, and Dst. + // + TxData = Token->Packet.TxData; + + NetCopyMem (&Head.Dst, &TxData->DestinationAddress, sizeof (IP4_ADDR)); + Head.Dst = NTOHL (Head.Dst); + + if (TxData->OverrideData) { + Override = TxData->OverrideData; + Head.Protocol = Override->Protocol; + Head.Tos = Override->TypeOfService; + Head.Ttl = Override->TimeToLive; + DontFragment = Override->DoNotFragment; + + NetCopyMem (&Head.Src, &Override->SourceAddress, sizeof (IP4_ADDR)); + NetCopyMem (&GateWay, &Override->GatewayAddress, sizeof (IP4_ADDR)); + + Head.Src = NTOHL (Head.Src); + GateWay = NTOHL (GateWay); + } else { + Head.Src = IpIf->Ip; + GateWay = IP4_ALLZERO_ADDRESS; + Head.Protocol = Config->DefaultProtocol; + Head.Tos = Config->TypeOfService; + Head.Ttl = Config->TimeToLive; + DontFragment = Config->DoNotFragment; + } + + Head.Fragment = IP4_HEAD_FRAGMENT_FIELD (DontFragment, FALSE, 0); + HeadLen = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03); + + // + // If don't fragment and fragment needed, return error + // + if (DontFragment && (TxData->TotalDataLength + HeadLen > IpSb->SnpMode.MaxPacketSize)) { + Status = EFI_BAD_BUFFER_SIZE; + goto ON_EXIT; + } + + // + // OK, it survives all the validation check. Wrap the token in + // a IP4_TXTOKEN_WRAP and the data in a netbuf + // + Status = EFI_OUT_OF_RESOURCES; + Wrap = NetAllocatePool (sizeof (IP4_TXTOKEN_WRAP)); + if (Wrap == NULL) { + goto ON_EXIT; + } + + Wrap->IpInstance = IpInstance; + Wrap->Token = Token; + Wrap->Sent = FALSE; + Wrap->Life = IP4_US_TO_SEC (Config->TransmitTimeout); + Wrap->Packet = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + IP4_MAX_HEADLEN, + 0, + Ip4FreeTxToken, + Wrap + ); + + if (Wrap->Packet == NULL) { + NetFreePool (Wrap); + goto ON_EXIT; + } + + Token->Status = EFI_NOT_READY; + + if (EFI_ERROR (NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap))) { + // + // NetbufFree will call Ip4FreeTxToken, which in turn will + // free the IP4_TXTOKEN_WRAP. Now, the token wrap hasn't been + // enqueued. + // + NetbufFree (Wrap->Packet); + goto ON_EXIT; + } + + Status = Ip4Output ( + IpSb, + IpInstance, + Wrap->Packet, + &Head, + TxData->OptionsBuffer, + TxData->OptionsLength, + GateWay, + Ip4OnPacketSent, + Wrap + ); + + if (EFI_ERROR (Status)) { + NetbufFree (Wrap->Packet); + goto ON_EXIT; + } + + // + // Mark the packet sent, so when Ip4FreeTxToken is called, it + // will signal the upper layer. + // + Wrap->Sent = TRUE; + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Receive a packet for the upper layer. If there are packets + pending on the child's receive queue, the receive request + will be fulfilled immediately. Otherwise, the request is + enqueued. When receive request is completed, the status in + the Token is updated and its event is signalled. + + @param This The IP4 child to receive packet. + @param Token The user's receive token + + @retval EFI_INVALID_PARAMETER The token is invalid. + @retval EFI_NOT_STARTED The IP4 child hasn't been started + @retval EFI_ACCESS_DENIED The token or event is already queued. + @retval EFI_SUCCESS The receive request has been issued. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4Receive ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_IP4_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // First validate the parameters + // + if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + Config = &IpInstance->ConfigData; + + // + // Current Udp implementation creates an IP child for each Udp child. + // It initates a asynchronous receive immediately no matter whether + // there is no mapping or not. Disable this for now. + // +#if 0 + if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } +#endif + + // + // Check whether the toke is already on the receive queue. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip4TokenExist, Token); + + if (EFI_ERROR (Status)) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Queue the token then check whether there is pending received packet. + // + Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Ip4InstanceDeliverPacket (IpInstance); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Cancel the transmitted but not recycled packet. If a matching + token is found, it will call Ip4CancelPacket to cancel the + packet. Ip4CancelPacket will cancel all the fragments of the + packet. When all the fragments are freed, the IP4_TXTOKEN_WRAP + will be deleted from the Map, and user's event signalled. + Because Ip4CancelPacket and other functions are all called in + line, so, after Ip4CancelPacket returns, the Item has been freed. + + @param Map The IP4 child's transmit queue + @param Item The current transmitted packet to test. + @param Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next Item. + @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled. + +**/ +STATIC +EFI_STATUS +Ip4CancelTxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + IP4_TXTOKEN_WRAP *Wrap; + + Token = (EFI_IP4_COMPLETION_TOKEN *) Context; + + // + // Return EFI_SUCCESS to check the next item in the map if + // this one doesn't match. + // + if ((Token != NULL) && (Token != Item->Key)) { + return EFI_SUCCESS; + } + + Wrap = (IP4_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + // + // Don't access the Item, Wrap and Token's members after this point. + // Item and wrap has been freed. And we no longer own the Token. + // + Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + + // + // If only one item is to be cancel, return EFI_ABORTED to stop + // iterating the map any more. + // + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the receive request. This is quiet simple, because + it is only enqueued in our local receive map. + + @param Map The IP4 child's receive queue + @param Item Current receive request to cancel. + @param Context The user's token to cancel + + @retval EFI_SUCCESS Continue to check the next receive request on the + queue. + @retval EFI_ABORTED The user's token (token != NULL) has been + cancelled. + +**/ +STATIC +EFI_STATUS +Ip4CancelRxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + EFI_IP4_COMPLETION_TOKEN *This; + + Token = (EFI_IP4_COMPLETION_TOKEN *) Context; + This = Item->Key; + + if ((Token != NULL) && (Token != This)) { + return EFI_SUCCESS; + } + + NetMapRemoveItem (Map, Item, NULL); + + This->Status = EFI_ABORTED; + This->Packet.RxData = NULL; + gBS->SignalEvent (This->Event); + + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the user's receive/transmit request. + + @param IpInstance The IP4 child + @param Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled + @retval EFI_NOT_FOUND The token isn't found on either the + transmit/receive queue + @retval EFI_DEVICE_ERROR Not all token is cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip4Cancel ( + IN IP4_PROTOCOL *IpInstance, + IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // First check the transmitted packet. Ip4CancelTxTokens returns + // EFI_ABORTED to mean that the token has been cancelled when + // token != NULL. So, return EFI_SUCCESS for this condition. + // + Status = NetMapIterate (&IpInstance->TxTokens, Ip4CancelTxTokens, Token); + + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // Check the receive queue. Ip4CancelRxTokens also returns EFI_ABORT + // for Token!=NULL and it is cancelled. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip4CancelRxTokens, Token); + + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // OK, if the Token is found when Token != NULL, the NetMapIterate + // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS. + // + if (Token != NULL) { + return EFI_NOT_FOUND; + } + + // + // If Token == NULL, cancel all the tokens. return error if no + // all of them are cancelled. + // + if (!NetMapIsEmpty (&IpInstance->TxTokens) || + !NetMapIsEmpty (&IpInstance->RxTokens)) { + + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the queued receive/transmit requests. If Token is NULL, + all the queued requests will be cancelled. It just validate + the parameter then pass them to Ip4Cancel. + + @param This The IP4 child to cancel the request + @param Token The token to cancel, if NULL, cancel all. + + @retval EFI_INVALID_PARAMETER This is NULL + @retval EFI_NOT_STARTED The IP4 child hasn't been configured. + @retval EFI_NO_MAPPING The IP4 child is configured to use the default, + but the default address hasn't been acquired. + @retval EFI_SUCCESS The Token is cancelled. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4Cancel ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + Status = Ip4Cancel (IpInstance, Token); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Poll the network stack. The EFI network stack is poll based. There + is no interrupt support for the network interface card. + + @param This The IP4 child to poll through + + @retval EFI_INVALID_PARAMETER The parameter is invalid + @retval EFI_NOT_STARTED The IP4 child hasn't been configured. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiIp4Poll ( + IN EFI_IP4_PROTOCOL *This + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + if (IpInstance->State == IP4_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } + + Mnp = IpInstance->Service->Mnp; + + // + // Don't lock the Poll function to enable the deliver of + // the packet polled up. + // + return Mnp->Poll (Mnp); +} + +EFI_IP4_PROTOCOL +mEfiIp4ProtocolTemplete = { + EfiIp4GetModeData, + EfiIp4Configure, + EfiIp4Groups, + EfiIp4Routes, + EfiIp4Transmit, + EfiIp4Receive, + EfiIp4Cancel, + EfiIp4Poll +}; + + +/** + Decrease the life of the transmitted packets. If it is + decreased to zero, cancel the packet. This function is + called by Ip4packetTimerTicking which time out both the + received-but-not-delivered and transmitted-but-not-recycle + packets. + + @param Map The IP4 child's transmit map. + @param Item Current transmitted packet + @param Context Not used. + + @retval EFI_SUCCESS Always returns EFI_SUCCESS + +**/ +EFI_STATUS +Ip4SentPacketTicking ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + IP4_TXTOKEN_WRAP *Wrap; + + Wrap = (IP4_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + if ((Wrap->Life > 0) && (--Wrap->Life == 0)) { + Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + } + + return EFI_SUCCESS; +} + + +/** + The heart beat timer of IP4 service instance. It times out + all of its IP4 children's received-but-not-delivered and + transmitted-but-not-recycle packets, and provides time input + for its IGMP protocol. + + @param Event The IP4 service instance's heart beat timer. + @param Context The IP4 service instance. + + @return None + +**/ +VOID +EFIAPI +Ip4TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_SERVICE *IpSb; + + IpSb = (IP4_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE); + + Ip4PacketTimerTicking (IpSb); + Ip4IgmpTicking (IpSb); +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h new file mode 100644 index 0000000000..1bbee93237 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Impl.h @@ -0,0 +1,258 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Ip4Impl.h + +Abstract: + + Ip4 internal functions and type defintions. + + +**/ + +#ifndef __EFI_IP4_IMPL_H__ +#define __EFI_IP4_IMPL_H__ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "IP4Common.h" +#include "IP4Driver.h" +#include "IP4If.h" +#include "Ip4Icmp.h" +#include "IP4Option.h" +#include "Ip4Igmp.h" +#include "IP4Route.h" +#include "IP4Input.h" +#include "IP4Output.h" + +enum { + IP4_PROTOCOL_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', '4', 'P'), + IP4_SERVICE_SIGNATURE = EFI_SIGNATURE_32 ('I', 'P', '4', 'S'), + + // + // The state of IP4 protocol. It starts from UNCONFIGED. if it is + // successfully configured, it goes to CONFIGED. if configure NULL + // is called, it becomes UNCONFIGED again. If (partly) destoried, it + // becomes DESTORY. + // + IP4_STATE_UNCONFIGED = 0, + IP4_STATE_CONFIGED, + IP4_STATE_DESTORY, + + // + // The state of IP4 service. It starts from UNSTARTED. It transits + // to STARTED if autoconfigure is started. If default address is + // configured, it becomes CONFIGED. and if partly destoried, it goes + // to DESTORY. + // + IP4_SERVICE_UNSTARTED = 0, + IP4_SERVICE_STARTED, + IP4_SERVICE_CONFIGED, + IP4_SERVICE_DESTORY, +}; + +// +// IP4_TXTOKEN_WRAP wraps the upper layer's transmit token. +// The user's data is kept in the Packet. When fragment is +// needed, each fragment of the Packet has a reference to the +// Packet, no data is actually copied. The Packet will be +// released when all the fragments of it have been recycled by +// MNP. Upon then, the IP4_TXTOKEN_WRAP will be released, and +// user's event signalled. +// +typedef struct { + IP4_PROTOCOL *IpInstance; + EFI_IP4_COMPLETION_TOKEN *Token; + NET_BUF *Packet; + BOOLEAN Sent; + INTN Life; +} IP4_TXTOKEN_WRAP; + +// +// IP4_RXDATA_WRAP wraps the data IP4 child delivers to the +// upper layers. The received packet is kept in the Packet. +// The Packet itself may be constructured from some fragments. +// All the fragments of the Packet is organized by a +// IP4_ASSEMBLE_ENTRY structure. If the Packet is recycled by +// the upper layer, the assemble entry and its associated +// fragments will be freed at last. +// +typedef struct { + NET_LIST_ENTRY Link; + IP4_PROTOCOL *IpInstance; + NET_BUF *Packet; + EFI_IP4_RECEIVE_DATA RxData; +} IP4_RXDATA_WRAP; + +typedef struct _IP4_PROTOCOL { + UINT32 Signature; + + EFI_IP4_PROTOCOL Ip4Proto; + EFI_HANDLE Handle; + INTN State; + + IP4_SERVICE *Service; + NET_LIST_ENTRY Link; // Link to all the IP protocol from the service + + // + // User's transmit/receive tokens, and received/deliverd packets + // + NET_MAP RxTokens; + NET_MAP TxTokens; // map between (User's Token, IP4_TXTOKE_WRAP) + NET_LIST_ENTRY Received; // Received but not delivered packet + NET_LIST_ENTRY Delivered; // Delivered and to be recycled packets + EFI_LOCK RecycleLock; + + // + // Instance's address and route tables. There are two route tables. + // RouteTable is used by the IP4 driver to route packet. EfiRouteTable + // is used to communicate the current route info to the upper layer. + // + IP4_INTERFACE *Interface; + NET_LIST_ENTRY AddrLink; // Ip instances with the same IP address. + IP4_ROUTE_TABLE *RouteTable; + + EFI_IP4_ROUTE_TABLE *EfiRouteTable; + UINT32 EfiRouteCount; + + // + // IGMP data for this instance + // + IP4_ADDR *Groups; // stored in network byte order + UINT32 GroupCount; + + EFI_IP4_CONFIG_DATA ConfigData; + +} IP4_PROTOCOL; + +typedef struct _IP4_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + INTN State; + BOOLEAN InDestory; + + // + // List of all the IP instances and interfaces, and default + // interface and route table and caches. + // + UINTN NumChildren; + NET_LIST_ENTRY Children; + + NET_LIST_ENTRY Interfaces; + + IP4_INTERFACE *DefaultInterface; + IP4_ROUTE_TABLE *DefaultRouteTable; + + // + // Ip reassemble utilities, and IGMP data + // + IP4_ASSEMBLE_TABLE Assemble; + IGMP_SERVICE_DATA IgmpCtrl; + + // + // Low level protocol used by this service instance + // + EFI_HANDLE Image; + EFI_HANDLE Controller; + + EFI_HANDLE MnpChildHandle; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + EFI_EVENT Timer; + + // + // Auto configure staff + // + EFI_IP4_CONFIG_PROTOCOL *Ip4Config; + EFI_EVENT DoneEvent; + EFI_EVENT ReconfigEvent; + + // + // The string representation of the current mac address of the + // NIC this IP4_SERVICE works on. + // + CHAR16 *MacString; +} IP4_SERVICE; + +#define IP4_INSTANCE_FROM_PROTOCOL(Ip4) \ + CR ((Ip4), IP4_PROTOCOL, Ip4Proto, IP4_PROTOCOL_SIGNATURE) + +#define IP4_SERVICE_FROM_PROTOCOL(Sb) \ + CR ((Sb), IP4_SERVICE, ServiceBinding, IP4_SERVICE_SIGNATURE) + +#define IP4_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured) + +extern EFI_IP4_PROTOCOL mEfiIp4ProtocolTemplete; + +EFI_STATUS +Ip4ServiceConfigMnp ( + IN IP4_SERVICE *IpSb, + IN BOOLEAN Force + ); + +VOID +Ip4InitProtocol ( + IN IP4_SERVICE *IpSb, + IN IP4_PROTOCOL *IpInstance + ); + +EFI_STATUS +Ip4CleanProtocol ( + IN IP4_PROTOCOL *IpInstance + ); + +EFI_STATUS +Ip4Cancel ( + IN IP4_PROTOCOL *IpInstance, + IN EFI_IP4_COMPLETION_TOKEN *Token + ); + +EFI_STATUS +Ip4Groups ( + IN IP4_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress + ); + +VOID +EFIAPI +Ip4TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +EFI_STATUS +Ip4SentPacketTicking ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c new file mode 100644 index 0000000000..f5c4c9e1d4 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c @@ -0,0 +1,1235 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Ip4Input.c + +Abstract: + + IP4 input process. + + +**/ + +#include "Ip4Impl.h" + + +/** + Create a empty assemble entry for the packet identified by + (Dst, Src, Id, Protocol). The default life for the packet is + 120 seconds. + + @param Dst The destination address + @param Src The source address + @param Id The ID field in IP header + @param Protocol The protocol field in IP header + + @return NULL if failed to allocate memory for the entry, otherwise + @return the point to just created reassemble entry. + +**/ +STATIC +IP4_ASSEMBLE_ENTRY * +Ip4CreateAssembleEntry ( + IN IP4_ADDR Dst, + IN IP4_ADDR Src, + IN UINT16 Id, + IN UINT8 Protocol + ) +{ + + IP4_ASSEMBLE_ENTRY *Assemble; + + Assemble = NetAllocatePool (sizeof (IP4_ASSEMBLE_ENTRY)); + + if (Assemble == NULL) { + return NULL; + } + + NetListInit (&Assemble->Link); + NetListInit (&Assemble->Fragments); + + Assemble->Dst = Dst; + Assemble->Src = Src; + Assemble->Id = Id; + Assemble->Protocol = Protocol; + Assemble->TotalLen = 0; + Assemble->CurLen = 0; + Assemble->Head = NULL; + Assemble->Info = NULL; + Assemble->Life = IP4_FRAGMENT_LIFE; + + return Assemble; +} + + +/** + Release all the fragments of a packet, then free the assemble entry + + @param Assemble The assemble entry to free + + @return None + +**/ +STATIC +VOID +Ip4FreeAssembleEntry ( + IN IP4_ASSEMBLE_ENTRY *Assemble + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + NET_BUF *Fragment; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) { + Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + NetListRemoveEntry (Entry); + NetbufFree (Fragment); + } + + NetFreePool (Assemble); +} + + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP4 service instance. + + @param Table The assemble table to initialize. + + @return NONE + +**/ +VOID +Ip4InitAssembleTable ( + IN IP4_ASSEMBLE_TABLE *Table + ) +{ + UINT32 Index; + + for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { + NetListInit (&Table->Bucket[Index]); + } +} + + +/** + Clean up the assemble table: remove all the fragments + and assemble entries. + + @param Table The assemble table to clean up + + @return None + +**/ +VOID +Ip4CleanAssembleTable ( + IN IP4_ASSEMBLE_TABLE *Table + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ASSEMBLE_ENTRY *Assemble; + UINT32 Index; + + for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link); + + NetListRemoveEntry (Entry); + Ip4FreeAssembleEntry (Assemble); + } + } +} + + +/** + Trim the packet to fit in [Start, End), and update the per + packet information. + + @param Packet Packet to trim + @param Start The sequence of the first byte to fit in + @param End One beyond the sequence of last byte to fit in. + + @return None + +**/ +STATIC +VOID +Ip4TrimPacket ( + IN NET_BUF *Packet, + IN INTN Start, + IN INTN End + ) +{ + IP4_CLIP_INFO *Info; + INTN Len; + + Info = IP4_GET_CLIP_INFO (Packet); + + ASSERT (Info->Start + Info->Length == Info->End); + ASSERT ((Info->Start < End) && (Start < Info->End)); + + if (Info->Start < Start) { + Len = Start - Info->Start; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD); + Info->Start = Start; + Info->Length -= Len; + } + + if (End < Info->End) { + Len = End - Info->End; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL); + Info->End = End; + Info->Length -= Len; + } +} + + +/** + Release all the fragments of the packet. This is the callback for + the assembled packet's OnFree. It will free the assemble entry, + which in turn will free all the fragments of the packet. + + @param Arg The assemble entry to free + + @return None + +**/ +STATIC +VOID +Ip4OnFreeFragments ( + IN VOID *Arg + ) +{ + Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *) Arg); +} + + +/** + Reassemble the IP fragments. If all the fragments of the packet + have been received, it will wrap the packet in a net buffer then + return it to caller. If the packet can't be assembled, NULL is + return. + + @param Table The assemble table used. + @param Packet The fragment to assemble + + @return NULL if the packet can't be reassemble. The point to just assembled + @return packet if all the fragments of the packet have arrived. + +**/ +STATIC +NET_BUF * +Ip4Reassemble ( + IN IP4_ASSEMBLE_TABLE *Table, + IN NET_BUF *Packet + ) +{ + IP4_HEAD *IpHead; + IP4_CLIP_INFO *This; + IP4_CLIP_INFO *Node; + IP4_ASSEMBLE_ENTRY *Assemble; + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Prev; + NET_LIST_ENTRY *Cur; + NET_BUF *Fragment; + NET_BUF *NewPacket; + INTN Index; + + IpHead = Packet->Ip; + This = IP4_GET_CLIP_INFO (Packet); + + ASSERT (IpHead != NULL); + + // + // First: find the related assemble entry + // + Assemble = NULL; + Index = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol); + + NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link); + + if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) && + (Assemble->Id == IpHead->Id) && (Assemble->Protocol == IpHead->Protocol)) { + break; + } + } + + // + // Create a new assemble entry if no assemble entry is related to this packet + // + if (Cur == &Table->Bucket[Index]) { + Assemble = Ip4CreateAssembleEntry ( + IpHead->Dst, + IpHead->Src, + IpHead->Id, + IpHead->Protocol + ); + + if (Assemble == NULL) { + goto DROP; + } + + NetListInsertHead (&Table->Bucket[Index], &Assemble->Link); + } + + // + // Find the point to insert the packet: before the first + // fragment with THIS.Start < CUR.Start. the previous one + // has PREV.Start <= THIS.Start < CUR.Start. + // + Head = &Assemble->Fragments; + + NET_LIST_FOR_EACH (Cur, Head) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (This->Start < IP4_GET_CLIP_INFO (Fragment)->Start) { + break; + } + } + + // + // Check whether the current fragment overlaps with the previous one. + // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to + // check whether THIS.Start < PREV.End for overlap. If two fragments + // overlaps, trim the overlapped part off THIS fragment. + // + if ((Prev = Cur->ForwardLink) != Head) { + Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + Node = IP4_GET_CLIP_INFO (Fragment); + + if (This->Start < Node->End) { + if (This->End <= Node->End) { + NetbufFree (Packet); + return NULL; + } + + Ip4TrimPacket (Packet, Node->End, This->End); + } + } + + // + // Insert the fragment into the packet. The fragment may be removed + // from the list by the following checks. + // + NetListInsertBefore (Cur, &Packet->List); + + // + // Check the packets after the insert point. It holds that: + // THIS.Start <= NODE.Start < NODE.End. The equality holds + // if PREV and NEXT are continuous. THIS fragment may fill + // several holes. Remove the completely overlapped fragments + // + while (Cur != Head) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Node = IP4_GET_CLIP_INFO (Fragment); + + // + // Remove fragments completely overlapped by this fragment + // + if (Node->End <= This->End) { + Cur = Cur->ForwardLink; + + NetListRemoveEntry (&Fragment->List); + Assemble->CurLen -= Node->Length; + + NetbufFree (Fragment); + continue; + } + + // + // The conditions are: THIS.Start <= NODE.Start, and THIS.End < + // NODE.End. Two fragments overlaps if NODE.Start < THIS.End. + // If two fragments start at the same offset, remove THIS fragment + // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)). + // + if (Node->Start < This->End) { + if (This->Start == Node->Start) { + NetListRemoveEntry (&Packet->List); + goto DROP; + } + + Ip4TrimPacket (Packet, This->Start, Node->Start); + } + + break; + } + + // + // Update the assemble info: increase the current length. If it is + // the frist fragment, update the packet's IP head and per packet + // info. If it is the last fragment, update the total length. + // + Assemble->CurLen += This->Length; + + if (This->Start == 0) { + // + // Once the first fragment is enqueued, it can't be removed + // from the fragment list. So, Assemble->Head always point + // to valid memory area. + // + ASSERT (Assemble->Head == NULL); + + Assemble->Head = IpHead; + Assemble->Info = IP4_GET_CLIP_INFO (Packet); + } + + // + // Don't update the length more than once. + // + if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (Assemble->TotalLen == 0)) { + Assemble->TotalLen = This->End; + } + + // + // Deliver the whole packet if all the fragments received. + // All fragments received if: + // 1. received the last one, so, the totoal length is know + // 2. received all the data. If the last fragment on the + // queue ends at the total length, all data is received. + // + if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) { + + NetListRemoveEntry (&Assemble->Link); + + // + // If the packet is properly formated, the last fragment's End + // equals to the packet's total length. Otherwise, the packet + // is a fake, drop it now. + // + Fragment = NET_LIST_USER_STRUCT (Head->BackLink, NET_BUF, List); + + if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) { + Ip4FreeAssembleEntry (Assemble); + return NULL; + } + + // + // Wrap the packet in a net buffer then deliver it up + // + NewPacket = NetbufFromBufList ( + &Assemble->Fragments, + 0, + 0, + Ip4OnFreeFragments, + Assemble + ); + + if (NewPacket == NULL) { + Ip4FreeAssembleEntry (Assemble); + return NULL; + } + + NewPacket->Ip = Assemble->Head; + CopyMem (IP4_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP4_CLIP_INFO)); + return NewPacket; + } + + return NULL; + +DROP: + NetbufFree (Packet); + return NULL; +} + + +/** + The IP4 input routine. It is called by the IP4_INTERFACE when a + IP4 fragment is received from MNP. + + @param Ip4Instance The IP4 child that request the receive, most like + it is NULL. + @param Packet The IP4 packet received. + @param IoStatus The return status of receive request. + @param Flag The link layer flag for the packet received, such + as multicast. + @param Context The IP4 service instance that own the MNP. + + @return None + +**/ +VOID +Ip4AccpetFrame ( + IN IP4_PROTOCOL *Ip4Instance, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP4_SERVICE *IpSb; + IP4_CLIP_INFO *Info; + IP4_HEAD *Head; + UINT32 HeadLen; + UINT32 OptionLen; + UINT32 TotalLen; + UINT16 Checksum; + + IpSb = (IP4_SERVICE *) Context; + + if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTORY)) { + goto DROP; + } + + // + // Check that the IP4 header is correctly formated + // + if (Packet->TotalSize < IP4_MIN_HEADLEN) { + goto RESTART; + } + + Head = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL); + HeadLen = (Head->HeadLen << 2); + TotalLen = NTOHS (Head->TotalLen); + + // + // Mnp may deliver frame trailer sequence up, trim it off. + // + if (TotalLen < Packet->TotalSize) { + NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE); + } + + if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) || + (TotalLen < HeadLen) || (TotalLen != Packet->TotalSize)) { + goto RESTART; + } + + // + // Some OS may send IP packets without checksum. + // + Checksum = ~NetblockChecksum ((UINT8 *) Head, HeadLen); + + if ((Head->Checksum != 0) && (Checksum != 0)) { + goto RESTART; + } + + // + // Convert the IP header to host byte order, then get the per packet info. + // + Packet->Ip = Ip4NtohHead (Head); + + Info = IP4_GET_CLIP_INFO (Packet); + Info->LinkFlag = Flag; + Info->CastType = Ip4GetHostCast (IpSb, Head->Dst, Head->Src); + Info->Start = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3; + Info->Length = Head->TotalLen - HeadLen; + Info->End = Info->Start + Info->Length; + Info->Status = EFI_SUCCESS; + + // + // The packet is destinated to us if the CastType is non-zero. + // + if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) { + goto RESTART; + } + + // + // Validate the options. Don't call the Ip4OptionIsValid if + // there is no option to save some CPU process. + // + OptionLen = HeadLen - IP4_MIN_HEADLEN; + + if ((OptionLen > 0) && !Ip4OptionIsValid ((UINT8 *) (Head + 1), OptionLen, TRUE)) { + goto RESTART; + } + + // + // Trim the head off, after this point, the packet is headless. + // and Packet->TotalLen == Info->Length. + // + NetbufTrim (Packet, HeadLen, TRUE); + + // + // Reassemble the packet if this is a fragment. The packet is a + // fragment if its head has MF (more fragment) set, or it starts + // at non-zero byte. + // + if ((Head->Fragment & IP4_HEAD_MF_MASK) || (Info->Start != 0)) { + // + // Drop the fragment if DF is set but it is fragmented. Gateway + // need to send a type 4 destination unreache ICMP message here. + // + if (Head->Fragment & IP4_HEAD_DF_MASK) { + goto RESTART; + } + + // + // The length of all but the last fragments is in the unit of 8 bytes. + // + if ((Head->Fragment & IP4_HEAD_MF_MASK) && (Info->Length % 8 != 0)) { + goto RESTART; + } + + Packet = Ip4Reassemble (&IpSb->Assemble, Packet); + + // + // Packet assembly isn't complete, start receive more packet. + // + if (Packet == NULL) { + goto RESTART; + } + } + + // + // Packet may have been changed. Head, HeadLen, TotalLen, and + // info must be reloaded bofore use. The ownership of the packet + // is transfered to the packet process logic. + // + Head = Packet->Ip; + IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS; + + switch (Head->Protocol) { + case IP4_PROTO_ICMP: + Ip4IcmpHandle (IpSb, Head, Packet); + break; + + case IP4_PROTO_IGMP: + Ip4IgmpHandle (IpSb, Head, Packet); + break; + + default: + Ip4Demultiplex (IpSb, Head, Packet); + } + + Packet = NULL; + +RESTART: + Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb); + +DROP: + if (Packet != NULL) { + NetbufFree (Packet); + } + + return ; +} + + +/** + Check whether this IP child accepts the packet. + + @param IpInstance The IP child to check + @param Head The IP header of the packet + @param Packet The data of the packet + + @return TRUE if the child wants to receive the packet, otherwise return FALSE. + +**/ +BOOLEAN +Ip4InstanceFrameAcceptable ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_ICMP_ERROR_HEAD Icmp; + EFI_IP4_CONFIG_DATA *Config; + IP4_CLIP_INFO *Info; + UINT16 Proto; + UINT32 Index; + + Config = &IpInstance->ConfigData; + + // + // Dirty trick for the Tiano UEFI network stack implmentation. If + // ReceiveTimeout == -1, the receive of the packet for this instance + // is disabled. The UEFI spec don't have such captibility. We add + // this to improve the performance because IP will make a copy of + // the received packet for each accepting instance. Some IP instances + // used by UDP/TCP only send packets, they don't wants to receive. + // + if (Config->ReceiveTimeout == (UINT32)(-1)) { + return FALSE; + } + + if (Config->AcceptPromiscuous) { + return TRUE; + } + + // + // Use protocol from the IP header embedded in the ICMP error + // message to filter, instead of ICMP itself. ICMP handle will + // can Ip4Demultiplex to deliver ICMP errors. + // + Proto = Head->Protocol; + + if (Proto == IP4_PROTO_ICMP) { + NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *) &Icmp.Head); + + if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) { + if (!Config->AcceptIcmpErrors) { + return FALSE; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Proto = Icmp.IpHead.Protocol; + } + } + + // + // Match the protocol + // + if (!Config->AcceptAnyProtocol && (Proto != Config->DefaultProtocol)) { + return FALSE; + } + + // + // Check for broadcast, the caller has computed the packet's + // cast type for this child's interface. + // + Info = IP4_GET_CLIP_INFO (Packet); + + if (IP4_IS_BROADCAST (Info->CastType)) { + return Config->AcceptBroadcast; + } + + // + // If it is a multicast packet, check whether we are in the group. + // + if (Info->CastType == IP4_MULTICAST) { + // + // Receive the multicast if the instance wants to receive all packets. + // + if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) { + return TRUE; + } + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (IpInstance->Groups[Index] == HTONL (Head->Dst)) { + break; + } + } + + return (BOOLEAN)(Index < IpInstance->GroupCount); + } + + return TRUE; +} + + +/** + Enqueue a shared copy of the packet to the IP4 child if the + packet is acceptable to it. Here the data of the packet is + shared, but the net buffer isn't. + + @param IpInstance The IP4 child to enqueue the packet to + @param Head The IP header of the received packet + @param Packet The data of the received packet + + @retval EFI_NOT_STARTED The IP child hasn't been configured. + @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource + @retval EFI_SUCCESS A shared copy the packet is enqueued to the child. + +**/ +EFI_STATUS +Ip4InstanceEnquePacket ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_CLIP_INFO *Info; + NET_BUF *Clone; + + // + // Check whether the packet is acceptable to this instance. + // + if (IpInstance->State != IP4_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (!Ip4InstanceFrameAcceptable (IpInstance, Head, Packet)) { + return EFI_INVALID_PARAMETER; + } + + // + // Enque a shared copy of the packet. + // + Clone = NetbufClone (Packet); + + if (Clone == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the receive time out for the assembled packet. If it expires, + // packet will be removed from the queue. + // + Info = IP4_GET_CLIP_INFO (Clone); + Info->Life = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout); + + NetListInsertTail (&IpInstance->Received, &Clone->List); + return EFI_SUCCESS; +} + + +/** + The signal handle of IP4's recycle event. It is called back + when the upper layer release the packet. + + @param Event The IP4's recycle event. + @param Context The context of the handle, which is a + IP4_RXDATA_WRAP + + @return None + +**/ +STATIC +VOID +EFIAPI +Ip4OnRecyclePacket ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_RXDATA_WRAP *Wrap; + + Wrap = (IP4_RXDATA_WRAP *) Context; + + NET_TRYLOCK (&Wrap->IpInstance->RecycleLock); + NetListRemoveEntry (&Wrap->Link); + NET_UNLOCK (&Wrap->IpInstance->RecycleLock); + + ASSERT (!NET_BUF_SHARED (Wrap->Packet)); + NetbufFree (Wrap->Packet); + + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + NetFreePool (Wrap); +} + + +/** + Wrap the received packet to a IP4_RXDATA_WRAP, which will be + delivered to the upper layer. Each IP4 child that accepts the + packet will get a not-shared copy of the packet which is wrapped + in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed + to the upper layer. Upper layer will signal the recycle event in + it when it is done with the packet. + + @param IpInstance The IP4 child to receive the packet + @param Packet The packet to deliver up. + + @return NULL if failed to wrap the packet, otherwise the wrapper. + +**/ +IP4_RXDATA_WRAP * +Ip4WrapRxData ( + IN IP4_PROTOCOL *IpInstance, + IN NET_BUF *Packet + ) +{ + IP4_RXDATA_WRAP *Wrap; + EFI_IP4_RECEIVE_DATA *RxData; + EFI_STATUS Status; + + Wrap = NetAllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum)); + + if (Wrap == NULL) { + return NULL; + } + + NetListInit (&Wrap->Link); + + Wrap->IpInstance = IpInstance; + Wrap->Packet = Packet; + RxData = &Wrap->RxData; + + NetZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NET_TPL_RECYCLE, + Ip4OnRecyclePacket, + Wrap, + &RxData->RecycleSignal + ); + + if (EFI_ERROR (Status)) { + NetFreePool (Wrap); + return NULL; + } + + ASSERT (Packet->Ip != NULL); + + // + // The application expects a network byte order header. + // + RxData->HeaderLength = (Packet->Ip->HeadLen << 2); + RxData->Header = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip); + + RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN; + RxData->Options = NULL; + + if (RxData->OptionsLength != 0) { + RxData->Options = (VOID *) (RxData->Header + 1); + } + + RxData->DataLength = Packet->TotalSize; + + // + // Build the fragment table to be delivered up. + // + RxData->FragmentCount = Packet->BlockOpNum; + NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount); + + return Wrap; +} + + +/** + Deliver the received packets to upper layer if there are both received + requests and enqueued packets. If the enqueued packet is shared, it will + duplicate it to a non-shared packet, release the shared packet, then + deliver the non-shared packet up. + + @param IpInstance The IP child to deliver the packet up. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the + packets. + @retval EFI_SUCCESS All the enqueued packets that can be delivered + are delivered up. + +**/ +EFI_STATUS +Ip4InstanceDeliverPacket ( + IN IP4_PROTOCOL *IpInstance + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + IP4_RXDATA_WRAP *Wrap; + NET_BUF *Packet; + NET_BUF *Dup; + UINT8 *Head; + + // + // Deliver a packet if there are both a packet and a receive token. + // + while (!NetListIsEmpty (&IpInstance->Received) && + !NetMapIsEmpty (&IpInstance->RxTokens)) { + + Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List); + + if (!NET_BUF_SHARED (Packet)) { + // + // If this is the only instance that wants the packet, wrap it up. + // + Wrap = Ip4WrapRxData (IpInstance, Packet); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NetListRemoveEntry (&Packet->List); + + } else { + // + // Create a duplicated packet if this packet is shared + // + Dup = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN); + + if (Dup == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the IP head over. The packet to deliver up is + // headless. Trim the head off after copy. The IP head + // may be not continuous before the data. + // + Head = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD); + Dup->Ip = (IP4_HEAD *) Head; + + NetCopyMem (Head, Packet->Ip, Packet->Ip->HeadLen << 2); + NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE); + + Wrap = Ip4WrapRxData (IpInstance, Dup); + + if (Wrap == NULL) { + NetbufFree (Dup); + return EFI_OUT_OF_RESOURCES; + } + + NetListRemoveEntry (&Packet->List); + NetbufFree (Packet); + + Packet = Dup; + } + + // + // Insert it into the delivered packet, then get a user's + // receive token, pass the wrapped packet up. + // + NET_TRYLOCK (&IpInstance->RecycleLock); + NetListInsertHead (&IpInstance->Delivered, &Wrap->Link); + NET_UNLOCK (&IpInstance->RecycleLock); + + Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL); + Token->Status = IP4_GET_CLIP_INFO (Packet)->Status; + Token->Packet.RxData = &Wrap->RxData; + + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + + +/** + Enqueue a received packet to all the IP children that share + the same interface. + + @param IpSb The IP4 service instance that receive the packet + @param Head The header of the received packet + @param Packet The data of the received packet + @param IpIf The interface to enqueue the packet to + + @return The number of the IP4 children that accepts the packet + +**/ +INTN +Ip4InterfaceEnquePacket ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN IP4_INTERFACE *IpIf + ) +{ + IP4_PROTOCOL *IpInstance; + IP4_CLIP_INFO *Info; + NET_LIST_ENTRY *Entry; + INTN Enqueued; + INTN LocalType; + INTN SavedType; + + // + // First, check that the packet is acceptable to this interface + // and find the local cast type for the interface. A packet sent + // to say 192.168.1.1 should NOT be delliever to 10.0.0.1 unless + // promiscuous receiving. + // + LocalType = 0; + Info = IP4_GET_CLIP_INFO (Packet); + + if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) { + // + // If the CastType is multicast, don't need to filter against + // the group address here, Ip4InstanceFrameAcceptable will do + // that later. + // + LocalType = Info->CastType; + + } else { + // + // Check the destination againist local IP. If the station + // address is 0.0.0.0, it means receiving all the IP destined + // to local non-zero IP. Otherwise, it is necessary to compare + // the destination to the interface's IP address. + // + if (IpIf->Ip == IP4_ALLZERO_ADDRESS) { + LocalType = IP4_LOCAL_HOST; + + } else { + LocalType = Ip4GetNetCast (Head->Dst, IpIf); + + if ((LocalType == 0) && IpIf->PromiscRecv) { + LocalType = IP4_PROMISCUOUS; + } + } + } + + if (LocalType == 0) { + return 0; + } + + // + // Iterate through the ip instances on the interface, enqueue + // the packet if filter passed. Save the original cast type, + // and pass the local cast type to the IP children on the + // interface. The global cast type will be restored later. + // + SavedType = Info->CastType; + Info->CastType = LocalType; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink); + NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE); + + if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) { + Enqueued++; + } + } + + Info->CastType = SavedType; + return Enqueued; +} + + +/** + Deliver the packet for each IP4 child on the interface. + + @param IpSb The IP4 service instance that received the packet + @param IpIf The IP4 interface to deliver the packet. + + @retval EFI_SUCCESS It always returns EFI_SUCCESS now + +**/ +EFI_STATUS +Ip4InterfaceDeliverPacket ( + IN IP4_SERVICE *IpSb, + IN IP4_INTERFACE *IpIf + ) +{ + IP4_PROTOCOL *Ip4Instance; + NET_LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink); + Ip4InstanceDeliverPacket (Ip4Instance); + } + + return EFI_SUCCESS; +} + + +/** + Demultiple the packet. the packet delivery is processed in two + passes. The first pass will enque a shared copy of the packet + to each IP4 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP4 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet bacause each IP child need + its own copy of the packet to make changes. + + @param IpSb The IP4 service instance that received the packet + @param Head The header of the received packet + @param Packet The data of the received packet + + @retval EFI_NOT_FOUND No IP child accepts the packet + @retval EFI_SUCCESS The packet is enqueued or delivered to some IP + children. + +**/ +EFI_STATUS +Ip4Demultiplex ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + NET_LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + INTN Enqueued; + + // + // Two pass delivery: first, enque a shared copy of the packet + // to each instance that accept the packet. + // + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured) { + Enqueued += Ip4InterfaceEnquePacket (IpSb, Head, Packet, IpIf); + } + } + + // + // Second: deliver a duplicate of the packet to each instance. + // Release the local reference first, so that the last instance + // getting the packet will not copy the data. + // + NetbufFree (Packet); + + if (Enqueued == 0) { + return EFI_NOT_FOUND; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured) { + Ip4InterfaceDeliverPacket (IpSb, IpIf); + } + } + + return EFI_SUCCESS; +} + + +/** + Timeout the fragment and enqueued packets. + + @param IpSb The IP4 service instance to timeout + + @return None + +**/ +VOID +Ip4PacketTimerTicking ( + IN IP4_SERVICE *IpSb + ) +{ + NET_LIST_ENTRY *InstanceEntry; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_PROTOCOL *IpInstance; + IP4_ASSEMBLE_ENTRY *Assemble; + NET_BUF *Packet; + IP4_CLIP_INFO *Info; + UINT32 Index; + + // + // First, time out the fragments. The packet's life is counting down + // once the first-arrived fragment was received. + // + for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link); + + if ((Assemble->Life > 0) && (--Assemble->Life == 0)) { + NetListRemoveEntry (Entry); + Ip4FreeAssembleEntry (Assemble); + } + } + } + + NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_PROTOCOL, Link); + + // + // Second, time out the assembled packets enqueued on each IP child. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) { + Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Info = IP4_GET_CLIP_INFO (Packet); + + if ((Info->Life > 0) && (--Info->Life == 0)) { + NetListRemoveEntry (Entry); + NetbufFree (Packet); + } + } + + // + // Third: time out the transmitted packets. + // + NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL); + } +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h new file mode 100644 index 0000000000..9a3af8fba7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.h @@ -0,0 +1,138 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Ip4Input.h + +Abstract: + + +**/ + +#ifndef __EFI_IP4_INPUT_H__ +#define __EFI_IP4_INPUT_H__ + +enum { + IP4_MIN_HEADLEN = 20, + IP4_MAX_HEADLEN = 60, + + IP4_ASSEMLE_HASH_SIZE = 31, + IP4_FRAGMENT_LIFE = 120, + IP4_MAX_PACKET_SIZE = 65535, +}; + +// +// Per packet information for input process. LinkFlag specifies whether +// the packet is received as Link layer unicast, multicast or broadcast. +// The CastType is the IP layer cast type, such as IP multicast or unicast. +// Start, End and Length are staffs used to assemble the packets. Start +// is the sequence number of the first byte of data in the packet. Length +// is the number of bytes of data. End = Start + Length, that is, the +// sequence number of last byte + 1. Each assembled packet has a count down +// life. If it isn't consumed before Life reaches zero, the packet is released. +// +typedef struct { + UINTN LinkFlag; + INTN CastType; + INTN Start; + INTN End; + INTN Length; + UINT32 Life; + EFI_STATUS Status; +} IP4_CLIP_INFO; + +// +// Structure used to assemble IP packets. +// +typedef struct { + NET_LIST_ENTRY Link; + + // + // Identity of one IP4 packet. Each fragment of a packet has + // the same (Dst, Src, Id, Protocol). + // + IP4_ADDR Dst; + IP4_ADDR Src; + UINT16 Id; + UINT8 Protocol; + + INTN TotalLen; + INTN CurLen; + NET_LIST_ENTRY Fragments; // List of all the fragments of this packet + + IP4_HEAD *Head; // IP head of the first fragment + IP4_CLIP_INFO *Info; // Per packet info of the first fragment + INTN Life; // Count down life for the packet. +} IP4_ASSEMBLE_ENTRY; + +// +// Each Ip service instance has an assemble table to reassemble +// the packets before delivery to its children. It is organized +// as hash table. +// +typedef struct { + NET_LIST_ENTRY Bucket[IP4_ASSEMLE_HASH_SIZE]; +} IP4_ASSEMBLE_TABLE; + +#define IP4_GET_CLIP_INFO(Packet) ((IP4_CLIP_INFO *) ((Packet)->ProtoData)) + +#define IP4_ASSEMBLE_HASH(Dst, Src, Id, Proto) \ + (((Dst) + (Src) + ((Id) << 16) + (Proto)) % IP4_ASSEMLE_HASH_SIZE) + +#define IP4_RXDATA_WRAP_SIZE(NumFrag) \ + (sizeof (IP4_RXDATA_WRAP) + sizeof (EFI_IP4_FRAGMENT_DATA) * ((NumFrag) - 1)) + +VOID +Ip4InitAssembleTable ( + IN IP4_ASSEMBLE_TABLE *Table + ); + +VOID +Ip4CleanAssembleTable ( + IN IP4_ASSEMBLE_TABLE *Table + ); + +VOID +Ip4AccpetFrame ( + IN IP4_PROTOCOL *Ip4Instance, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ); + +EFI_STATUS +Ip4Demultiplex ( + IN IP4_SERVICE *SbInstance, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ); + +INTN +Ip4InterfaceEnquePacket ( + IN IP4_SERVICE *SbInstance, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN IP4_INTERFACE *Interface + ); + +EFI_STATUS +Ip4InstanceDeliverPacket ( + IN IP4_PROTOCOL *Ip4Instance + ); + +VOID +Ip4PacketTimerTicking ( + IN IP4_SERVICE *IpSb + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c new file mode 100644 index 0000000000..e1f059e89f --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.c @@ -0,0 +1,231 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Ip4Option.c + +Abstract: + + IP4 option support functions + + +**/ + +#include "Ip4Impl.h" + + +/** + Validate the IP4 option format for both the packets we received + and will transmit. It will compute the ICMP error message fields + if the option is mal-formated. But this information isn't used. + + @param Option The first byte of the option + @param OptionLen The length of the whole option + @param Rcvd The option is from the packet we received if TRUE, + otherwise the option we wants to transmit. + + @return TRUE: The option is properly formated + @return FALSE: The option is mal-formated + +**/ +BOOLEAN +Ip4OptionIsValid ( + IN UINT8 *Option, + IN UINT32 OptionLen, + IN BOOLEAN Rcvd + ) +{ + UINT32 Cur; + UINT32 Len; + UINT32 Point; + UINT8 IcmpType; + UINT8 IcmpCode; + UINT32 IcmpPoint; + + IcmpType = ICMP_PARAMETER_PROBLEM; + IcmpCode = 0; + IcmpPoint = 0; + + Cur = 0; + + while (Cur < OptionLen) { + switch (Option[Cur]) { + case IP4_OPTION_NOP: + Cur++; + break; + + case IP4_OPTION_EOP: + Cur = OptionLen; + break; + + case IP4_OPTION_LSRR: + case IP4_OPTION_SSRR: + case IP4_OPTION_RR: + Len = Option[Cur + 1]; + Point = Option[Cur + 2]; + + // + // SRR/RR options are formated as |Type|Len|Point|Ip1|Ip2|... + // + if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) { + IcmpPoint = Cur + 1; + return FALSE; + } + + if ((Point > Len + 1) || (Point % 4 != 0)) { + IcmpPoint = Cur + 2; + return FALSE; + } + + // + // The Point must point pass the last entry if the packet is received + // by us. It must point to 4 if the packet is to be sent by us for + // source route option. + // + if ((Option[Cur] != IP4_OPTION_RR) && + ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) { + + IcmpType = ICMP_DEST_UNREACHABLE; + IcmpCode = ICMP_SOURCEROUTE_FAILED; + return FALSE; + } + + Cur += Len; + break; + + default: + Len = Option[Cur + 1]; + + if ((OptionLen - Cur < Len) || (Len < 2)) { + IcmpPoint = Cur + 1; + return FALSE; + } + + Cur = Cur + Len; + break; + } + + } + + return TRUE; +} + + +/** + Copy the option from the original option to buffer. It + handles the details such as: + 1. whether copy the single IP4 option to the first/non-first + fragments. + 2. Pad the options copied over to aligened to 4 bytes. + + @param Option The original option to copy from + @param OptionLen The length of the original option + @param FirstFragment Whether it is the first fragment + @param Buf The buffer to copy options to + @param BufLen The length of the buffer + + @retval EFI_SUCCESS The options are copied over + @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small. + +**/ +EFI_STATUS +Ip4CopyOption ( + IN UINT8 *Option, + IN UINT32 OptionLen, + IN BOOLEAN FirstFragment, + IN UINT8 *Buf, OPTIONAL + IN OUT UINT32 *BufLen + ) +{ + UINT8 OptBuf[40]; + UINT32 Cur; + UINT32 Next; + UINT8 Type; + UINT32 Len; + + ASSERT ((BufLen != NULL) && (OptionLen <= 40)); + + Cur = 0; + Next = 0; + + while (Cur < OptionLen) { + Type = Option[Cur]; + Len = Option[Cur + 1]; + + if (Type == IP4_OPTION_NOP) { + // + // Keep the padding, in case that the sender wants to align + // the option, say, to 4 bytes + // + OptBuf[Next] = IP4_OPTION_NOP; + Next++; + Cur++; + + } else if (Type == IP4_OPTION_EOP) { + // + // Don't append the EOP to avoid including only a EOP option + // + break; + + } else { + // + // don't copy options that is only valid for the first fragment + // + if (FirstFragment || (Type & IP4_OPTION_COPY_MASK)) { + NetCopyMem (OptBuf + Next, Option + Cur, Len); + Next += Len; + } + + Cur += Len; + } + } + + // + // Don't append an EOP only option. + // + if (Next == 0) { + *BufLen = 0; + return EFI_SUCCESS; + } + + // + // Append an EOP if the end of option doesn't coincide with the + // end of the IP header, that is, isn't aligned to 4 bytes.. + // + if ((Next % 4) != 0) { + OptBuf[Next] = IP4_OPTION_EOP; + Next++; + } + + // + // Head length is in the unit of 4 bytes. Now, Len is the + // acutal option length to appear in the IP header. + // + Len = ((Next + 3) &~0x03); + + // + // If the buffer is too small, set the BufLen then return + // + if ((Buf == NULL) || (*BufLen < Len)) { + *BufLen = Len; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy the option to the Buf, zero the buffer first to pad + // the options with NOP to align to 4 bytes. + // + NetZeroMem (Buf, Len); + NetCopyMem (Buf, OptBuf, Next); + *BufLen = Len; + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h new file mode 100644 index 0000000000..6af885f25b --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Option.h @@ -0,0 +1,52 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Option.h + +Abstract: + + IP4 option support routines. + + +**/ + +#ifndef __EFI_IP4_OPTION_H__ +#define __EFI_IP4_OPTION_H__ + +enum { + IP4_OPTION_EOP = 0, + IP4_OPTION_NOP = 1, + IP4_OPTION_LSRR = 131, // Loss source and record routing, 10000011 + IP4_OPTION_SSRR = 137, // Strict source and record routing, 10001001 + IP4_OPTION_RR = 7, // Record routing, 00000111 + + IP4_OPTION_COPY_MASK = 0x80, +}; + +BOOLEAN +Ip4OptionIsValid ( + IN UINT8 *Option, + IN UINT32 OptLen, + IN BOOLEAN Rcvd + ); + +EFI_STATUS +Ip4CopyOption ( + IN UINT8 *Option, + IN UINT32 OptLen, + IN BOOLEAN Fragment, + IN UINT8 *Buf, OPTIONAL + IN OUT UINT32 *BufLen + ); +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c new file mode 100644 index 0000000000..d595ab2999 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c @@ -0,0 +1,457 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Ip4Output.c + +Abstract: + + Transmit the IP4 packet + + +**/ + +#include "Ip4Impl.h" + +UINT16 mIp4Id; + + +/** + Prepend an IP4 head to the Packet. It will copy the options and + build the IP4 header fields. Used for IP4 fragmentation. + + @param Packet The packet to prepend IP4 header to + @param Head The caller supplied header. The caller should set + the following header fields: Tos, TotalLen, Id, + Fragment, Ttl, Protocol, Src and Dst. All the fields + are in host byte order. This function will fill in + the Ver, HeadLen, and checksum. + @param Option The orginal IP4 option to copy from + @param OptLen The length of the IP4 option + + @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of + Packet. + @retval EFI_SUCCESS The IP4 header is successfully added to the packet. + +**/ +EFI_STATUS +Ip4PrependHead ( + IN NET_BUF *Packet, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptLen + ) +{ + UINT32 HeadLen; + UINT32 Len; + IP4_HEAD *PacketHead; + BOOLEAN FirstFragment; + + // + // Prepend the options: first get the option length, then copy it over. + // + HeadLen = 0; + FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment); + + Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len); + + HeadLen = IP4_MIN_HEADLEN + Len; + ASSERT (((Len %4) == 0) && (HeadLen <= IP4_MAX_HEADLEN)); + + PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + + if (PacketHead == NULL) { + return EFI_BAD_BUFFER_SIZE; + } + + Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len); + + // + // Set the head up, convert the host byte order to network byte order + // + PacketHead->Ver = 4; + PacketHead->HeadLen = (UINT8) (HeadLen >> 2); + PacketHead->Tos = Head->Tos; + PacketHead->TotalLen = HTONS (Packet->TotalSize); + PacketHead->Id = HTONS (Head->Id); + PacketHead->Fragment = HTONS (Head->Fragment); + PacketHead->Checksum = 0; + PacketHead->Ttl = Head->Ttl; + PacketHead->Protocol = Head->Protocol; + PacketHead->Src = HTONL (Head->Src); + PacketHead->Dst = HTONL (Head->Dst); + PacketHead->Checksum = ~NetblockChecksum ((UINT8 *) PacketHead, HeadLen); + + Packet->Ip = PacketHead; + return EFI_SUCCESS; +} + + +/** + Select an interface to send the packet generated in the IP4 driver + itself, that is, not by the requests of IP4 child's consumer. Such + packets include the ICMP echo replies, and other ICMP error packets. + + @param IpSb The IP4 service that wants to send the packets. + @param Dst The destination of the packet + @param Src The source of the packet + + @return NULL if no proper interface is found, otherwise the interface that + @return can be used to send the system packet from. + +**/ +IP4_INTERFACE * +Ip4SelectInterface ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN IP4_ADDR Src + ) +{ + IP4_INTERFACE *IpIf; + IP4_INTERFACE *Selected; + NET_LIST_ENTRY *Entry; + + // + // Select the interface the Dst is on if one of the connected + // network. Some IP instance may be configured with 0.0.0.0/0, + // don't select that interface now. + // + IpIf = Ip4FindNet (IpSb, Dst); + + if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) { + return IpIf; + } + + // + // If source is one of the interface address, select it. + // + IpIf = Ip4FindInterface (IpSb, Src); + + if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) { + return IpIf; + } + + // + // Select a configured interface as the fall back. Always prefer + // an interface with non-zero address. + // + Selected = NULL; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) { + Selected = IpIf; + } + } + + return Selected; +} + + +/** + The default callback function for system generated packet. + It will free the packet. + + @param Ip4Instance The IP4 child that issued the transmission. It most + like is NULL. + @param Packet The packet that transmitted. + @param IoStatus The result of the transmission, succeeded or failed. + @param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK + for reference. + @param Context The context provided by us + + @return None + +**/ +VOID +Ip4SysPacketSent ( + IP4_PROTOCOL *Ip4Instance, + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ) +{ + NetbufFree (Packet); +} + + +/** + Transmit an IP4 packet. The packet comes either from the IP4 + child's consumer (IpInstance != NULL) or the IP4 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through some interface. + + @param IpSb The IP4 service instance to transmit the packet + @param IpInstance The IP4 child that issues the transmission. It is + NULL if the packet is from the system. + @param Packet The user data to send, excluding the IP header. + @param Head The caller supplied header. The caller should set + the following header fields: Tos, TotalLen, Id, tl, + Fragment, Protocol, Src and Dst. All the fields are + in host byte order. This function will fill in the + Ver, HeadLen, Fragment, and checksum. The Fragment + only need to include the DF flag. Ip4Output will + compute the MF and offset for you. + @param Option The original option to append to the IP headers + @param OptLen The length of the option + @param GateWay The next hop address to transmit packet to. + 255.255.255.255 means broadcast. + @param Callback The callback function to issue when transmission + completed. + @param Context The opaque context for the callback + + @retval EFI_NO_MAPPING There is no interface to the destination. + @retval EFI_NOT_FOUND There is no route to the destination + @retval EFI_SUCCESS The packet is successfully transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip4Output ( + IN IP4_SERVICE *IpSb, + IN IP4_PROTOCOL *IpInstance, OPTIONAL + IN NET_BUF *Packet, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptLen, + IN IP4_ADDR GateWay, + IN IP4_FRAME_CALLBACK Callback, + IN VOID *Context + ) +{ + IP4_INTERFACE *IpIf; + IP4_ROUTE_CACHE_ENTRY *CacheEntry; + IP4_ADDR Dest; + EFI_STATUS Status; + NET_BUF *Fragment; + UINT32 Index; + UINT32 HeadLen; + UINT32 PacketLen; + UINT32 Offset; + UINT32 Mtu; + UINT32 Num; + + // + // Select an interface/source for system packet, application + // should select them itself. + // + if (IpInstance == NULL) { + IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src); + } else { + IpIf = IpInstance->Interface; + } + + if (IpIf == NULL) { + return EFI_NO_MAPPING; + } + + if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) { + Head->Src = IpIf->Ip; + } + + // + // Route the packet unless overrided, that is, GateWay isn't zero. + // + if (GateWay == IP4_ALLZERO_ADDRESS) { + Dest = Head->Dst; + + if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) { + // + // Set the gateway to local broadcast if the Dest is + // is the broadcast address for the connected network + // or it is local broadcast. + // + GateWay = IP4_ALLONE_ADDRESS; + + } else if (IP4_IS_MULTICAST (Dest)) { + // + // Set the gateway to the destination if it is an multicast + // address. The IP4_INTERFACE won't consult ARP to send local + // broadcast and multicast. + // + GateWay = Head->Dst; + + } else { + // + // Consult the route table to route the packet + // + if (IpInstance == NULL) { + CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src); + } else { + CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src); + } + + if (CacheEntry == NULL) { + return EFI_NOT_FOUND; + } + + GateWay = CacheEntry->NextHop; + Ip4FreeRouteCacheEntry (CacheEntry); + } + } + + // + // OK, selected the source and route, fragment the packet then send + // them. Tag each fragment other than the first one as spawn from it. + // + Mtu = IpSb->SnpMode.MaxPacketSize; + HeadLen = sizeof (IP4_HEAD) + (OptLen + 3) &~0x03; + Head->Id = mIp4Id++; + + if (Packet->TotalSize + HeadLen > Mtu) { + // + // Packet is fragmented from the tail to the head, that is, the + // first frame sent is the last fragment of the packet. The first + // fragment is NOT sent in this loop. First compute how many + // fragments there are. + // + Mtu = (Mtu - HeadLen) & (~0x07); + Num = (Packet->TotalSize + Mtu - 1) / Mtu; + + // + // Initialize the packet length and Offset. Other than the last + // fragment, the packet length equals to MTU. The offset is always + // aligned to MTU. + // + PacketLen = Packet->TotalSize - (Num - 1) * Mtu; + Offset = Mtu * (Num - 1); + + for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) { + Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN); + + if (Fragment == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Update the header's fragment. The caller fills the IP4 header + // fields that are required by Ip4PrependHead except the fragment. + // + Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset); + Ip4PrependHead (Fragment, Head, Option, OptLen); + + // + // Transmit the fragments, pass the Packet address as the context. + // So, we can find all the fragments spawned from the Packet by + // compare the NetBuf and Context to the Packet. + // + Status = Ip4SendFrame ( + IpIf, + IpInstance, + Fragment, + GateWay, + Ip4SysPacketSent, + Packet + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + PacketLen = Mtu; + } + + // + // Trim the already sent data, then adjust the head's fragment field. + // + NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE); + Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0); + } + + // + // Send the first fragment, it is either the orginal packet, or the + // first fragment of a fragmented packet. It seems that there is a subtle + // bug here: what if the caller free the packet in Callback and IpIf (or + // MNP child used by that interface) still holds the fragments and try + // to access the data? The caller can free the packet if it recycles the + // consumer's (such as UDP) data in the Callback. But this can't happen. + // The detailed sequence is: + // 1. for the packets generated by IP4 driver itself: + // The Callback is Ip4SysPacketSent, which is the same as the + // fragments' callback. Ip4SysPacketSent simply calls NetbufFree + // to release its reference to the packet. So, no problem for + // system packets. + // + // 2. for the upper layer's packets (use UDP as an example): + // UDP requests the IP layer to transmit some data which is + // wrapped in an asynchronous token, the token is wrapped + // in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data + // in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP + // is bound with the Packet. It will only be freed when all + // the references to Packet have been released. Upon then, the + // Packet's OnFree callback will release the IP4_TXTOKEN_WRAP, + // and singal the user's recycle event. So, also no problem for + // upper layer's packets. + // + Ip4PrependHead (Packet, Head, Option, OptLen); + Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + Ip4CancelPacket (IpIf, Packet, Status); + return Status; +} + + +/** + The filter function to find a packet and all its fragments. + The packet's fragments have their Context set to the packet. + + @param Frame The frames hold by the low level interface + @param Context Context to the function, which is the packet. + + @retval TRUE This is the packet to cancel or its fragments. + @retval FALSE This is unrelated packet. + +**/ +STATIC +BOOLEAN +Ip4CancelPacketFragments ( + IP4_LINK_TX_TOKEN *Frame, + VOID *Context + ) +{ + if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) { + return TRUE; + } + + return FALSE; +} + + +/** + Cancel the Packet and all its fragments. + + @param IpIf The interface from which the Packet is sent + @param Packet The Packet to cancel + @param IoStatus The status returns to the sender. + + @return None + +**/ +VOID +Ip4CancelPacket ( + IN IP4_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ) +{ + Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet); +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h new file mode 100644 index 0000000000..e165e1d715 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.h @@ -0,0 +1,54 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Ip4Output.h + +Abstract: + + +**/ + +#ifndef __EFI_IP4_OUTPUT_H__ +#define __EFI_IP4_OUTPUT_H__ + +VOID +Ip4SysPacketSent ( + IP4_PROTOCOL *Ip4Instance, + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 Flag, + VOID *Context + ); + +EFI_STATUS +Ip4Output ( + IN IP4_SERVICE *IpSb, + IN IP4_PROTOCOL *IpInstance, OPTIONAL + IN NET_BUF *Data, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptLen, + IN IP4_ADDR GateWay, + IN IP4_FRAME_CALLBACK Callback, + IN VOID *Context + ); + +VOID +Ip4CancelPacket ( + IN IP4_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ); + +extern UINT16 mIp4Id; +#endif diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c new file mode 100644 index 0000000000..2cf1d0f29c --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.c @@ -0,0 +1,690 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Route.c + +Abstract: + + +**/ + +#include "Ip4Impl.h" + + +/** + Allocate a route entry then initialize it with the Dest/Netmaks + and Gateway. + + @param Dest The destination network + @param Netmask The destination network mask + @param GateWay The nexthop address + + @return NULL if failed to allocate memeory, otherwise the newly created + @return route entry. + +**/ +STATIC +IP4_ROUTE_ENTRY * +Ip4CreateRouteEntry ( + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR GateWay + ) +{ + IP4_ROUTE_ENTRY *RtEntry; + + RtEntry = NetAllocatePool (sizeof (IP4_ROUTE_ENTRY)); + + if (RtEntry == NULL) { + return NULL; + } + + NetListInit (&RtEntry->Link); + + RtEntry->RefCnt = 1; + RtEntry->Dest = Dest; + RtEntry->Netmask = Netmask; + RtEntry->NextHop = GateWay; + RtEntry->Flag = 0; + + return RtEntry; +} + + +/** + Free the route table entry. It is reference counted. + + @param RtEntry The route entry to free. + + @return NONE + +**/ +STATIC +VOID +Ip4FreeRouteEntry ( + IN IP4_ROUTE_ENTRY *RtEntry + ) +{ + ASSERT (RtEntry->RefCnt > 0); + + if (--RtEntry->RefCnt == 0) { + NetFreePool (RtEntry); + } +} + + +/** + Allocate and initialize a IP4 route cache entry. + + @param Dst The destination address + @param Src The source address + @param GateWay The next hop address + @param Tag The tag from the caller. This marks all the cache + entries spawned from one route table entry. + + @return NULL if failed to allocate memory for the cache, other point + @return to the created route cache entry. + +**/ +STATIC +IP4_ROUTE_CACHE_ENTRY * +Ip4CreateRouteCacheEntry ( + IN IP4_ADDR Dst, + IN IP4_ADDR Src, + IN IP4_ADDR GateWay, + IN UINTN Tag + ) +{ + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + + RtCacheEntry = NetAllocatePool (sizeof (IP4_ROUTE_CACHE_ENTRY)); + + if (RtCacheEntry == NULL) { + return NULL; + } + + NetListInit (&RtCacheEntry->Link); + + RtCacheEntry->RefCnt = 1; + RtCacheEntry->Dest = Dst; + RtCacheEntry->Src = Src; + RtCacheEntry->NextHop = GateWay; + RtCacheEntry->Tag = Tag; + + return RtCacheEntry; +} + + +/** + Free the route cache entry. It is reference counted. + + @param RtCacheEntry The route cache entry to free. + + @return None + +**/ +VOID +Ip4FreeRouteCacheEntry ( + IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry + ) +{ + ASSERT (RtCacheEntry->RefCnt > 0); + + if (--RtCacheEntry->RefCnt == 0) { + NetFreePool (RtCacheEntry); + } +} + + +/** + Initialize an empty route cache table. + + @param RtCache The rotue cache table to initialize. + + @return NONE + +**/ +VOID +Ip4InitRouteCache ( + IN IP4_ROUTE_CACHE *RtCache + ) +{ + UINT32 Index; + + for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) { + NetListInit (&(RtCache->CacheBucket[Index])); + } +} + + +/** + Clean up a route cache, that is free all the route cache + entries enqueued in the cache. + + @param RtCache The route cache table to clean up + + @return None + +**/ +VOID +Ip4CleanRouteCache ( + IN IP4_ROUTE_CACHE *RtCache + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtCache->CacheBucket[Index])) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + NetListRemoveEntry (Entry); + Ip4FreeRouteCacheEntry (RtCacheEntry); + } + } +} + + + +/** + Create an empty route table, includes its internal route cache + + None + + @return NULL if failed to allocate memory for the route table, otherwise + @return the point to newly created route table. + +**/ +IP4_ROUTE_TABLE * +Ip4CreateRouteTable ( + VOID + ) +{ + IP4_ROUTE_TABLE *RtTable; + UINT32 Index; + + RtTable = NetAllocatePool (sizeof (IP4_ROUTE_TABLE)); + + if (RtTable == NULL) { + return NULL; + } + + RtTable->RefCnt = 1; + RtTable->TotalNum = 0; + + for (Index = 0; Index < IP4_MASK_NUM; Index++) { + NetListInit (&(RtTable->RouteArea[Index])); + } + + RtTable->Next = NULL; + + Ip4InitRouteCache (&RtTable->Cache); + return RtTable; +} + + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param RtTable The route table to free. + + @return None + +**/ +VOID +Ip4FreeRouteTable ( + IN IP4_ROUTE_TABLE *RtTable + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ROUTE_ENTRY *RtEntry; + UINT32 Index; + + ASSERT (RtTable->RefCnt > 0); + + if (--RtTable->RefCnt > 0) { + return ; + } + + // + // Free all the route table entry and its route cache. + // + for (Index = 0; Index < IP4_MASK_NUM; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + NetListRemoveEntry (Entry); + Ip4FreeRouteEntry (RtEntry); + } + } + + Ip4CleanRouteCache (&RtTable->Cache); + + NetFreePool (RtTable); +} + + + +/** + Remove all the cache entries bearing the Tag. When a route cache + entry is created, it is tagged with the address of route entry + from which it is spawned. When a route entry is deleted, the cache + entries spawned from it are also deleted. + + @param RtCache Route cache to remove the entries from + @param Tag The Tag of the entries to remove + + @return None + +**/ +STATIC +VOID +Ip4PurgeRouteCache ( + IN IP4_ROUTE_CACHE *RtCache, + IN UINTN Tag + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) { + + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + if (RtCacheEntry->Tag == Tag) { + NetListRemoveEntry (Entry); + Ip4FreeRouteCacheEntry (RtCacheEntry); + } + } + } +} + + +/** + Add a route entry to the route table. All the IP4_ADDRs are in + host byte order. + + @param RtTable Route table to add route to + @param Dest The destination of the network + @param Netmask The netmask of the destination + @param Gateway The next hop address + + @retval EFI_ACCESS_DENIED The same route already exists + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry + @retval EFI_SUCCESS The route is added successfully. + +**/ +EFI_STATUS +Ip4AddRoute ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ) +{ + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Entry; + IP4_ROUTE_ENTRY *RtEntry; + + // + // All the route entries with the same netmask length are + // linke to the same route area + // + Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]); + + // + // First check whether the route exists + // + NET_LIST_FOR_EACH (Entry, Head) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) { + return EFI_ACCESS_DENIED; + } + } + + // + // Create a route entry and insert it to the route area. + // + RtEntry = Ip4CreateRouteEntry (Dest, Netmask, Gateway); + + if (RtEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (Gateway == IP4_ALLZERO_ADDRESS) { + RtEntry->Flag = IP4_DIRECT_ROUTE; + } + + NetListInsertHead (Head, &RtEntry->Link); + RtTable->TotalNum++; + + return EFI_SUCCESS; +} + + +/** + Remove a route entry and all the route caches spawn from it. + + @param RtTable The route table to remove the route from + @param Dest The destination network + @param Netmask The netmask of the Dest + @param Gateway The next hop address + + @retval EFI_SUCCESS The route entry is successfully removed + @retval EFI_NOT_FOUND There is no route entry in the table with that + properity. + +**/ +EFI_STATUS +Ip4DelRoute ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ) +{ + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ROUTE_ENTRY *RtEntry; + + Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]); + + NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) { + Ip4PurgeRouteCache (&RtTable->Cache, (UINTN) RtEntry); + NetListRemoveEntry (Entry); + Ip4FreeRouteEntry (RtEntry); + + RtTable->TotalNum--; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Find a route cache with the dst and src. This is used by ICMP + redirect messasge process. All kinds of redirect is treated as + host redirect according to RFC1122. So, only route cache entries + are modified according to the ICMP redirect message. + + @param RtTable The route table to search the cache for + @param Dest The destination address + @param Src The source address + + @return NULL if no route entry to the (Dest, Src). Otherwise the point + @return to the correct route cache entry. + +**/ +IP4_ROUTE_CACHE_ENTRY * +Ip4FindRouteCache ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src + ) +{ + NET_LIST_ENTRY *Entry; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + Index = IP4_ROUTE_CACHE_HASH (Dest, Src); + + NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + if ((RtCacheEntry->Dest == Dest) && (RtCacheEntry->Src == Src)) { + NET_GET_REF (RtCacheEntry); + return RtCacheEntry; + } + } + + return NULL; +} + + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (mask length == 32) to the shortest route area ( + default routes). In each route area, it will first search the instance's + route table, then the default route table. This is required by the following + requirements: + 1. IP search the route table for a most specific match + 2. The local route entries have precedence over the default route entry. + + @param RtTable The route table to search from + @param Dst The destionation address to search + + @return NULL if no route matches the Dst, otherwise the point to the + @return most specific route to the Dst. + +**/ +STATIC +IP4_ROUTE_ENTRY * +Ip4FindRouteEntry ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dst + ) +{ + NET_LIST_ENTRY *Entry; + IP4_ROUTE_ENTRY *RtEntry; + IP4_ROUTE_TABLE *Table; + INTN Index; + + RtEntry = NULL; + + for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) { + for (Table = RtTable; Table != NULL; Table = Table->Next) { + NET_LIST_FOR_EACH (Entry, &Table->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + if (IP4_NET_EQUAL (RtEntry->Dest, Dst, RtEntry->Netmask)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } + } + } + + + return NULL; +} + + +/** + Search the route table to route the packet. Return/creat a route + cache if there is a route to the destination. + + @param RtTable The route table to search from + @param Dest The destination address to search for + @param Src The source address to search for + + @return NULL if failed to route packet, otherwise a route cache + @return entry that can be used to route packet. + +**/ +IP4_ROUTE_CACHE_ENTRY * +Ip4Route ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src + ) +{ + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + IP4_ROUTE_CACHE_ENTRY *Cache; + IP4_ROUTE_ENTRY *RtEntry; + IP4_ADDR NextHop; + UINT32 Count; + + ASSERT (RtTable != NULL); + + Head = &RtTable->Cache.CacheBucket[IP4_ROUTE_CACHE_HASH (Dest, Src)]; + RtCacheEntry = Ip4FindRouteCache (RtTable, Dest, Src); + + // + // If found, promote the cache entry to the head of the hash bucket. LRU + // + if (RtCacheEntry != NULL) { + NetListRemoveEntry (&RtCacheEntry->Link); + NetListInsertHead (Head, &RtCacheEntry->Link); + return RtCacheEntry; + } + + // + // Search the route table for the most specific route + // + RtEntry = Ip4FindRouteEntry (RtTable, Dest); + + if (RtEntry == NULL) { + return NULL; + } + + // + // Found a route to the Dest, if it is a direct route, the packet + // will be send directly to the destination, such as for connected + // network. Otherwise, it is an indirect route, the packet will be + // send the next hop router. + // + if (RtEntry->Flag & IP4_DIRECT_ROUTE) { + NextHop = Dest; + } else { + NextHop = RtEntry->NextHop; + } + + Ip4FreeRouteEntry (RtEntry); + + // + // Create a route cache entry, and tag it as spawned from this route entry + // + RtCacheEntry = Ip4CreateRouteCacheEntry (Dest, Src, NextHop, (UINTN) RtEntry); + + if (RtCacheEntry == NULL) { + return NULL; + } + + NetListInsertHead (Head, &RtCacheEntry->Link); + NET_GET_REF (RtCacheEntry); + + // + // Each bucket of route cache can contain at most 64 entries. + // Remove the entries at the tail of the bucket. These entries + // are likely to be used least. + // + Count = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) { + if (++Count < IP4_ROUTE_CACHE_MAX) { + continue; + } + + Cache = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + NetListRemoveEntry (Entry); + Ip4FreeRouteCacheEntry (Cache); + } + + return RtCacheEntry; +} + + +/** + Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of + GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the + internal operation of the IP4 driver. + + @param IpInstance The IP4 child that requests the route table. + + @retval EFI_SUCCESS The route table is successfully build + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the rotue table. + +**/ +EFI_STATUS +Ip4BuildEfiRouteTable ( + IN IP4_PROTOCOL *IpInstance + ) +{ + IP4_SERVICE *IpSb; + NET_LIST_ENTRY *Entry; + IP4_ROUTE_TABLE *RtTable; + IP4_ROUTE_ENTRY *RtEntry; + EFI_IP4_ROUTE_TABLE *Table; + UINT32 Count; + INT32 Index; + + IpSb = IpInstance->Service; + RtTable = IpInstance->RouteTable; + + if (IpInstance->EfiRouteTable != NULL) { + NetFreePool (IpInstance->EfiRouteTable); + + IpInstance->EfiRouteTable = NULL; + IpInstance->EfiRouteCount = 0; + } + + Count = RtTable->TotalNum; + + if (RtTable->Next != NULL) { + Count += RtTable->Next->TotalNum; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + Table = NetAllocatePool (sizeof (EFI_IP4_ROUTE_TABLE) * Count); + + if (Table == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the route entry to EFI route table. Keep the order of + // route entry copied from most specific to default route. That + // is, interlevel the route entry from the instance's route area + // and those from the default route table's route area. + // + Count = 0; + + for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) { + for (RtTable = IpInstance->RouteTable; RtTable != NULL; RtTable = RtTable->Next) { + NET_LIST_FOR_EACH (Entry, &(RtTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + EFI_IP4 (Table[Count].SubnetAddress) = HTONL (RtEntry->Dest & RtEntry->Netmask); + EFI_IP4 (Table[Count].SubnetMask) = HTONL (RtEntry->Netmask); + EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop); + + Count++; + } + } + } + + IpInstance->EfiRouteTable = Table; + IpInstance->EfiRouteCount = Count; + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h new file mode 100644 index 0000000000..ce07b1d10c --- /dev/null +++ b/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Route.h @@ -0,0 +1,151 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Module Name: + + Ip4Route.h + +Abstract: + + EFI IP4 route table and route cache table defintions. + + +**/ + +#ifndef __EFI_IP4_ROUTE_H__ +#define __EFI_IP4_ROUTE_H__ + +#include "IP4Common.h" + +enum { + IP4_DIRECT_ROUTE = 0x00000001, + + IP4_ROUTE_CACHE_HASH = 31, + IP4_ROUTE_CACHE_MAX = 64, // Max NO. of cache entry per hash bucket +}; + +#define IP4_ROUTE_CACHE_HASH(Dst, Src) (((Dst) ^ (Src)) % IP4_ROUTE_CACHE_HASH) + +// +// The route entry in the route table. Dest/Netmask is the destion +// network. The nexthop is the gateway to send the packet to in +// order to reach the Dest/Netmask. If the Flag has IP4_DIRECT_ROUTE +// on, the gateway is the destination of the IP packet itself. Route +// enties of the connected network have the flag on. +// +typedef struct { + NET_LIST_ENTRY Link; + INTN RefCnt; + IP4_ADDR Dest; + IP4_ADDR Netmask; + IP4_ADDR NextHop; + UINT32 Flag; +} IP4_ROUTE_ENTRY; + +// +// The route cache entry. The route cache entry is optional. +// But it is necessary to support the ICMP redirect message. +// Check Ip4ProcessIcmpRedirect for information. +// +// The cache entry field Tag is used to tag all the route +// cache entry spawned from a route table entry. This makes +// it simple to delete all the route cache entries from a +// to-be-deleted route entry. +// +typedef struct { + NET_LIST_ENTRY Link; + INTN RefCnt; + IP4_ADDR Dest; + IP4_ADDR Src; + IP4_ADDR NextHop; + UINTN Tag; +} IP4_ROUTE_CACHE_ENTRY; + +// +// The route cache table is organized as a hash table. Each +// IP4 route table has a embedded route cache. For now the +// route cache and route table are binded togehter. But keep +// the route cache a seperated structure in case we want to +// detach them later. +// +typedef struct { + NET_LIST_ENTRY CacheBucket[IP4_ROUTE_CACHE_HASH]; +} IP4_ROUTE_CACHE; + +// +// Each IP4 instance has its own route table. Each ServiceBinding +// instance has a default route table and default address. +// +// All the route table entries with the same mask are linked +// together in one route area. For example, RouteArea[0] contains +// the default routes. A route table also contains a route cache. +// +typedef struct _IP4_ROUTE_TABLE IP4_ROUTE_TABLE; + +typedef struct _IP4_ROUTE_TABLE { + INTN RefCnt; + UINT32 TotalNum; + NET_LIST_ENTRY RouteArea[IP4_MASK_NUM]; + IP4_ROUTE_TABLE *Next; + IP4_ROUTE_CACHE Cache; +}; + +IP4_ROUTE_TABLE* +Ip4CreateRouteTable ( + VOID + ); + +VOID +Ip4FreeRouteTable ( + IN IP4_ROUTE_TABLE *RouteTable + ); + +EFI_STATUS +Ip4AddRoute ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ); + +EFI_STATUS +Ip4DelRoute ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ); + +IP4_ROUTE_CACHE_ENTRY * +Ip4FindRouteCache ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src + ); + +VOID +Ip4FreeRouteCacheEntry ( + IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry + ); + +IP4_ROUTE_CACHE_ENTRY * +Ip4Route ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src + ); + +EFI_STATUS +Ip4BuildEfiRouteTable ( + IN IP4_PROTOCOL *IpInstance + ); +#endif diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c index 5a648f5ffc..83fc3c6ae4 100644 --- a/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c @@ -139,7 +139,7 @@ MnpAddFreeNbuf ( for (Index = 0; Index < Count; Index++) { - Nbuf = NetbufAlloc (MnpServiceData->BufferLength); + Nbuf = NetbufAlloc (MnpServiceData->BufferLength + MnpServiceData->PaddingSize); if (Nbuf == NULL) { MNP_DEBUG_ERROR (("MnpAddFreeNbuf: NetBufAlloc failed.\n")); @@ -147,6 +147,14 @@ MnpAddFreeNbuf ( break; } + if (MnpServiceData->PaddingSize > 0) { + // + // Pad padding bytes before the media header + // + NetbufAllocSpace (Nbuf, MnpServiceData->PaddingSize, NET_BUF_TAIL); + NetbufTrim (Nbuf, MnpServiceData->PaddingSize, NET_BUF_HEAD); + } + NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf); } @@ -328,6 +336,12 @@ MnpInitializeServiceData ( // MnpServiceData->BufferLength = MnpServiceData->Mtu + SnpMode->MediaHeaderSize + NET_ETHER_FCS_SIZE; + // + // Make sure the protocol headers immediately following the media header + // 4-byte aligned + // + MnpServiceData->PaddingSize = (4 - SnpMode->MediaHeaderSize) & 0x3; + // // Initialize the FreeNetBufQue and pre-allocate some NET_BUFs. // diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c index 07be21dced..932b21c5ef 100644 --- a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c @@ -521,7 +521,6 @@ MnpServiceBindingDestroyChild ( return Status; } -//@MT: EFI_DRIVER_ENTRY_POINT (MnpDriverEntryPoint) EFI_STATUS EFIAPI diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h index 48ad0960cd..a387eb05d0 100644 --- a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h @@ -82,6 +82,7 @@ typedef struct _MNP_SERVICE_DATA { // store a packet. // UINT32 BufferLength; + UINT32 PaddingSize; NET_BUF *RxNbufCache; UINT8 *TxBuf; } MNP_SERVICE_DATA; diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c index 91b8d4e0b8..aafc8433a5 100644 --- a/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c @@ -974,7 +974,10 @@ MnpReceivePacket ( // // No receiver for this packet. // - NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL); + if (Trimmed > 0) { + NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL); + } + goto EXIT; } // diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c new file mode 100644 index 0000000000..258299418c --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/ComponentName.c @@ -0,0 +1,161 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + +#include "Mtftp4Driver.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +Mtftp4ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +Mtftp4ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName = { + Mtftp4ComponentNameGetDriverName, + Mtftp4ComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mMtftp4DriverNameTable[] = { + { + "eng", + L"MTFTP4 Network Service" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +Mtftp4ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This : A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language : A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName : A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCES : The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER : Language is NULL. + EFI_INVALID_PARAMETER : DriverName is NULL. + EFI_UNSUPPORTED : The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gMtftp4ComponentName.SupportedLanguages, + mMtftp4DriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +Mtftp4ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This : A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle :The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle :The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language : A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName : A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + + Returns: + EFI_SUCCESS :The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER : ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER : ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER : Language is NULL. + EFI_INVALID_PARAMETER : ControllerName is NULL. + EFI_UNSUPPORTED : The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED :The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c new file mode 100644 index 0000000000..9b30b3d730 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.c @@ -0,0 +1,643 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Driver.c + +Abstract: + + +**/ + +#include "Mtftp4Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding = { + Mtftp4DriverBindingSupported, + Mtftp4DriverBindingStart, + Mtftp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gMtftp4ServiceBindingTemplete = { + Mtftp4ServiceBindingCreateChild, + Mtftp4ServiceBindingDestroyChild +}; + +//@MT: EFI_DRIVER_ENTRY_POINT (Mtftp4DriverEntryPoint) + +EFI_STATUS +EFIAPI +Mtftp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The driver entry point which installs multiple protocols to the ImageHandle. + +Arguments: + + ImageHandle - The MTFTP's image handle + SystemTable - The system table + +Returns: + + EFI_SUCCESS - The handles are successfully installed on the image. Otherwise + some EFI_ERROR. + +--*/ +{ + return NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gMtftp4DriverBinding, + ImageHandle, + &gMtftp4ComponentName, + NULL, + NULL + ); +} + + +/** + Test whether MTFTP driver support this controller. + + @param This The MTFTP driver binding instance + @param Controller The controller to test + @param RemainingDevicePath The remaining device path + + @retval EFI_SUCCESS The controller has UDP service binding protocol + installed, MTFTP can support it. + @retval Others MTFTP can't support the controller. + +**/ +EFI_STATUS +Mtftp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiUdp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + +/** + Config a NULL UDP that is used to keep the connection between UDP + and MTFTP. Just leave the Udp child unconfigured. When UDP is + unloaded, MTFTP will be informed with DriverBinding Stop. + + @param UdpIo The UDP port to configure + @param Context The opaque parameter to the callback + + @retval EFI_SUCCESS It always return EFI_SUCCESS directly. + +**/ +EFI_STATUS +Mtftp4ConfigNullUdp ( + IN UDP_IO_PORT *UdpIo, + IN VOID *Context + ) +{ + return EFI_SUCCESS; +} + + +/** + Create then initialize a MTFTP service binding instance. + + @param Controller The controller to install the MTFTP service + binding on + @param Image The driver binding image of the MTFTP driver + @param Service The variable to receive the created service + binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the instance + @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep + connection with UDP. + @retval EFI_SUCCESS The service instance is created for the + controller. + +**/ +EFI_STATUS +Mtftp4CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + OUT MTFTP4_SERVICE **Service + ) +{ + MTFTP4_SERVICE *MtftpSb; + EFI_STATUS Status; + + *Service = NULL; + MtftpSb = NetAllocatePool (sizeof (MTFTP4_SERVICE)); + + if (MtftpSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MtftpSb->Signature = MTFTP4_SERVICE_SIGNATURE; + MtftpSb->ServiceBinding = gMtftp4ServiceBindingTemplete; + MtftpSb->InDestory = FALSE; + MtftpSb->ChildrenNum = 0; + NetListInit (&MtftpSb->Children); + + MtftpSb->Timer = NULL; + MtftpSb->TimerToGetMap = NULL; + MtftpSb->Controller = Controller; + MtftpSb->Image = Image; + MtftpSb->ConnectUdp = NULL; + + // + // Create the timer and a udp to be notified when UDP is uninstalled + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Mtftp4OnTimerTick, + MtftpSb, + &MtftpSb->Timer + ); + + if (EFI_ERROR (Status)) { + NetFreePool (MtftpSb); + return Status; + } + + // + // Create the timer used to time out the procedure which is used to + // get the default IP address. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &MtftpSb->TimerToGetMap + ); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (MtftpSb->Timer); + NetFreePool (MtftpSb); + return Status; + } + + MtftpSb->ConnectUdp = UdpIoCreatePort (Controller, Image, Mtftp4ConfigNullUdp, NULL); + + if (MtftpSb->ConnectUdp == NULL) { + gBS->CloseEvent (MtftpSb->TimerToGetMap); + gBS->CloseEvent (MtftpSb->Timer); + NetFreePool (MtftpSb); + return EFI_DEVICE_ERROR; + } + + *Service = MtftpSb; + return EFI_SUCCESS; +} + + +/** + Release all the resource used the MTFTP service binding instance. + + @param MtftpSb The MTFTP service binding instance. + + @return None + +**/ +VOID +Mtftp4CleanService ( + IN MTFTP4_SERVICE *MtftpSb + ) +{ + UdpIoFreePort (MtftpSb->ConnectUdp); + gBS->CloseEvent (MtftpSb->TimerToGetMap); + gBS->CloseEvent (MtftpSb->Timer); +} + + +/** + Start the MTFTP driver on this controller. MTFTP driver will + install a MTFTP SERVICE BINDING protocol on the supported + controller, which can be used to create/destroy MTFTP children. + + @param This The MTFTP driver binding protocol. + @param Controller The controller to manage. + @param RemainingDevicePath Remaining device path. + + @retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been + started on the controller. + @retval EFI_SUCCESS The MTFTP service binding is installed on the + controller. + +**/ +EFI_STATUS +Mtftp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + MTFTP4_SERVICE *MtftpSb; + EFI_STATUS Status; + + // + // Directly return if driver is already running. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiMtftp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = Mtftp4CreateService (Controller, This->DriverBindingHandle, &MtftpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the Mtftp4ServiceBinding Protocol onto Controller + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiMtftp4ServiceBindingProtocolGuid, + &MtftpSb->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + Mtftp4CleanService (MtftpSb); + NetFreePool (MtftpSb); + + return Status; +} + + +/** + Stop the MTFTP driver on controller. The controller is a UDP + child handle. + + @param This The MTFTP driver binding protocol + @param Controller The controller to stop + @param NumberOfChildren The number of children + @param ChildHandleBuffer The array of the child handle. + + @retval EFI_SUCCESS The driver is stopped on the controller. + @retval EFI_DEVICE_ERROR Failed to stop the driver on the controller. + +**/ +EFI_STATUS +Mtftp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + MTFTP4_SERVICE *MtftpSb; + MTFTP4_PROTOCOL *Instance; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // MTFTP driver opens UDP child, So, Controller is a UDP + // child handle. Locate the Nic handle first. Then get the + // MTFTP private data back. + // + NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp4ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + MtftpSb = MTFTP4_SERVICE_FROM_THIS (ServiceBinding); + + if (MtftpSb->InDestory) { + return EFI_SUCCESS; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + MtftpSb->InDestory = TRUE; + + while (!NetListIsEmpty (&MtftpSb->Children)) { + Instance = NET_LIST_HEAD (&MtftpSb->Children, MTFTP4_PROTOCOL, Link); + Mtftp4ServiceBindingDestroyChild (ServiceBinding, Instance->Handle); + } + + if (MtftpSb->ChildrenNum != 0) { + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + Status = gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + ServiceBinding + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Mtftp4CleanService (MtftpSb); + NetFreePool (MtftpSb); + + NET_RESTORE_TPL (OldTpl); + return EFI_SUCCESS; + +ON_ERROR: + MtftpSb->InDestory = FALSE; + + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Initialize a MTFTP protocol instance which is the child of MtftpSb. + + @param MtftpSb The MTFTP service binding protocol. + @param Instance The MTFTP instance to initialize. + + @return None + +**/ +VOID +Mtftp4InitProtocol ( + IN MTFTP4_SERVICE *MtftpSb, + IN MTFTP4_PROTOCOL *Instance + ) +{ + NetZeroMem (Instance, sizeof (MTFTP4_PROTOCOL)); + + Instance->Signature = MTFTP4_PROTOCOL_SIGNATURE; + NetListInit (&Instance->Link); + CopyMem (&Instance->Mtftp4, &gMtftp4ProtocolTemplate, sizeof (EFI_MTFTP4_PROTOCOL)); + Instance->State = MTFTP4_STATE_UNCONFIGED; + Instance->Indestory = FALSE; + Instance->Service = MtftpSb; + + NetListInit (&Instance->Blocks); +} + + +/** + Create a MTFTP child for the service binding instance, then + install the MTFTP protocol to the ChildHandle. + + @param This The MTFTP service binding instance. + @param ChildHandle The Child handle to install the MTFTP protocol. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child. + @retval EFI_SUCCESS The child is successfully create. + +**/ +EFI_STATUS +Mtftp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + MTFTP4_SERVICE *MtftpSb; + MTFTP4_PROTOCOL *Instance; + EFI_STATUS Status; + EFI_TPL OldTpl; + VOID *Udp4; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = NetAllocatePool (sizeof (*Instance)); + + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MtftpSb = MTFTP4_SERVICE_FROM_THIS (This); + + Mtftp4InitProtocol (MtftpSb, Instance); + + Instance->UnicastPort = UdpIoCreatePort ( + MtftpSb->Controller, + MtftpSb->Image, + Mtftp4ConfigNullUdp, + Instance + ); + + if (Instance->UnicastPort == NULL) { + NetFreePool (Instance); + return EFI_OUT_OF_RESOURCES; + } + + // + // Install the MTFTP protocol onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiMtftp4ProtocolGuid, + &Instance->Mtftp4, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->Handle = *ChildHandle; + + // + // Open the Udp4 protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + MtftpSb->ConnectUdp->UdpHandle, + &gEfiUdp4ProtocolGuid, + (VOID **) &Udp4, + gMtftp4DriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiMtftp4ProtocolGuid, + &Instance->Mtftp4, + NULL + ); + + goto ON_ERROR; + } + + // + // Add it to the parent's child list. + // + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + NetListInsertTail (&MtftpSb->Children, &Instance->Link); + MtftpSb->ChildrenNum++; + + NET_RESTORE_TPL (OldTpl); + +ON_ERROR: + + if (EFI_ERROR (Status)) { + UdpIoFreePort (Instance->UnicastPort); + NetFreePool (Instance); + } + + return Status; +} + + +/** + Destory one of the service binding's child. + + @param This The service binding instance + @param ChildHandle The child handle to destory + + @retval EFI_INVALID_PARAMETER The parameter is invaid. + @retval EFI_UNSUPPORTED The child may have already been destoried. + @retval EFI_SUCCESS The child is destoried and removed from the + parent's child list. + +**/ +EFI_STATUS +Mtftp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + MTFTP4_SERVICE *MtftpSb; + MTFTP4_PROTOCOL *Instance; + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiMtftp4ProtocolGuid, + (VOID **) &Mtftp4, + gMtftp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = MTFTP4_PROTOCOL_FROM_THIS (Mtftp4); + MtftpSb = MTFTP4_SERVICE_FROM_THIS (This); + + if (Instance->Service != MtftpSb) { + return EFI_INVALID_PARAMETER; + } + + if (Instance->Indestory) { + return EFI_SUCCESS; + } + + Instance->Indestory = TRUE; + + // + // Close the Udp4 protocol. + // + gBS->CloseProtocol ( + MtftpSb->ConnectUdp->UdpHandle, + &gEfiUdp4ProtocolGuid, + gMtftp4DriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the MTFTP4 protocol first to enable a top down destruction. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiMtftp4ProtocolGuid, + Mtftp4 + ); + + if (EFI_ERROR (Status)) { + Instance->Indestory = FALSE; + return Status; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR); + UdpIoFreePort (Instance->UnicastPort); + + NetListRemoveEntry (&Instance->Link); + MtftpSb->ChildrenNum--; + + NET_RESTORE_TPL (OldTpl); + + NetFreePool (Instance); + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h new file mode 100644 index 0000000000..a9b7ac7121 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Driver.h @@ -0,0 +1,69 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Driver.h + +Abstract: + + +**/ + +#ifndef __EFI_MTFTP4_DRIVER_H__ +#define __EFI_MTFTP4_DRIVER_H__ + +#include + +#include + +#include +#include + + +EFI_STATUS +Mtftp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +Mtftp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +Mtftp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +EFI_STATUS +Mtftp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +Mtftp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +extern EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName; +extern EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding; + +#endif diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf new file mode 100644 index 0000000000..d4198c5140 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf @@ -0,0 +1,68 @@ +#/** @file +# Component name for module Mtftp4 +# +# Copyright (c) 2007, Intel Corporation. +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Mtftp4Dxe + FILE_GUID = DC3641B8-2FA8-4ed3-BC1F-F9962A03454B + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = Mtftp4DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Mtftp4Option.c + Mtftp4Rrq.c + Mtftp4Impl.h + ComponentName.c + Mtftp4Support.c + Mtftp4Impl.c + Mtftp4Option.h + Mtftp4Support.h + Mtftp4Driver.h + Mtftp4Driver.c + Mtftp4Wrq.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + NetLib + UdpIoLib + + +[Protocols] + gEfiMtftp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiMtftp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa new file mode 100644 index 0000000000..c7b8360ac3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.msa @@ -0,0 +1,79 @@ + + + Mtftp4Dxe + DXE_DRIVER + DC3641B8-2FA8-4ed3-BC1F-F9962A03454B + 1.0 + Component name for module Mtftp4 + FIX ME! + Copyright (c) 2007, Intel Corporation. All rights reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + Mtftp4Dxe + + + + DebugLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + BaseLib + + + + Mtftp4Wrq.c + Mtftp4Driver.c + Mtftp4Driver.h + Mtftp4Support.h + Mtftp4Option.h + Mtftp4Impl.c + Mtftp4Support.c + ComponentName.c + Mtftp4Impl.h + Mtftp4Rrq.c + Mtftp4Option.c + + + + + + + + gEfiUdp4ProtocolGuid + + + gEfiUdp4ServiceBindingProtocolGuid + + + gEfiMtftp4ProtocolGuid + + + gEfiMtftp4ServiceBindingProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + Mtftp4DriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c new file mode 100644 index 0000000000..5bfd2538ef --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.c @@ -0,0 +1,892 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Impl.c + +Abstract: + + Interface routine for Mtftp4 + + +**/ + +#include "Mtftp4Impl.h" + +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4ReadFile ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ); + + +/** + Get the current operation parameter for the MTFTP session + + @param This The MTFTP protocol instance + @param ModeData The MTFTP mode data + + @retval EFI_INVALID_PARAMETER This or ModeData is NULL + @retval EFI_SUCCESS The operation parameter is saved in ModeData + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4GetModeData ( + IN EFI_MTFTP4_PROTOCOL *This, + OUT EFI_MTFTP4_MODE_DATA *ModeData + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (ModeData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + Instance = MTFTP4_PROTOCOL_FROM_THIS (This); + ModeData->ConfigData = Instance->Config; + ModeData->SupportedOptionCount = MTFTP4_SUPPORTED_OPTIONS; + ModeData->SupportedOptoins = mMtftp4SupportedOptions; + ModeData->UnsupportedOptionCount = 0; + ModeData->UnsupportedOptoins = NULL; + + NET_RESTORE_TPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Clean up the MTFTP session to get ready for new operation. + + @param Instance The MTFTP session to clean up + @param Result The result to return to the caller who initiated + the operation. + + @return None + +**/ +VOID +Mtftp4CleanOperation ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_STATUS Result + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + MTFTP4_BLOCK_RANGE *Block; + EFI_MTFTP4_TOKEN *Token; + + // + // Free various resources. + // + Token = Instance->Token; + + if (Token != NULL) { + Token->Status = Result; + + if (Token->Event != NULL) { + gBS->SignalEvent (Token->Event); + } + + Instance->Token = NULL; + } + + ASSERT (Instance->UnicastPort != NULL); + UdpIoCleanPort (Instance->UnicastPort); + + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + Instance->LastPacket = NULL; + } + + if (Instance->McastUdpPort != NULL) { + UdpIoFreePort (Instance->McastUdpPort); + Instance->McastUdpPort = NULL; + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->Blocks) { + Block = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link); + NetListRemoveEntry (Entry); + NetFreePool (Block); + } + + NetZeroMem (&Instance->RequestOption, sizeof (MTFTP4_OPTION)); + + Instance->Operation = 0; + + Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE; + Instance->LastBlock = 0; + Instance->ServerIp = 0; + Instance->ListeningPort = 0; + Instance->ConnectedPort = 0; + Instance->Gateway = 0; + Instance->PacketToLive = 0; + Instance->MaxRetry = 0; + Instance->CurRetry = 0; + Instance->Timeout = 0; + Instance->McastIp = 0; + Instance->McastPort = 0; + Instance->Master = TRUE; +} + + +/** + Configure the MTFTP session for new operation or reset the current + operation if ConfigData is NULL. + + @param This The MTFTP session to configure + @param ConfigData The configure parameters + + @retval EFI_INVALID_PARAMETER Some of the parameter is invalid. + @retval EFI_ACCESS_DENIED There is pending operation + @retval EFI_SUCCESS The instance is configured for operation. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4Configure ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_CONFIG_DATA *ConfigData + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_TPL OldTpl; + IP4_ADDR Ip; + IP4_ADDR Netmask; + IP4_ADDR Gateway; + IP4_ADDR ServerIp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP4_PROTOCOL_FROM_THIS (This); + + if (ConfigData == NULL) { + // + // Reset the operation if ConfigData is NULL + // + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + Mtftp4CleanOperation (Instance, EFI_ABORTED); + NetZeroMem (&Instance->Config, sizeof (EFI_MTFTP4_CONFIG_DATA)); + Instance->State = MTFTP4_STATE_UNCONFIGED; + + NET_RESTORE_TPL (OldTpl); + + } else { + // + // Configure the parameters for new operation. + // + NetCopyMem (&Ip, &ConfigData->StationIp, sizeof (IP4_ADDR)); + NetCopyMem (&Netmask, &ConfigData->SubnetMask, sizeof (IP4_ADDR)); + NetCopyMem (&Gateway, &ConfigData->GatewayIp, sizeof (IP4_ADDR)); + NetCopyMem (&ServerIp, &ConfigData->ServerIp, sizeof (IP4_ADDR)); + + Ip = NTOHL (Ip); + Netmask = NTOHL (Netmask); + Gateway = NTOHL (Gateway); + ServerIp = NTOHL (ServerIp); + + if (!Ip4IsUnicast (ServerIp, 0)) { + return EFI_INVALID_PARAMETER; + } + + if (!ConfigData->UseDefaultSetting && + ((!IP4_IS_VALID_NETMASK (Netmask) || !Ip4IsUnicast (Ip, Netmask)))) { + + return EFI_INVALID_PARAMETER; + } + + if ((Gateway != 0) && + (!IP4_NET_EQUAL (Gateway, Ip, Netmask) || !Ip4IsUnicast (Gateway, Netmask))) { + + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if ((Instance->State == MTFTP4_STATE_CONFIGED) && (Instance->Operation != 0)) { + NET_RESTORE_TPL (OldTpl); + return EFI_ACCESS_DENIED; + } + + Instance->Config = *ConfigData; + Instance->State = MTFTP4_STATE_CONFIGED; + + NET_RESTORE_TPL (OldTpl); + } + + return EFI_SUCCESS; +} + + +/** + Check packet for GetInfo. GetInfo is implemented with EfiMtftp4ReadFile. + It use Mtftp4GetInfoCheckPacket to inspect the first packet from server, + then abort the session. + + @param This The MTFTP4 protocol instance + @param Token The user's token + @param PacketLen The length of the packet + @param Packet The received packet. + + @retval EFI_ABORTED Abort the ReadFile operation and return. + +**/ +STATIC +EFI_STATUS +Mtftp4GetInfoCheckPacket ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Packet + ) +{ + MTFTP4_GETINFO_STATE *State; + EFI_STATUS Status; + UINT16 OpCode; + + State = (MTFTP4_GETINFO_STATE *) Token->Context; + OpCode = NTOHS (Packet->OpCode); + + // + // Set the GetInfo's return status according to the OpCode. + // + switch (OpCode) { + case EFI_MTFTP4_OPCODE_ERROR: + State->Status = EFI_TFTP_ERROR; + break; + + case EFI_MTFTP4_OPCODE_OACK: + State->Status = EFI_SUCCESS; + break; + + default: + State->Status = EFI_PROTOCOL_ERROR; + } + + // + // Allocate buffer then copy the packet over. Use gBS->AllocatePool + // in case NetAllocatePool will implements something tricky. + // + Status = gBS->AllocatePool (EfiBootServicesData, PacketLen, (VOID **) State->Packet); + + if (EFI_ERROR (Status)) { + State->Status = EFI_OUT_OF_RESOURCES; + return EFI_ABORTED; + } + + *(State->PacketLen) = PacketLen; + NetCopyMem (*(State->Packet), Packet, PacketLen); + + return EFI_ABORTED; +} + + +/** + Get the information of the download from the server. It is implemented + with EfiMtftp4ReadFile: build a token, then pass it to EfiMtftp4ReadFile. + In its check packet callback abort the opertions. + + @param This The MTFTP protocol instance + @param OverrideData The MTFTP override data + @param Filename The file to get information + @param ModeStr The mode to use + @param OptionCount The number of options to append + @param OptionList The options to append + @param PacketLength The variable to receive the packet length + @param Packet The variable to receive the packet. + + @retval EFI_INVALID_PARAMETER The parameter is invaid + @retval EFI_SUCCESS The information is got + @retval Others Failed to get the information. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4GetInfo ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_OVERRIDE_DATA *OverrideData, OPTIONAL + IN UINT8 *Filename, + IN UINT8 *ModeStr, OPTIONAL + IN UINT8 OptionCount, + IN EFI_MTFTP4_OPTION *OptionList, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP4_PACKET **Packet OPTIONAL + ) +{ + EFI_MTFTP4_TOKEN Token; + MTFTP4_GETINFO_STATE State; + EFI_STATUS Status; + + if ((This == NULL) || (Filename == NULL) || (PacketLength == NULL) || + (OptionCount && (OptionList == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (Packet != NULL) { + *Packet = NULL; + } + + *PacketLength = 0; + State.Packet = Packet; + State.PacketLen = PacketLength; + State.Status = EFI_SUCCESS; + + // + // Fill in the Token to issue an synchronous ReadFile operation + // + Token.Status = EFI_SUCCESS; + Token.Event = NULL; + Token.OverrideData = OverrideData; + Token.Filename = Filename; + Token.ModeStr = ModeStr; + Token.OptionCount = OptionCount; + Token.OptionList = OptionList; + Token.BufferSize = 0; + Token.Buffer = NULL; + Token.Context = &State; + Token.CheckPacket = Mtftp4GetInfoCheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = EfiMtftp4ReadFile (This, &Token); + + if (EFI_ABORTED == Status) { + return State.Status; + } + + return Status; +} + + +/** + Parse the packet into an array of options. The OptionList is allocated + by this function, and caller should free it when used. + + @param This The MTFTP protocol instance + @param PacketLen The length of the packet + @param Packet The packet to parse + @param OptionCount The size of the OptionList array allocated. + @param OptionList The allocated option array to save the option + addresses. + + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_NOT_FOUND There is no valid option in the packet + @retval EFI_SUCCESS The packet is parsed. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4ParseOptions ( + IN EFI_MTFTP4_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP4_PACKET *Packet, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL + ) +{ + EFI_STATUS Status; + + if ((This == NULL) || (PacketLen < MTFTP4_OPCODE_LEN) || + (Packet == NULL) || (OptionCount == NULL)) { + + return EFI_INVALID_PARAMETER; + } + + Status = Mtftp4ExtractOptions (Packet, PacketLen, OptionCount, OptionList); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (*OptionCount == 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + + +/** + Check whether the override data is valid. It will first + validate whether the server is a valid unicast. If a gateway + is provided in the Override, it also check that it is a + unicast on the connected network. + + @param Instance The MTFTP instance + @param Override The override data to validate. + + @return TRUE if the override data is valid, otherwise FALSE. + +**/ +STATIC +BOOLEAN +Mtftp4OverrideValid ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_OVERRIDE_DATA *Override + ) +{ + EFI_MTFTP4_CONFIG_DATA *Config; + IP4_ADDR Ip; + IP4_ADDR Netmask; + IP4_ADDR Gateway; + + NetCopyMem (&Ip, &Override->ServerIp, sizeof (IP4_ADDR)); + if (!Ip4IsUnicast (NTOHL (Ip), 0)) { + return FALSE; + } + + Config = &Instance->Config; + + NetCopyMem (&Gateway, &Override->GatewayIp, sizeof (IP4_ADDR)); + Gateway = NTOHL (Gateway); + + if (!Config->UseDefaultSetting && (Gateway != 0)) { + NetCopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR)); + NetCopyMem (&Ip, &Config->StationIp, sizeof (IP4_ADDR)); + + Netmask = NTOHL (Netmask); + Ip = NTOHL (Ip); + + if (!Ip4IsUnicast (Gateway, Netmask) || !IP4_NET_EQUAL (Gateway, Ip, Netmask)) { + return FALSE; + } + } + + return TRUE; +} + + +/** + Poll the UDP to get the IP4 default address, which may be retrieved + by DHCP. The default time out value is 5 seconds. If IP has retrieved + the default address, the UDP is reconfigured. + + @param Instance The Mtftp instance + @param UdpPort The UDP port to poll + @param UdpCfgData The UDP configure data to reconfigure the UDP + port. + + @return TRUE if the default address is retrieved and UDP is reconfigured. + @return Otherwise FALSE. + +**/ +BOOLEAN +Mtftp4GetMapping ( + IN MTFTP4_PROTOCOL *Instance, + IN UDP_IO_PORT *UdpPort, + IN EFI_UDP4_CONFIG_DATA *UdpCfgData + ) +{ + MTFTP4_SERVICE *Service; + EFI_IP4_MODE_DATA Ip4Mode; + EFI_UDP4_PROTOCOL *Udp; + EFI_STATUS Status; + + ASSERT (Instance->Config.UseDefaultSetting); + + Service = Instance->Service; + Udp = UdpPort->Udp; + + Status = gBS->SetTimer ( + Service->TimerToGetMap, + TimerRelative, + MTFTP4_TIME_TO_GETMAP * TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + while (!EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) { + Udp->Poll (Udp); + + if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip4Mode, NULL, NULL)) && + Ip4Mode.IsConfigured) { + + Udp->Configure (Udp, NULL); + return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS); + } + } + + return FALSE; +} + + +/** + Configure the UDP port for unicast receiving. + + @param UdpIo The UDP port + @param Instance The MTFTP session + + @retval EFI_SUCCESS The UDP port is successfully configured for the + session to unicast receive. + +**/ +STATIC +EFI_STATUS +Mtftp4ConfigUnicastPort ( + IN UDP_IO_PORT *UdpIo, + IN MTFTP4_PROTOCOL *Instance + ) +{ + EFI_MTFTP4_CONFIG_DATA *Config; + EFI_UDP4_CONFIG_DATA UdpConfig; + EFI_STATUS Status; + IP4_ADDR Ip; + + Config = &Instance->Config; + + UdpConfig.AcceptBroadcast = FALSE; + UdpConfig.AcceptPromiscuous = FALSE; + UdpConfig.AcceptAnyPort = FALSE; + UdpConfig.AllowDuplicatePort = FALSE; + UdpConfig.TypeOfService = 0; + UdpConfig.TimeToLive = 64; + UdpConfig.DoNotFragment = FALSE; + UdpConfig.ReceiveTimeout = 0; + UdpConfig.TransmitTimeout = 0; + UdpConfig.UseDefaultAddress = Config->UseDefaultSetting; + UdpConfig.StationAddress = Config->StationIp; + UdpConfig.SubnetMask = Config->SubnetMask; + UdpConfig.StationPort = 0; + UdpConfig.RemotePort = 0; + + Ip = HTONL (Instance->ServerIp); + NetCopyMem (&UdpConfig.RemoteAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfig); + + if ((Status == EFI_NO_MAPPING) && Mtftp4GetMapping (Instance, UdpIo, &UdpConfig)) { + return EFI_SUCCESS; + } + + return Status; +} + + +/** + Start the MTFTP session to do the operation, such as read file, + write file, and read directory. + + @param This The MTFTP session + @param Token The token than encapsues the user's request. + @param Operation The operation to do + + @retval EFI_INVALID_PARAMETER Some of the parameters are invalid. + @retval EFI_NOT_STARTED The MTFTP session hasn't been configured. + @retval EFI_ALREADY_STARTED There is pending operation for the session. + @retval EFI_SUCCESS The operation is successfully started. + +**/ +STATIC +EFI_STATUS +Mtftp4Start ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 Operation + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_MTFTP4_OVERRIDE_DATA *Override; + EFI_MTFTP4_CONFIG_DATA *Config; + EFI_TPL OldTpl; + EFI_STATUS Status; + + // + // Validate the parameters + // + if ((This == NULL) || (Token == NULL) || (Token->Filename == NULL) || + ((Token->OptionCount != 0) && (Token->OptionList == NULL))) { + return EFI_INVALID_PARAMETER; + } + + // + // User must provide at least one method to collect the data for download. + // + if (((Operation == EFI_MTFTP4_OPCODE_RRQ) || (Operation == EFI_MTFTP4_OPCODE_DIR)) && + ((Token->Buffer == NULL) && (Token->CheckPacket == NULL))) { + return EFI_INVALID_PARAMETER; + } + + // + // User must provide at least one method to provide the data for upload. + // + if ((Operation == EFI_MTFTP4_OPCODE_WRQ) && + ((Token->Buffer == NULL) && (Token->PacketNeeded == NULL))) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP4_PROTOCOL_FROM_THIS (This); + + Status = EFI_SUCCESS; + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (Instance->State != MTFTP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + } + + if (Instance->Operation != 0) { + Status = EFI_ACCESS_DENIED; + } + + if (EFI_ERROR (Status)) { + NET_RESTORE_TPL (OldTpl); + return Status; + } + + // + // Set the Operation now to prevent the application start other + // operations. + // + Instance->Operation = Operation; + Override = Token->OverrideData; + + if ((Override != NULL) && !Mtftp4OverrideValid (Instance, Override)) { + Status = EFI_INVALID_PARAMETER; + goto ON_ERROR; + } + + if (Token->OptionCount != 0) { + Status = Mtftp4ParseOption ( + Token->OptionList, + Token->OptionCount, + TRUE, + &Instance->RequestOption + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Set the operation parameters from the configuration or override data. + // + Config = &Instance->Config; + Instance->Token = Token; + Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE; + + NetCopyMem (&Instance->ServerIp, &Config->ServerIp, sizeof (IP4_ADDR)); + Instance->ServerIp = NTOHL (Instance->ServerIp); + + Instance->ListeningPort = Config->InitialServerPort; + Instance->ConnectedPort = 0; + + NetCopyMem (&Instance->Gateway, &Config->GatewayIp, sizeof (IP4_ADDR)); + Instance->Gateway = NTOHL (Instance->Gateway); + + Instance->MaxRetry = Config->TryCount; + Instance->Timeout = Config->TimeoutValue; + Instance->Master = TRUE; + + if (Override != NULL) { + NetCopyMem (&Instance->ServerIp, &Override->ServerIp, sizeof (IP4_ADDR)); + NetCopyMem (&Instance->Gateway, &Override->GatewayIp, sizeof (IP4_ADDR)); + + Instance->ServerIp = NTOHL (Instance->ServerIp); + Instance->Gateway = NTOHL (Instance->Gateway); + + Instance->ListeningPort = Override->ServerPort; + Instance->MaxRetry = Override->TryCount; + Instance->Timeout = Override->TimeoutValue; + } + + if (Instance->ListeningPort == 0) { + Instance->ListeningPort = MTFTP4_DEFAULT_SERVER_PORT; + } + + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = MTFTP4_DEFAULT_RETRY; + } + + if (Instance->Timeout == 0) { + Instance->Timeout = MTFTP4_DEFAULT_TIMEOUT; + } + + // + // Config the unicast UDP child to send initial request + // + Status = Mtftp4ConfigUnicastPort (Instance->UnicastPort, Instance); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Build and send an initial requests + // + if (Operation == EFI_MTFTP4_OPCODE_WRQ) { + Status = Mtftp4WrqStart (Instance, Operation); + } else { + Status = Mtftp4RrqStart (Instance, Operation); + } + + NET_RESTORE_TPL (OldTpl); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + // + // Return immediately for asynchronous operation or poll the + // instance for synchronous operation. + // + Token->Status = EFI_NOT_READY; + + if (Token->Event != NULL) { + return EFI_SUCCESS; + } + + while (Token->Status == EFI_NOT_READY) { + This->Poll (This); + } + + return Token->Status; + +ON_ERROR: + Mtftp4CleanOperation (Instance, Status); + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Read a file from the server. + + @param This The Mtftp protocol instance. + @param Token The user's request wrap token. + + @retval EFI_SUCCESS The ReadFile has finished, the file has been + downloaded if it is synchronous operation, + otherwise it has been initated. + @retval Others Some error happened. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4ReadFile ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ) +{ + return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_RRQ); +} + + +/** + Upload a file to the server. + + @param This The MTFTP protocol session + @param Token The user's request wrap token. + + @retval EFI_SUCCESS The WriteFile has finished, the file has been + uploaded if it is synchronous operation, otherwise + it has been initated. + @retval Others Some error happened. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4WriteFile ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ) +{ + return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_WRQ); +} + + +/** + Read a directory from the server. The only difference + between ReadFile and ReadDirectory is the opcode used. + + @param This The MTFTP protocol session + @param Token The user's request wrap token. + + @retval EFI_SUCCESS The ReadDirectory has finished, the directory has + been downloaded as a file if it is synchronous + operation, otherwise it has been initated. + @retval Others Some error happened. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4ReadDirectory ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ) +{ + return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_DIR); +} + + +/** + Poll the network stack to accelerate the packet process. + + @param This The MTFTP protocol instance. + + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED The MTFTP session hasn't been configured. + @retval EFI_DEVICE_ERROR The MTFTP session has been destoried. + +**/ +STATIC +EFI_STATUS +EFIAPI +EfiMtftp4Poll ( + IN EFI_MTFTP4_PROTOCOL *This + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_UDP4_PROTOCOL *Udp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP4_PROTOCOL_FROM_THIS (This); + + if (Instance->State == MTFTP4_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } else if (Instance->State == MTFTP4_STATE_DESTORY) { + return EFI_DEVICE_ERROR; + } + + Udp = Instance->UnicastPort->Udp; + return Udp->Poll (Udp); +} + +EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate = { + EfiMtftp4GetModeData, + EfiMtftp4Configure, + EfiMtftp4GetInfo, + EfiMtftp4ParseOptions, + EfiMtftp4ReadFile, + EfiMtftp4WriteFile, + EfiMtftp4ReadDirectory, + EfiMtftp4Poll +}; diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h new file mode 100644 index 0000000000..24afe6b7cf --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Impl.h @@ -0,0 +1,176 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Impl.h + +Abstract: + + Mtftp4 Implementation, it supports the following RFCs: + RFC1350 - THE TFTP PROTOCOL (REVISION 2) + RFC2090 - TFTP Multicast Option + RFC2347 - TFTP Option Extension + RFC2348 - TFTP Blocksize Option + RFC2349 - TFTP Timeout Interval and Transfer Size Options + + +**/ + +#ifndef __EFI_MTFTP4_IMPL_H__ +#define __EFI_MTFTP4_IMPL_H__ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _MTFTP4_SERVICE MTFTP4_SERVICE; +typedef struct _MTFTP4_PROTOCOL MTFTP4_PROTOCOL; + +#include "Mtftp4Driver.h" +#include "Mtftp4Option.h" +#include "Mtftp4Support.h" + +enum { + MTFTP4_SERVICE_SIGNATURE = EFI_SIGNATURE_32 ('T', 'F', 'T', 'P'), + MTFTP4_PROTOCOL_SIGNATURE = EFI_SIGNATURE_32 ('t', 'f', 't', 'p'), + + MTFTP4_DEFAULT_SERVER_PORT = 69, + MTFTP4_DEFAULT_TIMEOUT = 3, + MTFTP4_DEFAULT_RETRY = 5, + MTFTP4_DEFAULT_BLKSIZE = 512, + MTFTP4_TIME_TO_GETMAP = 5, + + MTFTP4_STATE_UNCONFIGED = 0, + MTFTP4_STATE_CONFIGED, + MTFTP4_STATE_DESTORY, +}; + +typedef struct _MTFTP4_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + + BOOLEAN InDestory; + + UINT16 ChildrenNum; + NET_LIST_ENTRY Children; + + EFI_EVENT Timer; // Ticking timer for all the MTFTP clients + EFI_EVENT TimerToGetMap; + + EFI_HANDLE Controller; + EFI_HANDLE Image; + + // + // This UDP child is used to keep the connection between the UDP + // and MTFTP, so MTFTP will be notified when UDP is uninstalled. + // + UDP_IO_PORT *ConnectUdp; +}; + +typedef struct _MTFTP4_PROTOCOL { + UINT32 Signature; + NET_LIST_ENTRY Link; + EFI_MTFTP4_PROTOCOL Mtftp4; + + INTN State; + BOOLEAN Indestory; + + MTFTP4_SERVICE *Service; + EFI_HANDLE Handle; + + EFI_MTFTP4_CONFIG_DATA Config; + + // + // Operation parameters: token and requested options. + // + EFI_MTFTP4_TOKEN *Token; + MTFTP4_OPTION RequestOption; + UINT16 Operation; + + // + // Blocks is a list of MTFTP4_BLOCK_RANGE which contains + // holes in the file + // + UINT16 BlkSize; + UINT16 LastBlock; + NET_LIST_ENTRY Blocks; + + // + // The server's communication end point: IP and two ports. one for + // initial request, one for its selected port. + // + IP4_ADDR ServerIp; + UINT16 ListeningPort; + UINT16 ConnectedPort; + IP4_ADDR Gateway; + UDP_IO_PORT *UnicastPort; + + // + // Timeout and retransmit status + // + NET_BUF *LastPacket; + UINT32 PacketToLive; + UINT32 CurRetry; + UINT32 MaxRetry; + UINT32 Timeout; + + // + // Parameter used by RRQ's multicast download. + // + IP4_ADDR McastIp; + UINT16 McastPort; + BOOLEAN Master; + UDP_IO_PORT *McastUdpPort; +}; + +typedef struct { + EFI_MTFTP4_PACKET **Packet; + UINT32 *PacketLen; + EFI_STATUS Status; +} MTFTP4_GETINFO_STATE; + +VOID +Mtftp4CleanOperation ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_STATUS Result + ); + +EFI_STATUS +Mtftp4WrqStart ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 Operation + ); + +EFI_STATUS +Mtftp4RrqStart ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 Operation + ); + +#define MTFTP4_SERVICE_FROM_THIS(a) \ + CR (a, MTFTP4_SERVICE, ServiceBinding, MTFTP4_SERVICE_SIGNATURE) + +#define MTFTP4_PROTOCOL_FROM_THIS(a) \ + CR (a, MTFTP4_PROTOCOL, Mtftp4, MTFTP4_PROTOCOL_SIGNATURE) + +extern EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate; +#endif diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c new file mode 100644 index 0000000000..b85784a926 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.c @@ -0,0 +1,542 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Option.c + +Abstract: + routines to process MTFTP4 options + + +**/ + +#include "Mtftp4Impl.h" + +UINT8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = { + "blksize", + "timeout", + "tsize", + "multicast" +}; + + +/** + Go through the packet to fill the Options array with the start + addresses of each MTFTP option name/value pair. + + @param Packet The packet to check + @param PacketLen The packet's length + @param Count The size of the Options on input. The actual + options on output + @param Options The option array to fill in + + @retval EFI_INVALID_PARAMETER The packet is mal-formated + @retval EFI_BUFFER_TOO_SMALL The Options array is too small + @retval EFI_SUCCESS The packet has been parsed into the Options array. + +**/ +STATIC +EFI_STATUS +Mtftp4FillOptions ( + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *Count, + OUT EFI_MTFTP4_OPTION *Options OPTIONAL + ) +{ + UINT8 *Cur; + UINT8 *Last; + UINT8 Num; + UINT8 *Name; + UINT8 *Value; + + Num = 0; + Cur = (UINT8 *) Packet + MTFTP4_OPCODE_LEN; + Last = (UINT8 *) Packet + PacketLen - 1; + + // + // process option name and value pairs. The last byte is always zero + // + while (Cur < Last) { + Name = Cur; + + while (*Cur != 0) { + Cur++; + } + + if (Cur == Last) { + return EFI_INVALID_PARAMETER; + } + + Value = ++Cur; + + while (*Cur != 0) { + Cur++; + } + + Num++; + + if ((Options != NULL) && (Num <= *Count)) { + Options[Num - 1].OptionStr = Name; + Options[Num - 1].ValueStr = Value; + } + + Cur++; + } + + if ((*Count < Num) || (Options == NULL)) { + *Count = Num; + return EFI_BUFFER_TOO_SMALL; + } + + *Count = Num; + return EFI_SUCCESS; +} + + +/** + Allocate and fill in a array of Mtftp options from the Packet. It + first calls Mtftp4FillOption to get the option number, then allocate + the array, at last, call Mtftp4FillOption again to save the options. + + @param Packet The packet to parse + @param PacketLen The length of the packet + @param OptionCount The number of options in the packet + @param OptionList The point to get the option array. + + @retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a + well-formated OACK packet. + @retval EFI_SUCCESS The option array is build + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array + +**/ +EFI_STATUS +Mtftp4ExtractOptions ( + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL + ) +{ + EFI_STATUS Status; + + *OptionCount = 0; + + if (OptionList != NULL) { + *OptionList = NULL; + } + + if (NTOHS (Packet->OpCode) != EFI_MTFTP4_OPCODE_OACK) { + return EFI_INVALID_PARAMETER; + } + + if (PacketLen == MTFTP4_OPCODE_LEN) { + return EFI_SUCCESS; + } + + // + // The last byte must be zero to terminate the options + // + if (*((UINT8 *) Packet + PacketLen - 1) != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Get the number of options + // + Status = Mtftp4FillOptions (Packet, PacketLen, OptionCount, NULL); + + if ((Status == EFI_SUCCESS) || (Status != EFI_BUFFER_TOO_SMALL)) { + return Status; + } + + // + // Allocate memory for the options, then call Mtftp4FillOptions to + // fill it if caller want that. + // + if (OptionList == NULL) { + return EFI_SUCCESS; + } + + *OptionList = NetAllocatePool (*OptionCount * sizeof (EFI_MTFTP4_OPTION)); + + if (*OptionList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mtftp4FillOptions (Packet, PacketLen, OptionCount, *OptionList); + return EFI_SUCCESS; +} + + +/** + Check whether two ascii strings are equel, ignore the case. + + @param Str1 The first ascii string + @param Str2 The second ascii string + + @retval TRUE Two strings are equal when case is ignored. + @retval FALSE Two string are not equal. + +**/ +BOOLEAN +NetStringEqualNoCase ( + IN UINT8 *Str1, + IN UINT8 *Str2 + ) +{ + UINT8 Ch1; + UINT8 Ch2; + + ASSERT ((Str1 != NULL) && (Str2 != NULL)); + + for (; (*Str1 != '\0') && (*Str2 != '\0'); Str1++, Str2++) { + Ch1 = *Str1; + Ch2 = *Str2; + + // + // Convert them to lower case then compare two + // + if (('A' <= Ch1) && (Ch1 <= 'Z')) { + Ch1 += 'a' - 'A'; + } + + if (('A' <= Ch2) && (Ch2 <= 'Z')) { + Ch2 += 'a' - 'A'; + } + + if (Ch1 != Ch2) { + return FALSE; + } + } + + return (BOOLEAN) (*Str1 == *Str2); +} + + +/** + Convert a string to a UINT32 number. + + @param Str The string to convert from + + @return The number get from the string + +**/ +UINT32 +NetStringToU32 ( + IN UINT8 *Str + ) +{ + UINT32 Num; + + ASSERT (Str != NULL); + + Num = 0; + + for (; NET_IS_DIGIT (*Str); Str++) { + Num = Num * 10 + (*Str - '0'); + } + + return Num; +} + + +/** + Convert a string of the format "192.168.0.1" to an IP address. + + @param Str The string representation of IP + @param Ip The varible to get IP. + + @retval EFI_INVALID_PARAMETER The IP string is invalid. + @retval EFI_SUCCESS The IP is parsed into the Ip + +**/ +STATIC +EFI_STATUS +NetStringToIp ( + IN UINT8 *Str, + OUT IP4_ADDR *Ip + ) +{ + UINT32 Byte; + UINT32 Addr; + UINTN Index; + + *Ip = 0; + Addr = 0; + + for (Index = 0; Index < 4; Index++) { + if (!NET_IS_DIGIT (*Str)) { + return EFI_INVALID_PARAMETER; + } + + Byte = NetStringToU32 (Str); + + if (Byte > 255) { + return EFI_INVALID_PARAMETER; + } + + Addr = (Addr << 8) | Byte; + + // + // Skip all the digitals and check whether the sepeator is the dot + // + while (NET_IS_DIGIT (*Str)) { + Str++; + } + + if ((Index < 3) && (*Str != '.')) { + return EFI_INVALID_PARAMETER; + } + + Str++; + } + + *Ip = Addr; + + return EFI_SUCCESS; +} + + +/** + Parse the MTFTP multicast option. + + @param Value The Mtftp multicast value string + @param Option The option to save the info into. + + @retval EFI_INVALID_PARAMETER The multicast value string is invalid. + @retval EFI_SUCCESS The multicast value is parsed into the Option + +**/ +STATIC +EFI_STATUS +Mtftp4ExtractMcast ( + IN UINT8 *Value, + IN MTFTP4_OPTION *Option + ) +{ + EFI_STATUS Status; + UINT32 Num; + + // + // The multicast option is formated like "204.0.0.1,1857,1" + // The server can also omit the ip and port, use ",,1" + // + if (*Value == ',') { + Option->McastIp = 0; + } else { + Status = NetStringToIp (Value, &Option->McastIp); + + if (EFI_ERROR (Status)) { + return Status; + } + + while (*Value && (*Value != ',')) { + Value++; + } + } + + if (*Value != ',') { + return EFI_INVALID_PARAMETER; + } + + Value++; + + // + // Convert the port setting. the server can send us a port number or + // empty string. such as the port in ",,1" + // + if (*Value == ',') { + Option->McastPort = 0; + } else { + Num = NetStringToU32 (Value); + + if (Num > 65535) { + return EFI_INVALID_PARAMETER; + } + + Option->McastPort = (UINT16)Num; + + while (NET_IS_DIGIT (*Value)) { + Value++; + } + } + + if (*Value != ',') { + return EFI_INVALID_PARAMETER; + } + + Value++; + + // + // Check the master/slave setting, 1 for master, 0 for slave. + // + Num = NetStringToU32 (Value); + + if ((Num != 0) && (Num != 1)) { + return EFI_INVALID_PARAMETER; + } + + Option->Master = (BOOLEAN)(Num == 1); + + while (NET_IS_DIGIT (*Value)) { + Value++; + } + + if (*Value != '\0') { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +/** + Parse the option in Options array to MTFTP4_OPTION which program + can access directly. + + @param Options The option array, which contains addresses of each + option's name/value string. + @param Count The number of options in the Options + @param Request Whether this is a request or OACK. The format of + multicast is different according to this setting. + @param MtftpOption The MTFTP4_OPTION for easy access. + + @retval EFI_INVALID_PARAMETER The option is mal-formated + @retval EFI_UNSUPPORTED Some option isn't supported + @retval EFI_SUCCESS The option are OK and has been parsed. + +**/ +EFI_STATUS +Mtftp4ParseOption ( + IN EFI_MTFTP4_OPTION *Options, + IN UINT32 Count, + IN BOOLEAN Request, + OUT MTFTP4_OPTION *MtftpOption + ) +{ + EFI_STATUS Status; + UINT32 Index; + UINT32 Value; + EFI_MTFTP4_OPTION *This; + + MtftpOption->Exist = 0; + + for (Index = 0; Index < Count; Index++) { + This = Options + Index; + + if ((This->OptionStr == NULL) || (This->ValueStr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (NetStringEqualNoCase (This->OptionStr, "blksize")) { + // + // block size option, valid value is between [8, 65464] + // + Value = NetStringToU32 (This->ValueStr); + + if ((Value < 8) || (Value > 65464)) { + return EFI_INVALID_PARAMETER; + } + + MtftpOption->BlkSize = (UINT16) Value; + MtftpOption->Exist |= MTFTP4_BLKSIZE_EXIST; + + } else if (NetStringEqualNoCase (This->OptionStr, "timeout")) { + // + // timeout option, valid value is between [1, 255] + // + Value = NetStringToU32 (This->ValueStr); + + if ((Value < 1) || (Value > 255)) { + return EFI_INVALID_PARAMETER; + } + + MtftpOption->Timeout = (UINT8) Value; + + } else if (NetStringEqualNoCase (This->OptionStr, "tsize")) { + // + // tsize option, the biggest transfer supported is 4GB with block size option + // + MtftpOption->Tsize = NetStringToU32 (This->ValueStr); + MtftpOption->Exist |= MTFTP4_TSIZE_EXIST; + + } else if (NetStringEqualNoCase (This->OptionStr, "multicast")) { + // + // Multicast option, if it is a request, the value must be a zero + // length string, otherwise, it is formated like "204.0.0.1,1857,1\0" + // + if (Request) { + if (*(This->ValueStr) != '\0') { + return EFI_INVALID_PARAMETER; + } + + } else { + Status = Mtftp4ExtractMcast (This->ValueStr, MtftpOption); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + MtftpOption->Exist |= MTFTP4_MCAST_EXIST; + + } else if (Request) { + // + // Ignore the unsupported option if it is a reply, and return + // EFI_UNSUPPORTED if it's a request according to the UEFI spec. + // + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + + +/** + Parse the options in the OACK packet to MTFTP4_OPTION which program + can access directly. + + @param Packet The OACK packet to parse + @param PacketLen The length of the packet + @param MtftpOption The MTFTP_OPTION for easy access. + + @retval EFI_INVALID_PARAMETER The packet option is mal-formated + @retval EFI_UNSUPPORTED Some option isn't supported + @retval EFI_SUCCESS The option are OK and has been parsed. + +**/ +EFI_STATUS +Mtftp4ParseOptionOack ( + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 PacketLen, + OUT MTFTP4_OPTION *MtftpOption + ) +{ + EFI_MTFTP4_OPTION *OptionList; + EFI_STATUS Status; + UINT32 Count; + + MtftpOption->Exist = 0; + + Status = Mtftp4ExtractOptions (Packet, PacketLen, &Count, &OptionList); + + if (EFI_ERROR (Status) || (Count == 0)) { + return Status; + } + + Status = Mtftp4ParseOption (OptionList, Count, FALSE, MtftpOption); + + NetFreePool (OptionList); + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h new file mode 100644 index 0000000000..a136304df4 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Option.h @@ -0,0 +1,73 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Option.h + +Abstract: + + Mtftp4 option process routines. + + +**/ + +#ifndef __EFI_MTFTP4_OPTION_H__ +#define __EFI_MTFTP4_OPTION_H__ + +enum { + MTFTP4_SUPPORTED_OPTIONS = 4, + MTFTP4_OPCODE_LEN = 2, + MTFTP4_ERRCODE_LEN = 2, + MTFTP4_BLKNO_LEN = 2, + MTFTP4_DATA_HEAD_LEN = 4, + + MTFTP4_BLKSIZE_EXIST = 0x01, + MTFTP4_TIMEOUT_EXIST = 0x02, + MTFTP4_TSIZE_EXIST = 0x04, + MTFTP4_MCAST_EXIST = 0x08, +}; + +typedef struct { + UINT16 BlkSize; + UINT8 Timeout; + UINT32 Tsize; + IP4_ADDR McastIp; + UINT16 McastPort; + BOOLEAN Master; + UINT32 Exist; +} MTFTP4_OPTION; + +EFI_STATUS +Mtftp4ExtractOptions ( + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL + ); + +EFI_STATUS +Mtftp4ParseOption ( + IN EFI_MTFTP4_OPTION *OptionList, + IN UINT32 Count, + IN BOOLEAN Request, + OUT MTFTP4_OPTION *Option + ); + +EFI_STATUS +Mtftp4ParseOptionOack ( + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 PacketLen, + OUT MTFTP4_OPTION *Option + ); + +extern UINT8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS]; +#endif diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c new file mode 100644 index 0000000000..665114c7f8 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c @@ -0,0 +1,735 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Rrq.c + +Abstract: + + Routines to process Rrq (download) + + +**/ + +#include "Mtftp4Impl.h" + +VOID +Mtftp4RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_POINTS *Points, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the MTFTP session to download. It will first initialize some + of the internal states then build and send a RRQ reqeuest packet, at + last, it will start receive for the downloading. + + @param Instance The Mtftp session + @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ + or EFI_MTFTP4_OPCODE_DIR. + + @retval EFI_SUCCESS The mtftp download session is started. + @retval Others Failed to start downloading. + +**/ +EFI_STATUS +Mtftp4RrqStart ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 Operation + ) +{ + EFI_STATUS Status; + + // + // The valid block number range are [1, 0xffff]. For example: + // the client sends an RRQ request to the server, the server + // transfers the DATA1 block. If option negoitation is ongoing, + // the server will send back an OACK, then client will send ACK0. + // + Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp4SendRequest (Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0); +} + + +/** + Build and send a ACK packet for the download session. + + @param Instance The Mtftp session + @param BlkNo The BlkNo to ack. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet + @retval EFI_SUCCESS The ACK has been sent + @retval Others Failed to send the ACK. + +**/ +EFI_STATUS +Mtftp4RrqSendAck ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 BlkNo + ) +{ + EFI_MTFTP4_PACKET *Ack; + NET_BUF *Packet; + + Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER)); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ack = (EFI_MTFTP4_PACKET *) NetbufAllocSpace ( + Packet, + sizeof (EFI_MTFTP4_ACK_HEADER), + FALSE + ); + + Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK); + Ack->Ack.Block[0] = HTONS (BlkNo); + + return Mtftp4SendPacket (Instance, Packet); +} + + +/** + Deliver the received data block to the user, which can be saved + in the user provide buffer or through the CheckPacket callback. + + @param Instance The Mtftp session + @param Packet The received data packet + @param Len The packet length + + @retval EFI_SUCCESS The data is saved successfully + @retval EFI_ABORTED The user tells to abort by return an error through + CheckPacket + @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is + updated to the actual buffer size needed. + +**/ +EFI_STATUS +Mtftp4RrqSaveBlock ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 Len + ) +{ + EFI_MTFTP4_TOKEN *Token; + EFI_STATUS Status; + UINT16 Block; + UINT64 Start; + UINT32 DataLen; + + Token = Instance->Token; + Block = NTOHS (Packet->Data.Block); + DataLen = Len - MTFTP4_DATA_HEAD_LEN; + + // + // This is the last block, save the block no + // + if (DataLen < Instance->BlkSize) { + Instance->LastBlock = Block; + Mtftp4SetLastBlockNum (&Instance->Blocks, Block); + } + + // + // Remove this block number from the file hole. If Mtftp4RemoveBlockNum + // returns EFI_NOT_FOUND, the block has been saved, don't save it again. + // + Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block); + + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else if (EFI_ERROR (Status)) { + return Status; + } + + if (Token->CheckPacket != NULL) { + Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet); + + if (EFI_ERROR (Status)) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, + "User aborted download" + ); + + return EFI_ABORTED; + } + } + + if (Token->Buffer != NULL) { + Start = MultU64x32 (Block - 1, Instance->BlkSize); + + if (Start + DataLen <= Token->BufferSize) { + NetCopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen); + + // + // Update the file size when received the last block + // + if (Instance->LastBlock == Block) { + Token->BufferSize = Start + DataLen; + } + + } else if (Instance->LastBlock != 0) { + // + // Don't save the data if the buffer is too small, return + // EFI_BUFFER_TOO_SMALL if received the last packet. This + // will give a accurate file length. + // + Token->BufferSize = Start + DataLen; + + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_DISK_FULL, + "User provided memory block is too small" + ); + + return EFI_BUFFER_TOO_SMALL; + } + } + + return EFI_SUCCESS; +} + + +/** + Function to process the received data packets. It will save the block + then send back an ACK if it is active. + + @param Instance The downloading MTFTP session + @param Packet The packet received + @param Len The length of the packet + @param Multicast Whether this packet is multicast or unicast + @param Completed Return whether the download has completed + + @retval EFI_SUCCESS The data packet is successfully processed + @retval EFI_ABORTED The download is aborted by the user + @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small + +**/ +EFI_STATUS +Mtftp4RrqHandleData ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 Len, + IN BOOLEAN Multicast, + OUT BOOLEAN *Completed + ) +{ + EFI_STATUS Status; + UINT16 BlockNum; + INTN Expected; + + *Completed = FALSE; + BlockNum = NTOHS (Packet->Data.Block); + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + ASSERT (Expected >= 0); + + // + // If we are active and received an unexpected packet, retransmit + // the last ACK then restart receiving. If we are passive, save + // the block. + // + if (Instance->Master && (Expected != BlockNum)) { + Mtftp4Retransmit (Instance); + return EFI_SUCCESS; + } + + Status = Mtftp4RrqSaveBlock (Instance, Packet, Len); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Reset the passive client's timer whenever it received a + // valid data packet. + // + if (!Instance->Master) { + Mtftp4SetTimeout (Instance); + } + + // + // Check whether we have received all the blocks. Send the ACK if we + // are active (unicast client or master client for multicast download). + // If we have received all the blocks, send an ACK even if we are passive + // to tell the server that we are done. + // + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + if (Instance->Master || (Expected < 0)) { + if (Expected < 0) { + // + // If we are passive client, then the just received Block maybe + // isn't the last block. We need to send an ACK to the last block + // to inform the server that we are done. If we are active client, + // the Block == Instance->LastBlock. + // + BlockNum = Instance->LastBlock; + *Completed = TRUE; + + } else { + BlockNum = (UINT16) (Expected - 1); + } + + Mtftp4RrqSendAck (Instance, BlockNum); + } + + return EFI_SUCCESS; +} + + +/** + Validate whether the options received in the server's OACK packet is valid. + The options are valid only if: + 1. The server doesn't include options not requested by us + 2. The server can only use smaller blksize than that is requested + 3. The server can only use the same timeout as requested + 4. The server doesn't change its multicast channel. + + @param This The downloading Mtftp session + @param Reply The options in the OACK packet + @param Request The requested options + + @return TRUE if the options in the OACK is OK, otherwise FALSE. + +**/ +BOOLEAN +Mtftp4RrqOackValid ( + IN MTFTP4_PROTOCOL *This, + IN MTFTP4_OPTION *Reply, + IN MTFTP4_OPTION *Request + ) +{ + + // + // It is invalid for server to return options we don't request + // + if ((Reply->Exist &~Request->Exist) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if (((Reply->Exist & MTFTP4_BLKSIZE_EXIST) && (Reply->BlkSize > Request->BlkSize)) || + ((Reply->Exist & MTFTP4_TIMEOUT_EXIST) && (Reply->Timeout != Request->Timeout))) { + return FALSE; + } + + // + // The server can send ",,master" to client to change its master + // setting. But if it use the specific multicast channel, it can't + // change the setting. + // + if ((Reply->Exist & MTFTP4_MCAST_EXIST) && (This->McastIp != 0)) { + if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) { + return FALSE; + } + + if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) { + return FALSE; + } + } + + return TRUE; +} + + +/** + Configure a UDP IO port to receive the multicast. + + @param McastIo The UDP IO port to configure + @param Context The opaque parameter to the function which is the + MTFTP session. + + @retval EFI_SUCCESS The udp child is successfully configured. + @retval Others Failed to configure the UDP child. + +**/ +STATIC +EFI_STATUS +Mtftp4RrqConfigMcastPort ( + IN UDP_IO_PORT *McastIo, + IN VOID *Context + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_MTFTP4_CONFIG_DATA *Config; + EFI_UDP4_CONFIG_DATA UdpConfig; + EFI_IPv4_ADDRESS Group; + EFI_STATUS Status; + IP4_ADDR Ip; + + Instance = (MTFTP4_PROTOCOL *) Context; + Config = &Instance->Config; + + UdpConfig.AcceptBroadcast = FALSE; + UdpConfig.AcceptPromiscuous = FALSE; + UdpConfig.AcceptAnyPort = FALSE; + UdpConfig.AllowDuplicatePort = FALSE; + UdpConfig.TypeOfService = 0; + UdpConfig.TimeToLive = 64; + UdpConfig.DoNotFragment = FALSE; + UdpConfig.ReceiveTimeout = 0; + UdpConfig.TransmitTimeout = 0; + UdpConfig.UseDefaultAddress = Config->UseDefaultSetting; + UdpConfig.StationAddress = Config->StationIp; + UdpConfig.SubnetMask = Config->SubnetMask; + UdpConfig.StationPort = Instance->McastPort; + UdpConfig.RemotePort = 0; + + Ip = HTONL (Instance->ServerIp); + NetCopyMem (&UdpConfig.RemoteAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Status = McastIo->Udp->Configure (McastIo->Udp, &UdpConfig); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // join the multicast group + // + Ip = HTONL (Instance->McastIp); + NetCopyMem (&Group, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + return McastIo->Udp->Groups (McastIo->Udp, TRUE, &Group); +} + + +/** + Function to process the OACK. It will first validate the OACK + packet, then update the various negotiated parameters. + + @param Instance The download MTFTP session + @param Packet The packet received + @param Len The packet length + @param Multicast Whether this packet is received as a multicast + @param Completed Returns whether the download has completed. NOT + used by this function. + + @retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child + @retval EFI_TFTP_ERROR Some error happened during the process + @retval EFI_SUCCESS The OACK is successfully processed. + +**/ +EFI_STATUS +Mtftp4RrqHandleOack ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 Len, + IN BOOLEAN Multicast, + OUT BOOLEAN *Completed + ) +{ + MTFTP4_OPTION Reply; + EFI_STATUS Status; + INTN Expected; + + *Completed = FALSE; + + // + // If already started the master download, don't change the + // setting. Master download always succeeds. + // + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + ASSERT (Expected != -1); + + if (Instance->Master && (Expected != 1)) { + return EFI_SUCCESS; + } + + // + // Parse and validate the options from server + // + NetZeroMem (&Reply, sizeof (MTFTP4_OPTION)); + + Status = Mtftp4ParseOptionOack (Packet, Len, &Reply); + + if (EFI_ERROR (Status) || + !Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) { + // + // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES. + // + if (Status != EFI_OUT_OF_RESOURCES) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, + "Mal-formated OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if (Reply.Exist & MTFTP4_MCAST_EXIST) { + + // + // Save the multicast info. Always update the Master, only update the + // multicast IP address, block size, timeoute at the first time. If IP + // address is updated, create a UDP child to receive the multicast. + // + Instance->Master = Reply.Master; + + if (Instance->McastIp == 0) { + if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, + "Illegal multicast setting" + ); + + return EFI_TFTP_ERROR; + } + + // + // Create a UDP child then start receive the multicast from it. + // + Instance->McastIp = Reply.McastIp; + Instance->McastPort = Reply.McastPort; + Instance->McastUdpPort = UdpIoCreatePort ( + Instance->Service->Controller, + Instance->Service->Image, + Mtftp4RrqConfigMcastPort, + Instance + ); + + if (Instance->McastUdpPort == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0); + + if (EFI_ERROR (Status)) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION, + "Failed to create socket to receive multicast packet" + ); + + return Status; + } + + // + // Update the parameters used. + // + if (Reply.BlkSize != 0) { + Instance->BlkSize = Reply.BlkSize; + } + + if (Reply.Timeout != 0) { + Instance->Timeout = Reply.Timeout; + } + } + + } else { + Instance->Master = TRUE; + + if (Reply.BlkSize != 0) { + Instance->BlkSize = Reply.BlkSize; + } + + if (Reply.Timeout != 0) { + Instance->Timeout = Reply.Timeout; + } + } + + // + // Send an ACK to (Expected - 1) which is 0 for unicast download, + // or tell the server we want to receive the Expected block. + // + return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1)); +} + + +/** + The packet process callback for MTFTP download. + + @param UdpPacket The packet received + @param Points The local/remote access point of the packet + @param IoStatus The status of the receiving + @param Context Opaque parameter, which is the MTFTP session + + @return None + +**/ +VOID +Mtftp4RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_POINTS *Points, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_MTFTP4_PACKET *Packet; + BOOLEAN Completed; + BOOLEAN Multicast; + EFI_STATUS Status; + UINT16 Opcode; + UINT32 Len; + + Instance = (MTFTP4_PROTOCOL *) Context; + NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE); + + Status = EFI_SUCCESS; + Packet = NULL; + Completed = FALSE; + Multicast = FALSE; + + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + // + // Find the port this packet is from to restart receive correctly. + // + Multicast = (BOOLEAN) (Points->LocalAddr == Instance->McastIp); + + if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Client send initial request to server's listening port. Server + // will select a UDP port to communicate with the client. The server + // is required to use the same port as RemotePort to multicast the + // data. + // + if (Points->RemotePort != Instance->ConnectedPort) { + if (Instance->ConnectedPort != 0) { + goto ON_EXIT; + } else { + Instance->ConnectedPort = Points->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + + if (UdpPacket->BlockOpNum > 1) { + Packet = NetAllocatePool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Call the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if ((Instance->Token->CheckPacket != NULL) && + ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp4, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP4_OPCODE_ERROR) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + switch (Opcode) { + case EFI_MTFTP4_OPCODE_DATA: + if ((Len > (UINT32) (MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) || + (Len < (UINT32) MTFTP4_DATA_HEAD_LEN)) { + goto ON_EXIT; + } + + Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed); + break; + + case EFI_MTFTP4_OPCODE_OACK: + if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) { + goto ON_EXIT; + } + + Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed); + break; + + case EFI_MTFTP4_OPCODE_ERROR: + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + + // + // Free the resources, then if !EFI_ERROR (Status), restart the + // receive, otherwise end the session. + // + if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) { + NetFreePool (Packet); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + if (!EFI_ERROR (Status) && !Completed) { + if (Multicast) { + Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0); + } else { + Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0); + } + } + + if (EFI_ERROR (Status) || Completed) { + Mtftp4CleanOperation (Instance, Status); + } +} diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c new file mode 100644 index 0000000000..af61c0bf69 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c @@ -0,0 +1,591 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Support.c + +Abstract: + + Support routines for Mtftp + + +**/ + +#include "Mtftp4Impl.h" + + +/** + Allocate a MTFTP4 block range, then init it to the + range of [Start, End] + + @param Start The start block number + @param End The last block number in the range + + @return NULL if failed to allocate memory, otherwise the created block range. + +**/ +STATIC +MTFTP4_BLOCK_RANGE * +Mtftp4AllocateRange ( + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP4_BLOCK_RANGE *Range; + + Range = NetAllocatePool (sizeof (MTFTP4_BLOCK_RANGE)); + + if (Range == NULL) { + return NULL; + } + + NetListInit (&Range->Link); + Range->Start = Start; + Range->End = End; + + return Range; +} + + +/** + Initialize the block range for either RRQ or WRQ. RRQ and WRQ have + different requirements for Start and End. For example, during start + up, WRQ initializes its whole valid block range to [0, 0xffff]. This + is bacause the server will send us a ACK0 to inform us to start the + upload. When the client received ACK0, it will remove 0 from the range, + get the next block number, which is 1, then upload the BLOCK1. For RRQ + without option negotiation, the server will directly send us the BLOCK1 + in response to the client's RRQ. When received BLOCK1, the client will + remove it from the block range and send an ACK. It also works if there + is option negotiation. + + @param Head The block range head to initialize + @param Start The Start block number. + @param End The last block number. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range + @retval EFI_SUCCESS The initial block range is created. + +**/ +EFI_STATUS +Mtftp4InitBlockRange ( + IN NET_LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP4_BLOCK_RANGE *Range; + + Range = Mtftp4AllocateRange (Start, End); + + if (Range == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NetListInsertTail (Head, &Range->Link); + return EFI_SUCCESS; +} + + +/** + Get the first valid block number on the range list. + + @param Head The block range head + + @return -1: if the block range is empty. Otherwise the first valid block number. + +**/ +INTN +Mtftp4GetNextBlockNum ( + IN NET_LIST_ENTRY *Head + ) +{ + MTFTP4_BLOCK_RANGE *Range; + + if (NetListIsEmpty (Head)) { + return -1; + } + + Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link); + return Range->Start; +} + + +/** + Set the last block number of the block range list. It will + remove all the blocks after the Last. MTFTP initialize the + block range to the maximum possible range, such as [0, 0xffff] + for WRQ. When it gets the last block number, it will call + this function to set the last block number. + + @param Head The block range list + @param Last The last block number + + @return None + +**/ +VOID +Mtftp4SetLastBlockNum ( + IN NET_LIST_ENTRY *Head, + IN UINT16 Last + ) +{ + MTFTP4_BLOCK_RANGE *Range; + + // + // Iterate from the tail to head to remove the block number + // after the last. + // + while (!NetListIsEmpty (Head)) { + Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link); + + if (Range->Start > Last) { + NetListRemoveEntry (&Range->Link); + NetFreePool (Range); + continue; + } + + if (Range->End > Last) { + Range->End = Last; + } + + return ; + } +} + + +/** + Remove the block number from the block range list. + + @param Head The block range list to remove from + @param Num The block number to remove + + @retval EFI_NOT_FOUND The block number isn't in the block range list + @retval EFI_SUCCESS The block number has been removed from the list + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource + +**/ +EFI_STATUS +Mtftp4RemoveBlockNum ( + IN NET_LIST_ENTRY *Head, + IN UINT16 Num + ) +{ + MTFTP4_BLOCK_RANGE *Range; + MTFTP4_BLOCK_RANGE *NewRange; + NET_LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, Head) { + + // + // Each block represents a hole [Start, End] in the file, + // skip to the first range with End >= Num + // + Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link); + + if (Range->End < Num) { + continue; + } + + // + // There are three different cases for Start + // 1. (Start > Num) && (End >= Num): + // because all the holes before this one has the condition of + // End < Num, so this block number has been removed. + // + // 2. (Start == Num) && (End >= Num): + // Need to increase the Start by one, and if End == Num, this + // hole has been removed completely, remove it. + // + // 3. (Start < Num) && (End >= Num): + // if End == Num, only need to decrease the End by one because + // we have (Start < Num) && (Num == End), so (Start <= End - 1). + // if (End > Num), the hold is splited into two holes, with + // [Start, Num - 1] and [Num + 1, End]. + // + if (Range->Start > Num) { + return EFI_NOT_FOUND; + + } else if (Range->Start == Num) { + Range->Start++; + + if (Range->Start > Range->End) { + NetListRemoveEntry (&Range->Link); + NetFreePool (Range); + } + + return EFI_SUCCESS; + + } else { + if (Range->End == Num) { + Range->End--; + } else { + NewRange = Mtftp4AllocateRange (Num + 1, (UINT16) Range->End); + + if (NewRange == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Range->End = Num - 1; + NetListInsertAfter (&Range->Link, &NewRange->Link); + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Build then transmit the request packet for the MTFTP session. + + @param Instance The Mtftp session + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request + @retval EFI_SUCCESS The request is built and sent + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp4SendRequest ( + IN MTFTP4_PROTOCOL *Instance + ) +{ + EFI_MTFTP4_PACKET *Packet; + EFI_MTFTP4_OPTION *Options; + EFI_MTFTP4_TOKEN *Token; + NET_BUF *Nbuf; + UINT8 *Mode; + UINT8 *Cur; + UINT32 Len; + UINTN Index; + + Token = Instance->Token; + Options = Token->OptionList; + Mode = Instance->Token->ModeStr; + + if (Mode == NULL) { + Mode = "octet"; + } + + // + // Compute the packet length + // + Len = (UINT32) (AsciiStrLen (Token->Filename) + AsciiStrLen (Mode) + 4); + + for (Index = 0; Index < Token->OptionCount; Index++) { + Len += (UINT32) (AsciiStrLen (Options[Index].OptionStr) + + AsciiStrLen (Options[Index].ValueStr) + 2); + } + + // + // Allocate a packet then copy the data over + // + if ((Nbuf = NetbufAlloc (Len)) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE); + Packet->OpCode = HTONS (Instance->Operation); + Cur = Packet->Rrq.Filename; + Cur = AsciiStrCpy (Cur, Token->Filename); + Cur = AsciiStrCpy (Cur, Mode); + + for (Index = 0; Index < Token->OptionCount; ++Index) { + Cur = AsciiStrCpy (Cur, Options[Index].OptionStr); + Cur = AsciiStrCpy (Cur, Options[Index].ValueStr); + } + + return Mtftp4SendPacket (Instance, Nbuf); +} + + +/** + Build then send an error message + + @param Instance The MTFTP session + @param ErrInfo The error code and message + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet + @retval EFI_SUCCESS The error packet is transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp4SendError ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ) +{ + NET_BUF *Packet; + EFI_MTFTP4_PACKET *TftpError; + UINT32 Len; + + Len = (UINT32) (AsciiStrLen (ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER)); + Packet = NetbufAlloc (Len); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE); + TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR); + TftpError->Error.ErrorCode = HTONS (ErrCode); + + AsciiStrCpy (TftpError->Error.ErrorMessage, ErrInfo); + + return Mtftp4SendPacket (Instance, Packet); +} + + +/** + The callback function called when the packet is transmitted. + It simply frees the packet. + + @param Packet The transmitted (or failed to) packet + @param Points The local and remote UDP access point + @param IoStatus The result of the transmission + @param Context Opaque parameter to the callback + + @return None + +**/ +STATIC +VOID +Mtftp4OnPacketSent ( + NET_BUF *Packet, + UDP_POINTS *Points, + EFI_STATUS IoStatus, + VOID *Context + ) +{ + NetbufFree (Packet); +} + + +/** + Set the timeout for the instance. User a longer time for + passive instances. + + @param Instance The Mtftp session to set time out + + @return None + +**/ +VOID +Mtftp4SetTimeout ( + IN MTFTP4_PROTOCOL *Instance + ) +{ + if (Instance->Master) { + Instance->PacketToLive = Instance->Timeout; + } else { + Instance->PacketToLive = Instance->Timeout * 2; + } +} + + +/** + Send the packet for the instance. It will first save a reference to + the packet for later retransmission. then determine the destination + port, listen port for requests, and connected port for others. At last, + send the packet out. + + @param Instance The Mtftp instance + @param Packet The packet to send + + @retval EFI_SUCCESS The packet is sent out + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp4SendPacket ( + IN MTFTP4_PROTOCOL *Instance, + IN NET_BUF *Packet + ) +{ + UDP_POINTS UdpPoint; + EFI_STATUS Status; + UINT16 OpCode; + + // + // Save the packet for retransmission + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + Instance->LastPacket = Packet; + + Instance->CurRetry = 0; + Mtftp4SetTimeout (Instance); + + UdpPoint.LocalAddr = 0; + UdpPoint.LocalPort = 0; + UdpPoint.RemoteAddr = Instance->ServerIp; + + // + // Send the requests to the listening port, other packets + // to the connected port + // + OpCode = NTOHS (*((UINT16 *) NetbufGetByte (Packet, 0, NULL))); + + if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) || + (OpCode == EFI_MTFTP4_OPCODE_WRQ)) { + UdpPoint.RemotePort = Instance->ListeningPort; + } else { + UdpPoint.RemotePort = Instance->ConnectedPort; + } + + NET_GET_REF (Packet); + + Status = UdpIoSendDatagram ( + Instance->UnicastPort, + Packet, + &UdpPoint, + Instance->Gateway, + Mtftp4OnPacketSent, + Instance + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + } + + return Status; +} + + +/** + Retransmit the last packet for the instance + + @param Instance The Mtftp instance + + @retval EFI_SUCCESS The last packet is retransmitted. + @retval Others Failed to retransmit. + +**/ +EFI_STATUS +Mtftp4Retransmit ( + IN MTFTP4_PROTOCOL *Instance + ) +{ + UDP_POINTS UdpPoint; + EFI_STATUS Status; + UINT16 OpCode; + + ASSERT (Instance->LastPacket != NULL); + + UdpPoint.LocalAddr = 0; + UdpPoint.LocalPort = 0; + UdpPoint.RemoteAddr = Instance->ServerIp; + + // + // Set the requests to the listening port, other packets to the connected port + // + OpCode = NTOHS (*(UINT16 *) NetbufGetByte (Instance->LastPacket, 0, NULL)); + + if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) || + (OpCode == EFI_MTFTP4_OPCODE_WRQ)) { + UdpPoint.RemotePort = Instance->ListeningPort; + } else { + UdpPoint.RemotePort = Instance->ConnectedPort; + } + + NET_GET_REF (Instance->LastPacket); + + Status = UdpIoSendDatagram ( + Instance->UnicastPort, + Instance->LastPacket, + &UdpPoint, + Instance->Gateway, + Mtftp4OnPacketSent, + Instance + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Instance->LastPacket); + } + + return Status; +} + + +/** + The timer ticking function for the Mtftp service instance. + + @param Event The ticking event + @param Context The Mtftp service instance + + @return None + +**/ +VOID +EFIAPI +Mtftp4OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MTFTP4_SERVICE *MtftpSb; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + MTFTP4_PROTOCOL *Instance; + EFI_MTFTP4_TOKEN *Token; + + MtftpSb = (MTFTP4_SERVICE *) Context; + + // + // Iterate through all the children of the Mtftp service instance. Time + // out the packet. If maximum retries reached, clean the session up. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) { + Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link); + + if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) { + continue; + } + + // + // Call the user's time out handler + // + Token = Instance->Token; + + if ((Token->TimeoutCallback != NULL) && + EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) { + + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + "User aborted the transfer in time out" + ); + + Mtftp4CleanOperation (Instance, EFI_ABORTED); + continue; + } + + // + // Retransmit the packet if haven't reach the maxmium retry count, + // otherwise exit the transfer. + // + if (++Instance->CurRetry < Instance->MaxRetry) { + Mtftp4Retransmit (Instance); + Mtftp4SetTimeout (Instance); + } else { + Mtftp4CleanOperation (Instance, EFI_TIMEOUT); + continue; + } + } +} diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h new file mode 100644 index 0000000000..be186866c5 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.h @@ -0,0 +1,96 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Support.h + +Abstract: + + Support routines for MTFTP + + +**/ + +#ifndef __EFI_MTFTP4_SUPPORT_H__ +#define __EFI_MTFTP4_SUPPORT_H__ + +// +// The structure representing a range of block numbers, [Start, End]. +// It is used to remember the holes in the MTFTP block space. If all +// the holes are filled in, then the download or upload has completed. +// +typedef struct { + NET_LIST_ENTRY Link; + INTN Start; + INTN End; +} MTFTP4_BLOCK_RANGE; + + +EFI_STATUS +Mtftp4InitBlockRange ( + IN NET_LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ); + +INTN +Mtftp4GetNextBlockNum ( + IN NET_LIST_ENTRY *Head + ); + +VOID +Mtftp4SetLastBlockNum ( + IN NET_LIST_ENTRY *Head, + IN UINT16 Last + ); + +EFI_STATUS +Mtftp4RemoveBlockNum ( + IN NET_LIST_ENTRY *Head, + IN UINT16 Num + ); + +VOID +Mtftp4SetTimeout ( + IN MTFTP4_PROTOCOL *Instance + ); + +EFI_STATUS +Mtftp4SendPacket ( + IN MTFTP4_PROTOCOL *Instance, + IN NET_BUF *Packet + ); + +EFI_STATUS +Mtftp4SendRequest ( + IN MTFTP4_PROTOCOL *Instance + ); + +EFI_STATUS +Mtftp4SendError ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ); + +EFI_STATUS +Mtftp4Retransmit ( + IN MTFTP4_PROTOCOL *Instance + ); + +VOID +EFIAPI +Mtftp4OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ); +#endif diff --git a/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c new file mode 100644 index 0000000000..5ac5dc8ec0 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c @@ -0,0 +1,522 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Mtftp4Wrq.c + +Abstract: + + Routines to process Wrq (upload) + + +**/ + +#include "Mtftp4Impl.h" + +VOID +Mtftp4WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_POINTS *Points, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the MTFTP session for pload. It will first init some states, + then send the WRQ request packet, and start receiving the packet. + + @param Instance The MTFTP session + @param Operation Redundant parameter, which is always + EFI_MTFTP4_OPCODE_WRQ here. + + @retval EFI_SUCCESS The upload process has been started. + @retval Others Failed to start the upload. + +**/ +EFI_STATUS +Mtftp4WrqStart ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 Operation + ) +{ + EFI_STATUS Status; + + // + // The valid block number range are [0, 0xffff]. For example: + // the client sends an WRQ request to the server, the server + // ACK with an ACK0 to let client start transfer the first + // packet. + // + Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp4SendRequest (Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0); +} + + +/** + Build then send a MTFTP data packet for the MTFTP upload session. + + @param Instance The MTFTP upload session + @param BlockNum The block number to send + + @retval EFI_OUT_OF_RESOURCES Failed to build the packet + @retval EFI_ABORTED The consumer of this child directs to abort the + transmission by return an error through + PacketNeeded + @retval EFI_SUCCESS The data is sent. + +**/ +EFI_STATUS +Mtftp4WrqSendBlock ( + IN MTFTP4_PROTOCOL *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP4_PACKET *Packet; + EFI_MTFTP4_TOKEN *Token; + NET_BUF *UdpPacket; + EFI_STATUS Status; + UINT16 DataLen; + UINT8 *DataBuf; + UINT64 Start; + + // + // Allocate a buffer to hold the user data + // + UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN); + + if (UdpPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet = (EFI_MTFTP4_PACKET *)NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE); + + Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA); + Packet->Data.Block = HTONS (BlockNum); + + // + // Read the block from either the buffer or PacketNeeded callback + // + Token = Instance->Token; + DataLen = Instance->BlkSize; + + if (Token->Buffer != NULL) { + Start = MultU64x32 (BlockNum - 1, Instance->BlkSize); + + if (Token->BufferSize < Start + Instance->BlkSize) { + DataLen = (UINT16) (Token->BufferSize - Start); + Instance->LastBlock = BlockNum; + Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + NetCopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen); + } + + } else { + // + // Get data from PacketNeeded + // + DataBuf = NULL; + Status = Token->PacketNeeded (&Instance->Mtftp4, Token, &DataLen, &DataBuf); + + if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { + if (DataBuf != NULL) { + gBS->FreePool (DataBuf); + } + + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + "User aborted the transfer" + ); + + return EFI_ABORTED; + } + + if (DataLen < Instance->BlkSize) { + Instance->LastBlock = BlockNum; + Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + NetCopyMem (Packet->Data.Data, DataBuf, DataLen); + gBS->FreePool (DataBuf); + } + } + + return Mtftp4SendPacket (Instance, UdpPacket); +} + + +/** + Function to handle received ACK packet. If the ACK number matches the + expected block number, and there are more data pending, send the next + block. Otherwise tell the caller that we are done. + + @param Instance The MTFTP upload session + @param Packet The MTFTP packet received + @param Len The packet length + @param Completed Return whether the upload has finished. + + @retval EFI_SUCCESS The ACK is successfully processed. + @retval EFI_TFTP_ERROR The block number loops back. + @retval Others Failed to transmit the next data packet. + +**/ +EFI_STATUS +Mtftp4WrqHandleAck ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 Len, + OUT BOOLEAN *Completed + ) +{ + UINT16 AckNum; + INTN Expected; + + *Completed = FALSE; + AckNum = NTOHS (Packet->Ack.Block[0]); + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + ASSERT (Expected >= 0); + + // + // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput + // restart receive. + // + if (Expected != AckNum) { + return EFI_SUCCESS; + } + + // + // Remove the acked block number, if this is the last block number, + // tell the Mtftp4WrqInput to finish the transfer. This is the last + // block number if the block range are empty.. + // + Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum); + + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + if (Expected < 0) { + // + // The block range is empty. It may either because the the last + // block has been ACKed, or the sequence number just looped back, + // that is, there is more than 0xffff blocks. + // + if (Instance->LastBlock == AckNum) { + ASSERT (Instance->LastBlock >= 1); + *Completed = TRUE; + return EFI_SUCCESS; + + } else { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + "Block number rolls back, not supported, try blksize option" + ); + + return EFI_TFTP_ERROR; + } + } + + return Mtftp4WrqSendBlock (Instance, (UINT16) Expected); +} + + +/** + Check whether the received OACK is valid. The OACK is valid + only if: + 1. It only include options requested by us + 2. It can only include a smaller block size + 3. It can't change the proposed time out value. + 4. Other requirements of the individal MTFTP options as required.s + + @param Reply The options included in the OACK + @param Request The options we requested + + @return TRUE if the options included in OACK is valid, otherwise FALSE. + +**/ +BOOLEAN +Mtftp4WrqOackValid ( + IN MTFTP4_OPTION *Reply, + IN MTFTP4_OPTION *Request + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((Reply->Exist &~Request->Exist) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if (((Reply->Exist & MTFTP4_BLKSIZE_EXIST) && (Reply->BlkSize > Request->BlkSize)) || + ((Reply->Exist & MTFTP4_TIMEOUT_EXIST) && (Reply->Timeout != Request->Timeout))) { + return FALSE; + } + + return TRUE; +} + + +/** + Function to handle the MTFTP OACK packet. It parses the packet's + options, and update the internal states of the session + + @param Instance The MTFTP session + @param Packet The received OACK packet + @param Len The length of the packet + @param Completed Whether the transmisson has completed. NOT used by + this function. + + @retval EFI_SUCCESS The OACK process is OK + @retval EFI_TFTP_ERROR Some error occured, and the session reset. + +**/ +EFI_STATUS +Mtftp4WrqHandleOack ( + IN MTFTP4_PROTOCOL *Instance, + IN EFI_MTFTP4_PACKET *Packet, + IN UINT32 Len, + OUT BOOLEAN *Completed + ) +{ + MTFTP4_OPTION Reply; + EFI_MTFTP4_PACKET Bogus; + EFI_STATUS Status; + INTN Expected; + + *Completed = FALSE; + + // + // Ignore the OACK if already started the upload + // + Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); + + if (Expected != 0) { + return EFI_SUCCESS; + } + + // + // Parse and validate the options from server + // + NetZeroMem (&Reply, sizeof (MTFTP4_OPTION)); + Status = Mtftp4ParseOptionOack (Packet, Len, &Reply); + + if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) { + // + // Don't send a MTFTP error packet when out of resource, it can + // only make it worse. + // + if (Status != EFI_OUT_OF_RESOURCES) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, + "Mal-formated OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if (Reply.BlkSize != 0) { + Instance->BlkSize = Reply.BlkSize; + } + + if (Reply.Timeout != 0) { + Instance->Timeout = Reply.Timeout; + } + + // + // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck, + // which will start the transmission of the first data block. + // + Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK); + Bogus.Ack.Block[0] = 0; + + return Mtftp4WrqHandleAck (Instance, &Bogus, sizeof (EFI_MTFTP4_ACK_HEADER), Completed); +} + + +/** + The input process routine for MTFTP upload. + + @param UdpPacket The received MTFTP packet. + @param Points The local/remote access point + @param IoStatus The result of the packet receiving + @param Context Opaque parameter for the callback, which is the + MTFTP session. + + @return None + +**/ +VOID +Mtftp4WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_POINTS *Points, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP4_PROTOCOL *Instance; + EFI_MTFTP4_PACKET *Packet; + BOOLEAN Completed; + EFI_STATUS Status; + UINT32 Len; + UINT16 Opcode; + + Instance = (MTFTP4_PROTOCOL *) Context; + NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE); + + Completed = FALSE; + Packet = NULL; + Status = EFI_SUCCESS; + + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Client send initial request to server's listening port. Server + // will select a UDP port to communicate with the client. + // + if (Points->RemotePort != Instance->ConnectedPort) { + if (Instance->ConnectedPort != 0) { + goto ON_EXIT; + } else { + Instance->ConnectedPort = Points->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + + if (UdpPacket->BlockOpNum > 1) { + Packet = NetAllocatePool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Call the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if ((Instance->Token->CheckPacket != NULL) && + ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp4, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP4_OPCODE_ERROR) { + Mtftp4SendError ( + Instance, + EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, + "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + switch (Opcode) { + case EFI_MTFTP4_OPCODE_ACK: + if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) { + goto ON_EXIT; + } + + Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed); + break; + + case EFI_MTFTP4_OPCODE_OACK: + if (Len <= MTFTP4_OPCODE_LEN) { + goto ON_EXIT; + } + + Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed); + break; + + case EFI_MTFTP4_OPCODE_ERROR: + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status) and not completed, + // restart the receive, otherwise end the session. + // + if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) { + NetFreePool (Packet); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + if (!EFI_ERROR (Status) && !Completed) { + Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0); + } + + // + // Status may have been updated by UdpIoRecvDatagram + // + if (EFI_ERROR (Status) || Completed) { + Mtftp4CleanOperation (Instance, Status); + } +} diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c new file mode 100644 index 0000000000..5f15c81e48 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.c @@ -0,0 +1,2401 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + bc.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// +// +EFI_STATUS +EFIAPI +PxeBcDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +PxeBcDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +PxeBcDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +extern +VOID +InitArpHeader ( + VOID + ); +extern +VOID +OptionsStrucInit ( + VOID + ); + +// +// helper routines +// + +/** + Convert number to ASCII value + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + @param Length Length of Buffer. + + @retval none none + +**/ +VOID +CvtNum ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ) +{ + UINTN Remainder; + + while (Length--) { + Remainder = Number % 10; + Number /= 10; + Buffer[Length] = (UINT8) ('0' + Remainder); + } +} + + +/** + Convert number to decimal ASCII value at Buffer location + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + + @retval none none + +**/ +VOID +UtoA10 ( + IN UINTN Number, + IN UINT8 *Buffer + ) +{ + INTN Index; + UINT8 BuffArray[31]; + + BuffArray[30] = 0; + CvtNum (Number, BuffArray, 30); + + for (Index = 0; Index < 30; ++Index) { + if (BuffArray[Index] != '0') { + break; + } + } + + CopyMem (Buffer, BuffArray + Index, 31 - Index); +} + + +/** + Convert ASCII numeric string to a UINTN value + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + + @retval Value UINTN value of the ASCII string. + +**/ +UINTN +AtoU ( + IN UINT8 *Buffer + ) +{ + UINTN Value; + INT8 Character; + + Value = 0; + Character = *Buffer++; + do { + Value = Value * 10 + Character - '0'; + Character = *Buffer++; + } while (Character); + + return Value; +} + + +/** + Convert ASCII numeric string to a UINTN value + + @param Number Numeric value to convert to decimal ASCII value. + @param Buffer Buffer to place ASCII version of the Number + + @retval Value UINTN value of the ASCII string. + +**/ +UINT64 +AtoU64 ( + IN UINT8 *Buffer + ) +{ + UINT64 Value; + UINT8 Character; + + Value = 0; + while ((Character = *Buffer++) != '\0') { + Value = MultU64x32 (Value, 10) + (Character - '0'); + } + + return Value; +} +// +// random number generator +// +#define RANDOM_MULTIPLIER 2053 +#define RANDOM_ADD_IN_VALUE 19 + +VOID +SeedRandom ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 InitialSeed + ) +/*++ + + Routine Description: + Initialize the Seed for the random number generator + + Arguments: + + Returns: + none - + +--*/ +{ + if (Private != NULL) { + Private->RandomSeed = InitialSeed; + } +} + + +/** + Generate and return a pseudo-random number + + + @retval Number UINT16 random number + +**/ +UINT16 +Random ( + IN PXE_BASECODE_DEVICE *Private + ) +{ + UINTN Number; + + if (Private != NULL) { + Number = -(INTN) Private->RandomSeed * RANDOM_MULTIPLIER + RANDOM_ADD_IN_VALUE; + + return Private->RandomSeed = (UINT16) Number; + } else { + return 0; + } +} +// +// calculate the internet checksum (RFC 1071) +// return 16 bit ones complement of ones complement sum of 16 bit words +// + +/** + Calculate the internet checksum (see RFC 1071) + + @param Packet Buffer which contains the data to be checksummed + @param Length Length to be checksummed + + @retval Checksum Returns the 16 bit ones complement of ones + complement sum of 16 bit words + +**/ +UINT16 +IpChecksum ( + IN UINT16 *Packet, + IN UINTN Length + ) +{ + UINT32 Sum; + UINT8 Odd; + + Sum = 0; + Odd = (UINT8) (Length & 1); + Length >>= 1; + while (Length--) { + Sum += *Packet++; + } + + if (Odd) { + Sum += *(UINT8 *) Packet; + } + + Sum = (Sum & 0xffff) + (Sum >> 16); + // + // in case above carried + // + Sum += Sum >> 16; + + return (UINT16) (~ (UINT16) Sum); +} + + +/** + Calculate the internet checksum (see RFC 1071) + on a non contiguous header and data + + @param Header Buffer which contains the data to be checksummed + @param HeaderLen Length to be checksummed + @param Message Buffer which contains the data to be checksummed + @param MessageLen Length to be checksummed + + @retval Checksum Returns the 16 bit ones complement of ones + complement sum of 16 bit words + +**/ +UINT16 +IpChecksum2 ( + IN UINT16 *Header, + IN UINTN HeaderLen, + IN UINT16 *Message, + IN UINTN MessageLen + ) +{ + UINT32 Sum; + + Sum = (UINT16)~IpChecksum (Header, HeaderLen) + (UINT16)~IpChecksum (Message, MessageLen); + + // + // in case above carried + // + Sum += Sum >> 16; + + return (UINT16) (~ (UINT16) Sum); +} + + +/** + Adjust the internet checksum (see RFC 1071) on a single word update. + + @param OldChkSum Checksum previously calculated + @param OldWord Value + @param NewWord New Value + + @retval Checksum Returns the 16 bit ones complement of ones + complement sum of 16 bit words + +**/ +UINT16 +UpdateChecksum ( + IN UINT16 OldChksum, + IN UINT16 OldWord, + IN UINT16 NewWord + ) +{ + UINT32 sum; + + sum = ~OldChksum + NewWord - OldWord; + // + // in case above carried + // + sum += sum >> 16; + return (UINT16) (~ (UINT16) sum); +} + + +/** + See if a callback is in play + + @param Private Pointer to Pxe BaseCode Protocol + + @retval 0 Callbacks are active on the handle + @retval 1 Callbacks are not active on the handle + +**/ +STATIC +BOOLEAN +SetMakeCallback ( + IN PXE_BASECODE_DEVICE *Private + ) +{ + Private->EfiBc.Mode->MakeCallbacks = (BOOLEAN) (gBS->HandleProtocol ( + Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID *) &Private->CallbackProtocolPtr + ) == EFI_SUCCESS); + + DEBUG ( + (DEBUG_INFO, + "\nMode->MakeCallbacks == %d ", + Private->EfiBc.Mode->MakeCallbacks) + ); + + DEBUG ( + (DEBUG_INFO, + "\nPrivate->CallbackProtocolPtr == %xh ", + Private->CallbackProtocolPtr) + ); + + if (Private->CallbackProtocolPtr != NULL) { + DEBUG ( + (DEBUG_INFO, + "\nCallbackProtocolPtr->Revision = %xh ", + Private->CallbackProtocolPtr->Revision) + ); + + DEBUG ( + (DEBUG_INFO, + "\nCallbackProtocolPtr->Callback = %xh ", + Private->CallbackProtocolPtr->Callback) + ); + } + + return Private->EfiBc.Mode->MakeCallbacks; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Routine which does an SNP->Receive over a timeout period and doing callbacks + + @param Private Pointer to Pxe BaseCode Protocol + @param Function What PXE function to callback + @param TimeoutEvent Timer event that will trigger when we have waited + too long for an incoming packet + @param HeaderSizePtr Pointer to the size of the Header size + @param BufferSizePtr Pointer to the size of the Buffer size + @param ProtocolPtr The protocol to sniff for (namely, UDP/TCP/etc) + + @retval 0 Something was returned + @retval !0 Like there was nothing to receive + (EFI_TIMEOUT/NOT_READY) + +**/ +EFI_STATUS +WaitForReceive ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN EFI_EVENT TimeoutEvent, + IN OUT UINTN *HeaderSizePtr, + IN OUT UINTN *BufferSizePtr, + IN OUT UINT16 *ProtocolPtr + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_PXE_CALLBACK CallbackPtr; + EFI_STATUS StatCode; + EFI_EVENT CallbackEvent; + + // + // Initialize pointer to SNP interface + // + SnpPtr = Private->SimpleNetwork; + + // + // Initialize pointer to PxeBc callback routine - if any + // + CallbackPtr = (Private->EfiBc.Mode->MakeCallbacks) ? Private->CallbackProtocolPtr->Callback : NULL; + + // + // Create callback event and set timer + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &CallbackEvent + ); + + if (EFI_ERROR (StatCode)) { + return EFI_DEVICE_ERROR; + } + + // + // every 100 milliseconds + // + StatCode = gBS->SetTimer ( + CallbackEvent, + TimerPeriodic, + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (CallbackEvent); + return EFI_DEVICE_ERROR; + } + // + // Loop until a packet is received or a receive error is detected or + // a callback abort is detected or a timeout event occurs. + // + for (;;) + { + // + // Poll for received packet. + // + *BufferSizePtr = BUFFER_ALLOCATE_SIZE; + + StatCode = SnpPtr->Receive ( + SnpPtr, + HeaderSizePtr, + BufferSizePtr, + Private->ReceiveBufferPtr, + 0, + 0, + ProtocolPtr + ); + + if (!EFI_ERROR (StatCode)) { + // + // Packet was received. Make received callback then return. + // + if (CallbackPtr != NULL) { + StatCode = CallbackPtr ( + Private->CallbackProtocolPtr, + Function, + TRUE, + (UINT32) *BufferSizePtr, + (EFI_PXE_BASE_CODE_PACKET *) Private->ReceiveBufferPtr + ); + + if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + StatCode = EFI_ABORTED; + } else { + StatCode = EFI_SUCCESS; + } + } + + break; + } + + if (StatCode != EFI_NOT_READY) { + break; + } + // + // Check for callback event. + // + if (!EFI_ERROR (gBS->CheckEvent (CallbackEvent))) { + // + // Make periodic callback if callback pointer is initialized. + // + if (CallbackPtr != NULL) { + StatCode = CallbackPtr ( + Private->CallbackProtocolPtr, + Function, + FALSE, + 0, + NULL + ); + + // + // Abort if directed to by callback routine. + // + if (StatCode != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + StatCode = EFI_ABORTED; + break; + } + } + } + // + // Check for timeout event. + // + if (TimeoutEvent == 0) { + StatCode = EFI_TIMEOUT; + break; + } + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + StatCode = EFI_TIMEOUT; + break; + } + // + // Check IGMP timer events. + // + IgmpCheckTimers (Private); + } + + gBS->CloseEvent (CallbackEvent); + + return StatCode; +} + + +/** + Routine which does an SNP->Transmit of a buffer + + @param Private Pointer to Pxe BaseCode Protocol + @param HeaderPtr Pointer to the buffer + @param PacketPtr Pointer to the packet to send + @param PacketLen The length of the entire packet to send + @param HardwareAddr Pointer to the MAC address of the destination + @param MediaProtocol What type of frame to create (RFC 1700) - IE. + Ethernet + @param Function What PXE function to callback + + @retval 0 Something was sent + @retval !0 An error was encountered during sending of a packet + +**/ +EFI_STATUS +SendPacket ( + PXE_BASECODE_DEVICE *Private, + VOID *HeaderPtr, + VOID *PacketPtr, + INTN PacketLen, + VOID *HardwareAddr, + UINT16 MediaProtocol, + IN EFI_PXE_BASE_CODE_FUNCTION Function + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + EFI_PXE_CALLBACK CallbackPtr; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + UINT32 IntStatus; + VOID *TxBuf; + + // + // + // + CallbackPtr = Private->EfiBc.Mode->MakeCallbacks ? Private->CallbackProtocolPtr->Callback : 0; + + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // clear prior interrupt status + // + StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, 0); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #1 %xh (%r)", + StatCode, + StatCode) + ); + return StatCode; + } + + Private->DidTransmit = FALSE; + + if (CallbackPtr != NULL) { + if (CallbackPtr ( + Private->CallbackProtocolPtr, + Function, + FALSE, + (UINT32) PacketLen, + PacketPtr + ) != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #2 %xh (%r)", + EFI_ABORTED, + EFI_ABORTED) + ); + return EFI_ABORTED; + } + } + // + // put packet in transmit queue + // headersize should be zero if not filled in + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not create transmit timeout event. %r\n", + StatCode) + ); + return EFI_DEVICE_ERROR; + } + + // + // 5 milliseconds + // + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + 50000 + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not set transmit timeout event timer. %r\n", + StatCode) + ); + gBS->CloseEvent (TimeoutEvent); + return EFI_DEVICE_ERROR; + } + + for (;;) { + StatCode = SnpPtr->Transmit ( + SnpPtr, + (UINTN) SnpPtr->Mode->MediaHeaderSize, + (UINTN) (PacketLen + SnpPtr->Mode->MediaHeaderSize), + HeaderPtr, + &SnpModePtr->CurrentAddress, + (EFI_MAC_ADDRESS *) HardwareAddr, + &MediaProtocol + ); + + if (StatCode != EFI_NOT_READY) { + break; + } + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + StatCode = EFI_TIMEOUT; + break; + } + } + + gBS->CloseEvent (TimeoutEvent); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #3 %xh (%r)", + StatCode, + StatCode) + ); + return StatCode; + } + // + // remove transmit buffer from snp's unused queue + // done this way in case someday things are buffered and we don't get it back + // immediately + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not create transmit status timeout event. %r\n", + StatCode) + ); + return EFI_DEVICE_ERROR; + } + + // + // 5 milliseconds + // + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + 50000 + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_ERROR, + "Could not set transmit status timeout event timer. %r\n", + StatCode) + ); + gBS->CloseEvent (TimeoutEvent); + return EFI_DEVICE_ERROR; + } + + for (;;) { + StatCode = SnpPtr->GetStatus (SnpPtr, &IntStatus, &TxBuf); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nSendPacket() Exit #4 %xh (%r)", + StatCode, + StatCode) + ); + break; + } + + if (IntStatus & EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT) { + Private->DidTransmit = TRUE; + } + + if (TxBuf != NULL) { + break; + } + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + StatCode = EFI_TIMEOUT; + break; + } + } + + gBS->CloseEvent (TimeoutEvent); + + return StatCode; +} +// +// +// + +/** + + +**/ +EFI_BIS_PROTOCOL * +PxebcBisStart ( + IN PXE_BASECODE_DEVICE *Private, + OUT BIS_APPLICATION_HANDLE *BisAppHandle, + OUT OPTIONAL EFI_BIS_DATA **BisDataSigInfo + ) +{ + EFI_STATUS EfiStatus; + EFI_HANDLE BisHandleBuffer; + UINTN BisHandleCount; + EFI_BIS_PROTOCOL *BisPtr; + EFI_BIS_VERSION BisInterfaceVersion; + BOOLEAN BisCheckFlag; + + BisHandleCount = sizeof (EFI_HANDLE); + BisCheckFlag = FALSE; + + // + // Locate BIS protocol handle (if present). + // If BIS protocol handle is not found, return NULL. + // + DEBUG ((DEBUG_INFO, "\ngBS->LocateHandle() ")); + + EfiStatus = gBS->LocateHandle ( + ByProtocol, + &gEfiBisProtocolGuid, + NULL, + &BisHandleCount, + &BisHandleBuffer + ); + + if (EFI_ERROR (EfiStatus)) { + // + // Any error means that there is no BIS. + // Note - It could mean that there are more than + // one BIS protocols installed, but that scenario + // is not yet supported. + // + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n gBS->LocateHandle() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + return NULL; + } + + if (BisHandleCount != sizeof BisHandleBuffer) { + // + // This really should never happen, but I am paranoid. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart() BisHandleCount != %d\n", + sizeof BisHandleBuffer) + ); + + return NULL; + } + + DEBUG ((DEBUG_INFO, "BIS handle found.")); + + // + // Locate BIS protocol interface. + // If the BIS protocol interface cannot be found, return NULL. + // + DEBUG ((DEBUG_INFO, "\ngBS->HandleProtocol() ")); + + EfiStatus = gBS->HandleProtocol ( + BisHandleBuffer, + &gEfiBisProtocolGuid, + (VOID **) &BisPtr + ); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n gBS->HandleProtocol() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + return NULL; + } + + if (BisPtr == NULL) { + // + // This really should never happen. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart()""\n gBS->HandleProtocoL() ""BIS protocol interface pointer is NULL!\n") + ); + + return NULL; + } + + DEBUG ((DEBUG_INFO, "BIS protocol interface found.")); + + // + // Check that all of the BIS API function pointers are not NULL. + // + if (BisPtr->Initialize == NULL || + BisPtr->Shutdown == NULL || + BisPtr->Free == NULL || + BisPtr->GetBootObjectAuthorizationCertificate == NULL || + BisPtr->GetBootObjectAuthorizationCheckFlag == NULL || + BisPtr->GetBootObjectAuthorizationUpdateToken == NULL || + BisPtr->GetSignatureInfo == NULL || + BisPtr->UpdateBootObjectAuthorization == NULL || + BisPtr->VerifyBootObject == NULL || + BisPtr->VerifyObjectWithCredential == NULL + ) { + DEBUG ( + ( + DEBUG_NET, + "\nPxebcBisStart()""\n BIS protocol interface is invalid." + "\n At least one BIS protocol function pointer is NULL.\n" + ) + ); + + return NULL; + } + // + // Initialize BIS. + // If BIS does not initialize, return NULL. + // + DEBUG ((DEBUG_INFO, "\nBisPtr->Initialize() ")); + + BisInterfaceVersion.Major = BIS_VERSION_1; + + EfiStatus = BisPtr->Initialize ( + BisPtr, + BisAppHandle, + &BisInterfaceVersion, + NULL + ); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BisPtr->Initialize() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + return NULL; + } + + DEBUG ( + (DEBUG_INFO, + " BIS version: %d.%d", + BisInterfaceVersion.Major, + BisInterfaceVersion.Minor) + ); + + // + // If the requested BIS API version is not supported, + // shutdown BIS and return NULL. + // + if (BisInterfaceVersion.Major != BIS_VERSION_1) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BIS version %d.%d not supported by PXE BaseCode.\n", + BisInterfaceVersion.Major, + BisInterfaceVersion.Minor) + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + // + // Get BIS check flag. + // If the BIS check flag cannot be read, shutdown BIS and return NULL. + // + DEBUG ((DEBUG_INFO, "\nBisPtr->GetBootObjectAuthorizationCheckFlag() ")); + + EfiStatus = BisPtr->GetBootObjectAuthorizationCheckFlag (*BisAppHandle, &BisCheckFlag); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BisPtr->GetBootObjectAuthorizationCheckFlag() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + // + // If the BIS check flag is FALSE, shutdown BIS and return NULL. + // + if (!BisCheckFlag) { + DEBUG ((DEBUG_INFO, "\nBIS check flag is FALSE.\n")); + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } else { + DEBUG ((DEBUG_INFO, "\nBIS check flag is TRUE.")); + } + // + // Early out if caller does not want signature information. + // + if (BisDataSigInfo == NULL) { + return BisPtr; + } + // + // Get BIS signature information. + // If the signature information cannot be read or is invalid, + // shutdown BIS and return NULL. + // + DEBUG ((DEBUG_INFO, "\nBisPtr->GetSignatureInfo() ")); + + EfiStatus = BisPtr->GetSignatureInfo (*BisAppHandle, BisDataSigInfo); + + if (EFI_ERROR (EfiStatus)) { + DEBUG ( + (DEBUG_WARN, + "\nPxebcBisStart()""\n BisPtr_GetSignatureInfo() %r (%xh)\n", + EfiStatus, + EfiStatus) + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + + if (*BisDataSigInfo == NULL) { + // + // This should never happen. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart()""\n BisPtr->GetSignatureInfo() Data pointer is NULL!\n") + ); + + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + + if ((*BisDataSigInfo)->Length < sizeof (EFI_BIS_SIGNATURE_INFO) || + (*BisDataSigInfo)->Length % sizeof (EFI_BIS_SIGNATURE_INFO) || + (*BisDataSigInfo)->Length > sizeof (EFI_BIS_SIGNATURE_INFO) * 63 + ) { + // + // This should never happen. + // + DEBUG ( + (DEBUG_NET, + "\nPxebcBisStart()""\n BisPtr->GetSignatureInfo() Invalid BIS siginfo length.\n") + ); + + BisPtr->Free (*BisAppHandle, *BisDataSigInfo); + BisPtr->Shutdown (*BisAppHandle); + return NULL; + } + + return BisPtr; +} + + +/** + + +**/ +VOID +PxebcBisStop ( + EFI_BIS_PROTOCOL *BisPtr, + BIS_APPLICATION_HANDLE BisAppHandle, + EFI_BIS_DATA *BisDataSigInfo + ) +{ + if (BisPtr == NULL) { + return ; + } + // + // Free BIS allocated resources and shutdown BIS. + // Return TRUE - BIS support is officially detected. + // + if (BisDataSigInfo != NULL) { + BisPtr->Free (BisAppHandle, BisDataSigInfo); + } + + BisPtr->Shutdown (BisAppHandle); +} + + +/** + + @return TRUE := verified + @return FALSE := not verified + +**/ +BOOLEAN +PxebcBisVerify ( + PXE_BASECODE_DEVICE *Private, + VOID *FileBuffer, + UINTN FileLength, + VOID *CredentialBuffer, + UINTN CredentialLength + ) +{ + EFI_BIS_PROTOCOL *BisPtr; + BIS_APPLICATION_HANDLE BisAppHandle; + EFI_BIS_DATA FileData; + EFI_BIS_DATA CredentialData; + EFI_STATUS EfiStatus; + BOOLEAN IsVerified; + + if (Private == NULL || FileBuffer == NULL || FileLength == 0 || CredentialBuffer == NULL || CredentialLength == 0) { + return FALSE; + } + + BisPtr = PxebcBisStart (Private, &BisAppHandle, NULL); + + if (BisPtr == NULL) { + return FALSE; + } + + FileData.Length = (UINT32) FileLength; + FileData.Data = FileBuffer; + CredentialData.Length = (UINT32) CredentialLength; + CredentialData.Data = CredentialBuffer; + + EfiStatus = BisPtr->VerifyBootObject ( + BisAppHandle, + &CredentialData, + &FileData, + &IsVerified + ); + + PxebcBisStop (BisPtr, BisAppHandle, NULL); + + return (BOOLEAN) ((EFI_ERROR (EfiStatus)) ? FALSE : (IsVerified ? TRUE : FALSE)); +} + + +/** + + @return TRUE := BIS present + @return FALSE := BIS not present + +**/ +BOOLEAN +PxebcBisDetect ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_BIS_PROTOCOL *BisPtr; + BIS_APPLICATION_HANDLE BisAppHandle; + EFI_BIS_DATA *BisDataSigInfo; + + BisPtr = PxebcBisStart (Private, &BisAppHandle, &BisDataSigInfo); + + if (BisPtr == NULL) { + return FALSE; + } + + PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo); + + return TRUE; +} + +static VOID *BCNotifyReg; + + +/** + Start and initialize the BaseCode protocol, Simple Network protocol and UNDI. + + @param Private Pointer to Pxe BaseCode Protocol + @param UseIPv6 Do we want to support IPv6? + + @return EFI_SUCCESS + @return EFI_INVALID_PARAMETER + @return EFI_UNSUPPORTED + @return EFI_ALREADY_STARTED + @return EFI_OUT_OF_RESOURCES + @return Status is also returned from SNP.Start() and SNP.Initialize(). + +**/ +EFI_STATUS +EFIAPI +BcStart ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIPv6 + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + EFI_STATUS Status; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + // + // Make sure BaseCode is not already started. + // + if (This->Mode->Started) { + DEBUG ((DEBUG_WARN, "\nBcStart() BC is already started.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_ALREADY_STARTED; + } + +#if !SUPPORT_IPV6 + // + // Fail if IPv6 is requested and not supported. + // + if (UseIPv6) { + DEBUG ((DEBUG_WARN, "\nBcStart() IPv6 is not supported.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_UNSUPPORTED; + } +#endif + // + // Setup shortcuts to SNP protocol and data structure. + // + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // Start and initialize SNP. + // + if (SnpModePtr->State == EfiSimpleNetworkStopped) { + StatCode = (*SnpPtr->Start) (SnpPtr); + + if (SnpModePtr->State != EfiSimpleNetworkStarted) { + DEBUG ((DEBUG_WARN, "\nBcStart() Could not start SNP.\n")); + EfiReleaseLock (&Private->Lock); + return StatCode; + } + } + // + // acquire memory for mode and transmit/receive buffers + // + if (SnpModePtr->State == EfiSimpleNetworkStarted) { + StatCode = (*SnpPtr->Initialize) (SnpPtr, 0, 0); + + if (SnpModePtr->State != EfiSimpleNetworkInitialized) { + DEBUG ((DEBUG_WARN, "\nBcStart() Could not initialize SNP.")); + EfiReleaseLock (&Private->Lock); + return StatCode; + } + } + // + // Dump debug info. + // + DEBUG ((DEBUG_INFO, "\nBC Start()")); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->State %Xh", + SnpModePtr->State) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->HwAddressSize %Xh", + SnpModePtr->HwAddressSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MediaHeaderSize %Xh", + SnpModePtr->MediaHeaderSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MaxPacketSize %Xh", + SnpModePtr->MaxPacketSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MacAddressChangeable %Xh", + SnpModePtr->MacAddressChangeable) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MultipleTxSupported %Xh", + SnpModePtr->MultipleTxSupported) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->CurrentAddress %Xh", + *((UINTN *)&SnpModePtr->CurrentAddress)) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->BroadcastAddress %Xh", + *((UINTN *)&SnpModePtr->BroadcastAddress)) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->PermanentAddress %Xh", + *((UINTN *)&SnpModePtr->PermanentAddress)) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->NvRamSize %Xh", + SnpModePtr->NvRamSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->NvRamAccessSize %Xh", + SnpModePtr->NvRamAccessSize) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->ReceiveFilterMask %Xh", + SnpModePtr->ReceiveFilterMask) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->ReceiveFilterSetting %Xh", + SnpModePtr->ReceiveFilterSetting) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MCastFilterCount %Xh", + SnpModePtr->MCastFilterCount) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MCastFilter %Xh", + SnpModePtr->MCastFilter) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->IfType %Xh", + SnpModePtr->IfType) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MediaPresentSupported %Xh", + SnpModePtr->MediaPresentSupported) + ); + DEBUG ( + (DEBUG_INFO, + "\nSnpModePtr->MediaPresent %Xh", + SnpModePtr->MediaPresent) + ); + + // + // If media check is supported and there is no media, + // return error to caller. + // + if (SnpModePtr->MediaPresentSupported && !SnpModePtr->MediaPresent) { + DEBUG ((DEBUG_WARN, "\nBcStart() Media not present.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_NO_MEDIA; + } + // + // Allocate Tx/Rx buffers + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + BUFFER_ALLOCATE_SIZE, + &Private->TransmitBufferPtr + ); + + if (!EFI_ERROR (Status)) { + ZeroMem (Private->TransmitBufferPtr, BUFFER_ALLOCATE_SIZE); + } else { + DEBUG ((DEBUG_NET, "\nBcStart() Could not alloc TxBuf.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + BUFFER_ALLOCATE_SIZE, + &Private->ReceiveBufferPtr + ); + + if (!EFI_ERROR (Status)) { + ZeroMem (Private->ReceiveBufferPtr, BUFFER_ALLOCATE_SIZE); + } else { + DEBUG ((DEBUG_NET, "\nBcStart() Could not alloc RxBuf.\n")); + gBS->FreePool (Private->TransmitBufferPtr); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + 256, + &Private->TftpErrorBuffer + ); + + if (EFI_ERROR (Status)) { + gBS->FreePool (Private->ReceiveBufferPtr); + gBS->FreePool (Private->TransmitBufferPtr); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->AllocatePool (EfiBootServicesData, 256, &Private->TftpAckBuffer); + + if (EFI_ERROR (Status)) { + gBS->FreePool (Private->TftpErrorBuffer); + gBS->FreePool (Private->ReceiveBufferPtr); + gBS->FreePool (Private->TransmitBufferPtr); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + // + // Initialize private BaseCode instance data + // + do { + Private->RandomPort = (UINT16) (Private->RandomPort + PXE_RND_PORT_LOW + Random (Private)); + } while (Private->RandomPort < PXE_RND_PORT_LOW); + + Private->Igmpv1TimeoutEvent = NULL; + Private->UseIgmpv1Reporting = TRUE; + Private->IpLength = IP_ADDRESS_LENGTH (Private->EfiBc.Mode); + + // + // Initialize Mode structure + // + ZeroMem (Private->EfiBc.Mode, sizeof (EFI_PXE_BASE_CODE_MODE)); + // + // check for callback protocol and set boolean + // + SetMakeCallback (Private); + Private->EfiBc.Mode->Started = TRUE; + Private->EfiBc.Mode->TTL = DEFAULT_TTL; + Private->EfiBc.Mode->ToS = DEFAULT_ToS; + Private->EfiBc.Mode->UsingIpv6 = UseIPv6; + + // + // Set to PXE_TRUE by the BC constructor if this BC implementation + // supports IPv6. + // + Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6; + + Private->EfiBc.Mode->Ipv6Available = FALSE; + // + // Set to TRUE by the BC constructor if this BC implementation + // supports BIS. + // + Private->EfiBc.Mode->BisSupported = TRUE; + Private->EfiBc.Mode->BisDetected = PxebcBisDetect (Private); + + // + // This field is set to PXE_TRUE by the BC Start() function. When this + // field is PXE_TRUE, ARP packets are sent as needed to get IP and MAC + // addresses. This can cause unexpected delays in the DHCP(), Discover() + // and MTFTP() functions. Setting this to PXE_FALSE will cause these + // functions to fail if the required IP/MAC information is not in the + // ARP cache. The value of this field can be changed by an application + // at any time. + // + Private->EfiBc.Mode->AutoArp = TRUE; + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return EFI_SUCCESS; +} + + +/** + Stop the BaseCode protocol, Simple Network protocol and UNDI. + + @param Private Pointer to Pxe BaseCode Protocol + + @retval 0 Successfully stopped + @retval !0 Failed + +**/ +EFI_STATUS +EFIAPI +BcStop ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ) +{ + // + // Lock the instance data + // + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + PxebcMode = Private->EfiBc.Mode; + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // Issue BC command + // + StatCode = EFI_NOT_STARTED; + + if (SnpModePtr->State == EfiSimpleNetworkInitialized) { + StatCode = (*SnpPtr->Shutdown) (SnpPtr); + } + + if (SnpModePtr->State == EfiSimpleNetworkStarted) { + StatCode = (*SnpPtr->Stop) (SnpPtr); + } + + if (Private->TransmitBufferPtr != NULL) { + gBS->FreePool (Private->TransmitBufferPtr); + Private->TransmitBufferPtr = NULL; + } + + if (Private->ReceiveBufferPtr != NULL) { + gBS->FreePool (Private->ReceiveBufferPtr); + Private->ReceiveBufferPtr = NULL; + } + + if (Private->ArpBuffer != NULL) { + gBS->FreePool (Private->ArpBuffer); + Private->ArpBuffer = NULL; + } + + if (Private->TftpErrorBuffer != NULL) { + gBS->FreePool (Private->TftpErrorBuffer); + Private->TftpErrorBuffer = NULL; + } + + if (Private->TftpAckBuffer != NULL) { + gBS->FreePool (Private->TftpAckBuffer); + Private->TftpAckBuffer = NULL; + } + + if (Private->Igmpv1TimeoutEvent != NULL) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + } + + Private->FileSize = 0; + + if (!Private->EfiBc.Mode->Started) { + StatCode = EFI_NOT_STARTED; + } else { + Private->EfiBc.Mode->Started = FALSE; + } + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +const IPV4_ADDR AllSystemsGroup = { 224, 0, 0, 1 }; + + +/** + Set up the IP filter + + @param Private Pointer to Pxe BaseCode Protocol + @param Filter Pointer to the filter + + @retval 0 Successfully set the filter + @retval !0 Failed + +**/ +EFI_STATUS +IpFilter ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_IP_FILTER *Filter + ) +{ + EFI_STATUS StatCode; + EFI_MAC_ADDRESS MACadds[PXE_IP_FILTER_SIZE]; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + EFI_SIMPLE_NETWORK_MODE *SnpModePtr; + UINT32 Enable; + UINT32 Disable; + UINTN Index; + UINTN Index2; + + PxebcMode = Private->EfiBc.Mode; + SnpPtr = Private->SimpleNetwork; + SnpModePtr = SnpPtr->Mode; + + // + // validate input parameters + // must have a filter + // must not have any extra filter bits set + // + if (Filter == NULL || + (Filter->Filters &~FILTER_BITS) + // + // must not have a count which is too large or with no IP list + // + || + (Filter->IpCnt && (!Filter->IpList || Filter->IpCnt > PXE_IP_FILTER_SIZE)) + // + // must not have incompatible filters - promiscuous incompatible with anything else + // + || + ( + (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) && + ((Filter->Filters &~EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) || Filter->IpCnt) + ) + ) { + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #1")); + return EFI_INVALID_PARAMETER; + } + // + // promiscuous multicast incompatible with multicast in IP list + // + if (Filter->IpCnt && (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST)) { + for (Index = 0; Index < Filter->IpCnt; ++Index) { + if (IS_MULTICAST (&Filter->IpList[Index])) { + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #2")); + return EFI_INVALID_PARAMETER; + } + } + } + // + // leave groups for all those multicast which are no longer enabled + // + for (Index = 0; Index < PxebcMode->IpFilter.IpCnt; ++Index) { + if (!IS_MULTICAST (&PxebcMode->IpFilter.IpList[Index])) { + continue; + } + + for (Index2 = 0; Index2 < Filter->IpCnt; ++Index2) { + if (!CompareMem (&PxebcMode->IpFilter.IpList[Index], &Filter->IpList[Index2], IP_ADDRESS_LENGTH (PxebcMode))) { + // + // still enabled + // + break; + } + } + // + // if we didn't find it, remove from group + // + if (Index2 == Filter->IpCnt) { + IgmpLeaveGroup (Private, &PxebcMode->IpFilter.IpList[Index]); + } + } + // + // set enable bits, convert multicast ip adds, join groups + // allways leave receive broadcast enabled at hardware layer + // + Index2 = 0; + + if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) { + Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } else { + if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) { + Enable = EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + } else { + Enable = EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + + for (Index = 0; Index < Filter->IpCnt; ++Index) { + PxebcMode->IpFilter.IpList[Index] = Filter->IpList[Index]; + + if (IS_MULTICAST (&Filter->IpList[Index])) { + EFI_IP_ADDRESS *TmpIp; + + Enable |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; + + // + // if this is the first group, add the all systems group to mcast list + // + if (!Index2) + { + TmpIp = (EFI_IP_ADDRESS *) &AllSystemsGroup; + --Index; + } else { + TmpIp = (EFI_IP_ADDRESS *) &Filter->IpList[Index]; + } + // + // get MAC address of IP + // + StatCode = (*SnpPtr->MCastIpToMac) (SnpPtr, PxebcMode->UsingIpv6, TmpIp, &MACadds[Index2++]); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_INFO, + "\nIpFilter() Exit #2 %Xh (%r)", + StatCode, + StatCode) + ); + return StatCode; + } + } else { + Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + } + } + } + + if (Filter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) { + Enable |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + } + } + // + // if nothing changed, just return + // + DEBUG ( + (DEBUG_INFO, + "\nsnp->ReceiveFilterSetting == %Xh Filter->IpCnt == %Xh", + SnpModePtr->ReceiveFilterSetting, + Filter->IpCnt) + ); + + if (SnpModePtr->ReceiveFilterSetting == Enable && !Filter->IpCnt) { + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #4")); + return EFI_SUCCESS; + } + // + // disable those currently set but not set in new filter + // + Disable = SnpModePtr->ReceiveFilterSetting &~Enable; + + StatCode = SnpPtr->ReceiveFilters (SnpPtr, Enable, Disable, FALSE, Index2, MACadds); + + PxebcMode->IpFilter.IpCnt = Filter->IpCnt; + + // + // join groups for all multicast in list + // + for (Index = 0; Index < Filter->IpCnt; ++Index) { + if (IS_MULTICAST (&Filter->IpList[Index])) { + IgmpJoinGroup (Private, &Filter->IpList[Index]); + } + } + + DEBUG ((DEBUG_INFO, "\nIpFilter() Exit #5 %Xh (%r)", StatCode, StatCode)); + + return StatCode; +} + + +/** + Call the IP filter + + @param Private Pointer to Pxe BaseCode Protocol + @param Filter Pointer to the filter + + @retval 0 Successfully set the filter + @retval !0 Failed + +**/ +EFI_STATUS +EFIAPI +BcIpFilter ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *Filter + ) +{ + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + UINTN Index; + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < Filter->IpCnt; ++Index) { + if ((Filter->IpList[Index].Addr[0]) == BROADCAST_IPv4) { + // + // The IP is a broadcast address. + // + return EFI_INVALID_PARAMETER; + } + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + if (Filter == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Issue BC command + // + StatCode = IpFilter (Private, Filter); + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + + +/** + Set the Base Code behavior parameters + + @param This Pointer to Pxe BaseCode Protocol + @param AutoArpPtr Boolean to do ARP stuff + @param SendGuidPtr Boolean whether or not to send GUID info + @param TimeToLivePtr Value for Total time to live + @param TypeOfServicePtr Value for Type of Service + @param MakeCallbackPtr Boolean to determine if we make callbacks + + @retval 0 Successfully set the parameters + @retval !0 Failed + +**/ +EFI_STATUS +EFIAPI +BcSetParameters ( + EFI_PXE_BASE_CODE_PROTOCOL *This, + BOOLEAN *AutoArpPtr, + BOOLEAN *SendGuidPtr, + UINT8 *TimeToLivePtr, + UINT8 *TypeOfServicePtr, + BOOLEAN *MakeCallbackPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_GUID TmpGuid; + UINT8 *SerialNumberPtr; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + DEBUG ((DEBUG_INFO, "\nSetParameters() Entry. ")); + + PxebcMode = Private->EfiBc.Mode; + StatCode = EFI_SUCCESS; + + if (SendGuidPtr != NULL) { + if (*SendGuidPtr) { + if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber (&TmpGuid, &SerialNumberPtr) != EFI_SUCCESS) { + return EFI_INVALID_PARAMETER; + } + } + } + + if (MakeCallbackPtr != NULL) { + if (*MakeCallbackPtr) { + if (!SetMakeCallback (Private)) { + return EFI_INVALID_PARAMETER; + } + } + + PxebcMode->MakeCallbacks = *MakeCallbackPtr; + } + + if (AutoArpPtr != NULL) { + PxebcMode->AutoArp = *AutoArpPtr; + } + + if (SendGuidPtr != NULL) { + PxebcMode->SendGUID = *SendGuidPtr; + } + + if (TimeToLivePtr != NULL) { + PxebcMode->TTL = *TimeToLivePtr; + } + + if (TypeOfServicePtr != NULL) { + PxebcMode->ToS = *TypeOfServicePtr; + } + // + // Unlock the instance data + // + DEBUG ((DEBUG_INFO, "\nSetparameters() Exit = %xh ", StatCode)); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} +// +// ////////////////////////////////////////////////////////// +// +// BC Set Station IP Routine +// + +/** + Set the station IP address + + @param This Pointer to Pxe BaseCode Protocol + @param StationIpPtr Pointer to the requested IP address to set in base + code + @param SubnetMaskPtr Pointer to the requested subnet mask for the base + code + + @retval EFI_SUCCESS Successfully set the parameters + @retval EFI_NOT_STARTED BC has not started + +**/ +EFI_STATUS +EFIAPI +BcSetStationIP ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *StationIpPtr, + IN EFI_IP_ADDRESS *SubnetMaskPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + UINT32 SubnetMask; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + StatCode = EFI_NOT_STARTED; + goto RELEASE_LOCK; + } + + PxebcMode = Private->EfiBc.Mode; + + if (!Private->GoodStationIp && ((StationIpPtr == NULL) || (SubnetMaskPtr == NULL))) { + // + // It's not allowed to only set one of the two addresses while there isn't a previous + // GOOD address configuration. + // + StatCode = EFI_INVALID_PARAMETER; + goto RELEASE_LOCK; + } + + if (SubnetMaskPtr != NULL) { + SubnetMask = SubnetMaskPtr->Addr[0]; + + if (SubnetMask & (SubnetMask + 1)) { + // + // the subnet mask is valid if it's with leading continuous 1 bits. + // + StatCode = EFI_INVALID_PARAMETER; + goto RELEASE_LOCK; + } + } else { + SubnetMaskPtr = &PxebcMode->SubnetMask; + SubnetMask = SubnetMaskPtr->Addr[0]; + } + + if (StationIpPtr == NULL) { + StationIpPtr = &PxebcMode->StationIp; + } + + if (!IS_INADDR_UNICAST (StationIpPtr) || + ((StationIpPtr->Addr[0] | SubnetMask) == BROADCAST_IPv4)) { + // + // The station IP is not a unicast address. + // + StatCode = EFI_INVALID_PARAMETER; + goto RELEASE_LOCK; + } + + PxebcMode->StationIp = *StationIpPtr; + PxebcMode->SubnetMask = *SubnetMaskPtr; + Private->GoodStationIp = TRUE; + +RELEASE_LOCK: + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + + return StatCode; +} + +EFI_DRIVER_BINDING_PROTOCOL mPxeBcDriverBinding = { + PxeBcDriverSupported, + PxeBcDriverStart, + PxeBcDriverStop, + 0xa, + NULL, + NULL +}; + + +/** + Test to see if this driver supports Controller. Any Controller + than contains a Snp protocol can be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *SnpPtr; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &SnpPtr, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Start the Base code driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + PXE_BASECODE_DEVICE *Private; + LOADFILE_DEVICE *pLF; + + // + // Allocate structures needed by BaseCode and LoadFile protocols. + // + Private = AllocateZeroPool (sizeof (PXE_BASECODE_DEVICE)); + + if (Private == NULL ) { + DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc PXE_BASECODE_DEVICE structure.\n")); + return EFI_OUT_OF_RESOURCES; + } + + pLF = AllocateZeroPool (sizeof (LOADFILE_DEVICE)); + if (pLF == NULL) { + DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc LOADFILE_DEVICE structure.\n")); + FreePool (Private); + return EFI_OUT_OF_RESOURCES; + } + + Private->EfiBc.Mode = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_MODE)); + if (Private->EfiBc.Mode == NULL) { + DEBUG ((EFI_D_NET, "\nBcNotifySnp() Could not alloc Mode structure.\n")); + FreePool (Private); + FreePool (pLF); + return EFI_OUT_OF_RESOURCES; + } + // + // Lock access, just in case + // + EfiInitializeLock (&Private->Lock, TPL_CALLBACK); + EfiAcquireLock (&Private->Lock); + + EfiInitializeLock (&pLF->Lock, TPL_CALLBACK); + EfiAcquireLock (&pLF->Lock); + + // + // Initialize PXE structure + // + // + // First initialize the internal 'private' data that the application + // does not see. + // + Private->Signature = PXE_BASECODE_DEVICE_SIGNATURE; + Private->Handle = Controller; + + // + // Get the NII interface + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->NiiPtr, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + (VOID **) &Private->NiiPtr, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + goto PxeBcError; + } + } + // + // Get the Snp interface + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Private->SimpleNetwork, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto PxeBcError; + } + + // + // Next, initialize the external 'public' data that + // the application does see. + // + Private->EfiBc.Revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION; + Private->EfiBc.Start = BcStart; + Private->EfiBc.Stop = BcStop; + Private->EfiBc.Dhcp = BcDhcp; + Private->EfiBc.Discover = BcDiscover; + Private->EfiBc.Mtftp = BcMtftp; + Private->EfiBc.UdpWrite = BcUdpWrite; + Private->EfiBc.UdpRead = BcUdpRead; + Private->EfiBc.Arp = BcArp; + Private->EfiBc.SetIpFilter = BcIpFilter; + Private->EfiBc.SetParameters = BcSetParameters; + Private->EfiBc.SetStationIp = BcSetStationIP; + Private->EfiBc.SetPackets = BcSetPackets; + + // + // Initialize BaseCode Mode structure + // + Private->EfiBc.Mode->Started = FALSE; + Private->EfiBc.Mode->TTL = DEFAULT_TTL; + Private->EfiBc.Mode->ToS = DEFAULT_ToS; + Private->EfiBc.Mode->UsingIpv6 = FALSE; + Private->EfiBc.Mode->AutoArp = TRUE; + + // + // Set to PXE_TRUE by the BC constructor if this BC + // implementation supports IPv6. + // + Private->EfiBc.Mode->Ipv6Supported = SUPPORT_IPV6; + +#if SUPPORT_IPV6 + Private->EfiBc.Mode->Ipv6Available = Private->NiiPtr->Ipv6Supported; +#else + Private->EfiBc.Mode->Ipv6Available = FALSE; +#endif + // + // Set to TRUE by the BC constructor if this BC + // implementation supports BIS. + // + Private->EfiBc.Mode->BisSupported = TRUE; + Private->EfiBc.Mode->BisDetected = PxebcBisDetect (Private); + + // + // Initialize LoadFile structure. + // + pLF->Signature = LOADFILE_DEVICE_SIGNATURE; + pLF->LoadFile.LoadFile = LoadFile; + pLF->Private = Private; + + // + // Install protocol interfaces. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiPxeBaseCodeProtocolGuid, + &Private->EfiBc, + &gEfiLoadFileProtocolGuid, + &pLF->LoadFile, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + goto PxeBcError; + } + // + // Release locks. + // + EfiReleaseLock (&pLF->Lock); + EfiReleaseLock (&Private->Lock); + return Status; + +PxeBcError: ; + gBS->FreePool (Private->EfiBc.Mode); + gBS->FreePool (Private); + gBS->FreePool (pLF); + return Status; +} + + +/** + Stop the Base code driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param NumberOfChildren Not used + @param ChildHandleBuffer Not used + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_LOAD_FILE_PROTOCOL *LfProtocol; + LOADFILE_DEVICE *LoadDevice; + + // + // Get our context back. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiLoadFileProtocolGuid, + (VOID **) &LfProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + LoadDevice = EFI_LOAD_FILE_DEV_FROM_THIS (LfProtocol); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiLoadFileProtocolGuid, + &LoadDevice->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &LoadDevice->Private->EfiBc, + NULL + ); + + if (!EFI_ERROR (Status)) { + + Status = gBS->CloseProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->FreePool (LoadDevice->Private->EfiBc.Mode); + gBS->FreePool (LoadDevice->Private); + gBS->FreePool (LoadDevice); + } + + return Status; +} + + +/** + Initialize the base code drivers and install the driver binding + + Standard EFI Image Entry + + @retval EFI_SUCCESS This driver was successfully bound + +**/ +EFI_STATUS +EFIAPI +InitializeBCDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Initialize EFI library + // + Status = EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &mPxeBcDriverBinding, + NULL, + COMPONENT_NAME, + NULL, + NULL + ); + + InitArpHeader (); + OptionsStrucInit (); + + return EFI_SUCCESS; +} + +/* eof - bc.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h new file mode 100644 index 0000000000..71d1518656 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Bc.h @@ -0,0 +1,564 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + bc.h + +Abstract: + + +**/ + +#ifndef _BC_H +#define _BC_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define CALLBACK_INTERVAL 100 // ten times a second +#define FILTER_BITS (EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | \ + EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST | \ + EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS | \ + EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST \ + ) + +#define WAIT_TX_TIMEOUT 1000 + +#define SUPPORT_IPV6 0 + +#define PXE_BASECODE_DEVICE_SIGNATURE 'pxed' + +// +// Determine the classes of IPv4 address +// +#define IS_CLASSA_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0x80) == 0x00) +#define IS_CLASSB_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0xc0) == 0x80) +#define IS_CLASSC_IPADDR(x) ((((EFI_IP_ADDRESS*)x)->v4.Addr[0] & 0xe0) == 0xc0) +#define IS_INADDR_UNICAST(x) ((IS_CLASSA_IPADDR(x) || IS_CLASSB_IPADDR(x) || IS_CLASSC_IPADDR(x)) && (((EFI_IP_ADDRESS*)x)->Addr[0] != 0) ) + +// +// Definitions for internet group management protocol version 2 message +// structure +// Per RFC 2236, November 1997 +// +#pragma pack(1) + +typedef struct { + UINT8 Type; + UINT8 MaxRespTime; // in tenths of a second + UINT16 Checksum; // ones complement of ones complement sum of + // 16 bit words of message + UINT32 GroupAddress; // for general query, all systems group, + // for group specific, the group +} IGMPV2_MESSAGE; + +#define IGMP_TYPE_QUERY 0x11 +#define IGMP_TYPE_REPORT 0x16 +#define IGMP_TYPE_V1REPORT 0x12 +#define IGMP_TYPE_LEAVE_GROUP 0x17 + +#define IGMP_DEFAULT_MAX_RESPONSE_TIME 10 // 10 second default +#pragma pack() + +#define MAX_MCAST_GROUPS 8 // most we allow ourselves to join at once +#define MAX_OFFERS 16 + +typedef struct { + UINTN Signature; + EFI_LOCK Lock; + BOOLEAN ShowErrorMessages; + EFI_TCP_PROTOCOL Tcp; + EFI_PXE_BASE_CODE_PROTOCOL EfiBc; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *CallbackProtocolPtr; + EFI_HANDLE Handle; + + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiPtr; + EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetwork; + UINT8 *TransmitBufferPtr; + UINT8 *ReceiveBufferPtr; + EFI_PXE_BASE_CODE_FUNCTION Function; + + UINTN OldestArpEntry; + UINTN MCastGroupCount; + EFI_EVENT Igmpv1TimeoutEvent; + BOOLEAN UseIgmpv1Reporting; + EFI_EVENT IgmpGroupEvent[MAX_MCAST_GROUPS]; + UINT16 RandomPort; + +#if SUPPORT_IPV6 + // + // TBD + // +#else + UINT32 MCastGroup[MAX_MCAST_GROUPS]; +#endif + + BOOLEAN GoodStationIp; + BOOLEAN DidTransmit; + UINTN IpLength; + VOID *DhcpPacketBuffer; + UINTN FileSize; + VOID *BootServerReceiveBuffer; + EFI_IP_ADDRESS ServerIp; + + // + // work area + // for dhcp + // + VOID *ReceiveBuffers; + VOID *TransmitBuffer; + UINTN NumOffersReceived; + UINT16 TotalSeconds; + + // + // arrays for different types of offers + // + UINT8 ServerCount[4]; + UINT8 OfferCount[4][MAX_OFFERS]; + UINT8 GotBootp; + UINT8 GotProxy[4]; + UINT8 BinlProxies[MAX_OFFERS]; + + UINT8 *ArpBuffer; + UINT8 *TftpAckBuffer; + UINT8 *TftpErrorBuffer; + IGMPV2_MESSAGE IgmpMessage; + BOOLEAN BigBlkNumFlag; + UINT8 Timeout; + UINT16 RandomSeed; +} PXE_BASECODE_DEVICE; + +// +// type index +// +#define DHCP_ONLY_IX 0 +#define PXE10_IX 1 +#define WfM11a_IX 2 +#define BINL_IX 3 + +#define PXE_RND_PORT_LOW 2070 + +// +// +// +#define LOADFILE_DEVICE_SIGNATURE 'pxel' + +typedef struct { + UINTN Signature; + EFI_LOCK Lock; + EFI_LOAD_FILE_PROTOCOL LoadFile; + PXE_BASECODE_DEVICE *Private; +} LOADFILE_DEVICE; + +#define EFI_BASE_CODE_DEV_FROM_THIS(a) CR (a, PXE_BASECODE_DEVICE, efi_bc, PXE_BASECODE_DEVICE_SIGNATURE); + +#define EFI_BASE_CODE_DEV_FROM_TCP(a) CR (a, PXE_BASECODE_DEVICE, Tcp, PXE_BASECODE_DEVICE_SIGNATURE); + +#define EFI_LOAD_FILE_DEV_FROM_THIS(a) CR (a, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE) + +EFI_BIS_PROTOCOL * +PxebcBisStart ( + PXE_BASECODE_DEVICE *Private, + BIS_APPLICATION_HANDLE *BisAppHandle, + EFI_BIS_DATA **BisDataSigInfo + ) +; + +VOID +PxebcBisStop ( + EFI_BIS_PROTOCOL *Bis, + BIS_APPLICATION_HANDLE BisAppHandle, + EFI_BIS_DATA *BisDataSigInfo + ) +; + +BOOLEAN +PxebcBisVerify ( + PXE_BASECODE_DEVICE *Private, + VOID *FileBuffer, + UINTN FileBufferLength, + VOID *CredentialBuffer, + UINTN CredentialBufferLength + ) +; + +BOOLEAN +PxebcBisDetect ( + PXE_BASECODE_DEVICE *Private + ) +; + +// +// Global Variables +// +extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName; + +// +// ////////////////////////////////////////////////////////// +// +// prototypes +// + +/** + Initialize the base code drivers and install the driver binding + + Standard EFI Image Entry + + @retval EFI_SUCCESS This driver was successfully bound + +**/ +EFI_STATUS +EFIAPI +InitializeBCDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +; + +EFI_STATUS +EFIAPI +BcStart ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIpv6 + ) +; + +EFI_STATUS +EFIAPI +BcStop ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ) +; + +EFI_STATUS +EFIAPI +BcDhcp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ) +; + +EFI_STATUS +EFIAPI +BcDiscover ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO * Info OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcMtftp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSize, + IN UINTN *BlockSize OPTIONAL, + IN EFI_IP_ADDRESS * ServerIp, + IN UINT8 *Filename, + IN EFI_PXE_BASE_CODE_MTFTP_INFO * Info OPTIONAL, + IN BOOLEAN DontUseBuffer + ) +; + +EFI_STATUS +EFIAPI +BcUdpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL + IN EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcUdpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcTcpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN UINT16 *UrgentPointer, + IN UINT32 *SequenceNumber, + IN UINT32 *AckNumber, + IN UINT16 *HlenResCode, + IN UINT16 *Window, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_TCP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL + IN EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_TCP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcTcpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_TCP_PORT *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_TCP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +; + +EFI_STATUS +EFIAPI +BcArp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_IP_ADDRESS * IpAddr, + IN EFI_MAC_ADDRESS * MacAddr OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcIpFilter ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter + ) +; + +EFI_STATUS +EFIAPI +BcSetParameters ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN BOOLEAN *NewAutoArp, OPTIONAL + IN BOOLEAN *NewSendGUID, OPTIONAL + IN UINT8 *NewTTL, OPTIONAL + IN UINT8 *NewToS, OPTIONAL + IN BOOLEAN *NewMakeCallback OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcSetStationIP ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_IP_ADDRESS * NewStationIp, OPTIONAL + IN EFI_IP_ADDRESS * NewSubnetMask OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +BcSetPackets ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + BOOLEAN *NewDhcpDiscoverValid, OPTIONAL + BOOLEAN *NewDhcpAckReceived, OPTIONAL + BOOLEAN *NewProxyOfferReceived, OPTIONAL + BOOLEAN *NewPxeDiscoverValid, OPTIONAL + BOOLEAN *NewPxeReplyReceived, OPTIONAL + BOOLEAN *NewPxeBisReplyReceived, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpAck, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewProxyOffer, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeReply, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeBisReply OPTIONAL + ) +; + +EFI_STATUS +EFIAPI +LoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +; + +EFI_STATUS +PxeBcLibGetSmbiosSystemGuidAndSerialNumber ( + IN EFI_GUID *SystemGuid, + OUT CHAR8 **SystemSerialNumber + ) +; + +#ifdef EFI_SIZE_REDUCTION_APPLIED + #define COMPONENT_NAME_CODE(code) + #define COMPONENT_NAME NULL +#else + #define COMPONENT_NAME_CODE(code) code + #define COMPONENT_NAME &gPxeBcComponentName +#endif + + +// +// Define SMBIOS tables. +// +#pragma pack(1) +typedef struct { + UINT8 AnchorString[4]; + UINT8 EntryPointStructureChecksum; + UINT8 EntryPointLength; + UINT8 MajorVersion; + UINT8 MinorVersion; + UINT16 MaxStructureSize; + UINT8 EntryPointRevision; + UINT8 FormattedArea[5]; + UINT8 IntermediateAnchorString[5]; + UINT8 IntermediateChecksum; + UINT16 TableLength; + UINT32 TableAddress; + UINT16 NumberOfSmbiosStructures; + UINT8 SmbiosBcdRevision; +} SMBIOS_STRUCTURE_TABLE; + +// +// Please note that SMBIOS structures can be odd byte aligned since the +// unformated section of each record is a set of arbitrary size strings. +// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 Handle[2]; +} SMBIOS_HEADER; + +typedef UINT8 SMBIOS_STRING; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Vendor; + SMBIOS_STRING BiosVersion; + UINT8 BiosSegment[2]; + SMBIOS_STRING BiosReleaseDate; + UINT8 BiosSize; + UINT8 BiosCharacteristics[8]; +} SMBIOS_TYPE0; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Manufacturer; + SMBIOS_STRING ProductName; + SMBIOS_STRING Version; + SMBIOS_STRING SerialNumber; + + // + // always byte copy this data to prevent alignment faults! + // + EFI_GUID Uuid; + + UINT8 WakeUpType; +} SMBIOS_TYPE1; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Manufacturer; + SMBIOS_STRING ProductName; + SMBIOS_STRING Version; + SMBIOS_STRING SerialNumber; +} SMBIOS_TYPE2; + +typedef struct { + SMBIOS_HEADER Hdr; + SMBIOS_STRING Manufacturer; + UINT8 Type; + SMBIOS_STRING Version; + SMBIOS_STRING SerialNumber; + SMBIOS_STRING AssetTag; + UINT8 BootupState; + UINT8 PowerSupplyState; + UINT8 ThermalState; + UINT8 SecurityStatus; + UINT8 OemDefined[4]; +} SMBIOS_TYPE3; + +typedef struct { + SMBIOS_HEADER Hdr; + UINT8 Socket; + UINT8 ProcessorType; + UINT8 ProcessorFamily; + SMBIOS_STRING ProcessorManufacture; + UINT8 ProcessorId[8]; + SMBIOS_STRING ProcessorVersion; + UINT8 Voltage; + UINT8 ExternalClock[2]; + UINT8 MaxSpeed[2]; + UINT8 CurrentSpeed[2]; + UINT8 Status; + UINT8 ProcessorUpgrade; + UINT8 L1CacheHandle[2]; + UINT8 L2CacheHandle[2]; + UINT8 L3CacheHandle[2]; +} SMBIOS_TYPE4; + +typedef union { + SMBIOS_HEADER *Hdr; + SMBIOS_TYPE0 *Type0; + SMBIOS_TYPE1 *Type1; + SMBIOS_TYPE2 *Type2; + SMBIOS_TYPE3 *Type3; + SMBIOS_TYPE4 *Type4; + UINT8 *Raw; +} SMBIOS_STRUCTURE_POINTER; +#pragma pack() + +#include "ip.h" +#include "dhcp.h" +#include "tftp.h" + +#endif /* _BC_H */ + +/* EOF - bc.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c b/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c new file mode 100644 index 0000000000..18dca38408 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/ComponentName.c @@ -0,0 +1,160 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + ComponentName.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = { + PxeBcComponentNameGetDriverName, + PxeBcComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = { + { + "eng", + L"PXE Base Code Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCESS - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gPxeBcComponentName.SupportedLanguages, + mPxeBcDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h new file mode 100644 index 0000000000..36f71f775a --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Dhcp.h @@ -0,0 +1,632 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef _DHCP_H +#define _DHCP_H + +// +// Definitions for DHCP version 4 UDP packet. +// The field names in this structure are defined and described in RFC 2131. +// +#pragma pack(1) + +typedef struct { + UINT8 op; +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + + UINT8 htype; + UINT8 hlen; + UINT8 hops; + UINT32 xid; + UINT16 secs; + UINT16 flags; +#define DHCP_BROADCAST_FLAG 0x8000 + + UINT32 ciaddr; + UINT32 yiaddr; + UINT32 siaddr; + UINT32 giaddr; + UINT8 chaddr[16]; + UINT8 sname[64]; + UINT8 file[128]; + UINT8 options[312]; +#define OP_PAD 0 +#define OP_END 255 +#define OP_SUBNET_MASK 1 +#define OP_TIME_OFFSET 2 +#define OP_ROUTER_LIST 3 +#define OP_TIME_SERVERS 4 +#define OP_NAME_SERVERS 5 +#define OP_DNS_SERVERS 6 +#define OP_LOG_SERVERS 7 +#define OP_COOKIE_SERVERS 8 +#define OP_LPR_SREVERS 9 +#define OP_IMPRESS_SERVERS 10 +#define OP_RES_LOC_SERVERS 11 +#define OP_HOST_NAME 12 +#define OP_BOOT_FILE_SZ 13 +#define OP_DUMP_FILE 14 +#define OP_DOMAIN_NAME 15 +#define OP_SWAP_SERVER 16 +#define OP_ROOT_PATH 17 +#define OP_EXTENSION_PATH 18 +#define OP_IP_FORWARDING 19 +#define OP_NON_LOCAL_SRC_RTE 20 +#define OP_POLICY_FILTER 21 +#define OP_MAX_DATAGRAM_SZ 22 +#define OP_DEFAULT_TTL 23 +#define OP_MTU_AGING_TIMEOUT 24 +#define OP_MTU_SIZES 25 +#define OP_MTU_TO_USE 26 +#define OP_ALL_SUBNETS_LOCAL 27 +#define OP_BROADCAST_ADD 28 +#define OP_PERFORM_MASK_DISCOVERY 29 +#define OP_RESPOND_TO_MASK_REQ 30 +#define OP_PERFORM_ROUTER_DISCOVERY 31 +#define OP_ROUTER_SOLICIT_ADDRESS 32 +#define OP_STATIC_ROUTER_LIST 33 +#define OP_USE_ARP_TRAILERS 34 +#define OP_ARP_CACHE_TIMEOUT 35 +#define OP_ETHERNET_ENCAPSULATION 36 +#define OP_TCP_DEFAULT_TTL 37 +#define OP_TCP_KEEP_ALIVE_INT 38 +#define OP_KEEP_ALIVE_GARBAGE 39 +#define OP_NIS_DOMAIN_NAME 40 +#define OP_NIS_SERVERS 41 +#define OP_NTP_SERVERS 42 +#define OP_VENDOR_SPECIFIC 43 +#define VEND_PXE_MTFTP_IP 1 +#define VEND_PXE_MTFTP_CPORT 2 +#define VEND_PXE_MTFTP_SPORT 3 +#define VEND_PXE_MTFTP_TMOUT 4 +#define VEND_PXE_MTFTP_DELAY 5 +#define VEND_PXE_DISCOVERY_CONTROL 6 +#define PXE_DISABLE_BROADCAST_DISCOVERY (1 << 0) +#define PXE_DISABLE_MULTICAST_DISCOVERY (1 << 1) +#define PXE_ACCEPT_ONLY_PXE_BOOT_SERVERS (1 << 2) +#define PXE_DO_NOT_PROMPT (1 << 3) +#define VEND_PXE_DISCOVERY_MCAST_ADDR 7 +#define VEND_PXE_BOOT_SERVERS 8 +#define VEND_PXE_BOOT_MENU 9 +#define VEND_PXE_BOOT_PROMPT 10 +#define VEND_PXE_MCAST_ADDRS_ALLOC 11 +#define VEND_PXE_CREDENTIAL_TYPES 12 +#define VEND_PXE_BOOT_ITEM 71 +#define OP_NBNS_SERVERS 44 +#define OP_NBDD_SERVERS 45 +#define OP_NETBIOS_NODE_TYPE 46 +#define OP_NETBIOS_SCOPE 47 +#define OP_XWINDOW_SYSTEM_FONT_SERVERS 48 +#define OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49 +#define OP_DHCP_REQ_IP_ADD 50 +#define OP_DHCP_LEASE_TIME 51 +#define OP_DHCP_OPTION_OVERLOAD 52 +#define OVLD_FILE 1 +#define OVLD_SRVR_NAME 2 +#define OP_DHCP_MESSAGE_TYPE 53 +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 +#define OP_DHCP_SERVER_IP 54 +#define OP_DHCP_PARM_REQ_LIST 55 +#define OP_DHCP_ERROR_MESSAGE 56 +#define OP_DHCP_MAX_MESSAGE_SZ 57 +#define OP_DHCP_RENEWAL_TIME 58 +#define OP_DHCP_REBINDING_TIME 59 +#define OP_DHCP_CLASS_IDENTIFIER 60 +#define OP_DHCP_CLIENT_IDENTIFIER 61 +#define OP_NISPLUS_DOMAIN_NAME 64 +#define OP_NISPLUS_SERVERS 65 +#define OP_DHCP_TFTP_SERVER_NAME 66 +#define OP_DHCP_BOOTFILE 67 +#define OP_MOBILE_IP_HOME_AGENTS 68 +#define OP_SMPT_SERVERS 69 +#define OP_POP3_SERVERS 70 +#define OP_NNTP_SERVERS 71 +#define OP_WWW_SERVERS 72 +#define OP_FINGER_SERVERS 73 +#define OP_IRC_SERVERS 74 +#define OP_STREET_TALK_SERVERS 75 +#define OP_STREET_TALK_DIR_ASSIST_SERVERS 76 +#define OP_NDS_SERVERS 85 +#define OP_NDS_TREE_NAME 86 +#define OP_NDS_CONTEXT 87 +#define OP_DHCP_SYSTEM_ARCH 93 +#define OP_DHCP_NETWORK_ARCH 94 +#define OP_DHCP_PLATFORM_ID 97 +} DHCPV4_STRUCT; + +// +// DHCPv4 option header +// +typedef struct { + UINT8 OpCode; + UINT8 Length; + // + // followed by Data[] + // +} DHCPV4_OP_HEADER; + +// +// Generic DHCPv4 option (header followed by data) +// +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Data[1]; +} DHCPV4_OP_STRUCT; + +// +// Maximum DHCP packet size on ethernet +// +#define MAX_DHCP_MSG_SZ (MAX_ENET_DATA_SIZE - sizeof (IPV4_HEADER) - sizeof (UDPV4_HEADER)) + +// +// Macros used in pxe_bc_dhcp.c and pxe_loadfile.c +// +#define DHCPV4_TRANSMIT_BUFFER (*(DHCPV4_STRUCT *) (Private->TransmitBuffer)) +#define DHCPV4_OPTIONS_BUFFER (*(struct optionsstr *) DHCPV4_TRANSMIT_BUFFER.options) + +#define DHCPV4_ACK_INDEX 0 +#define PXE_BINL_INDEX 1 +#define PXE_OFFER_INDEX 1 +#define PXE_ACK_INDEX 2 +#define PXE_BIS_INDEX 3 + +#define DHCPV4_ACK_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[DHCPV4_ACK_INDEX] +#define PXE_BINL_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_BINL_INDEX] +#define PXE_OFFER_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_OFFER_INDEX] +#define PXE_ACK_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_ACK_INDEX] +#define PXE_BIS_BUFFER ((struct DhcpReceiveBufferStruct *) Private->DhcpPacketBuffer)[PXE_BIS_INDEX] + +#define DHCPV4_ACK_PACKET DHCPV4_ACK_BUFFER.u.Dhcpv4 +#define PXE_BINL_PACKET PXE_BINL_BUFFER.u.Dhcpv4 +#define PXE_OFFER_PACKET PXE_OFFER_BUFFER.u.Dhcpv4 +#define PXE_ACK_PACKET PXE_ACK_BUFFER.u.Dhcpv4 +#define PXE_BIS_PACKET PXE_BIS_BUFFER.u.Dhcpv4 + +// +// network structure definitions +// +// +// some option definitions +// +#define DHCPV4_OPTION_LENGTH(type) (sizeof (type) - sizeof (DHCPV4_OP_HEADER)) + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Type; +} DHCPV4_OP_MESSAGE_TYPE; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Overload; +} DHCPV4_OP_OVERLOAD; + +// +// boot server list structure +// one or more contained in a pxe boot servers structure +// +typedef struct { + UINT8 IpCount; + EFI_IPv4_ADDRESS IpList[1]; // IP count of IPs +} PXEV4_SERVER_LIST; + +typedef struct { + UINT8 IpCount; + EFI_IPv6_ADDRESS IpList[1]; // IP count of IPs +} PXEV6_SERVER_LIST; + +typedef union { + PXEV4_SERVER_LIST Ipv4List; + PXEV6_SERVER_LIST Ipv6List; +} PXE_SERVER_LISTS; + +typedef struct { + UINT16 Type; + PXE_SERVER_LISTS u; +} PXE_SERVER_LIST; + +// +// pxe boot servers structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + PXE_SERVER_LIST ServerList[1]; // one or more +} PXE_OP_SERVER_LIST; + +// +// pxe boot item structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + UINT16 Type; + UINT16 Layer; +} PXE_OP_BOOT_ITEM; + +// +// pxe boot menu item structure +// +typedef struct { + UINT16 Type; + UINT8 DataLen; + UINT8 Data[1]; +} PXE_BOOT_MENU_ENTRY; + +// +// pxe boot menu structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + PXE_BOOT_MENU_ENTRY MenuItem[1]; +} PXE_OP_BOOT_MENU; + +// +// pxe boot prompt structure +// +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Timeout; + UINT8 Prompt[1]; +} PXE_OP_BOOT_PROMPT; + +#define PXE_BOOT_PROMPT_AUTO_SELECT 0 +#define PXE_BOOT_PROMPT_NO_TIMEOUT 255 + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Class[1]; +} DHCPV4_OP_CLASS; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 File[1]; +} DHCPV4_OP_BOOTFILE; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 VendorOptions[1]; +} DHCPV4_OP_VENDOR_OPTIONS; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 MaxSize[2]; +} DHCPV4_OP_MAX_MESSAGE_SIZE; + +typedef struct { + UINT8 _OP_SUBNET_MASK; /* 1 */ + UINT8 _OP_TIME_OFFSET; /* 2 */ + UINT8 _OP_ROUTER_LIST; /* 3 */ + UINT8 _OP_TIME_SERVERS; /* 4 */ + UINT8 _OP_NAME_SERVERS; /* 5 */ + UINT8 _OP_DNS_SERVERS; /* 6 */ + UINT8 _OP_HOST_NAME; /* 12 */ + UINT8 _OP_BOOT_FILE_SZ; /* 13 */ + UINT8 _OP_DOMAIN_NAME; /* 15 */ + UINT8 _OP_ROOT_PATH; /* 17 */ + UINT8 _OP_EXTENSION_PATH; /* 18 */ + UINT8 _OP_MAX_DATAGRAM_SZ; /* 22 */ + UINT8 _OP_DEFAULT_TTL; /* 23 */ + UINT8 _OP_BROADCAST_ADD; /* 28 */ + UINT8 _OP_NIS_DOMAIN_NAME; /* 40 */ + UINT8 _OP_NIS_SERVERS; /* 41 */ + UINT8 _OP_NTP_SERVERS; /* 42 */ + UINT8 _OP_VENDOR_SPECIFIC; /* 43 */ + UINT8 _OP_DHCP_REQ_IP_ADD; /* 50 */ + UINT8 _OP_DHCP_LEASE_TIME; /* 51 */ + UINT8 _OP_DHCP_SERVER_IP; /* 54 */ + UINT8 _OP_DHCP_RENEWAL_TIME; /* 58 */ + UINT8 _OP_DHCP_REBINDING_TIME; /* 59 */ + UINT8 _OP_DHCP_CLASS_IDENTIFIER; /* 60 */ + UINT8 _OP_DHCP_TFTP_SERVER_NAME; /* 66 */ + UINT8 _OP_DHCP_BOOTFILE; /* 67 */ + UINT8 _OP_DHCP_PLATFORM_ID; /* 97 */ + UINT8 VendorOption128; // vendor option 128 + UINT8 VendorOption129; // vendor option 129 + UINT8 VendorOption130; // vendor option 130 + UINT8 VendorOption131; // vendor option 131 + UINT8 VendorOption132; // vendor option 132 + UINT8 VendorOption133; // vendor option 133 + UINT8 VendorOption134; // vendor option 134 + UINT8 VendorOption135; // vendor option 135 +} DHCPV4_REQUESTED_OPTIONS_DATA; + +typedef struct { + DHCPV4_OP_HEADER Header; + DHCPV4_REQUESTED_OPTIONS_DATA Data; +} DHCPV4_OP_REQUESTED_OPTIONS; + +typedef struct opipstr { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_IP_ADDRESS; + +// +// ip list structure - e.g. router list +// +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS IpList[1]; +} DHCPV4_OP_IP_LIST; + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Type; + UINT8 Guid[sizeof (EFI_GUID)]; +} DHCPV4_OP_CLIENT_ID; + +// +// special options start - someday obsolete ??? +// +#define DHCPV4_OP_PLATFORM_ID DHCPV4_OP_CLIENT_ID + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT8 Type; // SNP = 2 + UINT8 MajorVersion; + UINT8 MinorVersion; +} DHCPV4_OP_NETWORK_INTERFACE; + +#define UNDI_TYPE 1 +#define SNP_TYPE 2 + +typedef struct { + DHCPV4_OP_HEADER Header; + UINT16 Type; +} DHCPV4_OP_ARCHITECTURE_TYPE; +// +// special options end - someday obsolete ??? +// +typedef struct { + UINT8 ClassIdentifier[10]; // PXEClient: + UINT8 Lit2[5]; // Arch: + UINT8 ArchitectureType[5]; // 00000 - 65536 + UINT8 Lit3[1]; // : + UINT8 InterfaceName[4]; // e.g. UNDI + UINT8 Lit4[1]; // : + UINT8 UndiMajor[3]; // 000 - 255 + UINT8 UndiMinor[3]; // 000 - 255 +} DHCPV4_CLASS_ID_DATA; + +typedef struct { + DHCPV4_OP_HEADER Header; + DHCPV4_CLASS_ID_DATA Data; +} DHCPV4_OP_CLASS_ID; + +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_REQUESTED_IP; + +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_SERVER_IP; + +typedef struct { + DHCPV4_OP_HEADER Header; + EFI_IPv4_ADDRESS Ip; +} DHCPV4_OP_SUBNET_MASK; + +typedef struct { // oppxedisctlstr { + DHCPV4_OP_HEADER Header; + UINT8 ControlBits; +} PXE_OP_DISCOVERY_CONTROL; + +#define DISABLE_BCAST (1 << 0) +#define DISABLE_MCAST (1 << 1) +#define USE_ACCEPT_LIST (1 << 2) +#define USE_BOOTFILE (1 << 3) + +#pragma pack() +// +// definitions of indices to populate option interest array +// +#define VEND_PXE_MTFTP_IP_IX 1 // multicast IP address of bootfile for MTFTP listen +#define VEND_PXE_MTFTP_CPORT_IX 2 // UDP Port to monitor for MTFTP responses - Intel order +#define VEND_PXE_MTFTP_SPORT_IX 3 // Server UDP Port for MTFTP open - Intel order +#define VEND_PXE_MTFTP_TMOUT_IX 4 // Listen timeout - secs +#define VEND_PXE_MTFTP_DELAY_IX 5 // Transmission timeout - secs +#define VEND_PXE_DISCOVERY_CONTROL_IX 6 // bit field +#define VEND_PXE_DISCOVERY_MCAST_ADDR_IX 7 // boot server discovery multicast address +#define VEND_PXE_BOOT_SERVERS_IX 8 // list of boot servers of form tp(2) cnt(1) ips[cnt] +#define VEND_PXE_BOOT_MENU_IX 9 +#define VEND_PXE_BOOT_PROMPT_IX 10 +#define VEND_PXE_MCAST_ADDRS_ALLOC_IX 0 // not used by PXE client +#define VEND_PXE_CREDENTIAL_TYPES_IX 11 +#define VEND_13_IX 0 // not used by PXE client +#define VEND_14_IX 0 // not used by PXE client +#define VEND_15_IX 0 // not used by PXE client +#define VEND_16_IX 0 // not used by PXE client +#define VEND_17_IX 0 // not used by PXE client +#define VEND_18_IX 0 // not used by PXE client +#define VEND_19_IX 0 // not used by PXE client +#define VEND_20_IX 0 // not used by PXE client +#define VEND_21_IX 0 // not used by PXE client +#define VEND_22_IX 0 // not used by PXE client +#define VEND_23_IX 0 // not used by PXE client +#define VEND_24_IX 0 // not used by PXE client +#define VEND_25_IX 0 // not used by PXE client +#define VEND_26_IX 0 // not used by PXE client +#define VEND_27_IX 0 // not used by PXE client +#define VEND_28_IX 0 // not used by PXE client +#define VEND_29_IX 0 // not used by PXE client +#define VEND_30_IX 0 // not used by PXE client +#define VEND_31_IX 0 // not used by PXE client +#define VEND_32_IX 0 // not used by PXE client +#define VEND_33_IX 0 // not used by PXE client +#define VEND_34_IX 0 // not used by PXE client +#define VEND_35_IX 0 // not used by PXE client +#define VEND_36_IX 0 // not used by PXE client +#define VEND_37_IX 0 // not used by PXE client +#define VEND_38_IX 0 // not used by PXE client +#define VEND_39_IX 0 // not used by PXE client +#define VEND_40_IX 0 // not used by PXE client +#define VEND_41_IX 0 // not used by PXE client +#define VEND_42_IX 0 // not used by PXE client +#define VEND_43_IX 0 // not used by PXE client +#define VEND_44_IX 0 // not used by PXE client +#define VEND_45_IX 0 // not used by PXE client +#define VEND_46_IX 0 // not used by PXE client +#define VEND_47_IX 0 // not used by PXE client +#define VEND_48_IX 0 // not used by PXE client +#define VEND_49_IX 0 // not used by PXE client +#define VEND_50_IX 0 // not used by PXE client +#define VEND_51_IX 0 // not used by PXE client +#define VEND_52_IX 0 // not used by PXE client +#define VEND_53_IX 0 // not used by PXE client +#define VEND_54_IX 0 // not used by PXE client +#define VEND_55_IX 0 // not used by PXE client +#define VEND_56_IX 0 // not used by PXE client +#define VEND_57_IX 0 // not used by PXE client +#define VEND_58_IX 0 // not used by PXE client +#define VEND_59_IX 0 // not used by PXE client +#define VEND_60_IX 0 // not used by PXE client +#define VEND_61_IX 0 // not used by PXE client +#define VEND_62_IX 0 // not used by PXE client +#define VEND_63_IX 0 // not used by PXE client +#define VEND_64_IX 0 // not used by PXE client +#define VEND_65_IX 0 // not used by PXE client +#define VEND_66_IX 0 // not used by PXE client +#define VEND_67_IX 0 // not used by PXE client +#define VEND_68_IX 0 // not used by PXE client +#define VEND_69_IX 0 // not used by PXE client +#define VEND_70_IX 0 // not used by PXE client +#define VEND_PXE_BOOT_ITEM_IX 12 + +#define MAX_OUR_PXE_OPT VEND_PXE_BOOT_ITEM // largest PXE option in which we are interested +#define MAX_OUR_PXE_IX VEND_PXE_BOOT_ITEM_IX // largest PXE option index +// +// define various types by options that are sent +// +#define WfM11a_OPTS ((1<> 8) & 0x00ff)) + +#define HTONL(v) \ + (UINT32) ((((v) << 24) & 0xff000000) + (((v) << 8) & 0x00ff0000) + (((v) >> 8) & 0x0000ff00) + (((v) >> 24) & 0x000000ff)) + +#define HTONLL(v) swap64 (v) + +#define U8PTR(na) ((UINT8 *) &(na)) + +#define NTOHS(ns) ((UINT16) (((*U8PTR (ns)) << 8) +*(U8PTR (ns) + 1))) + +#define NTOHL(ns) \ + ((UINT32) (((*U8PTR (ns)) << 24) + ((*(U8PTR (ns) + 1)) << 16) + ((*(U8PTR (ns) + 2)) << 8) +*(U8PTR (ns) + 3))) + +#endif /* _HTON_H_ */ + +/* EOF - hton.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h new file mode 100644 index 0000000000..cc878d8dd7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ia32/PxeArch.h @@ -0,0 +1,26 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeArch.h + +Abstract: + Defines PXE Arch type + + +**/ + +#ifndef _EFI_PXE_ARCH_H_ +#define _EFI_PXE_ARCH_H_ + +#define SYS_ARCH 0x6 + +#endif diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h new file mode 100644 index 0000000000..a482eb9cd9 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ip.h @@ -0,0 +1,736 @@ +/** @file + +Copyright (c) 2004 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef _IP_H_ +#define _IP_H_ + +#include "hton.h" + +// +// portability macros +// +#define UDP_FILTER_MASK (EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT | \ + EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER \ + ) + +#define PXE_BOOT_LAYER_MASK 0x7FFF +#define PXE_BOOT_LAYER_INITIAL 0x0000 +#define PXE_BOOT_LAYER_CREDENTIAL_FLAG 0x8000 +#define MAX_BOOT_SERVERS 32 + +// +// macro to evaluate IP address as TRUE if it is a multicast IP address +// +#define IS_MULTICAST(ptr) ((*((UINT8 *) ptr) & 0xf0) == 0xe0) + +// +// length macros +// +#define IP_ADDRESS_LENGTH(qp) (((qp)->UsingIpv6) ? sizeof (EFI_IPv6_ADDRESS) : sizeof (EFI_IPv4_ADDRESS)) + +#define MAX_FRAME_DATA_SIZE 1488 +#define ALLOCATE_SIZE(X) (((X) + 7) & 0xfff8) +#define MODE_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_MODE)) +#define BUFFER_ALLOCATE_SIZE (8192 + 512) +#define ROUTER_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY) * PXE_ROUTER_TABLE_SIZE)) +#define ARP_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_PXE_BASE_CODE_ARP_ENTRY) * PXE_ARP_CACHE_SIZE)) +#define FILTER_ALLOCATE_SIZE ALLOCATE_SIZE ((sizeof (EFI_IP_ADDRESS) * PXE_IP_FILTER_SIZE)) +#define PXE_ARP_CACHE_SIZE 8 +#define PXE_ROUTER_TABLE_SIZE 8 +#define PXE_IP_FILTER_SIZE 8 +#define ICMP_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) +#define TFTP_ERR_ALLOCATE_SIZE ALLOCATE_SIZE (sizeof (EFI_PXE_BASE_CODE_TFTP_ERROR)) + +// +// DHCP discover/request packets are sent to this UDP port. ProxyDHCP +// servers listen on this port for DHCP discover packets that have a +// class identifier (option 60) with 'PXEClient' in the first 9 bytes. +// Bootservers also listen on this port for PXE broadcast discover +// requests from PXE clients. +// +#define DHCP_SERVER_PORT 67 + +// +// When DHCP, proxyDHCP and Bootservers respond to DHCP and PXE broadcast +// discover requests by broadcasting the reply packet, the packet is +// broadcast to this port. +// +#define DHCP_CLIENT_PORT 68 + +// +// TFTP servers listen for TFTP open requests on this port. +// +#define TFTP_OPEN_PORT 69 + +// +// proxyDHCP and Bootservers listen on this port for a PXE unicast and/or +// multicast discover requests from PXE clients. A PXE discover request +// looks like a DHCP discover or DHCP request packet. +// +#define PXE_DISCOVERY_PORT 4011 + +// +// This port is used by the PXE client/server protocol tests. +// +#define PXE_PORT_PXETEST_PORT 0x8080 + +// +// Definitions for Ethertype protocol numbers and interface types +// Per RFC 1700, +// +#define PXE_PROTOCOL_ETHERNET_IP 0x0800 +#define PXE_PROTOCOL_ETHERNET_ARP 0x0806 +#define PXE_PROTOCOL_ETHERNET_RARP 0x8035 + +#define PXE_IFTYPE_ETHERNET 0x01 +#define PXE_IFTYPE_TOKENRING 0x04 +#define PXE_IFTYPE_FIBRE_CHANNEL 0x12 + +// +// Definitions for internet protocol version 4 header +// Per RFC 791, September 1981. +// +#define IPVER4 4 + +#pragma pack(1) // make network structures packed byte alignment +typedef union { + UINT8 B[4]; + UINT32 L; +} IPV4_ADDR; + +#define IPV4_HEADER_LENGTH(IpHeaderPtr) (((IpHeaderPtr)->VersionIhl & 0xf) << 2) + +#define SET_IPV4_VER_HDL(IpHeaderPtr, IpHeaderLen) { \ + (IpHeaderPtr)->VersionIhl = (UINT8) ((IPVER4 << 4) | ((IpHeaderLen) >> 2)); \ + } + +typedef struct { + UINT8 VersionIhl; + UINT8 TypeOfService; + UINT16 TotalLength; + UINT16 Id; + UINT16 FragmentFields; + UINT8 TimeToLive; + UINT8 Protocol; + UINT16 HeaderChecksum; + IPV4_ADDR SrcAddr; + IPV4_ADDR DestAddr; + // + // options are not implemented + // +} IPV4_HEADER; + +#define IP_FRAG_RSVD 0x8000 // reserved bit - must be zero +#define IP_NO_FRAG 0x4000 // do not fragment bit +#define IP_MORE_FRAG 0x2000 // not last fragment +#define IP_FRAG_OFF_MSK 0x1fff // fragment offset in 8 byte chunks +#define DEFAULT_RFC_TTL 64 + +#define PROT_ICMP 1 +#define PROT_IGMP 2 +#define PROT_TCP 6 +#define PROT_UDP 17 + +/* + * Definitions for internet control message protocol version 4 message + * structure. Per RFC 792, September 1981. + */ + +// +// icmp header for all icmp messages +// +typedef struct { + UINT8 Type; // message type + UINT8 Code; // type specific - 0 for types we implement + UINT16 Checksum; // ones complement of ones complement sum of 16 bit words of message +} ICMPV4_HEADER; + +#define ICMP_DEST_UNREACHABLE 3 +#define ICMP_SOURCE_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO 8 +#define ICMP_ECHO_REPLY 0 +#define ICMP_ROUTER_ADV 9 +#define ICMP_ROUTER_SOLICIT 10 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 +#define ICMP_TIMESTAMP 13 +#define ICMP_TIMESTAMP_REPLY 14 +#define ICMP_INFO_REQ 15 +#define ICMP_INFO_REQ_REPLY 16 +#define ICMP_SUBNET_MASK_REQ 17 +#define ICMP_SUBNET_MASK_REPLY 18 +// +// other ICMP message types ignored in this implementation +// +// icmp general messages +// +typedef struct { + ICMPV4_HEADER Header; + // + // generally unused except byte [0] for + // parameter problem message + // + UINT8 GenerallyUnused[4]; + // + // original message ip header of plus 64 + // bits of data + // + IPV4_HEADER IpHeader; +} ICMPV4_GENERAL_MESSAGE; + +// +// icmp req/rply message header +// +typedef struct { + ICMPV4_HEADER Header; + UINT16 Id; + UINT16 SequenceNumber; +} ICMPV4_REQUEST_REPLY_HEADER; + +// +// icmp echo message +// +typedef struct { + ICMPV4_REQUEST_REPLY_HEADER Header; + UINT8 EchoData[1]; // variable length data to be echoed +} ICMPV4_ECHO_MESSAGE; + +// +// icmp timestamp message - times are milliseconds since midnight UT - +// if non std, set high order bit +// +typedef struct { + ICMPV4_REQUEST_REPLY_HEADER Header; + UINT32 OriginalTime; // originating timestamp + UINT32 ReceiveTime; // receiving timestamp + UINT32 TransmitTime; // transmitting timestamp +} ICMPV4_TIMESTAMP_MESSAGE; + +// +// icmp info request structure - fill in source and dest net ip address on reply +// +typedef struct { + ICMPV4_REQUEST_REPLY_HEADER Header; +} ICMPV4_INFO_MESSAGE; + +// +// Definitions for internet control message protocol version 4 message structure +// Router discovery +// Per RFC 1256, September 1991. +// +// +// icmp router advertisement message +// +typedef struct { + ICMPV4_HEADER Header; + UINT8 NumberEntries; // number of address entries + UINT8 EntrySize; // number of 32 bit words per address entry + UINT16 Lifetime; // seconds to consider info valid + UINT32 RouterIp; + UINT32 Preferance; +} ICMPV4_ROUTER_ADVERTISE_MESSAGE; + +// +// icmp router solicitation message +// +typedef struct { + ICMPV4_HEADER Header; + UINT32 Reserved; +} ICMPV4_ROUTER_SOLICIT_MESSAGE; + +#define MAX_SOLICITATION_DELAY 1 // 1 second +#define SOLICITATION_INTERVAL 3 // 3 seconds +#define MAX_SOLICITATIONS 3 // 3 transmissions +#define V1ROUTER_PRESENT_TIMEOUT 400 // 400 second timeout until v2 reports can be sent +#define UNSOLICITED_REPORT_INTERVAL 10 // 10 seconds between unsolicited reports +#define BROADCAST_IPv4 0xffffffff + +// +// Definitions for address resolution protocol message structure +// Per RFC 826, November 1982 +// +typedef struct { + UINT16 HwType; // hardware type - e.g. ethernet (1) + UINT16 ProtType; // protocol type - for ethernet, 0x800 for IP + UINT8 HwAddLen; // byte length of a hardware address (e.g. 6 for ethernet) + UINT8 ProtAddLen; // byte length of a protocol address (e.g. 4 for ipv4) + UINT16 OpCode; + // + // source and dest hw and prot addresses follow - see example below + // +} ARP_HEADER; + +#define ETHERNET_ADD_SPC 1 + +#define ETHER_TYPE_IP 0x800 + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +// +// generic ARP packet +// +typedef struct { + ARP_HEADER ArpHeader; + EFI_MAC_ADDRESS SrcHardwareAddr; + EFI_IP_ADDRESS SrcProtocolAddr; + EFI_MAC_ADDRESS DestHardwareAddr; + EFI_IP_ADDRESS DestProtocolAddr; +} ARP_PACKET; + +#define ENET_HWADDLEN 6 +#define IPV4_PROTADDLEN 4 + +// +// Definitions for user datagram protocol version 4 pseudo header & header +// Per RFC 768, 28 August 1980 +// +typedef struct { + IPV4_ADDR SrcAddr; // source ip address + IPV4_ADDR DestAddr; // dest ip address + UINT8 Zero; // 0 + UINT8 Protocol; // protocol + UINT16 TotalLength; // UDP length - sizeof udpv4hdr + data length +} UDPV4_PSEUDO_HEADER; + +typedef struct { + UINT16 SrcPort; // source port identifier + UINT16 DestPort; // destination port identifier + UINT16 TotalLength; // total length header plus data + // + // ones complement of ones complement sum of 16 bit + // words of pseudo header, UDP header, and data + // zero checksum is transmitted as -0 (ones comp) + // zero transmitted means checksum not computed + // data follows + // + UINT16 Checksum; +} UDPV4_HEADER; + +typedef struct { + UDPV4_PSEUDO_HEADER Udpv4PseudoHeader; + UDPV4_HEADER Udpv4Header; +} UDPV4_HEADERS; + +// +// Definitions for transmission control protocol header +// Per RFC 793, September, 1981 +// +typedef struct { + IPV4_ADDR SrcAddr; // source ip address + IPV4_ADDR DestAddr; // dest ip address + UINT8 Zero; // 0 + UINT8 Protocol; // protocol + UINT16 TotalLength; // TCP length - TCP header length + data length +} TCPV4_PSEUDO_HEADER; + +typedef struct { + UINT16 SrcPort; // source port identifier + UINT16 DestPort; // destination port identifier + UINT32 SeqNumber; // Sequence number + UINT32 AckNumber; // Acknowledgement Number + // + // Nibble of HLEN (length of header in 32-bit multiples) + // 6bits of RESERVED + // Nibble of Code Bits + // + UINT16 HlenResCode; + UINT16 Window; // Software buffer size (sliding window size) in network-standard byte order + // + // ones complement of ones complement sum of 16 bit words of + // pseudo header, TCP header, and data + // zero checksum is transmitted as -0 (ones comp) + // zero transmitted means checksum not computed + // + UINT16 Checksum; + UINT16 UrgentPointer; // pointer to urgent data (allows sender to specify urgent data) +} TCPV4_HEADER; + +typedef struct { + TCPV4_PSEUDO_HEADER Tcpv4PseudoHeader; + TCPV4_HEADER Tcpv4Header; +} TCPV4_HEADERS; + +typedef struct { + UINT8 Kind; // one of the following: + UINT8 Length; // total option length including Kind and Lth + UINT8 Data[1]; // length = Lth - 2 +} TCPV4_OPTION; + +#define TCP_OP_END 0 // only used to pad to end of TCP header +#define TCP_NOP 1 // optional - may be used to pad between options to get alignment +#define TCP_MAX_SEG 2 // maximum receive segment size - only send at initial connection request +#define MAX_MEDIA_HDR_SIZE 64 +#define MIN_ENET_DATA_SIZE 64 +#define MAX_ENET_DATA_SIZE 1500 // temp def - make a network based var +#define MAX_IPV4_PKT_SIZE 65535 // maximum IP packet size +#define MAX_IPV4_DATA_SIZE (MAX_IPV4_PKT_SIZE - sizeof (IPV4_HEADER)) +#define MAX_IPV4_FRAME_DATA_SIZE (MAX_FRAME_DATA_SIZE - sizeof (IPV4_HEADER)) +#define REAS_IPV4_PKT_SIZE 576 // minimum IP packet size all IP host can handle +#define REAS_IPV4_DATA_SIZE (REAS_IPV4_PKT_SIZE - sizeof (IPV4_HEADER)) + +// +// +// +typedef union { + UINT8 Data[MAX_ENET_DATA_SIZE]; + ICMPV4_HEADER IcmpHeader; + IGMPV2_MESSAGE IgmpMessage; + struct { + UDPV4_HEADER UdpHeader; + UINT8 Data[1]; + } Udp; + struct { + TCPV4_HEADER TcpHeader; + UINT8 Data[1]; + } Tcp; +} PROTOCOL_UNION; + +// +// out buffer structure +// +typedef struct { + UINT8 MediaHeader[MAX_MEDIA_HDR_SIZE]; + IPV4_HEADER IpHeader; + // + // following union placement only valid if no option IP header + // + PROTOCOL_UNION u; +} IPV4_BUFFER; + +typedef struct { + IPV4_HEADER IpHeader; + // + // following union placement only valid if no option IP header + // + PROTOCOL_UNION u; +} IPV4_STRUCT; + +#pragma pack() // reset to default + + //////////////////////////////////////////////////////////// +// +// BC IP Filter Routine +// +EFI_STATUS +IpFilter ( + PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_IP_FILTER *Filter + ) +; + +// +// ////////////////////////////////////////////////////////////////////// +// +// Udp Write Routine - called by base code - e.g. TFTP - already locked +// +EFI_STATUS +UdpWrite ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIpPtr, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortptr, + IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL + IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSizePtr, + IN VOID *BufferPtr + ) +; + +// +// ///////////////////////////////////////////////////////////////////// +// +// Udp Read Routine - called by base code - e.g. TFTP - already locked +// +EFI_STATUS +UdpRead ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPorPtrt, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSizePtr, + IN VOID *BufferPtr, + IN EFI_EVENT TimeoutEvent + ) +; + +VOID +IgmpLeaveGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS * + ) +; + +VOID +IgmpJoinGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS * + ) +; + +// +// convert number to zero filled ascii value of length lth +// +VOID +CvtNum ( + UINTN Number, + UINT8 *BufferPtr, + INTN BufferLen + ) +; + +// +// convert number to ascii string at ptr +// +VOID +UtoA10 ( + UINTN Number, + UINT8 *BufferPtr + ) +; + +// +// convert ascii numeric string to UINTN +// +UINTN +AtoU ( + UINT8 *BufferPtr + ) +; + +UINT64 +AtoU64 ( + UINT8 *BufferPtr + ) +; + +// +// calculate the internet checksum (RFC 1071) +// return 16 bit ones complement of ones complement sum of 16 bit words +// +UINT16 +IpChecksum ( + UINT16 *MessagePtr, + UINTN ByteLength + ) +; + +// +// do checksum on non contiguous header and data +// +UINT16 +IpChecksum2 ( + UINT16 *Header, + UINTN HeaderLength, + UINT16 *Message, + UINTN MessageLength + ) +; + +// +// update checksum when only a single word changes +// +UINT16 +UpdateChecksum ( + UINT16 OldChecksum, + UINT16 OldWord, + UINT16 NewWord + ) +; + +VOID +SeedRandom ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 InitialSeed + ) +; + +UINT16 +Random ( + IN PXE_BASECODE_DEVICE *Private + ) +; + +EFI_STATUS +SendPacket ( + PXE_BASECODE_DEVICE *Private, + VOID *HeaderPtr, + VOID *PacketPtr, + INTN PacketLength, + VOID *HardwareAddress, + UINT16 MediaProtocol, + IN EFI_PXE_BASE_CODE_FUNCTION Function + ) +; + +VOID +HandleArpReceive ( + PXE_BASECODE_DEVICE *Private, + ARP_PACKET *ArpPacketPtr, + VOID *HeaderPtr + ) +; + +VOID +HandleIgmp ( + PXE_BASECODE_DEVICE *Private, + IGMPV2_MESSAGE *IgmpMessageptr, + UINTN IgmpMessageLen + ) +; + +VOID +IgmpCheckTimers ( + PXE_BASECODE_DEVICE *Private + ) +; // poll when doing a receive +// return hw add of IP and TRUE if available, otherwise FALSE +// +BOOLEAN +GetHwAddr ( + IN PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ProtocolAddressPtr, + EFI_MAC_ADDRESS *HardwareAddressPtr + ) +; + +EFI_STATUS +DoArp ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddressPtr, + OUT EFI_MAC_ADDRESS *HardwareAddressptr + ) +; + +BOOLEAN +OnSameSubnet ( + UINTN IpAddressLen, + EFI_IP_ADDRESS *Ip1, + EFI_IP_ADDRESS *Ip2, + EFI_IP_ADDRESS *SubnetMask + ) +; + +VOID +IpAddRouter ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *RouterIp + ) +; + +#define Ip4AddRouter(Private, Ipv4Ptr) IpAddRouter (Private, (EFI_IP_ADDRESS *) Ipv4Ptr) + +// +// routine to send ipv4 packet +// ipv4 + upper protocol header for length TotHdrLth in xmtbuf, ipv4 header length IpHdrLth +// routine fills in ipv4hdr Ver_Hdl, TotLth, and Checksum, moves in Data, and gets dest MAC address +// +EFI_STATUS +Ipv4Xmt ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIP, + UINTN IpHeaderLen, + UINTN TotalHeaderLen, + VOID *Data, + UINTN DataLen, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +; + +// +// send ipv4 packet with ipv4 option +// +EFI_STATUS +Ipv4SendWOp ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIP, + UINT8 *MessagePtr, + UINTN MessageLth, + UINT8 Protocol, + UINT8 *Option, + UINTN OptionLen, + UINT32 DestIp, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +; + +// +// send MsgLth message at MsgPtr - higher level protocol header already in xmtbuf, length HdrSize +// +EFI_STATUS +Ip4Send ( + IN PXE_BASECODE_DEVICE *Private, // pointer to instance data + IN UINTN MayFragment, // + IN UINT8 Protocol, // protocol + IN UINT32 SrcIp, // Source IP address + IN UINT32 DestIp, // Destination IP address + IN UINT32 GatewayIp, // used if not NULL and needed + IN UINTN HeaderSize, // protocol header byte length + IN UINT8 *MsgPtr, // pointer to data + IN UINTN MsgLength + ) +; // data byte length +// receive up to MsgLth message into MsgPtr for protocol Prot +// return message length, src/dest ips if select any, and pointer to protocol header +// +EFI_STATUS +IpReceive ( + IN PXE_BASECODE_DEVICE *Private, // pointer to instance data + UINT16 OpFlags, // Flags to determine if filtering on IP addresses + EFI_IP_ADDRESS *SrcIpPtr, // if filtering, O if accept any + EFI_IP_ADDRESS *DstIpPtr, // if filtering, O if accept any + UINT8 Protocol, // protocol + VOID *HeaderPtr, // address of where to put protocol header + UINTN HeaderSize, // protocol header byte length + UINT8 *MsgPtr, // pointer to data buffer + UINTN *MsgLenPtr, // pointer to data buffer length/ O - returned data length + IN EFI_EVENT TimeoutEvent + ) +; + +#if 0 +VOID +WaitForTxComplete ( + IN PXE_BASECODE_DEVICE *Private + ) +; +#endif +// +// routine to cycle waiting for a receive or timeout +// +EFI_STATUS +WaitForReceive ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN EFI_EVENT TimeoutEvent, + IN OUT UINTN *HeaderSizePtr, + IN OUT UINTN *BufferSizePtr, + IN OUT UINT16 *ProtocolPtr + ) +; + +#endif /* _IP_H_ */ + +/* EOF - ip.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h new file mode 100644 index 0000000000..6d06045df0 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Ipf/PxeArch.h @@ -0,0 +1,26 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeArch.h + +Abstract: + Defines PXE Arch type + + +**/ + +#ifndef _EFI_PXE_ARCH_H_ +#define _EFI_PXE_ARCH_H_ + +#define SYS_ARCH 0x2 + +#endif diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf new file mode 100644 index 0000000000..f5eaa0f622 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.inf @@ -0,0 +1,92 @@ +#/** @file +# Component name for module BC +# +# Copyright (c) 2007, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PxeBcDxe + FILE_GUID = A3f436EA-A127-4EF8-957C-8048606FF670 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = InitializeBCDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Pxe_bc_mtftp.c + Bc.c + Dhcp.h + Ip.h + Pxe_bc_ip.c + Pxe_bc_dhcp.c + Pxe_bc_arp.c + Hton.h + ComponentName.c + Bc.h + Pxe_loadfile.c + Tftp.h + Pxe_bc_igmp.c + Pxe_bc_udp.c + +[Sources.IA32] + Ia32\PxeArch.h + +[Sources.X64] + X64\PxeArch.h + +[Sources.IPF] + Ipf\PxeArch.h + +[Sources.EBC] + Ebc\PxeArch.h + Ebc\PxeArch.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + + +[Guids] + gEfiSmbiosTableGuid # ALWAYS_CONSUMED + + +[Protocols] + gEfiBisProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPxeBaseCodeCallbackProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPxeBaseCodeProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiLoadFileProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiNetworkInterfaceIdentifierProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcpProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiNetworkInterfaceIdentifierProtocolGuid_31 # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa new file mode 100644 index 0000000000..1a1f81d1e3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/PxeBcDxe.msa @@ -0,0 +1,106 @@ + + + BC + DXE_DRIVER + A3f436EA-A127-4EF8-957C-8048606FF670 + 1.0 + Component name for module BC + FIX ME! + Copyright (c) 2007, Intel Corporation. All rights reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + BC + + + + DebugLib + + + BaseMemoryLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + BaseLib + + + UefiLib + + + + pxe_bc_udp.c + pxe_bc_igmp.c + tftp.h + pxe_loadfile.c + bc.h + ComponentName.c + BcEntry.c + ipf\PxeArch.h + ebc\PxeArch.h + x64\PxeArch.h + pxe_bc_tcp.c + hton.h + pxe_bc_arp.c + pxe_bc_dhcp.c + pxe_bc_ip.c + ip.h + dhcp.h + bc.c + pxe_bc_mtftp.c + ia32\PxeArch.h + ebc\PxeArch.c + + + + + + + + gEfiNetworkInterfaceIdentifierProtocolGuid + + + gEfiDevicePathProtocolGuid + + + gEfiSimpleNetworkProtocolGuid + + + gEfiLoadFileProtocolGuid + + + gEfiPxeBaseCodeProtocolGuid + + + gEfiPxeBaseCodeCallbackProtocolGuid + + + gEfiBisProtocolGuid + + + + + gEfiSmbiosTableGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + InitializeBCDriver + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c new file mode 100644 index 0000000000..3654363e8e --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_arp.c @@ -0,0 +1,583 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_arp.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// Definitions for ARP +// Per RFC 826 +// +STATIC ARP_HEADER ArpHeader; + +#pragma pack(1) +STATIC struct { + UINT8 MediaHeader[14]; + ARP_HEADER ArpHeader; + UINT8 ArpData[64]; +} ArpReplyPacket; +#pragma pack() + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return none + +**/ +VOID +InitArpHeader ( + VOID + ) +{ + ArpHeader.HwType = HTONS (ETHERNET_ADD_SPC); + ArpHeader.ProtType = HTONS (ETHER_TYPE_IP); + ArpHeader.HwAddLen = ENET_HWADDLEN; + ArpHeader.ProtAddLen = IPV4_PROTADDLEN; + ArpHeader.OpCode = HTONS (ARP_REQUEST); + + CopyMem (&ArpReplyPacket.ArpHeader, &ArpHeader, sizeof (ARP_HEADER)); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +HandleArpReceive ( + IN PXE_BASECODE_DEVICE *Private, + IN ARP_PACKET *ArpPacketPtr, + IN VOID *MediaHeader + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_MAC_ADDRESS TmpMacAddr; + UINTN Index; + UINT8 *SrcHwAddr; + UINT8 *SrcPrAddr; + UINT8 *DstHwAddr; + UINT8 *DstPrAddr; + UINT8 *TmpPtr; + + // + // + // + PxeBcMode = Private->EfiBc.Mode; + SnpMode = Private->SimpleNetwork->Mode; + + // + // For now only ethernet addresses are supported. + // This will need to be updated when other media + // layers are supported by PxeBc, Snp and UNDI. + // + if (ArpPacketPtr->ArpHeader.HwType != HTONS (ETHERNET_ADD_SPC)) { + return ; + } + // + // For now only IP protocol addresses are supported. + // This will need to be updated when other protocol + // types are supported by PxeBc, Snp and UNDI. + // + if (ArpPacketPtr->ArpHeader.ProtType != HTONS (ETHER_TYPE_IP)) { + return ; + } + // + // For now only SNP hardware address sizes are supported. + // + if (ArpPacketPtr->ArpHeader.HwAddLen != SnpMode->HwAddressSize) { + return ; + } + // + // For now only PxeBc protocol address sizes are supported. + // + if (ArpPacketPtr->ArpHeader.ProtAddLen != Private->IpLength) { + return ; + } + // + // Ignore out of range opcodes + // + switch (ArpPacketPtr->ArpHeader.OpCode) { + case HTONS (ARP_REPLY): + case HTONS (ARP_REQUEST): + break; + + default: + return ; + } + // + // update entry in our ARP cache if we have it + // + SrcHwAddr = (UINT8 *) &ArpPacketPtr->SrcHardwareAddr; + SrcPrAddr = SrcHwAddr + SnpMode->HwAddressSize; + + for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) { + if (CompareMem ( + &PxeBcMode->ArpCache[Index].IpAddr, + SrcPrAddr, + Private->IpLength + )) { + continue; + } + + CopyMem ( + &PxeBcMode->ArpCache[Index].MacAddr, + SrcHwAddr, + SnpMode->HwAddressSize + ); + + break; + } + // + // Done if ARP packet was not for us. + // + DstHwAddr = SrcPrAddr + Private->IpLength; + DstPrAddr = DstHwAddr + SnpMode->HwAddressSize; + + if (CompareMem (DstPrAddr, &PxeBcMode->StationIp, Private->IpLength)) { + return ; + // + // not for us + // + } + // + // for us - if we did not update entry, add it + // + if (Index == PxeBcMode->ArpCacheEntries) { + // + // if we have a full table, get rid of oldest + // + if (Index == PXE_ARP_CACHE_SIZE) { + Index = Private->OldestArpEntry; + + if (++Private->OldestArpEntry == PXE_ARP_CACHE_SIZE) { + Private->OldestArpEntry = 0; + } + } else { + ++PxeBcMode->ArpCacheEntries; + } + + CopyMem ( + &PxeBcMode->ArpCache[Index].MacAddr, + SrcHwAddr, + SnpMode->HwAddressSize + ); + + CopyMem ( + &PxeBcMode->ArpCache[Index].IpAddr, + SrcPrAddr, + Private->IpLength + ); + } + // + // if this is not a request or we don't yet have an IP, finished + // + if (ArpPacketPtr->ArpHeader.OpCode != HTONS (ARP_REQUEST) || !Private->GoodStationIp) { + return ; + } + // + // Assemble ARP reply. + // + // + // Create media header. [ dest mac | src mac | prot ] + // + CopyMem ( + &ArpReplyPacket.MediaHeader[0], + SrcHwAddr, + SnpMode->HwAddressSize + ); + + CopyMem ( + &ArpReplyPacket.MediaHeader[SnpMode->HwAddressSize], + &SnpMode->CurrentAddress, + SnpMode->HwAddressSize + ); + + CopyMem ( + &ArpReplyPacket.MediaHeader[2 * SnpMode->HwAddressSize], + &((UINT8 *) MediaHeader)[2 * SnpMode->HwAddressSize], + sizeof (UINT16) + ); + + // + // ARP reply header is almost filled in, + // just insert the correct opcode. + // + ArpReplyPacket.ArpHeader.OpCode = HTONS (ARP_REPLY); + + // + // Now fill in ARP data. [ src mac | src prot | dest mac | dest prot ] + // + TmpPtr = ArpReplyPacket.ArpData; + CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize); + + TmpPtr += SnpMode->HwAddressSize; + CopyMem (TmpPtr, &PxeBcMode->StationIp, Private->IpLength); + + TmpPtr += Private->IpLength; + CopyMem (TmpPtr, SrcHwAddr, SnpMode->HwAddressSize); + + TmpPtr += SnpMode->HwAddressSize; + CopyMem (TmpPtr, SrcPrAddr, Private->IpLength); + + // + // Now send out the ARP reply. + // + CopyMem (&TmpMacAddr, SrcHwAddr, sizeof (EFI_MAC_ADDRESS)); + + SendPacket ( + Private, + &ArpReplyPacket.MediaHeader, + &ArpReplyPacket.ArpHeader, + sizeof (ARP_HEADER) + 2 * (Private->IpLength + SnpMode->HwAddressSize), + &TmpMacAddr, + PXE_PROTOCOL_ETHERNET_ARP, + EFI_PXE_BASE_CODE_FUNCTION_ARP + ); + + // + // Give time (100 microseconds) for ARP reply to get onto wire. + // + gBS->Stall (1000); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return TRUE := If IP address was found and MAC address was stored + @return FALSE := If IP address was not found + +**/ +BOOLEAN +GetHwAddr ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddrPtr, + OUT EFI_MAC_ADDRESS *HardwareAddrPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN HardwareAddrLength; + UINTN Index; + + PxeBcMode = Private->EfiBc.Mode; + HardwareAddrLength = Private->SimpleNetwork->Mode->HwAddressSize; + + for (Index = 0; Index < PxeBcMode->ArpCacheEntries; ++Index) { + if (!CompareMem ( + ProtocolAddrPtr, + &PxeBcMode->ArpCache[Index].IpAddr, + Private->IpLength + )) { + CopyMem ( + HardwareAddrPtr, + &PxeBcMode->ArpCache[Index].MacAddr, + HardwareAddrLength + ); + + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_SUCCESS := ARP request sent + @return other := ARP request could not be sent + +**/ +STATIC +EFI_STATUS +SendRequest ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddrPtr, + IN EFI_MAC_ADDRESS *HardwareAddrPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + ARP_PACKET *ArpPacket; + EFI_STATUS Status; + UINTN HardwareAddrLength; + UINT8 *SrcProtocolAddrPtr; + UINT8 *DestHardwareAddrptr; + UINT8 *DestProtocolAddrPtr; + + // + // + // + PxeBcMode = Private->EfiBc.Mode; + SnpMode = Private->SimpleNetwork->Mode; + HardwareAddrLength = SnpMode->HwAddressSize; + + // + // Allocate ARP buffer + // + if (Private->ArpBuffer == NULL) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + SnpMode->MediaHeaderSize + sizeof (ARP_PACKET), + &Private->ArpBuffer + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + ArpPacket = (VOID *) (Private->ArpBuffer + SnpMode->MediaHeaderSize); + + // + // for now, only handle one kind of hw and pr address + // + ArpPacket->ArpHeader = ArpHeader; + ArpPacket->ArpHeader.HwAddLen = (UINT8) HardwareAddrLength; + ArpPacket->ArpHeader.ProtAddLen = (UINT8) Private->IpLength; + + // + // rest more generic + // + SrcProtocolAddrPtr = (UINT8 *) (&ArpPacket->SrcHardwareAddr) + HardwareAddrLength; + DestHardwareAddrptr = SrcProtocolAddrPtr + Private->IpLength; + DestProtocolAddrPtr = DestHardwareAddrptr + HardwareAddrLength; + + CopyMem (DestProtocolAddrPtr, ProtocolAddrPtr, Private->IpLength); + CopyMem (DestHardwareAddrptr, HardwareAddrPtr, HardwareAddrLength); + CopyMem (SrcProtocolAddrPtr, &PxeBcMode->StationIp, Private->IpLength); + CopyMem ( + &ArpPacket->SrcHardwareAddr, + &SnpMode->CurrentAddress, + HardwareAddrLength + ); + + return SendPacket ( + Private, + Private->ArpBuffer, + ArpPacket, + sizeof (ARP_HEADER) + ((Private->IpLength + HardwareAddrLength) << 1), + &SnpMode->BroadcastAddress, + PXE_PROTOCOL_ETHERNET_ARP, + EFI_PXE_BASE_CODE_FUNCTION_ARP + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// check for address - if not there, send ARP request, wait and check again +// not how it would be done in a full system +// +#define ARP_REQUEST_TIMEOUT_MS 500 // try for half a second + + //////////////////////////////////////////////////////////// +// +// BC Arp Routine +// + +/** + + +**/ +EFI_STATUS +EFIAPI +BcArp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_IP_ADDRESS * ProtocolAddrPtr, + OUT EFI_MAC_ADDRESS * HardwareAddrPtr OPTIONAL + ) +{ + EFI_MAC_ADDRESS Mac; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + DEBUG ((DEBUG_INFO, "\nBcArp()")); + + // + // Issue BC command + // + if (ProtocolAddrPtr == NULL) { + DEBUG ( + (DEBUG_INFO, + "\nBcArp() Exit #1 %Xh (%r)", + EFI_INVALID_PARAMETER, + EFI_INVALID_PARAMETER) + ); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (HardwareAddrPtr == NULL) { + HardwareAddrPtr = &Mac; + } + + ZeroMem (HardwareAddrPtr, Private->SimpleNetwork->Mode->HwAddressSize); + + if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) { + DEBUG ( + (DEBUG_INFO, + "\nBcArp() Exit #2 %Xh (%r)", + EFI_SUCCESS, + EFI_SUCCESS) + ); + + EfiReleaseLock (&Private->Lock); + return EFI_SUCCESS; + } + + StatCode = DoArp (Private, ProtocolAddrPtr, HardwareAddrPtr); + + DEBUG ((DEBUG_INFO, "\nBcArp() Exit #3 %Xh (%r)", StatCode, StatCode)); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_SUCCESS := MAC address found + @return other := MAC address could not be found + +**/ +EFI_STATUS +DoArp ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *ProtocolAddrPtr, + OUT EFI_MAC_ADDRESS *HardwareAddrPtr + ) +{ + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + UINTN HeaderSize; + UINTN BufferSize; + UINT16 Protocol; + + DEBUG ((DEBUG_INFO, "\nDoArp()")); + + // + // + // + StatCode = SendRequest (Private, ProtocolAddrPtr, HardwareAddrPtr); + + if (EFI_ERROR (StatCode)) { + DEBUG ((DEBUG_INFO, "\nDoArp() Exit #1 %Xh (%r)", StatCode, StatCode)); + return StatCode; + } + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + ARP_REQUEST_TIMEOUT_MS * 10000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + // + // + // + for (;;) { + StatCode = WaitForReceive ( + Private, + EFI_PXE_BASE_CODE_FUNCTION_ARP, + TimeoutEvent, + &HeaderSize, + &BufferSize, + &Protocol + ); + + if (EFI_ERROR (StatCode)) { + break; + } + + if (Protocol != PXE_PROTOCOL_ETHERNET_ARP) { + continue; + } + + HandleArpReceive ( + Private, + (ARP_PACKET *) (Private->ReceiveBufferPtr + HeaderSize), + Private->ReceiveBufferPtr + ); + + if (GetHwAddr (Private, ProtocolAddrPtr, HardwareAddrPtr)) { + break; + } + } + + DEBUG ( + (DEBUG_INFO, + "\nDoArp() Exit #2 %Xh, (%r)", + StatCode, + StatCode) + ); + + gBS->CloseEvent (TimeoutEvent); + + return StatCode; +} + +/* eof - pxe_bc_arp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c new file mode 100644 index 0000000000..7dec5ec2d2 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_dhcp.c @@ -0,0 +1,3284 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_dhcp.c + +Abstract: + DHCP and PXE discovery protocol implementations. + + +**/ + +#include "Bc.h" + +#include "PxeArch.h" + +STATIC EFI_PXE_BASE_CODE_UDP_PORT DhcpServerPort = DHCP_SERVER_PORT; +STATIC EFI_PXE_BASE_CODE_UDP_PORT DHCPClientPort = DHCP_CLIENT_PORT; +STATIC EFI_PXE_BASE_CODE_UDP_PORT PseudoDhcpServerPort = PXE_DISCOVERY_PORT; +#define PSEUDO_DHCP_CLIENT_PORT PseudoDhcpServerPort +STATIC EFI_IP_ADDRESS BroadcastIP = { 0xffffffff }; +STATIC EFI_IP_ADDRESS DefaultSubnetMask = { 0xffffff00 }; + +typedef union { + DHCPV4_OP_STRUCT *OpPtr; + PXE_OP_SERVER_LIST *BootServersStr; + PXE_SERVER_LIST *BootServerList; + PXE_BOOT_MENU_ENTRY *BootMenuItem; + PXE_OP_DISCOVERY_CONTROL *DiscoveryControl; + PXE_OP_BOOT_MENU *BootMenu; + PXE_OP_BOOT_ITEM *BootItem; + DHCPV4_OP_VENDOR_OPTIONS *VendorOptions; + DHCPV4_OP_OVERLOAD *Overload; + DHCPV4_OP_CLASS *PxeClassStr; + DHCPV4_OP_SUBNET_MASK *SubnetMaskStr; + DHCPV4_OP_MESSAGE_TYPE *MessageType; + UINT8 *BytePtr; +} UNION_PTR; + +#pragma pack(1) +// +// option structure for DHCPREQUEST at end of DISCOVER options +// and for DHCPDECLINE +// +STATIC const struct requestopendstr { + DHCPV4_OP_REQUESTED_IP OpReqIP; + DHCPV4_OP_SERVER_IP DhcServerIpPtr; + UINT8 End[1]; +} +RequestOpEndStr = { + { + { + OP_DHCP_REQ_IP_ADD, + DHCPV4_OPTION_LENGTH(DHCPV4_OP_REQUESTED_IP) + } + }, + { + { + OP_DHCP_SERVER_IP, + DHCPV4_OPTION_LENGTH(DHCPV4_OP_SERVER_IP) + } + }, + { + OP_END + } +}; + +#define DHCP_REQ_OPTIONS (*(struct requestopendstr *) DHCPV4_OPTIONS_BUFFER.End) + +PXE_OP_BOOT_ITEM DefaultBootItem = { + { + VEND_PXE_BOOT_ITEM, + DHCPV4_OPTION_LENGTH(PXE_OP_BOOT_ITEM) + }, + 0, + 0 +}; + +// +// PXE discovery control default structure +// +STATIC PXE_OP_DISCOVERY_CONTROL DefaultDisCtl = { + { VEND_PXE_DISCOVERY_CONTROL, DHCPV4_OPTION_LENGTH(PXE_OP_DISCOVERY_CONTROL) }, + 0 +}; + +// +// PXE credentials option structure +// +typedef struct { + UINT8 c[4]; +} PXE_CREDENTIAL; + +typedef struct { + DHCPV4_OP_HEADER Header; + PXE_CREDENTIAL Credentials[1]; +} PXE_OP_CREDENTIAL_TYPES; + +// +// option structure for PXE discover (without credentials) +// +typedef struct { // discoveropendstr { + DHCPV4_OP_HEADER Header; // vendor options + PXE_OP_BOOT_ITEM BootItem; + UINT8 End[1]; // if credentials option, it starts here +} PXE_DISCOVER_OPTIONS; + +#define DISCOVERoptions (*(PXE_DISCOVER_OPTIONS *) DHCPV4_OPTIONS_BUFFER.End) +#define DISCREDoptions (*(PXE_OP_CREDENTIAL_TYPES *) DISCOVERoptions.End) + +// +// common option beginning for all our DHCP messages except +// DHCPDECLINE and DHCPRELEASE +// +STATIC struct optionsstr { + UINT8 DhcpCookie[4]; + DHCPV4_OP_MESSAGE_TYPE DhcpMessageType; + DHCPV4_OP_MAX_MESSAGE_SIZE DhcpMaxMessageSize; + DHCPV4_OP_REQUESTED_OPTIONS DhcpRequestedOptions; + DHCPV4_OP_PLATFORM_ID DhcpPlatformId; + DHCPV4_OP_NETWORK_INTERFACE DhcpNetworkInterface; + DHCPV4_OP_ARCHITECTURE_TYPE DhcpClientArchitecture; + DHCPV4_OP_CLASS_ID DhcpClassIdentifier; + UINT8 End[1]; +} DHCPOpStart; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +OptionsStrucInit ( + VOID + ) +{ + DHCPOpStart.DhcpCookie[0] = 99; + DHCPOpStart.DhcpCookie[1] = 130; + DHCPOpStart.DhcpCookie[2] = 83; + DHCPOpStart.DhcpCookie[3] = 99; + DHCPOpStart.DhcpMessageType.Header.OpCode = OP_DHCP_MESSAGE_TYPE; + DHCPOpStart.DhcpMessageType.Header.Length = 1; + DHCPOpStart.DhcpMessageType.Type = DHCPDISCOVER; + DHCPOpStart.DhcpMaxMessageSize.Header.OpCode = OP_DHCP_MAX_MESSAGE_SZ; + DHCPOpStart.DhcpMaxMessageSize.Header.Length = 2; + DHCPOpStart.DhcpMaxMessageSize.MaxSize[0] = MAX_DHCP_MSG_SZ >> 8; + DHCPOpStart.DhcpMaxMessageSize.MaxSize[1] = MAX_DHCP_MSG_SZ & 0xff; + DHCPOpStart.DhcpRequestedOptions.Header.OpCode = OP_DHCP_PARM_REQ_LIST; + DHCPOpStart.DhcpRequestedOptions.Header.Length = sizeof (DHCPV4_REQUESTED_OPTIONS_DATA); + DHCPOpStart.DhcpRequestedOptions.Data._OP_SUBNET_MASK = OP_SUBNET_MASK; /* 1 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_OFFSET = OP_TIME_OFFSET; /* 2 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_ROUTER_LIST = OP_ROUTER_LIST; /* 3 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_TIME_SERVERS = OP_TIME_SERVERS; /* 4 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NAME_SERVERS = OP_NAME_SERVERS; /* 5 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DNS_SERVERS = OP_DNS_SERVERS; /* 6 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_HOST_NAME = OP_HOST_NAME; /* 12 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_BOOT_FILE_SZ = OP_BOOT_FILE_SZ; /* 13 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DOMAIN_NAME = OP_DOMAIN_NAME; /* 15 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_ROOT_PATH = OP_ROOT_PATH; /* 17 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_EXTENSION_PATH = OP_EXTENSION_PATH; /* 18 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_MAX_DATAGRAM_SZ = OP_MAX_DATAGRAM_SZ; /* 22 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DEFAULT_TTL = OP_DEFAULT_TTL; /* 23 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_BROADCAST_ADD = OP_BROADCAST_ADD; /* 28 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_DOMAIN_NAME = OP_NIS_DOMAIN_NAME; /* 40 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NIS_SERVERS = OP_NIS_SERVERS; /* 41 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_NTP_SERVERS = OP_NTP_SERVERS; /* 42 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_VENDOR_SPECIFIC = OP_VENDOR_SPECIFIC; /* 43 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REQ_IP_ADD = OP_DHCP_REQ_IP_ADD; /* 50 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_LEASE_TIME = OP_DHCP_LEASE_TIME; /* 51 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_SERVER_IP = OP_DHCP_SERVER_IP; /* 54 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_RENEWAL_TIME = OP_DHCP_RENEWAL_TIME; /* 58 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_REBINDING_TIME = OP_DHCP_REBINDING_TIME; /* 59 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_CLASS_IDENTIFIER = OP_DHCP_CLASS_IDENTIFIER; /* 60 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_TFTP_SERVER_NAME = OP_DHCP_TFTP_SERVER_NAME; /* 66 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_BOOTFILE = OP_DHCP_BOOTFILE; /* 67 */ + DHCPOpStart.DhcpRequestedOptions.Data._OP_DHCP_PLATFORM_ID = OP_DHCP_PLATFORM_ID; /* 97 */ + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption128 = 128; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption129 = 129; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption130 = 130; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption131 = 131; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption132 = 132; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption133 = 133, DHCPOpStart.DhcpRequestedOptions.Data.VendorOption134 = 134; + DHCPOpStart.DhcpRequestedOptions.Data.VendorOption135 = 135; + DHCPOpStart.DhcpPlatformId.Header.OpCode = OP_DHCP_PLATFORM_ID; + DHCPOpStart.DhcpPlatformId.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_PLATFORM_ID); + DHCPOpStart.DhcpNetworkInterface.Header.OpCode = OP_DHCP_NETWORK_ARCH; + DHCPOpStart.DhcpNetworkInterface.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_NETWORK_INTERFACE); + DHCPOpStart.DhcpNetworkInterface.Type = 0; + DHCPOpStart.DhcpNetworkInterface.MajorVersion = 0; + DHCPOpStart.DhcpNetworkInterface.MinorVersion = 0; + DHCPOpStart.DhcpClientArchitecture.Header.OpCode = OP_DHCP_SYSTEM_ARCH; + DHCPOpStart.DhcpClientArchitecture.Header.Length = DHCPV4_OPTION_LENGTH (DHCPV4_OP_ARCHITECTURE_TYPE); + DHCPOpStart.DhcpClientArchitecture.Type = HTONS (SYS_ARCH); + DHCPOpStart.DhcpClassIdentifier.Header.OpCode = OP_DHCP_CLASS_IDENTIFIER; + DHCPOpStart.DhcpClassIdentifier.Header.Length = sizeof (DHCPV4_CLASS_ID_DATA); + CopyMem ( + DHCPOpStart.DhcpClassIdentifier.Data.ClassIdentifier, + "PXEClient:", + sizeof ("PXEClient:") + ); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit2, "Arch:", sizeof ("Arch:")); + CopyMem ( + DHCPOpStart.DhcpClassIdentifier.Data.ArchitectureType, + "xxxxx", + sizeof ("xxxxx") + ); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit3, ":", sizeof (":")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.InterfaceName, "XXXX", sizeof ("XXXX")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.Lit4, ":", sizeof (":")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMajor, "yyy", sizeof ("yyy")); + CopyMem (DHCPOpStart.DhcpClassIdentifier.Data.UndiMinor, "xxx", sizeof ("xxx")); + DHCPOpStart.End[0] = OP_END; +}; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// DHCPDECLINE option structure +// +struct opdeclinestr { + UINT8 DhcpCookie[4]; + DHCPV4_OP_MESSAGE_TYPE DhcpMessageType; + struct requestopendstr OpDeclineEnd; +}; + +#define DHCPDECLINEoptions (*(struct opdeclinestr *) DHCPV4_TRANSMIT_BUFFER.options) + +// +// DHCPRELEASE option structure +// +struct opreleasestr { + UINT8 DhcpCookie[4]; + DHCPV4_OP_MESSAGE_TYPE DhcpMessageType; + DHCPV4_OP_SERVER_IP DhcServerIpPtr; + UINT8 End[1]; +}; + +#define DHCPRELEASEoptions (*(struct opreleasestr *) DHCPV4_TRANSMIT_BUFFER.options) + +// +// array of PXE vendor options in which we are interested +// value 0 -> not of interest, else value is index into PXE OPTION array +// option values from 1 to MAX_OUR_PXE_OPT +// +STATIC UINT8 ourPXEopts[MAX_OUR_PXE_OPT] = { + VEND_PXE_MTFTP_IP_IX, // multicast IP address of bootfile for MTFTP listen + VEND_PXE_MTFTP_CPORT_IX, // UDP Port to monitor for MTFTP responses - Intel order + VEND_PXE_MTFTP_SPORT_IX, // Server UDP Port for MTFTP open - Intel order + VEND_PXE_MTFTP_TMOUT_IX, // Listen timeout - secs + VEND_PXE_MTFTP_DELAY_IX, // Transmission timeout - secs + VEND_PXE_DISCOVERY_CONTROL_IX, // bit field + VEND_PXE_DISCOVERY_MCAST_ADDR_IX, // boot server discovery multicast address + VEND_PXE_BOOT_SERVERS_IX, // list of boot servers of form tp(2) cnt(1) ips[cnt] + VEND_PXE_BOOT_MENU_IX, + VEND_PXE_BOOT_PROMPT_IX, + VEND_PXE_MCAST_ADDRS_ALLOC_IX, // not used by client + VEND_PXE_CREDENTIAL_TYPES_IX, + VEND_13_IX, // not used by client + VEND_14_IX, // not used by client + VEND_15_IX, // not used by client + VEND_16_IX, // not used by client + VEND_17_IX, // not used by client + VEND_18_IX, // not used by client + VEND_19_IX, // not used by client + VEND_20_IX, // not used by client + VEND_21_IX, // not used by client + VEND_22_IX, // not used by client + VEND_23_IX, // not used by client + VEND_24_IX, // not used by client + VEND_25_IX, // not used by client + VEND_26_IX, // not used by client + VEND_27_IX, // not used by client + VEND_28_IX, // not used by client + VEND_29_IX, // not used by client + VEND_30_IX, // not used by client + VEND_31_IX, // not used by client + VEND_32_IX, // not used by client + VEND_33_IX, // not used by client + VEND_34_IX, // not used by client + VEND_35_IX, // not used by client + VEND_36_IX, // not used by client + VEND_37_IX, // not used by client + VEND_38_IX, // not used by client + VEND_39_IX, // not used by client + VEND_40_IX, // not used by client + VEND_41_IX, // not used by client + VEND_42_IX, // not used by client + VEND_43_IX, // not used by client + VEND_44_IX, // not used by client + VEND_45_IX, // not used by client + VEND_46_IX, // not used by client + VEND_47_IX, // not used by client + VEND_48_IX, // not used by client + VEND_49_IX, // not used by client + VEND_50_IX, // not used by client + VEND_51_IX, // not used by client + VEND_52_IX, // not used by client + VEND_53_IX, // not used by client + VEND_54_IX, // not used by client + VEND_55_IX, // not used by client + VEND_56_IX, // not used by client + VEND_57_IX, // not used by client + VEND_58_IX, // not used by client + VEND_59_IX, // not used by client + VEND_60_IX, // not used by client + VEND_61_IX, // not used by client + VEND_62_IX, // not used by client + VEND_63_IX, // not used by client + VEND_64_IX, // not used by client + VEND_65_IX, // not used by client + VEND_66_IX, // not used by client + VEND_67_IX, // not used by client + VEND_68_IX, // not used by client + VEND_69_IX, // not used by client + VEND_70_IX, // not used by client + VEND_PXE_BOOT_ITEM_IX +}; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// array of options in which we are interested +// value 0 -> not of interest, else value is index into OPTION array +// option values from 1 to MAX_OUR_OPT +// +STATIC UINT8 OurDhcpOptions[MAX_OUR_OPT] = { + OP_SUBNET_MASK_IX, // OP_SUBNET_MASK 1 // data is the subnet mask + OP_TIME_OFFSET_IX, // OP_TIME_OFFSET 2 // data is the time offset of subnet to UTC in seconds + OP_ROUTER_LIST_IX, // OP_ROUTER_LIST 3 // list of routers on subnet + OP_TIME_SERVERS_IX, // OP_TIME_SERVERS 4 // list of time servers available + OP_NAME_SERVERS_IX, // OP_NAME_SERVERS 5 // list of name servers available + OP_DNS_SERVERS_IX, // OP_DNS_SERVERS 6 // list of DNS servers available + OP_LOG_SERVERS_IX, // OP_LOG_SERVERS 7 + OP_COOKIE_SERVERS_IX, // OP_COOKIE_SERVERS 8 + OP_LPR_SREVERS_IX, // OP_LPR_SREVERS 9 + OP_IMPRESS_SERVERS_IX, // OP_IMPRESS_SERVERS 10 + OP_RES_LOC_SERVERS_IX, // OP_RES_LOC_SERVERS 11 + OP_HOST_NAME_IX, // OP_HOST_NAME 12 // client name + OP_BOOT_FILE_SZ_IX, // OP_BOOT_FILE_SZ 13 // number of 512 blocks of boot file + OP_DUMP_FILE_IX, // OP_DUMP_FILE 14 // path name of dump file if client crashes + OP_DOMAIN_NAME_IX, // OP_DOMAIN_NAME 15 // domain name to use + OP_SWAP_SERVER_IX, // OP_SWAP_SERVER 16 + OP_ROOT_PATH_IX, // OP_ROOT_PATH 17 // path name containing root disk + OP_EXTENSION_PATH_IX, // OP_EXTENSION_PATH 18 // name of TFTP downloadable file of form of OP + OP_IP_FORWARDING_IX, // OP_IP_FORWARDING 19 // enable/disable IP packet forwarding + OP_NON_LOCAL_SRC_RTE_IX, // OP_NON_LOCAL_SRC_RTE 20 // enable/disable non local source routing + OP_POLICY_FILTER_IX, // OP_POLICY_FILTER 21 // policy filters for non local source routing + OP_MAX_DATAGRAM_SZ_IX, // OP_MAX_DATAGRAM_SZ 22 // maximum datagram reassembly size + OP_DEFAULT_TTL_IX, // OP_DEFAULT_TTL 23 // default IP time to live + OP_MTU_AGING_TIMEOUT_IX, // OP_MTU_AGING_TIMEOUT 24 + OP_MTU_SIZES_IX, // OP_MTU_SIZES 25 + OP_MTU_TO_USE_IX, // OP_MTU_TO_USE 26 + OP_ALL_SUBNETS_LOCAL_IX, // OP_ALL_SUBNETS_LOCAL 27 + OP_BROADCAST_ADD_IX, // OP_BROADCAST_ADD 28 // broadcast address used on subnet + OP_PERFORM_MASK_DISCOVERY_IX, // OP_PERFORM_MASK_DISCOVERY 29 // perform mask discovery using ICMP + OP_RESPOND_TO_MASK_REQ_IX, // OP_RESPOND_TO_MASK_REQ 30 // respond to subnet mask requests using ICMP + OP_PERFORM_ROUTER_DISCOVERY_IX, // OP_PERFORM_ROUTER_DISCOVERY 31 + OP_ROUTER_SOLICIT_ADDRESS_IX, // OP_ROUTER_SOLICIT_ADDRESS 32 + OP_STATIC_ROUTER_LIST_IX, // OP_STATIC_ROUTER_LIST 33 // list of dest/route pairs + OP_USE_ARP_TRAILERS_IX, // OP_USE_ARP_TRAILERS 34 + OP_ARP_CACHE_TIMEOUT_IX, // OP_ARP_CACHE_TIMEOUT 35 + OP_ETHERNET_ENCAPSULATION_IX, // OP_ETHERNET_ENCAPSULATION 36 // 0 -> RFC 894, 1 -> IEEE 802.3 (RFC 1042) + OP_TCP_DEFAULT_TTL_IX, // OP_TCP_DEFAULT_TTL 37 // default time to live when sending TCP segments + OP_TCP_KEEP_ALIVE_INT_IX, // OP_TCP_KEEP_ALIVE_INT 38 // keep alive interval in seconds + OP_KEEP_ALIVE_GARBAGE_IX, // OP_KEEP_ALIVE_GARBAGE 39 + OP_NIS_DOMAIN_NAME_IX, // OP_NIS_DOMAIN_NAME 40 + OP_NIS_SERVERS_IX, // OP_NIS_SERVERS 41 + OP_NTP_SERVERS_IX, // OP_NTP_SERVERS 42 + OP_VENDOR_SPECIFIC_IX, // OP_VENDOR_SPECIFIC 43 + OP_NBNS_SERVERS_IX, // OP_NBNS_SERVERS 44 + OP_NBDD_SERVERS_IX, // OP_NBDD_SERVERS 45 + OP_NETBIOS_NODE_TYPE_IX, // OP_NETBIOS_NODE_TYPE 46 + OP_NETBIOS_SCOPE_IX, // OP_NETBIOS_SCOPE 47 + OP_XWINDOW_SYSTEM_FONT_SERVERS_IX, // OP_XWINDOW_SYSTEM_FONT_SERVERS 48 + OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS_IX, // OP_XWINDOW_SYSTEM_DISPLAY_MANAGERS 49 + OP_DHCP_REQ_IP_ADD_IX, // OP_DHCP_REQ_IP_ADD 50 // requested IP address - in DHCPDISCOVER + OP_DHCP_LEASE_TIME_IX, // OP_DHCP_LEASE_TIME 51 // lease time requested/granted + OP_DHCP_OPTION_OVERLOAD_IX, // OP_DHCP_OPTION_OVERLOAD 52 // file/server name/both used to hold options + OP_DHCP_MESSAGE_TYPE_IX, // OP_DHCP_MESSAGE_TYPE 53 // message type + OP_DHCP_SERVER_IP_IX, // OP_DHCP_SERVER_IP 54 // IP of server + OP_DHCP_PARM_REQ_LIST_IX, // OP_DHCP_PARM_REQ_LIST 55 // list of requested parameters + OP_DHCP_ERROR_MESSAGE_IX, // OP_DHCP_ERROR_MESSAGE 56 // in DHCPNAK or DECLINE messages + OP_DHCP_MAX_MESSAGE_SZ_IX, // OP_DHCP_MAX_MESSAGE_SZ 57 // maximum DHCP message size client will accept + OP_DHCP_RENEWAL_TIME_IX, // OP_DHCP_RENEWAL_TIME 58 // time in seconds before transitioning to RENEWING state + OP_DHCP_REBINDING_TIME_IX, // OP_DHCP_REBINDING_TIME 59 // time in seconds before transitioning to REBINDING state + OP_DHCP_CLASS_IDENTIFIER_IX, // OP_DHCP_CLASS_IDENTIFIER 60 + OP_DHCP_CLIENT_IDENTIFIER_IX, // OP_DHCP_CLIENT_IDENTIFIER 61 + OP_RESERVED62_IX, // OP_RESERVED62 + OP_RESERVED63_IX, // OP_RESERVED63 + OP_NISPLUS_DOMAIN_NAME_IX, // OP_NISPLUS_DOMAIN_NAME 64 + OP_NISPLUS_SERVERS_IX, // OP_NISPLUS_SERVERS 65 + OP_DHCP_TFTP_SERVER_NAME_IX, // OP_DHCP_TFTP_SERVER_NAME 66 + OP_DHCP_BOOTFILE_IX // OP_DHCP_BOOTFILE 67 +}; + +#define RxBuf ((DHCP_RECEIVE_BUFFER *) (Private->ReceiveBuffers)) + +#pragma pack() + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @param Smbios Pointer to SMBIOS structure + @param StringNumber String number to return. 0 is used to skip all + strings and point to the next SMBIOS structure. + + @return Pointer to string, or pointer to next SMBIOS strcuture if StringNumber == 0 + +**/ +CHAR8 * +PxeBcLibGetSmbiosString ( + IN SMBIOS_STRUCTURE_POINTER *Smbios, + IN UINT16 StringNumber + ) +{ + UINT16 Index; + CHAR8 *String; + + // + // Skip over formatted section + // + String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length); + + // + // Look through unformated section + // + for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) { + if (StringNumber == Index) { + return String; + } + // + // Skip string + // + for (; *String != 0; String++) + ; + String++; + + if (*String == 0) { + // + // If double NULL then we are done. + // Return pointer to next structure in Smbios. + // if you pass in a 0 you will always get here + // + Smbios->Raw = (UINT8 *)++String; + return NULL; + } + } + + return NULL; +} + + +/** + This function gets system guid and serial number from the smbios table + + @param SystemGuid The pointer of returned system guid + @param SystemSerialNumber The pointer of returned system serial number + + @retval EFI_SUCCESS Successfully get the system guid and system serial + number + @retval EFI_NOT_FOUND Not find the SMBIOS table + +**/ +EFI_STATUS +PxeBcLibGetSmbiosSystemGuidAndSerialNumber ( + IN EFI_GUID *SystemGuid, + OUT CHAR8 **SystemSerialNumber + ) +{ + EFI_STATUS Status; + SMBIOS_STRUCTURE_TABLE *SmbiosTable; + SMBIOS_STRUCTURE_POINTER Smbios; + SMBIOS_STRUCTURE_POINTER SmbiosEnd; + UINT16 Index; + + Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable); + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + Smbios.Hdr = (SMBIOS_HEADER *) (UINTN) SmbiosTable->TableAddress; + SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength); + + for (Index = 0; Index < SmbiosTable->TableLength; Index++) { + if (Smbios.Hdr->Type == 1) { + if (Smbios.Hdr->Length < 0x19) { + // + // Older version did not support Guid and Serial number + // + continue; + } + // + // SMBIOS tables are byte packed so we need to do a byte copy to + // prevend alignment faults on Itanium-based platform. + // + CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID)); + *SystemSerialNumber = PxeBcLibGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber); + + return EFI_SUCCESS; + } + // + // Make Smbios point to the next record + // + PxeBcLibGetSmbiosString (&Smbios, 0); + + if (Smbios.Raw >= SmbiosEnd.Raw) { + // + // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e. + // given this we must double check against the lenght of + // the structure. + // + return EFI_SUCCESS; + } + } + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// add router list to list +// +STATIC +VOID +Ip4AddRouterList ( + PXE_BASECODE_DEVICE *Private, + DHCPV4_OP_IP_LIST *IpListPtr + ) +{ + EFI_IP_ADDRESS TmpIp; + INTN Index; + INTN num; + + if (IpListPtr == NULL) { + return ; + } + + for (Index = 0, num = IpListPtr->Header.Length >> 2; Index < num; ++Index) { + CopyMem (&TmpIp, &IpListPtr->IpList[Index], 4); + Ip4AddRouter (Private, &TmpIp); + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send ARP for our IP - fail if someone has it +// +STATIC +BOOLEAN +SetStationIP ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_MAC_ADDRESS DestMac; + EFI_STATUS EfiStatus; + + ZeroMem (&DestMac, sizeof DestMac); + + if (GetHwAddr(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac) + || DoArp(Private, (EFI_IP_ADDRESS *)&DHCP_REQ_OPTIONS.OpReqIP.Ip, (EFI_MAC_ADDRESS *)&DestMac) == EFI_SUCCESS) { + return FALSE; // somebody else has this IP + } + + CopyMem ( + (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->StationIp, + &DHCP_REQ_OPTIONS.OpReqIP.Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + + Private->GoodStationIp = TRUE; + + if (!Private->UseIgmpv1Reporting) { + return TRUE; + } + + if (Private->Igmpv1TimeoutEvent != NULL) { + return TRUE; + } + + EfiStatus = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->Igmpv1TimeoutEvent + ); + + if (EFI_ERROR (EfiStatus)) { + Private->Igmpv1TimeoutEvent = NULL; + return TRUE; + } + + EfiStatus = gBS->SetTimer ( + Private->Igmpv1TimeoutEvent, + TimerRelative, + (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000 + ); /* 400 seconds */ + + if (EFI_ERROR (EfiStatus)) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +AddRouters ( + PXE_BASECODE_DEVICE *Private, + DHCP_RECEIVE_BUFFER *RxBufPtr + ) +{ + Ip4AddRouterList ( + Private, + (DHCPV4_OP_IP_LIST *) RxBufPtr->OpAdds.PktOptAdds[OP_ROUTER_LIST_IX - 1] + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +EFI_STATUS +DoUdpWrite ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ClientIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr + ) +{ + UINTN Len; + + Len = sizeof DHCPV4_TRANSMIT_BUFFER; + + return UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + ClientIpPtr, + ClientPortPtr, + 0, + 0, + &Len, + Private->TransmitBuffer + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// initialize the DHCP structure +// +typedef struct { + UINT8 x[4]; +} C4Str; + +STATIC +VOID +InitDhcpv4TxBuf ( + PXE_BASECODE_DEVICE *Private + ) +{ + UINTN HwAddrLen; + UINT8 *String; + CHAR8 *SystemSerialNumber; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + + PxebcMode = Private->EfiBc.Mode; + + ZeroMem (&DHCPV4_TRANSMIT_BUFFER, sizeof (DHCPV4_STRUCT)); + DHCPV4_TRANSMIT_BUFFER.op = BOOTP_REQUEST; + DHCPV4_TRANSMIT_BUFFER.htype = Private->SimpleNetwork->Mode->IfType; + DHCPV4_TRANSMIT_BUFFER.flags = HTONS (DHCP_BROADCAST_FLAG); + CopyMem (&DHCPV4_OPTIONS_BUFFER, (VOID *) &DHCPOpStart, sizeof (DHCPOpStart)); + + // + // default to hardware address + // + HwAddrLen = Private->SimpleNetwork->Mode->HwAddressSize; + + if (HwAddrLen > sizeof DHCPV4_TRANSMIT_BUFFER.chaddr) { + HwAddrLen = sizeof DHCPV4_TRANSMIT_BUFFER.chaddr; + } + + String = (UINT8 *) &Private->SimpleNetwork->Mode->CurrentAddress; + + if (PxeBcLibGetSmbiosSystemGuidAndSerialNumber ( + (EFI_GUID *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, + &SystemSerialNumber + ) == EFI_SUCCESS) { + if (PxebcMode->SendGUID) { + HwAddrLen = sizeof (EFI_GUID); + String = (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid; + } + } else { + // + // GUID not yet set - send all 0xff's to show programable (via SetVariable) + // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff); + // GUID not yet set - send all 0's to show not programable + // + ZeroMem (DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof (EFI_GUID)); + } + + DHCPV4_TRANSMIT_BUFFER.hlen = (UINT8) HwAddrLen; + CopyMem (DHCPV4_TRANSMIT_BUFFER.chaddr, String, HwAddrLen); + + CvtNum ( + SYS_ARCH, + (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType, + sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.ArchitectureType + ); + + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.Type = Private->NiiPtr->Type; + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion = Private->NiiPtr->MajorVer; + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion = Private->NiiPtr->MinorVer; + + *(C4Str *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.InterfaceName = *(C4Str *) Private->NiiPtr->StringId; + + CvtNum ( + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MajorVersion, + (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor, + sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMajor + ); + + CvtNum ( + DHCPV4_OPTIONS_BUFFER.DhcpNetworkInterface.MinorVersion, + (UINT8 *) DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor, + sizeof DHCPV4_OPTIONS_BUFFER.DhcpClassIdentifier.Data.UndiMinor + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +UINT32 +DecodePxeOptions ( + DHCP_RECEIVE_BUFFER *RxBufPtr, + UINT8 *ptr, + INTN Len + ) +{ + UINT8 Op; + UINT8 *EndPtr; + INTN Index; + UNION_PTR LocalPtr; + UINT32 status; + + status = 0; + + for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) { + Op = ptr[0]; + Len = ptr[1]; + + switch (Op) { + case OP_PAD: + Len = -1; + break; + + case OP_END: + return status; + + default: + LocalPtr.BytePtr = ptr; + if (Op <= MAX_OUR_PXE_OPT) { + Index = ourPXEopts[Op - 1]; + if (Index) { + RxBufPtr->OpAdds.PxeOptAdds[Index - 1] = LocalPtr.OpPtr; + status |= 1 << Index; + if (Index == VEND_PXE_BOOT_ITEM && LocalPtr.BootItem->Header.Length == 3) { + RxBufPtr->OpAdds.Status |= USE_THREE_BYTE; + } + } + } + break; + } + } + + return status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +DecodeOptions ( + DHCP_RECEIVE_BUFFER *RxBufPtr, + UINT8 *ptr, + INTN Len + ) +{ + UINT8 Op; + UINT8 *EndPtr; + INTN Index; + UNION_PTR LocalPtr; + + for (EndPtr = ptr + Len; ptr < EndPtr; ptr += Len + 2) { + Op = ptr[0]; + Len = ptr[1]; + + switch (Op) { + case OP_PAD: + Len = -1; + break; + + case OP_END: + return ; + + default: + LocalPtr.BytePtr = ptr; + if (Op <= MAX_OUR_OPT) { + Index = OurDhcpOptions[Op - 1]; + if (Index) { + RxBufPtr->OpAdds.PktOptAdds[Index - 1] = LocalPtr.OpPtr; + if (Index == OP_VENDOR_SPECIFIC_IX) { + UINT32 status; + status = DecodePxeOptions ( + RxBufPtr, + (UINT8 *) LocalPtr.VendorOptions->VendorOptions, + LocalPtr.VendorOptions->Header.Length + ); + if (status) { + RxBufPtr->OpAdds.Status |= PXE_TYPE; + // + // check for all the MTFTP info options present - any missing is a nogo + // + if ((status & WfM11a_OPTS) == WfM11a_OPTS) { + RxBufPtr->OpAdds.Status |= WfM11a_TYPE; + } + + if (status & DISCOVER_OPTS) { + RxBufPtr->OpAdds.Status |= DISCOVER_TYPE; + } + + if (status & CREDENTIALS_OPT) { + RxBufPtr->OpAdds.Status |= CREDENTIALS_TYPE; + } + } + } + } + } + break; + } + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +Parse ( + DHCP_RECEIVE_BUFFER *RxBufPtr, + INTN Len + ) +{ + UNION_PTR LocalPtr; + + // + // initialize + // + SetMem (&RxBufPtr->OpAdds, sizeof RxBufPtr->OpAdds, 0); + + DecodeOptions ( + RxBufPtr, + RxBufPtr->u.Dhcpv4.options + 4, + Len - (sizeof RxBufPtr->u.Dhcpv4 - sizeof RxBufPtr->u.Dhcpv4.options + 4) + ); + + LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_OPTION_OVERLOAD_IX - 1]; + + if ((LocalPtr.OpPtr) && (LocalPtr.Overload->Overload & OVLD_SRVR_NAME)) { + DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.sname, sizeof RxBufPtr->u.Dhcpv4.sname); + } + + if (LocalPtr.OpPtr && (LocalPtr.Overload->Overload & OVLD_FILE)) { + DecodeOptions (RxBufPtr, RxBufPtr->u.Dhcpv4.file, sizeof RxBufPtr->u.Dhcpv4.file); + } else if (!RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && RxBufPtr->u.Dhcpv4.file[0]) { + RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] = (DHCPV4_OP_STRUCT *) (RxBufPtr->u.Dhcpv4.file - sizeof (DHCPV4_OP_HEADER)); + + RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length = (UINT8) AsciiStrLen (RxBufPtr->u.Dhcpv4.file); + } + + LocalPtr.OpPtr = RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_CLASS_IDENTIFIER_IX - 1]; + + if ((LocalPtr.OpPtr) && + LocalPtr.PxeClassStr->Header.Length >= 9 && + !CompareMem (LocalPtr.PxeClassStr->Class, "PXEClient", 9) + ) { + RxBufPtr->OpAdds.Status |= PXE_TYPE; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +CopyParseRxBuf ( + PXE_BASECODE_DEVICE *Private, + INTN RxBufIndex, + INTN PacketIndex + ) +{ + DHCP_RECEIVE_BUFFER *RxBufPtr; + + RxBufPtr = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[PacketIndex]; + + CopyMem ( + &RxBufPtr->u.Dhcpv4, + &RxBuf[RxBufIndex].u.Dhcpv4, + sizeof (RxBuf[RxBufIndex].u.Dhcpv4) + ); + + Parse (RxBufPtr, sizeof RxBufPtr->u.ReceiveBuffer); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +CopyProxyRxBuf ( + PXE_BASECODE_DEVICE *Private, + INTN RxBufIndex + ) +{ + Private->EfiBc.Mode->ProxyOfferReceived = TRUE; + CopyParseRxBuf (Private, RxBufIndex, PXE_OFFER_INDEX); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +CopyParse ( + PXE_BASECODE_DEVICE *Private, + EFI_PXE_BASE_CODE_PACKET *PacketPtr, + EFI_PXE_BASE_CODE_PACKET *NewPacketPtr, + INTN Index + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + + DhcpRxBuf = &((DHCP_RECEIVE_BUFFER *) Private->DhcpPacketBuffer)[Index]; + + CopyMem ( + (EFI_PXE_BASE_CODE_PACKET *) &DhcpRxBuf->u.Dhcpv4, + NewPacketPtr, + sizeof (*NewPacketPtr) + ); + + CopyMem (&*PacketPtr, &*NewPacketPtr, sizeof (*NewPacketPtr)); + + Parse (DhcpRxBuf, sizeof DhcpRxBuf->u.ReceiveBuffer); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +AckEdit ( + DHCP_RECEIVE_BUFFER *DhcpRxBuf + ) +{ + UNION_PTR LocalPtr; + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]; + + // + // check that an ACK + // if a DHCP type, must be DHCPOFFER and must have server id + // + return (BOOLEAN) + ( + (LocalPtr.OpPtr) && + (LocalPtr.MessageType->Type == DHCPACK) && + DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1] + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// if a discover type packet, make sure all required fields are present +// +BOOLEAN +DHCPOfferAckEdit ( + DHCP_RECEIVE_BUFFER *DhcpRxBuf + ) +{ + PXE_OP_SERVER_LIST *BootServerOpPtr; + UNION_PTR LocalPtr; + + if ((DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) == 0) { + return TRUE; + } + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1]; + + if (LocalPtr.OpPtr == NULL) { + LocalPtr.OpPtr = (DHCPV4_OP_STRUCT *) &DefaultDisCtl; + DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultDisCtl; + } + // + // make sure all required fields are here + // if mucticast enabled, need multicast address + // + if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST) && + (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1] || !IS_MULTICAST (((DHCPV4_OP_STRUCT *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Data)) + ) { + return FALSE; + // + // missing required field + // + } + // + // if a list, it better be good + // + BootServerOpPtr = (PXE_OP_SERVER_LIST *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1]; + + if (BootServerOpPtr != NULL) { + PXE_SERVER_LIST *BootServerListPtr; + INTN ServerListLen; + INTN ServerEntryLen; + + BootServerListPtr = BootServerOpPtr->ServerList; + ServerListLen = BootServerOpPtr->Header.Length; + + do { + EFI_IPv4_ADDRESS *IpListPtr; + INTN IpCnt; + + IpCnt = BootServerListPtr->u.Ipv4List.IpCount; + + ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS); + + if (ServerListLen < ServerEntryLen) { + // + // missing required field + // + return FALSE; + } + + IpListPtr = BootServerListPtr->u.Ipv4List.IpList; + + while (IpCnt--) { + if (IS_MULTICAST (IpListPtr)) { + // + // missing required field + // + return FALSE; + } else { + ++IpListPtr; + } + } + + BootServerListPtr = (PXE_SERVER_LIST *) IpListPtr; + } while (ServerListLen -= ServerEntryLen); + } + // + // else there must be a list if use list enabled or multicast and + // broadcast disabled + // + else if ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) || + ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST)) + ) { + // + // missing required field + // + return FALSE; + } + // + // if not USE_BOOTFILE or no bootfile given, must have menu stuff + // + if (!(LocalPtr.DiscoveryControl->ControlBits & USE_BOOTFILE) || + !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] + ) { + INTN MenuLth; + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1]; + + if (LocalPtr.OpPtr == NULL || !DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]) { + // + // missing required field + // + return FALSE; + } + // + // make sure menu valid + // + MenuLth = LocalPtr.BootMenu->Header.Length; + LocalPtr.BootMenuItem = LocalPtr.BootMenu->MenuItem; + + do { + INTN MenuItemLen; + + MenuItemLen = LocalPtr.BootMenuItem->DataLen; + + if (MenuItemLen == 0) { + // + // missing required field + // + return FALSE; + } + + MenuItemLen += sizeof (*LocalPtr.BootMenuItem) - sizeof (LocalPtr.BootMenuItem->Data); + + MenuLth -= MenuItemLen; + LocalPtr.BytePtr += MenuItemLen; + } while (MenuLth > 0); + + if (MenuLth != 0) { + // + // missing required field + // + return FALSE; + } + } + + if (!DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) { + DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1] = (DHCPV4_OP_STRUCT *) &DefaultBootItem; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +DHCPAckEdit ( + DHCP_RECEIVE_BUFFER *RxBufPtr + ) +{ + return (BOOLEAN) (DHCPOfferAckEdit (RxBufPtr) ? AckEdit (RxBufPtr) : FALSE); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// get an offer/ack +// +EFI_STATUS +GetOfferAck ( + PXE_BASECODE_DEVICE *Private, + BOOLEAN (*ExtraEdit)(DHCP_RECEIVE_BUFFER *DhcpRxBuf), + UINT16 OpFlags, // for Udp read + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ClientIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ClientPortPtr, + DHCP_RECEIVE_BUFFER *DhcpRxBuf, + EFI_EVENT TimeoutEvent + ) +/*++ +Routine description: + Wait for an OFFER/ACK packet. + +Parameters: + Private := Pointer to PxeBc interface + ExtraEdit := Pointer to extra option checking function + OpFlags := UdpRead() option flags + ServerIpPtr := + ServerPortPtr := + ClientIpPtr := + ClientPortPtr := + DhcpRxBuf := + TimeoutEvent := + +Returns: +--*/ +{ + EFI_IP_ADDRESS ServerIp; + EFI_STATUS StatCode; + INTN RxBufLen; + + for (;;) { + // + // Wait until we get a UDP packet. + // + ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS)); + RxBufLen = sizeof RxBuf[0].u.ReceiveBuffer; + + if ((StatCode = UdpRead ( + Private, + OpFlags, + ClientIpPtr, + ClientPortPtr, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + (UINTN *) &RxBufLen, + &DhcpRxBuf->u.Dhcpv4, + TimeoutEvent + )) != EFI_SUCCESS) { + if (StatCode == EFI_TIMEOUT) { + StatCode = EFI_NO_RESPONSE; + } + + break; + } + // + // got a packet - see if a good offer + // + if (DhcpRxBuf->u.Dhcpv4.op != BOOTP_REPLY) { + continue; + } + + if (DhcpRxBuf->u.Dhcpv4.xid != DHCPV4_TRANSMIT_BUFFER.xid) { + continue; + } + + if (*(UINT32 *) DHCPV4_TRANSMIT_BUFFER.options != * (UINT32 *) DhcpRxBuf->u.Dhcpv4.options) { + continue; + } + + if (*(UINT8 *) &DhcpRxBuf->u.Dhcpv4.yiaddr > 223) { + continue; + } + + if (CompareMem ( + DhcpRxBuf->u.Dhcpv4.chaddr, + DHCPV4_TRANSMIT_BUFFER.chaddr, + sizeof DhcpRxBuf->u.Dhcpv4.chaddr + )) { + // + // no good + // + continue; + } + + Parse (DhcpRxBuf, RxBufLen); + + if (!(*ExtraEdit) (DhcpRxBuf)) { + continue; + } + // + // Good DHCP packet. + // + StatCode = EFI_SUCCESS; + break; + } + + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// get DHCPOFFER's +// +EFI_STATUS +GetOffers ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_IP_ADDRESS ClientIp; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + INTN NumOffers; + INTN Index; + + // + // + // + ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS)); + NumOffers = 0; + + for (Index = 0; Index < (sizeof Private->ServerCount) / sizeof Private->ServerCount[0]; ++Index) { + Private->ServerCount[Index] = 0; + Private->GotProxy[Index] = 0; + } + + Private->GotBootp = 0; + // + // these we throw away + // + Private->GotProxy[DHCP_ONLY_IX] = 1; + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + // + // get offers + // + for (;;) { + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + UNION_PTR LocalPtr; + + DhcpRxBuf = &RxBuf[NumOffers]; + + if (( + StatCode = GetOfferAck ( + Private, + DHCPOfferAckEdit, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + &ServerIp, + &DhcpServerPort, + &ClientIp, + &DHCPClientPort, + DhcpRxBuf, + TimeoutEvent + ) +) != EFI_SUCCESS + ) { + break; + } + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]; + + // + // check type of offer + // + if (LocalPtr.OpPtr == NULL) { + // + // bootp - we only need one and make sure has bootfile + // + if (Private->GotBootp || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + continue; + } + + Private->GotBootp = (UINT8) (NumOffers + 1); + } + // + // if a DHCP type, must be DHCPOFFER and must have server id + // + else if (LocalPtr.MessageType->Type != DHCPOFFER || !DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]) { + continue; + } else { + INTN TypeIx; + + // + // get type - PXE10, WfM11a, or BINL + // + if (DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE) { + TypeIx = PXE10_IX; + } else if (DhcpRxBuf->OpAdds.Status & WfM11a_TYPE) { + // + // WfM - make sure it has a bootfile + // + if (!DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + continue; + } + + TypeIx = WfM11a_IX; + } else { + TypeIx = (DhcpRxBuf->OpAdds.Status & PXE_TYPE) ? BINL_IX : DHCP_ONLY_IX; + } + // + // check DHCP or proxy + // + if (DhcpRxBuf->u.Dhcpv4.yiaddr == 0) { + // + // proxy - only need one of each type if not BINL + // and must have at least PXE_TYPE + // + if (TypeIx == BINL_IX) { + Private->BinlProxies[Private->GotProxy[BINL_IX]++] = (UINT8) NumOffers; + } else if (Private->GotProxy[TypeIx]) { + continue; + } else { + Private->GotProxy[TypeIx] = (UINT8) (NumOffers + 1); + } + } else { + Private->OfferCount[TypeIx][Private->ServerCount[TypeIx]++] = (UINT8) NumOffers; + } + } + + if (++NumOffers == MAX_OFFERS) { + break; + } + } + + gBS->CloseEvent (TimeoutEvent); + Private->NumOffersReceived = NumOffers; + + return (Private->NumOffersReceived) ? EFI_SUCCESS : EFI_NO_RESPONSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send DHCPDECLINE +// +STATIC +VOID +DeclineOffer ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + UINT16 SaveSecs; + + PxebcMode = Private->EfiBc.Mode; + SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs; + + DHCPV4_TRANSMIT_BUFFER.secs = 0; + DHCPV4_TRANSMIT_BUFFER.flags = 0; + SetMem ( + DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opdeclinestr), + sizeof (DHCPOpStart) - sizeof (struct opdeclinestr), + OP_PAD + ); + DHCPDECLINEoptions.DhcpMessageType.Type = DHCPDECLINE; + CopyMem (&DHCPDECLINEoptions.OpDeclineEnd, &DHCP_REQ_OPTIONS, sizeof (struct requestopendstr)); + + { + EFI_IP_ADDRESS TmpIp; + + CopyMem (&TmpIp, &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, sizeof TmpIp); + + DoUdpWrite ( + Private, + &TmpIp, + &DhcpServerPort, + &PxebcMode->StationIp, + &DHCPClientPort + ); + } + + InitDhcpv4TxBuf (Private); + DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs; + Private->GoodStationIp = FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send DHCPRELEASE +// +STATIC +BOOLEAN +Release ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + UINT16 SaveSecs; + + PxebcMode = Private->EfiBc.Mode; + SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs; + DHCPV4_TRANSMIT_BUFFER.secs = 0; + + SetMem ( + DHCPV4_TRANSMIT_BUFFER.options + sizeof (struct opreleasestr), + sizeof (DHCPOpStart) - sizeof (struct opreleasestr), + OP_PAD + ); + + DHCPRELEASEoptions.DhcpMessageType.Type = DHCPRELEASE; + + CopyMem ( + &DHCPRELEASEoptions.DhcServerIpPtr, + &(DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1], + sizeof DHCPRELEASEoptions.DhcServerIpPtr + ); + + DHCPRELEASEoptions.End[0] = OP_END; + + { + EFI_IP_ADDRESS TmpIp; + + CopyMem (&TmpIp, &DHCPRELEASEoptions.DhcServerIpPtr.Ip, sizeof TmpIp); + + DoUdpWrite ( + Private, + &TmpIp, + &DhcpServerPort, + &PxebcMode->StationIp, + &DHCPClientPort + ); + } + + InitDhcpv4TxBuf (Private); + + DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs; + Private->GoodStationIp = FALSE; + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +GetBINLAck ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return FALSE; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return FALSE; + } + // + // + // + DhcpRxBuf = &PXE_BINL_BUFFER; + + for (;;) { + EFI_PXE_BASE_CODE_UDP_PORT BINLSrvPort; + + BINLSrvPort = 0; + + if (GetOfferAck ( + Private, + AckEdit, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + ServerIpPtr, + &BINLSrvPort, + &Private->EfiBc.Mode->StationIp, + &PSEUDO_DHCP_CLIENT_PORT, + DhcpRxBuf, + TimeoutEvent + ) != EFI_SUCCESS) { + break; + } + // + // make sure from whom we wanted + // + if (!DhcpRxBuf->u.Dhcpv4.yiaddr && !CompareMem ( + &ServerIpPtr->v4, + &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof (ServerIpPtr->v4) + )) { + gBS->CloseEvent (TimeoutEvent); + // + // got an ACK from server + // + return TRUE; + } + } + + gBS->CloseEvent (TimeoutEvent); + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// make sure we can get BINL +// send DHCPREQUEST to PXE server +// +STATIC +BOOLEAN +TryBINL ( + PXE_BASECODE_DEVICE *Private, + INTN OfferIx + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + EFI_IP_ADDRESS ServerIp; + UINT16 SaveSecs; + INTN Index; + + DhcpRxBuf = &RxBuf[OfferIx]; + + // + // use next server address first. + // + ServerIp.Addr[0] = DhcpRxBuf->u.Dhcpv4.siaddr; + if (ServerIp.Addr[0] == 0) { + // + // next server address is NULL, use option 54. + // + CopyMem ( + ((EFI_IPv4_ADDRESS *) &ServerIp), + &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + // + // client IP address - filled in by client if it knows it + // + CopyMem ( + ((EFI_IPv4_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr), + &DHCP_REQ_OPTIONS.OpReqIP.Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + + SetMem (&DHCP_REQ_OPTIONS, sizeof DHCP_REQ_OPTIONS, OP_PAD); + DHCPV4_TRANSMIT_BUFFER.flags = 0; + DHCPV4_OPTIONS_BUFFER.End[0] = OP_END; + AddRouters (Private, DhcpRxBuf); + SaveSecs = DHCPV4_TRANSMIT_BUFFER.secs; + + for (Index = 0; Index < 3; Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Index) { + DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds); + + // + // unicast DHCPREQUEST to PXE server + // + if (DoUdpWrite ( + Private, + &ServerIp, + &PseudoDhcpServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &PSEUDO_DHCP_CLIENT_PORT + ) != EFI_SUCCESS) { + break; + } + + if (!GetBINLAck (Private, &ServerIp)) { + continue; + } + // + // early exit failures + // make sure a good ACK + // + if (!DHCPOfferAckEdit (&PXE_BINL_BUFFER) || ( + !(PXE_BINL_BUFFER.OpAdds.Status & DISCOVER_TYPE) && !PXE_BINL_BUFFER.OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] + ) + ) { + break; + } + + Private->EfiBc.Mode->ProxyOfferReceived = TRUE; + return TRUE; + } + // + // failed - reset seconds field, etc. + // + Private->EfiBc.Mode->RouteTableEntries = 0; + // + // reset + // + DHCPV4_TRANSMIT_BUFFER.secs = SaveSecs; + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +TryFinishBINL ( + PXE_BASECODE_DEVICE *Private, + INTN OfferIx + ) +{ + if (TryBINL (Private, OfferIx)) { + return TRUE; + } + + return Release (Private); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +TryFinishProxyBINL ( + PXE_BASECODE_DEVICE *Private + ) +{ + INTN Index; + + for (Index = 0; Index < Private->GotProxy[BINL_IX]; ++Index) { + if (TryBINL (Private, Private->BinlProxies[Index])) { + return TRUE; + } + } + + return Release (Private); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// try to finish DORA - send DHCP request, wait for ACK, check with ARP +// +STATIC +BOOLEAN +TryFinishDORA ( + PXE_BASECODE_DEVICE *Private, + INTN OfferIx + ) +{ + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + EFI_IP_ADDRESS ClientIp; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS StatCode; + UNION_PTR LocalPtr; + EFI_EVENT TimeoutEvent; + + // + // send DHCP request + // if fail return false + // + DhcpRxBuf = &DHCPV4_ACK_BUFFER; + DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type = DHCPREQUEST; + CopyMem (&DHCP_REQ_OPTIONS, &RequestOpEndStr, sizeof (RequestOpEndStr)); +// DHCP_REQ_OPTIONS = RequestOpEndStr; + DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[OfferIx].u.Dhcpv4.yiaddr; + + CopyMem ( + &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, + &((DHCPV4_OP_SERVER_IP *) RxBuf[OfferIx].OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip + ); + + CopyMem ( + Private->EfiBc.Mode->SubnetMask.Addr, + &DefaultSubnetMask, + 4 + ); + + // + // broadcast DHCPREQUEST + // + if (DoUdpWrite ( + Private, + &BroadcastIP, + &DhcpServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &DHCPClientPort + ) != EFI_SUCCESS) { + return FALSE; + } + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return FALSE; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerPeriodic, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return FALSE; + } + // + // wait for ACK + // + for (;;) { + if (GetOfferAck ( + Private, + DHCPAckEdit, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP, + &ServerIp, + &DhcpServerPort, + &ClientIp, + &DHCPClientPort, + DhcpRxBuf, + TimeoutEvent + ) != EFI_SUCCESS) { + break; + } + // + // check type of response - need DHCPACK + // + if (CompareMem ( + &DHCP_REQ_OPTIONS.OpReqIP.Ip, + &DhcpRxBuf->u.Dhcpv4.yiaddr, + sizeof (EFI_IPv4_ADDRESS) + ) || CompareMem ( + &DHCP_REQ_OPTIONS.DhcServerIpPtr.Ip, + &((DHCPV4_OP_SERVER_IP *) DhcpRxBuf->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip, + sizeof (EFI_IPv4_ADDRESS) + )) { + continue; + } + // + // got ACK + // check with ARP that IP unused - good return true + // + if (!SetStationIP (Private)) { + // + // fail - send DHCPDECLINE and return false + // + DeclineOffer (Private); + break; + } + + LocalPtr.OpPtr = DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1]; + + if (LocalPtr.OpPtr != NULL) { + CopyMem ( + (EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask, + &LocalPtr.SubnetMaskStr->Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + AddRouters (Private, DhcpRxBuf); + gBS->CloseEvent (TimeoutEvent); + return TRUE; + } + + gBS->CloseEvent (TimeoutEvent); + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// try a DHCP server of appropriate type +// +STATIC +BOOLEAN +TryDHCPFinishDORA ( + PXE_BASECODE_DEVICE *Private, + INTN TypeIx + ) +{ + INTN Index; + + // + // go through the DHCP servers of the requested type + // + for (Index = 0; Index < Private->ServerCount[TypeIx]; ++Index) { + if (TryFinishDORA (Private, Index = Private->OfferCount[TypeIx][Index])) { + if (TypeIx == BINL_IX && !TryFinishBINL (Private, Index)) { + continue; + } + + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// try a DHCP only server and a proxy of appropriate type +// +STATIC +BOOLEAN +TryProxyFinishDORA ( + PXE_BASECODE_DEVICE *Private, + INTN TypeIx + ) +{ + INTN Index; + + if (!Private->GotProxy[TypeIx]) { + // + // no proxies of the type wanted + // + return FALSE; + } + // + // go through the DHCP only servers + // + for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) { + if (TryFinishDORA (Private, Private->OfferCount[DHCP_ONLY_IX][Index])) { + if (TypeIx != BINL_IX) { + CopyProxyRxBuf (Private, Private->GotProxy[TypeIx] - 1); + } else if (!TryFinishProxyBINL (Private)) { + // + // if didn't work with this DHCP, won't work with any + // + return FALSE; + } + + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// getting to the bottom of the barrel +// +STATIC +BOOLEAN +TryAnyWithBootfileFinishDORA ( + PXE_BASECODE_DEVICE *Private + ) +{ + // + // try a DHCP only server who has a bootfile + // + UNION_PTR LocalPtr; + INTN Index; + + for (Index = 0; Index < Private->ServerCount[DHCP_ONLY_IX]; ++Index) { + INTN offer; + + offer = Private->OfferCount[DHCP_ONLY_IX][Index]; + + if (RxBuf[offer].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] && TryFinishDORA (Private, offer)) { + return TRUE; + } + } + // + // really at bottom - see if be have any bootps + // + if (!Private->GotBootp) { + return FALSE; + } + + DHCP_REQ_OPTIONS.OpReqIP.Ip = *(EFI_IPv4_ADDRESS *) &RxBuf[Private->GotBootp - 1].u.Dhcpv4.yiaddr; + + if (!SetStationIP (Private)) { + return FALSE; + } + // + // treat BOOTP response as DHCP ACK packet + // + CopyParseRxBuf (Private, Private->GotBootp - 1, DHCPV4_ACK_INDEX); + + LocalPtr.OpPtr = RxBuf[Private->GotBootp - 1].OpAdds.PktOptAdds[OP_SUBNET_MASK_IX - 1]; + + if (LocalPtr.OpPtr != NULL) { + *(EFI_IPv4_ADDRESS *) &Private->EfiBc.Mode->SubnetMask = LocalPtr.SubnetMaskStr->Ip; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* DoDhcpDora() + */ +STATIC +EFI_STATUS +DoDhcpDora ( + PXE_BASECODE_DEVICE *Private, + BOOLEAN SortOffers + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS StatCode; + INTN NumOffers; + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP | EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST; + + Filter.IpCnt = 0; + Filter.reserved = 0; + + // + // set filter unicast or broadcast + // + if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + return StatCode; + } + // + // seed random number with hardware address + // + SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress); + + for (Private->Timeout = 1; + Private->Timeout < 17; + Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), Private->Timeout <<= 1 + ) { + INTN Index; + + InitDhcpv4TxBuf (Private); + DHCPV4_TRANSMIT_BUFFER.xid = Random (Private); + DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds); + + // + // broadcast DHCPDISCOVER + // + StatCode = DoUdpWrite ( + Private, + &BroadcastIP, + &DhcpServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &DHCPClientPort + ); + + if (StatCode != EFI_SUCCESS) { + return StatCode; + } + + CopyMem ( + &Private->EfiBc.Mode->DhcpDiscover, + (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + + // + // get DHCPOFFER's + // + if ((StatCode = GetOffers (Private)) != EFI_SUCCESS) { + if (StatCode != EFI_NO_RESPONSE) { + return StatCode; + } + + continue; + } + // + // select offer and reply DHCPREQUEST + // + if (SortOffers) { + if (TryDHCPFinishDORA(Private, PXE10_IX) || // try DHCP with PXE10 + TryDHCPFinishDORA(Private, WfM11a_IX) || // no - try with WfM + TryProxyFinishDORA(Private, PXE10_IX) || // no - try DHCP only and proxy with PXE10 + TryProxyFinishDORA(Private, WfM11a_IX) || // no - try DHCP only and proxy with WfM + TryDHCPFinishDORA(Private, BINL_IX) || // no - try with WfM + TryProxyFinishDORA(Private, BINL_IX) || // no - try DHCP only and proxy with PXE10 + TryAnyWithBootfileFinishDORA(Private)) + { + return EFI_SUCCESS; + } + + continue; + } + // + // FIFO order + // + NumOffers = Private->NumOffersReceived; + + for (Index = 0; Index < NumOffers; ++Index) { + // + // ignore proxies + // + if (!RxBuf[Index].u.Dhcpv4.yiaddr) { + continue; + } + // + // check if a bootp server + // + if (!RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1]) { + // + // it is - just check ARP + // + if (!SetStationIP (Private)) { + continue; + } + } + // + // else check if a DHCP only server + // + else if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE))) { + // + // it is a normal DHCP offer (without any PXE options), just finish the D.O.R.A by sending DHCP request. + // + if (!TryFinishDORA (Private, Index)) { + continue; + } + } else if (TryFinishDORA (Private, Index)) { + if (!(RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) && !TryFinishBINL (Private, Index)) { + continue; + } + } + + DEBUG ((DEBUG_WARN, "\nDoDhcpDora() Got packets. ")); + return EFI_SUCCESS; + } + // + // now look for DHCP onlys and a Proxy + // + for (Index = 0; Index < NumOffers; ++Index) { + INT8 Index2; + + // + // ignore proxies, bootps, non DHCP onlys, and bootable DHCPS + // + if (!RxBuf[Index].u.Dhcpv4.yiaddr || + !RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_MESSAGE_TYPE_IX - 1] || + RxBuf[Index].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE | PXE_TYPE) || + RxBuf[Index].OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] + ) { + continue; + } + // + // found non bootable DHCP only - try to find a proxy + // + for (Index2 = 0; Index2 < NumOffers; ++Index2) { + if (!RxBuf[Index2].u.Dhcpv4.yiaddr) { + if (!TryFinishDORA (Private, Index)) { + // + // DHCP no ACK + // + break; + } + + if (RxBuf[Index2].OpAdds.Status & (DISCOVER_TYPE | WfM11a_TYPE)) { + CopyProxyRxBuf (Private, Index2); + } else if (!TryFinishBINL (Private, Index2)) { + continue; + } + + DEBUG ((DEBUG_WARN, "\nDoDhcpDora() Got packets. ")); + return EFI_SUCCESS; + } + } + } + } + + return EFI_NO_RESPONSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// determine if the server ip is in the ip list +// +BOOLEAN +InServerList ( + EFI_IP_ADDRESS *ServerIpPtr, + PXE_SERVER_LISTS *ServerListPtr + ) +{ + UINTN Index; + + if (!ServerListPtr || !ServerListPtr->Ipv4List.IpCount) { + return TRUE; + } + + for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) { + if (!CompareMem ( + ServerIpPtr, + &ServerListPtr->Ipv4List.IpList[Index], + sizeof (EFI_IPv4_ADDRESS) + )) { + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +ExtractBootServerList ( + UINT16 Type, + DHCPV4_OP_STRUCT *ptr, + PXE_SERVER_LISTS **ServerListPtr + ) +{ + UNION_PTR LocalPtr; + INTN ServerListLen; + + LocalPtr.OpPtr = ptr; + ServerListLen = LocalPtr.BootServersStr->Header.Length; + + // + // find type + // + LocalPtr.BootServerList = LocalPtr.BootServersStr->ServerList; + + while (ServerListLen) { + INTN ServerEntryLen; + + ServerEntryLen = sizeof (PXEV4_SERVER_LIST) + 2 + (LocalPtr.BootServerList->u.Ipv4List.IpCount - 1) * + sizeof (EFI_IPv4_ADDRESS); + + if (NTOHS (LocalPtr.BootServerList->Type) == Type) { + *ServerListPtr = &LocalPtr.BootServerList->u; + return TRUE; + } + + (LocalPtr.BytePtr) += ServerEntryLen; + ServerListLen -= ServerEntryLen; + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +FreeMem ( + PXE_BASECODE_DEVICE *Private + ) +{ + if (Private->TransmitBuffer != NULL) { + gBS->FreePool (Private->TransmitBuffer); + Private->TransmitBuffer = NULL; + } + + if (Private->ReceiveBuffers != NULL) { + gBS->FreePool (Private->ReceiveBuffers); + Private->ReceiveBuffers = NULL; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +BOOLEAN +GetMem ( + PXE_BASECODE_DEVICE *Private + ) +{ + EFI_STATUS Status; + + if (Private->DhcpPacketBuffer == NULL) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1), + &Private->DhcpPacketBuffer + ); + + if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) { + Private->DhcpPacketBuffer = NULL; + FreeMem (Private); + return FALSE; + } + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_PXE_BASE_CODE_PACKET), + &Private->TransmitBuffer + ); + + if (EFI_ERROR (Status) || Private->TransmitBuffer == NULL) { + gBS->FreePool (Private->DhcpPacketBuffer); + Private->DhcpPacketBuffer = NULL; + Private->TransmitBuffer = NULL; + FreeMem (Private); + return FALSE; + } + + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (DHCP_RECEIVE_BUFFER) * (MAX_OFFERS), + &Private->ReceiveBuffers + ); + + if (EFI_ERROR (Status) || Private->ReceiveBuffers == NULL) { + gBS->FreePool (Private->TransmitBuffer); + gBS->FreePool (Private->DhcpPacketBuffer); + Private->DhcpPacketBuffer = NULL; + Private->TransmitBuffer = NULL; + Private->ReceiveBuffers = NULL; + FreeMem (Private); + return FALSE; + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +EFI_STATUS +EFIAPI +BcDhcp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + PXE_BASECODE_DEVICE *Private; + EFI_STATUS StatCode; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + Filter.IpCnt = 0; + Filter.reserved = 0; + + DEBUG ((DEBUG_INFO, "\nBcDhcp() Enter. ")); + + PxebcMode = Private->EfiBc.Mode; + + if (!GetMem (Private)) { + DEBUG ((DEBUG_ERROR, "\nBcDhcp() GetMem() failed.\n")); + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + PxebcMode->DhcpDiscoverValid = FALSE; + PxebcMode->DhcpAckReceived = FALSE; + PxebcMode->ProxyOfferReceived = FALSE; + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP; + + // + // Issue BC command + // + if (Private->TotalSeconds == 0) { + // + // put in seconds field of DHCP send packets + // + Private->TotalSeconds = 4; + } + + if ((StatCode = DoDhcpDora (Private, SortOffers)) == EFI_SUCCESS) { + // + // success - copy packets + // + PxebcMode->DhcpDiscoverValid = PxebcMode->DhcpAckReceived = TRUE; + + CopyMem ( + &PxebcMode->DhcpAck, + (EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_ACK_PACKET, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + + if (PxebcMode->ProxyOfferReceived) { + CopyMem ( + &PxebcMode->ProxyOffer, + (EFI_PXE_BASE_CODE_PACKET *) &PXE_OFFER_PACKET, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + } + } + // + // set filter back to unicast + // + IpFilter (Private, &Filter); + + FreeMem (Private); + + // + // Unlock the instance data + // + DEBUG ((DEBUG_WARN, "\nBcDhcp() Exit = %xh ", StatCode)); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +BOOLEAN +VerifyCredentialOption ( + UINT8 *tx, + UINT8 *rx + ) +{ + UINTN n; + + // + // Fail verification if either pointer is NULL. + // + if (tx == NULL || rx == NULL) { + return FALSE; + } + // + // Fail verification if tx[0] is not a credential type option + // or if the length is zero or not a multiple of four. + // + if (tx[0] != VEND_PXE_CREDENTIAL_TYPES || tx[1] == 0 || tx[1] % 4 != 0) { + return FALSE; + } + // + // Fail verification if rx[0] is not a credential type option + // or if the length is not equal to four. + // + if (rx[0] != VEND_PXE_CREDENTIAL_TYPES || rx[1] != 4) { + return FALSE; + } + // + // Look through transmitted credential types for a copy + // of the received credential type. + // + for (n = 0; n < tx[1]; n += 4) { + if (!CompareMem (&tx[n + 2], &rx[2], 4)) { + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +EFI_STATUS +DoDiscover ( + PXE_BASECODE_DEVICE *Private, + UINT16 OpFlags, + IN UINT16 Type, + IN UINT16 *LayerPtr, + IN BOOLEAN UseBis, + EFI_IP_ADDRESS *DestPtr, + PXE_SERVER_LISTS *ServerListPtr + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT ClientPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerPort; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_STATUS StatCode; + EFI_EVENT TimeoutEvent; + UINT8 OpLen; + + PxebcMode = Private->EfiBc.Mode; + + if (DestPtr->Addr[0] == 0) { + DEBUG ((DEBUG_WARN, "\nDoDiscover() !DestPtr->Addr[0]")); + return EFI_INVALID_PARAMETER; + } + // + // seed random number with hardware address + // + SeedRandom (Private, *(UINT16 *) &Private->SimpleNetwork->Mode->CurrentAddress); + + if (DestPtr->Addr[0] == BroadcastIP.Addr[0]) { + ClientPort = DHCPClientPort; + ServerPort = DhcpServerPort; + } else { + ClientPort = PSEUDO_DHCP_CLIENT_PORT; + ServerPort = PseudoDhcpServerPort; + } + + if (UseBis) { + *LayerPtr |= PXE_BOOT_LAYER_CREDENTIAL_FLAG; + } else { + *LayerPtr &= PXE_BOOT_LAYER_MASK; + } + + for (Private->Timeout = 1; + Private->Timeout < 5; + Private->TotalSeconds = (UINT16) (Private->TotalSeconds + Private->Timeout), ++Private->Timeout + ) { + InitDhcpv4TxBuf (Private); + // + // initialize DHCP message structure + // + DHCPV4_TRANSMIT_BUFFER.xid = Random (Private); + DHCPV4_TRANSMIT_BUFFER.secs = HTONS (Private->TotalSeconds); + CopyMem ( + &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &PxebcMode->StationIp, + sizeof DHCPV4_TRANSMIT_BUFFER.ciaddr + ); + + DHCPV4_OPTIONS_BUFFER.DhcpMessageType.Type = DHCPREQUEST; + DISCOVERoptions.Header.OpCode = OP_VENDOR_SPECIFIC; + DISCOVERoptions.BootItem.Header.OpCode = VEND_PXE_BOOT_ITEM; + DISCOVERoptions.BootItem.Header.Length = DHCPV4_OPTION_LENGTH (PXE_OP_BOOT_ITEM); + DISCOVERoptions.BootItem.Type = HTONS (Type); + DISCOVERoptions.BootItem.Layer = HTONS (*LayerPtr); + + if (UseBis) { + EFI_BIS_PROTOCOL *BisPtr; + BIS_APPLICATION_HANDLE BisAppHandle; + EFI_BIS_DATA *BisDataSigInfo; + EFI_BIS_SIGNATURE_INFO *BisSigInfo; + UINTN Index; + UINTN Index2; + + BisPtr = PxebcBisStart ( + Private, + &BisAppHandle, + &BisDataSigInfo + ); + + if (BisPtr == NULL) { + // + // %%TBD - In order to get here, BIS must have + // been present when PXEBC.Start() was called. + // BIS had to be shutdown/removed/damaged + // before PXEBC.Discover() was called. + // Do we need to document a specific error + // for this case? + // + return EFI_OUT_OF_RESOURCES; + } + // + // Compute number of credential types. + // + Index2 = BisDataSigInfo->Length / sizeof (EFI_BIS_SIGNATURE_INFO); + + DISCREDoptions.Header.OpCode = VEND_PXE_CREDENTIAL_TYPES; + + DISCREDoptions.Header.Length = (UINT8) (Index2 * sizeof (PXE_CREDENTIAL)); + + OpLen = (UINT8) (DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS) + sizeof (DHCPV4_OP_HEADER) + DISCREDoptions.Header.Length); + + BisSigInfo = (EFI_BIS_SIGNATURE_INFO *) BisDataSigInfo->Data; + + for (Index = 0; Index < Index2; ++Index) { + UINT32 x; + + CopyMem (&x, &BisSigInfo[Index], sizeof x); + x = HTONL (x); + CopyMem (&DISCREDoptions.Credentials[Index], &x, sizeof x); + } + + PxebcBisStop (BisPtr, BisAppHandle, BisDataSigInfo); + } else { + OpLen = DHCPV4_OPTION_LENGTH (PXE_DISCOVER_OPTIONS); + } + + DISCOVERoptions.Header.Length = OpLen; + + ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen - 1] = OP_END; + ((UINT8 *) &DISCOVERoptions)[sizeof (DHCPV4_OP_HEADER) + OpLen] = OP_END; + + StatCode = DoUdpWrite ( + Private, + DestPtr, + &ServerPort, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &ClientPort + ); + + if (StatCode != EFI_SUCCESS) { + return StatCode; + } + // + // + // + StatCode = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + StatCode = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Private->Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (StatCode)) { + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + // + // wait for ACK + // + for (;;) { + DHCP_RECEIVE_BUFFER *RxBufPtr; + UINT16 TmpType; + UINT16 TmpLayer; + + RxBufPtr = UseBis ? &PXE_BIS_BUFFER : &PXE_ACK_BUFFER; + ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + if (GetOfferAck ( + Private, + AckEdit, + OpFlags, + (EFI_IP_ADDRESS *) &Private->ServerIp, + 0, + (EFI_IP_ADDRESS *) &DHCPV4_TRANSMIT_BUFFER.ciaddr, + &ClientPort, + RxBufPtr, + TimeoutEvent + ) != EFI_SUCCESS) { + break; + } + // + // check type of response - need PXEClient DHCPACK of proper type with bootfile + // + if (!(RxBufPtr->OpAdds.Status & PXE_TYPE) || + (UseBis && (RxBufPtr->OpAdds.Status & USE_THREE_BYTE)) || + !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1] || + !RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1] || + !InServerList((EFI_IP_ADDRESS *)&((DHCPV4_OP_SERVER_IP *)RxBufPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX-1])->Ip, ServerListPtr)) { + + continue; + } + + TmpType = TmpLayer = 0; + + if (RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1]) { + TmpType = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Type); + + if (RxBufPtr->OpAdds.Status & USE_THREE_BYTE) { + TmpLayer = (UINT16) (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer >> 8); + } else { + TmpLayer = NTOHS (((PXE_OP_BOOT_ITEM *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_ITEM_IX - 1])->Layer); + } + } + + if (TmpType != Type) { + continue; + } + + if (UseBis) { + if (!RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1]) { + continue; + } + + if (!VerifyCredentialOption ( + (UINT8 *) &DISCREDoptions.Header, + (UINT8 *) RxBufPtr->OpAdds.PxeOptAdds[VEND_PXE_CREDENTIAL_TYPES_IX - 1] + )) { + continue; + } + } + + *LayerPtr = TmpLayer; + + if (UseBis) { + CopyMem ( + &PxebcMode->PxeBisReply, + &RxBufPtr->u.Dhcpv4, + sizeof (EFI_PXE_BASE_CODE_PACKET) + ); + + PxebcMode->PxeBisReplyReceived = TRUE; + + StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + FALSE, + &Private->ServerIp, + 0 + ); + + gBS->CloseEvent (TimeoutEvent); + return StatCode; + } + + PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = TRUE; + + CopyMem ( + &PxebcMode->PxeDiscover, + &*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER, + sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &DHCPV4_TRANSMIT_BUFFER) + ); + + CopyMem ( + &PxebcMode->PxeReply, + &*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4, + sizeof (*(EFI_PXE_BASE_CODE_PACKET *) &RxBufPtr->u.Dhcpv4) + ); + + AddRouters (Private, RxBufPtr); + + gBS->CloseEvent (TimeoutEvent); + return EFI_SUCCESS; + } + + gBS->CloseEvent (TimeoutEvent); + } + // + // end for loop + // + return EFI_TIMEOUT; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Parameters: + Private := Pointer to PxeBc interface + Type := + LayerPtr := + UseBis := + DiscoverInfoPtr := + McastServerListPtr := + ServerListPtr := + + +**/ +STATIC +EFI_STATUS +Discover ( + PXE_BASECODE_DEVICE *Private, + IN UINT16 Type, + IN UINT16 *LayerPtr, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO *DiscoverInfoPtr, + PXE_SERVER_LISTS *McastServerListPtr, + PXE_SERVER_LISTS *ServerListPtr + ) +{ + EFI_IP_ADDRESS DestIp; + EFI_STATUS StatCode; + + DEBUG ((DEBUG_INFO, "\nDiscover() Type=%d Layer=%d ", Type, *LayerPtr)); + + if (UseBis) { + DEBUG ((DEBUG_INFO, "BIS ")); + } + // + // get dest IP addr - mcast, bcast, or unicast + // + if (DiscoverInfoPtr->UseMCast) { + DestIp.v4 = DiscoverInfoPtr->ServerMCastIp.v4; + + DEBUG ( + (DEBUG_INFO, + "\nDiscover() MCast %d.%d.%d.%d ", + DestIp.v4.Addr[0], + DestIp.v4.Addr[1], + DestIp.v4.Addr[2], + DestIp.v4.Addr[3]) + ); + + if ((StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + UseBis, + &DestIp, + McastServerListPtr + )) != EFI_TIMEOUT) { + DEBUG ( + (DEBUG_WARN, + "\nDiscover() status == %r (%Xh)", + StatCode, + StatCode) + ); + + return StatCode; + } + } + + if (DiscoverInfoPtr->UseBCast) { + DEBUG ((DEBUG_INFO, "\nDiscver() BCast ")); + + if ((StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + UseBis, + &BroadcastIP, + McastServerListPtr + )) != EFI_TIMEOUT) { + + DEBUG ((DEBUG_WARN, "\nDiscover() status == %r (%Xh)", StatCode, StatCode)); + + return StatCode; + } + } + + if (DiscoverInfoPtr->UseUCast) { + UINTN Index; + + DEBUG ( + (DEBUG_INFO, + "\nDiscover() UCast IP#=%d ", + ServerListPtr->Ipv4List.IpCount) + ); + + for (Index = 0; Index < ServerListPtr->Ipv4List.IpCount; ++Index) { + CopyMem (&DestIp, &ServerListPtr->Ipv4List.IpList[Index], 4); + + DEBUG ( + (DEBUG_INFO, + "\nDiscover() UCast %d.%d.%d.%d ", + DestIp.v4.Addr[0], + DestIp.v4.Addr[1], + DestIp.v4.Addr[2], + DestIp.v4.Addr[3]) + ); + + if ((StatCode = DoDiscover ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + Type, + LayerPtr, + UseBis, + &DestIp, + 0 + )) != EFI_TIMEOUT) { + DEBUG ( + (DEBUG_WARN, + "\nDiscover() status == %r (%Xh)", + StatCode, + StatCode) + ); + + return StatCode; + } + } + } + + DEBUG ((DEBUG_WARN, "\nDiscover() TIMEOUT")); + + return EFI_TIMEOUT; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* BcDiscover() + */ + +/** + + +**/ +EFI_STATUS +EFIAPI +BcDiscover ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN UINT16 Type, + IN UINT16 *LayerPtr, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO * DiscoverInfoPtr OPTIONAL + ) +{ + EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo; + EFI_PXE_BASE_CODE_MODE *PxebcMode; + DHCP_RECEIVE_BUFFER *DhcpRxBuf; + PXE_SERVER_LISTS DefaultSrvList; + PXE_SERVER_LISTS *ServerListPtr; + PXE_SERVER_LISTS *McastServerListPtr; + UNION_PTR LocalPtr; + UINTN Index; + UINTN Index2; + BOOLEAN AcquiredSrvList; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + ServerListPtr = NULL; + McastServerListPtr = NULL; + AcquiredSrvList = FALSE; + + PxebcMode = Private->EfiBc.Mode; + + if (!GetMem (Private)) { + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + + if (UseBis) { + if (!PxebcMode->BisSupported) { + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER; + + if (Private->TotalSeconds == 0) { + // + // put in seconds field of DHCP send packets + // + Private->TotalSeconds = 4; + } + + ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO)); + + // + // if layer number not zero, use previous discover + // + if (*LayerPtr != 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0")); + + if (DiscoverInfoPtr != NULL) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && DiscoverInfoPtr != NULL\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (!PxebcMode->PxeDiscoverValid) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeDiscoverValid == 0\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (!PxebcMode->PxeReplyReceived) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeReplyReceived == 0\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (UseBis && !PxebcMode->PxeBisReplyReceived) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() layer != 0 && PxeBisReplyReceived == 0\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + DefaultInfo.UseUCast = TRUE; + DiscoverInfoPtr = &DefaultInfo; + + DefaultSrvList.Ipv4List.IpCount = 1; + CopyMem (&DefaultSrvList.Ipv4List.IpList[0], &Private->ServerIp, 4); + + ServerListPtr = &DefaultSrvList; + } + // + // layer is zero - see if info is supplied or if we need to use info from a cached offer + // + else if (!DiscoverInfoPtr) { + // + // not supplied - generate it + // make sure that there is cached, appropriate information + // if neither DhcpAck packet nor ProxyOffer packet has pxe info, fail + // + DhcpRxBuf = (PxebcMode->ProxyOfferReceived) ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER; + + if (!PxebcMode->DhcpAckReceived || !(DhcpRxBuf->OpAdds.Status & DISCOVER_TYPE)) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() !ack && !proxy")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + DiscoverInfoPtr = &DefaultInfo; + + LocalPtr.OpPtr = DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1]; + + // + // if multicast enabled, need multicast address + // + if (!(LocalPtr.DiscoveryControl->ControlBits & DISABLE_MCAST)) { + DefaultInfo.UseMCast = TRUE; + + CopyMem ( + ((EFI_IPv4_ADDRESS *) &DefaultInfo.ServerMCastIp), + &((DHCPV4_OP_IP_ADDRESS *) DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_MCAST_ADDR_IX - 1])->Ip, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + DefaultInfo.UseBCast = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & DISABLE_BCAST) == 0); + + DefaultInfo.MustUseList = (BOOLEAN) ((LocalPtr.DiscoveryControl->ControlBits & USE_ACCEPT_LIST) != 0); + + DefaultInfo.UseUCast = (BOOLEAN) + ( + (DefaultInfo.MustUseList) || + ((LocalPtr.DiscoveryControl->ControlBits & (DISABLE_MCAST | DISABLE_BCAST)) == (DISABLE_MCAST | DISABLE_BCAST)) + ); + + if ((DefaultInfo.UseUCast | DefaultInfo.MustUseList) && !ExtractBootServerList ( + Type, + DhcpRxBuf->OpAdds.PxeOptAdds[VEND_PXE_BOOT_SERVERS_IX - 1], + &ServerListPtr + )) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() type not in list")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + } + // + // Info supplied - make SrvList if required + // if we use ucast discovery or must use list, there better be one + // + else if (DiscoverInfoPtr->UseUCast || DiscoverInfoPtr->MustUseList) { + // + // there better be a list + // + if (DiscoverInfoPtr->IpCnt == 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() no bootserver list")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + // + // get its size + // + for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) { + if (DiscoverInfoPtr->SrvList[Index].Type == Type) { + if (DiscoverInfoPtr->SrvList[Index].AcceptAnyResponse) { + if (Index2 != 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() accept any?")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } else { + Index2 = 1; + DefaultSrvList.Ipv4List.IpCount = 0; + ServerListPtr = &DefaultSrvList; + break; + } + } else { + ++Index2; + } + } + } + + if (Index2 == 0) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() !Index2?")); + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + if (ServerListPtr == NULL) { + ServerListPtr = AllocatePool ( + sizeof (PXEV4_SERVER_LIST) + (Index2 - 1) * sizeof (EFI_IPv4_ADDRESS) + ); + + if (ServerListPtr == NULL) { + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + // + // build an array of IP addresses from the server list + // + AcquiredSrvList = TRUE; + ServerListPtr->Ipv4List.IpCount = (UINT8) Index2; + + for (Index = Index2 = 0; Index < DiscoverInfoPtr->IpCnt; ++Index) { + if (DiscoverInfoPtr->SrvList[Index].Type == Type) { + CopyMem ( + &ServerListPtr->Ipv4List.IpList[Index2++], + &DiscoverInfoPtr->SrvList[Index].IpAddr.v4, + sizeof ServerListPtr->Ipv4List.IpList[0] + ); + } + } + } + } + + if (DiscoverInfoPtr->MustUseList) { + McastServerListPtr = ServerListPtr; + } + + if (!(DiscoverInfoPtr->UseMCast || DiscoverInfoPtr->UseBCast || DiscoverInfoPtr->UseUCast)) { + DEBUG ((DEBUG_WARN, "\nBcDiscover() Nothing to use!\n")); + + EfiReleaseLock (&Private->Lock); + return EFI_INVALID_PARAMETER; + } + + PxebcMode->PxeDiscoverValid = PxebcMode->PxeReplyReceived = PxebcMode->PxeBisReplyReceived = FALSE; + + StatCode = Discover ( + Private, + Type, + LayerPtr, + UseBis, + DiscoverInfoPtr, + McastServerListPtr, + ServerListPtr + ); + + if (AcquiredSrvList) { + gBS->FreePool (ServerListPtr); + } + + FreeMem (Private); + + // + // Unlock the instance data + // + DEBUG ( + (DEBUG_INFO, + "\nBcDiscover() status == %r (%Xh)\n", + StatCode, + StatCode) + ); + + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +EFI_STATUS +EFIAPI +BcSetPackets ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + BOOLEAN *NewDhcpDiscoverValid, OPTIONAL + BOOLEAN *NewDhcpAckReceived, OPTIONAL + BOOLEAN *NewProxyOfferReceived, OPTIONAL + BOOLEAN *NewPxeDiscoverValid, OPTIONAL + BOOLEAN *NewPxeReplyReceived, OPTIONAL + BOOLEAN *NewPxeBisReplyReceived, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewDhcpAck, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewProxyOffer, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeReply, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET * NewPxeBisReply OPTIONAL + ) +{ + EFI_PXE_BASE_CODE_MODE *PxebcMode; + EFI_STATUS Status; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + PxebcMode = Private->EfiBc.Mode; + + if (Private->DhcpPacketBuffer == NULL) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (DHCP_RECEIVE_BUFFER) * (PXE_BIS_INDEX + 1), + &Private->DhcpPacketBuffer + ); + + if (EFI_ERROR (Status) || Private->DhcpPacketBuffer == NULL) { + Private->DhcpPacketBuffer = NULL; + EfiReleaseLock (&Private->Lock); + return EFI_OUT_OF_RESOURCES; + } + } + // + // Issue BC command + // + // + // reset + // + Private->FileSize = 0; + if (NewDhcpDiscoverValid != NULL) { + PxebcMode->DhcpDiscoverValid = *NewDhcpDiscoverValid; + } + + if (NewDhcpAckReceived != NULL) { + PxebcMode->DhcpAckReceived = *NewDhcpAckReceived; + } + + if (NewProxyOfferReceived != NULL) { + PxebcMode->ProxyOfferReceived = *NewProxyOfferReceived; + } + + if (NewPxeDiscoverValid != NULL) { + PxebcMode->PxeDiscoverValid = *NewPxeDiscoverValid; + } + + if (NewPxeReplyReceived != NULL) { + PxebcMode->PxeReplyReceived = *NewPxeReplyReceived; + } + + if (NewPxeBisReplyReceived != NULL) { + PxebcMode->PxeBisReplyReceived = *NewPxeBisReplyReceived; + } + + if (NewDhcpDiscover != NULL) { + CopyMem ( + &PxebcMode->DhcpDiscover, + NewDhcpDiscover, + sizeof *NewDhcpDiscover + ); + } + + if (NewDhcpAck != NULL) { + CopyParse (Private, &PxebcMode->DhcpAck, NewDhcpAck, DHCPV4_ACK_INDEX); + } + + if (NewProxyOffer != NULL) { + CopyParse (Private, &PxebcMode->ProxyOffer, NewProxyOffer, PXE_OFFER_INDEX); + } + + if (NewPxeDiscover != NULL) { + CopyMem ( + &PxebcMode->PxeDiscover, + NewPxeDiscover, + sizeof *NewPxeDiscover + ); + } + + if (NewPxeReply != NULL) { + CopyParse (Private, &PxebcMode->PxeReply, NewPxeReply, PXE_ACK_INDEX); + } + + if (NewPxeBisReply != NULL) { + CopyParse (Private, &PxebcMode->PxeBisReply, NewPxeBisReply, PXE_BIS_INDEX); + } + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return EFI_SUCCESS; +} + +/* eof - pxe_bc_dhcp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c new file mode 100644 index 0000000000..c5088dff32 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c @@ -0,0 +1,421 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + + +#define RAND_MAX 0x10000 + +#include "Bc.h" + +// +// Definitions for internet group management protocol version 2 message +// structure Per RFC 2236, November 1997 +// +STATIC UINT8 RouterAlertOption[4] = { 0x80 | 20, 4, 0, 0 }; +STATIC IPV4_ADDR AllRoutersGroup = { { 224, 0, 0, 2 } }; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +VOID +ClearGroupTimer ( + PXE_BASECODE_DEVICE *Private, + UINTN TimerId + ) +{ + if (Private == NULL) { + return ; + } + + if (TimerId >= Private->MCastGroupCount) { + return ; + } + + if (Private->IgmpGroupEvent[TimerId] == NULL) { + return ; + } + + gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]); + Private->IgmpGroupEvent[TimerId] = NULL; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +SetGroupTimer ( + PXE_BASECODE_DEVICE *Private, + UINTN TimerId, + UINTN MaxRespTime + ) +{ + EFI_STATUS EfiStatus; + + if (Private == NULL) { + return ; + } + + if (TimerId >= Private->MCastGroupCount) { + return ; + } + + if (Private->IgmpGroupEvent[TimerId] != NULL) { + gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]); + } + + EfiStatus = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->IgmpGroupEvent[TimerId] + ); + + if (EFI_ERROR (EfiStatus)) { + Private->IgmpGroupEvent[TimerId] = NULL; + return ; + } + + EfiStatus = gBS->SetTimer ( + Private->IgmpGroupEvent[TimerId], + TimerRelative, + MaxRespTime * 1000000 + Random (Private) % RAND_MAX + ); + + if (EFI_ERROR (EfiStatus)) { + gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]); + Private->IgmpGroupEvent[TimerId] = NULL; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +SendIgmpMessage ( + PXE_BASECODE_DEVICE *Private, + UINT8 Type, + INTN GroupId + ) +{ + Private->IgmpMessage.Type = Type; + Private->IgmpMessage.MaxRespTime = 0; + Private->IgmpMessage.Checksum = 0; + Private->IgmpMessage.GroupAddress = Private->MCastGroup[GroupId]; + Private->IgmpMessage.Checksum = IpChecksum ( + (UINT16 *) &Private->IgmpMessage, + sizeof Private->IgmpMessage + ); + + Ipv4SendWOp ( + Private, + 0, + (UINT8 *) &Private->IgmpMessage, + sizeof Private->IgmpMessage, + PROT_IGMP, + RouterAlertOption, + sizeof RouterAlertOption, + ((Type == IGMP_TYPE_LEAVE_GROUP) ? AllRoutersGroup.L : Private->IgmpMessage.GroupAddress), + EFI_PXE_BASE_CODE_FUNCTION_IGMP + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +ReportIgmp ( + PXE_BASECODE_DEVICE *Private, + INTN GroupId + ) +{ + // + // if version 1 querier, send v1 report + // + UINT8 Type; + + if (Private->Igmpv1TimeoutEvent != NULL) { + if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + Private->UseIgmpv1Reporting = TRUE; + } + } + + Type = (UINT8) (Private->UseIgmpv1Reporting ? IGMP_TYPE_V1REPORT : IGMP_TYPE_REPORT); + + SendIgmpMessage (Private, Type, GroupId); + ClearGroupTimer (Private, GroupId); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +IgmpCheckTimers ( + PXE_BASECODE_DEVICE *Private + ) +{ + UINTN GroupId; + + if (Private == NULL) { + return ; + } + + for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) { + if (Private->IgmpGroupEvent[GroupId] == NULL) { + continue; + } + + if (!EFI_ERROR (gBS->CheckEvent (Private->IgmpGroupEvent[GroupId]))) { + // + // send a report + // + ReportIgmp (Private, GroupId); + } + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return 0 := Group not found + @return other := Group ID# + +**/ +STATIC +INTN +FindMulticastGroup ( + PXE_BASECODE_DEVICE *Private, + UINT32 GroupAddress + ) +{ + UINTN GroupId; + + for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) { + if (Private->MCastGroup[GroupId] == GroupAddress) { + return GroupId + 1; + } + } + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +IgmpJoinGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *GroupPtr + ) +{ + UINT32 Grp; + + Grp = *(UINT32 *) GroupPtr; + + // + // see if we already have it or if we can't take anymore + // + if (FindMulticastGroup (Private, Grp) || Private->MCastGroupCount == MAX_MCAST_GROUPS) { + return ; + } + // + // add the group + // + Private->MCastGroup[Private->MCastGroupCount] = Grp; + + ReportIgmp (Private, Private->MCastGroupCount); + // + // send a report + // so it will get sent again per RFC 2236 + // + SetGroupTimer ( + Private, + Private->MCastGroupCount++, + UNSOLICITED_REPORT_INTERVAL * 10 + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +IgmpLeaveGroup ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *GroupPtr + ) +{ + UINT32 Grp; + UINTN GroupId; + + Grp = *(UINT32 *) GroupPtr; + + // + // if not in group, ignore + // + GroupId = FindMulticastGroup (Private, Grp); + + if (GroupId == 0) { + return ; + } + // + // if not v1 querrier, send leave group IGMP message + // + if (Private->Igmpv1TimeoutEvent != NULL) { + if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + Private->Igmpv1TimeoutEvent = NULL; + Private->UseIgmpv1Reporting = TRUE; + } else { + SendIgmpMessage (Private, IGMP_TYPE_LEAVE_GROUP, GroupId - 1); + } + } + + while (GroupId < Private->MCastGroupCount) { + Private->MCastGroup[GroupId - 1] = Private->MCastGroup[GroupId]; + Private->IgmpGroupEvent[GroupId - 1] = Private->IgmpGroupEvent[GroupId]; + ++GroupId; + } + + --Private->MCastGroupCount; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +VOID +HandleIgmp ( + PXE_BASECODE_DEVICE *Private, + IGMPV2_MESSAGE *IgmpMessagePtr, + UINTN IgmpLength + ) +{ + EFI_STATUS EfiStatus; + UINTN GroupId; + INTN MaxRespTime; + + if (Private == NULL) { + return ; + } + + if (Private->MCastGroupCount == 0) { + // + // if we don't belong to any multicast groups, ignore + // + return ; + } + // + // verify checksum + // + if (IpChecksum ((UINT16 *) IgmpMessagePtr, IgmpLength)) { + // + // bad checksum - ignore packet + // + return ; + } + + switch (IgmpMessagePtr->Type) { + case IGMP_TYPE_QUERY: + // + // if a version 1 querier, note the fact and set max resp time + // + MaxRespTime = IgmpMessagePtr->MaxRespTime; + + if (MaxRespTime == 0) { + Private->UseIgmpv1Reporting = TRUE; + + if (Private->Igmpv1TimeoutEvent != NULL) { + gBS->CloseEvent (Private->Igmpv1TimeoutEvent); + } + + EfiStatus = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->Igmpv1TimeoutEvent + ); + + if (EFI_ERROR (EfiStatus)) { + Private->Igmpv1TimeoutEvent = NULL; + } else { + EfiStatus = gBS->SetTimer ( + Private->Igmpv1TimeoutEvent, + TimerRelative, + (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000 + ); + } + + MaxRespTime = IGMP_DEFAULT_MAX_RESPONSE_TIME * 10; + } + // + // if a general query (!GroupAddress), set all our group timers + // + if (!IgmpMessagePtr->GroupAddress) { + for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) { + SetGroupTimer (Private, GroupId, MaxRespTime); + } + } else { + // + // specific query - set only specific group + // + GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress); + + if (GroupId != 0) { + SetGroupTimer (Private, GroupId - 1, MaxRespTime); + } + } + + break; + + // + // if we have a timer running for this group, clear it + // + case IGMP_TYPE_V1REPORT: + case IGMP_TYPE_REPORT: + GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress); + + if (GroupId != 0) { + ClearGroupTimer (Private, GroupId - 1); + } + + break; + } +} + +/* EOF - pxe_bc_igmp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c new file mode 100644 index 0000000000..6330679a8f --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_ip.c @@ -0,0 +1,846 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_ip.c + +Abstract: + + +**/ + +#include "Bc.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Check if two IP addresses are on the same subnet. + + @param IpLength Length of IP address in bytes. + @param Ip1 IP address to check. + @param Ip2 IP address to check. + @param SubnetMask Subnet mask to check with. + + @retval TRUE IP addresses are on the same subnet. + @retval FALSE IP addresses are on different subnets. + +**/ +BOOLEAN +OnSameSubnet ( + IN UINTN IpLength, + IN EFI_IP_ADDRESS *Ip1, + IN EFI_IP_ADDRESS *Ip2, + IN EFI_IP_ADDRESS *SubnetMask + ) +{ + if (IpLength == 0 || Ip1 == NULL || Ip2 == NULL || SubnetMask == NULL) { + return FALSE; + } + + while (IpLength-- != 0) { + if ((Ip1->v6.Addr[IpLength] ^ Ip2->v6.Addr[IpLength]) & SubnetMask->v6.Addr[IpLength]) { + return FALSE; + } + } + + return TRUE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Add router to router table. + + @param Private Pointer PxeBc instance data. + @param RouterIpPtr Pointer to router IP address. + + @return Nothing + +**/ +VOID +IpAddRouter ( + IN PXE_BASECODE_DEVICE *Private, + IN EFI_IP_ADDRESS *RouterIpPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + if (Private == NULL || RouterIpPtr == NULL) { + return ; + } + + PxeBcMode = Private->EfiBc.Mode; + + // + // if we are filled up or this is not on the same subnet, forget it + // + if ((PxeBcMode->RouteTableEntries == PXE_ROUTER_TABLE_SIZE) || + !OnSameSubnet(Private->IpLength, &PxeBcMode->StationIp, RouterIpPtr, &PxeBcMode->SubnetMask)) { + return ; + } + // + // make sure we don't already have it + // + for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) { + if (!CompareMem ( + &PxeBcMode->RouteTable[Index].GwAddr, + RouterIpPtr, + Private->IpLength + )) { + return ; + } + } + // + // keep it + // + ZeroMem ( + &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries], + sizeof (EFI_PXE_BASE_CODE_ROUTE_ENTRY) + ); + + CopyMem ( + &PxeBcMode->RouteTable[PxeBcMode->RouteTableEntries++].GwAddr, + RouterIpPtr, + Private->IpLength + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// return router ip to use for DestIp (0 if none) +// +STATIC +EFI_IP_ADDRESS * +GetRouterIp ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *DestIpPtr + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + if (Private == NULL || DestIpPtr == NULL) { + return NULL; + } + + PxeBcMode = Private->EfiBc.Mode; + + for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) { + if (OnSameSubnet ( + Private->IpLength, + &PxeBcMode->RouteTable[Index].IpAddr, + DestIpPtr, + &PxeBcMode->RouteTable[Index].SubnetMask + )) { + return &PxeBcMode->RouteTable[Index].GwAddr; + } + } + + return NULL; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// routine to send ipv4 packet +// ipv4 header of length HdrLth in TransmitBufferPtr +// routine fills in ipv4hdr Ver_Hdl, TotalLength, and Checksum, moves in Data +// and gets dest MAC address +// +#define IP_TX_BUFFER ((IPV4_BUFFER *) Private->TransmitBufferPtr) +#define IP_TX_HEADER IP_TX_BUFFER->IpHeader + +EFI_STATUS +Ipv4Xmt ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIp, + UINTN IpHeaderLength, + UINTN TotalHeaderLength, + VOID *Data, + UINTN DataLength, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +{ + EFI_MAC_ADDRESS DestMac; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_STATUS StatCode; + UINTN PacketLength; + + Snp = Private->SimpleNetwork; + PxeBcMode = Private->EfiBc.Mode; + StatCode = EFI_SUCCESS; + PacketLength = TotalHeaderLength + DataLength; + + // + // get dest MAC address + // multicast - convert to hw equiv + // unicast on same net, use arp + // on different net, arp for router + // + if (IP_TX_HEADER.DestAddr.L == BROADCAST_IPv4) { + CopyMem (&DestMac, &Snp->Mode->BroadcastAddress, sizeof (DestMac)); + } else if (IS_MULTICAST (&IP_TX_HEADER.DestAddr)) { + StatCode = (*Snp->MCastIpToMac) (Snp, PxeBcMode->UsingIpv6, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr, &DestMac); + } else { + UINT32 Ip; + + if (OnSameSubnet ( + Private->IpLength, + &PxeBcMode->StationIp, + (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr, + &PxeBcMode->SubnetMask + )) { + Ip = IP_TX_HEADER.DestAddr.L; + } else if (GatewayIp != 0) { + Ip = GatewayIp; + } else { + EFI_IP_ADDRESS *TmpIp; + + TmpIp = GetRouterIp (Private, (EFI_IP_ADDRESS *) &IP_TX_HEADER.DestAddr); + + if (TmpIp == NULL) { + DEBUG ( + (DEBUG_WARN, + "\nIpv4Xmit() Exit #1 %xh (%r)", + EFI_NO_RESPONSE, + EFI_NO_RESPONSE) + ); + + return EFI_NO_RESPONSE; + // + // no router + // + } + + Ip = TmpIp->Addr[0]; + } + + if (!GetHwAddr ( + Private, + (EFI_IP_ADDRESS *) &Ip, + (EFI_MAC_ADDRESS *) &DestMac + )) { + if (!PxeBcMode->AutoArp) { + DEBUG ( + (DEBUG_WARN, + "\nIpv4Xmit() Exit #2 %xh (%r)", + EFI_DEVICE_ERROR, + EFI_DEVICE_ERROR) + ); + + return EFI_DEVICE_ERROR; + } else { + StatCode = DoArp ( + Private, + (EFI_IP_ADDRESS *) &Ip, + (EFI_MAC_ADDRESS *) &DestMac + ); + } + } + } + + if (EFI_ERROR (StatCode)) { + DEBUG ((DEBUG_WARN, "\nIpv4Xmit() Exit #3 %xh (%r)", StatCode, StatCode)); + return StatCode; + } + // + // fill in packet info + // + SET_IPV4_VER_HDL (&IP_TX_HEADER, IpHeaderLength); + IP_TX_HEADER.TotalLength = HTONS (PacketLength); + IP_TX_HEADER.HeaderChecksum = IpChecksum ((UINT16 *) &IP_TX_HEADER, IpHeaderLength); + CopyMem (((UINT8 *) &IP_TX_HEADER) + TotalHeaderLength, Data, DataLength); + + // + // send it + // + return SendPacket ( + Private, + (UINT8 *) &IP_TX_HEADER - Snp->Mode->MediaHeaderSize, + &IP_TX_HEADER, + PacketLength, + &DestMac, + PXE_PROTOCOL_ETHERNET_IP, + Function + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send ipv4 packet with option +// +EFI_STATUS +Ipv4SendWOp ( + PXE_BASECODE_DEVICE *Private, + UINT32 GatewayIp, + UINT8 *Msg, + UINTN MessageLength, + UINT8 Prot, + UINT8 *Option, + UINTN OptionLength, + UINT32 DestIp, + EFI_PXE_BASE_CODE_FUNCTION Function + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN HdrLth; + + PxeBcMode = Private->EfiBc.Mode; + HdrLth = sizeof (IPV4_HEADER) + OptionLength; + + ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER)); + IP_TX_HEADER.TimeToLive = PxeBcMode->TTL; + IP_TX_HEADER.TypeOfService = PxeBcMode->ToS; + IP_TX_HEADER.Protocol = Prot; + IP_TX_HEADER.SrcAddr.L = *(UINT32 *) &PxeBcMode->StationIp; + IP_TX_HEADER.DestAddr.L = DestIp; + IP_TX_HEADER.Id = Random (Private); + CopyMem (IP_TX_BUFFER->u.Data, Option, OptionLength); + return Ipv4Xmt ( + Private, + GatewayIp, + HdrLth, + HdrLth, + Msg, + MessageLength, + Function + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// send MessageLength message at MessagePtr - higher level protocol header already in TransmitBufferPtr, length HdrSize +// +EFI_STATUS +Ip4Send ( + PXE_BASECODE_DEVICE *Private, // pointer to instance data + UINTN MayFrag, // + UINT8 Prot, // protocol + UINT32 SrcIp, // Source IP address + UINT32 DestIp, // Destination IP address + UINT32 GatewayIp, // used if not NULL and needed + UINTN HdrSize, // protocol header byte length + UINT8 *MessagePtr, // pointer to data + UINTN MessageLength // data byte length + ) +{ + EFI_STATUS StatCode; + UINTN TotDataLength; + + TotDataLength = HdrSize + MessageLength; + + if (TotDataLength > MAX_IPV4_DATA_SIZE) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #1 %xh (%r)", + EFI_BAD_BUFFER_SIZE, + EFI_BAD_BUFFER_SIZE) + ); + + return EFI_BAD_BUFFER_SIZE; + } + + ZeroMem ((VOID *) &IP_TX_HEADER, sizeof (IPV4_HEADER)); + IP_TX_HEADER.TimeToLive = DEFAULT_TTL; + IP_TX_HEADER.Protocol = Prot; + IP_TX_HEADER.SrcAddr.L = SrcIp; + IP_TX_HEADER.DestAddr.L = DestIp; + IP_TX_HEADER.Id = Random (Private); + + if (!MayFrag) { + *(UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_NO_FRAG >> 8; + } + // + // check for need to fragment + // + if (TotDataLength > MAX_IPV4_FRAME_DATA_SIZE) { + UINTN DataLengthSent; + UINT16 FragmentOffset; + + FragmentOffset = IP_MORE_FRAG; + // + // frag offset field + // + if (!MayFrag) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #2 %xh (%r)", + EFI_BAD_BUFFER_SIZE, + EFI_BAD_BUFFER_SIZE) + ); + + return EFI_BAD_BUFFER_SIZE; + } + // + // send out in fragments - first includes upper level header + // all are max and include more frag bit except last + // + * (UINT8 *) (&IP_TX_HEADER.FragmentFields) = IP_MORE_FRAG >> 8; + +#define IPV4_FRAG_SIZE (MAX_IPV4_FRAME_DATA_SIZE & 0xfff8) +#define IPV4_FRAG_OFF_INC (IPV4_FRAG_SIZE >> 3) + + DataLengthSent = IPV4_FRAG_SIZE - HdrSize; + + StatCode = Ipv4Xmt ( + Private, + GatewayIp, + sizeof (IPV4_HEADER), + sizeof (IPV4_HEADER) + HdrSize, + MessagePtr, + DataLengthSent, + Private->Function + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #3 %xh (%r)", + StatCode, + StatCode) + ); + + return StatCode; + } + + MessagePtr += DataLengthSent; + MessageLength -= DataLengthSent; + FragmentOffset += IPV4_FRAG_OFF_INC; + IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset); + + while (MessageLength > IPV4_FRAG_SIZE) { + StatCode = Ipv4Xmt ( + Private, + GatewayIp, + sizeof (IPV4_HEADER), + sizeof (IPV4_HEADER), + MessagePtr, + IPV4_FRAG_SIZE, + Private->Function + ); + + if (EFI_ERROR (StatCode)) { + DEBUG ( + (DEBUG_WARN, + "\nIp4Send() Exit #3 %xh (%r)", + StatCode, + StatCode) + ); + + return StatCode; + } + + MessagePtr += IPV4_FRAG_SIZE; + MessageLength -= IPV4_FRAG_SIZE; + FragmentOffset += IPV4_FRAG_OFF_INC; + IP_TX_HEADER.FragmentFields = HTONS (FragmentOffset); + } + + * (UINT8 *) (&IP_TX_HEADER.FragmentFields) &= ~(IP_MORE_FRAG >> 8); + HdrSize = 0; + } + // + // transmit + // + return Ipv4Xmt ( + Private, + GatewayIp, + sizeof (IPV4_HEADER), + sizeof (IPV4_HEADER) + HdrSize, + MessagePtr, + MessageLength, + Private->Function + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// return true if dst IP in receive header matched with what's enabled +// +STATIC +BOOLEAN +IPgood ( + PXE_BASECODE_DEVICE *Private, + IPV4_HEADER *IpHeader + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + PxeBcMode = Private->EfiBc.Mode; + + if (PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) { + return TRUE; + } + + if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) && + IS_MULTICAST (&IpHeader->DestAddr) + ) { + return TRUE; + } + + if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) && + PxeBcMode->StationIp.Addr[0] == IpHeader->DestAddr.L + ) { + return TRUE; + } + + if ((PxeBcMode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) && IpHeader->DestAddr.L == BROADCAST_IPv4) { + return TRUE; + } + + for (Index = 0; Index < PxeBcMode->IpFilter.IpCnt; ++Index) { + if (IpHeader->DestAddr.L == PxeBcMode->IpFilter.IpList[Index].Addr[0]) { + return TRUE; + } + } + + return FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// receive up to MessageLength message into MessagePtr for protocol Prot +// return message length, src/dest ips if select any, and pointer to protocol +// header routine will filter based on source and/or dest ip if OpFlags set. +// +EFI_STATUS +IpReceive ( + PXE_BASECODE_DEVICE *Private, + PXE_OPFLAGS OpFlags, + EFI_IP_ADDRESS *SrcIpPtr, + EFI_IP_ADDRESS *DestIpPtr, + UINT8 Prot, + VOID *HeaderPtr, + UINTN HdrSize, + UINT8 *MessagePtr, + UINTN *MessageLengthPtr, + EFI_EVENT TimeoutEvent + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_STATUS StatCode; + UINTN ByteCount; + UINTN FragmentCount; + UINTN ExpectedPacketLength; + UINTN Id; + BOOLEAN GotFirstFragment; + BOOLEAN GotLastFragment; + + DEBUG ( + (DEBUG_NET, + "\nIpReceive() Hdr=%Xh HdrSz=%d Data=%Xh DataSz=%d", + HeaderPtr, + HdrSize, + MessagePtr, + *MessageLengthPtr) + ); + + PxeBcMode = Private->EfiBc.Mode; + PxeBcMode->IcmpErrorReceived = FALSE; + + ExpectedPacketLength = 0; + GotFirstFragment = FALSE; + GotLastFragment = FALSE; + FragmentCount = 0; + ByteCount = 0; + Id = 0; + + for (;;) { + IPV4_HEADER IpHdr; + UINTN FFlds; + UINTN TotalLength; + UINTN FragmentOffset; + UINTN HeaderSize; + UINTN BufferSize; + UINTN IpHeaderLength; + UINTN DataLength; + UINT16 Protocol; + UINT8 *NextHdrPtr; + UINT8 *PacketPtr; + + StatCode = WaitForReceive ( + Private, + Private->Function, + TimeoutEvent, + &HeaderSize, + &BufferSize, + &Protocol + ); + + if (EFI_ERROR (StatCode)) { + return StatCode; + } + + PacketPtr = Private->ReceiveBufferPtr + HeaderSize; + + if (Protocol == PXE_PROTOCOL_ETHERNET_ARP) { + HandleArpReceive ( + Private, + (ARP_PACKET *) PacketPtr, + Private->ReceiveBufferPtr + ); + + continue; + } + + if (Protocol != PXE_PROTOCOL_ETHERNET_IP) { + continue; + } + +#define IpRxHeader ((IPV4_HEADER *) PacketPtr) + + // + // filter for version & check sum + // + IpHeaderLength = IPV4_HEADER_LENGTH (IpRxHeader); + + if ((IpRxHeader->VersionIhl >> 4) != IPVER4) { + continue; + } + + if (IpChecksum ((UINT16 *) IpRxHeader, IpHeaderLength)) { + continue; + } + + CopyMem (&IpHdr, IpRxHeader, sizeof (IpHdr)); + TotalLength = NTOHS (IpHdr.TotalLength); + + if (IpHdr.Protocol == PROT_TCP) { + // + // The NextHdrPtr is used to seed the header buffer we are passing back. + // That being the case, we want to see everything in pPkt which contains + // everything but the ethernet (or whatever) frame. IP + TCP in this case. + // + DataLength = TotalLength; + NextHdrPtr = PacketPtr; + } else { + DataLength = TotalLength - IpHeaderLength; + NextHdrPtr = PacketPtr + IpHeaderLength; + } + // + // If this is an ICMP, it might not be for us. + // Double check the state of the IP stack and the + // packet fields before assuming it is an ICMP + // error. ICMP requests are not supported by the + // PxeBc IP stack and should be ignored. + // + if (IpHdr.Protocol == PROT_ICMP) { + ICMPV4_HEADER *Icmpv4; + + Icmpv4 = (ICMPV4_HEADER *) NextHdrPtr; + + // + // For now only obvious ICMP error replies will be accepted by + // this stack. This still makes us vulnerable to DoS attacks. + // But at least we will not be killed by DHCP daemons. + // + switch (Icmpv4->Type) { + case ICMP_REDIRECT: + case ICMP_ECHO: + case ICMP_ROUTER_ADV: + case ICMP_ROUTER_SOLICIT: + case ICMP_TIMESTAMP: + case ICMP_TIMESTAMP_REPLY: + case ICMP_INFO_REQ: + case ICMP_INFO_REQ_REPLY: + case ICMP_SUBNET_MASK_REQ: + case ICMP_SUBNET_MASK_REPLY: + default: + continue; + + // + // %%TBD - This should be implemented. + // + case ICMP_ECHO_REPLY: + continue; + + case ICMP_DEST_UNREACHABLE: + case ICMP_TIME_EXCEEDED: + case ICMP_PARAMETER_PROBLEM: + case ICMP_SOURCE_QUENCH: + PxeBcMode->IcmpErrorReceived = TRUE; + + CopyMem ( + &PxeBcMode->IcmpError, + NextHdrPtr, + sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + + DEBUG ( + (DEBUG_NET, + "\nIpReceive() Exit #1 %Xh (%r)", + EFI_ICMP_ERROR, + EFI_ICMP_ERROR) + ); + } + + return EFI_ICMP_ERROR; + } + + if (IpHdr.Protocol == PROT_IGMP) { + HandleIgmp (Private, (IGMPV2_MESSAGE *) NextHdrPtr, DataLength); + + DEBUG ((DEBUG_NET, "\n IGMP")); + continue; + } + // + // check for protocol + // + if (IpHdr.Protocol != Prot) { + continue; + } + // + // do filtering + // + if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr && SrcIpPtr->Addr[0] != IpHdr.SrcAddr.L) { + DEBUG ((DEBUG_NET, "\n Not expected source IP address.")); + continue; + } + + if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) { + if (!IPgood (Private, &IpHdr)) { + continue; + } + } else if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP)) { + if (DestIpPtr == NULL) { + if (PxeBcMode->StationIp.Addr[0] != IpHdr.DestAddr.L) { + continue; + } + } else if (DestIpPtr->Addr[0] != IpHdr.DestAddr.L) { + continue; + } + } + // + // get some data we need + // + FFlds = NTOHS (IpHdr.FragmentFields); + FragmentOffset = ((FFlds & IP_FRAG_OFF_MSK) << 3); + + /* Keep count of fragments that belong to this session. + * If we get packets with a different IP ID number, + * ignore them. Ignored packets should be handled + * by the upper level protocol. + */ + if (FragmentCount == 0) { + Id = IpHdr.Id; + + if (DestIpPtr != NULL) { + DestIpPtr->Addr[0] = IpHdr.DestAddr.L; + } + + if (SrcIpPtr != NULL) { + SrcIpPtr->Addr[0] = IpHdr.SrcAddr.L; + } + } else { + if (IpHdr.Id != Id) { + continue; + } + } + + ++FragmentCount; + + /* Fragment management. + */ + if (FragmentOffset == 0) { + /* This is the first fragment (may also be the + * only fragment). + */ + GotFirstFragment = TRUE; + + /* If there is a separate protocol header buffer, + * copy the header, adjust the data pointer and + * the data length. + */ + if (HdrSize != 0) { + CopyMem (HeaderPtr, NextHdrPtr, HdrSize); + + NextHdrPtr += HdrSize; + DataLength -= HdrSize; + } + } else { + /* If there is a separate protocol header buffer, + * adjust the fragment offset. + */ + FragmentOffset -= HdrSize; + } + + /* See if this is the last fragment. + */ + if (!(FFlds & IP_MORE_FRAG)) { + // + // This is the last fragment (may also be the only fragment). + // + GotLastFragment = TRUE; + + /* Compute the expected length of the assembled + * packet. This will be used to decide if we + * have gotten all of the fragments. + */ + ExpectedPacketLength = FragmentOffset + DataLength; + } + + DEBUG ( + (DEBUG_NET, + "\n ID = %Xh Off = %d Len = %d", + Id, + FragmentOffset, + DataLength) + ); + + /* Check for receive buffer overflow. + */ + if (FragmentOffset + DataLength > *MessageLengthPtr) { + /* There is not enough space in the receive + * buffer for the fragment. + */ + DEBUG ( + (DEBUG_NET, + "\nIpReceive() Exit #3 %Xh (%r)", + EFI_BUFFER_TOO_SMALL, + EFI_BUFFER_TOO_SMALL) + ); + + return EFI_BUFFER_TOO_SMALL; + } + + /* Copy data into receive buffer. + */ + if (DataLength != 0) { + DEBUG ((DEBUG_NET, " To = %Xh", MessagePtr + FragmentOffset)); + + CopyMem (MessagePtr + FragmentOffset, NextHdrPtr, DataLength); + ByteCount += DataLength; + } + + /* If we have seen the first and last fragments and + * the receive byte count is at least as large as the + * expected byte count, return SUCCESS. + * + * We could be tricked by receiving a fragment twice + * but the upper level protocol should figure this + * out. + */ + if (GotFirstFragment && GotLastFragment && ByteCount >= ExpectedPacketLength) { + *MessageLengthPtr = ExpectedPacketLength; + return EFI_SUCCESS; + } + } +} + +/* eof - pxe_bc_ip.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c new file mode 100644 index 0000000000..43e1bbb429 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_mtftp.c @@ -0,0 +1,2193 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + pxe_bc_mtftp.c + +Abstract: + TFTP and MTFTP (multicast TFTP) implementation. + +Revision History + + +**/ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// The following #define is used to create a version that does not wait to +// open after a listen. This is just for a special regression test of MTFTP +// server to make sure multiple opens are handled correctly. Normally this +// next line should be a comment. +// #define SpecialNowaitVersion // comment out for normal operation +// + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "bc.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +UINT64 +Swap64 ( + UINT64 n + ) +{ + union { + UINT64 n; + UINT8 b[8]; + } u; + + UINT8 t; + + u.n = n; + + t = u.b[0]; + u.b[0] = u.b[7]; + u.b[7] = t; + + t = u.b[1]; + u.b[1] = u.b[6]; + u.b[6] = t; + + t = u.b[2]; + u.b[2] = u.b[5]; + u.b[5] = t; + + t = u.b[3]; + u.b[3] = u.b[4]; + u.b[4] = t; + + return u.n; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_SUCCESS := + @return EFI_TFTP_ERROR := + @return other := + +**/ +STATIC +EFI_STATUS +TftpUdpRead ( + PXE_BASECODE_DEVICE *Private, + UINT16 Operation, + VOID *HeaderPtr, + UINTN *BufferSizePtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *OurIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT16 Timeout + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_STATUS Status; + EFI_EVENT TimeoutEvent; + UINTN HeaderSize; + + // + // + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Timeout * 10000000 + 1000000 + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (TimeoutEvent); + return Status; + } + // + // + // + HeaderSize = Private->BigBlkNumFlag ? sizeof (struct Tftpv4Ack8) : sizeof (struct Tftpv4Ack); + +#define ERROR_MESSAGE_PTR ((struct Tftpv4Error *) HeaderPtr) + + Status = UdpRead ( + Private, + Operation, + OurIpPtr, + OurPortPtr, + ServerIpPtr, + ServerPortPtr, + &HeaderSize, + HeaderPtr, + BufferSizePtr, + BufferPtr, + TimeoutEvent + ); + + if (Status != EFI_SUCCESS || ERROR_MESSAGE_PTR->OpCode != HTONS (TFTP_ERROR)) { + gBS->CloseEvent (TimeoutEvent); + return Status; + } + // + // got an error packet + // write one byte error code followed by error message + // + PxeBcMode = Private->EfiBc.Mode; + PxeBcMode->TftpErrorReceived = TRUE; + PxeBcMode->TftpError.ErrorCode = (UINT8) NTOHS (ERROR_MESSAGE_PTR->ErrCode); + HeaderSize = MIN (*BufferSizePtr, sizeof PxeBcMode->TftpError.ErrorString); + CopyMem (PxeBcMode->TftpError.ErrorString, BufferPtr, HeaderSize); + + gBS->CloseEvent (TimeoutEvent); + return EFI_TFTP_ERROR; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +VOID +SendError ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr + ) +{ + struct Tftpv4Error *ErrStr; + UINTN Len; + + ErrStr = (VOID *) Private->TftpErrorBuffer; + Len = sizeof *ErrStr; + + ErrStr->OpCode = HTONS (TFTP_ERROR); + ErrStr->ErrCode = HTONS (TFTP_ERR_OPTION); + ErrStr->ErrMsg[0] = 0; + + UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + &Len, + ErrStr + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +SendAckAndGetData ( + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ReplyIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT16 Timeout, + UINTN *ReplyLenPtr, + UINT8 *PxeBcMode, + UINT64 *BlockNumPtr, + BOOLEAN AckOnly + ) +{ + struct Tftpv4Data DataBuffer; + struct Tftpv4Ack *Ack2Ptr; + struct Tftpv4Ack8 *Ack8Ptr; + EFI_STATUS Status; + UINTN Len; + + Ack2Ptr = (VOID *) Private->TftpAckBuffer; + Ack8Ptr = (VOID *) Private->TftpAckBuffer; + + if (Private->BigBlkNumFlag) { + Len = sizeof (struct Tftpv4Ack8); + + Ack8Ptr->OpCode = HTONS (TFTP_ACK8); + Ack8Ptr->BlockNum = Swap64 (*BlockNumPtr); + + Status = UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + &Len, + Ack8Ptr + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } else { + Len = sizeof (struct Tftpv4Ack); + + Ack2Ptr->OpCode = HTONS (TFTP_ACK); + Ack2Ptr->BlockNum = HTONS ((UINT16) *BlockNumPtr); + + Status = UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + &Len, + Ack2Ptr + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (AckOnly) { + // + // ACK of last packet. This is just a courtesy. + // Do not wait for response. + // + return EFI_SUCCESS; + } + // + // read reply + // + Status = TftpUdpRead ( + Private, + 0, + &DataBuffer, + ReplyLenPtr, + PxeBcMode, + ServerIpPtr, + ServerPortPtr, + ReplyIpPtr, + OurPortPtr, + Timeout + ); + + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + // + // got a good reply (so far) + // check for next data packet + // + if (!Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA)) { + if (Status == EFI_BUFFER_TOO_SMALL) { + SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr); + } + + *BlockNumPtr = NTOHS (DataBuffer.Header.BlockNum); + return Status; + } + + if (Private->BigBlkNumFlag && DataBuffer.Header.OpCode == HTONS (TFTP_DATA8)) { + if (Status == EFI_BUFFER_TOO_SMALL) { + SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr); + } + + *BlockNumPtr = Swap64 (*(UINT64 *) &DataBuffer.Header.BlockNum); + return Status; + } + + return EFI_PROTOCOL_ERROR; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +LockStepReceive ( + PXE_BASECODE_DEVICE *Private, + UINTN PacketSize, + UINT64 *BufferSizePtr, + UINT64 Offset, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_IP_ADDRESS *ReplyIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT64 LastBlock, + UINT16 Timeout, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_STATUS Status; + UINT64 BlockNum; + UINT64 BufferSize; + UINTN Retries; + UINTN SaveLen; + UINTN ReplyLen; + + ReplyLen = PacketSize; + BlockNum = LastBlock; + + DEBUG ((DEBUG_INFO, "\nLockStepReceive() PacketSize = %d", PacketSize)); + + if (DontUseBuffer) { + BufferSize = PacketSize; + } else { + BufferSize = *BufferSizePtr - Offset; + BufferPtr += Offset; + } + + while (ReplyLen >= 512 && ReplyLen == PacketSize) { + if (BufferSize < PacketSize) { + ReplyLen = (UINTN) ((BufferSize > 0) ? BufferSize : 0); + } + + SaveLen = ReplyLen; + + // + // write an ack packet and get data - retry up to NUM_ACK_RETRIES on timeout + // + Retries = NUM_ACK_RETRIES; + + do { + ReplyLen = SaveLen; + + Status = SendAckAndGetData ( + Private, + ServerIpPtr, + ServerPortPtr, + ReplyIpPtr, + OurPortPtr, + Timeout, + (UINTN *) &ReplyLen, + BufferPtr, + &BlockNum, + FALSE + ); + + if (!EFI_ERROR (Status) || Status == EFI_BUFFER_TOO_SMALL) { + if (BlockNum == LastBlock) { + DEBUG ((DEBUG_NET, "\nresend")); + // + // a resend - continue + // + Status = EFI_TIMEOUT; + } else if (Private->BigBlkNumFlag) { + if (BlockNum != ++LastBlock) { + DEBUG ((DEBUG_NET, "\nLockStepReceive() Exit #1a")); + // + // not correct blocknum - error + // + return EFI_PROTOCOL_ERROR; + } + } else { + LastBlock = (LastBlock + 1) & 0xFFFF; + if (BlockNum != LastBlock) { + DEBUG ((DEBUG_NET, "\nLockStepReceive() Exit #1b")); + return EFI_PROTOCOL_ERROR; + // + // not correct blocknum - error + // + } + } + } + } while (Status == EFI_TIMEOUT && --Retries); + + if (EFI_ERROR (Status)) { + if (Status != EFI_BUFFER_TOO_SMALL) { + SendError (Private, ServerIpPtr, ServerPortPtr, OurPortPtr); + } + + return Status; + } + + if (DontUseBuffer) { + BufferSize += ReplyLen; + } else { + BufferPtr += ReplyLen; + BufferSize -= ReplyLen; + } + } + // + // while (ReplyLen == PacketSize); + // + if (DontUseBuffer) { + if (BufferSizePtr != NULL) { + *BufferSizePtr = (BufferSize - PacketSize); + } + } else { + *BufferSizePtr -= BufferSize; + } + + /* Send ACK of last packet. */ + ReplyLen = 0; + + SendAckAndGetData ( + Private, + ServerIpPtr, + ServerPortPtr, + ReplyIpPtr, + OurPortPtr, + Timeout, + (UINTN *) &ReplyLen, + BufferPtr, + &BlockNum, + TRUE + ); + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// some literals +// +STATIC UINT8 Mode[] = MODE_BINARY; +STATIC UINT8 BlockSizeOp[] = OP_BLKSIZE; +STATIC UINT8 TsizeOp[] = OP_TFRSIZE; +STATIC UINT8 OverwriteOp[] = OP_OVERWRITE; +STATIC UINT8 BigBlkNumOp[] = OP_BIGBLKNUM; +STATIC EFI_PXE_BASE_CODE_UDP_PORT TftpRequestPort = TFTP_OPEN_PORT; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return Pointer to value field if option found or NULL if not found. + +**/ +STATIC +UINT8 * +FindOption ( + UINT8 *OptionPtr, + INTN OpLen, + UINT8 *OackPtr, + INTN OackSize + ) +{ + if ((OackSize -= OpLen) <= 0) { + return NULL; + } + + do { + if (!CompareMem (OackPtr, OptionPtr, OpLen)) { + return OackPtr + OpLen; + } + + ++OackPtr; + } while (--OackSize); + + return NULL; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#define BKSZOP 1 // block size +#define TSIZEOP 2 // transfer size +#define OVERWRITEOP 4 // overwrite +#define BIGBLKNUMOP 8 // big block numbers +STATIC +EFI_STATUS +TftpRwReq ( + UINT16 Req, + UINT16 Options, + PXE_BASECODE_DEVICE *Private, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + VOID *Buffer + ) +{ + union { + UINT8 Data[514]; + struct Tftpv4Req ReqStr; + } *u; + + UINT16 OpFlags; + INTN Len; + INTN TotalLen; + UINT8 *Ptr; + + if (*OurPortPtr == 0) { + OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT; + } else { + OpFlags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT; + } + // + // build the basic request - opcode, filename, mode + // + u = Buffer; + u->ReqStr.OpCode = HTONS (Req); + TotalLen = sizeof (Mode) + sizeof (u->ReqStr.OpCode) + (Len = 1 + AsciiStrLen (FilenamePtr)); + + CopyMem (u->ReqStr.FileName, FilenamePtr, Len); + Ptr = (UINT8 *) (u->ReqStr.FileName + Len); + + CopyMem (Ptr, Mode, sizeof (Mode)); + Ptr += sizeof (Mode); + + if (Options & BKSZOP) { + CopyMem (Ptr, BlockSizeOp, sizeof (BlockSizeOp)); + UtoA10 (*PacketSizePtr, Ptr + sizeof (BlockSizeOp)); + + TotalLen += (Len = 1 + AsciiStrLen (Ptr + sizeof (BlockSizeOp)) + sizeof (BlockSizeOp)); + + Ptr += Len; + } + + if (Options & TSIZEOP) { + CopyMem (Ptr, TsizeOp, sizeof (TsizeOp)); + CopyMem (Ptr + sizeof (TsizeOp), "0", 2); + TotalLen += sizeof (TsizeOp) + 2; + Ptr += sizeof (TsizeOp) + 2; + } + + if (Options & OVERWRITEOP) { + CopyMem (Ptr, OverwriteOp, sizeof (OverwriteOp)); + CopyMem (Ptr + sizeof (OverwriteOp), "1", 2); + TotalLen += sizeof (OverwriteOp) + 2; + Ptr += sizeof (OverwriteOp) + 2; + } + + if (Options & BIGBLKNUMOP) { + CopyMem (Ptr, BigBlkNumOp, sizeof (BigBlkNumOp)); + CopyMem (Ptr + sizeof (BigBlkNumOp), "8", 2); + TotalLen += sizeof (BigBlkNumOp) + 2; + Ptr += sizeof (BigBlkNumOp) + 2; + } + // + // send it + // + return UdpWrite ( + Private, + OpFlags, + ServerIpPtr, + ServerPortPtr, + 0, + 0, + OurPortPtr, + 0, + 0, + (UINTN *) &TotalLen, + u + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpRwReqwResp ( + UINT16 Req, + UINT16 Options, + PXE_BASECODE_DEVICE *Private, + VOID *HeaderPtr, + UINTN *PacketSizePtr, + UINTN *ReplyLenPtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *ServerReplyPortPtr, + EFI_PXE_BASE_CODE_UDP_PORT *OurPortPtr, + UINT8 *FilenamePtr, + UINT16 Timeout + ) +{ + EFI_STATUS Status; + UINTN SaveReplyLen; + INTN Retries; + UINT8 Buffer[514]; + + SaveReplyLen = *ReplyLenPtr; + Retries = 3; + Private->BigBlkNumFlag = FALSE; + *OurPortPtr = 0; + // + // generate random + // + do { + if (*OurPortPtr != 0) { + if (++ *OurPortPtr == 0) { + *OurPortPtr = PXE_RND_PORT_LOW; + } + } + // + // send request from our Ip = StationIp + // + if ((Status = TftpRwReq ( + Req, + Options, + Private, + ServerIpPtr, + ServerPortPtr, + OurPortPtr, + FilenamePtr, + PacketSizePtr, + Buffer + )) != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nTftpRwReqwResp() Exit #1 %xh (%r)", + Status, + Status) + ); + + return Status; + } + // + // read reply to our Ip = StationIp + // + *ReplyLenPtr = SaveReplyLen; + + Status = TftpUdpRead ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT, + HeaderPtr, + ReplyLenPtr, + BufferPtr, + ServerIpPtr, + ServerReplyPortPtr, + 0, + OurPortPtr, + Timeout + ); + } while (Status == EFI_TIMEOUT && --Retries); + + if (!Options || Status != EFI_TFTP_ERROR) { + DEBUG ( + (DEBUG_WARN, + "\nTftpRwReqwResp() Exit #2 %xh (%r)", + Status, + Status) + ); + return Status; + } + + Status = TftpRwReqwResp ( + Req, + 0, + Private, + HeaderPtr, + PacketSizePtr, + ReplyLenPtr, + BufferPtr, + ServerIpPtr, + ServerPortPtr, + ServerReplyPortPtr, + OurPortPtr, + FilenamePtr, + Timeout + ); + + DEBUG ((DEBUG_WARN, "\nTftpRwReqwResp() Exit #3 %xh (%r)", Status, Status)); + + return Status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// mtftp listen +// read on mcast ip, cport, from sport, for data packet +// returns success if gets multicast last packet or all up to last block +// if not missing, then finished +// + +/** + + +**/ +STATIC +EFI_STATUS +MtftpListen ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, + UINT64 *StartBlockPtr, + UINTN *NumMissedPtr, + UINT16 TransTimeout, + UINT16 ListenTimeout, + UINT64 FinalBlock, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_STATUS Status; + struct Tftpv4Ack Header; + UINT64 Offset; + UINT64 BlockNum; + UINT64 LastBlockNum; + UINT64 BufferSize; + UINTN NumMissed; + UINTN PacketSize; + UINTN SaveReplyLen; + UINTN ReplyLen; + UINT16 Timeout; + + LastBlockNum = *StartBlockPtr; + Timeout = ListenTimeout; + *NumMissedPtr = 0; + PacketSize = 0; + BufferSize = *BufferSizePtr; + ReplyLen = MAX_TFTP_PKT_SIZE;; + + // + // receive + // + do { + if ((SaveReplyLen = ReplyLen) > BufferSize) { + SaveReplyLen = (UINTN) BufferSize; + } + + /* %%TBD - add big block number support */ + + // + // get data - loop on resends + // + do { + ReplyLen = SaveReplyLen; + + if ((Status = TftpUdpRead ( + Private, + 0, + &Header, + &ReplyLen, + BufferPtr, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &MtftpInfoPtr->MCastIp, + &MtftpInfoPtr->CPort, + Timeout + )) != EFI_SUCCESS) { + return Status; + } + // + // make sure a data packet + // + if (Header.OpCode != HTONS (TFTP_DATA)) { + return EFI_PROTOCOL_ERROR; + } + } while ((BlockNum = NTOHS (Header.BlockNum)) == LastBlockNum); + + // + // make sure still going up + // + if (LastBlockNum > BlockNum) { + return EFI_PROTOCOL_ERROR; + } + + if (BlockNum - LastBlockNum > 0xFFFFFFFF) { + return EFI_PROTOCOL_ERROR; + } else { + NumMissed = (UINTN) (BlockNum - LastBlockNum - 1); + } + + LastBlockNum = BlockNum; + + // + // if first time through, some reinitialization + // + if (!PacketSize) { + *StartBlockPtr = BlockNum; + PacketSize = ReplyLen; + Timeout = TransTimeout; + } else { + *NumMissedPtr = (UINT16) (*NumMissedPtr + NumMissed); + } + // + // if missed packets, update start block, + // etc. and move packet to proper place in buffer + // + if (NumMissed) { + *StartBlockPtr = BlockNum; + if (!DontUseBuffer) { + Offset = NumMissed * PacketSize; + CopyMem (BufferPtr + Offset, BufferPtr, ReplyLen); + BufferPtr += Offset; + BufferSize -= Offset; + } + } + + if (!DontUseBuffer) { + BufferPtr += ReplyLen; + BufferSize -= ReplyLen; + } + } while (ReplyLen == PacketSize && BlockNum != FinalBlock); + + *BufferSizePtr = BufferSize; + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return // mtftp open session + @return // return code EFI_SUCCESS + @return // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done + @return // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest + @return // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all + @retval GOTUNI returns NO_DATA go will go to TFTP session) + +**/ +STATIC +EFI_STATUS +MtftpOpen ( + PXE_BASECODE_DEVICE * Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + UINTN *PacketSizePtr, + EFI_IP_ADDRESS * ServerIpPtr, + UINT8 *FilenamePtr, + EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr, + UINT8 *CompletionStatusPtr, +#define GOTUNI 1 +#define GOTMULTI 2 + IN BOOLEAN DontUseBuffer + ) +{ + EFI_STATUS Status; + EFI_IP_ADDRESS OurReplyIp; + struct Tftpv4Ack Header; + INTN ReplyLen; + INTN Retries; + UINT8 *BufferPtr2; + UINT8 TmpBuf[514]; + + Retries = NUM_MTFTP_OPEN_RETRIES; + BufferPtr2 = BufferPtr; + *PacketSizePtr = (UINTN) (MIN (*BufferSizePtr, MAX_TFTP_PKT_SIZE)); + + do { + // + // send a read request + // + *CompletionStatusPtr = 0; + + if ((Status = TftpRwReq ( + TFTP_RRQ, + 0, + Private, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &MtftpInfoPtr->CPort, + FilenamePtr, + PacketSizePtr, + TmpBuf + )) != EFI_SUCCESS) { + return Status; + } + + for (;;) { + // + // read reply + // + ZeroMem (&OurReplyIp, Private->IpLength); + ReplyLen = *PacketSizePtr; + + if ((Status = TftpUdpRead ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER, + &Header, + (UINTN *) &ReplyLen, + BufferPtr2, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &OurReplyIp, + &MtftpInfoPtr->CPort, + MtftpInfoPtr->TransmitTimeout + )) == EFI_SUCCESS) { + // + // check for first data packet + // + if (Header.OpCode != HTONS (TFTP_DATA)) { + return EFI_PROTOCOL_ERROR; + } + // + // check block num + // + if (Header.BlockNum != HTONS (1)) { + // + // it's not first + // if we are not the primary client, + // we probably got first and now second + // multicast but no unicast, so + // *CompletionStatusPtr = GOTMULTI - if this is + // the second, can just go on to listen + // starting with 2 as the last block + // received + // + if (Header.BlockNum != HTONS (2)) { + // + // not second + // + *CompletionStatusPtr = 0; + } + + return Status; + } + + // + // now actual + // + *PacketSizePtr = ReplyLen; + // + // see if a unicast data packet + // + if (!CompareMem ( + &OurReplyIp, + &Private->EfiBc.Mode->StationIp, + Private->IpLength + )) { + *CompletionStatusPtr |= GOTUNI; + // + // it is + // if already got multicast packet, + // got em both + // + if (*CompletionStatusPtr & GOTMULTI) { + break; + } + } else if (!CompareMem ( + &OurReplyIp, + &MtftpInfoPtr->MCastIp, + Private->IpLength + )) { + // + // otherwise see if a multicast data packet + // + *CompletionStatusPtr |= GOTMULTI; + // + // it is + // got first - bump pointer so that if + // second multi comes along, we're OK + // + if (!DontUseBuffer) { + BufferPtr2 = (UINT8 *) BufferPtr + ReplyLen; + } + // + // if already got unicast packet, + // got em both + // + if (*CompletionStatusPtr & GOTUNI) { + break; + } + } else { + // + // else protocol error + // + return EFI_PROTOCOL_ERROR; + } + } else if (Status == EFI_TIMEOUT) { + // + // bad return code - if timed out, retry + // + break; + } else { + // + // else just bad - failed MTFTP open + // + return Status; + } + } + } while (Status == EFI_TIMEOUT && --Retries); + + if (Status != EFI_SUCCESS) { + // + // open failed + // + return Status; + } + // + // got em both - go into receive mode + // routine to read rest of file after a successful open (TFTP or MTFTP) + // sends ACK and gets next data packet until short packet arrives, + // then sends ACK and (hopefully) times out + // + return LockStepReceive ( + Private, + (UINT16) ReplyLen, + BufferSizePtr, + ReplyLen, + BufferPtr, + ServerIpPtr, + &MtftpInfoPtr->SPort, + &MtftpInfoPtr->MCastIp, + &MtftpInfoPtr->CPort, + 1, + MtftpInfoPtr->TransmitTimeout, + DontUseBuffer + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +MtftpDownload ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS Status; + UINT64 StartBlock; + UINT64 LastBlock; + UINT64 LastStartBlock; + UINT64 BufferSize; + UINTN Offset; + UINTN NumMissed; + UINT16 TransTimeout; + UINT16 ListenTimeout; + UINT8 *BufferPtrLocal; + + TransTimeout = MtftpInfoPtr->TransmitTimeout; + ListenTimeout = MtftpInfoPtr->ListenTimeout; + LastBlock = 0; + LastStartBlock = 0; + Offset = 0; + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST; + Filter.IpCnt = 2; + Filter.IpList[0] = Private->EfiBc.Mode->StationIp; + Filter.IpList[1] = MtftpInfoPtr->MCastIp; + + if ((Status = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + return Status; + } + + for (;;) { + StartBlock = LastStartBlock; + BufferSize = *BufferSizePtr - Offset; + + if (DontUseBuffer) { + // + // overwrie the temp buf + // + BufferPtrLocal = BufferPtr; + } else { + BufferPtrLocal = BufferPtr + Offset; + + } + // + // special !!! do not leave enabled in saved version on Source Safe + // Following code put in in order to create a special version for regression + // test of MTFTP server to make sure it handles mulitple opens correctly. + // This code should NOT be enabled normally. + // +#ifdef SpecialNowaitVersion +#pragma message ("This is special version for MTFTP regression test") + if (StartBlock || !LastBlock) +#endif + if (((Status = MtftpListen ( + Private, + &BufferSize, + BufferPtrLocal, + ServerIpPtr, + MtftpInfoPtr, + &StartBlock, + &NumMissed, + TransTimeout, + ListenTimeout, + LastBlock, + DontUseBuffer + )) != EFI_SUCCESS) && (Status != EFI_TIMEOUT)) { + return Status; + // + // failed + // + } + // + // if none were received, start block is not reset + // + if (StartBlock == LastStartBlock) { + UINT8 CompStat; + + // + // timed out with none received - try MTFTP open + // + if ((Status = MtftpOpen ( + Private, + BufferSizePtr, + BufferPtr, + &Offset, + ServerIpPtr, + FilenamePtr, + MtftpInfoPtr, + &CompStat, + DontUseBuffer + )) != EFI_SUCCESS) { + // + // open failure - try TFTP + // + return Status; + } + // + // return code EFI_SUCCESS + // and *CompletionStatusPtr = GOTUNI | GOTMULTI means done + // and *CompletionStatusPtr = GOTMULTI means got first two multicast packets, use listen for rest + // and *CompletionStatusPtr = 0 means did not get first two multicast packets, use listen for all + // (do not get = GOTUNI - returns NO_DATA go will go to TFTP session) + // + if (CompStat == (GOTUNI | GOTMULTI)) { + // + // finished - got it all + // + return Status; + } + + if (CompStat) { + // + // offset is two packet lengths + // + Offset <<= 1; + // + // last block received + // + LastStartBlock = 2; + } else { + Offset = 0; + LastStartBlock = 0; + } + + ListenTimeout = TransTimeout; + continue; + } + // + // did we get the last block + // + if (Status == EFI_SUCCESS) { + // + // yes - set the file size if this was first time + // + if (!LastBlock) { + *BufferSizePtr -= BufferSize; + } + // + // if buffer was too small, finished + // + if (!DontUseBuffer) { + return EFI_BUFFER_TOO_SMALL; + } + // + // if we got them all, finished + // + if (!NumMissed && StartBlock == LastStartBlock + 1) { + return Status; + } + // + // did not get them all - set last block + // + LastBlock = (UINT16) (StartBlock - 1); + } + // + // compute listen timeout + // + ListenTimeout = (UINT16) ((NumMissed > MtftpInfoPtr->ListenTimeout) ? 0 : (MtftpInfoPtr->ListenTimeout - NumMissed)); + + // + // reset + // + Offset = 0; + LastStartBlock = 0; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpInfo ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + EFI_IP_ADDRESS *ServerIpPtr, + EFI_PXE_BASE_CODE_UDP_PORT SrvPort, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT OurPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort; + EFI_STATUS Status; + UINT64 BlockNum; + UINTN Offset; + UINTN ReplyLen; + UINT8 *Ptr; + + union { + struct Tftpv4Oack OAck2Ptr; + struct Tftpv4Ack Ack2Ptr; + struct Tftpv4Data Datastr; + } u; + + OurPort = 0; + ServerReplyPort = 0; + ReplyLen = sizeof (u.Datastr.Data); + + // + // send a write request with the blocksize option - + // sets our IP and port - and receive reply - sets his port + // will retry operation up to 3 times if no response, + // and will retry without options on an error reply + // + if ((Status = TftpRwReqwResp ( + TFTP_RRQ, + /* BIGBLKNUMOP | */BKSZOP | TSIZEOP, + Private, + &u, + PacketSizePtr, + &ReplyLen, + u.Datastr.Data, + ServerIpPtr, + &SrvPort, + &ServerReplyPort, + &OurPort, + FilenamePtr, + REQ_RESP_TIMEOUT + )) != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpInfo() Exit #1")); + return Status; + } + // + // check for good OACK + // + if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) { + // + // now parse it for options + // bigblk# + // + Ptr = FindOption ( + BigBlkNumOp, + sizeof (BigBlkNumOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen + sizeof (u.Ack2Ptr.BlockNum) + ); + + if (Ptr != NULL) { + if (AtoU (Ptr) == 8) { + Private->BigBlkNumFlag = TRUE; + } else { + return EFI_PROTOCOL_ERROR; + } + } + // + // blksize + // + Ptr = FindOption ( + BlockSizeOp, + sizeof (BlockSizeOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen += sizeof (u.Ack2Ptr.BlockNum) + ); + + *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512; + + // + // tsize + // + Ptr = FindOption ( + TsizeOp, + sizeof (TsizeOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen + ); + + if (Ptr != NULL) { + *BufferSizePtr = AtoU64 (Ptr); + + // + // teminate session with error + // + SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort); + + return EFI_SUCCESS; + } + + Offset = 0; + BlockNum = 0; + } else { + // + // if MTFTP get filesize, return unsupported + // + if (SrvPort != TftpRequestPort) { + SendError (Private, ServerIpPtr, &ServerReplyPort, &OurPort); + DEBUG ((DEBUG_WARN, "\nTftpInfo() Exit #3")); + return EFI_UNSUPPORTED; + } + + Offset = ReplyLen; + // + // last block received + // + BlockNum = 1; + } + // + // does not support the option - do a download with no buffer + // + *BufferSizePtr = 0; + + Status = LockStepReceive ( + Private, + (UINT16) ReplyLen, + BufferSizePtr, + Offset, + (INT8 *) &u, + ServerIpPtr, + &ServerReplyPort, + &Private->EfiBc.Mode->StationIp, + &OurPort, + BlockNum, + ACK_TIMEOUT, + TRUE + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpInfo() LockStepReceive() == %Xh", Status)); + } + + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpDownload ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + UINT8 *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + EFI_PXE_BASE_CODE_UDP_PORT SrvPort, + UINT16 Req, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT OurPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort; + EFI_STATUS Status; + UINT64 Offset; + UINT64 BlockNum; + UINTN ReplyLen; + UINT8 *Ptr; + + union { + struct Tftpv4Ack Ack2Ptr; + struct Tftpv4Oack OAck2Ptr; + struct Tftpv4Data Data; + struct Tftpv4Ack8 Ack8Ptr; + struct Tftpv4Data8 Data8; + } U; + + OurPort = 0; + ServerReplyPort = 0; + ReplyLen = (UINTN) ((*BufferSizePtr > 0xFFFF) ? 0xFFFF : *BufferSizePtr); + + // + // send a read request with the blocksize option - sets our IP and port + // - and receive reply - sets his port will retry operation up to 3 + // times if no response, and will retry without options on an error + // reply + // + if ((Status = TftpRwReqwResp ( + Req, + /* BIGBLKNUMOP | */BKSZOP, + Private, + &U, + PacketSizePtr, + &ReplyLen, + BufferPtr, + ServerIpPtr, + &SrvPort, + &ServerReplyPort, + &OurPort, + FilenamePtr, + REQ_RESP_TIMEOUT + )) != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #1 %xh (%r)", Status, Status)); + return Status; + } + // + // check for OACK + // + if (U.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) { + // + // get the OACK + // + CopyMem (U.Data.Data, BufferPtr, ReplyLen); + + Ptr = FindOption ( + BigBlkNumOp, + sizeof (BigBlkNumOp), + U.OAck2Ptr.OpAck[0].Option, + ReplyLen + sizeof (U.Ack2Ptr.BlockNum) + ); + + if (Ptr != NULL) { + if (AtoU (Ptr) == 8) { + Private->BigBlkNumFlag = TRUE; + } else { + return EFI_PROTOCOL_ERROR; + } + } + // + // now parse it for blocksize option + // + Ptr = FindOption ( + BlockSizeOp, + sizeof (BlockSizeOp), + U.OAck2Ptr.OpAck[0].Option, + ReplyLen += sizeof (U.Ack2Ptr.BlockNum) + ); + + ReplyLen = (Ptr != NULL) ? AtoU (Ptr) : 512; + + Offset = 0; + // + // last block received + // + BlockNum = 0; + } else if (U.Ack2Ptr.OpCode != HTONS (TFTP_DATA) || U.Ack2Ptr.BlockNum != HTONS (1)) { + // + // or data + // + DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #2 %xh (%r)", Status, Status)); + + return EFI_PROTOCOL_ERROR; + } else { + // + // got good data packet + // + Offset = ReplyLen; + // + // last block received + // + BlockNum = 1; + } + + if (PacketSizePtr != NULL) { + *PacketSizePtr = ReplyLen; + } + // + // routine to read rest of file after a successful open (TFTP or MTFTP) + // sends ACK and gets next data packet until short packet arrives, then sends + // ACK and (hopefully) times out + // if first packet has been read, BufferPtr and BufferSize must reflect fact + // + Status = LockStepReceive ( + Private, + ReplyLen, + BufferSizePtr, + Offset, + BufferPtr, + ServerIpPtr, + &ServerReplyPort, + &Private->EfiBc.Mode->StationIp, + &OurPort, + BlockNum, + ACK_TIMEOUT, + DontUseBuffer + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_WARN, "\nTftpDownload() Exit #3 %xh (%r)", Status, Status)); + + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = TftpInfo ( + Private, + BufferSizePtr, + ServerIpPtr, + SrvPort, + FilenamePtr, + PacketSizePtr + ); + + if (!EFI_ERROR (Status)) { + Status = EFI_BUFFER_TOO_SMALL; + } + } + } + + return Status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +STATIC +EFI_STATUS +TftpUpload ( + PXE_BASECODE_DEVICE *Private, + UINT64 *BufferSizePtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + BOOLEAN Overwrite + ) +{ + struct Tftpv4Ack Header; + EFI_PXE_BASE_CODE_UDP_PORT OurPort; + EFI_PXE_BASE_CODE_UDP_PORT ServerReplyPort; + EFI_STATUS Status; + UINT64 BlockNum; + UINT64 TransferSize; + UINTN ReplyLen; + UINTN TransferLen; + UINT16 Options; + UINT8 *Ptr; + + union { + struct Tftpv4Oack OAck2Ptr; + struct Tftpv4Ack Ack2Ptr; + struct Tftpv4Data Datastr; + } u; + + OurPort = 0; + ServerReplyPort = 0; + TransferSize = *BufferSizePtr; + ReplyLen = sizeof (u.Datastr.Data); + Options = (UINT16) ((Overwrite) ? OVERWRITEOP | BKSZOP : BKSZOP); + + // + // send a write request with the blocksize option - sets our IP and port - + // and receive reply - sets his port + // will retry operation up to 3 times if no response, and will retry without + // options on an error reply + // + if ((Status = TftpRwReqwResp ( + TFTP_WRQ, + Options, + Private, + &u, + PacketSizePtr, + &ReplyLen, + u.Datastr.Data, + ServerIpPtr, + &TftpRequestPort, + &ServerReplyPort, + &OurPort, + FilenamePtr, + REQ_RESP_TIMEOUT + )) != EFI_SUCCESS) { + return Status; + } + // + // check for OACK + // + if (u.OAck2Ptr.OpCode == HTONS (TFTP_OACK)) { + // + // parse it for blocksize option + // + Ptr = FindOption ( + BlockSizeOp, + sizeof (BlockSizeOp), + u.OAck2Ptr.OpAck[0].Option, + ReplyLen += sizeof (u.Ack2Ptr.BlockNum) + ); + *PacketSizePtr = (Ptr) ? AtoU (Ptr) : 512; + } + // + // or ACK + // + else if (u.Ack2Ptr.OpCode == HTONS (TFTP_ACK)) { + // + // option was not supported + // + *PacketSizePtr = 512; + } else { + return EFI_PROTOCOL_ERROR; + } + // + // loop + // + Header.OpCode = HTONS (TFTP_DATA); + BlockNum = 1; + Header.BlockNum = HTONS (1); + + do { + UINTN HeaderSize; + INTN Retries; + + Retries = NUM_ACK_RETRIES; + HeaderSize = sizeof (Header); + TransferLen = (UINTN) (MIN (*PacketSizePtr, TransferSize)); + + // + // write a data packet and get an ack + // + do { + // + // write + // + if ((Status = UdpWrite ( + Private, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + ServerIpPtr, + &ServerReplyPort, + 0, + 0, + &OurPort, + &HeaderSize, + &Header, + &TransferLen, + BufferPtr + )) != EFI_SUCCESS) { + return Status; + } + // + // read reply + // + ReplyLen = sizeof (u.Datastr.Data); + + if ((Status = TftpUdpRead ( + Private, + 0, + &u, + &ReplyLen, + u.Datastr.Data, + ServerIpPtr, + &ServerReplyPort, + 0, + &OurPort, + ACK_TIMEOUT + )) == EFI_SUCCESS) { + // + // check for ACK for this data packet + // + if (u.Ack2Ptr.OpCode != HTONS (TFTP_ACK)) { + return EFI_PROTOCOL_ERROR; + } + + if (u.Ack2Ptr.BlockNum != Header.BlockNum) { + // + // not for this packet - continue + // + Status = EFI_TIMEOUT; + } + } + } while (Status == EFI_TIMEOUT && --Retries); + + if (Status != EFI_SUCCESS) { + return Status; + } + + BufferPtr = (VOID *) ((UINT8 *) (BufferPtr) + TransferLen); + TransferSize -= TransferLen; + ++BlockNum; + Header.BlockNum = HTONS ((UINT16) BlockNum); + } while (TransferLen == *PacketSizePtr); + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return * EFI_INVALID_PARAMETER + @return * EFI_OUT_OF_RESOURCES + @return * EFI_BAD_BUFFER_SIZE + @return * Status is also returned from IpFilter(), TftpInfo(), MtftpDownload(), + @return * TftpDownload() and TftpUpload(). + +**/ +EFI_STATUS +PxeBcMtftp ( + PXE_BASECODE_DEVICE *Private, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + UINT64 *BufferSizePtr, + VOID *BufferPtr, + EFI_IP_ADDRESS *ServerIpPtr, + UINT8 *FilenamePtr, + UINTN *PacketSizePtr, + IN EFI_PXE_BASE_CODE_MTFTP_INFO *MtftpInfoPtr, OPTIONAL + IN BOOLEAN Overwrite, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS StatCode; + EFI_STATUS Status; + UINT64 BufferSizeLocal; + UINTN PacketSize; + UINT8 *BufferPtrLocal; + + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + Filter.IpCnt = 0; + Filter.reserved = 0; + + /* No error has occurred, yet. */ + Private->EfiBc.Mode->TftpErrorReceived = FALSE; + + /* We must at least have an MTFTP server IP address and + * a pointer to the buffer size. + */ + if (!ServerIpPtr || !BufferSizePtr) { + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #1")); + + return EFI_INVALID_PARAMETER; + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_MTFTP; + + // + // make sure filter set to unicast at start + // + if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + DEBUG ( + (DEBUG_NET, + "\nPxeBcMtftp() Exit IpFilter() == %Xh", + StatCode) + ); + + return StatCode; + } + // + // set unset parms to default values + // + if (!PacketSizePtr) { + *(PacketSizePtr = &PacketSize) = MAX_TFTP_PKT_SIZE; + } + + if (*PacketSizePtr > *BufferSizePtr) { + *PacketSizePtr = (UINTN) *BufferSizePtr; + } + + if (*PacketSizePtr < MIN_TFTP_PKT_SIZE) { + *PacketSizePtr = MIN_TFTP_PKT_SIZE; + return EFI_INVALID_PARAMETER; + } + + if (*PacketSizePtr > BUFFER_ALLOCATE_SIZE) { + *PacketSizePtr = BUFFER_ALLOCATE_SIZE; + } + + if (*PacketSizePtr > MAX_TFTP_PKT_SIZE) { + *PacketSizePtr = MAX_TFTP_PKT_SIZE; + } + + if (Operation == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) { + StatCode = TftpInfo ( + Private, + BufferSizePtr, + ServerIpPtr, + TftpRequestPort, + FilenamePtr, + PacketSizePtr + ); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit TftpInfo() == %Xh", + StatCode) + ); + } + + return StatCode; + } + + if (Operation == EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE) { + if (!MtftpInfoPtr || !MtftpInfoPtr->SPort) { + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #2")); + return EFI_INVALID_PARAMETER; + } else { + StatCode = TftpInfo ( + Private, + BufferSizePtr, + ServerIpPtr, + MtftpInfoPtr->SPort, + FilenamePtr, + PacketSizePtr + ); + + gBS->Stall (10000); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit TftpInfo() == %Xh", + StatCode) + ); + } + + return StatCode; + } + } + + if (!BufferPtr && !DontUseBuffer) { + // + // if dontusebuffer is false and no buffer??? + // + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #3")); + // + // DontUseBuffer can be true only for read_file operation + // + return EFI_INVALID_PARAMETER; + } + + if (DontUseBuffer) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + BUFFER_ALLOCATE_SIZE, + &BufferPtrLocal + ); + + if (EFI_ERROR (Status) || BufferPtrLocal == NULL) { + DEBUG ((DEBUG_NET, "\nPxeBcMtftp() Exit #4")); + return EFI_OUT_OF_RESOURCES; + } + + BufferSizeLocal = BUFFER_ALLOCATE_SIZE; + } else { + if (!*BufferSizePtr && Operation != EFI_PXE_BASE_CODE_TFTP_WRITE_FILE) { + DEBUG ((DEBUG_WARN, "\nPxeBcMtftp() Exit #5")); + return EFI_BAD_BUFFER_SIZE; + } + + BufferPtrLocal = BufferPtr; + BufferSizeLocal = *BufferSizePtr; + } + + switch (Operation) { + case EFI_PXE_BASE_CODE_MTFTP_READ_FILE: + if (FilenamePtr == NULL || + MtftpInfoPtr == NULL || + MtftpInfoPtr->MCastIp.Addr[0] == 0 || + MtftpInfoPtr->SPort == 0 || + MtftpInfoPtr->CPort == 0 || + MtftpInfoPtr->ListenTimeout == 0 || + MtftpInfoPtr->TransmitTimeout == 0 + ) { + StatCode = EFI_INVALID_PARAMETER; + break; + } + // + // try MTFTP - if fails, drop into TFTP read + // + if ((StatCode = MtftpDownload ( + Private, + &BufferSizeLocal, + BufferPtrLocal, + ServerIpPtr, + FilenamePtr, + MtftpInfoPtr, + DontUseBuffer + )) == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) { + if (BufferSizePtr /* %% !DontUseBuffer */ ) { + *BufferSizePtr = BufferSizeLocal; + } + + break; + } + // + // go back to unicast + // + if ((StatCode = IpFilter (Private, &Filter)) != EFI_SUCCESS) { + break; + } + + /* fall thru */ + case EFI_PXE_BASE_CODE_TFTP_READ_FILE: + if (FilenamePtr == NULL) { + StatCode = EFI_INVALID_PARAMETER; + break; + } + + StatCode = TftpDownload ( + Private, + &BufferSizeLocal, + BufferPtrLocal, + ServerIpPtr, + FilenamePtr, + PacketSizePtr, + TftpRequestPort, + TFTP_RRQ, + DontUseBuffer + ); + + if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) { + if (BufferSizePtr /* !DontUseBuffer */ ) { + *BufferSizePtr = BufferSizeLocal; + } + } + + break; + + case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE: + if (FilenamePtr == NULL || DontUseBuffer) { + // + // not a valid option + // + StatCode = EFI_INVALID_PARAMETER; + break; + } + + StatCode = TftpUpload ( + Private, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + FilenamePtr, + PacketSizePtr, + Overwrite + ); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #6 %xh (%r)", + StatCode, + StatCode) + ); + } + + return StatCode; + + case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY: + if (FilenamePtr == NULL || DontUseBuffer) { + // + // not a valid option + // + StatCode = EFI_INVALID_PARAMETER; + break; + } + + StatCode = TftpDownload ( + Private, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + FilenamePtr, + PacketSizePtr, + TftpRequestPort, + TFTP_DIR, + DontUseBuffer + ); + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #7 %xh (%r)", + StatCode, + StatCode) + ); + } + + return StatCode; + + case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY: + if (DontUseBuffer) { + StatCode = EFI_INVALID_PARAMETER; + break; + } + + if (MtftpInfoPtr == NULL || !MtftpInfoPtr->SPort) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #9 %xh (%r)", + EFI_INVALID_PARAMETER, + EFI_INVALID_PARAMETER) + ); + + return EFI_INVALID_PARAMETER; + } + + StatCode = TftpDownload ( + Private, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + (UINT8 *) "/", + PacketSizePtr, + MtftpInfoPtr->SPort, + TFTP_DIR, + DontUseBuffer + ); + + break; + + default: + StatCode = EFI_INVALID_PARAMETER; + } + + if (DontUseBuffer) { + gBS->FreePool (BufferPtrLocal); + } + + if (StatCode != EFI_SUCCESS) { + DEBUG ( + (DEBUG_WARN, + "\nPxeBcMtftp() Exit #8 %xh (%r)", + StatCode, + StatCode) + ); + } + + gBS->Stall (10000); + + return StatCode; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return * EFI_INVALID_PARAMETER + @return * Status is also returned from PxeBcMtftp(); + +**/ +EFI_STATUS +EFIAPI +BcMtftp ( + IN EFI_PXE_BASE_CODE_PROTOCOL * This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSizePtr, + IN UINTN *BlockSizePtr OPTIONAL, + IN EFI_IP_ADDRESS * ServerIpPtr, + IN UINT8 *FilenamePtr, + IN EFI_PXE_BASE_CODE_MTFTP_INFO * MtftpInfoPtr OPTIONAL, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER Filter; + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + if (!IS_INADDR_UNICAST (ServerIpPtr)) { + // + // The station IP is not a unicast address. + // + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + // + // Issue BC command + // + Filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + Filter.IpCnt = 0; + Filter.reserved = 0; + + DEBUG ((DEBUG_WARN, "\nBcMtftp() Op=%d Buf=%Xh", Operation, BufferPtr)); + + StatCode = PxeBcMtftp ( + Private, + Operation, + BufferSizePtr, + BufferPtr, + ServerIpPtr, + FilenamePtr, + BlockSizePtr, + MtftpInfoPtr, + Overwrite, + DontUseBuffer + ); + + // + // restore to unicast + // + IpFilter (Private, &Filter); + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* eof - PxeBcMtftp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c new file mode 100644 index 0000000000..fcb55acfa0 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_udp.c @@ -0,0 +1,517 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_bc_udp.c + +Abstract: + + +**/ + +#include "Bc.h" + +// +// ////////////////////////////////////////////////////////////////////// +// +// Udp Write Routine - called by base code - e.g. TFTP - already locked +// + +/** + + @return EFI_SUCCESS := + @return EFI_INVALID_PARAMETER := + @return other := + +**/ +EFI_STATUS +UdpWrite ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIpPtr, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, + IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL + IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSizeptr, + IN VOID *BufferPtr + ) +{ + UINTN TotalLength; + UINTN HeaderSize; + EFI_PXE_BASE_CODE_UDP_PORT DefaultSrcPort; + + // + // + // + HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0; + DefaultSrcPort = 0; + + // + // check parameters + // + if (BufferSizeptr == NULL || + BufferPtr == NULL || + DestIpPtr == NULL || + DestPortPtr == NULL || + (HeaderSizePtr != NULL && *HeaderSizePtr == 0) || + (HeaderSize != 0 && HeaderPtr == NULL) || + (GatewayIpPtr != NULL && !IS_INADDR_UNICAST(GatewayIpPtr)) || + (OpFlags &~(EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT)) + ) { + DEBUG ( + (DEBUG_WARN, + "\nUdpWrite() Exit #1 %xh (%r)", + EFI_INVALID_PARAMETER, + EFI_INVALID_PARAMETER) + ); + + return EFI_INVALID_PARAMETER; + } + + TotalLength = *BufferSizeptr + HeaderSize + sizeof (UDPV4_HEADER); + + if (TotalLength > 0x0000ffff) { + DEBUG ( + (DEBUG_WARN, + "\nUdpWrite() Exit #2 %xh (%r)", + EFI_BAD_BUFFER_SIZE, + EFI_BAD_BUFFER_SIZE) + ); + + return EFI_BAD_BUFFER_SIZE; + } + + if (SrcIpPtr == NULL) { + SrcIpPtr = &Private->EfiBc.Mode->StationIp; + } + + if (SrcPortPtr == NULL) { + SrcPortPtr = &DefaultSrcPort; + OpFlags |= EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT; + } + + if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) { + *SrcPortPtr = Private->RandomPort; + + if (++Private->RandomPort == 0) { + Private->RandomPort = PXE_RND_PORT_LOW; + } + } + +#define IpTxBuffer ((IPV4_BUFFER *) Private->TransmitBufferPtr) + // + // build pseudo header and udp header in transmit buffer + // +#define Udpv4Base ((UDPV4_HEADERS *) (IpTxBuffer->u.Data - sizeof (UDPV4_PSEUDO_HEADER))) + + Udpv4Base->Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0]; + Udpv4Base->Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0]; + Udpv4Base->Udpv4PseudoHeader.Zero = 0; + Udpv4Base->Udpv4PseudoHeader.Protocol = PROT_UDP; + Udpv4Base->Udpv4PseudoHeader.TotalLength = HTONS (TotalLength); + Udpv4Base->Udpv4Header.SrcPort = HTONS (*SrcPortPtr); + Udpv4Base->Udpv4Header.DestPort = HTONS (*DestPortPtr); + Udpv4Base->Udpv4Header.TotalLength = Udpv4Base->Udpv4PseudoHeader.TotalLength; + Udpv4Base->Udpv4Header.Checksum = 0; + + if (HeaderSize != 0) { + CopyMem (IpTxBuffer->u.Udp.Data, HeaderPtr, HeaderSize); + } + + HeaderSize += sizeof (UDPV4_HEADER); + + Udpv4Base->Udpv4Header.Checksum = IpChecksum2 ( + (UINT16 *) Udpv4Base, + HeaderSize + sizeof (UDPV4_PSEUDO_HEADER), + (UINT16 *) BufferPtr, + (UINT16) *BufferSizeptr + ); + + if (Udpv4Base->Udpv4Header.Checksum == 0) { + Udpv4Base->Udpv4Header.Checksum = 0xffff; + // + // transmit zero checksum as ones complement + // + } + + return Ip4Send ( + Private, + OpFlags, + PROT_UDP, + Udpv4Base->Udpv4PseudoHeader.SrcAddr.L, + Udpv4Base->Udpv4PseudoHeader.DestAddr.L, + (GatewayIpPtr) ? GatewayIpPtr->Addr[0] : 0, + HeaderSize, + BufferPtr, + *BufferSizeptr + ); +} +// +// ////////////////////////////////////////////////////////// +// +// BC Udp Write Routine +// + +/** + + @return EFI_SUCCESS := + @return other := + +**/ +EFI_STATUS +EFIAPI +BcUdpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIpPtr, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, + IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL + IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSizeptr, + IN VOID *BufferPtr + ) +{ + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_WRITE; + + // + // Issue BC command + // + StatCode = UdpWrite ( + Private, + OpFlags, + DestIpPtr, + DestPortPtr, + GatewayIpPtr, + SrcIpPtr, + SrcPortPtr, + HeaderSizePtr, + HeaderPtr, + BufferSizeptr, + BufferPtr + ); + + // + // Unlock the instance data + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} +// +// ///////////////////////////////////////////////////////////////////// +// +// Udp Read Routine - called by base code - e.g. TFTP - already locked +// + +/** + + @return EFI_SUCCESS := + @return EFI_INVALID_PARAMETER := + @return other := + +**/ +EFI_STATUS +UdpRead ( + IN PXE_BASECODE_DEVICE *Private, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL + IN UINTN *HeaderSizePtr, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSizeptr, + IN VOID *BufferPtr, + EFI_EVENT TimeoutEvent + ) +{ + EFI_STATUS StatCode; + EFI_IP_ADDRESS TmpSrcIp; + EFI_IP_ADDRESS TmpDestIp; + UINTN BufferSize; + UINTN HeaderSize; + + // + // combination structure of pseudo header/udp header + // +#pragma pack (1) + struct { + UDPV4_PSEUDO_HEADER Udpv4PseudoHeader; + UDPV4_HEADER Udpv4Header; + UINT8 ProtHdr[64]; + } Hdrs; +#pragma pack () + + HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0; + // + // read [with filtering] + // check parameters + // + if (BufferSizeptr == NULL || + BufferPtr == NULL || + (HeaderSize != 0 && HeaderPtr == NULL) || + (OpFlags &~UDP_FILTER_MASK) + // + // if filtering on a particular IP/Port, need it + // + || + (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr == NULL) || + (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && SrcPortPtr == NULL) || + (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && DestPortPtr == NULL) + ) { + DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #1 Invalid Parameter")); + return EFI_INVALID_PARAMETER; + } + + // + // in case we loop + // + BufferSize = *BufferSizeptr; + // + // we need source and dest IPs for pseudo header + // + if (SrcIpPtr == NULL) { + SrcIpPtr = &TmpSrcIp; + } + + if (DestIpPtr == NULL) { + DestIpPtr = &TmpDestIp; + TmpDestIp = Private->EfiBc.Mode->StationIp; + } + +#if SUPPORT_IPV6 + if (Private->EfiBc.Mode->UsingIpv6) { + // + // %%TBD + // + } +#endif + + for (;;) { + *BufferSizeptr = BufferSize; + + StatCode = IpReceive ( + Private, + OpFlags, + SrcIpPtr, + DestIpPtr, + PROT_UDP, + &Hdrs.Udpv4Header, + HeaderSize + sizeof Hdrs.Udpv4Header, + BufferPtr, + BufferSizeptr, + TimeoutEvent + ); + + if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) { + UINT16 SPort; + UINT16 DPort; + + SPort = NTOHS (Hdrs.Udpv4Header.SrcPort); + DPort = NTOHS (Hdrs.Udpv4Header.DestPort); + + // + // do filtering + // + if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && *SrcPortPtr != SPort) { + continue; + } + + if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && *DestPortPtr != DPort) { + continue; + } + // + // check checksum + // + if (StatCode == EFI_SUCCESS && Hdrs.Udpv4Header.Checksum) { + Hdrs.Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0]; + Hdrs.Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0]; + Hdrs.Udpv4PseudoHeader.Zero = 0; + Hdrs.Udpv4PseudoHeader.Protocol = PROT_UDP; + Hdrs.Udpv4PseudoHeader.TotalLength = Hdrs.Udpv4Header.TotalLength; + + if (Hdrs.Udpv4Header.Checksum == 0xffff) { + Hdrs.Udpv4Header.Checksum = 0; + } + + if (IpChecksum2 ( + (UINT16 *) &Hdrs.Udpv4PseudoHeader, + HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader) + sizeof (Hdrs.Udpv4Header), + (UINT16 *) BufferPtr, + *BufferSizeptr + )) { + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Hdrs.Udpv4PseudoHeader == %Xh", + Hdrs.Udpv4PseudoHeader) + ); + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Header size == %d", + HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader)) + ); + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() BufferPtr == %Xh", + BufferPtr) + ); + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Buffer size == %d", + *BufferSizeptr) + ); + DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #2 Device Error")); + return EFI_DEVICE_ERROR; + } + } + // + // all passed + // + if (SrcPortPtr != NULL) { + *SrcPortPtr = SPort; + } + + if (DestPortPtr != NULL) { + *DestPortPtr = DPort; + } + + if (HeaderSize != 0) { + CopyMem (HeaderPtr, Hdrs.ProtHdr, HeaderSize); + } + } + + if ((StatCode != EFI_SUCCESS) && (StatCode != EFI_TIMEOUT)) { + DEBUG ( + (DEBUG_INFO, + "\nUdpRead() Exit #3 %Xh %r", + StatCode, + StatCode) + ); + } + + return StatCode; + } +} +// +// ////////////////////////////////////////////////////////// +// +// BC Udp Read Routine +// + +/** + + @return EFI_SUCCESS := + @return other := + +**/ +EFI_STATUS +EFIAPI +BcUdpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_STATUS StatCode; + PXE_BASECODE_DEVICE *Private; + + // + // Lock the instance data and make sure started + // + StatCode = EFI_SUCCESS; + + if (This == NULL) { + DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); + return EFI_INVALID_PARAMETER; + } + + Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); + return EFI_INVALID_PARAMETER; + } + + EfiAcquireLock (&Private->Lock); + + if (This->Mode == NULL || !This->Mode->Started) { + DEBUG ((DEBUG_ERROR, "BC was not started.")); + EfiReleaseLock (&Private->Lock); + return EFI_NOT_STARTED; + } + + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_READ; + + // + // Issue BC command + // + StatCode = UdpRead ( + Private, + OpFlags, + DestIp, + DestPort, + SrcIp, + SrcPort, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr, + 0 + ); + + // + // Unlock the instance data and return + // + EfiReleaseLock (&Private->Lock); + return StatCode; +} + +/* eof - pxe_bc_udp.c */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c new file mode 100644 index 0000000000..7c5d0bc2fb --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_loadfile.c @@ -0,0 +1,1614 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + pxe_loadfile.c + +Abstract: + An implementation of the load file protocol for network devices. + + +**/ + + +#include "Bc.h" + +#define DO_MENU (EFI_SUCCESS) +#define NO_MENU (DO_MENU + 1) +#define LOCAL_BOOT (EFI_ABORTED) +#define AUTO_SELECT (NO_MENU) + +#define NUMBER_ROWS 25 // we set to mode 0 +#define MAX_MENULIST 23 + +#define Ctl(x) (0x1F & (x)) + +typedef union { + DHCPV4_OP_STRUCT *OpPtr; + PXE_BOOT_MENU_ENTRY *CurrentMenuItemPtr; + PXE_OP_DISCOVERY_CONTROL *DiscCtlOpStr; + PXE_OP_BOOT_MENU *MenuPtr; + UINT8 *BytePtr; +} UNION_PTR; + + + +/** + PxeBc callback routine for status updates and aborts. + + @param This Pointer to PxeBcCallback + interface + @param Function PxeBc function ID# + @param Received Receive/transmit flag + @param PacketLength Length of received packet (0 + == idle callback) + @param PacketPtr Pointer to received packet + (NULL == idle callback) + + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE + EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT + - + +**/ +STATIC +EFI_PXE_BASE_CODE_CALLBACK_STATUS +EFIAPI +bc_callback ( + IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL * This, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN BOOLEAN Received, + IN UINT32 PacketLength, + IN EFI_PXE_BASE_CODE_PACKET * PacketPtr OPTIONAL + ) +{ + STATIC UINTN Propeller; + + EFI_INPUT_KEY Key; + UINTN Row; + UINTN Col; + + Propeller = 0; + // + // Resolve Warning 4 unreferenced parameter problem + // + This = This; + + // + // Check for user abort. + // + if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_SUCCESS) { + if (!Key.ScanCode) { + if (Key.UnicodeChar == Ctl ('c')) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT; + } + } else if (Key.ScanCode == SCAN_ESC) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT; + } + } + // + // Do nothing if this is a receive. + // + if (Received) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + // + // The display code is only for these functions. + // + switch (Function) { + case EFI_PXE_BASE_CODE_FUNCTION_MTFTP: + // + // If this is a transmit and not a M/TFTP open request, + // return now. Do not print a dot for each M/TFTP packet + // that is sent, only for the open packets. + // + if (PacketLength != 0 && PacketPtr != NULL) { + if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + } + + break; + + case EFI_PXE_BASE_CODE_FUNCTION_DHCP: + case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER: + break; + + default: + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + // + // Display routines + // + if (PacketLength != 0 && PacketPtr != NULL) { + // + // Display a '.' when a packet is transmitted. + // + AsciiPrint ("."); + } else if (PacketLength == 0 && PacketPtr == NULL) { + // + // Display a propeller when waiting for packets if at + // least 200 ms have passed. + // + Row = gST->ConOut->Mode->CursorRow; + Col = gST->ConOut->Mode->CursorColumn; + + AsciiPrint ("%c", "/-\\|"[Propeller]); + gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row); + + Propeller = (Propeller + 1) & 3; + } + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; +} + +STATIC EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL _bc_callback = { + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION, + &bc_callback +}; + + +/** + Display an IPv4 address in dot notation. + + @param Ptr Pointer to IPv4 address. + + @return None + +**/ +STATIC +VOID +PrintIpv4 ( + UINT8 *Ptr + ) +{ + if (Ptr != NULL) { + AsciiPrint ("%d.%d.%d.%d", Ptr[0], Ptr[1], Ptr[2], Ptr[3]); + } +} + + +/** + Display client and server IP information. + + @param Private Pointer to PxeBc interface + + @return None + +**/ +STATIC +VOID +ShowMyInfo ( + IN PXE_BASECODE_DEVICE *Private + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + UINTN Index; + + // + // Do nothing if a NULL pointer is passed in. + // + if (Private == NULL) { + return ; + } + // + // Get pointer to PXE BaseCode mode structure + // + PxeBcMode = Private->EfiBc.Mode; + + // + // Display client IP address + // + AsciiPrint ("\rCLIENT IP: "); + PrintIpv4 (PxeBcMode->StationIp.v4.Addr); + + // + // Display subnet mask + // + AsciiPrint (" MASK: "); + PrintIpv4 (PxeBcMode->SubnetMask.v4.Addr); + + // + // Display DHCP and proxyDHCP IP addresses + // + if (PxeBcMode->ProxyOfferReceived) { + AsciiPrint ("\nDHCP IP: "); + PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr); + + AsciiPrint (" PROXY IP: "); + PrintIpv4 (((DHCPV4_OP_SERVER_IP *) PXE_OFFER_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr); + } else { + AsciiPrint (" DHCP IP: "); + PrintIpv4 (((DHCPV4_OP_SERVER_IP *) DHCPV4_ACK_BUFFER.OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1])->Ip.Addr); + } + // + // Display gateway IP addresses + // + for (Index = 0; Index < PxeBcMode->RouteTableEntries; ++Index) { + if ((Index % 3) == 0) { + AsciiPrint ("\r\nGATEWAY IP:"); + } + + AsciiPrint (" "); + PrintIpv4 (PxeBcMode->RouteTable[Index].GwAddr.v4.Addr); + AsciiPrint (" "); + } + + AsciiPrint ("\n"); +} + + +/** + Display prompt and wait for input. + + @param Private Pointer to PxeBc interface + @param BootPromptPtr Pointer to PXE boot prompt + option + + @retval AUTO_SELECT DO_MENU - + @retval NO_MENU + @retval LOCAL_BOOT + +**/ +STATIC +EFI_STATUS +DoPrompt ( + PXE_BASECODE_DEVICE *Private, + PXE_OP_BOOT_PROMPT *BootPromptPtr + ) +{ + EFI_STATUS Status; + EFI_EVENT TimeoutEvent; + EFI_EVENT SecondsEvent; + INT32 SecColumn; + INT32 SecRow; + UINT8 SaveChar; + UINT8 SecsLeft; + + // + // if auto select, just get right to it + // + if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_AUTO_SELECT) { + return AUTO_SELECT; + } + // + // if no timeout, go directly to display of menu + // + if (BootPromptPtr->Timeout == PXE_BOOT_PROMPT_NO_TIMEOUT) { + return DO_MENU; + } + // + // + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (Status)) { + return DO_MENU; + } + + Status = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + BootPromptPtr->Timeout * 10000000 + 100000 + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (TimeoutEvent); + return DO_MENU; + } + // + // + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &SecondsEvent + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (TimeoutEvent); + return DO_MENU; + } + + Status = gBS->SetTimer ( + SecondsEvent, + TimerPeriodic, + 10000000 + ); /* 1 second */ + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (SecondsEvent); + gBS->CloseEvent (TimeoutEvent); + return DO_MENU; + } + // + // display the prompt + // IMPORTANT! This prompt is an ASCII character string that may + // not be terminated with a NULL byte. + // + SaveChar = BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1]; + BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = 0; + + AsciiPrint ("%a ", BootPromptPtr->Prompt); + BootPromptPtr->Prompt[BootPromptPtr->Header.Length - 1] = SaveChar; + + // + // wait until time expires or selection made - menu or local + // + SecColumn = gST->ConOut->Mode->CursorColumn; + SecRow = gST->ConOut->Mode->CursorRow; + SecsLeft = BootPromptPtr->Timeout; + + gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow); + AsciiPrint ("(%d) ", SecsLeft); + + // + // set the default action to be AUTO_SELECT + // + Status = AUTO_SELECT; + + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + EFI_INPUT_KEY Key; + + if (!EFI_ERROR (gBS->CheckEvent (SecondsEvent))) { + --SecsLeft; + gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow); + AsciiPrint ("(%d) ", SecsLeft); + } + + if (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) { + UINT8 Buffer[512]; + UINTN BufferSize; + EFI_STATUS Status; + + BufferSize = sizeof Buffer; + + Status = Private->EfiBc.UdpRead ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT, + NULL, /* dest ip */ + NULL, /* dest port */ + NULL, /* src ip */ + NULL, /* src port */ + NULL, /* hdr size */ + NULL, /* hdr ptr */ + &BufferSize, + Buffer + ); + + continue; + } + + if (Key.ScanCode == 0) { + switch (Key.UnicodeChar) { + case Ctl ('c'): + Status = LOCAL_BOOT; + break; + + case Ctl ('m'): + case 'm': + case 'M': + Status = DO_MENU; + break; + + default: + continue; + } + } else { + switch (Key.ScanCode) { + case SCAN_F8: + Status = DO_MENU; + break; + + case SCAN_ESC: + Status = LOCAL_BOOT; + break; + + default: + continue; + } + } + + break; + } + + gBS->CloseEvent (SecondsEvent); + gBS->CloseEvent (TimeoutEvent); + + gST->ConOut->SetCursorPosition (gST->ConOut, SecColumn, SecRow); + AsciiPrint (" "); + + return Status; +} + + +/** + Display one menu item. + + @param MenuItemPtr Pointer to PXE menu item + option. + + @return None + +**/ +STATIC +VOID +PrintMenuItem ( + PXE_BOOT_MENU_ENTRY *MenuItemPtr + ) +{ + UINT8 Length; + UINT8 SaveChar; + + Length = (UINT8) MIN (70, MenuItemPtr->DataLen); + SaveChar = MenuItemPtr->Data[Length]; + + MenuItemPtr->Data[Length] = 0; + AsciiPrint (" %a\n", MenuItemPtr->Data); + MenuItemPtr->Data[Length] = SaveChar; +} + + +/** + Display and process menu. + + @param Private Pointer to PxeBc interface + @param RxBufferPtr Pointer to receive buffer + + @retval NO_MENU + @retval LOCAL_BOOT + +**/ +STATIC +EFI_STATUS +DoMenu ( + PXE_BASECODE_DEVICE *Private, + DHCP_RECEIVE_BUFFER *RxBufferPtr + ) +{ + PXE_OP_DISCOVERY_CONTROL *DiscoveryControlPtr; + PXE_BOOT_MENU_ENTRY *MenuItemPtrs[MAX_MENULIST]; + EFI_STATUS Status; + UNION_PTR Ptr; + UINTN SaveNumRte; + UINTN TopRow; + UINTN MenuLth; + UINTN NumMenuItems; + UINTN Index; + UINTN Longest; + UINTN Selected; + UINT16 Type; + UINT16 Layer; + BOOLEAN Done; + + Selected = 0; + Layer = 0; + + DEBUG ((DEBUG_WARN, "\nDoMenu() Enter.")); + + /* see if we have a menu/prompt */ + if (!(RxBufferPtr->OpAdds.Status & DISCOVER_TYPE)) { + DEBUG ( + (DEBUG_WARN, + "\nDoMenu() No menu/prompt info. OpAdds.Status == %xh ", + RxBufferPtr->OpAdds.Status) + ); + + return NO_MENU; + } + + DiscoveryControlPtr = (PXE_OP_DISCOVERY_CONTROL *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_DISCOVERY_CONTROL_IX - 1]; + + // + // if not USE_BOOTFILE or no bootfile given, must have menu stuff + // + if ((DiscoveryControlPtr->ControlBits & USE_BOOTFILE) && RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + DEBUG ((DEBUG_WARN, "\nDoMenu() DHCP w/ bootfile. ")); + return NO_MENU; + } + // + // do prompt & menu if necessary + // + Status = DoPrompt (Private, (PXE_OP_BOOT_PROMPT *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_PROMPT_IX - 1]); + + if (Status == LOCAL_BOOT) { + DEBUG ((DEBUG_WARN, "\nDoMenu() DoPrompt() returned LOCAL_BOOT. ")); + + return Status; + } + + Ptr.BytePtr = (UINT8 *) RxBufferPtr->OpAdds.PxeOptAdds[VEND_PXE_BOOT_MENU_IX - 1]; + + MenuLth = Ptr.MenuPtr->Header.Length; + Ptr.CurrentMenuItemPtr = Ptr.MenuPtr->MenuItem; + + // + // build menu items array + // + for (Longest = NumMenuItems = Index = 0; Index < MenuLth && NumMenuItems < MAX_MENULIST;) { + UINTN lth; + + lth = Ptr.CurrentMenuItemPtr->DataLen + sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data); + + MenuItemPtrs[NumMenuItems++] = Ptr.CurrentMenuItemPtr; + + if (lth > Longest) { + // + // check if too long + // + if ((Longest = lth) > 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))) { + Longest = 70 + (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data)); + } + } + + Index += lth; + Ptr.BytePtr += lth; + } + + if (Status != AUTO_SELECT) { + UINT8 BlankBuf[75]; + + SetMem (BlankBuf, sizeof BlankBuf, ' '); + BlankBuf[Longest + 5 - (sizeof (*Ptr.CurrentMenuItemPtr) - sizeof (Ptr.CurrentMenuItemPtr->Data))] = 0; + AsciiPrint ("\n"); + + // + // now put up menu + // + for (Index = 0; Index < NumMenuItems; ++Index) { + PrintMenuItem (MenuItemPtrs[Index]); + } + + TopRow = gST->ConOut->Mode->CursorRow - NumMenuItems; + + // + // now wait for a selection + // + Done = FALSE; + do { + // + // highlight selection + // + EFI_INPUT_KEY Key; + UINTN NewSelected; + + NewSelected = Selected; + + // + // highlight selected row + // + gST->ConOut->SetAttribute ( + gST->ConOut, + EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY) + ); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Selected); + + AsciiPrint (" --->%a\r", BlankBuf); + + PrintMenuItem (MenuItemPtrs[Selected]); + gST->ConOut->SetAttribute ( + gST->ConOut, + EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK) + ); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + NumMenuItems); + + // + // wait for a keystroke + // + while (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key) == EFI_NOT_READY) { + UINT8 TmpBuf[512]; + UINTN TmpBufLen; + + TmpBufLen = sizeof TmpBuf; + + Private->EfiBc.UdpRead ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT | + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT, + NULL, /* dest ip */ + NULL, /* dest port */ + NULL, /* src ip */ + NULL, /* src port */ + NULL, /* hdr size */ + NULL, /* hdr ptr */ + &TmpBufLen, + TmpBuf + ); + } + + if (!Key.ScanCode) { + switch (Key.UnicodeChar) { + case Ctl ('c'): + Key.ScanCode = SCAN_ESC; + break; + + case Ctl ('j'): /* linefeed */ + case Ctl ('m'): /* return */ + Done = TRUE; + break; + + case Ctl ('i'): /* tab */ + case ' ': + case 'd': + case 'D': + Key.ScanCode = SCAN_DOWN; + break; + + case Ctl ('h'): /* backspace */ + case 'u': + case 'U': + Key.ScanCode = SCAN_UP; + break; + + default: + Key.ScanCode = 0; + } + } + + switch (Key.ScanCode) { + case SCAN_LEFT: + case SCAN_UP: + if (NewSelected) { + --NewSelected; + } + + break; + + case SCAN_DOWN: + case SCAN_RIGHT: + if (++NewSelected == NumMenuItems) { + --NewSelected; + } + + break; + + case SCAN_PAGE_UP: + case SCAN_HOME: + NewSelected = 0; + break; + + case SCAN_PAGE_DOWN: + case SCAN_END: + NewSelected = NumMenuItems - 1; + break; + + case SCAN_ESC: + return LOCAL_BOOT; + } + + /* unhighlight last selected row */ + gST->ConOut->SetCursorPosition (gST->ConOut, 5, TopRow + Selected); + + AsciiPrint ("%a\r", BlankBuf); + + PrintMenuItem (MenuItemPtrs[Selected]); + + Selected = NewSelected; + } while (!Done); + } + + SaveNumRte = Private->EfiBc.Mode->RouteTableEntries; + + Type = NTOHS (MenuItemPtrs[Selected]->Type); + + if (Type == 0) { + DEBUG ((DEBUG_WARN, "\nDoMenu() Local boot selected. ")); + return LOCAL_BOOT; + } + + AsciiPrint ("Discover"); + + Status = Private->EfiBc.Discover ( + &Private->EfiBc, + Type, + &Layer, + (BOOLEAN) (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected), + 0 + ); + + if (EFI_ERROR (Status)) { + AsciiPrint ("\r \r"); + + DEBUG ( + (DEBUG_WARN, + "\nDoMenu() Return w/ %xh (%r).", + Status, + Status) + ); + + return Status; + } + + AsciiPrint ("\rBOOT_SERVER_IP: "); + PrintIpv4 ((UINT8 *) &Private->ServerIp); + + for (Index = SaveNumRte; Index < Private->EfiBc.Mode->RouteTableEntries; ++Index) { + if ((Index % 3) == 0) { + AsciiPrint ("\r\nGATEWAY IP:"); + } + + AsciiPrint (" "); + PrintIpv4 ((UINT8 *) &Private->EfiBc.Mode->RouteTable[Index].GwAddr); + AsciiPrint (" "); + } + + AsciiPrint ("\n"); + + DEBUG ((DEBUG_WARN, "\nDoMenu() Return w/ EFI_SUCCESS. ")); + + return EFI_SUCCESS; +} + + +/** + Get value 8- or 16-bit value from DHCP option. + + @param OpPtr Pointer to DHCP option + + @return Value from DHCP option + +**/ +STATIC +UINT16 +GetValue ( + DHCPV4_OP_STRUCT *OpPtr + ) +{ + if (OpPtr->Header.Length == 1) { + return OpPtr->Data[0]; + } else { + return NTOHS (OpPtr->Data); + } +} + + +/** + Locate opcode in buffer. + + @param BufferPtr Pointer to buffer + @param BufferLen Length of buffer + @param OpCode Option number + + @return Pointer to opcode, may be NULL + +**/ +STATIC +UINT8 * +_PxeBcFindOpt ( + UINT8 *BufferPtr, + UINTN BufferLen, + UINT8 OpCode + ) +{ + if (BufferPtr == NULL) { + return NULL; + } + + while (BufferLen != 0) { + if (*BufferPtr == OpCode) { + return BufferPtr; + } + + switch (*BufferPtr) { + case OP_END: + return NULL; + + case OP_PAD: + ++BufferPtr; + --BufferLen; + continue; + } + + if ((UINTN) BufferLen <= (UINTN) 2 + BufferPtr[1]) { + return NULL; + } + + BufferLen -= 2 + BufferPtr[1]; + BufferPtr += 2 + BufferPtr[1]; + } + + return NULL; +} + + +/** + Find option in packet + + @param PacketPtr Pointer to packet + @param OpCode option number + + @return Pointer to option in packet + +**/ +STATIC +UINT8 * +PxeBcFindDhcpOpt ( + EFI_PXE_BASE_CODE_PACKET *PacketPtr, + UINT8 OpCode + ) +{ + UINTN PacketLen; + UINT8 Overload; + UINT8 *OptionBufferPtr; + + // + // + // + PacketLen = 380; + Overload = 0; + + // + // Figure size of DHCP option space. + // + OptionBufferPtr = _PxeBcFindOpt ( + PacketPtr->Dhcpv4.DhcpOptions, + 380, + OP_DHCP_MAX_MESSAGE_SZ + ); + + if (OptionBufferPtr != NULL) { + if (OptionBufferPtr[1] == 2) { + UINT16 n; + + CopyMem (&n, &OptionBufferPtr[2], 2); + PacketLen = HTONS (n); + + if (PacketLen < sizeof (EFI_PXE_BASE_CODE_DHCPV4_PACKET)) { + PacketLen = 380; + } else { + PacketLen -= (PacketPtr->Dhcpv4.DhcpOptions - &PacketPtr->Dhcpv4.BootpOpcode) + 28; + } + } + } + // + // Look for option overloading. + // + OptionBufferPtr = _PxeBcFindOpt ( + PacketPtr->Dhcpv4.DhcpOptions, + PacketLen, + OP_DHCP_OPTION_OVERLOAD + ); + + if (OptionBufferPtr != NULL) { + if (OptionBufferPtr[1] == 1) { + Overload = OptionBufferPtr[2]; + } + } + // + // Look for caller's option. + // + OptionBufferPtr = _PxeBcFindOpt ( + PacketPtr->Dhcpv4.DhcpOptions, + PacketLen, + OpCode + ); + + if (OptionBufferPtr != NULL) { + return OptionBufferPtr; + } + + if (Overload & OVLD_FILE) { + OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpBootFile, 128, OpCode); + + if (OptionBufferPtr != NULL) { + return OptionBufferPtr; + } + } + + if (Overload & OVLD_SRVR_NAME) { + OptionBufferPtr = _PxeBcFindOpt (PacketPtr->Dhcpv4.BootpSrvName, 64, OpCode); + + if (OptionBufferPtr != NULL) { + return OptionBufferPtr; + } + } + + return NULL; +} + + +/** + Download file into buffer + + @param Private Pointer to PxeBc interface + @param BufferSize pointer to size of download + buffer + @param Buffer Pointer to buffer + + @return EFI_BUFFER_TOO_SMALL - + @return EFI_NOT_FOUND - + @return EFI_PROTOCOL_ERROR - + +**/ +STATIC +EFI_STATUS +DownloadFile ( + IN PXE_BASECODE_DEVICE *Private, + IN OUT UINT64 *BufferSize, + IN VOID *Buffer + ) +{ + EFI_PXE_BASE_CODE_MTFTP_INFO MtftpInfo; + EFI_PXE_BASE_CODE_TFTP_OPCODE OpCode; + DHCP_RECEIVE_BUFFER *RxBuf; + EFI_STATUS Status; + UINTN BlockSize; + + RxBuf = (DHCP_RECEIVE_BUFFER *) Private->BootServerReceiveBuffer; + BlockSize = 0x8000; + + DEBUG ((EFI_D_WARN, "\nDownloadFile() Enter.")); + + if (Buffer == NULL || *BufferSize == 0 || *BufferSize < Private->FileSize) { + if (Private->FileSize != 0) { + *BufferSize = Private->FileSize; + return EFI_BUFFER_TOO_SMALL; + } + + AsciiPrint ("\nTSize"); + + OpCode = EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE; + } else if (RxBuf->OpAdds.Status & WfM11a_TYPE) { + OpCode = EFI_PXE_BASE_CODE_MTFTP_READ_FILE; + + ZeroMem (&MtftpInfo, sizeof MtftpInfo); + + *(IPV4_ADDR *) &MtftpInfo.MCastIp = *(IPV4_ADDR *) RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_IP - 1]->Data; + + CopyMem ( + &MtftpInfo.CPort, + RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_CPORT - 1]->Data, + sizeof MtftpInfo.CPort + ); + + CopyMem ( + &MtftpInfo.SPort, + RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_SPORT - 1]->Data, + sizeof MtftpInfo.SPort + ); + + MtftpInfo.ListenTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_TMOUT - 1]); + + MtftpInfo.TransmitTimeout = GetValue (RxBuf->OpAdds.PxeOptAdds[VEND_PXE_MTFTP_DELAY - 1]); + + AsciiPrint ("\nMTFTP"); + } else { + AsciiPrint ("\nTFTP"); + + OpCode = EFI_PXE_BASE_CODE_TFTP_READ_FILE; + } + + Private->FileSize = 0; + + RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data[RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Header.Length] = 0; + + Status = Private->EfiBc.Mtftp ( + &Private->EfiBc, + OpCode, + Buffer, + FALSE, + BufferSize, + &BlockSize, + &Private->ServerIp, + (UINT8 *) RxBuf->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]->Data, + &MtftpInfo, + FALSE + ); + + if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #1 %Xh", Status)); + return Status; + } + + if (sizeof (UINTN) < sizeof (UINT64) && *BufferSize > 0xFFFFFFFF) { + Private->FileSize = 0xFFFFFFFF; + } else { + Private->FileSize = (UINTN) *BufferSize; + } + + if (OpCode == EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE) { + DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #2")); + return EFI_BUFFER_TOO_SMALL; + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "\nDownloadFile() Exit #3 %Xh", Status)); + return Status; + } + + if (Private->EfiBc.Mode->BisSupported && Private->EfiBc.Mode->BisDetected && Private->EfiBc.Mode->PxeBisReplyReceived) { + UINT64 CredentialLen; + UINTN BlockSize; + UINT8 CredentialFilename[256]; + UINT8 *op; + VOID *CredentialBuffer; + + // + // Get name of credential file. It may be in the BOOTP + // bootfile field or a DHCP option. + // + ZeroMem (CredentialFilename, sizeof CredentialFilename); + + op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_DHCP_BOOTFILE); + + if (op != NULL) { + if (op[1] == 0) { + /* No credential filename */ + return EFI_NOT_FOUND; + } + + CopyMem (CredentialFilename, &op[2], op[1]); + } else { + if (Private->EfiBc.Mode->PxeBisReply.Dhcpv4.BootpBootFile[0] == 0) { + /* No credential filename */ + return EFI_NOT_FOUND; + } + + CopyMem (CredentialFilename, &op[2], 128); + } + // + // Get size of credential file. It may be available as a + // DHCP option. If not, use the TFTP get file size. + // + CredentialLen = 0; + + op = PxeBcFindDhcpOpt (&Private->EfiBc.Mode->PxeBisReply, OP_BOOT_FILE_SZ); + + if (op != NULL) { + /* + * This is actually the size of the credential file + * buffer. The actual credential file size will be + * returned when we download the file. + */ + if (op[1] == 2) { + UINT16 n; + + CopyMem (&n, &op[2], 2); + CredentialLen = HTONS (n) * 512; + } + } + + if (CredentialLen == 0) { + BlockSize = 8192; + + Status = Private->EfiBc.Mtftp ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + &CredentialLen, + &BlockSize, + &Private->ServerIp, + CredentialFilename, + NULL, + FALSE + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (CredentialLen == 0) { + // + // %%TBD -- EFI error for invalid credential + // file. + // + return EFI_PROTOCOL_ERROR; + } + } + // + // Allocate credential file buffer. + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + (UINTN) CredentialLen, + &CredentialBuffer + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Download credential file. + // + BlockSize = 8192; + + Status = Private->EfiBc.Mtftp ( + &Private->EfiBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + CredentialBuffer, + FALSE, + &CredentialLen, + &BlockSize, + &Private->ServerIp, + CredentialFilename, + NULL, + FALSE + ); + + if (EFI_ERROR (Status)) { + gBS->FreePool (CredentialBuffer); + return Status; + } + // + // Verify credentials. + // + if (PxebcBisVerify (Private, Buffer, Private->FileSize, CredentialBuffer, (UINTN) CredentialLen)) { + Status = EFI_SUCCESS; + } else { + // + // %%TBD -- An EFI error code for failing credential verification. + // + Status = EFI_PROTOCOL_ERROR; + } + + gBS->FreePool (CredentialBuffer); + } + + return Status; +} + + +/** + Start PXE DHCP. Get DHCP and proxyDHCP information. + Display remote boot menu and prompt. Select item from menu. + + @param Private Pointer to PxeBc interface + @param BufferSize Pointer to download buffer + size + @param Buffer Pointer to download buffer + + @retval EFI_SUCCESS + @retval EFI_NOT_READY + +**/ +STATIC +EFI_STATUS +LoadfileStart ( + IN PXE_BASECODE_DEVICE *Private, + IN OUT UINT64 *BufferSize, + IN VOID *Buffer + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_STATUS Status; + VOID *RxBuf; + + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Enter.")); + + // + // Try to start BaseCode, for now only IPv4 is supported + // so don't try to start using IPv6. + // + Status = Private->EfiBc.Start (&Private->EfiBc, FALSE); + + if (EFI_ERROR (Status)) { + if (Status != EFI_ALREADY_STARTED) { + DEBUG ((DEBUG_NET, "\nLoadfileStart() Exit BC.Start() == %xh", Status)); + return Status; + } + } + // + // Get pointers to PXE mode structure, SNP protocol structure + // and SNP mode structure. + // + PxeBcMode = Private->EfiBc.Mode; + Snp = Private->SimpleNetwork; + SnpMode = Snp->Mode; + + // + // Display client MAC address, like 16-bit PXE ROMs + // + AsciiPrint ("\nCLIENT MAC ADDR: "); + + { + UINTN Index; + UINTN hlen; + + hlen = SnpMode->HwAddressSize; + + for (Index = 0; Index < hlen; ++Index) { + AsciiPrint ("%02x ", SnpMode->CurrentAddress.Addr[Index]); + } + } + + AsciiPrint ("\nDHCP"); + + Status = Private->EfiBc.Dhcp (&Private->EfiBc, TRUE); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit BC.Dhcp() == %Xh", Status)); + AsciiPrint ("\r \r"); + return Status; + } + + ShowMyInfo (Private); + + RxBuf = PxeBcMode->ProxyOfferReceived ? &PXE_OFFER_BUFFER : &DHCPV4_ACK_BUFFER; +#define RxBufferPtr ((DHCP_RECEIVE_BUFFER *) RxBuf) + + Status = DoMenu (Private, RxBufferPtr); + + if (Status == EFI_SUCCESS) { + // + // did a discovery - take info from discovery packet + // + RxBuf = &PXE_ACK_BUFFER; + } else if (Status == NO_MENU) { + // + // did not do a discovery - take info from rxbuf + // + Private->ServerIp.Addr[0] = RxBufferPtr->u.Dhcpv4.siaddr; + + if (!(Private->ServerIp.Addr[0])) { + *(IPV4_ADDR *) &Private->ServerIp = *(IPV4_ADDR *) RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_SERVER_IP_IX - 1]->Data; + } + } else { + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit DoMenu() == %Xh", Status)); + return Status; + } + + if (!RxBufferPtr->OpAdds.PktOptAdds[OP_DHCP_BOOTFILE_IX - 1]) { + DEBUG ((DEBUG_WARN, "\nLoadfileStart() Exit Not ready?")); + return EFI_NOT_READY; + } + // + // check for file size option sent + // + if (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]) { + Private->FileSize = 512 * NTOHS (RxBufferPtr->OpAdds.PktOptAdds[OP_BOOT_FILE_SZ_IX - 1]->Data); + } + + Private->BootServerReceiveBuffer = RxBufferPtr; + + Status = DownloadFile (Private, BufferSize, Buffer); + + DEBUG ( + (DEBUG_WARN, + "\nLoadfileStart() Exit. DownloadFile() = %Xh", + Status) + ); + + return Status; +} + + +/** + Loadfile interface for PxeBc interface + + @param This Pointer to Loadfile interface + @param FilePath Not used and not checked + @param BootPolicy Must be TRUE + @param BufferSize Pointer to buffer size + @param Buffer Pointer to download buffer or + NULL + + @return EFI_INVALID_PARAMETER - + @return EFI_UNSUPPORTED - + @return EFI_SUCCESS - + @return EFI_BUFFER_TOO_SMALL - + +**/ +EFI_STATUS +EFIAPI +LoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ) +{ + LOADFILE_DEVICE *LoadfilePtr; + UINT64 TmpBufSz; + INT32 OrigMode; + INT32 OrigAttribute; + BOOLEAN RemoveCallback; + BOOLEAN NewMakeCallback; + EFI_STATUS Status; + EFI_STATUS TempStatus; + // + // + // + OrigMode = gST->ConOut->Mode->Mode; + OrigAttribute = gST->ConOut->Mode->Attribute; + RemoveCallback = FALSE; + + AsciiPrint ("Running LoadFile()\n"); + + // + // Resolve Warning 4 unreferenced parameter problem + // + FilePath = NULL; + + // + // If either if these parameters are NULL, we cannot continue. + // + if (This == NULL || BufferSize == NULL) { + DEBUG ((DEBUG_WARN, "\nLoadFile() This or BufferSize == NULL")); + return EFI_INVALID_PARAMETER; + } + // + // We only support BootPolicy == TRUE + // + if (!BootPolicy) { + DEBUG ((DEBUG_WARN, "\nLoadFile() BootPolicy == FALSE")); + return EFI_UNSUPPORTED; + } + // + // Get pointer to LoadFile protocol structure. + // + LoadfilePtr = CR (This, LOADFILE_DEVICE, LoadFile, LOADFILE_DEVICE_SIGNATURE); + + if (LoadfilePtr == NULL) { + DEBUG ( + (DEBUG_NET, + "\nLoadFile() Could not get pointer to LoadFile structure") + ); + return EFI_INVALID_PARAMETER; + } + // + // Lock interface + // + EfiAcquireLock (&LoadfilePtr->Lock); + + // + // Set console output mode and display attribute + // + if (OrigMode != 0) { + gST->ConOut->SetMode (gST->ConOut, 0); + } + + gST->ConOut->SetAttribute ( + gST->ConOut, + EFI_TEXT_ATTR (EFI_LIGHTGRAY,EFI_BLACK) + ); + + // + // See if BaseCode already has a Callback protocol attached. + // If there is none, attach our own Callback protocol. + // + Status = gBS->HandleProtocol ( + LoadfilePtr->Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID *) &LoadfilePtr->Private->CallbackProtocolPtr + ); + + if (Status == EFI_SUCCESS) { + // + // There is already a callback routine. Do nothing. + // + DEBUG ((DEBUG_WARN, "\nLoadFile() BC callback exists.")); + + } else if (Status == EFI_UNSUPPORTED) { + // + // No BaseCode Callback protocol found. Add our own. + // + Status = gBS->InstallProtocolInterface ( + &LoadfilePtr->Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + EFI_NATIVE_INTERFACE, + &_bc_callback + ); + + DEBUG ((DEBUG_WARN, "\nLoadFile() Callback install status == %xh", Status)); + + RemoveCallback = (BOOLEAN) (Status == EFI_SUCCESS); + + if (LoadfilePtr->Private->EfiBc.Mode != NULL && LoadfilePtr->Private->EfiBc.Mode->Started) { + NewMakeCallback = TRUE; + LoadfilePtr->Private->EfiBc.SetParameters ( + &LoadfilePtr->Private->EfiBc, + NULL, + NULL, + NULL, + NULL, + &NewMakeCallback + ); + } + + } else { + DEBUG ((DEBUG_WARN, "\nLoadFile() Callback check status == %xh", Status)); + } + // + // Check for starting or for continuing after already getting + // the file size. + // + if (LoadfilePtr->Private->FileSize == 0) { + TmpBufSz = 0; + Status = LoadfileStart (LoadfilePtr->Private, &TmpBufSz, Buffer); + + if (sizeof (UINTN) < sizeof (UINT64) && TmpBufSz > 0xFFFFFFFF) { + *BufferSize = 0xFFFFFFFF; + } else { + *BufferSize = (UINTN) TmpBufSz; + } + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // This is done so loadfile will work even if the boot manager + // did not make the first call with Buffer == NULL. + // + Buffer = NULL; + } + } else if (Buffer == NULL) { + DEBUG ((DEBUG_WARN, "\nLoadfile() Get buffer size")); + + // + // Continuing from previous LoadFile request. Make sure there + // is a buffer and that it is big enough. + // + *BufferSize = LoadfilePtr->Private->FileSize; + Status = EFI_BUFFER_TOO_SMALL; + } else { + DEBUG ((DEBUG_WARN, "\nLoadFile() Download file")); + + // + // Everything looks good, try to download the file. + // + TmpBufSz = *BufferSize; + Status = DownloadFile (LoadfilePtr->Private, &TmpBufSz, Buffer); + + // + // Next call to loadfile will start DHCP process again. + // + LoadfilePtr->Private->FileSize = 0; + } + // + // If we added a callback protocol, now is the time to remove it. + // + if (RemoveCallback) { + NewMakeCallback = FALSE; + TempStatus = LoadfilePtr->Private->EfiBc.SetParameters ( + &LoadfilePtr->Private->EfiBc, + NULL, + NULL, + NULL, + NULL, + &NewMakeCallback + ); + + if (TempStatus == EFI_SUCCESS) { + gBS->UninstallProtocolInterface ( + LoadfilePtr->Private->Handle, + &gEfiPxeBaseCodeCallbackProtocolGuid, + &_bc_callback + ); + } + } + // + // Restore display mode and attribute + // + if (OrigMode != 0) { + gST->ConOut->SetMode (gST->ConOut, OrigMode); + } + + gST->ConOut->SetAttribute (gST->ConOut, OrigAttribute); + + // + // Unlock interface + // + EfiReleaseLock (&LoadfilePtr->Lock); + + DEBUG ((DEBUG_WARN, "\nBC.Loadfile() Status == %xh\n", Status)); + + if (Status == EFI_SUCCESS) { + return EFI_SUCCESS; + + } else if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Error is only displayed when we are actually trying to + // download the boot image. + // + if (Buffer == NULL) { + return EFI_BUFFER_TOO_SMALL; + } + + AsciiPrint ("\nPXE-E05: Download buffer is smaller than requested file.\n"); + + } else if (Status == EFI_DEVICE_ERROR) { + AsciiPrint ("\nPXE-E07: Network device error. Check network connection.\n"); + + } else if (Status == EFI_OUT_OF_RESOURCES) { + AsciiPrint ("\nPXE-E09: Could not allocate I/O buffers.\n"); + + } else if (Status == EFI_NO_MEDIA) { + AsciiPrint ("\nPXE-E12: Could not detect network connection. Check cable.\n"); + + } else if (Status == EFI_NO_RESPONSE) { + AsciiPrint ("\nPXE-E16: Valid PXE offer not received.\n"); + + } else if (Status == EFI_TIMEOUT) { + AsciiPrint ("\nPXE-E18: Timeout. Server did not respond.\n"); + + } else if (Status == EFI_ABORTED) { + AsciiPrint ("\nPXE-E21: Remote boot cancelled.\n"); + + } else if (Status == EFI_ICMP_ERROR) { + AsciiPrint ("\nPXE-E22: Client received ICMP error from server.\n"); + + if (LoadfilePtr->Private->EfiBc.Mode != NULL) { + if (LoadfilePtr->Private->EfiBc.Mode->IcmpErrorReceived) { + + AsciiPrint ( + "PXE-E98: Type: %xh Code: %xh ", + LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type, + LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code + ); + + switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Type) { + case 0x03: + switch (LoadfilePtr->Private->EfiBc.Mode->IcmpError.Code) { + case 0x00: /* net unreachable */ + AsciiPrint ("Net unreachable"); + break; + + case 0x01: /* host unreachable */ + AsciiPrint ("Host unreachable"); + break; + + case 0x02: /* protocol unreachable */ + AsciiPrint ("Protocol unreachable"); + break; + + case 0x03: /* port unreachable */ + AsciiPrint ("Port unreachable"); + break; + + case 0x04: /* Fragmentation needed */ + AsciiPrint ("Fragmentation needed"); + break; + + case 0x05: /* Source route failed */ + AsciiPrint ("Source route failed"); + break; + } + + break; + } + + AsciiPrint ("\n"); + } + } + + } else if (Status == EFI_TFTP_ERROR) { + AsciiPrint ("\nPXE-E23: Client received TFTP error from server.\n"); + + if (LoadfilePtr->Private->EfiBc.Mode != NULL) { + if (LoadfilePtr->Private->EfiBc.Mode->TftpErrorReceived) { + AsciiPrint ( + "PXE-E98: Code: %xh %a\n", + LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorCode, + LoadfilePtr->Private->EfiBc.Mode->TftpError.ErrorString + ); + } + } + + } else { + AsciiPrint ("\nPXE-E99: Unexpected network error: %xh\n", Status); + } + + LoadfilePtr->Private->EfiBc.Stop (&LoadfilePtr->Private->EfiBc); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h b/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h new file mode 100644 index 0000000000..f81d86c163 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/Tftp.h @@ -0,0 +1,154 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + tftp.h + +Abstract: + + +**/ + +#ifndef __TFTP_H__ +#define __TFTP_H__ + +// +// Definitions for trivial file transfer protocol functionality with IP v4 +// Per RFC 1350, July 1992 and RFC 2347, 8, and 9, May 1998 +// +#pragma pack(1) +// +// max and min packet sizes +// (all data packets in transmission except last) +// +#define MAX_TFTP_PKT_SIZE (BUFFER_ALLOCATE_SIZE - 512) +#define MIN_TFTP_PKT_SIZE 512 + +// +// TFTPv4 OpCodes +// +#define TFTP_RRQ 1 // read request +#define TFTP_WRQ 2 // write request +#define TFTP_DATA 3 // data +#define TFTP_ACK 4 // acknowledgement +#define TFTP_ERROR 5 // error packet +#define TFTP_OACK 6 // option acknowledge +#define TFTP_DIR 7 // read directory request +#define TFTP_DATA8 8 +#define TFTP_ACK8 9 + +// +// request packet (read or write) +// Fields shown (except file name) are not to be referenced directly, +// since their placement is variable within a request packet. +// All are null terminated case insensitive ascii strings. +// +struct Tftpv4Req { + UINT16 OpCode; // TFTP Op code + UINT8 FileName[2]; // file name + UINT8 Mode[2]; // "netascii" or "octet" + struct { // optionally, one or more option requests + UINT8 Option[2]; // option name + UINT8 Value[2]; // value requested + } OpReq[1]; +}; + +// +// modes +// +#define MODE_ASCII "netascii" +#define MODE_BINARY "octet" + +// +// option strings +// +#define OP_BLKSIZE "blksize" // block size option +#define OP_TIMEOUT "timeout" // time to wait before retransmitting +#define OP_TFRSIZE "tsize" // total transfer size option +#define OP_OVERWRITE "overwrite" // overwrite file option +#define OP_BIGBLKNUM "bigblk#" // big block number +// See RFC 2347, 8, and 9 for more information on TFTP options +// option acknowledge packet (optional) +// options not acknowledged are rejected +// +struct Tftpv4Oack { + UINT16 OpCode; // TFTP Op code + struct { // optionally, one or more option acknowledgements + UINT8 Option[2]; // option name (of those requested) + UINT8 Value[2]; // value acknowledged + } OpAck[1]; +}; + +// +// acknowledge packet +// +struct Tftpv4Ack { + UINT16 OpCode; // TFTP Op code + UINT16 BlockNum; +}; + +// +// data packet +// +struct Tftpv4Data { + struct Tftpv4Ack Header; + UINT8 Data[512]; +}; + +// +// big block number ack packet +// +struct Tftpv4Ack8 { + UINT16 OpCode; + UINT64 BlockNum; +}; + +// +// big block number data packet +// +struct Tftpv4Data8 { + struct Tftpv4Ack8 Header; + UINT8 Data[506]; +}; + +// +// error packet +// +struct Tftpv4Error { + UINT16 OpCode; // TFTP Op code + UINT16 ErrCode; // error code + UINT8 ErrMsg[1]; // error message (nul terminated) +}; + +#pragma pack() +// +// error codes +// +#define TFTP_ERR_UNDEF 0 // Not defined, see error message (if any). +#define TFTP_ERR_NOT_FOUND 1 // File not found. +#define TFTP_ERR_ACCESS 2 // Access violation. +#define TFTP_ERR_FULL 3 // Disk full or allocation exceeded. +#define TFTP_ERR_ILLEGAL 4 // Illegal TFTP operation. +#define TFTP_ERR_BAD_ID 5 // Unknown transfer ID. +#define TFTP_ERR_EXISTS 6 // File already exists. +#define TFTP_ERR_NO_USER 7 // No such user. +#define TFTP_ERR_OPTION 8 // Option negotiation termination +// +// some defines +// +#define REQ_RESP_TIMEOUT 5 // Wait five seconds for request response. +#define ACK_TIMEOUT 4 // Wait four seconds for ack response. +#define NUM_ACK_RETRIES 3 +#define NUM_MTFTP_OPEN_RETRIES 3 + +#endif /* __TFTP_H__ */ + +/* EOF - tftp.h */ diff --git a/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h b/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h new file mode 100644 index 0000000000..a8fa293a16 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeBcDxe/X64/PxeArch.h @@ -0,0 +1,26 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeArch.h + +Abstract: + Defines PXE Arch type + + +**/ + +#ifndef _EFI_PXE_ARCH_H_ +#define _EFI_PXE_ARCH_H_ + +#define SYS_ARCH 0x7 + +#endif diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c new file mode 100644 index 0000000000..7186a9f159 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/ComponentName.c @@ -0,0 +1,169 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + ComponentName.c + +Abstract: + PxeDhcp4 component name protocol declarations + + +**/ + +#include "PxeDhcp4.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +PxeDhcp4ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +PxeDhcp4ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gPxeDhcp4ComponentName = { + PxeDhcp4ComponentNameGetDriverName, + PxeDhcp4ComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mPxeDhcp4DriverNameTable[] = { + { + "eng", + L"PXE DHCPv4 Driver" + }, + { + NULL, + NULL + } +}; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCESS - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gPxeDhcp4ComponentName.SupportedLanguages, + mPxeDhcp4DriverNameTable, + DriverName + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} + +/* EOF - ComponentName.c */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c new file mode 100644 index 0000000000..fe4da51ea3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.c @@ -0,0 +1,355 @@ +/** @file + +Copyright (c) 2004 - 2005, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4.c + +Abstract: + + +**/ + + +#include "PxeDhcp4.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// Prototypes +// Driver model protocol interface +// +EFI_STATUS +EFIAPI +PxeDhcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +EFI_STATUS +EFIAPI +PxeDhcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +PxeDhcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +PxeDhcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// PXE DHCP Protocol Interface +// +EFI_DRIVER_BINDING_PROTOCOL gPxeDhcp4DriverBinding = { + PxeDhcp4DriverBindingSupported, + PxeDhcp4DriverBindingStart, + PxeDhcp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// PxeDhcp4 Driver Entry point funtion +// + +/** + Register Driver Binding protocol for this driver. + + @param entry EFI_IMAGE_ENTRY_POINT) + + @retval EFI_SUCCESS Driver loaded. + @retval other Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +PxeDhcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gPxeDhcp4DriverBinding, + NULL, + COMPONENT_NAME, + NULL, + NULL + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Test to see if this driver supports ControllerHandle. Any + ControllerHandle that contains a PxeBaseCode protocol can be + supported. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeDhcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + + // + // Open the IO Abstraction(s) needed to perform the supported test. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + (VOID **) &PxeBc, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Close the I/O Abstraction(s) used to perform the supported test. + // + return gBS->CloseProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Start this driver on ControllerHandle by opening a PxeBaseCode + protocol and installing a PxeDhcp4 protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Not used, always produce all possible children. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeDhcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + PXE_DHCP4_PRIVATE_DATA *Private; + + // + // Connect to the PxeBaseCode interface on ControllerHandle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + (VOID **) &PxeBc, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // BaseCode has already grabbed the SimpleNetwork interface + // so just do a HandleProtocol() to get it. + // + Status = gBS->HandleProtocol ( + ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp + ); + + if (EFI_ERROR (Status)) { + goto error_exit; + } + + ASSERT (Snp); + + // + // Initialize the PXE DHCP device instance. + // + Private = AllocateZeroPool (sizeof (PXE_DHCP4_PRIVATE_DATA)); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto error_exit; + } + + Private->Signature = PXE_DHCP4_PRIVATE_DATA_SIGNATURE; + Private->PxeBc = PxeBc; + Private->Snp = Snp; + Private->Handle = ControllerHandle; + Private->PxeDhcp4.Revision = EFI_PXE_DHCP4_PROTOCOL_REVISION; + Private->PxeDhcp4.Run = PxeDhcp4Run; + Private->PxeDhcp4.Setup = PxeDhcp4Setup; + Private->PxeDhcp4.Init = PxeDhcp4Init; + Private->PxeDhcp4.Select = PxeDhcp4Select; + Private->PxeDhcp4.Renew = PxeDhcp4Renew; + Private->PxeDhcp4.Rebind = PxeDhcp4Rebind; + Private->PxeDhcp4.Release = PxeDhcp4Release; + Private->PxeDhcp4.Data = NULL; + + // + // Install protocol interfaces for the PXE DHCP device. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiPxeDhcp4ProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->PxeDhcp4 + ); + + if (!EFI_ERROR (Status)) { + return Status; + } + +error_exit: ; + gBS->CloseProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return Status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Stop this driver on ControllerHandle by removing PXE DHCP + protocol and closing the PXE Base Code protocol on + ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on. + @param NumberOfChildren Not used. + @param ChildHandleBuffer Not used. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeDhcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_PXE_DHCP4_PROTOCOL *PxeDhcp4; + PXE_DHCP4_PRIVATE_DATA *Private; + + // + // Get our context back. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPxeDhcp4ProtocolGuid, + (VOID **) &PxeDhcp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (PxeDhcp4); + + // + // Release allocated resources + // + if (Private->PxeDhcp4.Data) { + FreePool (Private->PxeDhcp4.Data); + Private->PxeDhcp4.Data = NULL; + } + // + // Uninstall our protocol + // + Status = gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiPxeDhcp4ProtocolGuid, + &Private->PxeDhcp4 + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Close any consumed protocols + // + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Release our private data + // + FreePool (Private); + + return Status; +} + +/* EOF - PxeDhcp4.c */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h new file mode 100644 index 0000000000..b33ed3e5bc --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4.h @@ -0,0 +1,353 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4.h + +Abstract: + Common header for PxeDhcp4 protocol driver + + +**/ +#ifndef _PXEDHCP4_H +#define _PXEDHCP4_H + + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// PxeDhcp4 protocol instance data +// +typedef struct { + // + // Signature field used to locate beginning of containment record. + // + UINTN Signature; + +#define PXE_DHCP4_PRIVATE_DATA_SIGNATURE EFI_SIGNATURE_32 ('p', 'x', 'D', '4') + // + // Device handle the protocol is bound to. + // + EFI_HANDLE Handle; + + // + // Public PxeDhcp4 protocol interface. + // + EFI_PXE_DHCP4_PROTOCOL PxeDhcp4; + + // + // Consumed PxeBc, Snp and PxeDhcp4Callback protocol interfaces. + // + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_PXE_DHCP4_CALLBACK_PROTOCOL *callback; + + // + // PxeDhcp4 called function for PxeDhcp4Callback. + // + EFI_PXE_DHCP4_FUNCTION function; + + // + // Timeout event and flag for PxeDhcp4Callback. + // + EFI_EVENT TimeoutEvent; + BOOLEAN TimeoutOccurred; + + // + // Periodic event and flag for PxeDhcp4Callback. + // + EFI_EVENT PeriodicEvent; + BOOLEAN PeriodicOccurred; + + // + // DHCP server IP address. + // + UINT32 ServerIp; + + // + // DHCP renewal and rebinding times, in seconds. + // + UINT32 RenewTime; + UINT32 RebindTime; + UINT32 LeaseTime; + + // + // Number of offers received & allocated offer list. + // + UINTN offers; + DHCP4_PACKET *offer_list; + + // + // + // + BOOLEAN StopPxeBc; + +} PXE_DHCP4_PRIVATE_DATA; + +#define PXE_DHCP4_PRIVATE_DATA_FROM_THIS(a) CR (a, PXE_DHCP4_PRIVATE_DATA, PxeDhcp4, PXE_DHCP4_PRIVATE_DATA_SIGNATURE) + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// Protocol function prototypes. +// +extern +EFI_STATUS +EFIAPI +PxeDhcp4Run ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN OPTIONAL UINTN OpLen, + IN OPTIONAL VOID *OpList + ) +; + +extern +EFI_STATUS +EFIAPI +PxeDhcp4Setup ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN EFI_PXE_DHCP4_DATA *Data + ) +; + +extern +EFI_STATUS +EFIAPI +PxeDhcp4Init ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN seconds_timeout, + OUT UINTN *offer_list_entries, + OUT DHCP4_PACKET **offer_list + ) +; + +extern +EFI_STATUS +EFIAPI +PxeDhcp4Select ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN seconds_timeout, + IN DHCP4_PACKET *offer_list + ) +; + +extern +EFI_STATUS +EFIAPI +PxeDhcp4Renew ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + UINTN seconds_timeout + ) +; + +extern +EFI_STATUS +EFIAPI +PxeDhcp4Rebind ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + UINTN seconds_timeout + ) +; + +extern +EFI_STATUS +EFIAPI +PxeDhcp4Release ( + IN EFI_PXE_DHCP4_PROTOCOL *This + ) +; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// Support function prototypes. +// +extern +UINT16 +htons ( + UINTN n + ) +; + +extern +UINT32 +htonl ( + UINTN n + ) +; + +extern +VOID +EFIAPI +timeout_notify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +; + +extern +VOID +EFIAPI +periodic_notify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +; + +extern +EFI_STATUS +find_opt ( + IN DHCP4_PACKET *Packet, + IN UINT8 OpCode, + IN UINTN Skip, + OUT DHCP4_OP **OpPtr + ) +; + +extern +EFI_STATUS +add_opt ( + IN DHCP4_PACKET *Packet, + IN DHCP4_OP *OpPtr + ) +; + +extern +EFI_STATUS +start_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN OPTIONAL EFI_IP_ADDRESS *station_ip, + IN OPTIONAL EFI_IP_ADDRESS *subnet_mask + ) +; + +extern +VOID +stop_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private + ) +; + +extern +EFI_STATUS +start_receive_events ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN UINTN seconds_timeout + ) +; + +extern +VOID +stop_receive_events ( + IN PXE_DHCP4_PRIVATE_DATA *Private + ) +; + +extern +EFI_STATUS +tx_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN EFI_IP_ADDRESS *dest_ip, + IN OPTIONAL EFI_IP_ADDRESS *gateway_ip, + IN EFI_IP_ADDRESS *src_ip, + IN VOID *buffer, + IN UINTN BufferSize + ) +; + +extern +EFI_STATUS +rx_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + OUT VOID *buffer, + OUT UINTN *BufferSize, + IN OUT EFI_IP_ADDRESS *dest_ip, + IN OUT EFI_IP_ADDRESS *src_ip, + IN UINT16 op_flags + ) +; + +extern +EFI_STATUS +tx_rx_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN OUT EFI_IP_ADDRESS *ServerIp, + IN OPTIONAL EFI_IP_ADDRESS *gateway_ip, + IN OPTIONAL EFI_IP_ADDRESS *client_ip, + IN OPTIONAL EFI_IP_ADDRESS *subnet_mask, + IN DHCP4_PACKET *tx_pkt, + OUT DHCP4_PACKET *rx_pkt, + IN INTN + ( + *rx_vfy) + ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN DHCP4_PACKET *tx_pkt, + IN DHCP4_PACKET *rx_pkt, + IN UINTN rx_pkt_size + ), + IN UINTN seconds_timeout + ) +; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// +// Global variable definitions. +// +extern EFI_COMPONENT_NAME_PROTOCOL gPxeDhcp4ComponentName; + +EFI_STATUS +EFIAPI +PxeDhcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + Register Driver Binding protocol for this driver. + +Arguments: + (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT) + +Returns: + EFI_SUCCESS - Driver loaded. + other - Driver not loaded. + +--*/ +; + +#ifdef EFI_SIZE_REDUCTION_APPLIED + #define COMPONENT_NAME_CODE(code) + #define COMPONENT_NAME NULL +#else + #define COMPONENT_NAME_CODE(code) code + #define COMPONENT_NAME &gPxeDhcp4ComponentName +#endif + +#endif /* _PXEDHCP4_H */ + +/* EOF - PxeDhcp4.h */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf new file mode 100644 index 0000000000..70daa05cc5 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.inf @@ -0,0 +1,64 @@ +#/** @file +# Component name for module PxeDhcp4 +# +# Copyright (c) 2007, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PxeDhcp4Dxe + FILE_GUID = a46c3330-be36-4977-9d24-a7cf92eef0fe + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = PxeDhcp4DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + support.c + PxeDhcp4Release.c + PxeDhcp4Setup.c + ComponentName.c + PxeDhcp4RenewRebind.c + PxeDhcp4.h + PxeDhcp4.c + PxeDhcp4InitSelect.c + PxeDhcp4Run.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + + +[Protocols] + gEfiPxeBaseCodeProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPxeDhcp4CallbackProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPxeDhcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa new file mode 100644 index 0000000000..5db7cf42c3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Dxe.msa @@ -0,0 +1,78 @@ + + + PxeDhcp4 + DXE_DRIVER + a46c3330-be36-4977-9d24-a7cf92eef0fe + 1.0 + Component name for module PxeDhcp4 + FIX ME! + Copyright (c) 2007, Intel Corporation. All rights reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + PxeDhcp4 + + + + DebugLib + + + BaseMemoryLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + + PxeDhcp4Run.c + PxeDhcp4InitSelect.c + PxeDhcp4Entry.c + PxeDhcp4.c + PxeDhcp4.h + PxeDhcp4RenewRebind.c + ComponentName.c + PxeDhcp4Setup.c + PxeDhcp4Release.c + support.c + + + + + + + + gEfiPxeDhcp4ProtocolGuid + + + gEfiPxeDhcp4CallbackProtocolGuid + + + gEfiSimpleNetworkProtocolGuid + + + gEfiPxeBaseCodeProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + PxeDhcp4DriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c new file mode 100644 index 0000000000..b7849d0a87 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4InitSelect.c @@ -0,0 +1,784 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4InitSelect.c + +Abstract: + + +**/ + + +#include "PxeDhcp4.h" + +#define DebugPrint(x) +// +// #define DebugPrint(x) Aprint x +// + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + +**/ +STATIC +INTN +offer_verify ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN DHCP4_PACKET *tx_pkt, + IN DHCP4_PACKET *rx_pkt, + IN UINTN rx_pkt_size + ) +{ + EFI_STATUS EfiStatus; + DHCP4_PACKET *tmp; + DHCP4_OP *msg_type_op; + DHCP4_OP *srvid_op; + UINT32 magik; + + // + // Verify parameters. Touch unused parameters to keep + // compiler happy. + // + ASSERT (Private); + ASSERT (rx_pkt); + + if (Private == NULL || rx_pkt == NULL) { + return -2; + } + + tx_pkt = tx_pkt; + rx_pkt_size = rx_pkt_size; + + // + // This may be a BOOTP Reply or DHCP Offer packet. + // If there is no DHCP magik number, assume that + // this is a BOOTP Reply packet. + // + magik = htonl (DHCP4_MAGIK_NUMBER); + + while (!CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) { + // + // If there is no DHCP message type option, assume + // this is a BOOTP reply packet and cache it. + // + EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op); + + if (EFI_ERROR (EfiStatus)) { + break; + } + // + // If there is a DHCP message type option, it must be a + // DHCP offer packet + // + if (msg_type_op->len != 1) { + return -1; + } + + if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) { + return -1; + } + // + // There must be a server identifier option. + // + EfiStatus = find_opt ( + rx_pkt, + DHCP4_SERVER_IDENTIFIER, + 0, + &srvid_op + ); + + if (EFI_ERROR (EfiStatus)) { + return -1; + } + + if (srvid_op->len != 4) { + return -1; + } + // + // Good DHCP offer packet. + // + break; + } + // + // Good DHCP (or BOOTP) packet. Cache it! + // + EfiStatus = gBS->AllocatePool ( + EfiBootServicesData, + (Private->offers + 1) * sizeof (DHCP4_PACKET), + (VOID **) &tmp + ); + + if (EFI_ERROR (EfiStatus)) { + return -2; + } + + ASSERT (tmp); + + if (Private->offers != 0) { + CopyMem ( + tmp, + Private->offer_list, + Private->offers * sizeof (DHCP4_PACKET) + ); + + gBS->FreePool (Private->offer_list); + } + + CopyMem (&tmp[Private->offers++], rx_pkt, sizeof (DHCP4_PACKET)); + + Private->offer_list = tmp; + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + +**/ +STATIC +INTN +acknak_verify ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN DHCP4_PACKET *tx_pkt, + IN DHCP4_PACKET *rx_pkt, + IN UINTN rx_pkt_size + ) +{ + EFI_STATUS EfiStatus; + DHCP4_OP *msg_type_op; + DHCP4_OP *srvid_op; + DHCP4_OP *renew_op; + DHCP4_OP *rebind_op; + DHCP4_OP *lease_time_op; + UINT32 magik; + + // + // Verify parameters. Touch unused parameters to + // keep compiler happy. + // + ASSERT (Private); + ASSERT (rx_pkt); + + if (Private == NULL || rx_pkt == NULL) { + return -2; + } + + tx_pkt = tx_pkt; + rx_pkt_size = rx_pkt_size; + + // + // This must be a DHCP Ack message. + // + magik = htonl (DHCP4_MAGIK_NUMBER); + + if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) { + return -1; + } + + EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op); + + if (EFI_ERROR (EfiStatus)) { + return -1; + } + + if (msg_type_op->len != 1) { + return -1; + } + + if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) { + return -1; + } + // + // There must be a server identifier. + // + EfiStatus = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op); + + if (EFI_ERROR (EfiStatus)) { + return -1; + } + + if (srvid_op->len != 4) { + return -1; + } + // + // There should be a renewal time. + // If there is not, we will default to the 7/8 of the rebinding time. + // + EfiStatus = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op); + + if (EFI_ERROR (EfiStatus)) { + renew_op = NULL; + } else if (renew_op->len != 4) { + renew_op = NULL; + } + // + // There should be a rebinding time. + // If there is not, we will default to 7/8 of the lease time. + // + EfiStatus = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op); + + if (EFI_ERROR (EfiStatus)) { + rebind_op = NULL; + } else if (rebind_op->len != 4) { + rebind_op = NULL; + } + // + // There should be a lease time. + // If there is not, we will default to one week. + // + EfiStatus = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op); + + if (EFI_ERROR (EfiStatus)) { + lease_time_op = NULL; + } else if (lease_time_op->len != 4) { + lease_time_op = NULL; + } + // + // Packet looks good. Double check the renew, rebind and lease times. + // + CopyMem (&Private->ServerIp, srvid_op->data, 4); + + if (renew_op != NULL) { + CopyMem (&Private->RenewTime, renew_op->data, 4); + Private->RenewTime = htonl (Private->RenewTime); + } else { + Private->RenewTime = 0; + } + + if (rebind_op != NULL) { + CopyMem (&Private->RebindTime, rebind_op->data, 4); + Private->RebindTime = htonl (Private->RebindTime); + } else { + Private->RebindTime = 0; + } + + if (lease_time_op != NULL) { + CopyMem (&Private->LeaseTime, lease_time_op->data, 4); + Private->LeaseTime = htonl (Private->LeaseTime); + } else { + Private->LeaseTime = 0; + } + + if (Private->LeaseTime < 60) { + Private->LeaseTime = 7 * 86400; + } + + if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) { + Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8; + } + + if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) { + Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8; + } + + return 1; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4Init ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN seconds_timeout, + OUT UINTN *Offers, + OUT DHCP4_PACKET **OfferList + ) +{ + PXE_DHCP4_PRIVATE_DATA *Private; + DHCP4_PACKET offer; + EFI_IP_ADDRESS bcast_ip; + EFI_STATUS EfiStatus; + + // + // Verify parameters and protocol state. + // + if (This == NULL || + seconds_timeout < DHCP4_MIN_SECONDS || + seconds_timeout > DHCP4_MAX_SECONDS || + Offers == NULL || + OfferList == NULL + ) { + // + // Return parameters are not initialized when + // parameters are invalid! + // + return EFI_INVALID_PARAMETER; + } + + *Offers = 0; + *OfferList = NULL; + + // + // Check protocol state. + // + if (This->Data == NULL) { + return EFI_NOT_STARTED; + } + + if (!This->Data->SetupCompleted) { + return EFI_NOT_READY; + } + +#if 0 + if (!is_good_discover (&This->Data->Discover)) { + // + // %%TBD - check discover packet fields + // + } +#endif + // + // Get pointer to our instance data. + // + Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This); + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->PxeBc == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Setup variables... + // + Private->offers = 0; + Private->offer_list = NULL; + + EfiStatus = gBS->HandleProtocol ( + Private->Handle, + &gEfiPxeDhcp4CallbackProtocolGuid, + (VOID *) &Private->callback + ); + + if (EFI_ERROR (EfiStatus)) { + Private->callback = NULL; + } + + Private->function = EFI_PXE_DHCP4_FUNCTION_INIT; + + // + // Increment the transaction ID. + // + { + UINT32 xid; + + CopyMem (&xid, &This->Data->Discover.dhcp4.xid, sizeof (UINT32)); + + xid = htonl (htonl (xid) + 1); + + CopyMem (&This->Data->Discover.dhcp4.xid, &xid, sizeof (UINT32)); + } + // + // Transmit discover and wait for offers... + // + SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF); + + EfiStatus = tx_rx_udp ( + Private, + &bcast_ip, + NULL, + NULL, + NULL, + &This->Data->Discover, + &offer, + &offer_verify, + seconds_timeout + ); + + if (EFI_ERROR (EfiStatus)) { + if (Private->offer_list) { + gBS->FreePool (Private->offer_list); + } + + Private->offers = 0; + Private->offer_list = NULL; + Private->callback = NULL; + + DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus)); + return EfiStatus; + } + + *Offers = Private->offers; + *OfferList = Private->offer_list; + + Private->offers = 0; + Private->offer_list = NULL; + Private->callback = NULL; + + This->Data->InitCompleted = TRUE; + This->Data->SelectCompleted = FALSE; + This->Data->IsBootp = FALSE; + This->Data->IsAck = FALSE; + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4Select ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN seconds_timeout, + IN DHCP4_PACKET *Offer + ) +{ + PXE_DHCP4_PRIVATE_DATA *Private; + EFI_STATUS EfiStatus; + DHCP4_PACKET request; + DHCP4_PACKET acknak; + EFI_IP_ADDRESS bcast_ip; + EFI_IP_ADDRESS zero_ip; + EFI_IP_ADDRESS local_ip; + DHCP4_OP *srvid; + DHCP4_OP *op; + UINT32 dhcp4_magik; + UINT8 buf[16]; + BOOLEAN is_bootp; + + // + // Verify parameters. + // + if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS || Offer == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Check protocol state. + // + if (This->Data == NULL) { + return EFI_NOT_STARTED; + } + + if (!This->Data->SetupCompleted) { + return EFI_NOT_READY; + } + // + // Get pointer to instance data. + // + Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This); + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->PxeBc == NULL) { + return EFI_DEVICE_ERROR; + } + +#if 0 + if (!is_good_discover (&This->Data->Discover)) { + // + // %%TBD - check discover packet fields + // + } +#endif + // + // Setup useful variables... + // + SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF); + + ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS)); + + ZeroMem (&local_ip, sizeof (EFI_IP_ADDRESS)); + local_ip.v4.Addr[0] = 127; + local_ip.v4.Addr[3] = 1; + + This->Data->SelectCompleted = FALSE; + This->Data->IsBootp = FALSE; + This->Data->IsAck = FALSE; + + EfiStatus = gBS->HandleProtocol ( + Private->Handle, + &gEfiPxeDhcp4CallbackProtocolGuid, + (VOID *) &Private->callback + ); + + if (EFI_ERROR (EfiStatus)) { + Private->callback = NULL; + } + + Private->function = EFI_PXE_DHCP4_FUNCTION_SELECT; + + // + // Verify offer packet fields. + // + if (Offer->dhcp4.op != BOOTP_REPLY) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (Offer->dhcp4.htype != This->Data->Discover.dhcp4.htype) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (Offer->dhcp4.hlen != This->Data->Discover.dhcp4.hlen) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (CompareMem (&Offer->dhcp4.xid, &This->Data->Discover.dhcp4.xid, 4)) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!CompareMem (&Offer->dhcp4.yiaddr, &bcast_ip, 4)) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!CompareMem (&Offer->dhcp4.yiaddr, &zero_ip, 4)) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!CompareMem (&Offer->dhcp4.yiaddr, &local_ip, 4)) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (CompareMem ( + &Offer->dhcp4.chaddr, + &This->Data->Discover.dhcp4.chaddr, + 16 + )) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + // + // DHCP option checks + // + dhcp4_magik = htonl (DHCP4_MAGIK_NUMBER); + is_bootp = TRUE; + + if (!CompareMem (&Offer->dhcp4.magik, &dhcp4_magik, 4)) { + // + // If present, DHCP message type must be offer. + // + EfiStatus = find_opt (Offer, DHCP4_MESSAGE_TYPE, 0, &op); + + if (!EFI_ERROR (EfiStatus)) { + if (op->len != 1 || op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + is_bootp = FALSE; + } + // + // If present, DHCP max message size must be valid. + // + EfiStatus = find_opt (Offer, DHCP4_MAX_MESSAGE_SIZE, 0, &op); + + if (!EFI_ERROR (EfiStatus)) { + if (op->len != 2 || ((op->data[0] << 8) | op->data[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + } + // + // If present, DHCP server identifier must be valid. + // + EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &op); + + if (!EFI_ERROR (EfiStatus)) { + if (op->len != 4 || !CompareMem (op->data, &bcast_ip, 4) || !CompareMem (op->data, &zero_ip, 4)) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + } + // + // If present, DHCP subnet mask must be valid. + // + EfiStatus = find_opt ( + Offer, + DHCP4_SUBNET_MASK, + 0, + &op + ); + + if (!EFI_ERROR (EfiStatus)) { + if (op->len != 4) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + } + } + // + // Early out for BOOTP. + // + This->Data->IsBootp = is_bootp; + if (is_bootp) { + // + // Copy offer packet to instance data. + // + CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET)); + + // + // Copy discover to request and offer to acknak. + // + CopyMem ( + &This->Data->Request, + &This->Data->Discover, + sizeof (DHCP4_PACKET) + ); + + CopyMem ( + &This->Data->AckNak, + &This->Data->Offer, + sizeof (DHCP4_PACKET) + ); + + // + // Set state flags. + // + This->Data->SelectCompleted = TRUE; + This->Data->IsAck = TRUE; + + Private->callback = NULL; + return EFI_SUCCESS; + } + // + // Copy discover packet contents to request packet. + // + CopyMem (&request, &This->Data->Discover, sizeof (DHCP4_PACKET)); + + This->Data->IsAck = FALSE; + + // + // Change DHCP message type from discover to request. + // + EfiStatus = find_opt (&request, DHCP4_MESSAGE_TYPE, 0, &op); + + if (EFI_ERROR (EfiStatus) && EfiStatus != EFI_NOT_FOUND) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (EfiStatus == EFI_NOT_FOUND) { + EfiStatus = find_opt (&request, DHCP4_END, 0, &op); + + if (EFI_ERROR (EfiStatus)) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + op->op = DHCP4_MESSAGE_TYPE; + op->len = 1; + + op->data[1] = DHCP4_END; + } + + op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST; + + // + // Copy server identifier option from offer to request. + // + EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &srvid); + + if (EFI_ERROR (EfiStatus)) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + if (srvid->len != 4) { + Private->callback = NULL; + return EFI_INVALID_PARAMETER; + } + + EfiStatus = add_opt (&request, srvid); + + if (EFI_ERROR (EfiStatus)) { + DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus)); + Private->callback = NULL; + return EfiStatus; + } + // + // Add requested IP address option to request packet. + // + op = (DHCP4_OP *) buf; + op->op = DHCP4_REQUESTED_IP_ADDRESS; + op->len = 4; + CopyMem (op->data, &Offer->dhcp4.yiaddr, 4); + + EfiStatus = add_opt (&request, op); + + if (EFI_ERROR (EfiStatus)) { + DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus)); + Private->callback = NULL; + return EfiStatus; + } + // + // Transimit DHCP request and wait for DHCP ack... + // + SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF); + + EfiStatus = tx_rx_udp ( + Private, + &bcast_ip, + NULL, + NULL, + NULL, + &request, + &acknak, + &acknak_verify, + seconds_timeout + ); + + if (EFI_ERROR (EfiStatus)) { + DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus)); + Private->callback = NULL; + return EfiStatus; + } + // + // Set Data->IsAck and return. + // + EfiStatus = find_opt (&acknak, DHCP4_MESSAGE_TYPE, 0, &op); + + if (EFI_ERROR (EfiStatus)) { + Private->callback = NULL; + return EFI_DEVICE_ERROR; + } + + if (op->len != 1) { + Private->callback = NULL; + return EFI_DEVICE_ERROR; + } + + switch (op->data[0]) { + case DHCP4_MESSAGE_TYPE_ACK: + This->Data->IsAck = TRUE; + break; + + case DHCP4_MESSAGE_TYPE_NAK: + This->Data->IsAck = FALSE; + break; + + default: + Private->callback = NULL; + return EFI_DEVICE_ERROR; + } + // + // Copy packets into instance data... + // + CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET)); + CopyMem (&This->Data->Request, &request, sizeof (DHCP4_PACKET)); + CopyMem (&This->Data->AckNak, &acknak, sizeof (DHCP4_PACKET)); + + This->Data->SelectCompleted = TRUE; + + Private->callback = NULL; + return EFI_SUCCESS; +} + +/* eof - PxeDhcp4InitSelect.c */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c new file mode 100644 index 0000000000..119acffceb --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Release.c @@ -0,0 +1,247 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4Release.c + +Abstract: + Transmit release packet, free allocations and shutdown PxeDhcp4. + + +**/ + + +#include "PxeDhcp4.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4Release ( + IN EFI_PXE_DHCP4_PROTOCOL *This + ) +{ + PXE_DHCP4_PRIVATE_DATA *Private; + EFI_IP_ADDRESS ServerIp; + EFI_IP_ADDRESS client_ip; + EFI_IP_ADDRESS gateway_ip; + EFI_IP_ADDRESS subnet_mask; + EFI_STATUS efi_status; + DHCP4_OP *op; + UINT8 op_list[20]; + + // + // Check for invalid parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Release does nothing if the protocol has never been setup. + // + if (This->Data == NULL) { + return EFI_NOT_STARTED; + } + // + // Fail if we do not have valid instance data. + // + Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This); + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->PxeBc == NULL) { + return EFI_DEVICE_ERROR; + } + // + // If this is a BOOTP session and there is not a DHCP Ack + // packet, just release storage and return. + // + if (This->Data->IsBootp || !This->Data->IsAck) { + gBS->FreePool (This->Data); + This->Data = NULL; + + if (Private->StopPxeBc) { + Private->PxeBc->Stop (Private->PxeBc); + } + + return EFI_SUCCESS; + } + // + // Build option list for DHCP Release packet. + // If any errors occur, just release storage and return. + // + // + // Message type is first. + // + op_list[0] = DHCP4_MESSAGE_TYPE; + op_list[1] = 1; + op_list[2] = DHCP4_MESSAGE_TYPE_RELEASE; + + // + // Followed by server identifier. + // + efi_status = find_opt ( + &This->Data->Request, + DHCP4_SERVER_IDENTIFIER, + 0, + &op + ); + + if (EFI_ERROR (efi_status)) { + gBS->FreePool (This->Data); + This->Data = NULL; + + if (Private->StopPxeBc) { + Private->PxeBc->Stop (Private->PxeBc); + } + + return EFI_SUCCESS; + } + + if (op->len != 4) { + gBS->FreePool (This->Data); + This->Data = NULL; + + if (Private->StopPxeBc) { + Private->PxeBc->Stop (Private->PxeBc); + } + + return EFI_SUCCESS; + } + + CopyMem (&ServerIp, op->data, 4); + + op_list[3] = DHCP4_SERVER_IDENTIFIER; + op_list[4] = 4; + CopyMem (&op_list[5], &ServerIp, 4); + + // + // Followed by end. + // + op_list[9] = DHCP4_END; + + // + // We need a subnet mask for IP stack operation. + // + efi_status = find_opt ( + &This->Data->AckNak, + DHCP4_SUBNET_MASK, + 0, + &op + ); + + if (EFI_ERROR (efi_status)) { + gBS->FreePool (This->Data); + This->Data = NULL; + + if (Private->StopPxeBc) { + Private->PxeBc->Stop (Private->PxeBc); + } + + return EFI_SUCCESS; + } + + if (op->len != 4) { + gBS->FreePool (This->Data); + This->Data = NULL; + + if (Private->StopPxeBc) { + Private->PxeBc->Stop (Private->PxeBc); + } + + return EFI_SUCCESS; + } + + ZeroMem (&subnet_mask, sizeof (EFI_IP_ADDRESS)); + CopyMem (&subnet_mask, op->data, 4); + + // + // Gateway IP address may be needed. + // + ZeroMem (&gateway_ip, sizeof (EFI_IP_ADDRESS)); + CopyMem (&gateway_ip, &This->Data->AckNak.dhcp4.giaddr, 4); + + // + // Client IP address needed for IP stack operation. + // + ZeroMem (&client_ip, sizeof (EFI_IP_ADDRESS)); + CopyMem (&client_ip, &This->Data->AckNak.dhcp4.yiaddr, 4); + + // + // Enable UDP... + // + efi_status = start_udp (Private, &client_ip, &subnet_mask); + + if (EFI_ERROR (efi_status)) { + gBS->FreePool (This->Data); + This->Data = NULL; + + if (Private->StopPxeBc) { + Private->PxeBc->Stop (Private->PxeBc); + } + + return efi_status; + } + // + // Gather information out of DHCP request packet needed for + // DHCP release packet. + // + // + // Setup DHCP Release packet. + // + CopyMem (&This->Data->Request.dhcp4.ciaddr, &client_ip, 4); + + ZeroMem (&This->Data->Request.dhcp4.yiaddr, 12); + + ZeroMem (&This->Data->Request.dhcp4.sname, 64 + 128); + + This->Data->Request.dhcp4.hops = 0; + This->Data->Request.dhcp4.secs = 0; + This->Data->Request.dhcp4.flags = 0; + + ZeroMem ( + &This->Data->Request.dhcp4.options, + sizeof This->Data->Request.dhcp4.options + ); + + CopyMem (&This->Data->Request.dhcp4.options, op_list, 10); + + // + // Transmit DHCP Release packet. + // + tx_udp ( + Private, + &ServerIp, + &gateway_ip, + &client_ip, + &This->Data->Request, + DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE) + ); + + gBS->Stall (1000000); /* 1/10th second */ + + // + // Shutdown PXE BaseCode and release local storage. + // + stop_udp (Private); + + gBS->FreePool (This->Data); + This->Data = NULL; + + if (Private->StopPxeBc) { + Private->PxeBc->Stop (Private->PxeBc); + } + + return EFI_SUCCESS; +} + +/* eof - PxeDhcp4Release.c */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c new file mode 100644 index 0000000000..193bb1880a --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4RenewRebind.c @@ -0,0 +1,408 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4RenewRebind.c + +Abstract: + + +**/ + + +#include "PxeDhcp4.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + Parameters: + + @return -2 = ignore, stop waiting + @return -1 = ignore, keep waiting + @return 0 = accept, keep waiting + @return 1 = accept, stop waiting + +**/ +STATIC +INTN +acknak_verify ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN DHCP4_PACKET *tx_pkt, + IN DHCP4_PACKET *rx_pkt, + IN UINTN rx_pkt_size + ) +{ + EFI_STATUS efi_status; + DHCP4_OP *msg_type_op; + DHCP4_OP *srvid_op; + DHCP4_OP *renew_op; + DHCP4_OP *rebind_op; + DHCP4_OP *lease_time_op; + UINT32 magik; + + // + // Verify parameters. Unused parameters are also touched + // to make the compiler happy. + // + ASSERT (Private); + ASSERT (rx_pkt); + + if (Private == NULL || rx_pkt == NULL) { + return -2; + } + + tx_pkt = tx_pkt; + rx_pkt_size = rx_pkt_size; + + // + // This must be a DHCP Ack message. + // + magik = htonl (DHCP4_MAGIK_NUMBER); + + if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) { + return -1; + } + + efi_status = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op); + + if (EFI_ERROR (efi_status)) { + return -1; + } + + if (msg_type_op->len != 1) { + return -1; + } + + if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) { + return -1; + } + // + // There must be a server identifier. + // + efi_status = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op); + + if (EFI_ERROR (efi_status)) { + return -1; + } + + if (srvid_op->len != 4) { + return -1; + } + // + // There should be a renewal time. + // If there is not, we will default to the 7/8 of the rebinding time. + // + efi_status = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op); + + if (EFI_ERROR (efi_status)) { + renew_op = NULL; + } else if (renew_op->len != 4) { + renew_op = NULL; + } + // + // There should be a rebinding time. + // If there is not, we will default to 7/8 of the lease time. + // + efi_status = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op); + + if (EFI_ERROR (efi_status)) { + rebind_op = NULL; + } else if (rebind_op->len != 4) { + rebind_op = NULL; + } + // + // There should be a lease time. + // If there is not, we will default to one week. + // + efi_status = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op); + + if (EFI_ERROR (efi_status)) { + lease_time_op = NULL; + } else if (lease_time_op->len != 4) { + lease_time_op = NULL; + } + // + // Packet looks good. Double check the renew, rebind and lease times. + // + CopyMem (&Private->ServerIp, srvid_op->data, 4); + + if (renew_op != NULL) { + CopyMem (&Private->RenewTime, renew_op->data, 4); + Private->RenewTime = htonl (Private->RenewTime); + } else { + Private->RenewTime = 0; + } + + if (rebind_op != NULL) { + CopyMem (&Private->RebindTime, rebind_op->data, 4); + Private->RebindTime = htonl (Private->RebindTime); + } else { + Private->RebindTime = 0; + } + + if (lease_time_op != NULL) { + CopyMem (&Private->LeaseTime, lease_time_op->data, 4); + Private->LeaseTime = htonl (Private->LeaseTime); + } else { + Private->LeaseTime = 0; + } + + if (Private->LeaseTime < 60) { + Private->LeaseTime = 7 * 86400; + } + + if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) { + Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8; + } + + if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) { + Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8; + } + + return 1; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +STATIC +EFI_STATUS +EFIAPI +renew_rebind ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN seconds_timeout, + IN BOOLEAN renew + ) +{ + PXE_DHCP4_PRIVATE_DATA *Private; + EFI_IP_ADDRESS ServerIp; + EFI_IP_ADDRESS client_ip; + EFI_IP_ADDRESS subnet_mask; + EFI_IP_ADDRESS gateway_ip; + DHCP4_PACKET Request; + DHCP4_PACKET AckNak; + DHCP4_OP *op; + EFI_STATUS efi_status; + + // + // Check for invalid parameters. + // + if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS) { + return EFI_INVALID_PARAMETER; + } + // + // Check for proper protocol state. + // + if (This->Data == NULL) { + return EFI_NOT_STARTED; + } + + if (!This->Data->SelectCompleted) { + return EFI_NOT_READY; + } + + if (This->Data->IsBootp) { + return EFI_SUCCESS; + } + + if (!This->Data->IsAck) { + return EFI_INVALID_PARAMETER; + } + // + // Get pointer to instance data. + // + Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This); + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->PxeBc == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Copy Discover packet to temporary request packet + // to be used for Renew/Rebind operation. + // + CopyMem (&Request, &This->Data->Discover, sizeof (DHCP4_PACKET)); + + CopyMem (&Request.dhcp4.ciaddr, &This->Data->AckNak.dhcp4.yiaddr, 4); + + Request.dhcp4.flags = 0; /* Reply does not need to be broadcast. */ + + // + // Change message type from discover to request. + // + efi_status = find_opt (&Request, DHCP4_MESSAGE_TYPE, 0, &op); + + if (EFI_ERROR (efi_status)) { + return EFI_INVALID_PARAMETER; + } + + if (op->len != 1) { + return EFI_INVALID_PARAMETER; + } + + op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST; + + // + // Need a subnet mask. + // + efi_status = find_opt ( + &This->Data->AckNak, + DHCP4_SUBNET_MASK, + 0, + &op + ); + + if (EFI_ERROR (efi_status)) { + return EFI_INVALID_PARAMETER; + } + + if (op->len != 4) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&subnet_mask, sizeof (EFI_IP_ADDRESS)); + CopyMem (&subnet_mask, op->data, 4); + + // + // Need a server IP address (renew) or a broadcast + // IP address (rebind). + // + ZeroMem (&gateway_ip, sizeof (EFI_IP_ADDRESS)); + + if (renew) { + efi_status = find_opt ( + &This->Data->AckNak, + DHCP4_SERVER_IDENTIFIER, + 0, + &op + ); + + if (EFI_ERROR (efi_status)) { + return EFI_INVALID_PARAMETER; + } + + if (op->len != 4) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&ServerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&ServerIp, op->data, 4); + + // + // + // + if (CompareMem (&This->Data->AckNak.dhcp4.giaddr, &gateway_ip, 4)) { + CopyMem (&gateway_ip, &This->Data->AckNak.dhcp4.giaddr, 4); + } + } else { + SetMem (&ServerIp, sizeof (EFI_IP_ADDRESS), 0xFF); + } + // + // Need a client IP address. + // + ZeroMem (&client_ip, sizeof (EFI_IP_ADDRESS)); + CopyMem (&client_ip, &Request.dhcp4.ciaddr, 4); + + // + // + // + efi_status = gBS->HandleProtocol ( + Private->Handle, + &gEfiPxeDhcp4CallbackProtocolGuid, + (VOID *) &Private->callback + ); + + if (EFI_ERROR (efi_status)) { + Private->callback = NULL; + } + + Private->function = renew ? EFI_PXE_DHCP4_FUNCTION_RENEW : EFI_PXE_DHCP4_FUNCTION_REBIND; + + // + // Transimit DHCP request and wait for DHCP ack... + // + efi_status = tx_rx_udp ( + Private, + &ServerIp, + &gateway_ip, + &client_ip, + &subnet_mask, + &Request, + &AckNak, + &acknak_verify, + seconds_timeout + ); + + if (EFI_ERROR (efi_status)) { + Private->callback = NULL; + return efi_status; + } + // + // Copy server identifier, renewal time and rebinding time + // from temporary ack/nak packet into cached ack/nak packet. + // + efi_status = find_opt ( + &This->Data->AckNak, + DHCP4_SERVER_IDENTIFIER, + 0, + &op + ); + + if (!EFI_ERROR (efi_status)) { + if (op->len == 4) { + CopyMem (op->data, &Private->ServerIp, 4); + } + } + + efi_status = find_opt (&This->Data->AckNak, DHCP4_RENEWAL_TIME, 0, &op); + + if (!EFI_ERROR (efi_status)) { + if (op->len == 4) { + CopyMem (op->data, &Private->RenewTime, 4); + } + } + + efi_status = find_opt (&This->Data->AckNak, DHCP4_REBINDING_TIME, 0, &op); + + if (!EFI_ERROR (efi_status)) { + if (op->len == 4) { + CopyMem (op->data, &Private->RebindTime, 4); + } + } + + Private->callback = NULL; + return efi_status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4Renew ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN seconds_timeout + ) +{ + return renew_rebind (This, seconds_timeout, TRUE); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4Rebind ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN UINTN seconds_timeout + ) +{ + return renew_rebind (This, seconds_timeout, FALSE); +} + +/* eof - PxeDhcp4RenewRebind.c */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c new file mode 100644 index 0000000000..e796d12676 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Run.c @@ -0,0 +1,191 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4Run.c + +Abstract: + Simplified entry point for starting basic PxeDhcp4 client operation. + + +**/ + + +#include "PxeDhcp4.h" + +EFI_STATUS +EFIAPI +PxeDhcp4Run ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN OPTIONAL UINTN OpLen, + IN OPTIONAL VOID *OpList + ) +{ + PXE_DHCP4_PRIVATE_DATA *Private; + DHCP4_PACKET *offer_list; + EFI_STATUS efi_status; + EFI_IP_ADDRESS zero_ip; + UINTN offers; + UINTN timeout; + UINTN n; + UINT16 seconds; + + // + // Validate parameters. + // + if (This == NULL || (OpLen != 0 && OpList == NULL) || (OpLen == 0 && OpList != NULL)) { + return EFI_INVALID_PARAMETER; + } + + for (n = 0; n < OpLen;) { + switch (((UINT8 *) OpList)[n]) { + case DHCP4_PAD: + ++n; + continue; + + case DHCP4_END: + ++n; + break; + + default: + n += 2 + ((UINT8 *) OpList)[n + 1]; + continue; + } + + break; + } + + if (n != OpLen) { + return EFI_INVALID_PARAMETER; + } + // + // Get pointer to instance data. + // + Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This); + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->PxeBc == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Initialize DHCP discover packet. + // + efi_status = PxeDhcp4Setup (This, NULL); + + if (EFI_ERROR (efi_status)) { + return efi_status; + } + + for (n = 0; n < OpLen;) { + switch (((UINT8 *) OpList)[n]) { + case DHCP4_PAD: + ++n; + continue; + + case DHCP4_END: + ++n; + break; + + default: + efi_status = add_opt ( + &This->Data->Discover, + (DHCP4_OP *) &(((UINT8 *) OpList)[n]) + ); + + if (EFI_ERROR (efi_status)) { + return efi_status; + } + + n += 2 + ((UINT8 *) OpList)[n + 1]; + continue; + } + + break; + } + // + // Basic DHCP D.O.R.A. + // 1, 2, 4, 8, 16 & 32 second timeouts. + // Callback routine can be used to break out earlier. + // + ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS)); + + for (timeout = 1;;) { + // + // Broadcast DHCP discover and wait for DHCP offers. + // + efi_status = PxeDhcp4Init (This, timeout, &offers, &offer_list); + + if ((efi_status != EFI_SUCCESS) && + (efi_status != EFI_TIMEOUT) && + (efi_status != EFI_NO_RESPONSE)) { + return efi_status; + } + // + // Try to select from each DHCP or BOOTP offer. + // + for (n = 0; n < offers; ++n) { + // + // Ignore proxyDHCP offers. + // + if (!CompareMem (&offer_list[n].dhcp4.yiaddr, &zero_ip, 4)) { + continue; + } + // + // Issue DHCP Request and wait for DHCP Ack/Nak. + // + efi_status = PxeDhcp4Select ( + This, + timeout, + &offer_list[n] + ); + + if (EFI_ERROR (efi_status)) { + continue; + } + // + // Exit when we have got our DHCP Ack. + // + if (This->Data->IsAck) { + return EFI_SUCCESS; + } + } + // + // No DHCP Acks. Release DHCP Offer list storage. + // + if (offer_list != NULL) { + gBS->FreePool (offer_list); + offer_list = NULL; + } + // + // Try again until we have used up >= DHCP4_MAX_SECONDS. + // + if ((timeout <<= 1) > DHCP4_MAX_SECONDS) { + if (!EFI_ERROR (efi_status)) { + efi_status = EFI_TIMEOUT; + } + + return efi_status; + } + // + // Next timeout value. + // + CopyMem (&seconds, &This->Data->Discover.dhcp4.secs, 2); + + seconds = htons (htons (seconds) + timeout); + + CopyMem (&This->Data->Discover.dhcp4.secs, &seconds, 2); + } +} + +/* eof - PxeDhcp4Run.c */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c new file mode 100644 index 0000000000..29e4ed05b5 --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/PxeDhcp4Setup.c @@ -0,0 +1,258 @@ +/** @file + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + PxeDhcp4Setup.c + +Abstract: + + +**/ + +#include "PxeDhcp4.h" + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +EFIAPI +PxeDhcp4Setup ( + IN EFI_PXE_DHCP4_PROTOCOL *This, + IN EFI_PXE_DHCP4_DATA *Data + ) +{ + PXE_DHCP4_PRIVATE_DATA *Private; + DHCP4_HEADER *Packet; + EFI_STATUS EfiStatus; + UINT8 *OpLen; + UINT8 *OpPtr; + + // + // Return error if parameters are invalid. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This); + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (This->Data != NULL) { + return EFI_ALREADY_STARTED; + } + + if (Private->PxeBc == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Check contents of provided Data structure. + // + if (Data != NULL) { + // + // Do protocol state checks first. + // + if (Data->SelectCompleted) { + if (!Data->InitCompleted || !Data->SetupCompleted) { + return EFI_INVALID_PARAMETER; + } + + if (Data->IsBootp && !Data->IsAck) { + return EFI_INVALID_PARAMETER; + } + } else if (Data->InitCompleted) { + if (!Data->SetupCompleted || Data->IsBootp || Data->IsAck) { + return EFI_INVALID_PARAMETER; + } + } else if (Data->SetupCompleted) { + if (Data->IsBootp || Data->IsAck) { + return EFI_INVALID_PARAMETER; + } + } + // + // Do packet content checks. + // + if (Data->SetupCompleted) { + // + // %%TBD - check discover packet + // + } + + if (Data->SelectCompleted) { + if (Data->IsBootp) { + // + // %%TBD - check offer packet + // + if (CompareMem ( + &Data->Discover, + &Data->Request, + sizeof (DHCP4_PACKET) + )) { + return EFI_INVALID_PARAMETER; + } + + if (CompareMem ( + &Data->Offer, + &Data->AckNak, + sizeof (DHCP4_PACKET) + )) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // %%TBD - check offer, request & acknak packets + // + } + } + } + // + // Allocate data structure. Return error + // if there is not enough available memory. + // + This->Data = AllocatePool (sizeof (EFI_PXE_DHCP4_DATA)); + if (This->Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Start PxeBc because we want to use its UdpWrite, UdpRead and + // SetFilter calls. + // + EfiStatus = Private->PxeBc->Start (Private->PxeBc, FALSE); + + if (EFI_ERROR (EfiStatus)) { + if (EfiStatus != EFI_ALREADY_STARTED) { + FreePool (This->Data); + This->Data = NULL; + Private->PxeBc->Stop (Private->PxeBc); + return EfiStatus; + } + + Private->StopPxeBc = FALSE; + } else { + Private->StopPxeBc = TRUE; + } + // + // Use new data. + // + if (Data != NULL) { + CopyMem (This->Data, Data, sizeof (EFI_PXE_DHCP4_DATA)); + return EFI_SUCCESS; + } + // + // Initialize new public data structure. + // + ZeroMem (This->Data, sizeof (EFI_PXE_DHCP4_DATA)); + + // + // Fill in default DHCP discover packet. + // Check for MAC addresses of strange lengths, just in case. + // + Packet = &This->Data->Discover.dhcp4; + + Packet->op = BOOTP_REQUEST; + + Packet->htype = Private->Snp->Mode->IfType; + + if (Private->Snp->Mode->HwAddressSize > 16) { + Packet->hlen = 16; + } else { + Packet->hlen = (UINT8) Private->Snp->Mode->HwAddressSize; + } + + Packet->hops = 0; /* Set to zero per RFC 2131. */ + + if (Packet->hlen < sizeof Packet->xid) { + if (Packet->hlen != 0) { + CopyMem ( + &Packet->xid, + &Private->Snp->Mode->CurrentAddress, + Packet->hlen + ); + } + } else { + CopyMem ( + &Packet->xid, + &Private->Snp->Mode->CurrentAddress.Addr[Packet->hlen - sizeof Packet->xid], + sizeof Packet->xid + ); + } + // + // %%TBD - xid should be randomized + // + Packet->secs = htons (DHCP4_INITIAL_SECONDS); + + Packet->flags = htons (DHCP4_BROADCAST_FLAG); + + if (Packet->hlen != 0) { + CopyMem (Packet->chaddr, &Private->Snp->Mode->CurrentAddress, Packet->hlen); + } + + Packet->magik = htonl (DHCP4_MAGIK_NUMBER); + + OpPtr = Packet->options; + + *OpPtr++ = DHCP4_MESSAGE_TYPE; + *OpPtr++ = 1; + *OpPtr++ = DHCP4_MESSAGE_TYPE_DISCOVER; + + *OpPtr++ = DHCP4_MAX_MESSAGE_SIZE; + *OpPtr++ = 2; + *OpPtr++ = (UINT8) ((DHCP4_DEFAULT_MAX_MESSAGE_SIZE >> 8) & 0xFF); + *OpPtr++ = (UINT8) (DHCP4_DEFAULT_MAX_MESSAGE_SIZE & 0xFF); + + *OpPtr++ = DHCP4_PARAMETER_REQUEST_LIST; + OpLen = OpPtr; + *OpPtr++ = 0; + *OpPtr++ = DHCP4_SUBNET_MASK; + *OpPtr++ = DHCP4_TIME_OFFSET; + *OpPtr++ = DHCP4_ROUTER_LIST; + *OpPtr++ = DHCP4_TIME_SERVERS; + *OpPtr++ = DHCP4_NAME_SERVERS; + *OpPtr++ = DHCP4_DNS_SERVERS; + *OpPtr++ = DHCP4_HOST_NAME; + *OpPtr++ = DHCP4_BOOT_FILE_SIZE; + *OpPtr++ = DHCP4_MESSAGE_TYPE; + *OpPtr++ = DHCP4_DOMAIN_NAME; + *OpPtr++ = DHCP4_ROOT_PATH; + *OpPtr++ = DHCP4_EXTENSION_PATH; + *OpPtr++ = DHCP4_MAX_DATAGRAM_SIZE; + *OpPtr++ = DHCP4_DEFAULT_TTL; + *OpPtr++ = DHCP4_BROADCAST_ADDRESS; + *OpPtr++ = DHCP4_NIS_DOMAIN_NAME; + *OpPtr++ = DHCP4_NIS_SERVERS; + *OpPtr++ = DHCP4_NTP_SERVERS; + *OpPtr++ = DHCP4_VENDOR_SPECIFIC; + *OpPtr++ = DHCP4_REQUESTED_IP_ADDRESS; + *OpPtr++ = DHCP4_LEASE_TIME; + *OpPtr++ = DHCP4_SERVER_IDENTIFIER; + *OpPtr++ = DHCP4_RENEWAL_TIME; + *OpPtr++ = DHCP4_REBINDING_TIME; + *OpPtr++ = DHCP4_CLASS_IDENTIFIER; + *OpPtr++ = DHCP4_TFTP_SERVER_NAME; + *OpPtr++ = DHCP4_BOOTFILE; + *OpPtr++ = 128; + *OpPtr++ = 129; + *OpPtr++ = 130; + *OpPtr++ = 131; + *OpPtr++ = 132; + *OpPtr++ = 133; + *OpPtr++ = 134; + *OpPtr++ = 135; + *OpLen = (UINT8) ((OpPtr - OpLen) - 1); + + *OpPtr++ = DHCP4_END; + + This->Data->SetupCompleted = TRUE; + + return EFI_SUCCESS; +} + +/* eof - PxeDhcp4Setup.c */ diff --git a/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c new file mode 100644 index 0000000000..2e750522ca --- /dev/null +++ b/MdeModulePkg/Universal/Network/PxeDhcp4Dxe/Support.c @@ -0,0 +1,1086 @@ +/** @file + +Copyright (c) 2004 - 2005, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + support.c + +Abstract: + Miscellaneous support routines for PxeDhcp4 protocol. + + +**/ + + +#include "PxeDhcp4.h" + +#define DebugPrint(x) +// +// #define DebugPrint(x) Aprint x +// + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +UINT16 +htons ( + UINTN n + ) +{ + return (UINT16) ((n >> 8) | (n << 8)); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +UINT32 +htonl ( + UINTN n + ) +{ + return (UINT32) ((n >> 24) | ((n >> 8) & 0xFF00) | ((n & 0xFF00) << 8) | (n << 24)); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +EFIAPI +timeout_notify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ASSERT (Context); + + if (Context != NULL) { + ((PXE_DHCP4_PRIVATE_DATA *) Context)->TimeoutOccurred = TRUE; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +EFIAPI +periodic_notify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ASSERT (Context); + + if (Context != NULL) { + ((PXE_DHCP4_PRIVATE_DATA *) Context)->PeriodicOccurred = TRUE; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_SUCCESS := Option was found + @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL + @return EFI_INVALID_PARAMETER := OpCode == DHCP4_PAD + @return EFI_INVALID_PARAMETER := OpCode == DHCP4_END && Skip != 0 + @return EFI_INVALID_PARAMETER := DHCP magik number in Packet is not valid + @return EFI_NOT_FOUND := op-code was not found in packet + @return EFI_INVALID_PARAMETER := If present, DHCP_MAX_MESSAGE_SIZE option + @return does not have a valid value. + +**/ +EFI_STATUS +find_opt ( + IN DHCP4_PACKET *Packet, + IN UINT8 OpCode, + IN UINTN Skip, + OUT DHCP4_OP **OpPtr + ) +{ + UINTN msg_size; + UINTN buf_len; + UINTN n; + UINT8 *buf; + UINT8 *end_ptr; + UINT8 overload; + + // + // Verify parameters. + // + if (Packet == NULL || OpPtr == NULL || OpCode == DHCP4_PAD || (OpCode == DHCP4_END && Skip != 0)) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->dhcp4.magik != htonl (DHCP4_MAGIK_NUMBER)) { + return EFI_INVALID_PARAMETER; + } + // + // Initialize search variables. + // + *OpPtr = NULL; + + msg_size = DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE); + + overload = 0; + end_ptr = NULL; + + buf = Packet->dhcp4.options; + buf_len = msg_size - (Packet->dhcp4.options - Packet->raw); + + // + // Start searching for requested option. + // + for (n = 0;;) { + // + // If match is found, decrement skip count and return + // when desired match is found. + // + if (buf[n] == OpCode) { + *OpPtr = (DHCP4_OP *) &buf[n]; + + if (Skip-- == 0) { + return EFI_SUCCESS; + } + } + // + // Skip past current option. Check for option overload + // and message size options since these will affect the + // amount of data to be searched. + // + switch (buf[n]) { + case DHCP4_PAD: + // + // Remember the first pad byte of a group. This + // could be the end of a badly formed packet. + // + if (end_ptr == NULL) { + end_ptr = &buf[n]; + } + + ++n; + break; + + case DHCP4_END: + // + // If we reach the end we are done. + // + end_ptr = NULL; + return EFI_NOT_FOUND; + + case DHCP4_OPTION_OVERLOAD: + // + // Remember the option overload value since it + // could cause the search to continue into + // the fname and sname fields. + // + end_ptr = NULL; + + if (buf[n + 1] == 1) { + overload = buf[n + 2]; + } + + n += 2 + buf[n + 1]; + break; + + case DHCP4_MAX_MESSAGE_SIZE: + // + // Remember the message size value since it could + // change the amount of option buffer to search. + // + end_ptr = NULL; + + if (buf[n + 1] == 2 && buf == Packet->dhcp4.options) { + msg_size = ((buf[n + 2] << 8) | buf[n + 3]) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE); + + if (msg_size < 328) { + return EFI_INVALID_PARAMETER; + } + + buf_len = msg_size - (Packet->dhcp4.options - Packet->raw); + + if (n + 2 + buf[n + 1] > buf_len) { + return EFI_INVALID_PARAMETER; + } + } + + /* fall thru */ + default: + end_ptr = NULL; + + n += 2 + buf[n + 1]; + } + // + // Keep searching until the end of the buffer is reached. + // + if (n < buf_len) { + continue; + } + // + // Reached end of current buffer. Check if we are supposed + // to search the fname and sname buffers. + // + if (buf == Packet->dhcp4.options && + (overload == DHCP4_OVERLOAD_FNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME) + ) { + buf = Packet->dhcp4.fname; + buf_len = 128; + n = 0; + continue; + } + + if (buf != Packet->dhcp4.sname && (overload == DHCP4_OVERLOAD_SNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)) { + buf = Packet->dhcp4.sname; + buf_len = 64; + n = 0; + continue; + } + // + // End of last buffer reached. If this was a search + // for the end of the options, go back to the start + // of the current pad block. + // + if (OpCode == DHCP4_END && end_ptr != NULL) { + *OpPtr = (DHCP4_OP *) end_ptr; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL + @return EFI_INVALID_PARAMETER := OpPtr->op == DHCP4_PAD || OpPtr->op == DHCP4_END + @return EFI_INVALID_PARAMETER := DHCP magik number in DHCP packet is not valid + @return EFI_INVALID_PARAMETER := If DHCP_MAX_MESSAGE_SIZE option is present and + @return is not valid + @return EFI_INVALID_PARAMETER := If DHCP_OPTION_OVERLOAD option is present and + @return is not valid + @return EFI_DEVICE_ERROR := Cannot determine end of packet + @return EFI_BUFFER_TOO_SMALL := Not enough room in packet to add option + @return EFI_SUCCESS := Option added to DHCP packet + +**/ +EFI_STATUS +add_opt ( + IN DHCP4_PACKET *Packet, + IN DHCP4_OP *OpPtr + ) +{ + EFI_STATUS efi_status; + DHCP4_OP *msg_size_op; + DHCP4_OP *overload_op; + DHCP4_OP *op; + UINTN msg_size; + UINTN buf_len; + UINT32 magik; + UINT8 *buf; + + // + // Verify parameters. + // + ASSERT (Packet); + ASSERT (OpPtr); + + if (Packet == NULL || OpPtr == NULL) { + return EFI_INVALID_PARAMETER; + } + + switch (OpPtr->op) { + case DHCP4_PAD: + case DHCP4_END: + // + // No adding PAD or END. + // + return EFI_INVALID_PARAMETER; + } + // + // Check the DHCP magik number. + // + CopyMem (&magik, &Packet->dhcp4.magik, 4); + + if (magik != htonl (DHCP4_MAGIK_NUMBER)) { + return EFI_INVALID_PARAMETER; + } + // + // Find the DHCP message size option. + // + msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE; + + efi_status = find_opt ( + Packet, + DHCP4_MAX_MESSAGE_SIZE, + 0, + &msg_size_op + ); + + if (EFI_ERROR (efi_status)) { + if (efi_status != EFI_NOT_FOUND) { + DebugPrint ( + ("%s:%d:%r\n", + __FILE__, + __LINE__, + efi_status) + ); + return efi_status; + } + + msg_size_op = NULL; + } else { + CopyMem (&msg_size, msg_size_op->data, 2); + msg_size = htons (msg_size); + + if (msg_size < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) { + return EFI_INVALID_PARAMETER; + } + } + // + // Find the DHCP option overload option. + // + efi_status = find_opt ( + Packet, + DHCP4_OPTION_OVERLOAD, + 0, + &overload_op + ); + + if (EFI_ERROR (efi_status)) { + if (efi_status != EFI_NOT_FOUND) { + DebugPrint ( + ("%s:%d:%r\n", + __FILE__, + __LINE__, + efi_status) + ); + return efi_status; + } + + overload_op = NULL; + } else { + if (overload_op->len != 1) { + return EFI_INVALID_PARAMETER; + } + + switch (overload_op->data[0]) { + case 1: + case 2: + case 3: + break; + + default: + return EFI_INVALID_PARAMETER; + } + } + // + // Find the end of the packet. + // + efi_status = find_opt (Packet, DHCP4_END, 0, &op); + + if (EFI_ERROR (efi_status)) { + return EFI_INVALID_PARAMETER; + } + // + // Find which buffer the end is in. + // + if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.options)) { + buf_len = (msg_size - ((UINT8 *) &Packet->dhcp4.options - (UINT8 *) &Packet->raw)) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE); + } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.fname)) { + buf_len = 128; + } else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.sname)) { + buf_len = 64; + } else { + return EFI_DEVICE_ERROR; + } + // + // Add option to current buffer if there is no overlow. + // + if ((UINTN) ((&op->op - buf) + 3 + op->len) < buf_len) { + CopyMem (op, OpPtr, OpPtr->len + 2); + + op->data[op->len] = DHCP4_END; + + return EFI_SUCCESS; + } + // + // Error if there is no space for option. + // + return EFI_BUFFER_TOO_SMALL; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_INVALID_PARAMETER := Private == NULL || Private->PxeBc == NULL + @return EFI_INVALID_PARAMETER := Only one of StationIp and SubnetMask is given + @return EFI_SUCCESS := UDP stack is ready + @return other := Error from PxeBc->SetIpFilter() or PxeBc->SetStationIp() + +**/ +EFI_STATUS +start_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN OPTIONAL EFI_IP_ADDRESS *StationIp, + IN OPTIONAL EFI_IP_ADDRESS *SubnetMask + ) +{ + EFI_PXE_BASE_CODE_IP_FILTER bcast_filter; + EFI_STATUS efi_status; + + // + // + // + ASSERT (Private); + ASSERT (Private->PxeBc); + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->PxeBc == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (StationIp != NULL && SubnetMask == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (StationIp == NULL && SubnetMask != NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Setup broadcast receive filter... + // + ZeroMem (&bcast_filter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + + bcast_filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST; + bcast_filter.IpCnt = 0; + + efi_status = Private->PxeBc->SetIpFilter ( + Private->PxeBc, + &bcast_filter + ); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + return efi_status; + } + // + // Configure station IP address and subnet mask... + // + efi_status = Private->PxeBc->SetStationIp ( + Private->PxeBc, + StationIp, + SubnetMask + ); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + } + + return efi_status; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +stop_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private + ) +{ + // + // + // + ASSERT (Private); + ASSERT (Private->PxeBc); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + +**/ +EFI_STATUS +start_receive_events ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN UINTN SecondsTimeout + ) +{ + EFI_STATUS efi_status; + UINTN random; + + // + // + // + ASSERT (Private); + ASSERT (SecondsTimeout); + + if (Private == NULL || SecondsTimeout == 0) { + return EFI_INVALID_PARAMETER; + } + // + // Need a bettern randomizer... + // For now adjust the timeout value by the least significant + // digit in the MAC address. + // + random = 0; + + if (Private->PxeDhcp4.Data != NULL) { + if (Private->PxeDhcp4.Data->Discover.dhcp4.hlen != 0 && Private->PxeDhcp4.Data->Discover.dhcp4.hlen <= 16) { + random = 0xFFF & Private->PxeDhcp4.Data->Discover.dhcp4.chaddr[Private->PxeDhcp4.Data->Discover.dhcp4.hlen - 1]; + } + } + // + // Setup timeout event and start timer. + // + efi_status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + &timeout_notify, + Private, + &Private->TimeoutEvent + ); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + return efi_status; + } + + efi_status = gBS->SetTimer ( + Private->TimeoutEvent, + TimerRelative, + SecondsTimeout * 10000000 + random + ); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + gBS->CloseEvent (Private->TimeoutEvent); + return efi_status; + } + + Private->TimeoutOccurred = FALSE; + + // + // Setup periodic event for callbacks + // + efi_status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + &periodic_notify, + Private, + &Private->PeriodicEvent + ); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + gBS->CloseEvent (Private->TimeoutEvent); + return efi_status; + } + + efi_status = gBS->SetTimer ( + Private->PeriodicEvent, + TimerPeriodic, + 1000000 + ); /* 1/10th second */ + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + gBS->CloseEvent (Private->TimeoutEvent); + gBS->CloseEvent (Private->PeriodicEvent); + return efi_status; + } + + Private->PeriodicOccurred = FALSE; + + return EFI_SUCCESS; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +VOID +stop_receive_events ( + IN PXE_DHCP4_PRIVATE_DATA *Private + ) +{ + // + // + // + ASSERT (Private); + + if (Private == NULL) { + return ; + } + // + // + // + gBS->CloseEvent (Private->TimeoutEvent); + Private->TimeoutOccurred = FALSE; + + // + // + // + gBS->CloseEvent (Private->PeriodicEvent); + Private->PeriodicOccurred = FALSE; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_INVALID_PARAMETER := Private == NULL || dest_ip == NULL || + @return buffer == NULL || BufferSize < 300 || Private->PxeBc == NULL + @return EFI_SUCCESS := Buffer was transmitted + @return other := Return from PxeBc->UdpWrite() + +**/ +EFI_STATUS +tx_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN EFI_IP_ADDRESS *dest_ip, + IN OPTIONAL EFI_IP_ADDRESS *gateway_ip, + IN EFI_IP_ADDRESS *src_ip, + IN VOID *buffer, + IN UINTN BufferSize + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT dest_port; + EFI_PXE_BASE_CODE_UDP_PORT src_port; + EFI_IP_ADDRESS zero_ip; + + // + // + // + ASSERT (Private); + ASSERT (dest_ip); + ASSERT (buffer); + ASSERT (BufferSize >= 300); + + if (Private == NULL || dest_ip == NULL || buffer == NULL || BufferSize < 300) { + return EFI_INVALID_PARAMETER; + } + + ASSERT (Private->PxeBc); + + if (Private->PxeBc == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Transmit DHCP discover packet... + // + ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS)); + + if (src_ip == NULL) { + src_ip = &zero_ip; + } + + dest_port = DHCP4_SERVER_PORT; + src_port = DHCP4_CLIENT_PORT; + + return Private->PxeBc->UdpWrite ( + Private->PxeBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT, + dest_ip, + &dest_port, + gateway_ip, + src_ip, + &src_port, + NULL, + NULL, + &BufferSize, + buffer + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/** + + @return EFI_INVALID_PARAMETER := + @return EFI_SUCCESS := Packet received + @return other := Return from PxeBc->UdpRead() + +**/ +EFI_STATUS +rx_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + OUT VOID *buffer, + IN OUT UINTN *BufferSize, + IN OUT EFI_IP_ADDRESS *dest_ip, + IN OUT EFI_IP_ADDRESS *src_ip, + IN UINT16 op_flags + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT dest_port; + EFI_PXE_BASE_CODE_UDP_PORT src_port; + + // + // + // + ASSERT (Private); + ASSERT (buffer); + ASSERT (dest_ip); + ASSERT (src_ip); + + if (Private == NULL || buffer == NULL || dest_ip == NULL || src_ip == NULL || BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + ASSERT (Private->PxeBc); + + if (Private->PxeBc == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Check for packet + // + *BufferSize = sizeof (DHCP4_PACKET); + + dest_port = DHCP4_CLIENT_PORT; + src_port = DHCP4_SERVER_PORT; + + return Private->PxeBc->UdpRead ( + Private->PxeBc, + op_flags, + dest_ip, + &dest_port, + src_ip, + &src_port, + NULL, + NULL, + BufferSize, + buffer + ); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +EFI_STATUS +tx_rx_udp ( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN OUT EFI_IP_ADDRESS *ServerIp, + IN OPTIONAL EFI_IP_ADDRESS *gateway_ip, + IN OPTIONAL EFI_IP_ADDRESS *client_ip, + IN OPTIONAL EFI_IP_ADDRESS *SubnetMask, + IN DHCP4_PACKET *tx_pkt, + OUT DHCP4_PACKET *rx_pkt, + IN INTN (*rx_vfy)( + IN PXE_DHCP4_PRIVATE_DATA *Private, + IN DHCP4_PACKET *tx_pkt, + IN DHCP4_PACKET *rx_pkt, + IN UINTN rx_pkt_size + ), + IN UINTN SecondsTimeout + ) +/*++ +Routine description: + Transmit DHCP packet and wait for replies. + +Parameters: + Private := Pointer to PxeDhcp4 private data + ServerIp := Pointer to server IP address + gateway_ip := Pointer to gateway IP address or NULL + client_ip := Pointer to client IP address or NULL + SubnetMask := Pointer to subnet mask or NULL + tx_pkt := Pointer to DHCP packet to transmit + rx_pkt := Pointer to DHCP packet receive buffer + rx_vfy := Pointer to DHCP packet receive verification routine + SecondsTimeout := Number of seconds until timeout + +Returns: + EFI_INVALID_PARAMETER := Private == NULL || ServerIp == NULL || + tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL || Private->PxeBc == NULL + EFI_ABORTED := Receive aborted + EFI_TIMEOUT := No packets received + EFI_SUCCESS := Packet(s) received + other := Returns from other PxeDhcp4 support routines +--*/ +{ + EFI_PXE_DHCP4_CALLBACK_STATUS CallbackStatus; + EFI_IP_ADDRESS dest_ip; + EFI_IP_ADDRESS src_ip; + EFI_STATUS efi_status; + DHCP4_OP *msg_size_op; + UINTN pkt_size; + UINTN n; + UINT16 msg_size; + UINT16 op_flags; + BOOLEAN done_flag; + BOOLEAN got_packet; + + // + // Bad programmer check... + // + ASSERT (Private); + ASSERT (ServerIp); + ASSERT (tx_pkt); + ASSERT (rx_pkt); + ASSERT (rx_vfy); + + if (Private == NULL || ServerIp == NULL || tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL) { + return EFI_INVALID_PARAMETER; + } + + ASSERT (Private->PxeBc); + + if (Private->PxeBc == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Enable UDP... + // + efi_status = start_udp (Private, client_ip, SubnetMask); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + return efi_status; + } + // + // Get length of transmit packet... + // + msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE; + + efi_status = find_opt ( + tx_pkt, + DHCP4_MAX_MESSAGE_SIZE, + 0, + &msg_size_op + ); + + if (!EFI_ERROR (efi_status)) { + CopyMem (&msg_size, msg_size_op->data, 2); + + if ((msg_size = htons (msg_size)) < 328) { + msg_size = 328; + } + } + // + // Transmit packet... + // + efi_status = tx_udp ( + Private, + ServerIp, + gateway_ip, + client_ip, + tx_pkt, + msg_size - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE) + ); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + stop_udp (Private); + return efi_status; + } + // + // Enable periodic and timeout events... + // + efi_status = start_receive_events (Private, SecondsTimeout); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + stop_udp (Private); + return efi_status; + } + // + // Wait for packet(s)... + // +#if 0 + if (!client_ip) { + Aprint ("client_ip == NULL "); + } else { + Aprint ( + "client_ip == %d.%d.%d.%d ", + client_ip->v4.Addr[0], + client_ip->v4.Addr[1], + client_ip->v4.Addr[2], + client_ip->v4.Addr[3] + ); + } + + if (!ServerIp) { + Aprint ("ServerIp == NULL\n"); + } else { + Aprint ( + "ServerIp == %d.%d.%d.%d\n", + ServerIp->v4.Addr[0], + ServerIp->v4.Addr[1], + ServerIp->v4.Addr[2], + ServerIp->v4.Addr[3] + ); + } +#endif + + done_flag = FALSE; + got_packet = FALSE; + + while (!done_flag) { + // + // Check for timeout event... + // + if (Private->TimeoutOccurred) { + efi_status = EFI_SUCCESS; + break; + } + // + // Check for periodic event... + // + if (Private->PeriodicOccurred && Private->callback != NULL) { + CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE; + + if (Private->callback->Callback != NULL) { + CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, 0, NULL); + } + + switch (CallbackStatus) { + case EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE: + break; + + case EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT: + default: + stop_receive_events (Private); + stop_udp (Private); + return EFI_ABORTED; + } + + Private->PeriodicOccurred = FALSE; + } + // + // Check for packet... + // + if (client_ip == NULL) { + SetMem (&dest_ip, sizeof (EFI_IP_ADDRESS), 0xFF); + } else { + CopyMem (&dest_ip, client_ip, sizeof (EFI_IP_ADDRESS)); + } + + SetMem (&src_ip, sizeof (EFI_IP_ADDRESS), 0xFF); + + if (CompareMem (&src_ip, &ServerIp, sizeof (EFI_IP_ADDRESS))) { + ZeroMem (&src_ip, sizeof (EFI_IP_ADDRESS)); + op_flags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP; + } else { + op_flags = 0; + } + + efi_status = rx_udp ( + Private, + rx_pkt, + &pkt_size, + &dest_ip, + &src_ip, + op_flags + ); + + if (efi_status == EFI_TIMEOUT) { + efi_status = EFI_SUCCESS; + continue; + } + + if (EFI_ERROR (efi_status)) { + break; + } + // + // Some basic packet sanity checks.. + // + if (pkt_size < 300) { + continue; + } + + if (rx_pkt->dhcp4.op != BOOTP_REPLY) { + continue; + } + + if (tx_pkt->dhcp4.htype != rx_pkt->dhcp4.htype) { + continue; + } + + if ((n = tx_pkt->dhcp4.hlen) != rx_pkt->dhcp4.hlen) { + continue; + } + + if (CompareMem (&tx_pkt->dhcp4.xid, &rx_pkt->dhcp4.xid, 4)) { + continue; + } + + if (n != 0) { + if (n >= 16) { + n = 16; + } + + if (CompareMem (tx_pkt->dhcp4.chaddr, rx_pkt->dhcp4.chaddr, n)) { + continue; + } + } + // + // Internal callback packet verification... + // + switch ((*rx_vfy) (Private, tx_pkt, rx_pkt, pkt_size)) { + case -2: /* ignore and stop */ + stop_receive_events (Private); + stop_udp (Private); + return EFI_ABORTED; + + case -1: /* ignore and wait */ + continue; + + case 0: /* accept and wait */ + break; + + case 1: /* accept and stop */ + done_flag = TRUE; + break; + + default: + ASSERT (0); + } + // + // External callback packet verification... + // + CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE; + + if (Private->callback != NULL) { + if (Private->callback->Callback != NULL) { + CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, (UINT32) pkt_size, rx_pkt); + } + } + + switch (CallbackStatus) { + case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE: + continue; + + case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT: + done_flag = TRUE; + break; + + case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT: + stop_receive_events (Private); + stop_udp (Private); + return EFI_ABORTED; + + case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE: + default: + break; + } + // + // We did! We did get a packet! + // + got_packet = TRUE; + } + // + // + // + stop_receive_events (Private); + stop_udp (Private); + + if (EFI_ERROR (efi_status)) { + DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status)); + return efi_status; + } + + if (got_packet) { + return EFI_SUCCESS; + } else { + return EFI_TIMEOUT; + } +} + +/* eof - support.c */ diff --git a/MdeModulePkg/Universal/Network/SnpDxe/snp.h b/MdeModulePkg/Universal/Network/SnpDxe/snp.h index 624a44c971..90c9f3d563 100644 --- a/MdeModulePkg/Universal/Network/SnpDxe/snp.h +++ b/MdeModulePkg/Universal/Network/SnpDxe/snp.h @@ -41,20 +41,6 @@ Revision history: #define FOUR_GIGABYTES (UINT64) 0x100000000ULL -// -// Driver Consumed Protocol Prototypes -// -//@MT:#include EFI_PROTOCOL_DEFINITION (DevicePath) -//@MT:#include EFI_PROTOCOL_DEFINITION (PciIo) -//@MT:#include EFI_PROTOCOL_DEFINITION (EfiNetworkInterfaceIdentifier) - -// -// Driver Produced Protocol Prototypes -// -//@MT:#include EFI_PROTOCOL_DEFINITION (DriverBinding) -//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName) -//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName2) -//@MT:#include EFI_PROTOCOL_DEFINITION (SimpleNetwork) #define SNP_DRIVER_SIGNATURE EFI_SIGNATURE_32 ('s', 'n', 'd', 's') #define MAX_MAP_LENGTH 100 diff --git a/MdeModulePkg/Universal/Network/SnpDxe/start.c b/MdeModulePkg/Universal/Network/SnpDxe/start.c index 0a43f8e60c..10c9554a0f 100644 --- a/MdeModulePkg/Universal/Network/SnpDxe/start.c +++ b/MdeModulePkg/Universal/Network/SnpDxe/start.c @@ -33,7 +33,7 @@ pxe_start ( SNP_DRIVER *snp ) { - PXE_CPB_START_30 *cpb; + PXE_CPB_START_30 *cpb; PXE_CPB_START_31 *cpb_31; cpb = snp->cpb; diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c index ea7d732b84..536a01a1d5 100644 --- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c @@ -118,11 +118,11 @@ Tcp4GetMode ( AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr; - EFI_IP4 (AccessPoint->StationAddress) = Tcb->LocalEnd.Ip; + NetCopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); AccessPoint->SubnetMask = Tcb->SubnetMask; AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); - EFI_IP4 (AccessPoint->RemoteAddress) = Tcb->RemoteEnd.Ip; + NetCopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); @@ -428,11 +428,11 @@ Tcp4ConfigurePcb ( Tcb->TTL = CfgData->TimeToLive; Tcb->TOS = CfgData->TypeOfService; - Tcb->LocalEnd.Ip = EFI_IP4 (CfgData->AccessPoint.StationAddress); + NetCopyMem (&Tcb->LocalEnd.Ip, &CfgData->AccessPoint.StationAddress, sizeof (IP4_ADDR)); Tcb->LocalEnd.Port = HTONS (CfgData->AccessPoint.StationPort); Tcb->SubnetMask = CfgData->AccessPoint.SubnetMask; - Tcb->RemoteEnd.Ip = EFI_IP4 (CfgData->AccessPoint.RemoteAddress); + NetCopyMem (&Tcb->RemoteEnd.Ip, &CfgData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort); Option = CfgData->ControlOption; diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c index 002cd61089..d7f9fe73f5 100644 --- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c @@ -143,7 +143,6 @@ Tcp4DestroyTimer ( mTcp4Timer.TimerEvent = NULL; } -//@MT: EFI_DRIVER_ENTRY_POINT (Tcp4DriverEntryPoint) EFI_STATUS EFIAPI diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c index 2f7a49fcb8..898d18f611 100644 --- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c @@ -103,8 +103,8 @@ TcpSendIpPacket ( Override.TimeToLive = 255; Override.DoNotFragment = FALSE; Override.Protocol = EFI_IP_PROTO_TCP; - EFI_IP4 (Override.GatewayAddress) = 0; - EFI_IP4 (Override.SourceAddress) = Src; + NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); + NetCopyMem (&Override.SourceAddress, &Src, sizeof (EFI_IPv4_ADDRESS)); Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override); diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c index 8f5a997957..4d2e80fcad 100644 --- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c @@ -144,6 +144,7 @@ Tcp4Configure ( EFI_TCP4_OPTION *Option; SOCKET *Sock; EFI_STATUS Status; + IP4_ADDR Ip; if (NULL == This) { return EFI_INVALID_PARAMETER; @@ -153,26 +154,25 @@ Tcp4Configure ( // Tcp protocol related parameter check will be conducted here // if (NULL != TcpConfigData) { - if ((EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) != 0) && - !Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.RemoteAddress), 0)) { + + NetCopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); + if ((Ip != 0) && !Ip4IsUnicast (NTOHL (Ip), 0)) { + return EFI_INVALID_PARAMETER; + } + + if (TcpConfigData->AccessPoint.ActiveFlag && + (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) { return EFI_INVALID_PARAMETER; } if (!TcpConfigData->AccessPoint.UseDefaultAddress) { - if (!Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.StationAddress), 0) || - !IP4_IS_VALID_NETMASK (EFI_NTOHL (TcpConfigData->AccessPoint.SubnetMask)) - ) { + + NetCopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR)); + if (!Ip4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (Ip))) { return EFI_INVALID_PARAMETER; } } - if (TcpConfigData->AccessPoint.ActiveFlag && - (0 == TcpConfigData->AccessPoint.RemotePort || - (EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) == 0)) - ) { - return EFI_INVALID_PARAMETER; - } - Option = TcpConfigData->ControlOption; if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h index dba46aa80a..3e6575be92 100644 --- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h @@ -32,7 +32,6 @@ extern UINT16 mTcp4RandomPort; // // Driver Produced Protocol Prototypes // -//@MT:#include EFI_PROTOCOL_PRODUCER (Tcp4) #define TCP4_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Tcp", PrintArg) #define TCP4_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Tcp", PrintArg) diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c index 9cb55a567d..1cc8488111 100644 --- a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c @@ -267,7 +267,7 @@ TcpFindTcbByPeer ( NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); - if ((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip) && + if (EFI_IP4_EQUAL (*Addr, Tcb->LocalEnd.Ip) && (LocalPort == Tcb->LocalEnd.Port)) { return TRUE; @@ -277,7 +277,7 @@ TcpFindTcbByPeer ( NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); - if (((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip)) && + if (EFI_IP4_EQUAL (*Addr, Tcb->LocalEnd.Ip) && (LocalPort == Tcb->LocalEnd.Port)) { return TRUE; @@ -983,9 +983,9 @@ TcpSetVariableData ( // This tcp instance belongs to the Tcp4Service. // Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; - EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip; + NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); - EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip; + NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); Tcp4ServicePoint++; @@ -1005,9 +1005,9 @@ TcpSetVariableData ( // This tcp instance belongs to the Tcp4Service. // Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; - EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip; + NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); - EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip; + NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); Tcp4ServicePoint++; diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c index 7b47ef44a9..ada73d79db 100644 --- a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c @@ -473,7 +473,6 @@ Udp4ServiceBindingDestroyChild ( return EFI_SUCCESS; } -//@MT: EFI_DRIVER_ENTRY_POINT (Udp4DriverEntryPoint) EFI_STATUS EFIAPI diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c index e6fddd8052..e5576820a6 100644 --- a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c @@ -424,7 +424,7 @@ Udp4FindInstanceByPort ( continue; } - if (EFI_IP_EQUAL (ConfigData->StationAddress, *Address) && + if (EFI_IP4_EQUAL (ConfigData->StationAddress, *Address) && (ConfigData->StationPort == Port)) { // // if both the address and the port are the same, return TRUE. @@ -566,8 +566,8 @@ Udp4IsReconfigurable ( } if (!NewConfigData->UseDefaultAddress && - (!EFI_IP_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) || - !EFI_IP_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) { + (!EFI_IP4_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) || + !EFI_IP4_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) { // // If the instance doesn't use the default address, and the new address or // new subnet mask is different from the old values. @@ -576,15 +576,14 @@ Udp4IsReconfigurable ( } } - if (!EFI_IP_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) { + if (!EFI_IP4_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) { // // The remoteaddress is not the same. // return FALSE; } - if ((EFI_IP4 (NewConfigData->RemoteAddress) != 0) && - (NewConfigData->RemotePort != OldConfigData->RemotePort)) { + if (!EFI_IP4_EQUAL (NewConfigData->RemoteAddress, mZeroIp4Addr) && (NewConfigData->RemotePort != OldConfigData->RemotePort)) { // // The RemotePort differs if it's designated in the configdata. // @@ -667,6 +666,7 @@ Udp4ValidateTxToken ( EFI_UDP4_CONFIG_DATA *ConfigData; EFI_UDP4_SESSION_DATA *UdpSessionData; IP4_ADDR SourceAddress; + IP4_ADDR GatewayAddress; if (TxToken->Event == NULL) { return EFI_INVALID_PARAMETER; @@ -700,12 +700,15 @@ Udp4ValidateTxToken ( return EFI_INVALID_PARAMETER; } - if ((TxData->GatewayAddress != NULL) && - !Ip4IsUnicast(EFI_NTOHL (*(TxData->GatewayAddress)), 0)) { - // - // The specified GatewayAddress is not a unicast IPv4 address while it's not 0. - // - return EFI_INVALID_PARAMETER; + if (TxData->GatewayAddress != NULL) { + NetCopyMem (&GatewayAddress, TxData->GatewayAddress, sizeof (IP4_ADDR)); + + if (!Ip4IsUnicast (NTOHL (GatewayAddress), 0)) { + // + // The specified GatewayAddress is not a unicast IPv4 address while it's not 0. + // + return EFI_INVALID_PARAMETER; + } } ConfigData = &Instance->ConfigData; @@ -713,9 +716,9 @@ Udp4ValidateTxToken ( if (UdpSessionData != NULL) { - SourceAddress = EFI_NTOHL (UdpSessionData->SourceAddress); + NetCopyMem (&SourceAddress, &UdpSessionData->SourceAddress, sizeof (IP4_ADDR)); - if ((SourceAddress != 0) && !Ip4IsUnicast (SourceAddress, 0)) { + if ((SourceAddress != 0) && !Ip4IsUnicast (HTONL (SourceAddress), 0)) { // // Check whether SourceAddress is a valid IPv4 address in case it's not zero. // The configured station address is used if SourceAddress is zero. @@ -730,13 +733,13 @@ Udp4ValidateTxToken ( return EFI_INVALID_PARAMETER; } - if (EFI_IP4 (UdpSessionData->DestinationAddress) == 0) { + if (EFI_IP4_EQUAL (UdpSessionData->DestinationAddress, mZeroIp4Addr)) { // // The DestinationAddress specified in the UdpSessionData is 0. // return EFI_INVALID_PARAMETER; } - } else if (EFI_IP4 (ConfigData->RemoteAddress) == 0) { + } else if (EFI_IP4_EQUAL (ConfigData->RemoteAddress, mZeroIp4Addr)) { // // the configured RemoteAddress is all zero, and the user doens't override the // destination address. @@ -959,9 +962,9 @@ Udp4LeaveGroup ( McastIp = Arg; - if ((McastIp != NULL) && ((UINTN) EFI_IP4 (*McastIp) != (UINTN) (Item->Key))) { + if ((McastIp != NULL) && (!EFI_IP4_EQUAL (*McastIp, (UINTN) Item->Key))) { // - // McastIp is not NULL and the multicast address contained in the Item + // McastIp is not NULL and the multicast address contained in the Item // is not the same as McastIp. // return EFI_SUCCESS; @@ -1169,16 +1172,16 @@ Udp4MatchDgram ( return FALSE; } - if ((EFI_IP4 (ConfigData->RemoteAddress) != 0) && - !EFI_IP_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) { + if (!EFI_IP4_EQUAL (ConfigData->RemoteAddress, mZeroIp4Addr) && + !EFI_IP4_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) { // // This datagram doesn't come from the instance's specified sender. // return FALSE; } - if ((EFI_IP4 (ConfigData->StationAddress) == 0) || - EFI_IP_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) { + if (EFI_IP4_EQUAL (ConfigData->StationAddress, mZeroIp4Addr) || + EFI_IP4_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) { // // The instance is configured to receive datagrams destinated to any station IP or // the destination address of this datagram matches the configured station IP. @@ -1186,7 +1189,7 @@ Udp4MatchDgram ( return TRUE; } - Destination = EFI_IP4 (Udp4Session->DestinationAddress); + NetCopyMem (&Destination, &Udp4Session->DestinationAddress, sizeof (IP4_ADDR)); if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) { // @@ -1513,11 +1516,12 @@ Udp4Demultiplex ( gRT->GetTime (&RxData.TimeStamp, NULL); - Udp4Session = &RxData.UdpSession; - EFI_IP4 (Udp4Session->SourceAddress) = NetSession->Source; - EFI_IP4 (Udp4Session->DestinationAddress) = NetSession->Dest; - Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort); - Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort); + Udp4Session = &RxData.UdpSession; + Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort); + Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort); + + NetCopyMem (&Udp4Session->SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS)); + NetCopyMem (&Udp4Session->DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS)); // // Trim the UDP header. @@ -1637,12 +1641,13 @@ Udp4SendPortUnreach ( // // Fill the override data. // - Override.DoNotFragment = FALSE; - Override.TypeOfService = 0; - Override.TimeToLive = 255; - Override.Protocol = EFI_IP_PROTO_ICMP; - EFI_IP4 (Override.SourceAddress) = NetSession->Dest; - EFI_IP4 (Override.GatewayAddress) = 0; + Override.DoNotFragment = FALSE; + Override.TypeOfService = 0; + Override.TimeToLive = 255; + Override.Protocol = EFI_IP_PROTO_ICMP; + + NetCopyMem (&Override.SourceAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS)); + NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); // // Send out this icmp packet. @@ -1682,10 +1687,11 @@ Udp4IcmpHandler ( Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL); - EFI_IP4 (Udp4Session.SourceAddress) = NetSession->Source; - EFI_IP4 (Udp4Session.DestinationAddress) = NetSession->Dest; - Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort); - Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort); + NetCopyMem (&Udp4Session.SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS)); + NetCopyMem (&Udp4Session.DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS)); + + Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort); + Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort); NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { // @@ -1696,7 +1702,7 @@ Udp4IcmpHandler ( if (!Instance->Configured || Instance->ConfigData.AcceptPromiscuous || Instance->ConfigData.AcceptAnyPort || - (EFI_IP4 (Instance->ConfigData.StationAddress) == 0)) { + EFI_IP4_EQUAL (Instance->ConfigData.StationAddress, mZeroIp4Addr)) { // // Don't try to deliver the ICMP error to this instance if it is not configured, // or it's configured to be promiscuous or accept any port or accept all the diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c index 5343bd5c4b..b3daacba23 100644 --- a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c @@ -153,6 +153,8 @@ Udp4Configure ( IP4_ADDR SubnetMask; IP4_ADDR RemoteAddress; EFI_IP4_CONFIG_DATA Ip4ConfigData; + IP4_ADDR LocalAddr; + IP4_ADDR RemoteAddr; if (This == NULL) { return EFI_INVALID_PARAMETER; @@ -171,9 +173,14 @@ Udp4Configure ( if (UdpConfigData != NULL) { - StationAddress = EFI_NTOHL (UdpConfigData->StationAddress); - SubnetMask = EFI_NTOHL (UdpConfigData->SubnetMask); - RemoteAddress = EFI_NTOHL (UdpConfigData->RemoteAddress); + NetCopyMem (&StationAddress, &UdpConfigData->StationAddress, sizeof (IP4_ADDR)); + NetCopyMem (&SubnetMask, &UdpConfigData->SubnetMask, sizeof (IP4_ADDR)); + NetCopyMem (&RemoteAddress, &UdpConfigData->RemoteAddress, sizeof (IP4_ADDR)); + + StationAddress = NTOHL (StationAddress); + SubnetMask = NTOHL (SubnetMask); + RemoteAddress = NTOHL (RemoteAddress); + if (!UdpConfigData->UseDefaultAddress && (!IP4_IS_VALID_NETMASK (SubnetMask) || @@ -251,9 +258,11 @@ Udp4Configure ( // // Pre calculate the checksum for the pseudo head, ignore the UDP length first. // + NetCopyMem (&LocalAddr, &Instance->ConfigData.StationAddress, sizeof (IP4_ADDR)); + NetCopyMem (&RemoteAddr, &Instance->ConfigData.RemoteAddress, sizeof (IP4_ADDR)); Instance->HeadSum = NetPseudoHeadChecksum ( - EFI_IP4 (Instance->ConfigData.StationAddress), - EFI_IP4 (Instance->ConfigData.RemoteAddress), + LocalAddr, + RemoteAddr, EFI_IP_PROTO_UDP, 0 ); @@ -332,13 +341,21 @@ Udp4Groups ( UDP4_INSTANCE_DATA *Instance; EFI_IP4_PROTOCOL *Ip; EFI_TPL OldTpl; + IP4_ADDR McastIp; - if ((This == NULL) || - (JoinFlag && (MulticastAddress == NULL)) || - (JoinFlag && !IP4_IS_MULTICAST (EFI_NTOHL (*MulticastAddress)))) { + if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) { return EFI_INVALID_PARAMETER; } + McastIp = 0; + if (JoinFlag) { + NetCopyMem (&McastIp, MulticastAddress, sizeof (IP4_ADDR)); + + if (IP4_IS_MULTICAST (NTOHL (McastIp))) { + return EFI_INVALID_PARAMETER; + } + } + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); if (Instance->IsNoMapping) { @@ -370,11 +387,7 @@ Udp4Groups ( // if (JoinFlag) { - NetMapInsertTail ( - &Instance->McastIps, - (VOID *) (UINTN) EFI_IP4 (*MulticastAddress), - NULL - ); + NetMapInsertTail (&Instance->McastIps, (VOID *) (UINTN) McastIp, NULL); } else { NetMapIterate (&Instance->McastIps, Udp4LeaveGroup, MulticastAddress); @@ -506,6 +519,7 @@ Udp4Transmit ( NET_BUF *Packet; EFI_UDP4_HEADER *Udp4Header; EFI_UDP4_CONFIG_DATA *ConfigData; + IP4_ADDR Source; IP4_ADDR Destination; EFI_UDP4_TRANSMIT_DATA *TxData; EFI_UDP4_SESSION_DATA *UdpSessionData; @@ -590,25 +604,26 @@ Udp4Transmit ( // Set the SourceAddress, SrcPort and Destination according to the specified // UdpSessionData. // - if (EFI_IP4 (UdpSessionData->SourceAddress) != 0) { - Override.SourceAddress = UdpSessionData->SourceAddress; + if (!EFI_IP4_EQUAL (UdpSessionData->SourceAddress, mZeroIp4Addr)) { + NetCopyMem (&Override.SourceAddress, &UdpSessionData->SourceAddress, sizeof (EFI_IPv4_ADDRESS)); } if (UdpSessionData->SourcePort != 0) { Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort); } - Destination = EFI_IP4 (UdpSessionData->DestinationAddress); - if (UdpSessionData->DestinationPort != 0) { Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort); } + NetCopyMem (&Source, &Override.SourceAddress, sizeof (IP4_ADDR)); + NetCopyMem (&Destination, &UdpSessionData->DestinationAddress, sizeof (IP4_ADDR)); + // // calculate the pseudo head checksum using the overridden parameters. // HeadSum = NetPseudoHeadChecksum ( - EFI_IP4 (Override.SourceAddress), + Source, Destination, EFI_IP_PROTO_UDP, 0 @@ -617,8 +632,9 @@ Udp4Transmit ( // // UdpSessionData is NULL, use the address and port information previously configured. // - Destination = EFI_IP4 (ConfigData->RemoteAddress); - HeadSum = Instance->HeadSum; + NetCopyMem (&Destination, &ConfigData->RemoteAddress, sizeof (IP4_ADDR)); + + HeadSum = Instance->HeadSum; } // @@ -635,8 +651,12 @@ Udp4Transmit ( // // Fill the IpIo Override data. // - EFI_IP4 (Override.GatewayAddress) = (TxData->GatewayAddress != NULL) ? - EFI_IP4 (*(TxData->GatewayAddress)) : 0; + if (TxData->GatewayAddress != NULL) { + NetCopyMem (&Override.GatewayAddress, TxData->GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); + } else { + NetZeroMem (&Override.GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); + } + Override.Protocol = EFI_IP_PROTO_UDP; Override.TypeOfService = ConfigData->TypeOfService; Override.TimeToLive = ConfigData->TimeToLive; -- 2.39.2