diff options
Diffstat (limited to '')
114 files changed, 91125 insertions, 0 deletions
diff --git a/private/ntos/tdi/isnp/dirs b/private/ntos/tdi/isnp/dirs new file mode 100644 index 000000000..a93e8e700 --- /dev/null +++ b/private/ntos/tdi/isnp/dirs @@ -0,0 +1,28 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + ipx \ + nb \ + spx + +OPTIONAL_DIRS= diff --git a/private/ntos/tdi/isnp/inc/bind.h b/private/ntos/tdi/isnp/inc/bind.h new file mode 100644 index 000000000..60294349d --- /dev/null +++ b/private/ntos/tdi/isnp/inc/bind.h @@ -0,0 +1,563 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + bind.h + +Abstract: + + Private include file for the ISN transport. It defines the + structures used for binding between IPX and the upper drivers. + +Author: + + Adam Barr (adamba) 04-Oct-1993 + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#ifndef _ISN_BIND_ +#define _ISN_BIND_ + +// +// Retrieve the common definitions. +// + +#include <isnkrnl.h> + + +// +// Define the IOCTL used for binding between the upper +// drivers and IPX. +// + +#define _IPX_CONTROL_CODE(request,method) \ + CTL_CODE(FILE_DEVICE_TRANSPORT, request, method, FILE_ANY_ACCESS) + +#define IOCTL_IPX_INTERNAL_BIND _IPX_CONTROL_CODE( 0x1234, METHOD_BUFFERED ) + + +// +// Identifier for the drivers in ISN. +// + +#define IDENTIFIER_NB 0 +#define IDENTIFIER_SPX 1 +#define IDENTIFIER_RIP 2 +#define IDENTIFIER_IPX 3 + +#ifdef _PNP_POWER +// +// This the number of PVOIDs in the beginning of the SEND_RESERVED +// section of a packet header, to be set aside by the ISN clients (NB/SPX) +// for IPX's private use. +// +#define SEND_RESERVED_COMMON_SIZE 8 +#endif + +// +// Definition of a RIP router table entry. +// + +typedef struct _IPX_ROUTE_ENTRY { + UCHAR Network[4]; + USHORT NicId; + UCHAR NextRouter[6]; + NDIS_HANDLE NdisBindingContext; + USHORT Flags; + USHORT Timer; + UINT Segment; + USHORT TickCount; + USHORT HopCount; + LIST_ENTRY AlternateRoute; + LIST_ENTRY NicLinkage; + struct { + LIST_ENTRY Linkage; + ULONG Reserved[1]; + } PRIVATE; +} IPX_ROUTE_ENTRY, * PIPX_ROUTE_ENTRY; + +// +// Definition of the Flags values. +// + +#define IPX_ROUTER_PERMANENT_ENTRY 0x0001 // entry should never be deleted +#define IPX_ROUTER_LOCAL_NET 0x0002 // locally attached network +#define IPX_ROUTER_SCHEDULE_ROUTE 0x0004 // call ScheduleRouteHandler after using +#define IPX_ROUTER_GLOBAL_WAN_NET 0x0008 // this is for rip's global network number + + +// +// Definition of the structure provided on a find +// route/find route completion call. +// + +// +// [SA] Bug #15094 added node number to the structure. +// + +typedef struct _IPX_FIND_ROUTE_REQUEST { + UCHAR Network[4]; + UCHAR Node[6] ; + IPX_LOCAL_TARGET LocalTarget; + UCHAR Identifier; + UCHAR Type; + UCHAR Reserved1[2]; + PVOID Reserved2; + LIST_ENTRY Linkage; +} IPX_FIND_ROUTE_REQUEST, *PIPX_FIND_ROUTE_REQUEST; + +// +// Definitions for the Type value. +// + +#define IPX_FIND_ROUTE_NO_RIP 1 // fail if net is not in database +#define IPX_FIND_ROUTE_RIP_IF_NEEDED 2 // return net if in database, otherwise RIP out +#define IPX_FIND_ROUTE_FORCE_RIP 3 // re-RIP even if net is in database + + +// +// Structure used when querying the line information +// for a specific NID ID. +// + +typedef struct _IPX_LINE_INFO { + UINT LinkSpeed; + UINT MaximumPacketSize; + UINT MaximumSendSize; + UINT MacOptions; +} IPX_LINE_INFO, *PIPX_LINE_INFO; + + + +// +// Functions provided by the upper driver. +// + +typedef VOID +(*IPX_INTERNAL_RECEIVE) ( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize +); + +typedef VOID +(*IPX_INTERNAL_RECEIVE_COMPLETE) ( + IN USHORT NicId +); + +typedef VOID +(*IPX_INTERNAL_STATUS) ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength +); + +typedef VOID +(*IPX_INTERNAL_SEND_COMPLETE) ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +); + +typedef VOID +(*IPX_INTERNAL_TRANSFER_DATA_COMPLETE) ( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred +); + +typedef VOID +(*IPX_INTERNAL_FIND_ROUTE_COMPLETE) ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute +); + +typedef VOID +(*IPX_INTERNAL_LINE_UP) ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData +); + +typedef VOID +(*IPX_INTERNAL_LINE_DOWN) ( + IN USHORT NicId +); + +typedef VOID +(*IPX_INTERNAL_SCHEDULE_ROUTE) ( + IN PIPX_ROUTE_ENTRY RouteEntry +); + +#if defined(_PNP_POWER) + +// +// following opcodes are used when calling the +// above handler. +// +typedef enum _IPX_PNP_OPCODE { + IPX_PNP_ADD_DEVICE, // 0 - addition of the first adapter + IPX_PNP_DELETE_DEVICE, // 1 - deletion of the last adapter + IPX_PNP_TRANSLATE_DEVICE, // 2 - translate device resource + IPX_PNP_TRANSLATE_ADDRESS, // 3 - translate address resource + IPX_PNP_ADDRESS_CHANGE, // 4 - Adapter address or Reserved address changed + IPX_PNP_MAX_OPCODES, // 5 +} IPX_PNP_OPCODE, *PIPX_PNP_OPCODE; + +// +// PnP event notification handler. +// +typedef VOID +(*IPX_INTERNAL_PNP_NOTIFICATION) ( + IN IPX_PNP_OPCODE PnPOpcode, + IN OUT PVOID PnpData +); + +// +// Pointer to this structure is passed in PnPData portion of +// the above handler when the opcode is ADD_DEVICE or DELETE_DEVICE. +// +typedef struct _IPX_PNP_INFO { + ULONG NetworkAddress; + UCHAR NodeAddress[6]; + BOOLEAN NewReservedAddress; // where the above is a new reserved + // address for the Ipx clients. + BOOLEAN FirstORLastDevice; // is this a first card arrival or last card deletion. + IPX_LINE_INFO LineInfo; // New LineInfo. + NIC_HANDLE NicHandle; +} IPX_PNP_INFO, *PIPX_PNP_INFO; + +#endif _PNP_POWER + +// +// Input to the bind IOCTL +// + +typedef struct _IPX_INTERNAL_BIND_INPUT { + USHORT Version; + UCHAR Identifier; + BOOLEAN BroadcastEnable; + UINT LookaheadRequired; + UINT ProtocolOptions; + IPX_INTERNAL_RECEIVE ReceiveHandler; + IPX_INTERNAL_RECEIVE_COMPLETE ReceiveCompleteHandler; + IPX_INTERNAL_STATUS StatusHandler; + IPX_INTERNAL_SEND_COMPLETE SendCompleteHandler; + IPX_INTERNAL_TRANSFER_DATA_COMPLETE TransferDataCompleteHandler; + IPX_INTERNAL_FIND_ROUTE_COMPLETE FindRouteCompleteHandler; + IPX_INTERNAL_LINE_UP LineUpHandler; + IPX_INTERNAL_LINE_DOWN LineDownHandler; + IPX_INTERNAL_SCHEDULE_ROUTE ScheduleRouteHandler; +#if defined(_PNP_POWER) + IPX_INTERNAL_PNP_NOTIFICATION PnPHandler; +#endif _PNP_POWER + ULONG RipParameters; +} IPX_INTERNAL_BIND_INPUT, * PIPX_INTERNAL_BIND_INPUT; + +#if defined(_PNP_POWER) +#define ISN_VERSION 2 +#endif _PNP_POWER +// +// Bit mask values for RipParameters. +// + +#define IPX_RIP_PARAM_GLOBAL_NETWORK 0x00000001 // single network for all WANS + + + +// +// Functions provided by the lower driver. +// + +typedef NDIS_STATUS +(*IPX_INTERNAL_SEND) ( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength +); + +typedef VOID +(*IPX_INTERNAL_FIND_ROUTE) ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest +); + +typedef NTSTATUS +(*IPX_INTERNAL_QUERY) ( + IN ULONG InternalQueryType, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif _PNP_POWER + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +); + +typedef VOID +(*IPX_INTERNAL_TRANSFER_DATA)( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + +// +// Definitions of the internal query types. In all cases +// STATUS_SUCCESS is returned if the request succeeds, and +// STATUS_BUFFER_TOO_SMALL is returned, and BufferLengthNeeded +// set if specified, if the buffer is too short. Other +// return codes are defined below. The routine never pends. +// + +// +// This is used to query the line info. NicId specifies which one +// to query. Buffer contains an IPX_LINE_INFO structure which is +// used to return the information. Other return values: +// +// STATUS_INVALID_PARAMETER - NicId is invalid. +// + +#define IPX_QUERY_LINE_INFO 1 + +// +// This is used to query the maximum NicId. NicId is unused. The +// Buffer contains a USHORT which is used to return the information. +// + +#define IPX_QUERY_MAXIMUM_NIC_ID 2 + +// +// This is used to determine if the IPX address specified was sent +// by our local machine. If the address is the source address of a +// received frame, NicId should be the ID that was indicated; otherwise +// it should be set to 0. Buffer holds a TDI_ADDRESS_IPX. This +// call returns STATUS_SUCCESS if the address is local, and +// STATUS_NO_SUCH_DEVICE if not. +// + +#define IPX_QUERY_IS_ADDRESS_LOCAL 3 + +// +// This is used to query the receive buffer space of a given NicId. +// Buffer contains a ULONG which is used to return the information. +// It returns STATUS_INVALID_PARAMETER if NicId is invalid. +// + +#define IPX_QUERY_RECEIVE_BUFFER_SPACE 4 + +// +// This is used to query the local IPX address of a given NicId. +// Buffer contains a TDI_ADDRESS_IPX structure (the Socket is +// returned as 0). If it is queried on net 0 it returns the +// virtual network if there is one, otherwise STATUS_INVALID_PARAMETER. +// It returns STATUS_INVALID_PARAMETER if NicId is invalid. +// + +#define IPX_QUERY_IPX_ADDRESS 5 + +// +// This is used to return the source routing information for +// a give remote address. NicId will be the NIC the packet was +// received from. The IPX_SOURCE_ROUTING_QUERY is contained +// in Buffer. Always returns STATUS_SUCCESS, although the +// SourceRoutingLength may be 0 for unknown remotes. +// +// The source routing is return in the direction it was received +// from the remote, not the direction used in replying. The +// MaximumSendSize includes the IPX header (as it does in +// IPX_LINE_INFO). +// + +#define IPX_QUERY_SOURCE_ROUTING 6 + +typedef struct _IPX_SOURCE_ROUTING_INFO { + USHORT Identifier; // input: the caller's IDENTIFIER_SPX, _NB, etc. + UCHAR RemoteAddress[6]; // input: the remote address + UCHAR SourceRouting[18]; // output: room for the maximum source route + USHORT SourceRoutingLength; // output: the valid length of source route + ULONG MaximumSendSize; // output: based on nic and source routing +} IPX_SOURCE_ROUTING_INFO, * PIPX_SOURCE_ROUTING_INFO; + +// +// This is used to query the maximum NicId over which outgoing type +// 20 packets should be sent. It will be less than or equal to +// the IPX_QUERY_MAXIMUM_NIC_ID value. What's excluded are down wan +// lines and dialin wan lines if DisableDialinNetbios bit 1 is set. +// + +#define IPX_QUERY_MAX_TYPE_20_NIC_ID 7 + +#if defined(_PNP_POWER) + +// +// This are used by NB to pass down these TDI queries which cannot +// be completed in NB. +// + +#define IPX_QUERY_DATA_LINK_ADDRESS 8 +#define IPX_QUERY_NETWORK_ADDRESS 9 + +#endif _PNP_POWER + +// +// Output of a non-RIP bind. +// + +typedef struct _IPX_INTERNAL_BIND_OUTPUT { + USHORT Version; + UCHAR Node[6]; + UCHAR Network[4]; + USHORT MacHeaderNeeded; + USHORT IncludedHeaderOffset; + IPX_LINE_INFO LineInfo; + IPX_INTERNAL_SEND SendHandler; + IPX_INTERNAL_FIND_ROUTE FindRouteHandler; + IPX_INTERNAL_QUERY QueryHandler; + IPX_INTERNAL_TRANSFER_DATA TransferDataHandler; +} IPX_INTERNAL_BIND_OUTPUT, * PIPX_INTERNAL_BIND_OUTPUT; + + + +// +// Lower driver functions provided only for RIP. +// + +typedef UINT +(*IPX_INTERNAL_GET_SEGMENT) ( + IN UCHAR Network[4] +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_ROUTE) ( + IN UINT Segment, + IN UCHAR Network[4] +); + +typedef BOOLEAN +(*IPX_INTERNAL_ADD_ROUTE) ( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry +); + +typedef BOOLEAN +(*IPX_INTERNAL_DELETE_ROUTE) ( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_FIRST_ROUTE) ( + IN UINT Segment +); + +typedef PIPX_ROUTE_ENTRY +(*IPX_INTERNAL_GET_NEXT_ROUTE) ( + IN UINT Segment +); + +typedef VOID +(*IPX_INTERNAL_INCREMENT_WAN_INACTIVITY) ( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +typedef ULONG +(*IPX_INTERNAL_QUERY_WAN_INACTIVITY) ( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif + +); + +// +// Describes a single network. +// + +typedef struct _IPX_NIC_DATA { + USHORT NicId; + UCHAR Node[6]; + UCHAR Network[4]; + IPX_LINE_INFO LineInfo; + NDIS_MEDIUM DeviceType; + ULONG EnableWanRouter; +} IPX_NIC_DATA, * PIPX_NIC_DATA; + + +// +// Describes all networks. +// + +typedef struct _IPX_NIC_INFO_BUFFER { + USHORT NicCount; + USHORT VirtualNicId; + UCHAR VirtualNetwork[4]; + IPX_NIC_DATA NicData[1]; +} IPX_NIC_INFO_BUFFER, * PIPX_NIC_INFO_BUFFER; + + +// +// Output from a RIP bind (the actual structure size is +// based on the number of IPX_NIC_DATA elements in the +// final IPX_NIC_INFO_BUFFER structure). +// + +typedef struct _IPX_INTERNAL_BIND_RIP_OUTPUT { + USHORT Version; + USHORT MaximumNicCount; + USHORT MacHeaderNeeded; + USHORT IncludedHeaderOffset; + IPX_INTERNAL_SEND SendHandler; + UINT SegmentCount; + KSPIN_LOCK * SegmentLocks; + IPX_INTERNAL_GET_SEGMENT GetSegmentHandler; + IPX_INTERNAL_GET_ROUTE GetRouteHandler; + IPX_INTERNAL_ADD_ROUTE AddRouteHandler; + IPX_INTERNAL_DELETE_ROUTE DeleteRouteHandler; + IPX_INTERNAL_GET_FIRST_ROUTE GetFirstRouteHandler; + IPX_INTERNAL_GET_NEXT_ROUTE GetNextRouteHandler; + IPX_INTERNAL_INCREMENT_WAN_INACTIVITY IncrementWanInactivityHandler; + IPX_INTERNAL_QUERY_WAN_INACTIVITY QueryWanInactivityHandler; + IPX_INTERNAL_TRANSFER_DATA TransferDataHandler; + IPX_NIC_INFO_BUFFER NicInfoBuffer; +} IPX_INTERNAL_BIND_RIP_OUTPUT, * PIPX_INTERNAL_BIND_RIP_OUTPUT; + +#endif // _ISN_BIND_ + + +#ifndef _IPXCP_CONFIG_ +#define _IPXCP_CONFIG_ + +typedef struct _IPXCP_CONFIGURATION { + USHORT Version; + USHORT Length; + UCHAR Network[4]; + UCHAR LocalNode[6]; + UCHAR RemoteNode[6]; + ULONG ConnectionClient; // 0 - Server, 1 - Client +} IPXCP_CONFIGURATION, *PIPXCP_CONFIGURATION; + +#endif // _IPXCP_CONFIG_ + diff --git a/private/ntos/tdi/isnp/inc/ioctls.h b/private/ntos/tdi/isnp/inc/ioctls.h new file mode 100644 index 000000000..e7dd7b81a --- /dev/null +++ b/private/ntos/tdi/isnp/inc/ioctls.h @@ -0,0 +1,155 @@ +#define VER_IOCH "@(#)MCS ipx/h/ioctls.h 1.00.00 - 08 APR 1993"; + +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Driver for Windows NT +* +* Module: ipx/h/ioctls.h +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +***************************************************************************** +* +* Functional Description: +* +* IOCTL defines +* +****************************************************************************/ + +/** Ioctls for IPX - (X) = User callable **/ + +/** + ioctls will values 100 - 150 were added for the NT port. +**/ + +#define I_MIPX (('I' << 24) | ('D' << 16) | ('P' << 8)) +#define MIPX_SETNODEADDR I_MIPX | 0 /* Set the node address */ +#define MIPX_SETNETNUM I_MIPX | 1 /* Set the network number */ +#define MIPX_SETPTYPE I_MIPX | 2 /* (X) Set the packet type */ +#define MIPX_SENTTYPE I_MIPX | 3 /* (X) Set the xport type */ +#define MIPX_SETPKTSIZE I_MIPX | 4 /* Set the packet size */ +#define MIPX_SETSAP I_MIPX | 5 /* Set the sap/type field */ +#define MIPX_SENDOPTS I_MIPX | 6 /* (X) Send options on recv */ +#define MIPX_NOSENDOPTS I_MIPX | 7 /* (X) Don't send options on recv */ +#define MIPX_SENDSRC I_MIPX | 8 /* (X) Send source address up */ +#define MIPX_NOSENDSRC I_MIPX | 9 /* (X) Don't Send source address up */ +#define MIPX_CONVBCAST I_MIPX | 10 /* Convert TKR bcast to func addr */ +#define MIPX_NOCONVBCAST I_MIPX | 11 /* Don't cnvrt TKR bcast to funcaddr */ +#define MIPX_SETCARDTYPE I_MIPX | 12 /* Set 802.3 or ETH type */ +#define MIPX_STARGROUP I_MIPX | 13 /* This is stargroup */ +#define MIPX_SWAPLENGTH I_MIPX | 14 /* Set flag for swapping 802.3 length */ +#define MIPX_SENDDEST I_MIPX | 15 /* (X) Send dest. address up */ +#define MIPX_NOSENDDEST I_MIPX | 16 /* (X) Don't send dest. address up */ +#define MIPX_SENDFDEST I_MIPX | 17 /* (X) Send final dest. address up */ +#define MIPX_NOSENDFDEST I_MIPX | 18 /* (X) Don't send final dest. up */ + +/** Added for NT port **/ + +#define MIPX_SETVERSION I_MIPX | 100 /* Set card version */ +#define MIPX_GETSTATUS I_MIPX | 101 +#define MIPX_SENDADDROPT I_MIPX | 102 /* (X) Send ptype w/addr on recv */ +#define MIPX_NOSENDADDROPT I_MIPX | 103 /* (X) Stop sending ptype on recv */ +#define MIPX_CHECKSUM I_MIPX | 104 /* Enable/Disable checksum */ +#define MIPX_GETPKTSIZE I_MIPX | 105 /* Get max packet size */ +#define MIPX_SENDHEADER I_MIPX | 106 /* Send header with data */ +#define MIPX_NOSENDHEADER I_MIPX | 107 /* Don't send header with data */ +#define MIPX_SETCURCARD I_MIPX | 108 /* Set current card for IOCTLs */ +#define MIPX_SETMACTYPE I_MIPX | 109 /* Set the Cards MAC type */ +#define MIPX_DOSROUTE I_MIPX | 110 /* Do source routing on this card*/ +#define MIPX_NOSROUTE I_MIPX | 111 /* Don't source routine the card*/ +#define MIPX_SETRIPRETRY I_MIPX | 112 /* Set RIP retry count */ +#define MIPX_SETRIPTO I_MIPX | 113 /* Set RIP timeout */ +#define MIPX_SETTKRSAP I_MIPX | 114 /* Set the token ring SAP */ +#define MIPX_SETUSELLC I_MIPX | 115 /* Put LLC hdr on packets */ +#define MIPX_SETUSESNAP I_MIPX | 116 /* Put SNAP hdr on packets */ +#define MIPX_8023LEN I_MIPX | 117 /* 1=make even, 0=dont make even*/ +#define MIPX_SENDPTYPE I_MIPX | 118 /* Send ptype in options on recv*/ +#define MIPX_NOSENDPTYPE I_MIPX | 119 /* Don't send ptype in options */ +#define MIPX_FILTERPTYPE I_MIPX | 120 /* Filter on recv ptype */ +#define MIPX_NOFILTERPTYPE I_MIPX | 121 /* Don't Filter on recv ptype */ +#define MIPX_SETSENDPTYPE I_MIPX | 122 /* Set pkt type to send with */ +#define MIPX_GETCARDINFO I_MIPX | 123 /* Get info on a card */ +#define MIPX_SENDCARDNUM I_MIPX | 124 /* Send card num up in options */ +#define MIPX_NOSENDCARDNUM I_MIPX | 125 /* Dont send card num in options*/ +#define MIPX_SETROUTER I_MIPX | 126 /* Set router enabled flag */ +#define MIPX_SETRIPAGE I_MIPX | 127 /* Set RIP age timeout */ +#define MIPX_SETRIPUSAGE I_MIPX | 128 /* Set RIP usage timeout */ +#define MIPX_SETSROUTEUSAGE I_MIPX| 129 /* Set the SROUTE usage timeout */ +#define MIPX_SETINTNET I_MIPX | 130 /* Set internal network number */ +#define MIPX_NOVIRTADDR I_MIPX | 131 /* Turn off virtual net num */ +#define MIPX_VIRTADDR I_MIPX | 132 /* Turn on virtual net num */ +#define MIPX_SETBCASTFLAG I_MIPX | 133 /* Turn on bcast flag in addr */ +#define MIPX_NOBCASTFLAG I_MIPX | 134 /* Turn off bcast flag in addr */ +#define MIPX_GETNETINFO I_MIPX | 135 /* Get info on a network num */ +#define MIPX_SETDELAYTIME I_MIPX | 136 /* Set cards delay time */ +#define MIPX_SETROUTEADV I_MIPX | 137 /* Route advertise timeout */ +#define MIPX_SETSOCKETS I_MIPX | 138 /* Set default sockets */ +#define MIPX_SETLINKSPEED I_MIPX | 139 /* Set the link speed for a card*/ +#define MIPX_SETWANFLAG I_MIPX | 140 +#define MIPX_GETCARDCHANGES I_MIPX | 141 /* Wait for card changes */ +#define MIPX_GETMAXADAPTERS I_MIPX | 142 +#define MIPX_REUSEADDRESS I_MIPX | 143 +#define MIPX_RERIPNETNUM I_MIPX | 144 /* ReRip a network */ + +/** For Source Routing Support **/ + +#define MIPX_SRCLEAR I_MIPX | 200 /* Clear the source routing table*/ +#define MIPX_SRDEF I_MIPX | 201 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRBCAST I_MIPX | 202 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRMULTI I_MIPX | 203 /* 0=Single Rte, 1=All Routes */ +#define MIPX_SRREMOVE I_MIPX | 204 /* Remove a node from the table */ +#define MIPX_SRLIST I_MIPX | 205 /* Get the source routing table */ +#define MIPX_SRGETPARMS I_MIPX | 206 /* Get source routing parms */ + +#define MIPX_SETSHOULDPUT I_MIPX | 210 /* Turn on should put call */ +#define MIPX_DELSHOULDPUT I_MIPX | 211 /* Turn off should put call */ +#define MIPX_GETSHOULDPUT I_MIPX | 212 /* Get ptr to mipx_shouldput */ + +/** Added for ISN **/ + +#define MIPX_RCVBCAST I_MIPX | 300 /* (X) Enable broadcast reception */ +#define MIPX_NORCVBCAST I_MIPX | 301 /* (X) Disable broadcast reception */ +#define MIPX_ADAPTERNUM I_MIPX | 302 /* Get maximum adapter number */ +#define MIPX_NOTIFYCARDINFO I_MIPX | 303 /* Pend until card info changes */ +#define MIPX_LOCALTARGET I_MIPX | 304 /* Get local target for address */ +#define MIPX_NETWORKINFO I_MIPX | 305 /* Return info about remote net */ +#define MIPX_ZEROSOCKET I_MIPX | 306 /* Use 0 as source socket on sends */ + +/** Ioctls for SPX **/ + +#define I_MSPX (('S' << 24) | ('P' << 16) | ('P' << 8)) +#define MSPX_SETADDR I_MSPX | 0 /* Set the network address */ +#define MSPX_SETPKTSIZE I_MSPX | 1 /* Set the packet size per card */ +#define MSPX_SETDATASTREAM I_MSPX | 2 /* Set datastream type */ + +/** Added for NT port **/ + +#define MSPX_SETASLISTEN I_MSPX | 100 /* Set as a listen socket */ +#define MSPX_GETSTATUS I_MSPX | 101 /* Get running status */ +#define MSPX_GETQUEUEPTR I_MSPX | 102 /* Get ptr to the streams queue */ +#define MSPX_SETDATAACK I_MSPX | 103 /* Set DATA ACK option */ +#define MSPX_NODATAACK I_MSPX | 104 /* Turn off DATA ACK option */ +#define MSPX_SETMAXPKTSOCK I_MSPX | 105 /* Set the packet size per socket */ +#define MSPX_SETWINDOWCARD I_MSPX | 106 /* Set window size for card */ +#define MSPX_SETWINDOWSOCK I_MSPX | 107 /* Set window size for 1 socket */ +#define MSPX_SENDHEADER I_MSPX | 108 /* Send header with data */ +#define MSPX_NOSENDHEADER I_MSPX | 109 /* Don't send header with data */ +#define MSPX_GETPKTSIZE I_MSPX | 110 /* Get the packet size per card */ +#define MSPX_SETCONNCNT I_MSPX | 111 /* Set the conn req count */ +#define MSPX_SETCONNTO I_MSPX | 112 /* Set the conn req timeout */ +#define MSPX_SETALIVECNT I_MSPX | 113 /* Set the keepalive count */ +#define MSPX_SETALIVETO I_MSPX | 114 /* Set the keepalive timeout */ +#define MSPX_SETALWAYSEOM I_MSPX | 115 /* Turn on always EOM flag */ +#define MSPX_NOALWAYSEOM I_MSPX | 116 /* Turn off always EOM flag */ diff --git a/private/ntos/tdi/isnp/inc/isn.h b/private/ntos/tdi/isnp/inc/isn.h new file mode 100644 index 000000000..7b7e23601 --- /dev/null +++ b/private/ntos/tdi/isnp/inc/isn.h @@ -0,0 +1,41 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + isn.h + +Abstract: + + Private include file for the ISN transport. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + + +#define ISN_NT 1 + + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include <ntddk.h> +#include <tdikrnl.h> +#include <ndis.h> +#include <cxport.h> +#include <bind.h> + diff --git a/private/ntos/tdi/isnp/ipx/action.c b/private/ntos/tdi/isnp/ipx/action.c new file mode 100644 index 000000000..807391cae --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/action.c @@ -0,0 +1,1802 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiAction + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#include <packon.h> + +typedef struct _GET_PKT_SIZE { + ULONG Unknown; + ULONG MaxDatagramSize; +} GET_PKT_SIZE, *PGET_PKT_SIZE; + + +// +// These structures are used to set and query information +// about our source routing table. +// + +typedef struct _SR_GET_PARAMETERS { + ULONG BoardNumber; // 0-based + ULONG SrDefault; // 0 = single route, 1 = all routes + ULONG SrBroadcast; + ULONG SrMulticast; +} SR_GET_PARAMETERS, *PSR_GET_PARAMETERS; + +typedef struct _SR_SET_PARAMETER { + ULONG BoardNumber; // 0-based + ULONG Parameter; // 0 = single route, 1 = all routes +} SR_SET_PARAMETER, *PSR_SET_PARAMETER; + +typedef struct _SR_SET_REMOVE { + ULONG BoardNumber; // 0-based + UCHAR MacAddress[6]; // remote to drop routing for +} SR_SET_REMOVE, *PSR_SET_REMOVE; + +typedef struct _SR_SET_CLEAR { + ULONG BoardNumber; // 0-based +} SR_SET_CLEAR, *PSR_SET_CLEAR; + +#include <packoff.h> + +typedef struct _ISN_ACTION_GET_DETAILS { + USHORT NicId; // passed by caller, returns count if it is 0 + BOOLEAN BindingSet; // returns TRUE if in a set + UCHAR Type; // 1 = lan, 2 = up wan, 3 = down wan + ULONG FrameType; // returns 0 through 3 + ULONG NetworkNumber; // returns virtual net if NicId is 0 + UCHAR Node[6]; // adapter MAC address + WCHAR AdapterName[64]; // terminated with Unicode NULL +} ISN_ACTION_GET_DETAILS, *PISN_ACTION_GET_DETAILS; + + + +NTSTATUS +IpxTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiAction request for the transport + provider. + +Arguments: + + Device - The device for the operation. + + Request - Describes the action request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PADDRESS_FILE AddressFile; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + PBINDING Binding, MasterBinding; + PADAPTER Adapter; + union { + PISN_ACTION_GET_LOCAL_TARGET GetLocalTarget; + PISN_ACTION_GET_NETWORK_INFO GetNetworkInfo; + PISN_ACTION_GET_DETAILS GetDetails; + PSR_GET_PARAMETERS GetSrParameters; + PSR_SET_PARAMETER SetSrParameter; + PSR_SET_REMOVE SetSrRemove; + PSR_SET_CLEAR SetSrClear; + PIPX_ADDRESS_DATA IpxAddressData; + PGET_PKT_SIZE GetPktSize; + PIPX_NETNUM_DATA IpxNetnumData; + } u; // BUGBUG: Make these unaligned?? + PIPX_ROUTE_ENTRY RouteEntry; + PNWLINK_ACTION NwlinkAction; + ULONG Segment; + ULONG AdapterNum; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + if (NdisBuffer == NULL) { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + + return STATUS_NOT_SUPPORTED; + } + + + // + // Make sure we have enough room for just the header not + // including the data. + // + + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { + IPX_DEBUG (ACTION, ("Nwlink action failed, buffer too small\n")); + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + + // + // Make sure that the correct file object is being used. + // + + if (NwlinkAction->OptionType == NWLINK_OPTION_ADDRESS) { + + if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + IPX_DEBUG (ACTION, ("Nwlink action failed, not address file\n")); + return STATUS_INVALID_HANDLE; + } + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != IPX_ADDRESSFILE_SIGNATURE)) { + + IPX_DEBUG (ACTION, ("Nwlink action failed, bad address file\n")); + return STATUS_INVALID_HANDLE; + } + + } else if (NwlinkAction->OptionType != NWLINK_OPTION_CONTROL) { + + IPX_DEBUG (ACTION, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); + return STATUS_NOT_SUPPORTED; + } + + + // + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + // + + + Status = STATUS_SUCCESS; + + switch (NwlinkAction->Option) { + + //DbgPrint("NwlinkAction->Option is (%x)\n", NwlinkAction->Option); + // + // This first group support the winsock helper dll. + // In most cases the corresponding sockopt is shown in + // the comment, as well as the contents of the Data + // part of the action buffer. + // + + case MIPX_SETSENDPTYPE: + + // + // IPX_PTYPE: Data is a single byte packet type. + // + + if (DataLength >= 1) { + IPX_DEBUG (ACTION, ("%lx: MIPX_SETSENDPTYPE %x\n", AddressFile, NwlinkAction->Data[0])); + AddressFile->DefaultPacketType = NwlinkAction->Data[0]; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_FILTERPTYPE: + + // + // IPX_FILTERPTYPE: Data is a single byte to filter on. + // + + if (DataLength >= 1) { + IPX_DEBUG (ACTION, ("%lx: MIPX_FILTERPTYPE %x\n", AddressFile, NwlinkAction->Data[0])); + AddressFile->FilteredType = NwlinkAction->Data[0]; + AddressFile->FilterOnPacketType = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_NOFILTERPTYPE: + + // + // IPX_STOPFILTERPTYPE. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOFILTERPTYPE\n", AddressFile)); + AddressFile->FilterOnPacketType = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveFlagsAddressing || + AddressFile->ReceiveIpxHeader || AddressFile->IsSapSocket); + break; + + case MIPX_SENDADDROPT: + + // + // IPX_EXTENDED_ADDRESS (TRUE). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SENDADDROPT\n", AddressFile)); + AddressFile->ExtendedAddressing = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NOSENDADDROPT: + + // + // IPX_EXTENDED_ADDRESS (FALSE). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOSENDADDROPT\n", AddressFile)); + AddressFile->ExtendedAddressing = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ReceiveFlagsAddressing || AddressFile->ReceiveIpxHeader || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_SETRCVFLAGS: + + // + // No sockopt yet. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SETRCVFLAGS\n", AddressFile)); + AddressFile->ReceiveFlagsAddressing = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NORCVFLAGS: + + // + // No sockopt yet. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NORCVFLAGS\n", AddressFile)); + AddressFile->ReceiveFlagsAddressing = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveIpxHeader || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_SENDHEADER: + + // + // IPX_RECVHDR (TRUE); + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_SENDHEADER\n", AddressFile)); + AddressFile->ReceiveIpxHeader = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + break; + + case MIPX_NOSENDHEADER: + + // + // IPX_RECVHDR (FALSE); + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOSENDHEADER\n", AddressFile)); + AddressFile->ReceiveIpxHeader = FALSE; + AddressFile->SpecialReceiveProcessing = (BOOLEAN) + (AddressFile->ExtendedAddressing || AddressFile->ReceiveFlagsAddressing || + AddressFile->FilterOnPacketType || AddressFile->IsSapSocket); + break; + + case MIPX_RCVBCAST: + + // + // Broadcast reception enabled. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_RCVBCAST\n", AddressFile)); + CTEGetLock (&Device->Lock, &LockHandle); + + if (!AddressFile->EnableBroadcast) { + + AddressFile->EnableBroadcast = TRUE; + IpxAddBroadcast (Device); + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_NORCVBCAST: + + // + // Broadcast reception disabled. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NORCVBCAST\n", AddressFile)); + CTEGetLock (&Device->Lock, &LockHandle); + + if (AddressFile->EnableBroadcast) { + + AddressFile->EnableBroadcast = FALSE; + IpxRemoveBroadcast (Device); + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_GETPKTSIZE: + + // + // IPX_MAXSIZE. + // + // BUGBUG: Figure out what the first length is for. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_GETPKTSIZE\n", AddressFile)); + if (DataLength >= sizeof(GET_PKT_SIZE)) { + u.GetPktSize = (PGET_PKT_SIZE)(NwlinkAction->Data); + u.GetPktSize->Unknown = 0; + u.GetPktSize->MaxDatagramSize = Device->Information.MaxDatagramSize; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_ADAPTERNUM: + + // + // IPX_MAX_ADAPTER_NUM. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ADAPTERNUM\n", AddressFile)); + if (DataLength >= sizeof(ULONG)) { + *(UNALIGNED ULONG *)(NwlinkAction->Data) = Device->SapNicCount; + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_ADAPTERNUM2: + + // + // IPX_MAX_ADAPTER_NUM. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ADAPTERNUM2\n", AddressFile)); + if (DataLength >= sizeof(ULONG)) { + *(UNALIGNED ULONG *)(NwlinkAction->Data) = MIN (Device->MaxBindings, Device->ValidBindings); + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case MIPX_GETCARDINFO: + case MIPX_GETCARDINFO2: + + // + // GETCARDINFO is IPX_ADDRESS. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_GETCARDINFO (%d)\n", + AddressFile, *(UNALIGNED UINT *)NwlinkAction->Data)); + if (DataLength >= sizeof(IPX_ADDRESS_DATA)) { + u.IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + AdapterNum = u.IpxAddressData->adapternum+1; + + if (((AdapterNum >= 1) && (AdapterNum <= Device->SapNicCount)) || + ((NwlinkAction->Option == MIPX_GETCARDINFO2) && (AdapterNum <= (ULONG) MIN (Device->MaxBindings, Device->ValidBindings)))) { + +#ifdef _PNP_POWER +// Get lock + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(Device, AdapterNum); +#else + Binding = Device->Bindings[AdapterNum]; +#endif + if (Binding == NULL) { + + // + // This should be a binding in the WAN range + // of an adapter which is currently not + // allocated. We scan back to the previous + // non-NULL binding, which should be on the + // same adapter, and return a down line with + // the same characteristics as that binding. + // + + UINT i = AdapterNum; + + do { + --i; +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + } while (Binding == NULL); + + CTEAssert (Binding->Adapter->MacInfo.MediumAsync); + CTEAssert (i >= Binding->Adapter->FirstWanNicId); + CTEAssert (AdapterNum <= Binding->Adapter->LastWanNicId); + + u.IpxAddressData->status = FALSE; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + + } else { + + if ((Binding->Adapter->MacInfo.MediumAsync) && + (Device->WanGlobalNetworkNumber)) { + + // + // In this case we make it look like one big wan + // net, so the line is "up" or "down" depending + // on whether we have given him the first indication + // or not. + // + + u.IpxAddressData->status = Device->GlobalNetworkIndicated; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Device->GlobalWanNetwork; + + } else { + + u.IpxAddressData->status = Binding->LineUp; + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + } + + } + + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + + Adapter = Binding->Adapter; + u.IpxAddressData->wan = Adapter->MacInfo.MediumAsync; + u.IpxAddressData->maxpkt = + (NwlinkAction->Option == MIPX_GETCARDINFO) ? + Binding->AnnouncedMaxDatagramSize : + Binding->RealMaxDatagramSize; + u.IpxAddressData->linkspeed = Binding->MediumSpeed; +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } else { + + Status = STATUS_INVALID_PARAMETER; + } + + } else { +#if 1 + // + // Support the old format query for now. + // + + typedef struct _IPX_OLD_ADDRESS_DATA { + UINT adapternum; + UCHAR netnum[4]; + UCHAR nodenum[6]; + } IPX_OLD_ADDRESS_DATA, *PIPX_OLD_ADDRESS_DATA; + + if (DataLength >= sizeof(IPX_OLD_ADDRESS_DATA)) { + u.IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + AdapterNum = u.IpxAddressData->adapternum+1; + + if ((AdapterNum >= 1) && (AdapterNum <= Device->SapNicCount)) { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, AdapterNum)) { + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[AdapterNum]) { + *(UNALIGNED ULONG *)u.IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(u.IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_INVALID_PARAMETER; + } + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } +#else + Status = STATUS_BUFFER_TOO_SMALL; +#endif + } + break; + + case MIPX_NOTIFYCARDINFO: + + // + // IPX_ADDRESS_NOTIFY. + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_NOTIFYCARDINFO (%lx)\n", AddressFile, Request)); + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // If the device is open and there is room in the + // buffer for the data, insert it in our queue. + // It will be completed when a change happens or + // the driver is unloaded. + // + + if (Device->State == DEVICE_STATE_OPEN) { + if (DataLength >= sizeof(IPX_ADDRESS_DATA)) { + InsertTailList( + &Device->AddressNotifyQueue, + REQUEST_LINKAGE(Request) + ); + IoSetCancelRoutine (Request, IpxCancelAction); + if (Request->Cancel) { + (VOID)RemoveTailList (&Device->AddressNotifyQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + Status = STATUS_CANCELLED; + } else { + IpxReferenceDevice (Device, DREF_ADDRESS_NOTIFY); + Status = STATUS_PENDING; + } + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + } else { + Status = STATUS_DEVICE_NOT_READY; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_LINECHANGE: + + // + // IPX_ADDRESS_NOTIFY. + // + + IPX_DEBUG (ACTION, ("MIPX_LINECHANGE (%lx)\n", Request)); + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // If the device is open and there is room in the + // buffer for the data, insert it in our queue. + // It will be completed when a change happens or + // the driver is unloaded. + // + + if (Device->State == DEVICE_STATE_OPEN) { + + InsertTailList( + &Device->LineChangeQueue, + REQUEST_LINKAGE(Request) + ); + + IoSetCancelRoutine (Request, IpxCancelAction); + if (Request->Cancel) { + (VOID)RemoveTailList (&Device->LineChangeQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + Status = STATUS_CANCELLED; + } else { + IpxReferenceDevice (Device, DREF_LINE_CHANGE); + Status = STATUS_PENDING; + } + } else { + Status = STATUS_DEVICE_NOT_READY; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + break; + + case MIPX_GETNETINFO_NR: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // A query on network 0 means that the caller wants + // information about our directly attached net. + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + // + // The tick count is the number of 1/18.21 second ticks + // it takes to deliver a 576-byte packet. Our link speed + // is in 100 bit-per-second units. We calculate it as + // follows (LS is the LinkSpeed): + // + // 576 bytes 8 bits 1 second 1821 ticks + // * ------ * ------------- * ---------- + // 1 byte LS * 100 bits 100 seconds + // + // which becomes 839 / LinkSpeed -- we add LinkSpeed + // to the top to round up. + // + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + + +#ifdef _PNP_POWER + Segment = RipGetSegment(u.IpxNetnumData->netnum); + + // + // To maintain the lock order: BindAccessLock > RIP table + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // Fail the call, we don't have a route yet. + // + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO_NR failed net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + + } + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +#else + Segment = RipGetSegment(u.IpxNetnumData->netnum); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId])) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // Fail the call, we don't have a route yet. + // + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO_NR failed net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + Status = STATUS_BAD_NETWORK_PATH; + + } + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + + } + + break; + + case MIPX_RERIPNETNUM: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // BUGBUG: Allow net 0 queries?? + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + + Segment = RipGetSegment(u.IpxNetnumData->netnum); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_RERIPNETNUM queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId]) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_RERIPNETNUM queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } + + break; + + case MIPX_GETNETINFO: + + // + // A request for network information about the immediate + // route to a network (this is called by sockets apps). + // + + if (DataLength < sizeof(IPX_NETNUM_DATA)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.IpxNetnumData = (PIPX_NETNUM_DATA)(NwlinkAction->Data); + + // + // BUGBUG: Allow net 0 queries?? + // + + if (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum == 0) { + + if (Device->LinkSpeed == 0) { + u.IpxNetnumData->netdelay = 16; + } else { + u.IpxNetnumData->netdelay = (USHORT)((839 + Device->LinkSpeed) / + (Device->LinkSpeed)); + } + u.IpxNetnumData->hopcount = 0; + u.IpxNetnumData->cardnum = 0; + RtlMoveMemory (u.IpxNetnumData->router, Device->SourceAddress.NodeAddress, 6); + + } else { + + Segment = RipGetSegment(u.IpxNetnumData->netnum); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(MIN (Device->MaxBindings, Binding->MasterBinding->NicId) - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, u.IpxNetnumData->netnum); + + if ((RouteEntry != NULL) && + (Binding = Device->Bindings[RouteEntry->NicId])) { + + u.IpxNetnumData->hopcount = RouteEntry->HopCount; + u.IpxNetnumData->netdelay = RouteEntry->TickCount; + + if (Binding->BindingSetMember) { + u.IpxNetnumData->cardnum = (INT)(Binding->MasterBinding->NicId - 1); + } else { + u.IpxNetnumData->cardnum = (INT)(RouteEntry->NicId - 1); + } + RtlMoveMemory (u.IpxNetnumData->router, RouteEntry->NextRouter, 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (*(UNALIGNED ULONG *)u.IpxNetnumData->netnum, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.IpxNetnumData; + InsertTailList( + &Device->Segments[Segment].WaitingReripNetnum, + REQUEST_LINKAGE(Request)); + + IPX_DEBUG (ACTION, ("MIPX_GETNETINFO queued net %lx\n", + REORDER_ULONG(*(UNALIGNED ULONG *)(u.IpxNetnumData->netnum)))); + + } + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } + + break; + + case MIPX_SENDPTYPE: + case MIPX_NOSENDPTYPE: + + // + // For the moment just use OptionsLength >= 1 to indicate + // that the send options include the packet type. + // + // BUGBUG: Do we need to worry about card num being there? + // + +#if 0 + IPX_DEBUG (ACTION, ("%lx: MIPS_%sSENDPTYPE\n", AddressFile, + NwlinkAction->Option == MIPX_SENDPTYPE ? "" : "NO")); +#endif + break; + + case MIPX_ZEROSOCKET: + + // + // Sends from this address should be from socket 0; + // This is done the simple way by just putting the + // information in the address itself, instead of + // making it per address file (this is OK since + // this call is not exposed through winsock). + // + + IPX_DEBUG (ACTION, ("%lx: MIPX_ZEROSOCKET\n", AddressFile)); + AddressFile->Address->SendSourceSocket = 0; + AddressFile->Address->LocalAddress.Socket = 0; + break; + + + // + // This next batch are the source routing options. They + // are submitted by the IPXROUTE program. + // + // BUGBUG: Do we expose all binding set members to this? + + case MIPX_SRGETPARMS: + + if (DataLength >= sizeof(SR_GET_PARAMETERS)) { + u.GetSrParameters = (PSR_GET_PARAMETERS)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.GetSrParameters->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRGETPARMS (%d)\n", u.GetSrParameters->BoardNumber+1)); + u.GetSrParameters->SrDefault = (Binding->AllRouteDirected) ? 1 : 0; + u.GetSrParameters->SrBroadcast = (Binding->AllRouteBroadcast) ? 1 : 0; + u.GetSrParameters->SrMulticast = (Binding->AllRouteMulticast) ? 1 : 0; + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.GetSrParameters->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRGETPARMS (%d)\n", u.GetSrParameters->BoardNumber+1)); + u.GetSrParameters->SrDefault = (Binding->AllRouteDirected) ? 1 : 0; + u.GetSrParameters->SrBroadcast = (Binding->AllRouteBroadcast) ? 1 : 0; + u.GetSrParameters->SrMulticast = (Binding->AllRouteMulticast) ? 1 : 0; + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRDEF: + case MIPX_SRBCAST: + case MIPX_SRMULTI: + + if (DataLength >= sizeof(SR_SET_PARAMETER)) { + u.SetSrParameter = (PSR_SET_PARAMETER)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrParameter->BoardNumber+1)) { + if (NwlinkAction->Option == MIPX_SRDEF) { + + // + // BUGBUG: The compiler generates strange + // code which always makes this path be + // taken???? + // + + IPX_DEBUG (ACTION, ("MIPX_SRDEF %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteDirected = (BOOLEAN)u.SetSrParameter->Parameter; + + } else if (NwlinkAction->Option == MIPX_SRBCAST) { + + IPX_DEBUG (ACTION, ("MIPX_SRBCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteBroadcast = (BOOLEAN)u.SetSrParameter->Parameter; + + } else { + + IPX_DEBUG (ACTION, ("MIPX_SRMCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteMulticast = (BOOLEAN)u.SetSrParameter->Parameter; + + } + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrParameter->BoardNumber+1]) { + if (NwlinkAction->Option == MIPX_SRDEF) { + + // + // BUGBUG: The compiler generates strange + // code which always makes this path be + // taken???? + // + + IPX_DEBUG (ACTION, ("MIPX_SRDEF %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteDirected = (BOOLEAN)u.SetSrParameter->Parameter; + + } else if (NwlinkAction->Option == MIPX_SRBCAST) { + + IPX_DEBUG (ACTION, ("MIPX_SRBCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteBroadcast = (BOOLEAN)u.SetSrParameter->Parameter; + + } else { + + IPX_DEBUG (ACTION, ("MIPX_SRMCAST %d (%d)\n", + u.SetSrParameter->Parameter, u.SetSrParameter->BoardNumber+1)); + Binding->AllRouteMulticast = (BOOLEAN)u.SetSrParameter->Parameter; + + } + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRREMOVE: + + if (DataLength >= sizeof(SR_SET_REMOVE)) { + u.SetSrRemove = (PSR_SET_REMOVE)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrRemove->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRREMOVE %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x (%d)\n", + u.SetSrRemove->MacAddress[0], + u.SetSrRemove->MacAddress[1], + u.SetSrRemove->MacAddress[2], + u.SetSrRemove->MacAddress[3], + u.SetSrRemove->MacAddress[4], + u.SetSrRemove->MacAddress[5], + u.SetSrRemove->BoardNumber+1)); + MacSourceRoutingRemove (Binding, u.SetSrRemove->MacAddress); + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrRemove->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRREMOVE %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x (%d)\n", + u.SetSrRemove->MacAddress[0], + u.SetSrRemove->MacAddress[1], + u.SetSrRemove->MacAddress[2], + u.SetSrRemove->MacAddress[3], + u.SetSrRemove->MacAddress[4], + u.SetSrRemove->MacAddress[5], + u.SetSrRemove->BoardNumber+1)); + MacSourceRoutingRemove (Binding, u.SetSrRemove->MacAddress); + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MIPX_SRCLEAR: + + if (DataLength >= sizeof(SR_SET_CLEAR)) { + u.SetSrClear = (PSR_SET_CLEAR)(NwlinkAction->Data); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (Binding = NIC_ID_TO_BINDING(Device, u.SetSrClear->BoardNumber+1)) { + + IPX_DEBUG (ACTION, ("MIPX_SRCLEAR (%d)\n", u.SetSrClear->BoardNumber+1)); + MacSourceRoutingClear (Binding); + + } else { + Status = STATUS_INVALID_PARAMETER; + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (Binding = Device->Bindings[u.SetSrClear->BoardNumber+1]) { + + IPX_DEBUG (ACTION, ("MIPX_SRCLEAR (%d)\n", u.SetSrClear->BoardNumber+1)); + MacSourceRoutingClear (Binding); + + } else { + Status = STATUS_INVALID_PARAMETER; + } +#endif + } else { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + + // + // These are new for ISN (not supported in NWLINK). + // + + case MIPX_LOCALTARGET: + + // + // A request for the local target for an IPX address. + // + + if (DataLength < sizeof(ISN_ACTION_GET_LOCAL_TARGET)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)(NwlinkAction->Data); + Segment = RipGetSegment((PUCHAR)&u.GetLocalTarget->IpxAddress.NetworkAddress); + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See if this route is local. + // + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&u.GetLocalTarget->IpxAddress.NetworkAddress); + + if ((RouteEntry != NULL) && + (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY)) { + + // + // This is a local net, to send to it you just use + // the appropriate NIC ID and the real MAC address. + // + + if ((RouteEntry->Flags & IPX_ROUTER_LOCAL_NET) == 0) { + + // + // It's the virtual net, send via the first card. + // +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, 1); +#else + u.GetLocalTarget->LocalTarget.NicId = 1; +#endif + + } else { + +#ifdef _PNP_POWER + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId); + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); + + } else { + + FILL_LOCAL_TARGET(&u.GetLocalTarget->LocalTarget, RouteEntry->NicId); + + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[RouteEntry->NicId]; + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + u.GetLocalTarget->LocalTarget.NicId = Binding->NicId; + } else { + + u.GetLocalTarget->LocalTarget.NicId = RouteEntry->NicId; + + } +#endif + + } + + RtlCopyMemory( + u.GetLocalTarget->LocalTarget.MacAddress, + u.GetLocalTarget->IpxAddress.NodeAddress, + 6); + + } else { + + // + // This call will return STATUS_PENDING if we successfully + // queue a RIP request for the packet. + // + + Status = RipQueueRequest (u.GetLocalTarget->IpxAddress.NetworkAddress, RIP_REQUEST); + CTEAssert (Status != STATUS_SUCCESS); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this request for completion when the RIP response + // arrives. We save the network in the information + // field for easier retrieval later. + // + + REQUEST_INFORMATION(Request) = (ULONG)u.GetLocalTarget; + InsertTailList( + &Device->Segments[Segment].WaitingLocalTarget, + REQUEST_LINKAGE(Request)); + + } + +#ifdef _PNP_POWER + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + } +#ifndef _PNP_POWER + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#endif + + + break; + + case MIPX_NETWORKINFO: + + // + // A request for network information about the immediate + // route to a network. + // + + if (DataLength < sizeof(ISN_ACTION_GET_NETWORK_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetNetworkInfo = (PISN_ACTION_GET_NETWORK_INFO)(NwlinkAction->Data); + + if (u.GetNetworkInfo->Network == 0) { + + // + // This is information about the local card. + // + + u.GetNetworkInfo->LinkSpeed = Device->LinkSpeed * 12; + u.GetNetworkInfo->MaximumPacketSize = Device->Information.MaxDatagramSize; + + } else { + + Segment = RipGetSegment((PUCHAR)&u.GetNetworkInfo->Network); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // See which net card this is routed on. + // + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&u.GetNetworkInfo->Network); + + if ((RouteEntry != NULL) && +#ifdef _PNP_POWER + (Binding = NIC_ID_TO_BINDING(Device, RouteEntry->NicId))) { +#else + (Binding = Device->Bindings[RouteEntry->NicId])) { +#endif + + // + // Our medium speed is stored in 100 bps, we + // convert to bytes/sec by multiplying by 12 + // (should really be 100/8 = 12.5). + // + + u.GetNetworkInfo->LinkSpeed = Binding->MediumSpeed * 12; + u.GetNetworkInfo->MaximumPacketSize = Binding->AnnouncedMaxDatagramSize; + + } else { + + // + // Fail the call, we don't have a route yet. + // BUGBUG: This requires that a packet has been + // sent to this net already; nwrdr says this is + // OK, they will send their connect request + // before they query. On the server it should + // have RIP running so all nets should be in + // the database. + // + + Status = STATUS_BAD_NETWORK_PATH; + + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + + break; + + case MIPX_CONFIG: + + // + // A request for details on every binding. + // + + if (DataLength < sizeof(ISN_ACTION_GET_DETAILS)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetDetails = (PISN_ACTION_GET_DETAILS)(NwlinkAction->Data); + + if (u.GetDetails->NicId == 0) { + + // + // This is information about the local card. We also + // tell him the total number of bindings in NicId. + // + + u.GetDetails->NetworkNumber = Device->VirtualNetworkNumber; + u.GetDetails->NicId = (USHORT)MIN (Device->MaxBindings, Device->ValidBindings); + + } else { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, u.GetDetails->NicId); +#else + Binding = Device->Bindings[u.GetDetails->NicId]; +#endif + + if ((Binding != NULL) && + (u.GetDetails->NicId <= MIN (Device->MaxBindings, Device->ValidBindings))) { + + ULONG StringLoc; +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + u.GetDetails->NetworkNumber = Binding->LocalAddress.NetworkAddress; + if (Binding->Adapter->MacInfo.MediumType == NdisMediumArcnet878_2) { + u.GetDetails->FrameType = ISN_FRAME_TYPE_ARCNET; + } else { + u.GetDetails->FrameType = Binding->FrameType; + } + u.GetDetails->BindingSet = Binding->BindingSetMember; + if (Binding->Adapter->MacInfo.MediumAsync) { + if (Binding->LineUp) { + u.GetDetails->Type = 2; + } else { + u.GetDetails->Type = 3; + } + } else { + u.GetDetails->Type = 1; + } + + RtlCopyMemory (u.GetDetails->Node, Binding->LocalMacAddress.Address, 6); + + // + // Copy the adapter name, including the final NULL. + // + + StringLoc = (Binding->Adapter->AdapterNameLength / sizeof(WCHAR)) - 2; + while (Binding->Adapter->AdapterName[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory( + u.GetDetails->AdapterName, + &Binding->Adapter->AdapterName[StringLoc+1], + Binding->Adapter->AdapterNameLength - ((StringLoc+1) * sizeof(WCHAR))); + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } else { + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + Status = STATUS_INVALID_PARAMETER; + + } + } + + break; + + + // + // The Option was not supported, so fail. + // + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (!NT_SUCCESS(Status)) { + IPX_DEBUG (ACTION, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); + } +#endif + + return Status; + +} /* IpxTdiAction */ + + +VOID +IpxCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel an Action. + What is done to cancel it is specific to each action. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PREQUEST Request = (PREQUEST)Irp; + CTELockHandle LockHandle; + PLIST_ENTRY p; + BOOLEAN Found; + UINT IOCTLType; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + // + // Find the request on the address notify queue. + // + + Found = FALSE; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (p = Device->AddressNotifyQueue.Flink; + p != &Device->AddressNotifyQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + IOCTLType = MIPX_NOTIFYCARDINFO; + break; + } + } + + if (!Found) { + for (p = Device->LineChangeQueue.Flink; + p != &Device->LineChangeQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + IOCTLType = MIPX_LINECHANGE; + break; + } + } + } + + CTEFreeLock (&Device->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + IpxCompleteRequest (Request); + IpxFreeRequest(Device, Request); + if (IOCTLType == MIPX_NOTIFYCARDINFO) { + IPX_DEBUG(ACTION, ("Cancelled action NOTIFYCARDINFO %lx\n", Request)); + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } else { + IPX_DEBUG(ACTION, ("Cancelled action LINECHANGE %lx\n", Request)); + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + } + + } +#if DBG + else { + IPX_DEBUG(ACTION, ("Cancelled action orphan %lx\n", Request)); + } +#endif + +} /* IpxCancelAction */ + + +VOID +IpxAbortLineChanges( + IN PVOID ControlChannelContext + ) + +/*++ + +Routine Description: + + This routine aborts any line change IRPs posted by the + control channel with the specified open context. It is + called when a control channel is being shut down. + +Arguments: + + ControlChannelContext - The context assigned to the control + channel when it was opened. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = IpxDevice; + CTELockHandle LockHandle; + LIST_ENTRY AbortList; + PLIST_ENTRY p; + PREQUEST Request; + KIRQL irql; + + + InitializeListHead (&AbortList); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock (&Device->Lock, &LockHandle); + + p = Device->LineChangeQueue.Flink; + + while (p != &Device->LineChangeQueue) { + LARGE_INTEGER ControlChId; + + Request = LIST_ENTRY_TO_REQUEST(p); + + CCID_FROM_REQUEST(ControlChId, Request); + + p = p->Flink; + + if (ControlChId.QuadPart == ((PLARGE_INTEGER)ControlChannelContext)->QuadPart) { + RemoveEntryList (REQUEST_LINKAGE(Request)); + InsertTailList (&AbortList, REQUEST_LINKAGE(Request)); + } + } + + while (!IsListEmpty (&AbortList)) { + + p = RemoveHeadList (&AbortList); + Request = LIST_ENTRY_TO_REQUEST(p); + + IPX_DEBUG(ACTION, ("Aborting line change %lx\n", Request)); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + + IpxCompleteRequest (Request); + IpxFreeRequest(Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock(&Device->Lock, &LockHandle); + } + + CTEFreeLock(&Device->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); +} /* IpxAbortLineChanges */ + diff --git a/private/ntos/tdi/isnp/ipx/adapter.c b/private/ntos/tdi/isnp/ipx/adapter.c new file mode 100644 index 000000000..479570e48 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/adapter.c @@ -0,0 +1,636 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + adapter.c + +Abstract: + + This module contains code which implements the ADAPTER object. + Routines are provided to reference, and dereference transport + adapter objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// These are init only until binding is really dynamic. +// +#ifndef _PNP_POWER +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxCreateAdapter) +#endif +#endif _PNP_POWER + + + +VOID +IpxRefBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Binding - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Binding->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Binding->ReferenceCount); + +} /* IpxRefBinding */ + + +VOID +IpxDerefBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Binding - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Binding->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + IpxDestroyBinding (Binding); + } + +} /* IpxDerefBinding */ + + +NTSTATUS +IpxCreateAdapter( + IN PDEVICE Device, + IN PUNICODE_STRING AdapterName, + IN OUT PADAPTER *AdapterPtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Adapter - Pointer to a pointer to a transport device context object. + + AdapterName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + PADAPTER Adapter; +#if 0 + UINT i, j; +#endif + + Adapter = (PADAPTER)IpxAllocateMemory (sizeof(ADAPTER) + AdapterName->Length + sizeof(WCHAR), MEMORY_ADAPTER, "Adapter"); + +#ifdef _PNP_POWER + if (Adapter == NULL) { + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create adapter %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create adapter %lx failed\n", AdapterName)); + } + return STATUS_INSUFFICIENT_RESOURCES; + } + + IPX_DEBUG (ADAPTER, ("Create adapter %lx %lx succeeded\n", Adapter, AdapterName)); +#else + if (Adapter == NULL) { + IPX_DEBUG (ADAPTER, ("Create adapter %ws failed\n", AdapterName->Buffer)); + return STATUS_INSUFFICIENT_RESOURCES; + } + + IPX_DEBUG (ADAPTER, ("Create adapter %ws succeeded\n", AdapterName->Buffer)); +#endif + + RtlZeroMemory(Adapter, sizeof(ADAPTER)); + + // + // Copy over the adapter name. + // + + Adapter->AdapterNameLength = AdapterName->Length + sizeof(WCHAR); + Adapter->AdapterName = (PWCHAR)(Adapter+1); + RtlCopyMemory( + Adapter->AdapterName, + AdapterName->Buffer, + AdapterName->Length); + Adapter->AdapterName[AdapterName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + +#if DBG + RtlCopyMemory(Adapter->Signature1, "IAD1", 4); +#endif + + Adapter->Type = IPX_ADAPTER_SIGNATURE; + Adapter->Size = sizeof(ADAPTER); + + CTEInitLock (&Adapter->Lock); + + InitializeListHead (&Adapter->RequestCompletionQueue); + + InitializeListHead (&Adapter->ReceiveBufferPoolList); + + ExInitializeSListHead (&Adapter->ReceiveBufferList); + + Adapter->Device = Device; + Adapter->DeviceLock = &Device->Lock; + IpxReferenceDevice (Device, DREF_ADAPTER); + +#if 0 + Adapter->ReceiveBufferPool.Next = NULL; + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Adapter->Bindings[i] = NULL; + } + Adapter->BindingCount = 0; + + for (i = 0; i < IDENTIFIER_TOTAL; i++) { + for (j = 0; j < SOURCE_ROUTE_HASH_SIZE; j++) { + Adapter->SourceRoutingHeads[i][j] = (PSOURCE_ROUTE)NULL; + } + } +#endif + + // + // BUGBUG: For the moment, we have to do the source + // routing operation on any type where broadcast + // may not be used for discovery -- improve this + // hopefully. + // + + Adapter->SourceRoutingEmpty[IDENTIFIER_RIP] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_IPX] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_SPX] = FALSE; + Adapter->SourceRoutingEmpty[IDENTIFIER_NB] = TRUE; + +#ifdef _PNP_POWER + // + // Lock here? [BUGBUGZZ] + // + Adapter->ReferenceCount = 1; +#endif + + *AdapterPtr = Adapter; + + return STATUS_SUCCESS; + +} /* IpxCreateAdapter */ + + +VOID +IpxDestroyAdapter( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Adapter - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ULONG Database, Hash; + PSOURCE_ROUTE Current; + ULONG ReceiveBufferPoolSize; + PIPX_RECEIVE_BUFFER ReceiveBuffer; + PIPX_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PDEVICE Device = Adapter->Device; + PLIST_ENTRY p; + UINT i; + + IPX_DEBUG (ADAPTER, ("Destroy adapter %lx\n", Adapter)); + + // + // Free any receive buffer pools this adapter has. + // + + ReceiveBufferPoolSize = FIELD_OFFSET (IPX_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(IPX_RECEIVE_BUFFER) * Device->InitReceiveBuffers) + + (Adapter->MaxReceivePacketSize * Device->InitReceiveBuffers); + + while (!IsListEmpty (&Adapter->ReceiveBufferPoolList)) { + + p = RemoveHeadList (&Adapter->ReceiveBufferPoolList); + ReceiveBufferPool = CONTAINING_RECORD (p, IPX_RECEIVE_BUFFER_POOL, Linkage); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + IpxDeinitializeReceiveBuffer (Adapter, ReceiveBuffer, Adapter->MaxReceivePacketSize); + + } + + IPX_DEBUG (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + IpxFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + } + + // + // Free all the source routing information for this adapter. + // + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + for (Hash = 0; Hash < SOURCE_ROUTE_HASH_SIZE; Hash++) { + + while (Adapter->SourceRoutingHeads[Database][Hash]) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + } + } + } + + IpxDereferenceDevice (Adapter->Device, DREF_ADAPTER); + IpxFreeMemory (Adapter, sizeof(ADAPTER) + Adapter->AdapterNameLength, MEMORY_ADAPTER, "Adapter"); + +} /* IpxDestroyAdapter */ + + +NTSTATUS +IpxCreateBinding( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding OPTIONAL, + IN ULONG NetworkNumberIndex, + IN PWCHAR AdapterName, + IN OUT PBINDING *BindingPtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a binding structure. + +Arguments: + + Device - The device. + + ConfigBinding - Information about this binding. If this is + NULL then this is a WAN binding and all the relevant + information will be filled in by the caller. + + NetworkNumberIndex - The index in the frame type array for + ConfigBinding indicating which frame type this binding is for. + Not used if ConfigBinding is not provided. + + AdapterName - Used for error logging. + + BindingPtr - Returns the allocated binding structure. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + PBINDING Binding; +#ifdef _PNP_POWER + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + if (s != NULL) { + goto GotBinding; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopBinding(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN failed\n")); + } +#endif + return STATUS_INSUFFICIENT_RESOURCES; + } + +GotBinding: + + Binding = CONTAINING_RECORD (s, BINDING, PoolLinkage); + +#else + Binding = (PBINDING)IpxAllocateMemory (sizeof(BINDING), MEMORY_ADAPTER, "Binding"); + + // + // We can't vsprintf a %ws at DPC level, so we check for + // that. Only WAN bindings will be created then. + // + + if (Binding == NULL) { +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws failed\n", AdapterName)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN failed\n")); + } +#endif + return STATUS_INSUFFICIENT_RESOURCES; + } +#endif + +#if DBG + if (KeGetCurrentIrql() == 0) { + IPX_DEBUG (ADAPTER, ("Create binding %ws succeeded, %lx\n", AdapterName, Binding)); + } else { + IPX_DEBUG (ADAPTER, ("Create binding WAN succeeded\n")); + } +#endif + + RtlZeroMemory(Binding, sizeof(BINDING)); + + // + // Initialize the reference count. + // + + Binding->ReferenceCount = 1; +#if DBG + Binding->RefTypes[BREF_BOUND] = 1; +#endif + +#if DBG + RtlCopyMemory(Binding->Signature1, "IBI1", 4); +#endif + + Binding->Type = IPX_BINDING_SIGNATURE; + Binding->Size = sizeof(BINDING); + + Binding->Device = Device; + Binding->DeviceLock = &Device->Lock; + + if (ConfigBinding != NULL) { + + ULONG Temp = ConfigBinding->NetworkNumber[NetworkNumberIndex]; + Binding->ConfiguredNetworkNumber = REORDER_ULONG (Temp); + + Binding->AutoDetect = ConfigBinding->AutoDetect[NetworkNumberIndex]; + Binding->DefaultAutoDetect = ConfigBinding->DefaultAutoDetect[NetworkNumberIndex]; + + Binding->AllRouteDirected = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_DEF]; + Binding->AllRouteBroadcast = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_BC]; + Binding->AllRouteMulticast = (BOOLEAN)ConfigBinding->Parameters[BINDING_ALL_ROUTE_MC]; + + } + + Binding->ReceiveBroadcast = TRUE; +#if 0 + Binding->BindingSetMember = FALSE; + Binding->NextBinding = (PBINDING)NULL; + Binding->DialOutAsync = FALSE; +#endif + + // + // We set Binding->FrameType later, after we can map it based on the + // media type of the adapter we bind to. + // + + *BindingPtr = Binding; + + return STATUS_SUCCESS; + +} /* IpxCreateBinding */ + + +VOID +IpxDestroyBinding( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine destroys a binding structure. + +Arguments: + + Binding - Pointer to a transport binding structure. + +Return Value: + + None. + +--*/ + +{ + IPX_DEBUG (ADAPTER, ("Destroy binding %lx\n", Binding)); + +#ifdef _PNP_POWER + + IPX_PUSH_ENTRY_LIST( + &IpxDevice->BindingList, + &Binding->PoolLinkage, + &IpxDevice->SListsLock); +#else + IpxFreeMemory (Binding, sizeof(BINDING), MEMORY_ADAPTER, "Binding"); +#endif + +} /* IpxDestroyBinding */ + + +#ifdef _PNP_POWER +VOID +IpxAllocateBindingPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 bindings to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_BINDING_POOL BindingPool; + UINT BindingPoolSize; + UINT BindingNum; + PBINDING Binding; + CTELockHandle LockHandle; + + BindingPoolSize = FIELD_OFFSET (IPX_BINDING_POOL, Bindings[0]) + + (sizeof(BINDING) * Device->InitBindings); + + BindingPool = (PIPX_BINDING_POOL)IpxAllocateMemory (BindingPoolSize, MEMORY_PACKET, "BindingPool"); + + if (BindingPool == NULL) { + IPX_DEBUG (PNP, ("Could not allocate binding pool memory\n")); + return; + } + + + IPX_DEBUG (PNP, ("Initializing Binding pool %lx, %d bindings\n", + BindingPool, Device->InitBindings)); + + BindingPool->BindingCount = Device->InitBindings; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (BindingNum = 0; BindingNum < BindingPool->BindingCount; BindingNum++) { + + Binding = &BindingPool->Bindings[BindingNum]; + IPX_PUSH_ENTRY_LIST (&Device->BindingList, &Binding->PoolLinkage, &Device->SListsLock); + +#ifdef IPX_TRACK_POOL + Binding->Pool = BindingPool; +#endif + } + + InsertTailList (&Device->BindingPoolList, &BindingPool->Linkage); + + Device->AllocatedBindings += BindingPool->BindingCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateBindingPool */ + + +PSINGLE_LIST_ENTRY +IpxPopBinding( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a binding from the device context's pool. + If there are no bindings in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated binding. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedBindings < Device->MaxPoolBindings) { + + // + // Allocate a pool and try again. + // + + IpxAllocateBindingPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->BindingList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopBinding */ +#endif diff --git a/private/ntos/tdi/isnp/ipx/address.c b/private/ntos/tdi/isnp/ipx/address.c new file mode 100644 index 000000000..1049bc8de --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/address.c @@ -0,0 +1,1843 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Map all generic accesses to the same one. +// + +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + +TDI_ADDRESS_IPX UNALIGNED * +IpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_IPX. + +Arguments: + + Transport - The generic TDI address. + +Return Value: + + A pointer to the IPX address, or NULL if none is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the IPX one. + // + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) { + if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) { + return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* IpxParseTdiAddress */ + + +BOOLEAN +IpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + IpxPrint0 ("IpxValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + IpxPrint0 ("IpxValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + IpxPrint0 ("IpxValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* IpxValidateTdiAddress */ + +#if DBG + +VOID +IpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG Network, + IN UCHAR Node[6], + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine fills in a TRANSPORT_ADDRESS in the specified + buffer, given the socket, network and node. + +Arguments: + + AddressBuffer - The buffer that will hold the address. + + Network - The network number. + + Node - The node address. + + Socket - The socket. + +Return Value: + + None. + +--*/ + +{ + TA_IPX_ADDRESS UNALIGNED * IpxAddress; + + IpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + + IpxAddress->TAAddressCount = 1; + IpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + IpxAddress->Address[0].Address[0].NetworkAddress = Network; + IpxAddress->Address[0].Address[0].Socket = Socket; + RtlCopyMemory(IpxAddress->Address[0].Address[0].NodeAddress, Node, 6); + +} /* IpxBuildTdiAddress */ +#endif + + +NTSTATUS +IpxOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + Device - pointer to the device describing the IPX transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED *AddressName; + USHORT Socket; + ULONG DesiredShareAccess; + CTELockHandle LockHandle; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // + // If we are a dedicated router, we cannot let addresses + // be opened. + // + + if (Device->DedicatedRouter) { + return STATUS_NOT_SUPPORTED; + } + + // + // The network name is in the EA, passed in the request. + // + + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) { + IpxPrint1("OpenAddress: REQUEST %lx has no EA\n", Request); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + AddressName = (PTA_ADDRESS)&name->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one of type IPX. + // + + for (i=0;i<name->TAAddressCount;i++) { + if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) { + if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) { + Socket = ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket; + found = TRUE; + } + break; + + } else { + + AddressName = (PTA_ADDRESS)(AddressName->Address + + AddressName->AddressLength); + + } + + } + + if (!found) { + IPX_DEBUG (ADDRESS, ("OpenAddress, request %lx has no IPX Address\n", Request)); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + if (Socket == 0) { + + Socket = IpxAssignSocket (Device); + + if (Socket == 0) { + IPX_DEBUG (ADDRESS, ("OpenAddress, no unique socket found\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } else { + IPX_DEBUG (ADDRESS, ("OpenAddress, assigned socket %lx\n", REORDER_USHORT(Socket))); + } + + } else { + + IPX_DEBUG (ADDRESS, ("OpenAddress, socket %lx\n", REORDER_USHORT(Socket))); + + } + + // + // get an address file structure to represent this address. + // + + AddressFile = IpxCreateAddressFile (Device); + + if (AddressFile == (PADDRESS_FILE)NULL) { + return status; + } + + // + // We mark this socket specially. + // + + if (Socket == SAP_SOCKET) { + AddressFile->IsSapSocket = TRUE; + AddressFile->SpecialReceiveProcessing = TRUE; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); + + CTEGetLock (&Device->Lock, &LockHandle); + + Address = IpxLookupAddress (Device, Socket); + + if (Address == NULL) { + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // This address doesn't exist. Create it. + // registering it. + // + + Address = IpxCreateAddress ( + Device, + Socket); + + if (Address != (PADDRESS)NULL) { + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + +#ifdef ISN_NT + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &Address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess); + ExReleaseResource (&Device->AddressResource); + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + return status; + + } + +#endif + + ExReleaseResource (&Device->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // + + if (Device->State == DEVICE_STATE_STOPPING) { + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DEVICE_NOT_READY; + + } else { + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->Request = Request; + AddressFile->Address = Address; + + CTEGetLock (&Address->Lock, &LockHandle); + InsertTailList (&Address->AddressFileDatabase, &AddressFile->Linkage); + CTEFreeLock (&Address->Lock, LockHandle); + + AddressFile->Request = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } + + } else { + + ExReleaseResource (&Device->AddressResource); + + // + // If the address could not be created, and is not in the + // process of being created, then we can't open up an address. + // Since we can't use the AddressLock to deref, we just destroy + // the address file. + // + + IpxDestroyAddressFile (AddressFile); + + } + + } else { + + CTEFreeLock (&Device->Lock, LockHandle); + + IPX_DEBUG (ADDRESS, ("Add to address %lx\n", Address)); + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + Address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) { + + ExReleaseResource (&Device->AddressResource); + + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + +#ifdef ISN_NT + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&Device->AddressResource); + + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + ExReleaseResource (&Device->AddressResource); + + CTEGetLock (&Address->Lock, &LockHandle); + + InsertTailList ( + &Address->AddressFileDatabase, + &AddressFile->Linkage); + + AddressFile->Request = NULL; + AddressFile->Address = Address; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->State = ADDRESSFILE_STATE_OPEN; + + IpxReferenceAddress (Address, AREF_ADDRESS_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + CTEFreeLock (&Address->Lock, LockHandle); + + status = STATUS_SUCCESS; + + } + } + + // + // Remove the reference from IpxLookupAddress. + // + + IpxDereferenceAddress (Address, AREF_LOOKUP); + } + + return status; + +} /* IpxOpenAddress */ + + +USHORT +IpxAssignSocket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine assigns a socket that is unique within a range + of SocketUniqueness. + +Arguments: + + Device - Pointer to the device context. + +Return Value: + + The assigned socket number, or 0 if a unique one cannot + be found. + +--*/ + +{ + USHORT InitialSocket, CurrentSocket, AddressSocket; + ULONG CurrentHash; + BOOLEAN Conflict; + PLIST_ENTRY p; + PADDRESS Address; + CTELockHandle LockHandle; + + // + // Loop through all possible sockets, starting at + // Device->CurrentSocket, looking for a suitable one. + // Device->CurrentSocket rotates through the possible + // sockets to improve the chances of finding one + // quickly. + // + + CTEGetLock (&Device->Lock, &LockHandle); + + InitialSocket = Device->CurrentSocket; + Device->CurrentSocket = (USHORT)(Device->CurrentSocket + Device->SocketUniqueness); + if ((USHORT)(Device->CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) { + Device->CurrentSocket = Device->SocketStart; + } + + CurrentSocket = InitialSocket; + + do { + + // + // Scan all addresses; if we find one with a socket + // that conflicts with this one, we can't use it. + // + // NOTE: Device->Lock is acquired here. + // + + Conflict = FALSE; + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + AddressSocket = REORDER_USHORT(Address->Socket); + + if ((AddressSocket + Device->SocketUniqueness > CurrentSocket) && + (AddressSocket < CurrentSocket + Device->SocketUniqueness)) { + Conflict = TRUE; + break; + } + } + + // + // If we've found a conflict, no need to check the other + // queues. + // + + if (Conflict) { + break; + } + } + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // We intentionally free the lock here so that we + // never spend too much time with it held. + // + + if (!Conflict) { + + // + // We went through the address list without + // finding a conflict; use this socket. + // + + return REORDER_USHORT(CurrentSocket); + } + + CurrentSocket = (USHORT)(CurrentSocket + Device->SocketUniqueness); + if ((USHORT)(CurrentSocket+Device->SocketUniqueness) > Device->SocketEnd) { + CurrentSocket = Device->SocketStart; + } + + CTEGetLock (&Device->Lock, &LockHandle); + + } while (CurrentSocket != InitialSocket); + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // Could not find one to assign. + // + + return (USHORT)0; + +} /* IpxAssignSocket */ + + +PADDRESS +IpxCreateAddress( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Socket - The socket to assign to this address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PADDRESS Address; + PIPX_SEND_RESERVED SendReserved; + PIPX_RECEIVE_RESERVED ReceiveReserved; + NDIS_STATUS Status; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + Address = (PADDRESS)IpxAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + if (Address == NULL) { + IPX_DEBUG (ADDRESS, ("Create address %lx failed\n", REORDER_USHORT(Socket))); + return NULL; + } + + IPX_DEBUG (ADDRESS, ("Create address %lx (%lx)\n", Address, REORDER_USHORT(Socket))); + RtlZeroMemory (Address, sizeof(ADDRESS)); + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleSendPacket(Device, &Address->SendPacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail1; + } +#endif + + if (IpxInitializeSendPacket (Device, &Address->SendPacket, Address->SendPacketHeader) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail1: +#endif + Address->SendPacketInUse = TRUE; + } else { + SendReserved = SEND_RESERVED(&Address->SendPacket); + SendReserved->Address = Address; + SendReserved->OwnedByAddress = TRUE; + Address->SendPacketInUse = FALSE; +#ifdef IPX_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + + +#if BACK_FILL + { + PIPX_SEND_RESERVED BackFillReserved; + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleSendPacket(Device, &Address->BackFillPacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail2; + } +#endif + if (IpxInitializeBackFillPacket (Device, &Address->BackFillPacket, NULL) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail2: +#endif + Address->BackFillPacketInUse = TRUE; + } else { + BackFillReserved = SEND_RESERVED(&Address->BackFillPacket); + BackFillReserved->Address = Address; + Address->BackFillPacketInUse = FALSE; + BackFillReserved->OwnedByAddress = TRUE; +#ifdef IPX_TRACK_POOL + BackFillReserved->Pool = NULL; +#endif + } + } +#endif + +#ifndef IPX_OWN_PACKETS + IpxAllocateSingleReceivePacket(Device, &Address->ReceivePacket, &Status); + if (Status != NDIS_STATUS_SUCCESS) { + goto Fail3; + } +#endif + if (IpxInitializeReceivePacket (Device, &Address->ReceivePacket) != STATUS_SUCCESS) { +#ifndef IPX_OWN_PACKETS +Fail3: +#endif + Address->ReceivePacketInUse = TRUE; + } else { + ReceiveReserved = RECEIVE_RESERVED(&Address->ReceivePacket); + ReceiveReserved->Address = Address; + ReceiveReserved->OwnedByAddress = TRUE; + Address->ReceivePacketInUse = FALSE; +#ifdef IPX_TRACK_POOL + ReceiveReserved->Pool = NULL; +#endif + } + + Address->Type = IPX_ADDRESS_SIGNATURE; + Address->Size = sizeof (ADDRESS); + + Address->Device = Device; + Address->DeviceLock = &Device->Lock; + CTEInitLock (&Address->Lock); + + InitializeListHead (&Address->AddressFileDatabase); + + Address->ReferenceCount = 1; +#if DBG + Address->RefTypes[AREF_ADDRESS_FILE] = 1; +#endif + Address->Socket = Socket; + Address->SendSourceSocket = Socket; + + // + // Save our local address for building datagrams quickly. + // + + RtlCopyMemory (&Address->LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + Address->LocalAddress.Socket = Socket; + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + InsertTailList (&Device->AddressDatabases[IPX_HASH_SOCKET(Socket)], &Address->Linkage); + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IpxReferenceDevice (Device, DREF_ADDRESS); + + return Address; + +} /* IpxCreateAddress */ + + +NTSTATUS +IpxVerifyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a ADDRESS_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PADDRESS Address; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == IPX_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + Address = AddressFile->Address; + + if ((Address->Size == sizeof (ADDRESS)) && + (Address->Type == IPX_ADDRESS_SIGNATURE) ) { + + CTEGetLock (&Address->Lock, &LockHandle); + + if (!Address->Stopping) { + + IpxReferenceAddressFileLock (AddressFile, AFREF_VERIFY); + + } else { + + IpxPrint1("IpxVerifyAddressFile: A %lx closing\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + CTEFreeLock (&Address->Lock, LockHandle); + + } else { + + IpxPrint1("IpxVerifyAddressFile: A %lx bad signature\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + IpxPrint1("IpxVerifyAddressFile: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + IpxPrint1("IpxVerifyAddressFile: AF %lx exception\n", Address); + return GetExceptionCode(); + } + + return status; + +} /* IpxVerifyAddressFile */ + + +VOID +IpxDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by IpxDerefAddress when + the reference count goes to 0. + + This thread is only queued by IpxDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Parameter; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle; + + IPX_DEBUG (ADDRESS, ("Destroy address %lx (%lx)\n", Address, REORDER_USHORT(Address->Socket))); + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Address->Linkage); + CTEFreeLock (&Device->Lock, LockHandle); + + if (!Address->SendPacketInUse) { + IpxDeinitializeSendPacket (Device, &Address->SendPacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleSendPacket (Device, Address->SendPacket); +#endif + } + + if (!Address->ReceivePacketInUse) { + IpxDeinitializeReceivePacket (Device, &Address->ReceivePacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleReceivePacket (Device, Address->ReceivePacket); +#endif + } + +#if BACK_FILL + if (!Address->BackFillPacketInUse) { + IpxDeinitializeBackFillPacket (Device, &Address->BackFillPacket); +#ifndef IPX_OWN_PACKETS + IpxFreeSingleSendPacket (Device, Address->BackFillPacket); +#endif + } +#endif + IpxFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + + IpxDereferenceDevice (Device, DREF_ADDRESS); + +} /* IpxDestroyAddress */ + + +#if DBG +VOID +IpxRefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement(&Address->ReferenceCount); + +} /* IpxRefAddress */ + + +VOID +IpxRefAddressLock( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + // ++Address->ReferenceCount; + (VOID)InterlockedIncrement(&Address->ReferenceCount); + +} /* IpxRefAddressLock */ +#endif + + +VOID +IpxDerefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &Address->ReferenceCount, + (ULONG)-1, + Address->DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + IpxDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + IpxDestroyAddress(Address); +#endif + + } + +} /* IpxDerefAddress */ + + +VOID +IpxDerefAddressSync( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddress to remove it from the system. This routine can + only be called when we are synchronized (inside an IPX_SYNC_START/ + IPX_SYNC_END pair, with a lock held, or in an indication). + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &Address->ReferenceCount, + (ULONG)-1, + Address->DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + IpxDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + IpxDestroyAddress(Address); +#endif + + } + +} /* IpxDerefAddressSync */ + + +PADDRESS_FILE +IpxCreateAddressFile( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile; + + CTEGetLock (&Device->Lock, &LockHandle); + + AddressFile = (PADDRESS_FILE)IpxAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + if (AddressFile == NULL) { + IPX_DEBUG (ADDRESS, ("Create address file failed\n")); + CTEFreeLock (&Device->Lock, LockHandle); + return NULL; + } + + IPX_DEBUG (ADDRESS, ("Create address file %lx\n", AddressFile)); + + RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE)); + + AddressFile->Type = IPX_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + + CTEFreeLock (&Device->Lock, LockHandle); + +#if 0 + AddressFile->SpecialReceiveProcessing = FALSE; + AddressFile->ExtendedAddressing = FALSE; + AddressFile->ReceiveIpxHeader = FALSE; + AddressFile->FilterOnPacketType = FALSE; + AddressFile->DefaultPacketType = 0; + AddressFile->Address = NULL; +#ifdef ISN_NT + AddressFile->FileObject = NULL; +#endif +#endif + + AddressFile->Device = Device; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + AddressFile->ReferenceCount = 1; +#if DBG + AddressFile->RefTypes[AFREF_CREATE] = 1; +#endif + AddressFile->CloseRequest = (PREQUEST)NULL; + + // + // Initialize the request handlers. + // + + AddressFile->RegisteredReceiveDatagramHandler = FALSE; + AddressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler; + AddressFile->ReceiveDatagramHandlerContext = NULL; + + // + // [CH] Added these handlers for chained buffer receives + // + AddressFile->RegisteredChainedReceiveDatagramHandler = FALSE; + AddressFile->ChainedReceiveDatagramHandler = TdiDefaultChainedRcvDatagramHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = NULL; + + AddressFile->RegisteredErrorHandler = FALSE; + AddressFile->ErrorHandler = TdiDefaultErrorHandler; + AddressFile->ErrorHandlerContext = NULL; + + return AddressFile; + +} /* IpxCreateAddressFile */ + + +NTSTATUS +IpxDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by IpxDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PADDRESS Address; + PDEVICE Device; + PREQUEST CloseRequest; + + IPX_DEBUG (ADDRESS, ("Destroy address file %lx\n", AddressFile)); + + Address = AddressFile->Address; + Device = AddressFile->Device; + + if (Address) { + + // + // This addressfile was associated with an address. + // + + CTEGetLock (&Address->Lock, &LockHandle); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (Address->AddressFileDatabase.Flink == &Address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + CTEGetLock (&Device->Lock, &LockHandle1); + Address->Stopping = TRUE; + if (Device->LastAddress == Address) { + Device->LastAddress = NULL; + } + CTEFreeLock (&Device->Lock, LockHandle1); + + } + + AddressFile->Address = NULL; + +#ifdef ISN_NT + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; +#endif + + CTEFreeLock (&Address->Lock, LockHandle); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + // + // Now dereference the owning address. + // + + IpxDereferenceAddress (Address, AREF_ADDRESS_FILE); + + } + + // + // Save this for later completion. + // + + CloseRequest = AddressFile->CloseRequest; + + // + // return the addressFile to the pool of address files + // + + IpxFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + + if (CloseRequest != (PREQUEST)NULL) { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + IpxCompleteRequest (CloseRequest); + IpxFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} /* IpxDestroyAddressFile */ + + +#if DBG +VOID +IpxRefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + 1, + AddressFile->AddressLock); + +} /* IpxRefAddressFile */ + + +VOID +IpxRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + //++AddressFile->ReferenceCount; + (VOID)InterlockedIncrement(&AddressFile->ReferenceCount); + +} /* IpxRefAddressFileLock */ + + +VOID +IpxRefAddressFileSync( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + 1, + AddressFile->AddressLock); + +} /* IpxRefAddressFileSync */ + + +VOID +IpxDerefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + (ULONG)-1, + AddressFile->AddressLock); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) { + IpxDestroyAddressFile (AddressFile); + } + +} /* IpxDerefAddressFile */ + + +VOID +IpxDerefAddressFileSync( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IpxDestroyAddressFile to remove it from the system. This routine + can only be called when we are synchronized (inside an IPX_SYNC_START/ + IPX_SYNC_END pair, with a lock held, or in an indication). + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = IPX_ADD_ULONG ( + &AddressFile->ReferenceCount, + (ULONG)-1, + AddressFile->AddressLock); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) { + IpxDestroyAddressFile (AddressFile); + } + +} /* IpxDerefAddressFileSync */ +#endif + + +PADDRESS +IpxLookupAddress( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + ULONG Hash = IPX_HASH_SOCKET (Socket); + + p = Device->AddressDatabases[Hash].Flink; + + for (p = Device->AddressDatabases[Hash].Flink; + p != &Device->AddressDatabases[Hash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if (Address->Stopping) { + continue; + } + + if (Address->Socket == Socket) { + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + IpxReferenceAddressLock (Address, AREF_LOOKUP); + return Address; + + } + + } + + // + // The specified address was not found. + // + + return NULL; + +} /* IpxLookupAddress */ + + +NTSTATUS +IpxStopAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + CTELockHandle LockHandle; + PREQUEST Request; + PADDRESS Address = AddressFile->Address; + PLIST_ENTRY p; + KIRQL irql; + + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock (&Address->Lock, &LockHandle); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + CTEFreeLock (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + return STATUS_SUCCESS; + } + + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + + while (!(IsListEmpty(&AddressFile->ReceiveDatagramQueue))) { + + p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue); + Request = LIST_ENTRY_TO_REQUEST (p); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + CTEFreeLock(&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + IoAcquireCancelSpinLock( &irql ); + CTEGetLock(&Address->Lock, &LockHandle); + + } + + CTEFreeLock(&Address->Lock, LockHandle); + IoReleaseCancelSpinLock( irql ); + +} /* IpxStopAddressFile */ + + +NTSTATUS +IpxCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PADDRESS Address; + PADDRESS_FILE AddressFile; + CTELockHandle LockHandle; + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + AddressFile->CloseRequest = Request; + + // + // We assume that addressFile has already been verified + // at this point. + // + + Address = AddressFile->Address; + CTEAssert (Address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); +#ifdef ISN_NT + IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess); +#endif + ExReleaseResource (&Device->AddressResource); + + // + // If this address file had broadcasts enabled, turn it off. + // + + CTEGetLock (&Device->Lock, &LockHandle); + if (AddressFile->EnableBroadcast) { + AddressFile->EnableBroadcast = FALSE; + IpxRemoveBroadcast (Device); + } + CTEFreeLock (&Device->Lock, LockHandle); + + IpxStopAddressFile (AddressFile); + IpxDereferenceAddressFile (AddressFile, AFREF_CREATE); + + return STATUS_PENDING; + +} /* IpxCloseAddressFile */ + + diff --git a/private/ntos/tdi/isnp/ipx/config.c b/private/ntos/tdi/isnp/ipx/config.c new file mode 100644 index 000000000..f5d8aefbf --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/config.c @@ -0,0 +1,1715 @@ +/*++ + + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN IPX module. + +Revision History: + + Sanjay Anand (SanjayAn) 19-Sept-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Local functions used to access the registry. +// + +NTSTATUS +IpxGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxGetBindingValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxGetFrameType( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +IpxReadLinkageInformation( + IN PCONFIG Config + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxGetConfiguration) +#pragma alloc_text(INIT,IpxFreeConfiguration) + +#ifndef _PNP_POWER +#pragma alloc_text(INIT,IpxGetConfigValue) +#pragma alloc_text(INIT,IpxGetBindingValue) +#pragma alloc_text(INIT,IpxGetFrameType) +#pragma alloc_text(INIT,IpxWriteDefaultAutoDetectType) +#endif + +#pragma alloc_text(INIT,IpxAddBind) +#pragma alloc_text(INIT,IpxAddExport) +#pragma alloc_text(INIT,IpxReadLinkageInformation) +#endif + + + +NTSTATUS +IpxGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by IPX to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + DriverObject - Used for logging errors. + + RegistryPath - The name of IPX's node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ + +{ + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + NTSTATUS Status; + ULONG Zero = 0; + ULONG One = 1; + ULONG Five = 5; + ULONG Eight = 8; + ULONG Ten = 10; + ULONG Fifteen = 15; + ULONG Fifty = 50; + ULONG DefaultSocketStart = 0x4000; + ULONG DefaultSocketEnd = 0x8000; + ULONG RipSegments = RIP_SEGMENTS; + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"DedicatedRouter", &Zero } , + { L"InitDatagrams", &Ten } , + { L"MaxDatagrams", &Fifty } , + { L"RipAgeTime", &Five } , // minutes + { L"RipCount", &Five } , + { L"RipTimeout", &One } , // half-second + { L"RipUsageTime", &Fifteen } , // minutes + { L"SourceRouteUsageTime", &Ten } , // minutes + { L"SocketUniqueness", &Eight } , + { L"SocketStart", &DefaultSocketStart } , + { L"SocketEnd", &DefaultSocketEnd } , + { L"VirtualNetworkNumber", &Zero } , + { L"MaxMemoryUsage", &Zero } , + { L"RipTableSize", &RipSegments } , + { L"VirtualNetworkOptional", &One } , + { L"EthernetPadToEven", &One } , + { L"EthernetExtraPadding", &Zero } , + { L"SingleNetworkActive", &Zero } , + { L"DisableDialoutSap", &Zero } , + { L"DisableDialinNetbios", &One } , + { L"VerifySourceAddress", &One } }; + UINT i; + + + // + // Allocate memory for the main config structure. + // + + Config = IpxAllocateMemory (sizeof(CONFIG), MEMORY_CONFIG, "Config"); + if (Config == NULL) { + IpxWriteResourceErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + sizeof(CONFIG), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->DeviceName.Buffer = NULL; + InitializeListHead (&Config->BindingList); + Config->DriverObject = DriverObject; + + // + // Read in the NDIS binding information. + // + // IpxReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)IpxAllocateMemory(RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG, "RegistryPathBuffer"); + if (RegistryPathBuffer == NULL) { + IpxFreeConfiguration(Config); + IpxWriteResourceErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->RegistryPathBuffer = RegistryPathBuffer; + + // + // Determine what name to export and who to bind to. + // + + Status = IpxReadLinkageInformation (Config); + if (Status != STATUS_SUCCESS) { + + // + // It logged an error if it failed. + // + + IpxFreeConfiguration(Config); + return Status; + } + + // + // Read the per-transport (as opposed to per-binding) + // parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2-14) Call IpxGetConfigValue for each of the keys we + // care about. + // + + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = IpxGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + + } + + // + // 15) Stop + // + + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + IpxFreeConfiguration(Config); + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 905, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + // + // For PnP, we need to keep this path around + // +#ifndef _PNP_POWER + IpxFreeMemory (RegistryPathBuffer, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG, "RegistryPathBuffer"); +#endif _PNP_POWER + + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} /* IpxGetConfiguration */ + + +VOID +IpxFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to get free any storage that was allocated + by IpxGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PBINDING_CONFIG Binding; + + while (!IsListEmpty (&Config->BindingList)) { + p = RemoveHeadList (&Config->BindingList); + Binding = CONTAINING_RECORD (p, BINDING_CONFIG, Linkage); + IpxFreeMemory (Binding->AdapterName.Buffer, Binding->AdapterName.MaximumLength, MEMORY_CONFIG, "NameBuffer"); + IpxFreeMemory (Binding, sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + } + + if (Config->DeviceName.Buffer) { + IpxFreeMemory (Config->DeviceName.Buffer, Config->DeviceName.MaximumLength, MEMORY_CONFIG, "DeviceName"); + } + + IpxFreeMemory (Config, sizeof(CONFIG), MEMORY_CONFIG, "Config"); + +} /* IpxFreeConfiguration */ + + +NTSTATUS +IpxGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 904, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + Config->Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + + return STATUS_SUCCESS; + +} /* IpxGetConfigValue */ + + +NTSTATUS +IpxGetBindingValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the NetConfig\DriverNN + node to set the per-binding values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the BINDING_CONFIG structure. + + EntryContext - The index in Binding->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PBINDING_CONFIG Binding = (PBINDING_CONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("Binding parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + Binding->Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + + return STATUS_SUCCESS; + +} /* IpxGetBindingValue */ + + +NTSTATUS +IpxGetFrameType( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues. + It is called for each of the entry in the "PktType" and + "NetworkNumber" multi-strings for a given binding. + +Arguments: + + ValueName - The name of the value ("PktType" or "NetworkNumber" -- ignored). + + ValueType - The type of the value (REG_MULTI_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the BINDING_CONFIG structure. + + EntryContext - A pointer to a count of multi-string entries. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PBINDING_CONFIG Binding = (PBINDING_CONFIG)Context; + ULONG IntegerValue; + PWCHAR Cur; + PULONG Count = (PULONG)EntryContext; + + if ((ValueType != REG_SZ) || + (*Count >= 4)) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IntegerValue = 0; + for (Cur = (PWCHAR)(ValueData); ; Cur++) { + if (*Cur >= L'0' && *Cur <= L'9') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'0'); + } else if (*Cur >= L'A' && *Cur <= L'F') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'A' + 10); + } else if (*Cur >= L'a' && *Cur <= L'f') { + IntegerValue = (IntegerValue * 16) + (*Cur - L'a' + 10); + } else { + break; + } + } + + if (((PWCHAR)ValueName)[0] == L'P') { + + // + // PktType. We map arcnet to 802_3 so the code around + // here can assume there are only four packets type -- + // the frame type is ignored later for arcnet. + // + + if ((IntegerValue > ISN_FRAME_TYPE_ARCNET) && + (IntegerValue != ISN_FRAME_TYPE_AUTO)) { + + IpxWriteGeneralErrorLog( + (PVOID)Binding->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 903, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (CONFIG, ("PktType(%d) is %lx\n", *Count, IntegerValue)); + if (IntegerValue == ISN_FRAME_TYPE_ARCNET) { + Binding->FrameType[*Count] = ISN_FRAME_TYPE_802_3; + } else { + Binding->FrameType[*Count] = IntegerValue; + } + + } else { + + // + // NetworkNumber + // + + IPX_DEBUG (CONFIG, ("NetworkNumber(%d) is %d\n", *Count, IntegerValue)); + Binding->NetworkNumber[*Count] = IntegerValue; + + } + + ++(*Count); + + return STATUS_SUCCESS; + +} /* IpxGetFrameType */ + + +NTSTATUS +IpxAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a Config structure. It + also queries the per-binding information and stores it. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PBINDING_CONFIG Binding; + PULONG CurBindNum = ((PULONG)EntryContext); + RTL_QUERY_REGISTRY_TABLE QueryTable[BINDING_PARAMETERS+4]; + ULONG FrameTypeCount, NetworkNumberCount; + ULONG StringLoc; + BOOLEAN AutoDetect; + ULONG AutoDetectLoc; + ULONG SlideCount; + PWCHAR NameBuffer; + NTSTATUS Status; + BOOLEAN FrameTypeUsed[ISN_FRAME_TYPE_MAX]; + ULONG Zero = 0; + ULONG One = 1; + ULONG DefaultBindSap = 0x8137; + ULONG DefaultAutoDetectType = ISN_FRAME_TYPE_802_2; + PWSTR Subkey = L"NetConfig\\12345678901234567890"; // BUGBUG: hack + PWSTR ValueDataWstr = (PWSTR)ValueData; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[BINDING_PARAMETERS] = { + { L"MaxPktSize", &Zero } , + { L"BindSap", &DefaultBindSap } , + { L"DefaultAutoDetectType", &DefaultAutoDetectType } , + { L"SourceRouting", &One } , + { L"SourceRouteDef", &Zero } , + { L"SourceRouteBcast", &Zero } , + { L"SourceRouteMcast", &Zero } , + { L"EnableFuncaddr", &One } , + { L"EnableWanRouter", &One } }; + ULONG BindingPreference[ISN_FRAME_TYPE_MAX] = { + ISN_FRAME_TYPE_802_2, + ISN_FRAME_TYPE_802_3, + ISN_FRAME_TYPE_ETHERNET_II, + ISN_FRAME_TYPE_SNAP }; + + UINT i, j, k; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + + Binding = (PBINDING_CONFIG)IpxAllocateMemory (sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + if (Binding == NULL) { + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + sizeof(BINDING_CONFIG), + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + NameBuffer = (PWCHAR)IpxAllocateMemory (ValueLength, MEMORY_CONFIG, "NameBuffer"); + if (NameBuffer == NULL) { + IpxFreeMemory (Binding, sizeof(BINDING_CONFIG), MEMORY_CONFIG, "Binding"); + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + ValueLength, + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Binding->AdapterName.Buffer = NameBuffer; + Binding->AdapterName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Binding->AdapterName.MaximumLength = (USHORT)ValueLength; + + Binding->DriverObject = Config->DriverObject; + + FrameTypeCount = 0; + NetworkNumberCount = 0; + + // + // The structure is allocated OK, insert it into the list. + // + + InsertTailList (&Config->BindingList, &Binding->Linkage); + ++(*CurBindNum); + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the NetConfig\XXXX key below IPX + // (we construct the right name in Subkey, + // first scan back to find the \, then copy + // the rest over, including the final '\0'). + // + + StringLoc = (ValueLength / sizeof(WCHAR)) - 2; + while (ValueDataWstr[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory(&Subkey[10], &ValueDataWstr[StringLoc+1], ValueLength - ((StringLoc+1) * sizeof(WCHAR))); + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call IpxGetFrameType for each part of the + // "PktType" multi-string. + // + + QueryTable[1].QueryRoutine = IpxGetFrameType; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = L"PktType"; + QueryTable[1].EntryContext = &FrameTypeCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call IpxGetFrameType for each part of the + // "NetworkNumber" multi-string. + // + + QueryTable[2].QueryRoutine = IpxGetFrameType; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[2].Name = L"NetworkNumber"; + QueryTable[2].EntryContext = &NetworkNumberCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4-11) Call IpxGetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < BINDING_PARAMETERS; i++) { + + QueryTable[i+3].QueryRoutine = IpxGetBindingValue; + QueryTable[i+3].Flags = 0; + QueryTable[i+3].Name = ParameterValues[i].KeyName; + QueryTable[i+3].EntryContext = (PVOID)i; + QueryTable[i+3].DefaultType = REG_DWORD; + QueryTable[i+3].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+3].DefaultLength = sizeof(ULONG); + + } + + // + // 12) Stop + // + + QueryTable[BINDING_PARAMETERS+3].QueryRoutine = NULL; + QueryTable[BINDING_PARAMETERS+3].Flags = 0; + QueryTable[BINDING_PARAMETERS+3].Name = NULL; + + + IPX_DEBUG (CONFIG, ("Read bind key for %ws (%ws)\n", ValueData, Subkey)); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Binding, + NULL); + + if (Status != STATUS_SUCCESS) { + + // + // The binding will get freed during cleanup. + // + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 906, + Status, + Subkey, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + if (FrameTypeCount == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_NO_FRAME_TYPES, + 907, + Status, + Subkey + 10, + 0, + NULL); + } + + if (FrameTypeCount > NetworkNumberCount) { + for (i = NetworkNumberCount; i <FrameTypeCount; i++) { + Binding->NetworkNumber[i] = 0; + } + } + Binding->FrameTypeCount = FrameTypeCount; + + // + // Go through and eliminate duplicates from the frame + // type array. + // + + for (i = 0; i < Binding->FrameTypeCount; i++) { + + for (j = i+1; j < Binding->FrameTypeCount; j++) { + + if (Binding->FrameType[j] == Binding->FrameType[i]) { + + IPX_DEBUG (CONFIG, ("Frame types %d and %d identical\n", i, j)); + + // + // A duplicate, slide everything else down. + // + + for (k = j+1; k < Binding->FrameTypeCount; k++) { + Binding->FrameType[k-1] = Binding->FrameType[k]; + Binding->NetworkNumber[k-1] = Binding->NetworkNumber[k]; + } + --Binding->FrameTypeCount; + + --j; // so we check whoever just moved into this spot. + } + } + } + + + // + // Mark all the explicitly configured frame types, and + // see if we have to auto-detect. + // + + for (i = 0; i < 4; i++) { + FrameTypeUsed[i] = FALSE; + } + + AutoDetect = FALSE; + for (i = 0; i < Binding->FrameTypeCount; i++) { + if (Binding->FrameType[i] == ISN_FRAME_TYPE_AUTO) { + AutoDetectLoc = i; + AutoDetect = TRUE; + } else { + Binding->AutoDetect[i] = FALSE; + Binding->DefaultAutoDetect[i] = FALSE; + FrameTypeUsed[Binding->FrameType[i]] = TRUE; + } + } + + if (!AutoDetect) { + IPX_DEBUG (AUTO_DETECT, ("No bindings auto-detected\n")); + return STATUS_SUCCESS; + } + + // + // Slide everything that is past the auto-detect point up + // to the end. + // + + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(3-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(3-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(3-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(3-Binding->FrameTypeCount)]; + } + + // + // Now fill in any frame types that are not hard-coded, + // this will start at AutoDetectLoc and exactly fill up + // the gap created when we slid things up above. We + // first put the default auto-detect at the first spot. + // + + if (!FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]]) { + Binding->FrameType[AutoDetectLoc] = Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = TRUE; + ++AutoDetectLoc; + FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]] = TRUE; + } + + // + // Now fill in the array, using the preference order in + // the BindingPreference array (this comes into effect + // because the first frame type in our list that we + // find is used). + // + + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + if (!FrameTypeUsed[BindingPreference[i]]) { + Binding->FrameType[AutoDetectLoc] = BindingPreference[i]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = FALSE; + ++AutoDetectLoc; + } + } + + Binding->FrameTypeCount = ISN_FRAME_TYPE_MAX; + +#if DBG + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + i, Binding->FrameType[i], Binding->NetworkNumber[i], Binding->AutoDetect[i])); + } +#endif + + return STATUS_SUCCESS; + +} /* IpxAddBind */ + + +NTSTATUS +IpxAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string. It + saves the first callback string in the Config structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a ULONG that goes to 1 after the + first call to this routine (so we know to ignore other ones). + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + IPX_DEBUG (CONFIG, ("Read export value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)IpxAllocateMemory (ValueLength, MEMORY_CONFIG, "DeviceName"); + if (NameBuffer == NULL) { + IpxWriteResourceErrorLog( + (PVOID)Config->DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + ValueLength, + MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->DeviceName.Buffer = NameBuffer; + Config->DeviceName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->DeviceName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* IpxAddExport */ + + +NTSTATUS +IpxReadLinkageInformation( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to read its linkage information + from the registry. + +Arguments: + + Config - The config structure which will have per-binding information + linked on to it. + +Return Value: + + The status of the operation. + +--*/ + +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[3]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG ValueReadOk; + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 1) Call IpxAddExport for each string in "Export" + // + + QueryTable[1].QueryRoutine = IpxAddExport; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = Export; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[2].QueryRoutine = NULL; + QueryTable[2].Flags = 0; + QueryTable[2].Name = NULL; + + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 901, + Status, + Export, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + +#ifndef _PNP_POWER +// +// This will be done as and when adapters appear. +// + // + // 1) Change to call IpxAddBind for each string in "Bind" + // + + QueryTable[1].QueryRoutine = IpxAddBind; + QueryTable[1].Flags = 0; // not required + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&Config->BindCount; + QueryTable[1].DefaultType = REG_NONE; + + Config->BindCount = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + // + // For the moment fail if we find no bindings -- eventually when + // we support dynamic binding we should stick around in this case. + // + + if ((Status != STATUS_SUCCESS) || (Config->BindCount == 0)) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 902, + Status, + Bind, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } +#endif + return STATUS_SUCCESS; + +} /* IpxReadLinkageInformation */ + + +VOID +IpxWriteDefaultAutoDetectType( + IN PUNICODE_STRING RegistryPath, + IN struct _ADAPTER * Adapter, + IN ULONG FrameType + ) + +/*++ + +Routine Description: + + This routine is called when we were unable to detect the default + auto-detect type and instead found a different one. We update + the "DefaultAutoDetectType" in the registry. + +Arguments: + + RegistryPath - The name of IPX's node in the registry. + + Adapter - The adapter which we auto-detected on. + + FrameType - The new auto-detected value. + +Return Value: + + None. + +--*/ + +{ + PWSTR FullRegistryPath; + PUCHAR CurRegistryPath; + ULONG FullRegistryPathLength; + ULONG AdapterNameLength; + WCHAR NetConfigName[] = L"\\NetConfig"; + static PWCHAR FrameTypeNames[4] = { L"Ethernet II", L"802.3", L"802.2", L"SNAP" }; + PWCHAR CurAdapterName; + NTSTATUS Status; + + + // + // We need to allocate a buffer which contains the registry path, + // followed by "NetConfig", followed by the adapter name, and + // then NULL-terminated. + // + + CurAdapterName = &Adapter->AdapterName[(Adapter->AdapterNameLength/sizeof(WCHAR))-2]; + while (*CurAdapterName != L'\\') { + --CurAdapterName; + } + CurAdapterName; + AdapterNameLength = Adapter->AdapterNameLength - ((CurAdapterName - Adapter->AdapterName) * sizeof(WCHAR)) - sizeof(WCHAR); + + FullRegistryPathLength = RegistryPath->Length + sizeof(NetConfigName) + AdapterNameLength; + + FullRegistryPath = (PWSTR)IpxAllocateMemory (FullRegistryPathLength, MEMORY_CONFIG, "FullRegistryPath"); + if (FullRegistryPath == NULL) { + IpxWriteResourceErrorLog( + IpxDevice->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + FullRegistryPathLength, + MEMORY_CONFIG); + return; + } + + CurRegistryPath = (PUCHAR)FullRegistryPath; + RtlCopyMemory (CurRegistryPath, RegistryPath->Buffer, RegistryPath->Length); + CurRegistryPath += RegistryPath->Length; + RtlCopyMemory (CurRegistryPath, NetConfigName, sizeof(NetConfigName) - sizeof(WCHAR)); + CurRegistryPath += (sizeof(NetConfigName) - sizeof(WCHAR)); + RtlCopyMemory (CurRegistryPath, CurAdapterName, AdapterNameLength); + CurRegistryPath += AdapterNameLength; + *(PWCHAR)CurRegistryPath = L'\0'; + + Status = RtlWriteRegistryValue( + RTL_REGISTRY_ABSOLUTE, + FullRegistryPath, + L"DefaultAutoDetectType", + REG_DWORD, + &FrameType, + sizeof(ULONG)); + + IpxFreeMemory (FullRegistryPath, FullRegistryPathLength, MEMORY_CONFIG, "FullRegistryPath"); + + IpxWriteGeneralErrorLog( + IpxDevice->DeviceObject, + EVENT_IPX_NEW_DEFAULT_TYPE, + 888, + STATUS_SUCCESS, + FrameTypeNames[FrameType], + 0, + NULL); + +} /* IpxWriteDefaultAutoDetectType */ + + +#ifdef _PNP_POWER +// +// Vnet# and VnetOptional +// +#define VIRTUAL_NETWORK_PARAMETERS 2 + +NTSTATUS +IpxPnPGetVirtualNetworkNumber ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by IPX to read the virtual network number + from the registry. This is called on appearance/disappearance of an + adapter from the system. We read the registry, starting at RegistryPath, + to get the value of the VirtualNetworkNumber parameter. If it doesn't + exist, we use the default set in ipxcnfg.h file. + Adapted from IpxGetConfiguration(). + +Arguments: + + Config - Contians the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_DEVICE_CONFIGURATION_ERROR + otherwise. + +--*/ + +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[VIRTUAL_NETWORK_PARAMETERS+2]; + NTSTATUS Status; + ULONG Zero = 0; + ULONG One = 1; + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[VIRTUAL_NETWORK_PARAMETERS] = { + { L"VirtualNetworkNumber", &Zero } , + { L"VirtualNetworkOptional", &One } }; + UINT i; + + // + // Read the virtual net number from the parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below IPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2) Call IpxGetConfigValue for the virtual net number key + // + + QueryTable[1].QueryRoutine = IpxGetConfigValue; + QueryTable[1].Flags = 0; + QueryTable[1].Name = ParameterValues[0].KeyName; + QueryTable[1].EntryContext = (PVOID)CONFIG_VIRTUAL_NETWORK; + QueryTable[1].DefaultType = REG_DWORD; + QueryTable[1].DefaultData = (PVOID)(ParameterValues[0].DefaultValue); + QueryTable[1].DefaultLength = sizeof(ULONG); + + // + // 2) Call IpxGetConfigValue for the virtual net optional key + // + + QueryTable[2].QueryRoutine = IpxGetConfigValue; + QueryTable[2].Flags = 0; + QueryTable[2].Name = ParameterValues[1].KeyName; + QueryTable[2].EntryContext = (PVOID)CONFIG_VIRTUAL_OPTIONAL; + QueryTable[2].DefaultType = REG_DWORD; + QueryTable[2].DefaultData = (PVOID)(ParameterValues[1].DefaultValue); + QueryTable[2].DefaultLength = sizeof(ULONG); + + // + // 15) Stop + // + + QueryTable[3].QueryRoutine = NULL; + QueryTable[3].Flags = 0; + QueryTable[3].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 905, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; + +} /* IpxPnPGetNetworkNumber */ + + +NTSTATUS +IpxPnPGetAdapterParameters( + IN PCONFIG Config, + IN PNDIS_STRING DeviceName, + IN OUT PBINDING_CONFIG Binding + ) +/*++ + +Routine Description: + + This routine is called by IPX to read the adapter-specific parameters + from the registry on PnP appearance of an adapter in the system. + We read the registry, starting at RegistryPath\NetConfig\DeviceName. + + Adapted from IpxAddBind(). + +Arguments: + + Config - Config structure - supplies the DeviceObject and RegistryPathBuffer. + + DeviceName - name of the adapter that was added. + + Binding - Returns the configuration information per adapter. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_DEVICE_CONFIGURATION_ERROR + otherwise. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[BINDING_PARAMETERS+4]; + ULONG FrameTypeCount, NetworkNumberCount; + ULONG StringLoc; + BOOLEAN AutoDetect; + ULONG AutoDetectLoc; + ULONG SlideCount; + PWCHAR NameBuffer; + NTSTATUS Status; + BOOLEAN FrameTypeUsed[ISN_FRAME_TYPE_MAX]; + ULONG Zero = 0; + ULONG One = 1; + ULONG DefaultBindSap = 0x8137; + ULONG DefaultAutoDetectType = ISN_FRAME_TYPE_802_2; + PWSTR Subkey = L"NetConfig\\12345678901234567890"; // BUGBUG: hack + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[BINDING_PARAMETERS] = { + { L"MaxPktSize", &Zero } , + { L"BindSap", &DefaultBindSap } , + { L"DefaultAutoDetectType", &DefaultAutoDetectType } , + { L"SourceRouting", &One } , + { L"SourceRouteDef", &Zero } , + { L"SourceRouteBcast", &Zero } , + { L"SourceRouteMcast", &Zero } , + { L"EnableFuncaddr", &One } , + { L"EnableWanRouter", &One } }; + ULONG BindingPreference[ISN_FRAME_TYPE_MAX] = { + ISN_FRAME_TYPE_802_2, + ISN_FRAME_TYPE_802_3, + ISN_FRAME_TYPE_ETHERNET_II, + ISN_FRAME_TYPE_SNAP }; + + UINT i, j, k; + + FrameTypeCount = 0; + NetworkNumberCount = 0; + + // + // The structure is allocated OK, insert it into the list. + // + +// InsertTailList (&Config->BindingList, &Binding->Linkage); +// ++(*CurBindNum); + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the NetConfig\XXXX key below IPX + // (we construct the right name in Subkey, + // first scan back to find the \, then copy + // the rest over, including the final '\0'). + // + StringLoc = (DeviceName->MaximumLength / sizeof(WCHAR)) - 2; + while (DeviceName->Buffer[StringLoc] != L'\\') { + --StringLoc; + } + RtlCopyMemory(&Subkey[10], &DeviceName->Buffer[StringLoc+1], DeviceName->MaximumLength - ((StringLoc+1) * sizeof(WCHAR))); + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call IpxGetFrameType for each part of the + // "PktType" multi-string. + // + + QueryTable[1].QueryRoutine = IpxGetFrameType; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = L"PktType"; + QueryTable[1].EntryContext = &FrameTypeCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call IpxGetFrameType for each part of the + // "NetworkNumber" multi-string. + // + + QueryTable[2].QueryRoutine = IpxGetFrameType; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[2].Name = L"NetworkNumber"; + QueryTable[2].EntryContext = &NetworkNumberCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4-11) Call IpxGetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < BINDING_PARAMETERS; i++) { + + QueryTable[i+3].QueryRoutine = IpxGetBindingValue; + QueryTable[i+3].Flags = 0; + QueryTable[i+3].Name = ParameterValues[i].KeyName; + QueryTable[i+3].EntryContext = (PVOID)i; + QueryTable[i+3].DefaultType = REG_DWORD; + QueryTable[i+3].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+3].DefaultLength = sizeof(ULONG); + + } + + // + // 12) Stop + // + + QueryTable[BINDING_PARAMETERS+3].QueryRoutine = NULL; + QueryTable[BINDING_PARAMETERS+3].Flags = 0; + QueryTable[BINDING_PARAMETERS+3].Name = NULL; + + + IPX_DEBUG (CONFIG, ("Read bind key for %ws (%ws)\n", DeviceName->Buffer, Subkey)); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Binding, + NULL); + + if (Status != STATUS_SUCCESS) { + + // + // The binding will get freed during cleanup. + // + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 906, + Status, + Subkey, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + if (FrameTypeCount == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_NO_FRAME_TYPES, + 907, + Status, + Subkey + 10, + 0, + NULL); + } + + if (FrameTypeCount > NetworkNumberCount) { + for (i = NetworkNumberCount; i <FrameTypeCount; i++) { + Binding->NetworkNumber[i] = 0; + } + } + Binding->FrameTypeCount = FrameTypeCount; + + // + // Go through and eliminate duplicates from the frame + // type array. + // + + for (i = 0; i < Binding->FrameTypeCount; i++) { + + for (j = i+1; j < Binding->FrameTypeCount; j++) { + + if (Binding->FrameType[j] == Binding->FrameType[i]) { + + IPX_DEBUG (CONFIG, ("Frame types %d and %d identical\n", i, j)); + + // + // A duplicate, slide everything else down. + // + + for (k = j+1; k < Binding->FrameTypeCount; k++) { + Binding->FrameType[k-1] = Binding->FrameType[k]; + Binding->NetworkNumber[k-1] = Binding->NetworkNumber[k]; + } + --Binding->FrameTypeCount; + + --j; // so we check whoever just moved into this spot. + } + } + } + + + // + // Mark all the explicitly configured frame types, and + // see if we have to auto-detect. + // + + for (i = 0; i < 4; i++) { + FrameTypeUsed[i] = FALSE; + } + + AutoDetect = FALSE; + for (i = 0; i < Binding->FrameTypeCount; i++) { + if ((Binding->FrameType[i] == ISN_FRAME_TYPE_AUTO)) { + AutoDetectLoc = i; + AutoDetect = TRUE; + } else { + Binding->AutoDetect[i] = FALSE; + Binding->DefaultAutoDetect[i] = FALSE; + FrameTypeUsed[Binding->FrameType[i]] = TRUE; + } + } + + if (!AutoDetect) { + IPX_DEBUG (AUTO_DETECT, ("No bindings auto-detected\n")); + return STATUS_SUCCESS; + } + + // + // Slide everything that is past the auto-detect point up + // to the end. + // + + // + // Fixed this loop which can spill over if the FrameTypeCount is 4 and the SlideCount > 0. + // Here, the FrameTypeCount is 1-based, whereas the indices are 0-based, we need to make + // the index 1-based for this to work. So, instead of (3-Binding->FrameTypeCount), we use + // (4-Binding->FrameTypeCount). This loop copies all the non-auto-detect frametypes down to + // the bottom of the array to make space after the last auto-detect frame-type for filling + // in the frametypes in the preference order. + // +#if 0 + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(3-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(3-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(3-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(3-Binding->FrameTypeCount)]; + } +#else + SlideCount = Binding->FrameTypeCount - AutoDetectLoc - 1; + for (j = 3; j > 3 - SlideCount; j--) { + Binding->FrameType[j] = Binding->FrameType[j-(4-Binding->FrameTypeCount)]; + Binding->NetworkNumber[j] = Binding->NetworkNumber[j-(4-Binding->FrameTypeCount)]; + Binding->AutoDetect[j] = Binding->AutoDetect[j-(4-Binding->FrameTypeCount)]; + Binding->DefaultAutoDetect[j] = Binding->DefaultAutoDetect[j-(4-Binding->FrameTypeCount)]; + } +#endif + + // + // Now fill in any frame types that are not hard-coded, + // this will start at AutoDetectLoc and exactly fill up + // the gap created when we slid things up above. We + // first put the default auto-detect at the first spot. + // + + if (!FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]]) { + Binding->FrameType[AutoDetectLoc] = Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = TRUE; + ++AutoDetectLoc; + FrameTypeUsed[Binding->Parameters[BINDING_DEFAULT_AUTO_DETECT]] = TRUE; + } + + // + // Now fill in the array, using the preference order in + // the BindingPreference array (this comes into effect + // because the first frame type in our list that we + // find is used). + // + + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + if (!FrameTypeUsed[BindingPreference[i]]) { + Binding->FrameType[AutoDetectLoc] = BindingPreference[i]; + Binding->NetworkNumber[AutoDetectLoc] = 0; + Binding->AutoDetect[AutoDetectLoc] = TRUE; + Binding->DefaultAutoDetect[AutoDetectLoc] = FALSE; + ++AutoDetectLoc; + } + } + + Binding->FrameTypeCount = ISN_FRAME_TYPE_MAX; + +#if DBG + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + i, Binding->FrameType[i], Binding->NetworkNumber[i], Binding->AutoDetect[i])); + } +#endif + + return STATUS_SUCCESS; +} /* IpxPnPGetAdapterParameters */ + +#endif _PNP_POWER + diff --git a/private/ntos/tdi/isnp/ipx/config.h b/private/ntos/tdi/isnp/ipx/config.h new file mode 100644 index 000000000..ba6e76d83 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/config.h @@ -0,0 +1,132 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.h + +Abstract: + + Private include file for the ISN IPX module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + + +// +// These are used to index into the Parameters array in CONFIG. +// + +#define CONFIG_DEDICATED_ROUTER 0 +#define CONFIG_INIT_DATAGRAMS 1 +#define CONFIG_MAX_DATAGRAMS 2 +#define CONFIG_RIP_AGE_TIME 3 +#define CONFIG_RIP_COUNT 4 +#define CONFIG_RIP_TIMEOUT 5 +#define CONFIG_RIP_USAGE_TIME 6 +#define CONFIG_ROUTE_USAGE_TIME 7 +#define CONFIG_SOCKET_UNIQUENESS 8 +#define CONFIG_SOCKET_START 9 +#define CONFIG_SOCKET_END 10 +#define CONFIG_VIRTUAL_NETWORK 11 +#define CONFIG_MAX_MEMORY_USAGE 12 +#define CONFIG_RIP_TABLE_SIZE 13 +#define CONFIG_VIRTUAL_OPTIONAL 14 +#define CONFIG_ETHERNET_PAD 15 +#define CONFIG_ETHERNET_LENGTH 16 +#define CONFIG_SINGLE_NETWORK 17 +#define CONFIG_DISABLE_DIALOUT_SAP 18 +#define CONFIG_DISABLE_DIALIN_NB 19 +#define CONFIG_VERIFY_SOURCE_ADDRESS 20 + +#define CONFIG_PARAMETERS 21 + +// +// Main configuration structure. +// + +typedef struct _CONFIG { + + ULONG Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING DeviceName; // device name exported + PWSTR RegistryPathBuffer; // path to config info + ULONG BindCount; // entries in BindingList + LIST_ENTRY BindingList; // one per binding + PDRIVER_OBJECT DriverObject; // used for logging errors + +} CONFIG, * PCONFIG; + + +// +// These are used to index into the Parameters array in BINDING_CONFIG. +// + +#define BINDING_MAX_PKT_SIZE 0 +#define BINDING_BIND_SAP 1 +#define BINDING_DEFAULT_AUTO_DETECT 2 +#define BINDING_SOURCE_ROUTE 3 +#define BINDING_ALL_ROUTE_DEF 4 +#define BINDING_ALL_ROUTE_BC 5 +#define BINDING_ALL_ROUTE_MC 6 +#define BINDING_ENABLE_FUNC_ADDR 7 +#define BINDING_ENABLE_WAN 8 + +#define BINDING_PARAMETERS 9 + + +// +// One of these is allocated per adapter we are to bind to. +// + +typedef struct _BINDING_CONFIG { + + LIST_ENTRY Linkage; // for chaining on BindingList + NDIS_STRING AdapterName; // NDIS adapter to bind to + ULONG FrameTypeCount; // number of frame types defined (max. 4) + // == number of valid entries in arrays: + ULONG FrameType[ISN_FRAME_TYPE_MAX]; // ISN_FRAME_TYPE_XXX + ULONG NetworkNumber[ISN_FRAME_TYPE_MAX]; // may be 0 + BOOLEAN AutoDetect[ISN_FRAME_TYPE_MAX]; // remove if net number can't be found + BOOLEAN DefaultAutoDetect[ISN_FRAME_TYPE_MAX]; // use this if multiple or none found + ULONG Parameters[BINDING_PARAMETERS]; // index defined above + PDRIVER_OBJECT DriverObject; // used for logging errors + +} BINDING_CONFIG, * PBINDING_CONFIG; + + +NTSTATUS +IpxGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ); + +VOID +IpxFreeConfiguration ( + IN PCONFIG Config + ); + +VOID +IpxWriteDefaultAutoDetectType( + IN PUNICODE_STRING RegistryPath, + IN struct _ADAPTER * Adapter, + IN ULONG FrameType + ); + +#ifdef _PNP_POWER +NTSTATUS +IpxPnPGetVirtualNetworkNumber ( + IN PCONFIG Config + ); + +NTSTATUS +IpxPnPGetAdapterParameters( + IN PCONFIG Config, + IN PNDIS_STRING DeviceName, + IN OUT PBINDING_CONFIG Binding + ); +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/ipx/device.c b/private/ntos/tdi/isnp/ipx/device.c new file mode 100644 index 000000000..f2d9d19f4 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/device.c @@ -0,0 +1,599 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + device.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxCreateDevice) +#endif + + + +VOID +IpxRefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Device->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement(&Device->ReferenceCount); + +} /* IpxRefDevice */ + + +VOID +IpxDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + IpxDestroyDevice (Device); + } + +} /* IpxDerefDevice */ + + +NTSTATUS +IpxCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN ULONG SegmentCount, + IN OUT PDEVICE *DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + SegmentCount - The number of segments in the RIP router table. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + ULONG LocksOffset; + ULONG SegmentsOffset; + ULONG DeviceNameOffset; + UINT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors) and the RIP fields. + // + + DeviceSize = sizeof(DEVICE) + + (sizeof(CTELock) * SegmentCount) + + (sizeof(ROUTER_SEGMENT) * SegmentCount) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + IPX_DEBUG(DEVICE, ("Create device %ws failed %lx\n", DeviceName->Buffer, status)); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + Device = (PDEVICE)deviceObject->DeviceExtension; + + IPX_DEBUG(DEVICE, ("Create device %ws succeeded %lx\n", DeviceName->Buffer)); + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + Device->DeviceObject = deviceObject; + + LocksOffset = sizeof(DEVICE); + SegmentsOffset = LocksOffset + (sizeof(CTELock) * SegmentCount); + DeviceNameOffset = SegmentsOffset + (sizeof(ROUTER_SEGMENT) * SegmentCount); + + // + // Set some internal pointers. + // + + Device->SegmentLocks = (CTELock *)(((PUCHAR)Device) + LocksOffset); + Device->Segments = (PROUTER_SEGMENT)(((PUCHAR)Device) + SegmentsOffset); + Device->SegmentCount = SegmentCount; + + for (i = 0; i < SegmentCount; i++) { + + CTEInitLock (&Device->SegmentLocks[i]); + InitializeListHead (&Device->Segments[i].WaitingForRoute); + InitializeListHead (&Device->Segments[i].FindWaitingForRoute); + InitializeListHead (&Device->Segments[i].WaitingLocalTarget); + InitializeListHead (&Device->Segments[i].WaitingReripNetnum); + InitializeListHead (&Device->Segments[i].Entries); + Device->Segments[i].EnumerateLocation = &Device->Segments[i].Entries; + + } + + // + // Copy over the device name. + // + + Device->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + Device->DeviceName = (PWCHAR)(((PUCHAR)Device) + DeviceNameOffset); + RtlCopyMemory( + Device->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + Device->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + Device->ReferenceCount = 1; +#if DBG + Device->RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->Signature1, "IDC1", 4); + RtlCopyMemory(Device->Signature2, "IDC2", 4); +#endif + + Device->Information.Version = 0x0100; + Device->Information.MaxSendSize = 0; // no sends allowed + Device->Information.MaxConnectionUserData = 0; + Device->Information.ServiceFlags = + TDI_SERVICE_CONNECTIONLESS_MODE | TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_ROUTE_DIRECTED; + Device->Information.MinimumLookaheadData = 128; + Device->Information.NumberOfResources = IPX_TDI_RESOURCES; + KeQuerySystemTime (&Device->Information.StartTime); + + Device->Statistics.Version = 0x0100; + +#if 0 + // + // These will be filled in after all the binding is done. + // + + Device->Information.MaxDatagramSize = 0; + Device->Information.MaximumLookaheadData = 0; + + + Device->SourceRoutingUsed = FALSE; + Device->SourceRoutingTime = 0; + Device->RipPacketCount = 0; + + Device->RipShortTimerActive = FALSE; + Device->RipSendTime = 0; +#endif + + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&Device->AddressResource); + +#ifdef _PNP_POWER + // + // Init the resource that guards the binding array/indices + // + CTEInitLock (&Device->BindAccessLock); +#endif _PNP_POWER + + InitializeListHead (&Device->WaitingRipPackets); + CTEInitTimer (&Device->RipShortTimer); + CTEInitTimer (&Device->RipLongTimer); + + CTEInitTimer (&Device->SourceRoutingTimer); + + // + // initialize the various fields in the device context + // + + CTEInitLock (&Device->Interlock); + CTEInitLock (&Device->Lock); + CTEInitLock (&Device->SListsLock); + + Device->ControlChannelIdentifier.QuadPart = 1; + + InitializeListHead (&Device->GlobalSendPacketList); + InitializeListHead (&Device->GlobalReceivePacketList); + InitializeListHead (&Device->GlobalReceiveBufferList); +#if BACK_FILL + InitializeListHead (&Device->GlobalBackFillPacketList); +#endif + + InitializeListHead (&Device->AddressNotifyQueue); + InitializeListHead (&Device->LineChangeQueue); + + for (i = 0; i < IPX_ADDRESS_HASH_COUNT; i++) { + InitializeListHead (&Device->AddressDatabases[i]); + } + +#if BACK_FILL + InitializeListHead (&Device->BackFillPoolList); +#endif + InitializeListHead (&Device->SendPoolList); + InitializeListHead (&Device->ReceivePoolList); + +#ifdef _PNP_POWER + InitializeListHead (&Device->BindingPoolList); +#endif + + ExInitializeSListHead (&Device->SendPacketList); + ExInitializeSListHead (&Device->ReceivePacketList); +#if BACK_FILL + ExInitializeSListHead (&Device->BackFillPacketList); +#endif + +#ifdef _PNP_POWER + ExInitializeSListHead (&Device->BindingList); +#endif + +#if 0 + Device->MemoryUsage = 0; + Device->SendPacketList.Next = NULL; + Device->ReceivePacketList.Next = NULL; + Device->Bindings = NULL; + Device->BindingCount = 0; +#endif + + KeQuerySystemTime (&Device->IpxStartTime); + + Device->State = DEVICE_STATE_CLOSED; + Device->AutoDetectState = AUTO_DETECT_STATE_INIT; + + Device->Type = IPX_DEVICE_SIGNATURE; + Device->Size = sizeof (DEVICE); + + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} /* IpxCreateDevice */ + + +VOID +IpxDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PIPX_SEND_POOL SendPool; + PIPX_SEND_PACKET SendPacket; + PIPX_RECEIVE_POOL ReceivePool; + PIPX_RECEIVE_PACKET ReceivePacket; + PIPX_ROUTE_ENTRY RouteEntry; + UINT SendPoolSize; + UINT ReceivePoolSize; + UINT i; +#if BACK_FILL + PIPX_SEND_POOL BackFillPool; + UINT BackFillPoolSize; + PIPX_SEND_PACKET BackFillPacket; +#endif + +#ifdef _PNP_POWER + PIPX_BINDING_POOL BindingPool; + UINT BindingPoolSize; + PBINDING Binding; +#endif + + IPX_DEBUG (DEVICE, ("Destroy device %lx\n", Device)); + + // + // Take all the packets out of its pools. + // + +#if _PNP_POWER + BindingPoolSize = FIELD_OFFSET (IPX_BINDING_POOL, Bindings[0]) + + (sizeof(BINDING) * Device->InitBindings); + + while (!IsListEmpty (&Device->BindingPoolList)) { + + p = RemoveHeadList (&Device->BindingPoolList); + BindingPool = CONTAINING_RECORD (p, IPX_BINDING_POOL, Linkage); + IPX_DEBUG (PACKET, ("Free binding pool %lx\n", BindingPool)); + IpxFreeMemory (BindingPool, BindingPoolSize, MEMORY_PACKET, "BindingPool"); + + } +#endif + +#ifdef IPX_OWN_PACKETS + +#if BACK_FILL + BackFillPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams); + while (!IsListEmpty (&Device->BackFillPoolList)) { + + p = RemoveHeadList (&Device->BackFillPoolList); + BackFillPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + for (i = 0; i < BackFillPool->PacketCount; i++) { + BackFillPacket = &BackFillPool->Packets[i]; + IpxDeinitializeBackFillPacket (Device, BackFillPacket); + } + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", BackFillPool)); + IpxFreeMemory (BackFillPool, BackFillPoolSize, MEMORY_PACKET, "BackPool"); + } +#endif + + SendPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams) + + (((IPX_MAXIMUM_MAC + sizeof(IPX_HEADER) + 3) & ~3) * Device->InitDatagrams); + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + for (i = 0; i < SendPool->PacketCount; i++) { + + SendPacket = &SendPool->Packets[i]; + IpxDeinitializeSendPacket (Device, SendPacket); + + } + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", SendPool)); + IpxFreeMemory (SendPool, SendPoolSize, MEMORY_PACKET, "SendPool"); + } + + ReceivePoolSize = FIELD_OFFSET (IPX_RECEIVE_POOL, Packets[0]) + + (sizeof(IPX_RECEIVE_PACKET) * Device->InitReceivePackets); + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, IPX_RECEIVE_POOL, Linkage); + + for (i = 0; i < ReceivePool->PacketCount; i++) { + + ReceivePacket = &ReceivePool->Packets[i]; + IpxDeinitializeReceivePacket (Device, ReceivePacket); + + } + + IPX_DEBUG (PACKET, ("Free receive packet pool %lx\n", ReceivePool)); + IpxFreeMemory (ReceivePool, ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + } +#else + +#if BACK_FILL + + while (s = IPX_POP_ENTRY_LIST(&Device->BackFillPacketList, &Device->Lock)) { + PIPX_SEND_RESERVED Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + IPX_SEND_PACKET BackFillPacket; + + BackFillPacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeBackFillPacket (Device, &BackFillPacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED)); + } + + while (!IsListEmpty (&Device->BackFillPoolList)) { + + p = RemoveHeadList (&Device->BackFillPoolList); + BackFillPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", BackFillPool)); + NdisFreePacketPool (BackFillPool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (BackFillPool, sizeof(IPX_SEND_POOL), MEMORY_PACKET, "BafiPool"); + } +#endif + + while (s = IPX_POP_ENTRY_LIST(&Device->SendPacketList, &Device->Lock)){ + PIPX_SEND_RESERVED Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + IPX_SEND_PACKET SendPacket; + PUCHAR Header = Reserved->Header; + + SendPacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeSendPacket (Device, &SendPacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED)); + } + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, IPX_SEND_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", SendPool)); + NdisFreePacketPool (SendPool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (SendPool->Header, PACKET_HEADER_SIZE * Device->InitDatagrams, MEMORY_PACKET, "SendPool"); + + IpxFreeMemory (SendPool, sizeof(IPX_SEND_POOL), MEMORY_PACKET, "SendPool"); + } + + while (s = IPX_POP_ENTRY_LIST(&Device->ReceivePacketList, &Device->Lock)){ + PIPX_RECEIVE_RESERVED Reserved = CONTAINING_RECORD (s, IPX_RECEIVE_RESERVED, PoolLinkage); + IPX_RECEIVE_PACKET ReceivePacket; + + ReceivePacket.Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + IpxDeinitializeReceivePacket (Device, &ReceivePacket); + Device->MemoryUsage -= (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_RECEIVE_RESERVED)); + } + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, IPX_RECEIVE_POOL, Linkage); + + IPX_DEBUG (PACKET, ("Free packet pool %lx\n", ReceivePool)); + NdisFreePacketPool (ReceivePool->PoolHandle); + Device->MemoryUsage -= FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]); + + IpxFreeMemory (ReceivePool, sizeof(IPX_RECEIVE_POOL), MEMORY_PACKET, "ReceivePool"); + } + +#endif IPX_OWN_PACKETS + // + // Destroy all rip table entries. + // + + for (i = 0; i < Device->SegmentCount; i++) { + + RouteEntry = RipGetFirstRoute(i); + while (RouteEntry != NULL) { + + (VOID)RipDeleteRoute(i, RouteEntry); + IpxFreeMemory(RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + RouteEntry = RipGetNextRoute(i); + + } + + } + + IPX_DEBUG (DEVICE, ("Final memory use is %d\n", Device->MemoryUsage)); +#if DBG + for (i = 0; i < MEMORY_MAX; i++) { + if (IpxMemoryTag[i].BytesAllocated != 0) { + IPX_DEBUG (DEVICE, ("Tag %d: %d bytes left\n", i, IpxMemoryTag[i].BytesAllocated)); + } + } +#endif + + // + // If we are being unloaded then someone is waiting for this + // event to finish the cleanup, since we may be at DISPATCH_LEVEL; + // otherwise it is during load and we can just kill ourselves here. + // + + if (Device->UnloadWaiting) { + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + + } else { + + CTEAssert (KeGetCurrentIrql() < DISPATCH_LEVEL); + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice (Device->DeviceObject); + } + +} /* IpxDestroyDevice */ + diff --git a/private/ntos/tdi/isnp/ipx/dirs b/private/ntos/tdi/isnp/ipx/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/ipx/driver.c b/private/ntos/tdi/isnp/ipx/driver.c new file mode 100644 index 000000000..7c32cd0e3 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/driver.c @@ -0,0 +1,4219 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + + Sanjay Anand (SanjayAn) 18-Sept-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + + +PDEVICE IpxDevice = NULL; +PIPX_PADDING_BUFFER IpxPaddingBuffer = NULL; + +#if DBG + +UCHAR IpxTempDebugBuffer[150]; +ULONG IpxDebug = 0x0; +ULONG IpxMemoryDebug = 0xffffffd3; +UCHAR IpxDebugMemory[IPX_MEMORY_LOG_SIZE][64]; +PUCHAR IpxDebugMemoryLoc = IpxDebugMemory[0]; +PUCHAR IpxDebugMemoryEnd = IpxDebugMemory[IPX_MEMORY_LOG_SIZE]; + +VOID +IpxDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (IpxTempDebugBuffer, 150); + ArgLen = vsprintf(IpxTempDebugBuffer, FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (IpxDebugMemoryLoc, 64); + RtlCopyMemory( IpxDebugMemoryLoc, IpxTempDebugBuffer, ArgLen ); + + IpxDebugMemoryLoc += 64; + if (IpxDebugMemoryLoc >= IpxDebugMemoryEnd) { + IpxDebugMemoryLoc = IpxDebugMemory[0]; + } + } +} + + +DEFINE_LOCK_STRUCTURE(IpxMemoryInterlock); +MEMORY_TAG IpxMemoryTag[MEMORY_MAX]; + +DEFINE_LOCK_STRUCTURE(IpxGlobalInterlock); + +#endif + +#if DBG + +// +// Use for debug printouts +// + +PUCHAR FrameTypeNames[5] = { "Ethernet II", "802.3", "802.2", "SNAP", "Arcnet" }; +#define OutputFrameType(_Binding) \ + (((_Binding)->Adapter->MacInfo.MediumType == NdisMediumArcnet878_2) ? \ + FrameTypeNames[4] : \ + FrameTypeNames[(_Binding)->FrameType]) +#endif + + +#ifdef IPX_PACKET_LOG + +ULONG IpxPacketLogDebug = IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_SEND_OTHER; +USHORT IpxPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(IpxPacketLogLock); +IPX_PACKET_LOG_ENTRY IpxPacketLog[IPX_PACKET_LOG_LENGTH]; +PIPX_PACKET_LOG_ENTRY IpxPacketLogLoc = IpxPacketLog; +PIPX_PACKET_LOG_ENTRY IpxPacketLogEnd = &IpxPacketLog[IPX_PACKET_LOG_LENGTH]; + +VOID +IpxLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID IpxHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PIPX_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&IpxPacketLogLock, &LockHandle); + + PacketLog = IpxPacketLogLoc; + + ++IpxPacketLogLoc; + if (IpxPacketLogLoc >= IpxPacketLogEnd) { + IpxPacketLogLoc = IpxPacketLog; + } + *(UNALIGNED ULONG *)IpxPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&IpxPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(IPX_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, Length); + } else { + RtlCopyMemory(&PacketLog->IpxHeader, IpxHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* IpxLogPacket */ + +#endif // IPX_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +// +// This is now shared with other modules +// +#ifndef _PNP_POWER +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, + IN PUNICODE_STRING RegistryPath + ); + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ); + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigAdapter, + IN ULONG FrameTypeIndex + ); + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ); +#endif _PNP_POWER + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) + +// +// These routines can be called at any time in case of PnP. +// +#ifndef _PNP_POWER +#pragma alloc_text(INIT,IpxResolveAutoDetect) +#pragma alloc_text(INIT,IpxResolveBindingSets) +#pragma alloc_text(INIT,IpxBindToAdapter) +#endif + +#endif + +UCHAR VirtualNode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + +ULONG IpxFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the IPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of IPX's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UINT SuccessfulOpens, ValidBindings; +#ifdef _PNP_POWER + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("NWLNKIPX"); +#else + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("IPX Transport"); +#endif + PDEVICE Device; + PBINDING Binding; + PADAPTER Adapter; + ULONG BindingCount, BindingIndex; + PBINDING * BindingArray; + PLIST_ENTRY p; + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG Temp; + UINT i; + BOOLEAN CountedWan; + + PCONFIG Config = NULL; + PBINDING_CONFIG ConfigBinding; + +#if 0 + DbgPrint ("IPX: FailLoad at %lx\n", &IpxFailLoad); + DbgBreakPoint(); + + if (IpxFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + + // + // This ordering matters because we use it to quickly + // determine if packets are internally generated or not. + // + + CTEAssert (IDENTIFIER_NB < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_SPX < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP < IDENTIFIER_IPX); + CTEAssert (IDENTIFIER_RIP_INTERNAL > IDENTIFIER_IPX); + + // + // We assume that this structure is not packet in between + // the fields. + // + + CTEAssert (FIELD_OFFSET (TDI_ADDRESS_IPX, Socket) + sizeof(USHORT) == 12); + + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + + IPX_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&IpxGlobalInterlock); + CTEInitLock (&IpxMemoryInterlock); + for (i = 0; i < MEMORY_MAX; i++) { + IpxMemoryTag[i].Tag = i; + IpxMemoryTag[i].BytesAllocated = 0; + } +#endif +#ifdef IPX_PACKET_LOG + CTEInitLock (&IpxPacketLogLock); +#endif + +#ifdef IPX_OWN_PACKETS + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + IPX_DEBUG (DEVICE, ("IPX loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = IpxGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed, it logged an error. + // + + PANIC (" Failed to initialize transport, IPX initialization failed.\n"); + return status; + + } + +#ifdef _PNP_POWER + // + // Initialize the TDI layer. + // + TdiInitialize(); +#endif + + // + // make ourselves known to the NDIS wrapper. + // + + status = IpxRegisterProtocol ((PNDIS_STRING)&ProtocolName); + + if (!NT_SUCCESS (status)) { + + IpxFreeConfiguration(Config); + PANIC ("IpxInitialize: RegisterProtocol failed!\n"); + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 607, + status, + NULL, + 0, + NULL); + + return status; + + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = IpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = IpxDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = IpxDispatchDeviceControl; + + DriverObject->DriverUnload = IpxUnload; + + SuccessfulOpens = 0; + + status = IpxCreateDevice( + DriverObject, + &Config->DeviceName, + Config->Parameters[CONFIG_RIP_TABLE_SIZE], + &Device); + + if (!NT_SUCCESS (status)) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return status; + } + + IpxDevice = Device; + + + // + // Save the relevant configuration parameters. + // + + Device->DedicatedRouter = (BOOLEAN)(Config->Parameters[CONFIG_DEDICATED_ROUTER] != 0); + Device->InitDatagrams = Config->Parameters[CONFIG_INIT_DATAGRAMS]; + Device->MaxDatagrams = Config->Parameters[CONFIG_MAX_DATAGRAMS]; + Device->RipAgeTime = Config->Parameters[CONFIG_RIP_AGE_TIME]; + Device->RipCount = Config->Parameters[CONFIG_RIP_COUNT]; + Device->RipTimeout = + ((Config->Parameters[CONFIG_RIP_TIMEOUT] * 500) + (RIP_GRANULARITY/2)) / + RIP_GRANULARITY; + Device->RipUsageTime = Config->Parameters[CONFIG_RIP_USAGE_TIME]; + Device->SourceRouteUsageTime = Config->Parameters[CONFIG_ROUTE_USAGE_TIME]; + Device->SocketUniqueness = Config->Parameters[CONFIG_SOCKET_UNIQUENESS]; + Device->SocketStart = (USHORT)Config->Parameters[CONFIG_SOCKET_START]; + Device->SocketEnd = (USHORT)Config->Parameters[CONFIG_SOCKET_END]; + Device->MemoryLimit = Config->Parameters[CONFIG_MAX_MEMORY_USAGE]; + Device->VerifySourceAddress = (BOOLEAN)(Config->Parameters[CONFIG_VERIFY_SOURCE_ADDRESS] != 0); + + Device->InitReceivePackets = (Device->InitDatagrams + 1) / 2; + Device->InitReceiveBuffers = (Device->InitDatagrams + 1) / 2; + + Device->MaxReceivePackets = 10; // BUGBUG: config this? + Device->MaxReceiveBuffers = 10; + +#ifdef _PNP_POWER + Device->InitBindings = 5; // BUGBUG: config this? + + // + // RAS max is 240 (?) + 10 max LAN + // + Device->MaxPoolBindings = 250; // BUGBUG: config this? +#endif + + // + // Have to reverse this. + // +#ifndef _PNP_POWER +// +// Look at this only when the first adapter appears. +// + Temp = Config->Parameters[CONFIG_VIRTUAL_NETWORK]; + Device->VirtualNetworkNumber = REORDER_ULONG (Temp); +#endif + + Device->VirtualNetworkOptional = (BOOLEAN)(Config->Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + Device->CurrentSocket = Device->SocketStart; + + Device->EthernetPadToEven = (BOOLEAN)(Config->Parameters[CONFIG_ETHERNET_PAD] != 0); + Device->EthernetExtraPadding = (Config->Parameters[CONFIG_ETHERNET_LENGTH] & 0xfffffffe) + 1; + + Device->SingleNetworkActive = (BOOLEAN)(Config->Parameters[CONFIG_SINGLE_NETWORK] != 0); + Device->DisableDialoutSap = (BOOLEAN)(Config->Parameters[CONFIG_DISABLE_DIALOUT_SAP] != 0); + Device->DisableDialinNetbios = (UCHAR)(Config->Parameters[CONFIG_DISABLE_DIALIN_NB]); + +#ifdef _PNP_POWER +// +// Used later to access the registry. +// + Device->RegistryPathBuffer = Config->RegistryPathBuffer; + Device->RegistryPath.Length = RegistryPath->Length; + Device->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + Device->RegistryPath.Buffer = Device->RegistryPathBuffer; +#endif _PNP_POWER + + // + // ActiveNetworkWan will start as FALSE, which is correct. + // + + // + // Allocate our initial packet pool. We do not allocate + // receive and receive buffer pools until we need them, + // because in many cases we never do. + // + +#if BACK_FILL + IpxAllocateBackFillPool (Device); +#endif + + IpxAllocateSendPool (Device); + +#ifdef _PNP_POWER + IpxAllocateBindingPool (Device); +#endif + + // + // Allocate one 1-byte buffer for odd length packets. + // + + IpxPaddingBuffer = IpxAllocatePaddingBuffer(Device); + + if ( IpxPaddingBuffer == (PIPX_PADDING_BUFFER)NULL ) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_RESOURCE_POOL, + 801, + STATUS_INSUFFICIENT_RESOURCES, + NULL, + 0, + NULL); + + IpxFreeConfiguration(Config); + IpxDeregisterProtocol(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the loopback structures + // + IpxInitLoopback(); + +// +// All this will be done on appearance of adapters. +// + +#ifndef _PNP_POWER + + // + // Bind to all the configured adapters. + // + + InitializeListHead (&Device->InitialBindingList); + + p = Config->BindingList.Flink; + + while (p != &Config->BindingList) { + + ConfigBinding = CONTAINING_RECORD (p, BINDING_CONFIG, Linkage); + p = p->Flink; + + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + + // + // If successful, this queues them on Device->InitialBindingList. + // + + status = IpxBindToAdapter (Device, ConfigBinding, i); + + // + // If this failed because the adapter could not be bound + // to, then don't try any more frame types on this adapter. + // For other failures we do try the other frame types. + // + + if (status == STATUS_DEVICE_DOES_NOT_EXIST) { + break; + } + + if (status != STATUS_SUCCESS) { + continue; + } + + if (ConfigBinding->AutoDetect[i]) { + Device->AutoDetect = TRUE; + } + + ++SuccessfulOpens; + + } + + } + + + IpxFreeConfiguration(Config); + + if (SuccessfulOpens == 0) { + + IpxDereferenceDevice (Device, DREF_CREATE); + + } else { + + IPX_DEFINE_SYNC_CONTEXT (SyncContext); + + // + // Allocate the device binding array and transfer those + // on the list to it. First count up the bindings. + // + + BindingCount = 0; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + Adapter->FirstWanNicId = (USHORT)(BindingCount+1); + Adapter->LastWanNicId = (USHORT)(BindingCount + Adapter->WanNicIdCount); + BindingCount += Adapter->WanNicIdCount; + } else { + ++BindingCount; + } + } + + BindingArray = (PBINDING *)IpxAllocateMemory ((BindingCount+1) * sizeof(BINDING), MEMORY_BINDING, "Binding array"); + + if (BindingArray == NULL) { + + while (!IsListEmpty (&Device->InitialBindingList)) { + p = RemoveHeadList (&Device->InitialBindingList); + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + IpxDestroyBinding (Binding); + } + + IpxDereferenceDevice (Device, DREF_CREATE); + SuccessfulOpens = 0; + goto InitFailed; + } + + RtlZeroMemory (BindingArray, (BindingCount+1) * sizeof(BINDING)); + + // + // Now walk the list transferring bindings to the array. + // + + BindingIndex = 1; + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + ) { + + Binding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + p = p->Flink; // we overwrite the linkage in here, so save it. + + BindingArray[BindingIndex] = Binding; + Binding->NicId = (USHORT)BindingIndex; + + if (Binding->ConfiguredNetworkNumber != 0) { + + // + // If the configured network number is non-zero, then + // use it, unless we are unable to insert a rip table + // entry for it (duplicates are OK because they will + // become binding set members -- BUGBUG: What if the + // duplicate is a different media or frame type, then + // it won't get noted as a binding set). + // + + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // These are a union with the InitialLinkage fields. + // + + Binding->NextBinding = NULL; + Binding->CurrentSendBinding = NULL; + + Adapter = Binding->Adapter; + + if (Adapter->MacInfo.MediumAsync) { + CTEAssert (Adapter->FirstWanNicId == BindingIndex); + BindingIndex += Adapter->WanNicIdCount; + } else { + ++BindingIndex; + } + } + + CTEAssert (BindingIndex == BindingCount+1); + + Device->Bindings = BindingArray; + Device->BindingCount = BindingCount; + + + // + // Queue a request to discover our locally attached + // adapter addresses. This must succeed because we + // just allocated our send packet pool. We need + // to wait for this, either because we are + // auto-detecting or because we need to determine + // if there are multiple cards on the same network. + // + + KeInitializeEvent( + &Device->AutoDetectEvent, + NotificationEvent, + FALSE + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_RUNNING; + + // + // Make this 0; after we are done waiting, which means + // the packet has been completed, we set it to the + // correct value. + // + + Device->IncludedHeaderOffset = 0; + + IPX_BEGIN_SYNC (&SyncContext); + status = RipQueueRequest (0xffffffff, RIP_REQUEST); + IPX_END_SYNC (&SyncContext); + + CTEAssert (status == STATUS_PENDING); + + // + // This is set when this rip send completes. + // + + IPX_DEBUG (AUTO_DETECT, ("Waiting for AutoDetectEvent\n")); + + KeWaitForSingleObject( + &Device->AutoDetectEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_PROCESSING; + + // + // Now that we are done receiving responses, insert the + // current network number for every auto-detect binding + // to the rip database. + // + + for (i = 1; i <= Device->BindingCount; i++) { + + Binding = Device->Bindings[i]; + + // + // Skip empty WAN slots or bindings that were configured + // for a certain network number, we inserted those above. + // If no network number was detected, also skip it. + // + + if ((!Binding) || + (Binding->ConfiguredNetworkNumber != 0) || + (Binding->TentativeNetworkAddress == 0)) { + + continue; + } + + IPX_DEBUG (AUTO_DETECT, ("Final score for %lx on %lx is %d - %d\n", + REORDER_ULONG(Binding->TentativeNetworkAddress), + Binding, + Binding->MatchingResponses, + Binding->NonMatchingResponses)); + + // + // We don't care about the status. + // + + status = RipInsertLocalNetwork( + Binding->TentativeNetworkAddress, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if ((status != STATUS_SUCCESS) && + (status != STATUS_DUPLICATE_NAME)) { + + // + // We failed to insert, keep it at zero, hopefully + // we will be able to update later. + // + +#if DBG + DbgPrint ("IPX: Could not insert net %lx for binding %lx\n", + REORDER_ULONG(Binding->LocalAddress.NetworkAddress), + Binding); +#endif + CTEAssert (Binding->LocalAddress.NetworkAddress == 0); + + } else { + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + } + + ValidBindings = Device->BindingCount; + + if (Device->AutoDetect) { + + ValidBindings = IpxResolveAutoDetect (Device, ValidBindings, RegistryPath); + + } + + Device->ValidBindings = ValidBindings; + + // + // Now see if any bindings are actually on the same + // network. This sets Device->HighestExternalNicId + // and Device->HighestType20NicId. + // + + IpxResolveBindingSets (Device, ValidBindings); + + + // + // For multiple adapters, use the offset of the first...why not. + // + +#if 0 + Device->IncludedHeaderOffset = Device->Bindings[1]->DefHeaderSize; +#endif + + Device->IncludedHeaderOffset = MAC_HEADER_SIZE; + + // + // Success; see if there is a virtual network configured. + // + + if (Device->VirtualNetworkNumber != 0) { + + status = RipInsertLocalNetwork( + Device->VirtualNetworkNumber, + 0, // NIC ID + Device->Bindings[1]->Adapter->NdisBindingHandle, + 1); + + if (status != STATUS_SUCCESS) { + + // + // Log the appropriate error, then ignore the + // virtual network. If the error was + // INSUFFICIENT_RESOURCES, the RIP module + // will have already logged an error. + // + + if (status == STATUS_DUPLICATE_NAME) { + + IPX_DEBUG (AUTO_DETECT, ("Ignoring virtual network %lx, conflict\n", REORDER_ULONG (Device->VirtualNetworkNumber))); + + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_IPX_INTERNAL_NET_INVALID, + 0, + REORDER_ULONG (Device->VirtualNetworkNumber)); + } + + Device->VirtualNetworkNumber = 0; + goto NoVirtualNetwork; + + } + + Device->VirtualNetwork = TRUE; + Device->MultiCardZeroVirtual = FALSE; + RtlCopyMemory(Device->SourceAddress.NodeAddress, VirtualNode, 6); + Device->SourceAddress.NetworkAddress = Device->VirtualNetworkNumber; + + // + // This will get set to FALSE if RIP binds. + // + + Device->RipResponder = TRUE; + + } else { + +NoVirtualNetwork: + + Device->VirtualNetwork = FALSE; + + // + // See if we need to be set up for the fake + // virtual network. + // + + if (ValidBindings > 1) { + + CTEAssert (Device->VirtualNetworkOptional); + + // + // In this case we return as our local node the + // address of the first card. We will also only + // direct SAP sends to that card. + // + + Device->MultiCardZeroVirtual = TRUE; + + } else { + + Device->MultiCardZeroVirtual = FALSE; + } + + RtlCopyMemory(&Device->SourceAddress, &Device->Bindings[1]->LocalAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + + } + + + // + // Now get SapNicCount -- regular adapters are counted + // as one, but all the WAN lines together only count for one. + // We also calculate FirstLanNicId and FirstWanNicId here. + // + + CountedWan = FALSE; + Device->SapNicCount = 0; + + Device->FirstLanNicId = (USHORT)-1; + Device->FirstWanNicId = (USHORT)-1; + + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + + if (Device->Bindings[i]) { + + if (Device->Bindings[i]->Adapter->MacInfo.MediumAsync) { + + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = i; + } + + if (CountedWan) { + continue; + } else { + CountedWan = TRUE; + } + + } else { + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = i; + } + + } + + } else { + + // + // NULL bindings are WANs and are not the first one, + // so don't count them. + // + + CTEAssert (Device->FirstWanNicId != -1); + CTEAssert (CountedWan); + continue; + } + + ++Device->SapNicCount; + + } + } + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = 1; + } + if (Device->FirstWanNicId == (USHORT)-1) { + Device->FirstWanNicId = 1; + } + + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = Device->Bindings[1]->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = Device->Bindings[1]->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = Device->Bindings[1]->RealMaxDatagramSize; // smallest binding value + + if (Device->Bindings[1]->LineUp) { + LinkSpeed = Device->Bindings[1]->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = Device->Bindings[1]->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= ValidBindings; i++) { + + Binding = Device->Bindings[i]; + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = Device->Bindings[1]->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + Device->State = DEVICE_STATE_OPEN; + Device->AutoDetectState = AUTO_DETECT_STATE_DONE; + + IPX_DEBUG (DEVICE, ("Node is %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x, ", + Device->SourceAddress.NodeAddress[0], Device->SourceAddress.NodeAddress[1], + Device->SourceAddress.NodeAddress[2], Device->SourceAddress.NodeAddress[3], + Device->SourceAddress.NodeAddress[4], Device->SourceAddress.NodeAddress[5])); + IPX_DEBUG (DEVICE, ("Network is %lx\n", + REORDER_ULONG (Device->SourceAddress.NetworkAddress))); + + + // + // Start the timer which updates the RIP database + // periodically. For the first one we do a ten + // second timeout (hopefully this is enough time + // for RIP to start if it is going to). + // + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + } + +InitFailed: + + if (SuccessfulOpens == 0) { + + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + return STATUS_DEVICE_DOES_NOT_EXIST; + + } else { + + return STATUS_SUCCESS; + } + +#else // _PNP_POWER +{ + PBIND_ARRAY_ELEM BindingArray; + PTA_ADDRESS TdiRegistrationAddress; + + // + // Pre-allocate the binding array + // Later, we will allocate the LAN/WAN and SLAVE bindings separately + // [BUGBUGZZ] Read the array size from registry? + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + Device->MaxBindings = MAX_BINDINGS; + + // + // Allocate the TA_ADDRESS structure - this will be used in all TdiRegisterNetAddress + // notifications. + // + TdiRegistrationAddress = (PTA_ADDRESS)IpxAllocateMemory ( + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + if (TdiRegistrationAddress == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxFreeMemory(BindingArray, sizeof(BindingArray), MEMORY_BINDING, "Binding Array"); + IpxDereferenceDevice (Device, DREF_CREATE); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + RtlZeroMemory (BindingArray, MAX_BINDINGS * sizeof(BIND_ARRAY_ELEM)); + RtlZeroMemory (TdiRegistrationAddress, 2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)); + + Device->Bindings = BindingArray; + + TdiRegistrationAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + TdiRegistrationAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + + // + // Store the pointer in the Device. + // + Device->TdiRegistrationAddress = TdiRegistrationAddress; + + // + // Device state is loaded, but not opened. It is opened when at least + // one adapter has appeared. + // + Device->State = DEVICE_STATE_LOADED; + + Device->FirstLanNicId = Device->FirstWanNicId = (USHORT)1; // will be changed later + + IpxFreeConfiguration(Config); + + // + // We use this event when unloading to signal that we + // can proceed...initialize it here so we know it is + // ready to go when unload is called. + // + + KeInitializeEvent( + &IpxDevice->UnloadEvent, + NotificationEvent, + FALSE + ); + + return STATUS_SUCCESS; +} +#endif // _PNP_POWER +} /* DriverEntry */ + + +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, +#ifdef _PNP_POWER + IN CTELockHandle *LockHandle1, +#endif + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is called for auto-detect bindings to + remove any bindings that were not successfully found. + It also updates "DefaultAutoDetectType" in the registry + if needed. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + + RegistryPath - The path to the ipx registry, used if we have + to write a value back. + +Return Value: + + The updated number of bindings. + +--*/ + +{ + PBINDING Binding, TmpBinding; + UINT i, j; + + // + // Get rid of any auto-detect devices which we + // could not find nets for. We also remove any + // devices which are not the first ones + // auto-detected on a particular adapter. + // + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + + if (!Binding) { + continue; + } + + // + // If this was auto-detected and was not the default, + // or it was the default, but nothing was detected for + // it *and* something else *was* detected (which means + // we will use that frame type when we get to it), + // we may need to remove this binding. + // + + if (Binding->AutoDetect && + (!Binding->DefaultAutoDetect || + (Binding->DefaultAutoDetect && + (Binding->LocalAddress.NetworkAddress == 0) && + Binding->Adapter->AutoDetectResponse))) { + + if ((Binding->LocalAddress.NetworkAddress == 0) || + (Binding->Adapter->AutoDetectFound)) { + + // + // Remove this binding. + // + + if (Binding->LocalAddress.NetworkAddress == 0) { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) no net found\n", + i, Binding->FrameType)); + } else { + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) adapter already auto-detected\n", + i, Binding->FrameType)); + } + + CTEAssert (Binding->NicId == i); + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + // + // Remove any routes through this NIC, and + // adjust any NIC ID's above this one in the + // database down by one. + // + + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDeleted); + + Binding->Adapter->Bindings[Binding->FrameType] = NULL; + for (j = i+1; j <= ValidBindings; j++) { +#ifndef _PNP_POWER + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#else + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#endif _PNP_POWER + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, NULL); +#else + Device->Bindings[ValidBindings] = NULL; +#endif + --Binding->Adapter->BindingCount; + --ValidBindings; + + --i; // so we check the binding that was just moved. + + // + // Wait 100 ms before freeing the binding, + // in case an indication is using it. + // + + KeStallExecutionProcessor(100000); + + IpxDestroyBinding (Binding); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detected OK\n", + i, Binding->FrameType)); + +#if DBG + DbgPrint ("IPX: Auto-detected non-default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + + // + // Save it in the registry for the next boot. + // +#ifdef _PNP_POWER +// +// This cannot be done at DPC, so, drop the IRQL +// + IPX_FREE_LOCK1(&Device->BindAccessLock, *LockHandle1); + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); + IPX_GET_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + IpxWriteDefaultAutoDetectType( + RegistryPath, + Binding->Adapter, + Binding->FrameType); +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + } + + } else { + + if (Binding->AutoDetect) { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) auto-detect default\n", + i, Binding->FrameType)); + +#if DBG + if (Binding->LocalAddress.NetworkAddress != 0) { + DbgPrint ("IPX: Auto-detected default frame type %s, net %lx\n", + OutputFrameType(Binding), + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); + } else { + DbgPrint ("IPX: Using default auto-detect frame type %s\n", + OutputFrameType(Binding)); + } +#endif + + Binding->Adapter->AutoDetectFound = TRUE; + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Binding %d (%d) not auto-detected\n", + i, Binding->FrameType)); + } + + } + + } + + + for (i = 1; i <= ValidBindings; i++) { +#ifdef _PNP_POWER + if (Binding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (Binding = Device->Bindings[i]) { +#endif + CTEAssert (Binding->NicId == i); + IPX_DEBUG (AUTO_DETECT, ("Binding %lx, type %d, auto %d\n", + Binding, Binding->FrameType, Binding->AutoDetect)); + } + + } + + return ValidBindings; + +} /* IpxResolveAutoDetect */ + + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ) + +/*++ + +Routine Description: + + This routine is called to determine if we have any + binding sets and rearrange the bindings the way we + like. The order is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + +Arguments: + + Device - The IPX device object. + + ValidBindings - The total number of bindings present. + +Return Value: + + None. + +--*/ + +{ + PBINDING Binding, MasterBinding, TmpBinding; + UINT i, j; + ULONG WanCount, DuplicateCount; + + // + // First loop through and push all the wan bindings + // to the end. + // +#ifdef _PNP_POWER + + WanCount = Device->HighestExternalNicId - Device->HighestLanNicId; + +#else + + WanCount = 0; + + // + // For PnP, we dont do this as the bindings are in order + // at the time of insertion + // + for (i = 1; i <= (ValidBindings-WanCount); ) { + + Binding = Device->Bindings[i]; + + if ((Binding == NULL) || Binding->Adapter->MacInfo.MediumAsync) { + + // + // Put this binding at the end, and slide all the + // others down. If it is a NULL WAN binding then we + // don't have to do some of this. + // + +#if DBG + // + // Any non-NULL bindings should be correct in this + // respect at any point. + // + + if (Binding != NULL) { + CTEAssert (Binding->NicId == i); + } +#endif + + // + // If the Binding is NULL we won't have anything in the + // database at this binding, but we still need to adjust + // any NIC ID's in the database which are above this. + // + + RipAdjustForBindingChange ((USHORT)i, (USHORT)ValidBindings, IpxBindingMoved); + + // + // Slide the bindings above this down. + // + + for (j = i+1; j <= ValidBindings; j++) { + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } + + // + // Put this binding at the end. + // + + Device->Bindings[ValidBindings] = Binding; + if (Binding != NULL) { + if ((Binding->Adapter->MacInfo.MediumAsync) && + (Binding->Adapter->FirstWanNicId == Binding->NicId)) { + Binding->Adapter->FirstWanNicId = (USHORT)ValidBindings; + Binding->Adapter->LastWanNicId += (USHORT)(ValidBindings - Binding->NicId); + } + Binding->NicId = (USHORT)ValidBindings; + } + ++WanCount; + + // + // Keep i the same, to check the new binding at + // this position. + // + + } else { + + i++; + + } + + } +#endif _PNP_POWER + // + // Now go through and find the LAN duplicates and + // create binding sets from them. + // + + DuplicateCount = 0; + + for (i = 1; i <= (ValidBindings-(WanCount+DuplicateCount)); ) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + CTEAssert (Binding != NULL); // because we are only looking at LAN bindings + + CTEAssert (!Binding->Adapter->MacInfo.MediumAsync); + + if (Binding->LocalAddress.NetworkAddress == 0) { + i++; + continue; + } + + // + // See if any previous bindings match the + // frame type, medium type, and number of + // this network (for the moment we match on + // frame type and medium type too so that we + // don't have to worry about different frame + // formats and header offsets within a set). + // + + for (j = 1; j < i; j++) { +#ifdef _PNP_POWER + MasterBinding = NIC_ID_TO_BINDING(Device, j); +#else + MasterBinding = Device->Bindings[j]; +#endif + if ((MasterBinding->LocalAddress.NetworkAddress == Binding->LocalAddress.NetworkAddress) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + break; + } + + } + + if (j == i) { + i++; + continue; + } + + // + // We have a duplicate. First slide it down to the + // end. Note that we change any router entries that + // use our real NicId to use the real NicId of the + // master (there should be no entries in the rip + // database that have the NicId of a binding slave). + // + + RipAdjustForBindingChange (Binding->NicId, MasterBinding->NicId, IpxBindingMoved); + + for (j = i+1; j <= ValidBindings; j++) { +#ifdef _PNP_POWER + TmpBinding = NIC_ID_TO_BINDING(Device, j); + INSERT_BINDING(Device, j-1, TmpBinding); +#else + TmpBinding = Device->Bindings[j]; + Device->Bindings[j-1] = TmpBinding; +#endif + if (TmpBinding) { + if ((TmpBinding->Adapter->MacInfo.MediumAsync) && + (TmpBinding->Adapter->FirstWanNicId == TmpBinding->NicId)) { + --TmpBinding->Adapter->FirstWanNicId; + --TmpBinding->Adapter->LastWanNicId; + } + --TmpBinding->NicId; + } + } +#ifdef _PNP_POWER + INSERT_BINDING(Device, ValidBindings, Binding); +#else + Device->Bindings[ValidBindings] = Binding; +#endif + + Binding->NicId = (USHORT)ValidBindings; + ++DuplicateCount; + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %lx is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Keep i the same, to check the new binding at + // this position. + // + + } +#ifndef _PNP_POWER + Device->HighestExternalNicId = (USHORT)(ValidBindings - DuplicateCount); + Device->HighestType20NicId = (USHORT)(ValidBindings-(WanCount+DuplicateCount)); +#else + Device->HighestLanNicId -= (USHORT)DuplicateCount; + + if (Device->HighestLanNicId == 0) { + CTEAssert(FALSE); + } + + Device->HighestExternalNicId -= (USHORT)DuplicateCount; + Device->HighestType20NicId -= (USHORT)DuplicateCount; + Device->SapNicCount -= (USHORT)DuplicateCount; +#endif _PNP_POWER +} /* IpxResolveBindingSets */ + + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding, +#ifdef _PNP_POWER + IN PADAPTER *AdapterPtr, +#endif + IN ULONG FrameTypeIndex + ) + +/*++ + +Routine Description: + + This routine handles binding the transport to a new + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Device - The IPX device object. + + ConfigBinding - The configuration info for this binding. + + AdapterPtr - pointer to the adapter to bind to in case of PnP. + + FrameTypeIndex - The index into ConfigBinding's array of frame + types for this adapter. The routine is called once for + every valid frame type. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + +#ifndef _PNP_POWER + // + // Adapter came in as a parameter + // + PADAPTER Adapter = NULL; +#else + PADAPTER Adapter = *AdapterPtr; +#endif + + PBINDING Binding, OldBinding; + ULONG FrameType, MappedFrameType; + PLIST_ENTRY p; + + // + // We can't bind more than one adapter unless we have a + // virtual network configured or we are allowed to run + // with a virtual network of 0. + // + + if (Device->BindingCount == 1) { + if ((Device->VirtualNetworkNumber == 0) && + (!Device->VirtualNetworkOptional)) { + + IPX_DEBUG (ADAPTER, ("Cannot bind to more than one adapter\n")); + DbgPrint ("IPX: Disallowing multiple bind ==> VirtualNetwork is 0\n"); + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 666, + STATUS_NOT_SUPPORTED, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + return STATUS_NOT_SUPPORTED; + } + } + + + // + // First allocate the memory for the binding. + // + + status = IpxCreateBinding( + Device, + ConfigBinding, + FrameTypeIndex, + ConfigBinding->AdapterName.Buffer, + &Binding); + + if (status != STATUS_SUCCESS) { + return status; + } + + FrameType = ConfigBinding->FrameType[FrameTypeIndex]; + +// +// In PnP case, we dont need to check for existing adapters since +// we supply a NULL adapter in the parameters if it needs to be created +// +#ifndef _PNP_POWER + + // + // Check if there is already an NDIS binding to this adapter, + // and if so, that there is not already a binding with this + // frame type. + // + + + for (p = Device->InitialBindingList.Flink; + p != &Device->InitialBindingList; + p = p->Flink) { + + OldBinding = CONTAINING_RECORD (p, BINDING, InitialLinkage); + + if (RtlEqualMemory( + OldBinding->Adapter->AdapterName, + ConfigBinding->AdapterName.Buffer, + OldBinding->Adapter->AdapterNameLength)) { + + Adapter = OldBinding->Adapter; + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + break; + + } + } +#endif _PNP_POWER + + if (Adapter == NULL) { + + // + // No binding to this adapter exists, so create a + // new one. + // + + status = IpxCreateAdapter( + Device, + &ConfigBinding->AdapterName, + &Adapter); + + if (status != STATUS_SUCCESS) { + IpxDestroyBinding(Binding); + return status; + } + + // + // Save these now (they will be the same for all bindings + // on this adapter). + // + + Adapter->ConfigMaxPacketSize = ConfigBinding->Parameters[BINDING_MAX_PKT_SIZE]; + Adapter->SourceRouting = (BOOLEAN)ConfigBinding->Parameters[BINDING_SOURCE_ROUTE]; + Adapter->EnableFunctionalAddress = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_FUNC_ADDR]; + Adapter->EnableWanRouter = (BOOLEAN)ConfigBinding->Parameters[BINDING_ENABLE_WAN]; + + Adapter->BindSap = (USHORT)ConfigBinding->Parameters[BINDING_BIND_SAP]; + Adapter->BindSapNetworkOrder = REORDER_USHORT(Adapter->BindSap); + CTEAssert (Adapter->BindSap == 0x8137); + CTEAssert (Adapter->BindSapNetworkOrder == 0x3781); + + // + // Now fire up NDIS so this adapter talks + // + + status = IpxInitializeNdis( + Adapter, + ConfigBinding); + + if (!NT_SUCCESS (status)) { + + // + // Log an error. + // + + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + ConfigBinding->AdapterName.Buffer, + 0, + NULL); + + IpxDestroyAdapter (Adapter); + IpxDestroyBinding (Binding); + + // + // Returning this status informs the caller to not + // try any more frame types on this adapter. + // + + return STATUS_DEVICE_DOES_NOT_EXIST; + + } + + // + // For 802.5 bindings we need to start the source routing + // timer to time out old entries. + // + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && + (Adapter->SourceRouting)) { + + if (!Device->SourceRoutingUsed) { + + Device->SourceRoutingUsed = TRUE; + IpxReferenceDevice (Device, DREF_SR_TIMER); + + CTEStartTimer( + &Device->SourceRoutingTimer, + 60000, // one minute timeout + MacSourceRoutingTimeout, + (PVOID)Device); + } + } + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + IPX_DEBUG (ADAPTER, ("Create new bind to adapter %ws, type %d\n", + ConfigBinding->AdapterName.Buffer, + MappedFrameType)); + + IpxAllocateReceiveBufferPool (Adapter); + +#ifdef _PNP_POWER + *AdapterPtr = Adapter; +#endif + } +#ifdef _PNP_POWER + else { + // + // get the mapped frame type + // + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + FrameType, + &MappedFrameType); + + if (Adapter->Bindings[MappedFrameType] != NULL) { + + IPX_DEBUG (ADAPTER, ("Bind to adapter %ws, type %d exists\n", + Adapter->AdapterName, + MappedFrameType)); + + // + // If this was the auto-detect default for this + // adapter and it failed, we need to make the + // previous one the default, so that at least + // one binding will stick around. + // + + if (ConfigBinding->DefaultAutoDetect[FrameTypeIndex]) { + IPX_DEBUG (ADAPTER, ("Default auto-detect changed from %d to %d\n", + FrameType, MappedFrameType)); + Adapter->Bindings[MappedFrameType]->DefaultAutoDetect = TRUE; + } + + IpxDestroyBinding (Binding); + + return STATUS_NOT_SUPPORTED; + } + + IPX_DEBUG (ADAPTER, ("Using existing bind to adapter %ws, type %d\n", + Adapter->AdapterName, + MappedFrameType)); + } +#endif _PNP_POWER + + // + // The local node address starts out the same as the + // MAC address of the adapter (on WAN this will change). + // The local MAC address can also change for WAN. + // + + RtlCopyMemory (Binding->LocalAddress.NodeAddress, Adapter->LocalMacAddress.Address, 6); + RtlCopyMemory (Binding->LocalMacAddress.Address, Adapter->LocalMacAddress.Address, 6); + + + // + // Save the send handler. + // + + Binding->SendFrameHandler = NULL; + Binding->FrameType = MappedFrameType; + + // + // BUGBUG: Put this in InitializeBindingInfo. + // + + switch (Adapter->MacInfo.RealMediumType) { + case NdisMedium802_3: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrame802_3802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_3802_2; break; + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrame802_3EthernetII; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_3Snap; break; + } + break; + case NdisMedium802_5: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrame802_5802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrame802_5Snap; break; + } + break; + case NdisMediumFddi: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameFddi802_3; break; + case ISN_FRAME_TYPE_802_2: Binding->SendFrameHandler = IpxSendFrameFddi802_2; break; + case ISN_FRAME_TYPE_SNAP: Binding->SendFrameHandler = IpxSendFrameFddiSnap; break; + } + break; + case NdisMediumArcnet878_2: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_802_3: Binding->SendFrameHandler = IpxSendFrameArcnet878_2; break; + } + break; + case NdisMediumWan: + switch (MappedFrameType) { + case ISN_FRAME_TYPE_ETHERNET_II: Binding->SendFrameHandler = IpxSendFrameWanEthernetII; break; + } + break; + } + + if (Binding->SendFrameHandler == NULL) { + DbgPrint ("BUGBUG!: SendFrameHandler is NULL\n"); + } + + Adapter->Bindings[MappedFrameType] = Binding; + ++Adapter->BindingCount; + + Binding->Adapter = Adapter; + +#ifndef _PNP_POWER + InsertTailList (&Device->InitialBindingList, &Binding->InitialLinkage); +#endif _PNP_POWER + + // + // NicId and ExternalNicId will be filled in later when the binding + // is assigned a spot in the Device->Bindings array. + // + + // + // Initialize the per-binding MAC information + // + + if ((Adapter->ConfigMaxPacketSize == 0) || + (Adapter->MaxSendPacketSize < Adapter->ConfigMaxPacketSize)) { + Binding->MaxSendPacketSize = Adapter->MaxSendPacketSize; + } else { + Binding->MaxSendPacketSize = Adapter->ConfigMaxPacketSize; + } + Binding->MediumSpeed = Adapter->MediumSpeed; + if (Adapter->MacInfo.MediumAsync) { + Binding->LineUp = FALSE; + } else { + Binding->LineUp = TRUE; + } + + MacInitializeBindingInfo( + Binding, + Adapter); + + return STATUS_SUCCESS; + +} /* IpxBindToAdapter */ + + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ) + +/*++ + +Routine Description: + + This routine returns TRUE if the specified SourceAddress indicates + the packet was sent by us, and FALSE otherwise. + +Arguments: + + SourceAddress - The source IPX address. + +Return Value: + + TRUE if the address is local. + +--*/ + +{ + PBINDING Binding; + UINT i; + + // + // First see if it is a virtual network address or not. + // + + if (RtlEqualMemory (VirtualNode, SourceAddress->NodeAddress, 6)) { + + // + // This is us if we have a virtual network configured. + // If we don't have a virtual node, we fall through to the + // other check -- an arcnet card configured as node 1 will + // have what we think of as the "virtual node" as its + // real node address. + // + + if ((IpxDevice->VirtualNetwork) && + (IpxDevice->VirtualNetworkNumber == SourceAddress->NetworkAddress)) { + return TRUE; + } + + } + + // + // Check through our list of adapters to see if one of + // them is the source node. + // + { + ULONG Index = MIN (IpxDevice->MaxBindings, IpxDevice->ValidBindings); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + if (((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) && +#else + if (((Binding = IpxDevice->Bindings[i]) != NULL) && +#endif _PNP_POWER + (RtlEqualMemory (Binding->LocalAddress.NodeAddress, SourceAddress->NodeAddress, 6))) { + return TRUE; + } + } + } + + return FALSE; + +} /* IpxIsAddressLocal */ + + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine handles unbinding the transport from an + adapter. It can be called at any point during the life + of the transport. + +Arguments: + + Binding - The adapter to unbind. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + PADAPTER Adapter = Binding->Adapter; + + Adapter->Bindings[Binding->FrameType] = NULL; + --Adapter->BindingCount; + + IpxDereferenceBinding (Binding, BREF_BOUND); + + if (Adapter->BindingCount == 0) { + + // + // DereferenceAdapter is a NULL macro for load-only. + // + // BUGBUG: Revisit Post 4.0 + // +#ifdef _PNP_LATER + // + // Take away the creation reference. When the in-use ref is taken off, + // we destroy this adapter. + // + IpxDereferenceAdapter(Adapter); +#else + // + // Free the packet pools, etc. and close the + // adapter. + // + + IpxCloseNdis (Adapter); + + IpxDestroyAdapter (Adapter); +#endif + } + + return STATUS_SUCCESS; + +} /* IpxUnBindFromAdapter */ + + +VOID +IpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has IPX open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + + + UNREFERENCED_PARAMETER (DriverObject); + + IpxDevice->State = DEVICE_STATE_STOPPING; + + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + + // + // Walk the list of device contexts. + // + + for (i = 1; i <= IpxDevice->BindingCount; i++) { +#ifdef _PNP_POWER + if ((Binding = NIC_ID_TO_BINDING(IpxDevice, i)) != NULL) { + INSERT_BINDING(IpxDevice, i, NULL); +#else + if (IpxDevice->Bindings[i] != NULL) { + Binding = IpxDevice->Bindings[i]; + IpxDevice->Bindings[i] = NULL; +#endif _PNP_POWER + + IpxUnBindFromAdapter (Binding); + + } + + } + +#ifdef _PNP_POWER + + IpxFreeMemory ( IpxDevice->Bindings, + IpxDevice->MaxBindings * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + // + // Deallocate the TdiRegistrationAddress and RegistryPathBuffer. + // + IpxFreeMemory ( IpxDevice->TdiRegistrationAddress, + (2 * sizeof(USHORT) + sizeof(TDI_ADDRESS_IPX)), + MEMORY_ADDRESS, + "Tdi Address"); + + IpxFreeMemory ( IpxDevice->RegistryPathBuffer, + IpxDevice->RegistryPath.Length + sizeof(WCHAR), + MEMORY_CONFIG, + "RegistryPathBuffer"); + +#endif + + KeResetEvent( + &IpxDevice->UnloadEvent + ); + IpxDevice->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + IpxDereferenceDevice (IpxDevice, DREF_CREATE); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Now free the padding buffer. + // + + IpxFreePaddingBuffer (IpxDevice); + + // + // Now do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&IpxDevice->AddressResource); + IoDeleteDevice (IpxDevice->DeviceObject); + + // + // Finally, remove ourselves as an NDIS protocol. + // + + IpxDeregisterProtocol(); + +} /* IpxUnload */ + + +NTSTATUS +IpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPX device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = IpxDevice; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PREQUEST Request; + UINT i; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + +#ifdef _PNP_POWER + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#else + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = IpxOpenAddress (Device, Request); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = STATUS_NOT_SUPPORTED; + break; + } + + } else { + + CTEGetLock (&Device->Lock, &LockHandle); + + // + // LowPart is in the OPEN_CONTEXT directly. + // HighPart goes into the upper 2 bytes of the OPEN_TYPE. + // + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier.LowPart); + + (ULONG)(REQUEST_OPEN_TYPE(Request)) = (Device->ControlChannelIdentifier.HighPart << 16); + (ULONG)(REQUEST_OPEN_TYPE(Request)) |= IPX_FILE_TYPE_CONTROL; + + ++(Device->ControlChannelIdentifier.QuadPart); + + if (Device->ControlChannelIdentifier.QuadPart > MAX_CCID) { + Device->ControlChannelIdentifier.QuadPart = 1; + } + + CTEFreeLock (&Device->Lock, LockHandle); + + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile->Address + // which is removed by IpxCloseAddressFile. + // + + Status = IpxVerifyAddressFile(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = IpxCloseAddressFile (Device, Request); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + IPX_DEBUG (DEVICE, ("CCID: (%d, %d)\n", ControlChannelId.HighPart, ControlChannelId.LowPart)); + + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + if (Device->UpperDriverControlChannel[i].QuadPart == + ControlChannelId.QuadPart) { + Status = IpxInternalUnbind (Device, i); + break; + } + } + + break; + } + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)(REQUEST_OPEN_TYPE(Request)) & IPX_CC_MASK) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Status = IpxVerifyAddressFile(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + IpxStopAddressFile (AddressFile); + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case IPX_FILE_TYPE_CONTROL: + { + LARGE_INTEGER ControlChannelId; + + CCID_FROM_REQUEST(ControlChannelId, Request); + + // + // Check for any line change IRPs submitted by this + // address. + // + + IpxAbortLineChanges ((PVOID)&ControlChannelId); + + Status = STATUS_SUCCESS; + break; + } + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* IpxDispatchOpenClose */ + +#define IOCTL_IPX_LOAD_SPX _IPX_CONTROL_CODE( 0x5678, METHOD_BUFFERED ) + +NTSYSAPI +NTSTATUS +NTAPI +ZwLoadDriver( + IN PUNICODE_STRING DriverServiceName + ); + + +NTSTATUS +IpxDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + static NDIS_STRING SpxServiceName = NDIS_STRING_CONST ("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NwlnkSpx"); + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_TDI_QUERY_DIRECT_SENDDG_HANDLER: { + + PULONG EntryPoint; + + // + // This is the LanmanServer trying to get the send + // entry point. + // + + IPX_DEBUG (BIND, ("Direct send entry point being returned\n")); + + EntryPoint = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + *EntryPoint = (ULONG)IpxTdiSendDatagram; + + Status = STATUS_SUCCESS; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + break; + } + + case IOCTL_IPX_INTERNAL_BIND: + + // + // This is a client trying to bind. + // + + CTEAssert ((IOCTL_IPX_INTERNAL_BIND & 0x3) == METHOD_BUFFERED); + CTEAssert (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL); + +#ifdef _PNP_POWER + + if ((Device->State == DEVICE_STATE_CLOSED) || + (Device->State == DEVICE_STATE_STOPPING)) { +#else + if (Device->State != DEVICE_STATE_OPEN) { +#endif + Status = STATUS_INVALID_DEVICE_STATE; + + } else { + + Status = IpxInternalBind (Device, Irp); + + } + + CTEAssert (Status != STATUS_PENDING); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + + case IOCTL_IPX_LOAD_SPX: + + // + // The SPX helper dll is asking us to load SPX. + // + + Status = ZwLoadDriver (&SpxServiceName); + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + break; + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = IpxDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + return Status; + +} /* IpxDispatchDeviceControl */ + + +NTSTATUS +IpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = IpxDevice; + PREQUEST Request; + + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + + if (Device->State == DEVICE_STATE_OPEN) { + + // + // Allocate a request to track this IRP. + // + + Request = IpxAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); +#if DBG + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; +#endif + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (REQUEST_MINOR_FUNCTION(Request)) { + + case TDI_SEND_DATAGRAM: + Status = IpxTdiSendDatagram (DeviceObject, Request); + break; + + case TDI_ACTION: + Status = IpxTdiAction (Device, Request); + break; + + case TDI_QUERY_INFORMATION: + Status = IpxTdiQueryInformation (Device, Request); + break; + + case TDI_RECEIVE_DATAGRAM: + Status = IpxTdiReceiveDatagram (Request); + break; + + case TDI_SET_EVENT_HANDLER: + Status = IpxTdiSetEventHandler (Request); + break; + + case TDI_SET_INFORMATION: + Status = IpxTdiSetInformation (Device, Request); + break; + + + // + // Something we don't know about was submitted. + // + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Return the immediate status code to the caller. + // + + if (Status == STATUS_PENDING) { + + return STATUS_PENDING; + + } else { + + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + return Status; + } + + } else { + + // + // The device was not open. + // + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + +} /* IpxDispatchInternal */ + + +PVOID +IpxpAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = IpxDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + IpxPrint1 ("IPX: Could not allocate %d: limit\n", BytesNeeded); + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' XPI'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + IpxPrint1("IPX: Could not allocate %d: no pool\n", BytesNeeded); + if (ChargeDevice) { + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_TRANSPORT_RESOURCE_POOL, + BytesNeeded, + Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; +} /* IpxpAllocateMemory */ + + +VOID +IpxpFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* IpxpFreeMemory */ + +#if DBG + + +PVOID +IpxpAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = IpxpAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &IpxMemoryInterlock); + } + + return Memory; + +} /* IpxpAllocateTaggedMemory */ + + +VOID +IpxpFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with IpxpAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + (VOID)IPX_ADD_ULONG( + &IpxMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &IpxMemoryInterlock); + + IpxpFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* IpxpFreeTaggedMemory */ + +#endif + + +VOID +IpxWriteResourceErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry which has + a %3 value that needs to be converted to a string. It is currently + used for EVENT_TRANSPORT_RESOURCE_POOL and EVENT_IPX_INTERNAL_NET_ + INVALID. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - The transport event code. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated -- will be put in the dump data. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet and converted for use as the %3 string. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + PDEVICE Device = IpxDevice; + static WCHAR UniqueErrorBuffer[9] = L"00000000"; + UINT CurrentDigit; + INT i; + + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + i = 8; + do { + CurrentDigit = TempUniqueError & 0xf; + TempUniqueError >>= 4; + i--; + if (CurrentDigit >= 0xa) { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + UniqueErrorBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } while (TempUniqueError); + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR)); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer + i, sizeof(UniqueErrorBuffer) - (i * sizeof(WCHAR))); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteResourceErrorLog */ + + +VOID +IpxWriteGeneralErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + DeviceObject - Pointer to the system device object, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR DriverName[9] = L"NwlnkIpx"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceObject->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (DeviceObject->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteGeneralErrorLog */ + + +VOID +IpxWriteOidErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + DeviceObject - Pointer to the system device object. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + PDEVICE Device = IpxDevice; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* IpxWriteOidErrorLog */ + + +#ifdef _PNP_POWER +VOID +IpxPnPUpdateDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + Updates datagram sizes, lookahead sizes, etc. in the Device as a result + of a new binding coming in. + +Arguments: + + Device - The IPX device object. + +Return Value: + + None. + +--*/ +{ + ULONG AnnouncedMaxDatagram, RealMaxDatagram, MaxLookahead; + ULONG LinkSpeed, MacOptions; + ULONG i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + IPX_GET_LOCK(&Device->BindAccessLock, &LockHandle); + + // + // Calculate some values based on all the bindings. + // + + MaxLookahead = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MaxLookaheadData; // largest binding value + AnnouncedMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->AnnouncedMaxDatagramSize; // smallest binding value + RealMaxDatagram = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->RealMaxDatagramSize; // smallest binding value + + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->LineUp) { + LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; // smallest binding value + } else { + LinkSpeed = 0xffffffff; + } + MacOptions = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->Adapter->MacInfo.MacOptions; // AND of binding values + + for (i = 2; i <= Device->ValidBindings; i++) { + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, i); + + if (!Binding) { + continue; + } + + if (Binding->MaxLookaheadData > MaxLookahead) { + MaxLookahead = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < AnnouncedMaxDatagram) { + AnnouncedMaxDatagram = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < RealMaxDatagram) { + RealMaxDatagram = Binding->RealMaxDatagramSize; + } + + if (Binding->LineUp && (Binding->MediumSpeed < LinkSpeed)) { + LinkSpeed = Binding->MediumSpeed; + } + MacOptions &= Binding->Adapter->MacInfo.MacOptions; + + } + + Device->Information.MaxDatagramSize = AnnouncedMaxDatagram; + Device->RealMaxDatagramSize = RealMaxDatagram; + Device->Information.MaximumLookaheadData = MaxLookahead; + + // + // If we couldn't find anything better, use the speed from + // the first binding. + // + + if (LinkSpeed == 0xffffffff) { + Device->LinkSpeed = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->MediumSpeed; + } else { + Device->LinkSpeed = LinkSpeed; + } + Device->MacOptions = MacOptions; + + IPX_FREE_LOCK(&Device->BindAccessLock, LockHandle); +} + +VOID +IpxPnPUpdateBindingArray( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ) + +/*++ + +Routine Description: + + This routine is called to update the binding array to + add the new bindings that appeared in this PnP event. + The order of bindings in the array is as follows: + + - First comes the first binding to each LAN network + - Following that are all WAN bindings + - Following that are any duplicate bindings to LAN networks + (the others in the "binding set"). + + This routine inserts the bindings while maintaining this + order by resolving binding sets. + + The bindings are also inserted into the RIP database. + + If "global wan net" is true we will advertise up to + and including the first wan binding as the highest nic + id; otherwise we advertise up to and including the last + wan binding. In all cases the duplicate bindings are + hidden. + + Updates the SapNicCount, Device->FirstLanNicId and Device->FirstWanNicId + +Arguments: + + Device - The IPX device object. + + Adapter - The adapter added in this PnP event + + ValidBindings - the number of bindings valid for this adapter (if LAN) + +Return Value: + + None. + +--*/ +{ + ULONG i, j; + PBINDING Binding, MasterBinding; + NTSTATUS status; + + // + // Insert in proper place; if WAN, after all the WAN bindings + // If LAN, check for binding sets and insert in proper place + // Also, insert into the Rip Tables. + // + + // + // Go thru' the bindings for this adapter, inserting into the + // binding array in place + // + for (i = 0; i < ConfigBinding->FrameTypeCount; i++) { + ULONG MappedFrameType; + + // + // Store in the preference order. + // Map the frame types since we could have a case where the user selects a FrameType (say, EthernetII on FDDI) + // which maps to a different FrameType (802.2). Then we would fail to find the binding in the adapter array; + // we could potentialy add a binding twice (if two frame types map to the same Frame, then we would go to the + // mapped one twice). This is taken care of by purging dups from the ConfigBinding->FrameType array when we + // create the bindings off of the Adapter (see call to IpxBindToAdapter). + // + + MacMapFrameType( + Adapter->MacInfo.RealMediumType, + ConfigBinding->FrameType[i], + &MappedFrameType); + + Binding = Adapter->Bindings[MappedFrameType]; + + if (!Binding){ + continue; + } + + CTEAssert(Binding->FrameType == MappedFrameType); + + if (Adapter->MacInfo.MediumAsync) { + // + // WAN: Place after the HighestExternalNicId, with space for WanLine # of bindings. + // Update the First/LastWanNicId. + // + Adapter->FirstWanNicId = (USHORT)Device->HighestExternalNicId+1; + Adapter->LastWanNicId = (USHORT)(Device->HighestExternalNicId + Adapter->WanNicIdCount); + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+Adapter->WanNicIdCount >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, Adapter->WanNicIdCount); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // Move Slaves down by WanNicIdCount# of entries + // + for (j = Device->ValidBindings; j > Device->HighestExternalNicId; j--) { + INSERT_BINDING(Device, j+Adapter->WanNicIdCount, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+Adapter->WanNicIdCount)->NicId += (USHORT)Adapter->WanNicIdCount; + } + } + + // + // Insert the WAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestExternalNicId+1, Binding); + SET_VERSION(Device, Device->HighestExternalNicId+1); + + Binding->NicId = (USHORT)Device->HighestExternalNicId+1; + + // + // Update the indices + // + Device->HighestExternalNicId += (USHORT)Adapter->WanNicIdCount; + Device->ValidBindings += (USHORT)Adapter->WanNicIdCount; + Device->BindingCount += (USHORT)Adapter->WanNicIdCount; + Device->SapNicCount++; + + // + // Since we initialize FirstWanNicId to 1, we need to compare against that. + // In case of no LAN bindings, we are fine since we have only one WAN binding initally + // (all the other WAN lines have place holders). + // + if (Device->FirstWanNicId == (USHORT)1) { + Device->FirstWanNicId = Binding->NicId; + } + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + + // + // Since WAN can have only one frame type, break + // + break; + + } else { + + Device->BindingCount++; + + // + // Make sure we dont overflow the array + // Re-alloc the array to fit the new bindings + // + if (Device->ValidBindings+1 >= Device->MaxBindings) { + status = IpxPnPReallocateBindingArray(Device, 1); + CTEAssert(status == STATUS_SUCCESS); + } + + // + // LAN: Figure out if it is a slave binding only for non-auto-detect bindings. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (j = 1; j < Index; j++) { + MasterBinding = NIC_ID_TO_BINDING_NO_ILOCK(Device, j); + if ((MasterBinding->ConfiguredNetworkNumber) && + (MasterBinding->ConfiguredNetworkNumber == Binding->ConfiguredNetworkNumber) && + (MasterBinding->FrameType == Binding->FrameType) && + (MasterBinding->Adapter->MacInfo.MediumType == Binding->Adapter->MacInfo.MediumType)) { + + CTEAssert(Binding->ConfiguredNetworkNumber); + break; + } + } + } + + if (j < Device->HighestExternalNicId) { + // + // Slave binding + // + + // + // Now make MasterBinding the head of a binding set. + // + + if (MasterBinding->BindingSetMember) { + + // + // Just insert ourselves in the chain. + // + +#if DBG + DbgPrint ("IPX: %ws is also on network %lx\n", + Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Add %lx to binding set of %lx\n", Binding, MasterBinding)); + + CTEAssert (MasterBinding->CurrentSendBinding); + Binding->NextBinding = MasterBinding->NextBinding; + + } else { + + // + // Start the chain with the two bindings in it. + // + +#if DBG + DbgPrint ("IPX: %lx and %lx are on the same network %lx, will load balance\n", + MasterBinding->Adapter->AdapterName, Binding->Adapter->AdapterName, + REORDER_ULONG (Binding->LocalAddress.NetworkAddress)); +#endif + IPX_DEBUG (AUTO_DETECT, ("Create new %lx in binding set of %lx\n", Binding, MasterBinding)); + + MasterBinding->BindingSetMember = TRUE; + MasterBinding->CurrentSendBinding = MasterBinding; + MasterBinding->MasterBinding = MasterBinding; + Binding->NextBinding = MasterBinding; + + } + + MasterBinding->NextBinding = Binding; + Binding->BindingSetMember = TRUE; + Binding->ReceiveBroadcast = FALSE; + Binding->CurrentSendBinding = NULL; + Binding->MasterBinding = MasterBinding; + + // + // Since the master binding looks like all members of + // the binding set to people querying from above, we have + // to make it the worst-case of all the elements. Generally + // these will be equal since the frame type and media is + // the same. + // + + if (Binding->MaxLookaheadData > MasterBinding->MaxLookaheadData) { + MasterBinding->MaxLookaheadData = Binding->MaxLookaheadData; + } + if (Binding->AnnouncedMaxDatagramSize < MasterBinding->AnnouncedMaxDatagramSize) { + MasterBinding->AnnouncedMaxDatagramSize = Binding->AnnouncedMaxDatagramSize; + } + if (Binding->RealMaxDatagramSize < MasterBinding->RealMaxDatagramSize) { + MasterBinding->RealMaxDatagramSize = Binding->RealMaxDatagramSize; + } + if (Binding->MediumSpeed < MasterBinding->MediumSpeed) { + MasterBinding->MediumSpeed = Binding->MediumSpeed; + } + + // + // Place the binding after the last slave binding + // + INSERT_BINDING(Device, Device->ValidBindings+1, Binding); + SET_VERSION(Device, Device->ValidBindings+1); + + Binding->NicId = (USHORT)Device->ValidBindings+1; + + // + // Update the indices + // + Device->ValidBindings++; + + } else { + + // + // Not a binding set slave binding - just add it after the last LAN binding + // + + // + // Move WAN and Slaves down by 1 entry + // + for (j = Device->ValidBindings; j > Device->HighestLanNicId; j--) { + INSERT_BINDING(Device, j+1, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + if (NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, j+1)->NicId++; + } + } + + // + // Insert the LAN binding in the place just allocated + // + INSERT_BINDING(Device, Device->HighestLanNicId+1, Binding); + SET_VERSION(Device, Device->HighestLanNicId+1); + Binding->NicId = (USHORT)Device->HighestLanNicId+1; + + // + // Update the indices + // + Device->HighestLanNicId++; + Device->HighestExternalNicId++; + Device->ValidBindings++; + Device->HighestType20NicId++; + Device->SapNicCount++; + + if (Device->FirstLanNicId == (USHORT)-1) { + Device->FirstLanNicId = Binding->NicId; + } + + } + + } + + // + // Insert this binding in the RIP Tables + // + if (Binding->ConfiguredNetworkNumber != 0) { + status = RipInsertLocalNetwork( + Binding->ConfiguredNetworkNumber, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->Adapter->MediumSpeed) / Binding->Adapter->MediumSpeed)); + + if ((status == STATUS_SUCCESS) || + (status == STATUS_DUPLICATE_NAME)) { + + Binding->LocalAddress.NetworkAddress = Binding->ConfiguredNetworkNumber; + } + } + + // + // BUGBUGZZ Make this inline later + // + // This should be done after all the auto-detect bindings have been thrown away. + // + // IpxPnPUpdateDevice(Device, Binding); + } +} /* IpxPnPUpdateBindingArray */ + + +VOID +IpxPnPToLoad() +/*++ + +Routine Description: + + This routine takes the driver to LOADED state (from OPEN) when all + PnP adapters have been removed from the machine. + +Arguments: + + None. + +Return Value: + + None. When the function returns, the driver is in LOADED state. + +--*/ + +{ + PBINDING Binding; + PREQUEST Request; + PLIST_ENTRY p; + UINT i; + NTSTATUS ntStatus; + + IPX_DEBUG(PNP, ("Going back to loaded state\n")); + + // + // Inform TDI clients about the open of our device object. + // + if ((ntStatus = TdiDeregisterDeviceObject(IpxDevice->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterDeviceObject failed: %lx", ntStatus)); + } + + // + // Complete any pending address notify requests. + // + + while ((p = ExInterlockedRemoveHeadList( + &IpxDevice->AddressNotifyQueue, + &IpxDevice->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + REQUEST_STATUS(Request) = STATUS_DEVICE_NOT_READY; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IpxCompleteRequest (Request); + IpxFreeRequest (IpxDevice, Request); + + IpxDereferenceDevice (IpxDevice, DREF_ADDRESS_NOTIFY); + } + + // + // Cancel the source routing timer if used. + // + + if (IpxDevice->SourceRoutingUsed) { + + IpxDevice->SourceRoutingUsed = FALSE; + if (CTEStopTimer (&IpxDevice->SourceRoutingTimer)) { + IpxDereferenceDevice (IpxDevice, DREF_SR_TIMER); + } + } + + + // + // Cancel the RIP long timer, and if we do that then + // send a RIP DOWN message if needed. + // + + if (CTEStopTimer (&IpxDevice->RipLongTimer)) { + + if (IpxDevice->RipResponder) { + + if (RipQueueRequest (IpxDevice->VirtualNetworkNumber, RIP_DOWN) == STATUS_PENDING) { + + // + // If we queue a request, it will stop the timer. + // + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } + + IpxDereferenceDevice (IpxDevice, DREF_LONG_TIMER); + + } else { + + // + // We couldn't stop the timer, which means it is running, + // so we need to wait for the event that is kicked when + // the RIP DOWN messages are done. + // + + if (IpxDevice->RipResponder) { + + KeWaitForSingleObject( + &IpxDevice->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + } + } +} /* IpxPnPToLoad */ + + +NTSTATUS +IpxPnPReallocateBindingArray( + IN PDEVICE Device, + IN ULONG Size + ) +/*++ + +Routine Description: + + This routine reallocates the binding array when the number of bindings go above + Device->MaxBindings. + +Arguments: + + Device - pointer to the device. + Size - the number of new entries required. + +Return Value: + + None. + +--*/ +{ + PBIND_ARRAY_ELEM BindingArray; + ULONG Pad=2; // extra bindings we keep around + ULONG NewSize = Size + Pad + Device->MaxBindings; + + // + // The absolute max WAN bindings. + // + CTEAssert(Size < 2048); + + // + // Re-allocate the new array + // + BindingArray = (PBIND_ARRAY_ELEM)IpxAllocateMemory ( + NewSize * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + if (BindingArray == NULL) { + IpxWriteGeneralErrorLog( + (PVOID)Device->DeviceObject, + EVENT_IPX_NO_ADAPTERS, + 802, + STATUS_DEVICE_DOES_NOT_EXIST, + NULL, + 0, + NULL); + IpxDereferenceDevice (Device, DREF_CREATE); + + DbgPrint ("Failed to allocate memory in binding array expansion\n"); + + // + // Unload the driver here? In case of WAN, we can tolerate this failure. What about LAN? [BUGBUGZZ] + // + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory (BindingArray, NewSize * sizeof(BIND_ARRAY_ELEM)); + + // + // Copy the old array into the new one. + // + RtlCopyMemory (BindingArray, Device->Bindings, (Device->ValidBindings+1) * sizeof(BIND_ARRAY_ELEM)); + + // + // Free the old one. + // + IpxFreeMemory ( Device->Bindings, + Device->MaxBindings * sizeof(BIND_ARRAY_ELEM), + MEMORY_BINDING, + "Binding array"); + + IPX_DEBUG(PNP, ("Expand bindarr old: %lx, new: %lx, oldsize: %lx\n", + Device->Bindings, BindingArray, Device->MaxBindings)); + + // + // Use interlocked exchange to assign this since we dont take the BindAccessLock anymore. + // + // Device->Bindings = BindingArray; + SET_VALUE(Device->Bindings, BindingArray); + + Device->MaxBindings = (USHORT)NewSize; + + return STATUS_SUCCESS; +} +#endif _PNP_POWER + diff --git a/private/ntos/tdi/isnp/ipx/event.c b/private/ntos/tdi/isnp/ipx/event.c new file mode 100644 index 000000000..a64f85d34 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/event.c @@ -0,0 +1,143 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + + 1. Added a new event type - TDI_EVENT_CHAINED_RECEIVE_DATAGRAM +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +IpxTdiSetEventHandler( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Request - Pointer to the request + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + CTELockHandle LockHandle; + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PADDRESS_FILE AddressFile; + + // + // Get the Address this is associated with; if there is none, get out. + // + + AddressFile = REQUEST_OPEN_CONTEXT(Request); + Status = IpxVerifyAddressFile (AddressFile); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + CTEGetLock (&AddressFile->Address->Lock, &LockHandle); + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(Request); + + switch (Parameters->EventType) { + + case TDI_EVENT_RECEIVE_DATAGRAM: + + if (Parameters->EventHandler == NULL) { + AddressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)TdiDefaultRcvDatagramHandler; + AddressFile->ReceiveDatagramHandlerContext = NULL; + AddressFile->RegisteredReceiveDatagramHandler = FALSE; + } else { + AddressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)Parameters->EventHandler; + AddressFile->ReceiveDatagramHandlerContext = Parameters->EventContext; + AddressFile->RegisteredReceiveDatagramHandler = TRUE; + } + + break; + // + // [SA] New event handler to receive chained buffers + // + case TDI_EVENT_CHAINED_RECEIVE_DATAGRAM: + + if (Parameters->EventHandler == NULL) { + AddressFile->ChainedReceiveDatagramHandler = + (PTDI_IND_CHAINED_RECEIVE_DATAGRAM)TdiDefaultChainedRcvDatagramHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = NULL; + AddressFile->RegisteredChainedReceiveDatagramHandler = FALSE; + } else { + AddressFile->ChainedReceiveDatagramHandler = + (PTDI_IND_CHAINED_RECEIVE_DATAGRAM)Parameters->EventHandler; + AddressFile->ChainedReceiveDatagramHandlerContext = Parameters->EventContext; + AddressFile->RegisteredChainedReceiveDatagramHandler = TRUE; + } + + break; + + case TDI_EVENT_ERROR: + + if (Parameters->EventHandler == NULL) { + AddressFile->ErrorHandler = + (PTDI_IND_ERROR)TdiDefaultErrorHandler; + AddressFile->ErrorHandlerContext = NULL; + AddressFile->RegisteredErrorHandler = FALSE; + } else { + AddressFile->ErrorHandler = + (PTDI_IND_ERROR)Parameters->EventHandler; + AddressFile->ErrorHandlerContext = Parameters->EventContext; + AddressFile->RegisteredErrorHandler = TRUE; + } + + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + + } /* switch */ + + CTEFreeLock (&AddressFile->Address->Lock, LockHandle); + + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + REQUEST_INFORMATION(Request) = 0; + + return Status; + +} /* IpxTdiSetEventHandler */ + diff --git a/private/ntos/tdi/isnp/ipx/ind.c b/private/ntos/tdi/isnp/ipx/ind.c new file mode 100644 index 000000000..f43a524bc --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ind.c @@ -0,0 +1,4047 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ind.c + +Abstract: + + This module contains code which implements the indication handler + for the IPX transport provider. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + + 1. Added IpxReceivePacket which receives buffers that can be owned + 2. Changed IpxReceiveIndication to call a new function IpxReceiveIndicationNew + which takes an extra parameter to indicate whether this is a chained receive or + not. + 3. Changed IpxProcessDatagram to take the MDL ptr to indicate chained receive, + a client count and the headerbuffersize as params. + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This is declared here so it will be in the same function +// as IpxReceiveIndication and we can inline it. +// + + +#if defined(_M_IX86) +_inline +#endif +VOID +IpxProcessDatagram( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING Binding, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_DATAGRAM_OPTIONS DatagramOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast, + IN PINT pTdiClientCount, + IN UINT HeaderBufferSize, + IN PMDL pMdl, + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routing handles incoming IPX datagrams. + +Arguments: + + Device - The IPX device. + + Adapter - The adapter the frame was received on. + + Binding - The binding of the adapter it was received on. + + MacReceiveContext - The context to use when calling + NdisTransferData. + + DatagramOptions - Contains the datagram options, which + consists of room for the packet type, padding, and + the local target of the remote the frame was received from. + + LookaheadBuffer - The lookahead data. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The length of the packet, starting at the IPX + header. + + Broadcast - TRUE if the packet was broadcast. + + pTdiClientCount - to return count of the number of TDI clients above us + so NDIS can obtain that many ref counts on the buffer. + + HeaderBufferSize - the size of the MAC header buffer - used to determine + the offsets into the TSDU. + + pMdl - Mdl chain pointer - non-NULL if chained receive + + BindingContext - In case of loopback, this contains IPX_LOOPBACK_COOKIE + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PIPX_HEADER IpxHeader = (PIPX_HEADER)LookaheadBuffer; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PADDRESS_FILE ReferencedAddressFile; + PREQUEST Request; + PIPX_RECEIVE_BUFFER ReceiveBuffer; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_IPX UNALIGNED * DatagramAddress; + ULONG IndicateBytesCopied; + IPX_ADDRESS_EXTENDED_FLAGS SourceAddress; + ULONG SourceAddressLength; + ULONG RequestCount; + PNDIS_BUFFER NdisBuffer; + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PIRP Irp; + UINT ByteOffset, BytesToTransfer; + ULONG BytesTransferred; + BOOLEAN LastAddressFile; + ULONG IndicateOffset; + PNDIS_PACKET ReceivePacket; + PIPX_RECEIVE_RESERVED Reserved; + PLIST_ENTRY p, q; + PSINGLE_LIST_ENTRY s; + USHORT DestinationSocket; + USHORT SourceSocket; + ULONG Hash; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // First scan the device's address database, looking for + // the destination socket of this frame. + // + + DestinationSocket = *(USHORT UNALIGNED *)&IpxHeader->DestinationSocket; + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if ((Address = Device->LastAddress) && + (Address->Socket == DestinationSocket)) { + + // + // Device->LastAddress cannot be stopping, so + // we use it. + // + + IpxReferenceAddressLock (Address, AREF_RECEIVE); + IPX_FREE_LOCK (&Device->Lock, LockHandle); + goto FoundAddress; + } + + Hash = IPX_DEST_SOCKET_HASH (IpxHeader); + + for (p = Device->AddressDatabases[Hash].Flink; + p != &Device->AddressDatabases[Hash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if ((Address->Socket == DestinationSocket) && + (!Address->Stopping)) { + IpxReferenceAddressLock (Address, AREF_RECEIVE); + Device->LastAddress = Address; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + goto FoundAddress; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // If we had found an address we would have jumped + // past here. + // + + return; + +FoundAddress: + + SourceSocket = *(USHORT UNALIGNED *)&IpxHeader->SourceSocket; + IpxBuildTdiAddress( + &SourceAddress.IpxAddress, + (*(ULONG UNALIGNED *)(IpxHeader->SourceNetwork) == 0) ? + Binding->LocalAddress.NetworkAddress : + *(UNALIGNED ULONG *)(IpxHeader->SourceNetwork), + IpxHeader->SourceNode, + SourceSocket); + + DatagramOptions->PacketType = IpxHeader->PacketType; + + + // + // Now that we have found the address, scan its list of + // address files for clients that want this datagram. + // + // If we have to release the address lock to indicate to + // a client, we reference the current address file. If + // we get an IRP we transfer the reference to that; + // otherwise we store the address file in ReferencedAddressFile + // and deref it the next time we release the lock. + // + + ReferencedAddressFile = NULL; + RequestCount = 0; + + ++Device->TempDatagramsReceived; + Device->TempDatagramBytesReceived += (PacketSize - sizeof(IPX_HEADER)); + + // + // If LastAddressFile is TRUE, it means we did an indication + // to the client on the last address file in the address' + // list, and we did not reacquire the lock when we were + // done. + // + + LastAddressFile = FALSE; + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; // next address file + } + + // + // Set these to the common values, then change them. + // + + SourceAddressLength = sizeof(TA_IPX_ADDRESS); + IndicateOffset = sizeof(IPX_HEADER); + + if (AddressFile->SpecialReceiveProcessing) { + + // + // On dial out lines, we don't indicate packets to + // the SAP socket if DisableDialoutSap is set. + // + + if ((AddressFile->IsSapSocket) && + (Binding->DialOutAsync) && + (Device->DisableDialoutSap || Device->SingleNetworkActive)) { + + // + // Go to the next address file (although it will + // likely fail this test too). + // + + continue; + + } + + // + // Set this, since generally we want it. + // + + SourceAddress.PacketType = IpxHeader->PacketType; + + // + // See if we fail a packet type filter. + // + + if (AddressFile->FilterOnPacketType) { + if (AddressFile->FilteredType != IpxHeader->PacketType) { + continue; + } + } + + // + // Calculate how long the addresses expected are. + // + + if (AddressFile->ReceiveFlagsAddressing || + AddressFile->ExtendedAddressing) { + + SourceAddress.Flags = 0; + if (Broadcast) { + SourceAddress.Flags = IPX_EXTENDED_FLAG_BROADCAST; + } + if (IpxIsAddressLocal((TDI_ADDRESS_IPX UNALIGNED *) + &SourceAddress.IpxAddress.Address[0].Address[0])) { + SourceAddress.Flags |= IPX_EXTENDED_FLAG_LOCAL; + } + SourceAddressLength = sizeof(IPX_ADDRESS_EXTENDED_FLAGS); + SourceAddress.IpxAddress.Address[0].AddressLength += + (sizeof(IPX_ADDRESS_EXTENDED_FLAGS) - sizeof(TA_IPX_ADDRESS)); + + } + + // + // Determine how much of the packet the client wants. + // + + if (AddressFile->ReceiveIpxHeader) { + IndicateOffset = 0; + } + } + + // + // First scan the address' receive datagram queue + // for datagrams that match. We do a quick check + // to see if the list is empty. + // + + q = AddressFile->ReceiveDatagramQueue.Flink; + if (q != &AddressFile->ReceiveDatagramQueue) { + + do { + + Request = LIST_ENTRY_TO_REQUEST(q); + + DatagramInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))-> + ReceiveDatagramInformation; + + if ((DatagramInformation != NULL) && + (DatagramInformation->RemoteAddress != NULL) && + (DatagramAddress = IpxParseTdiAddress(DatagramInformation->RemoteAddress)) && + (DatagramAddress->Socket != SourceSocket)) { + + // + // The address that this datagram is looking for is + // not satisfied by this frame. + // + // BUGBUG: Speed this up; worry about node and network? + // + + q = q->Flink; + continue; // next receive datagram on this address file + + } else { + + // + // We found a datagram on the queue. + // + + IPX_DEBUG (RECEIVE, ("Found RDG on %lx\n", AddressFile)); + RemoveEntryList (q); + REQUEST_INFORMATION(Request) = 0; + + goto HandleDatagram; + + } + + } while (q != &AddressFile->ReceiveDatagramQueue); + + } + + // + // If we found a datagram we would have jumped past here, + // so looking for a datagram failed; see if the + // client has a receive datagram handler registered. + // + + // + // Look for the chained receive handler if the MDL is not NULL + // + if (pMdl && AddressFile->RegisteredChainedReceiveDatagramHandler) { + + // + // Chained receive both above and below => we indicate the entire MDL up. + // Offset the LookaheadBuffer by the size of the MAC header. + // + LookaheadBufferOffset += HeaderBufferSize; + + IpxReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // Set this so we can exit without reacquiring + // the lock. + // + + if (p == &Address->AddressFileDatabase) { + LastAddressFile = TRUE; + } + + IndicateBytesCopied = 0; + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + IPX_DEBUG(RECEIVE, ("ChainedIndicate RecvLen: %d, StartOffset: %d, Tsdu: %lx\n", + PacketSize - IndicateOffset, IndicateOffset, pMdl)); + + // + // Will return SUCCESS if the client did not take ownership of the Tsdu + // PENDING if the client took ownership and will free it later (using TdiFreeReceiveChain). + // DATA_NOT_ACCEPTED if the client did not take ownership and did not copy the data. + // + + // + // Since NDIS needs an array of PNDIS_PACKETs when the TDI client returns this packet, + // we pass the Packet as the ReceiveContext here. The TDI client will pass in the address + // of this context on a ReturnPacket. + // Also, NDIS needs the PacketArray (not to be confused with the array of packetptrs. mentioned + // above) on an NdisTransferData call. These clients dont do this, but other clients like + // NB, SPX, RIP or TDI clients that do not have this new interface, can call NdisTransferData + // so we pass in the PacketArray as a parameter to them. + // + Status = (*AddressFile->ChainedReceiveDatagramHandler)( + AddressFile->ChainedReceiveDatagramHandlerContext, + SourceAddressLength, + &SourceAddress, + sizeof(IPX_DATAGRAM_OPTIONS), + DatagramOptions, + Adapter->MacInfo.CopyLookahead, // TdiRcvFlags|Adapter->MacInfo.CopyLookahead, Receive datagram flags + PacketSize - IndicateOffset, // ReceiveLength + IndicateOffset+LookaheadBufferOffset, // StartingOffset + pMdl, // Tsdu - MDL chain + (PNDIS_PACKET)MacReceiveContext); // TransportContext - pointer to the packet + + if (Status != STATUS_DATA_NOT_ACCEPTED) { + + if (Status == STATUS_PENDING) { + // + // We assume here that the client referenced the packet which will + // be removed when the packet is freed. + // Increment the Tdi client count + // + (*pTdiClientCount)++; + } + + // + // The handler accepted the data or did not + // return an IRP; in either case there is + // nothing else to do, so go to the next + // address file. + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + // RequestCount should always be 0 here. + // + + + //if (RequestCount == 0) { + // return; + //} + goto BreakWithoutLock; + } + + } else { + // + // Since no IRP can be returned here, we continue to the next addressfile + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + + //if (RequestCount == 0) { + // return; + //} + goto BreakWithoutLock; + } + } + + } else if (AddressFile->RegisteredReceiveDatagramHandler) { + + IpxReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // Set this so we can exit without reacquiring + // the lock. + // + + if (p == &Address->AddressFileDatabase) { + LastAddressFile = TRUE; + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + IndicateBytesCopied = 0; + + if (PacketSize > LookaheadBufferSize) { + IPX_DEBUG(RECEIVE, ("Indicate %d/%d to %lx on %lx\n", + LookaheadBufferSize, PacketSize, + AddressFile->ReceiveDatagramHandler, AddressFile)); + } + + Status = (*AddressFile->ReceiveDatagramHandler)( + AddressFile->ReceiveDatagramHandlerContext, + SourceAddressLength, + &SourceAddress, + sizeof(IPX_DATAGRAM_OPTIONS), + DatagramOptions, + Adapter->MacInfo.CopyLookahead, + LookaheadBufferSize - IndicateOffset, // indicated + PacketSize - IndicateOffset, // available + &IndicateBytesCopied, // taken + LookaheadBuffer + IndicateOffset, // data + &Irp); + + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The handler accepted the data or did not + // return an IRP; in either case there is + // nothing else to do, so go to the next + // address file. + // + + ReferencedAddressFile = AddressFile; + if (!LastAddressFile) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } else { + + // + // In this case we have no cleanup, so just leave + // if there are no datagrams pending. + // + + if (RequestCount == 0) { + return; + } + goto BreakWithoutLock; + } + + } else { + + // + // The client returned an IRP. + // + + IPX_DEBUG (RECEIVE, ("Indicate IRP %lx, taken %d\n", Irp, IndicateBytesCopied)); + + Request = IpxAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ReferencedAddressFile = AddressFile; + IPX_GET_LOCK (&Address->Lock, &LockHandle); + continue; + } + + if (!LastAddressFile) { + IPX_GET_LOCK (&Address->Lock, &LockHandle); + } + +#if DBG + // + // Make sure the IRP file object is right. + // + + if (IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext != AddressFile) { + DbgPrint ("IRP %lx does not match AF %lx, H %lx C %lx\n", + Irp, AddressFile, + AddressFile->ReceiveDatagramHandler, + AddressFile->ReceiveDatagramHandlerContext); + DbgBreakPoint(); + } +#endif + // + // Set up the information field so we know + // how much to skip in it. + // + + IpxTransferReferenceAddressFile (AddressFile, AFREF_INDICATION, AFREF_RCV_DGRAM); + REQUEST_INFORMATION(Request) = IndicateBytesCopied; + + // + // Fall out of the if and continue via + // HandleDatagram... + // + + } + + } else { + + // + // No posted datagram, no handler; go to the next + // address file. + // + + continue; // next address file + + } + +HandleDatagram: + + // + // At this point, Request is set to the request + // that will hold for this address file, and + // REQUEST_INFORMATION() is the offset to start + // the transfer at. + // + + // + // First copy over the source address while it is handy. + // + + DatagramInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))-> + ReturnDatagramInformation; + + if (DatagramInformation != NULL) { + + RtlCopyMemory( + DatagramInformation->RemoteAddress, + &SourceAddress, + (ULONG)DatagramInformation->RemoteAddressLength < SourceAddressLength ? + DatagramInformation->RemoteAddressLength : SourceAddressLength); + RtlCopyMemory( + DatagramInformation->Options, + &DatagramOptions, + (ULONG)DatagramInformation->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS) ? + DatagramInformation->OptionsLength : sizeof(IPX_DATAGRAM_OPTIONS)); + + } + + // + // Now check if this is the first request that will + // take the data, otherwise queue it up. + // + + if (RequestCount == 0) { + + // + // First one; we need to allocate a packet for the transfer. + // + + //if (Address->ReceivePacketInUse) { + if (InterlockedExchangeAdd(&Address->ReceivePacketInUse, 0) != 0) { + // + // Need a packet, check the pool. + // + + s = IpxPopReceivePacket (Device); + + if (s == NULL) { + + // + // None in pool, fail the request. + // + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_INSUFFICIENT_RESOURCES; + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + if (!LastAddressFile) { + continue; + } else { + goto BreakWithoutLock; + } + + } + + Reserved = CONTAINING_RECORD (s, IPX_RECEIVE_RESERVED, PoolLinkage); + ReceivePacket = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } else { + + // Address->ReceivePacketInUse = TRUE; + InterlockedIncrement(&Address->ReceivePacketInUse); + + ReceivePacket = PACKET(&Address->ReceivePacket); + Reserved = RECEIVE_RESERVED(&Address->ReceivePacket); + + } + + CTEAssert (IsListEmpty(&Reserved->Requests)); + + Reserved->SingleRequest = Request; + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + + ByteOffset = REQUEST_INFORMATION(Request) + LookaheadBufferOffset + IndicateOffset; + BytesToTransfer = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))->ReceiveLength; + + if (BytesToTransfer > (PacketSize - IndicateOffset)) { + BytesToTransfer = PacketSize - IndicateOffset; + } + + } else { + + if (RequestCount == 1) { + + // + // There is already one request. We need to + // allocate a buffer. + // + + s = IpxPopReceiveBuffer (Adapter); + + if (s == NULL) { + + // + // No buffers, fail the request. + // + // BUGBUG: Should we fail the transfer for the + // first request too? + // + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_INSUFFICIENT_RESOURCES; + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + if (!LastAddressFile) { + continue; + } else { + goto BreakWithoutLock; + } + } + + ReceiveBuffer = CONTAINING_RECORD(s, IPX_RECEIVE_BUFFER, PoolLinkage); + NdisBuffer = ReceiveBuffer->NdisBuffer; + + // + // Convert this to a queued multiple piece request. + // + + InsertTailList(&Reserved->Requests, REQUEST_LINKAGE(Reserved->SingleRequest)); + Reserved->SingleRequest = NULL; + Reserved->ReceiveBuffer = ReceiveBuffer; + + ByteOffset = LookaheadBufferOffset; + BytesToTransfer = PacketSize; + + } + + InsertTailList(&Reserved->Requests, REQUEST_LINKAGE(Request)); + + } + + // + // We are done setting up this address file's transfer, + // proceed to the next one. + // + + ++RequestCount; + + if (LastAddressFile) { + goto BreakWithoutLock; + } + + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + +BreakWithoutLock: + + if (ReferencedAddressFile) { + IpxDereferenceAddressFileSync (ReferencedAddressFile, AFREF_INDICATION); + ReferencedAddressFile = NULL; + } + + + // + // We can be transferring directly into a request's buffer, + // transferring into an intermediate buffer, or not + // receiving the packet at all. + // + + if (RequestCount > 0) { + + // + // If this is true, then ReceivePacket, Reserved, + // and NdisBuffer are all set up correctly. + // + + CTEAssert (ReceivePacket); + CTEAssert (Reserved == (PIPX_RECEIVE_RESERVED)(ReceivePacket->ProtocolReserved)); + + + NdisChainBufferAtFront(ReceivePacket, NdisBuffer); + + IPX_DEBUG (RECEIVE, ("Transfer into %lx, offset %d bytes %d\n", + NdisBuffer, ByteOffset, BytesToTransfer)); + + if (BindingContext == (PVOID)IPX_LOOPBACK_COOKIE) { + + IPX_DEBUG (LOOPB, ("Loopback Copy from packet: %lx to packet: %lx\n", ReceivePacket, MacReceiveContext)); + + NdisCopyFromPacketToPacket( + ReceivePacket, // Destination + 0, // DestinationOffset + BytesToTransfer, // BytesToCopy + (PNDIS_PACKET)MacReceiveContext, // Source + ByteOffset, // SourceOffset - loopback packet + &BytesTransferred); // BytesCopied + + NdisStatus = NDIS_STATUS_SUCCESS; + + } else { + NdisTransferData( + &NdisStatus, + Adapter->NdisBindingHandle, + MacReceiveContext, + ByteOffset, + BytesToTransfer, + ReceivePacket, + &BytesTransferred); + } + + if (NdisStatus != NDIS_STATUS_PENDING) { + + IpxTransferDataComplete( + (NDIS_HANDLE)Adapter, + ReceivePacket, + NdisStatus, + BytesTransferred); + } + } + + + IpxDereferenceAddressSync (Address, AREF_RECEIVE); + +} /* IpxProcessDatagram */ + + + +NDIS_STATUS +IpxReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + // + // Call the actual receive indication handler and indicate that this is not a + // chained receive + // + + return IpxReceiveIndicationNew ( + BindingContext, + ReceiveContext, // ReceiveContext + HeaderBuffer, + HeaderBufferSize, + LookaheadBuffer, + LookaheadBufferSize, + PacketSize, // PacketSize + NULL, // pMdl - non-NULL => chained receive. + NULL // pTdiClientCount - used in chained recv case to keep count of TDI clients + ); + +} + + +NDIS_STATUS +IpxReceiveIndicationNew( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize, + IN PMDL pMdl, + IN PINT pTdiClientCount + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + + pMdl - pointer to MDL chain if chained, NULL if this came from indication. + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + + IPX_DATAGRAM_OPTIONS DatagramOptions; + PADAPTER Adapter = (PADAPTER)BindingContext; + PBINDING Binding; + PDEVICE Device = IpxDevice; + PUCHAR Header = (PUCHAR)HeaderBuffer; + PUCHAR Lookahead = (PUCHAR)LookaheadBuffer; + ULONG PacketLength; + UINT IpxPacketSize; + ULONG Length802_3; + USHORT Saps; + ULONG DestinationNetwork; + ULONG SourceNetwork; + PUCHAR DestinationNode; + USHORT DestinationSocket; + ULONG IpxHeaderOffset; + PIPX_HEADER IpxHeader; + UINT i; + BOOLEAN IsBroadcast; + BOOLEAN IsLoopback = FALSE; +#if DBG + PUCHAR DestMacAddress; + ULONG ReceiveFlag; +#endif + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif _PNP_POWER + + // + // Reject packets that are too short to hold even the + // basic IPX header (this ignores any extra 802.2 etc. + // headers but is good enough because a runt will fail + // the IPX header packet length check). + // + + if (PacketSize < sizeof(IPX_HEADER)) { + return STATUS_SUCCESS; + } + + // + // If this is a loopback packet, no need to do figure out the + // MAC header. + // + if (BindingContext == (PVOID)IPX_LOOPBACK_COOKIE) { + +#ifdef _PNP_POWER + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(IpxDevice, 1); + + if (!Binding) { + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto NotValidLoopback; + } + + Adapter = Binding->Adapter; + + // + // Bump up the ref count so the adapter doesn't disappear from under + // us. + // + IpxReferenceAdapter(Adapter); + + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, 0); +#else + if ((Binding = IpxDevice->Bindings[1]) == NULL) { + goto NotValidLoopback; + } + + Adapter = Binding->Adapter; + + DatagramOptions.LocalTarget.NicId = 0; +#endif + + // + // Do this copy later, from the IpxHeader. + // + // RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Binding->LocalAddress.NodeAddress, 6); + + if (Binding->Adapter->MacInfo.MediumType == NdisMedium802_5) { + DatagramOptions.LocalTarget.MacAddress[0] &= 0x7f; + } + + // + // Ipx header starts at the top of the LookAheadBuffer + // + IpxHeaderOffset = 0; + + IPX_DEBUG (LOOPB, ("Loopback packet received: %lx\n", ReceiveContext)); + +#if DBG + DestMacAddress = DatagramOptions.LocalTarget.MacAddress; +#endif + + IsLoopback = TRUE; + goto Loopback; + } + +#ifdef _PNP_POWER + // + // Bump up the ref count so the adapter doesn't disappear from under + // us. + // + IpxReferenceAdapter(Adapter); +#endif + + // + // The first step is to construct the 8-byte local + // target from the packet. We store it in the 9-byte + // datagram options, leaving one byte at the front + // for use by IpxProcessDatagram when indicating to + // its TDI clients. + // + +#if DBG + Binding = NULL; +#endif + + if (Adapter->MacInfo.MediumType == NdisMedium802_3) { + + // + // Try to figure out what the packet type is. + // +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + if (Header[12] < 0x06) { + + // + // An 802.3 header; check the next bytes. They may + // be E0/E0 (802.2), FFFF (raw 802.3) or A0/A0 (SNAP). + // + + Saps = *(UNALIGNED USHORT *)(Lookahead); + + if (Saps == 0xffff) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 0; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + + } else if (Saps == 0xe0e0) { + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 3; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + } + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValid802_3; + } + IpxHeaderOffset = 8; + Length802_3 = ((Header[12] << 8) | Header[13]); + goto Valid802_3; + } + } + + goto NotValid802_3; + + } else { + + // + // It has an ethertype, see if it is ours. + // + + if (*(UNALIGNED USHORT *)(Header+12) == Adapter->BindSapNetworkOrder) { + + if (Adapter->MacInfo.MediumAsync) { + + *((ULONG UNALIGNED *)(&Binding)) = *((ULONG UNALIGNED *)(&Header[2])); + + CTEAssert(Binding != NULL); + + if ((Binding != NULL) && + (Binding->LineUp)) { + + IpxHeaderOffset = 0; + Length802_3 = PacketSize; // set this so the check succeeds + + // + // Check if this is a type 20 packet and + // we are disabling them on dialin lines -- we do + // this check here to avoid impacting the main + // indication path for LANs. + // + // The 0x02 bit of DisableDialinNetbios controls + // WAN->LAN packets, which we handle here. + // + + if ((!Binding->DialOutAsync) && + ((Device->DisableDialinNetbios & 0x02) != 0)) { + + IpxHeader = (PIPX_HEADER)Lookahead; // IpxHeaderOffset is 0 + if (IpxHeader->PacketType == 0x14) { +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + return STATUS_SUCCESS; + } + } + + goto Valid802_3; + } + goto NotValid802_3; + + } else if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_ETHERNET_II]) == NULL) { + goto NotValid802_3; + } + + IpxHeaderOffset = 0; + Length802_3 = PacketSize; // set this so the check succeeds + goto Valid802_3; + + } + } + + goto NotValid802_3; + +Valid802_3: + + if (Length802_3 > PacketSize) { + goto NotValid802_3; + } else if (Length802_3 < PacketSize) { + PacketSize = Length802_3; + if (LookaheadBufferSize > Length802_3) { + LookaheadBufferSize = Length802_3; + } + } + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+6, 6); +#if DBG + DestMacAddress = Header; +#endif + + } else if (Adapter->MacInfo.MediumType == NdisMedium802_5) { + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + Saps = *(USHORT UNALIGNED *)(Lookahead); + + if (Saps == 0xe0e0) { + + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValid802_5; + } + + IpxHeaderOffset = 3; + goto Valid802_5; + } + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValid802_5; + } + IpxHeaderOffset = 8; + goto Valid802_5; + } + } + + goto NotValid802_5; + +Valid802_5: +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+8, 6); + DatagramOptions.LocalTarget.MacAddress[0] &= 0x7f; + +#if DBG + DestMacAddress = Header+2; +#endif + + } else if (Adapter->MacInfo.MediumType == NdisMediumFddi) { + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + Saps = *(USHORT UNALIGNED *)(Lookahead); + + if (Saps == 0xe0e0) { + + if (Lookahead[2] == 0x03) { + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 3; + goto ValidFddi; + } + + } else if (Saps == 0xffff) { + + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 0; + goto ValidFddi; + + } else if (Saps == 0xaaaa) { + + if ((Lookahead[2] == 0x03) && + (*(UNALIGNED USHORT *)(Lookahead+6) == Adapter->BindSapNetworkOrder)) { + + if ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]) == NULL) { + goto NotValidFddi; + } + IpxHeaderOffset = 8; + goto ValidFddi; + } + } + + goto NotValidFddi; + +ValidFddi: + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, Header+7, 6); + +#if DBG + DestMacAddress = Header+1; +#endif + + + } else { + + // + // NdisMediumArcnet878_2 + // + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + if ((Header[2] == ARCNET_PROTOCOL_ID) && + ((Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_3]) != NULL)) { + + IpxHeaderOffset = 0; + RtlZeroMemory (DatagramOptions.LocalTarget.MacAddress, 5); + DatagramOptions.LocalTarget.MacAddress[5] = Header[0]; + + } else { + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+2, Header+1, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + } + +#if DBG + DestMacAddress = Header+2; // BUGBUG Need to log less than six bytes +#endif + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_ADAPTER_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + + // + // Make sure this didn't slip through. + // + + CTEAssert (Binding != NULL); +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + DatagramOptions.LocalTarget.NicId = Binding->NicId; +#endif + +Loopback: + + // + // Now that we have validated the header and constructed + // the local target, indicate the packet to the correct + // client. + // + + IpxHeader = (PIPX_HEADER)(Lookahead + IpxHeaderOffset); + + PacketLength = (IpxHeader->PacketLength[0] << 8) | IpxHeader->PacketLength[1]; + + IpxPacketSize = PacketSize - IpxHeaderOffset; + + if (PacketLength > IpxPacketSize) { + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, DestMacAddress, DatagramOptions.LocalTarget.MacAddress, (USHORT)PacketSize, IpxHeader, IpxHeader+1); + } +#endif + IPX_DEBUG (BAD_PACKET, ("Packet len %d, IPX len %d\n", + PacketLength, IpxPacketSize)); + + return NDIS_STATUS_SUCCESS; + + } else if (PacketLength < IpxPacketSize) { + + IpxPacketSize = PacketLength; + if (LookaheadBufferSize > (PacketLength + IpxHeaderOffset)) { + LookaheadBufferSize = PacketLength + IpxHeaderOffset; + } + + } + + // + // Bug #33595 - (hotfixed in 3.51, checked into 4.0 beta2) + // Customer problem where NT allowed RIP/SAP to reply to an 802.5 functional address in the IPX source node. The source + // MAC address was proper in this case. We need to check for the case where if the packet's source network is the same + // as that of the binding it came on (=> did not come thru a router), then the SourceNodeAddress in the IPX header + // should be equal to the SourceAddress in the MAC header. + // + // This check is controlled through a registry value - VerifySourceAddress. + // In case of Arcnet, this check will not succeed. + // Also, for WAN, the node addresses will not match, so avoid check for those. + + // + // If the source network is 0, we drop it. Auto-detect frames should have matching node (MAC) addresses. + // Loopback packets dont have a valid header, so skip this test for them. + // + // BUGBUG: For loopback pkts, do all the processing above, so we can avoid all these checks for IsLoopback here. + // Also, to prevent the RtlCopyMemory into the localtarget above, try to use the MAC header to indicate the + // correct binding to us so we dont use the first one always. + // + // CAVEAT:: when using the MAC header as a binding pointer, ensure that we use the adapter corresp, to that binding + // to enque all the receive requests. currently we enqueue them onto the first bindings adapter. + // + if (((*(UNALIGNED ULONG *)IpxHeader->SourceNetwork == Binding->LocalAddress.NetworkAddress) || + (*(UNALIGNED ULONG *)IpxHeader->SourceNetwork == 0)) && + (!IPX_NODE_EQUAL (IpxHeader->SourceNode, DatagramOptions.LocalTarget.MacAddress)) && + Device->VerifySourceAddress && + !IsLoopback && + !Adapter->MacInfo.MediumAsync && + (Adapter->MacInfo.MediumType != NdisMediumArcnet878_2)) { + + IPX_DEBUG(BAD_PACKET, ("Local packet: Src MAC %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x ", + DatagramOptions.LocalTarget.MacAddress[0], + DatagramOptions.LocalTarget.MacAddress[1], + DatagramOptions.LocalTarget.MacAddress[2], + DatagramOptions.LocalTarget.MacAddress[3], + DatagramOptions.LocalTarget.MacAddress[4], + DatagramOptions.LocalTarget.MacAddress[5])); + + IPX_DEBUG(BAD_PACKET, ("IPX Src Node %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + IpxHeader->SourceNode[0], + IpxHeader->SourceNode[1], + IpxHeader->SourceNode[2], + IpxHeader->SourceNode[3], + IpxHeader->SourceNode[4], + IpxHeader->SourceNode[5])); + +#ifdef IPX_PACKET_LOG + ReceiveFlag = IPX_PACKET_LOG_RCV_ALL; + if (PACKET_LOG(ReceiveFlag)) { + IpxLogPacket( + FALSE, + DestMacAddress, + DatagramOptions.LocalTarget.MacAddress, + (USHORT)IpxPacketSize, + IpxHeader, + IpxHeader+1); + } +#endif + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + + return NDIS_STATUS_SUCCESS; + } + + DestinationSocket = *(USHORT UNALIGNED *)&IpxHeader->DestinationSocket; + + // + // In order to have consistent local targets, copy over the target from the IpxHeader. + // + if (IsLoopback) { + IPX_DEBUG (LOOPB, ("Loopback packet copied the localtarget: %lx\n", IpxHeader->DestinationNode)); + // RtlCopyMemory (DatagramOptions.LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + *((UNALIGNED ULONG *)DatagramOptions.LocalTarget.MacAddress) = + *((UNALIGNED ULONG *)IpxHeader->DestinationNode); + + *((UNALIGNED USHORT *)(DatagramOptions.LocalTarget.MacAddress+4)) = + *((UNALIGNED USHORT *)(IpxHeader->DestinationNode+4)); + } + + ++Device->Statistics.PacketsReceived; + + if (DestinationSocket != RIP_SOCKET) { + + DestinationNetwork = *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork; + DestinationNode = IpxHeader->DestinationNode; + +RecheckPacket: + + if (Device->MultiCardZeroVirtual) { + + if ((DestinationNetwork == Binding->LocalAddress.NetworkAddress) || + (DestinationNetwork == 0)) { + + if (IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress)) { + IsBroadcast = FALSE; + goto DestinationOk; + } else { + if ((IsBroadcast = IPX_NODE_BROADCAST(DestinationNode)) && + (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + } + + // + // If this is a binding set slave, check for the master's + // address. + // + + if ((Binding->BindingSetMember) && + (IPX_NODE_EQUAL (DestinationNode, Binding->MasterBinding->LocalAddress.NodeAddress))) { + goto DestinationOk; + } + + } else { + IsBroadcast = IPX_NODE_BROADCAST(DestinationNode); + } + + } else { + + if ((DestinationNetwork == Device->SourceAddress.NetworkAddress) || + (DestinationNetwork == 0)) { + + if (IPX_NODE_EQUAL (DestinationNode, Device->SourceAddress.NodeAddress)) { + IsBroadcast = FALSE; + goto DestinationOk; + } else { + if ((IsBroadcast = IPX_NODE_BROADCAST(DestinationNode)) && + (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + } + } else { + IsBroadcast = IPX_NODE_BROADCAST(DestinationNode); + } + + // + // We need to check for frames that are sent to the + // binding node and net, because if we have a virtual + // net we won't catch them in the check above. This + // will include any Netbios frames, since they don't + // use the virtual net. Doing the check like this will slow + // down netbios indications just a bit on a machine with + // a virtual network, but it saves a jump for other traffic + // vs. adding the check up there (the assumption is if we + // have a virtual net most traffic is NCP). + // + // Note that IsBroadcast is already set, so we don't have + // to do that. + // + + if ((Device->VirtualNetwork) && + ((DestinationNetwork == Binding->LocalAddress.NetworkAddress) || + (DestinationNetwork == 0))) { + + if (IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress)) { + goto DestinationOk; + } else { + if (IsBroadcast && (Binding->ReceiveBroadcast)) { + goto DestinationOk; + } + + } + + // + // If this is a binding set slave, check for the master's + // address. + // + + if ((Binding->BindingSetMember) && + (IPX_NODE_EQUAL (DestinationNode, Binding->MasterBinding->LocalAddress.NodeAddress))) { + goto DestinationOk; + } + } + } + + // + // If this was a loopback packet that was sent on the second binding (but showed back up on the first one), + // then the networknumbers will not match. Allow the receive on the first binding itself. + // + if (IsLoopback) { + IPX_DEBUG (LOOPB, ("Loopback packet forced on first binding: %lx\n", ReceiveContext)); + goto DestinationOk; + } + + // + // If we did not receive this packet, it might be because + // our network is still 0 and this packet was actually + // sent to the real network number. If so we try to + // update our local address, and if successful we + // re-check the packet. We don't insert if we are + // not done with auto detection, to avoid colliding + // with that. + // + // To avoid problems if we are a router, we only update + // on packets that are broadcast or sent to us. + // + + if ((Binding->LocalAddress.NetworkAddress == 0) && + (Device->AutoDetectState == AUTO_DETECT_STATE_DONE) && + (DestinationNetwork != 0) && + (IsBroadcast || + IPX_NODE_EQUAL (DestinationNode, Binding->LocalAddress.NodeAddress))) { + + CTEAssert (Binding->NicId != 0); + + if (IpxUpdateBindingNetwork( + Device, + Binding, + DestinationNetwork) == STATUS_SUCCESS) { + + IPX_DEBUG (RIP, ("Binding %d reconfigured to network %lx\n", + Binding->NicId, + REORDER_ULONG(Binding->LocalAddress.NetworkAddress))); + + // + // Jump back and re-process the packet; we know + // we won't loop through here again because the + // binding's network is now non-zero. + // + + goto RecheckPacket; + + } + } + + + // + // The only frames that will not already have jumped to + // DestinationOk are those to or from the SAP socket, + // so we check for those. + // + + if ((*(USHORT UNALIGNED *)&IpxHeader->SourceSocket == SAP_SOCKET) || + (DestinationSocket == SAP_SOCKET)) { + +DestinationOk: + + // + // An IPX packet sent to us, or a SAP packet (which + // are not sent to the virtual address but still need + // to be indicated and not forwarded to RIP). + // + + if (DestinationSocket == NB_SOCKET) { +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_NB | IPX_PACKET_LOG_RCV_ALL; +#endif + if (((!IsBroadcast) || (Device->UpperDrivers[IDENTIFIER_NB].BroadcastEnable)) && + (Device->UpperDriverBound[IDENTIFIER_NB])) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_NB, Adapter, Header, HeaderBufferSize); + } + + // + // We add HeaderBufferSize to the IpxHeaderOffset field since we do an NdisCopyFromPacketToPacket + // in IpxTransferData, which needs offset from the beginning of the packet. + // NdisTransferData adds the offset passed in to the beginning of the IPX packet. + // + (*Device->UpperDrivers[IDENTIFIER_NB].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize); + + Device->ReceiveCompletePending[IDENTIFIER_NB] = TRUE; + } + + // + // The router needs to see Netbios type 20 broadcasts. + // + + if (IsBroadcast && + (IpxHeader->PacketType == 0x14) && + (Binding->ReceiveBroadcast)) { + goto RipIndication; + } + + } else if (IpxHeader->PacketType == SPX_PACKET_TYPE) { + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_SPX | IPX_PACKET_LOG_RCV_ALL; +#endif + + if (((!IsBroadcast) || (Device->UpperDrivers[IDENTIFIER_SPX].BroadcastEnable)) && + (Device->UpperDriverBound[IDENTIFIER_SPX])) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_SPX, Adapter, Header, HeaderBufferSize); + } + + (*Device->UpperDrivers[IDENTIFIER_SPX].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize); + + Device->ReceiveCompletePending[IDENTIFIER_SPX] = TRUE; + } + + } else { + + IPX_DEBUG (RECEIVE, ("Received packet type %d, length %d\n", + Binding->FrameType, + IpxPacketSize)); + IPX_DEBUG (RECEIVE, ("Source %lx %2.2x-%2.2x-%2.2x-%2.2x %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + *(USHORT UNALIGNED *)&IpxHeader->SourceSocket, + IpxHeader->SourceNetwork[0], + IpxHeader->SourceNetwork[1], + IpxHeader->SourceNetwork[2], + IpxHeader->SourceNetwork[3], + IpxHeader->SourceNode[0], + IpxHeader->SourceNode[1], + IpxHeader->SourceNode[2], + IpxHeader->SourceNode[3], + IpxHeader->SourceNode[4], + IpxHeader->SourceNode[5])); + IPX_DEBUG (RECEIVE, ("Destination %d %2.2x-%2.2x-%2.2x-%2.2x %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + DestinationSocket, + IpxHeader->DestinationNetwork[0], + IpxHeader->DestinationNetwork[1], + IpxHeader->DestinationNetwork[2], + IpxHeader->DestinationNetwork[3], + IpxHeader->DestinationNode[0], + IpxHeader->DestinationNode[1], + IpxHeader->DestinationNode[2], + IpxHeader->DestinationNode[3], + IpxHeader->DestinationNode[4], + IpxHeader->DestinationNode[5])); + +#if DBG + if (IpxHeader->DestinationSocket == IpxPacketLogSocket) { + ReceiveFlag = IPX_PACKET_LOG_RCV_SOCKET | IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_RCV_ALL; + } else { + ReceiveFlag = IPX_PACKET_LOG_RCV_OTHER | IPX_PACKET_LOG_RCV_ALL; + } +#endif + + // + // Fiddle with this if so in the general case + // the jump is not made (BUGBUG the compiler + // still rearranges it). + // + + if (Adapter->MacInfo.MediumType != NdisMedium802_5) { + +CallProcessDatagram: + // + // [SA] Returns a status now which needs to be returned to NDIS + // Also, MDL is passed in. + // We need to pass in the HeaderBufferSize too.... + // + IpxProcessDatagram( + Device, + Adapter, + Binding, + ReceiveContext, + &DatagramOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, // lookaheadbufferoffset + IpxPacketSize, + IsBroadcast, + pTdiClientCount, + HeaderBufferSize, + pMdl, + BindingContext); + + } else { + if (!IsLoopback) { + MacUpdateSourceRouting (IDENTIFIER_IPX, Adapter, Header, HeaderBufferSize); + } + goto CallProcessDatagram; + } + + // + // The router needs to see type 20 broadcasts. + // + + if (IsBroadcast && + (IpxHeader->PacketType == 0x14) && + (Binding->ReceiveBroadcast)) { + goto RipIndication; + } + } + + } else { + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_ALL; +#endif + + // + // We need to let non-type 20 broadcast frames go to RIP to allow for lan-specific + // broadcasts. For logon over IPX, this allows the logon request to get thru the WAN + // line. + // + // if ( !IsBroadcast ) { + +RipIndication:; + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_RIP, Adapter, Header, HeaderBufferSize); + } + + // + // We hide binding sets from the router, to avoid + // misordering packets which it routes. + // + + if (!IsLoopback && Binding->BindingSetMember) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&DatagramOptions.LocalTarget, MIN (Device->MaxBindings, Binding->MasterBinding->NicId)); +#else + DatagramOptions.LocalTarget.NicId = Binding->MasterBinding->NicId; +#endif + } + + (*Device->UpperDrivers[IDENTIFIER_RIP].ReceiveHandler)( + (IsLoopback) ? BindingContext : Adapter->NdisBindingHandle, + ReceiveContext, + &DatagramOptions.LocalTarget, + Adapter->MacInfo.MacOptions, + (PUCHAR)IpxHeader, + LookaheadBufferSize - IpxHeaderOffset, + (IsLoopback) ? IpxHeaderOffset+HeaderBufferSize : IpxHeaderOffset, + IpxPacketSize); + + Device->ReceiveCompletePending[IDENTIFIER_RIP] = TRUE; + } + // } + } + + } else { + + if ((Binding->ReceiveBroadcast) || + (!IPX_NODE_BROADCAST(IpxHeader->DestinationNode))) { + + SourceNetwork = *(UNALIGNED LONG *)IpxHeader->SourceNetwork; + + // + // Sent to the RIP socket; check if this binding needs a + // network number. + // + + if ((Binding->LocalAddress.NetworkAddress == 0) && + ((SourceNetwork = *(UNALIGNED LONG *)IpxHeader->SourceNetwork) != 0)) { + + switch (Device->AutoDetectState) { + + case AUTO_DETECT_STATE_DONE: + + // + // We are done with auto-detect and running. + // Make sure this packet is useful. If the source + // MAC address and source IPX node are the same then + // it was not routed, and we also check that it is not + // an IPX broadcast (otherwise a misconfigured client + // might confuse us). + // + + if ((RtlEqualMemory( + IpxHeader->SourceNode, + DatagramOptions.LocalTarget.MacAddress, + 6)) && + (*(UNALIGNED ULONG *)(IpxHeader->DestinationNode) != 0xffffffff) && + (*(UNALIGNED USHORT *)(IpxHeader->DestinationNode+4) != 0xffff)) { + + CTEAssert (Binding->NicId != 0); + + if (IpxUpdateBindingNetwork( + Device, + Binding, + *(UNALIGNED LONG *)IpxHeader->SourceNetwork) == STATUS_SUCCESS) { + + IPX_DEBUG (RIP, ("Binding %d is network %lx\n", + Binding->NicId, + REORDER_ULONG(Binding->LocalAddress.NetworkAddress))); + + } + } + + break; + + case AUTO_DETECT_STATE_RUNNING: + + // + // We are waiting for rip responses to figure out our + // network number. We count the responses that match + // and do not match our current value; when the non- + // matching number exceeds it we switch (to whatever + // this frame happens to have). Note that on the first + // non-zero response this will be the case and we will + // switch to that network. + // + // After auto-detect is done we call RipInsertLocalNetwork + // for whatever the current network is on each binding. + // + + if (SourceNetwork == Binding->TentativeNetworkAddress) { + + ++Binding->MatchingResponses; + + } else { + + ++Binding->NonMatchingResponses; + + if (Binding->NonMatchingResponses > Binding->MatchingResponses) { + + IPX_DEBUG (AUTO_DETECT, ("Switching to net %lx on %lx (%d - %d)\n", + REORDER_ULONG(SourceNetwork), + Binding, + Binding->NonMatchingResponses, + Binding->MatchingResponses)); + + Binding->TentativeNetworkAddress = SourceNetwork; + Binding->MatchingResponses = 1; + Binding->NonMatchingResponses = 0; + } + + } + + // + // If we are auto-detecting and we have just found + // a default, set this so that RIP stops trying + // to auto-detect on other nets. BUGBUG: Unless we + // are on a server doing multiple detects. + // + + if (Binding->DefaultAutoDetect) { + Adapter->DefaultAutoDetected = TRUE; + } + Adapter->AutoDetectResponse = TRUE; + + break; + + default: + + // + // We are still initializing, or are processing auto-detect + // responses, not the right time to start updating stuff. + // + + break; + + } + + } + + + // + // See if any packets are waiting for a RIP response. + // + + if (Device->RipPacketCount > 0) { + + RIP_PACKET UNALIGNED * RipPacket = (RIP_PACKET UNALIGNED *)(IpxHeader+1); + + if ((IpxPacketSize >= sizeof(IPX_HEADER) + sizeof(RIP_PACKET)) && + (RipPacket->Operation == RIP_RESPONSE) && + (RipPacket->NetworkEntry.NetworkNumber != 0xffffffff)) { + + RipProcessResponse( + Device, + &DatagramOptions.LocalTarget, + RipPacket); + } + } + + + // + // See if this is a RIP response for our virtual network + // and we are the only person who could respond to it. + // We also respond to general queries on WAN lines since + // we are the only machine on it. + // + + if (Device->RipResponder) { + + PRIP_PACKET RipPacket = + (PRIP_PACKET)(IpxHeader+1); + + if ((IpxPacketSize >= sizeof(IPX_HEADER) + sizeof(RIP_PACKET)) && + (RipPacket->Operation == RIP_REQUEST) && + ((RipPacket->NetworkEntry.NetworkNumber == Device->VirtualNetworkNumber) || + (Adapter->MacInfo.MediumAsync && (RipPacket->NetworkEntry.NetworkNumber == 0xffffffff)))) { + + // + // Update this so our response goes out correctly. + // + + if (!IsLoopback && Adapter->MacInfo.MediumType == NdisMedium802_5) { + MacUpdateSourceRouting (IDENTIFIER_IPX, Adapter, Header, HeaderBufferSize); + } + + RipSendResponse( + Binding, + (TDI_ADDRESS_IPX UNALIGNED *)(IpxHeader->SourceNetwork), + &DatagramOptions.LocalTarget); + } + } + +#if DBG + ReceiveFlag = IPX_PACKET_LOG_RCV_RIP | IPX_PACKET_LOG_RCV_ALL; +#endif + + // + // See if the RIP upper driver wants it too. + // + + goto RipIndication; + } + + } + + +#ifdef _PNP_POWER + IpxDereferenceAdapter(Adapter); + IpxDereferenceBinding1(Binding, BREF_ADAPTER_ACCESS); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(ReceiveFlag)) { + IpxLogPacket( + FALSE, + DestMacAddress, + DatagramOptions.LocalTarget.MacAddress, + (USHORT)IpxPacketSize, + IpxHeader, + IpxHeader+1); + } +#endif + return NDIS_STATUS_SUCCESS; + + // + // These are the failure routines for the various media types. + // They only differ in the debug logging. + // + +NotValid802_3: + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header, Header+6, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + +NotValid802_5: + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+2, Header+8, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + return NDIS_STATUS_SUCCESS; + +NotValidFddi: + +#ifdef _PNP_POWER + + IpxDereferenceAdapter(Adapter); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif +NotValidLoopback: + +#ifdef IPX_PACKET_LOG + if (PACKET_LOG(IPX_PACKET_LOG_RCV_ALL)) { + IpxLogPacket(FALSE, Header+1, Header+7, (USHORT)PacketSize, LookaheadBuffer, (PUCHAR)LookaheadBuffer + sizeof(IPX_HEADER)); + } +#endif + + return NDIS_STATUS_SUCCESS; + +} /* IpxReceiveIndication */ + + +VOID +IpxReceiveComplete( + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a connection(less) frame has been received on the + physical link. We dispatch to the correct packet handler here. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + +Return Value: + + None + +--*/ + +{ + + PADAPTER Adapter = (PADAPTER)BindingContext; + PREQUEST Request; + PADDRESS_FILE AddressFile; + PLIST_ENTRY linkage; + + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&Adapter->RequestCompletionQueue)) { + + linkage = IPX_REMOVE_HEAD_LIST( + &Adapter->RequestCompletionQueue, + Adapter->DeviceLock); + + if (!IPX_LIST_WAS_EMPTY (&Adapter->RequestCompletionQueue, linkage)) { + + Request = LIST_ENTRY_TO_REQUEST(linkage); + AddressFile = REQUEST_OPEN_CONTEXT(Request); + + IPX_DEBUG (RECEIVE, ("Completing RDG on %lx\n", AddressFile)); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IpxCompleteRequest(Request); + IpxFreeRequest(Adapter->Device, Request); + + IpxDereferenceAddressFileSync (AddressFile, AFREF_RCV_DGRAM); + + } else { + + // + // IPX_REMOVE_HEAD_LIST returned nothing, so don't + // bother looping back. + // + + break; + + } + + } + + // + // Unwind this loop for speed. + // + + if (IpxDevice->AnyUpperDriverBound) { + + PDEVICE Device = IpxDevice; + + if ((Device->UpperDriverBound[0]) && + (Device->ReceiveCompletePending[0])) { + + (*Device->UpperDrivers[0].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[0] = FALSE; + + } + + if ((Device->UpperDriverBound[1]) && + (Device->ReceiveCompletePending[1])) { + + (*Device->UpperDrivers[1].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[1] = FALSE; + + } + + if ((Device->UpperDriverBound[2]) && + (Device->ReceiveCompletePending[2])) { + + (*Device->UpperDrivers[2].ReceiveCompleteHandler)( + (USHORT)1); // BUGBUG: Fix NIC ID or remove. + Device->ReceiveCompletePending[2] = FALSE; + + } + + } + +} /* IpxReceiveComplete */ + + +NTSTATUS +IpxUpdateBindingNetwork( + IN PDEVICE Device, + IN PBINDING Binding, + IN ULONG Network + ) + +/*++ + +Routine Description: + + This routine is called when we have decided that we now know + the network number for a binding which we previously thought + was zero. + +Arguments: + + Device - The IPX device. + + Binding - The binding being updated. + + Network - The new network number. + +Return Value: + + The status of the operation. + +--*/ + +{ + NTSTATUS Status; + PADDRESS Address; + ULONG CurrentHash; + PLIST_ENTRY p; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Only binding set members should have these different, + // and they will not have a network of 0. + // + + Status = RipInsertLocalNetwork( + Network, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if (Status == STATUS_SUCCESS) { + + Binding->LocalAddress.NetworkAddress = Network; + + // + // Update the device address if we have no virtual net + // and there is one binding (!Device->MultiCardZeroVirtual) + // or this is the first binding, which is the one we + // appear to be if a) we have no virtual net defined and + // b) we are bound to multiple cards. + // +#ifdef _PNP_POWER + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + + if (!Device->VirtualNetwork) { + + Device->SourceAddress.NetworkAddress = Network; + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = Network; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Let SPX know because it fills in its own headers. + // + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_PNP_INFO IpxPnPInfo; + + IpxPnPInfo.NewReservedAddress = TRUE; + IpxPnPInfo.NetworkAddress = Network; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADDRESS_CHANGED to SPX: net addr: %lx\n", Network)); + } + + } + } +#else + if ((!Device->VirtualNetwork) && + ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1))) { + + Device->SourceAddress.NetworkAddress = Network; + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = Network; + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Let SPX know because it fills in its own + // headers. When we indicate a line up on NIC ID + // 0 it knows to requery the local address. + // + // BUGBUG: Line up indication to RIP/NB?? + // + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + + IPX_LINE_INFO LineInfo; + LineInfo.LinkSpeed = Device->LinkSpeed; + LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + LineInfo.MacOptions = Device->MacOptions; + + (*Device->UpperDrivers[IDENTIFIER_SPX].LineUpHandler)( + 0, + &LineInfo, + Binding->Adapter->MacInfo.RealMediumType, + NULL); + + } + } +#endif + } else if (Status == STATUS_DUPLICATE_NAME) { + + // + // If it was a duplicate we still set the binding's local + // address to the value so we can detect binding sets. + // + + Binding->LocalAddress.NetworkAddress = Network; + + } + + return Status; + +} /* IpxUpdateBindingNetwork */ + + +INT +IpxReceivePacket ( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET Packet + ) +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + The packet passed up from NDIS can be held on to by the TDI clients + that request TDI_EVENT_RECEIVE_EX_DATAGRAM events with us. + +Arguments: + + ProtocolBindingContext - The Adapter Binding specified at initialization time. + + Packet - contains the packet received as well as some mediaspecific info. + +Return Value: + + return of IpxReceiveIndicationNew(), + +--*/ +{ + UINT HeaderBufferSize = NDIS_GET_PACKET_HEADER_SIZE(Packet); + UINT firstbufferLength, bufferLength; + PNDIS_BUFFER pFirstBuffer; + PUCHAR headerBuffer; + NTSTATUS ntStatus; + INT tdiClientCount = 0; + + // + // Query the number of buffers, the first MDL's descriptor and the packet length + // + NdisGetFirstBufferFromPacket(Packet, // packet + &pFirstBuffer, // first buffer descriptor + &headerBuffer, // ptr to the start of packet + &firstbufferLength,// length of the header+lookahead + &bufferLength); // length of the bytes in the buffers + + // + // ReceiveContext is the packet itself + // + + ntStatus = IpxReceiveIndicationNew ( + ProtocolBindingContext, + Packet, // ReceiveContext + headerBuffer, + HeaderBufferSize, + headerBuffer + HeaderBufferSize, // LookaheadBuffer + bufferLength - HeaderBufferSize, // LookaheadBufferSize + bufferLength - HeaderBufferSize, // PacketSize - since the whole packet is indicated + pFirstBuffer, // pMdl + &tdiClientCount // tdi client count + ); + + IPX_DEBUG(RECEIVE, ("IpxReceivePacket: Tdi Client Count is: %lx\n", tdiClientCount)); + + return tdiClientCount; +} /* IpxReceivePacket */ + + +#ifdef _PNP_POWER + +#if defined(_M_IX86) +_inline +#endif +BOOLEAN +IpxNewVirtualNetwork( + IN PDEVICE Device, + IN BOOLEAN NewVirtualNetwork + ) +/*++ + +Routine Description: + + If the virtualnetwork number changed, this function records this fact + in the device. + + Called with the BINDACCESSLOCK held. +Arguments: + + Device - Pointer to the Device. + + NewVirtualNetwork - boolean to indicate if the virtual net# changed. + +Return Value: + + BOOLEAN - to indicate whether SPX's reserved address was changed. + +--*/ +{ + NTSTATUS ntStatus; + UCHAR VirtualNode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + BOOLEAN ReservedAddrChanged = FALSE; + + if (Device->VirtualNetworkNumber) { + + if (NewVirtualNetwork) { + // + // If a new one appeared. + // + + ntStatus = RipInsertLocalNetwork( + Device->VirtualNetworkNumber, + 0, // NIC ID + NIC_ID_TO_BINDING(Device, 1)->Adapter->NdisBindingHandle, + 1); + + if (ntStatus != STATUS_SUCCESS) { + + // + // Log the appropriate error, then ignore the + // virtual network. If the error was + // INSUFFICIENT_RESOURCES, the RIP module + // will have already logged an error. + // + + if (ntStatus == STATUS_DUPLICATE_NAME) { + + IPX_DEBUG (AUTO_DETECT, ("Ignoring virtual network %lx, conflict\n", REORDER_ULONG (Device->VirtualNetworkNumber))); + + IpxWriteResourceErrorLog( + Device->DeviceObject, + EVENT_IPX_INTERNAL_NET_INVALID, + 0, + REORDER_ULONG (Device->VirtualNetworkNumber)); + } + + Device->VirtualNetworkNumber = 0; + goto NoVirtualNetwork; + + } + + // + // If the number is non-zero now, a new one appeared + // + Device->VirtualNetwork = TRUE; + Device->MultiCardZeroVirtual = FALSE; + RtlCopyMemory(Device->SourceAddress.NodeAddress, VirtualNode, 6); + Device->SourceAddress.NetworkAddress = Device->VirtualNetworkNumber; + ReservedAddrChanged = TRUE; + + // + // If RIP is not bound, then this node is a RipResponder + // + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + Device->RipResponder = TRUE; + } + } + + } else { +NoVirtualNetwork: + Device->VirtualNetwork = FALSE; + + // + // See if we need to be set up for the fake + // virtual network. + // + + if (Device->ValidBindings > 1) { + + CTEAssert (Device->VirtualNetworkOptional); + + // + // In this case we return as our local node the + // address of the first card. We will also only + // direct SAP sends to that card. + // + + Device->MultiCardZeroVirtual = TRUE; + + } else { + + Device->MultiCardZeroVirtual = FALSE; + } + + if (NewVirtualNetwork) { + // + // The virtual network number disappeared this time + // + + // + // Remove the prev. net # from the RIP tables here + // + RipAdjustForBindingChange (0, 0, IpxBindingDeleted); + + // + // If we were a RipResponder, we are not anymore + // + if (Device->RipResponder) { + Device->RipResponder = FALSE; + } + } + + // + // Since there is not virtual network number, SPX's reserved address is + // the address of the first binding. This could have changed because of + // several reasons: if there was a WAN binding only earlier and this time + // a LAN binding appeared, or if the first LAN binding disappeared. Instead + // of checking for all these conditions, check if the Device's sourceaddress + // and that of the first mis-match. + // NB uses the address of the first device always and hence does not need + // this mechanism to determine if this is a reserved address change. + // + if (!RtlEqualMemory( &Device->SourceAddress, + &NIC_ID_TO_BINDING(Device, 1)->LocalAddress, + FIELD_OFFSET(TDI_ADDRESS_IPX,Socket))) { + + RtlCopyMemory( &Device->SourceAddress, + &NIC_ID_TO_BINDING(Device, 1)->LocalAddress, + FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + + ReservedAddrChanged = TRUE; + } + } + + return ReservedAddrChanged; +} + + +VOID +IpxBindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ) + +/*++ + +Routine Description: + + This routine receives a Plug and Play notification about a new + adapter in the machine. We are called here only if this adapter + is to be bound to us, so we don't make any checks for this. + +Arguments: + + Status - NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING + + BindContext - context to represent this bind indication + + DeviceName - Name of the adapter that appeared (e.g. \Device\Lance1) + + SystemSpecific1/2 - Not used here + +Return Value: + + Status - NDIS_STATUS_SUCCESS + +--*/ +{ + NTSTATUS ntStatus; + PDEVICE Device = IpxDevice; + PADAPTER Adapter = NULL; + CONFIG Config; + UINT i; + ULONG Temp, SuccessfulOpens=0; + PBINDING Binding; + BINDING_CONFIG ConfigBinding; + ULONG ValidBindings; + USHORT AutoDetectReject; + BOOLEAN NewVirtualNetwork = FALSE; + BOOLEAN FirstDevice = FALSE; + BOOLEAN ReservedAddrChanged = FALSE; + IPX_PNP_INFO IpxPnPInfo; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // Used for error logging + // + Config.DriverObject = (PDRIVER_OBJECT)Device->DeviceObject; + + Config.RegistryPathBuffer = Device->RegistryPathBuffer; + ConfigBinding.AdapterName = *DeviceName; + + // + // Read the registry to see if a virtual network number appeared/disappeared + // + ntStatus = IpxPnPGetVirtualNetworkNumber(&Config); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the vnet#: registrypathbuffer: %lx\n", Device->RegistryPathBuffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + Temp = REORDER_ULONG (Config.Parameters[CONFIG_VIRTUAL_NETWORK]); + + // + // If the virtual network number changed, record this fact. + // + if (Device->VirtualNetworkNumber != Temp) { + NewVirtualNetwork = TRUE; + Device->VirtualNetworkNumber = Temp; + } + + Device->VirtualNetworkOptional = (BOOLEAN)(Config.Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + IPX_DEBUG(PNP, ("Virtual net # is: %lx\n", Temp)); + + // + // For each FrameType and Network Number configured, initialize the + // FrameType array in the CONFIG_BINDING + // + ntStatus = IpxPnPGetAdapterParameters( + &Config, + DeviceName, + &ConfigBinding); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the adapter params: DeviceName: %lx\n", DeviceName->Buffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + IPX_DEBUG(PNP, ("ConfigBinding.FrameTypeCount: %lx\n", ConfigBinding.FrameTypeCount)); + + // + // Reset the auto-detect state to init so that if a receive occurs on this binding + // before we can place this binding in the device's binding array, we know of it. + // + Device->AutoDetectState = AUTO_DETECT_STATE_INIT; + + // + // Register adapter with NDIS; query the various parameters; get the WAN line count + // if this is a WAN adapter. + // Allocate the bindings corresponding to this adapter + // + for (i = 0; i < ConfigBinding.FrameTypeCount; i++) { + + // + // If successful, this queues them on Device->InitialBindingList. [BUGBUGZZ] not right now + // Adapter is NULL first time and is allocated then. In subsequent calls, + // it is not NULL and the bindings are hooked to this adapter. + + ntStatus = IpxBindToAdapter (Device, &ConfigBinding, &Adapter, i); + + // + // If this failed because the adapter could not be bound + // to, then don't try any more frame types on this adapter. + // For other failures we do try the other frame types. + // + + if (ntStatus == STATUS_DEVICE_DOES_NOT_EXIST) { + break; + } + + // + // If the status is STATUS_NOT_SUPPORTED, then this frametype mapped to a previously + // initialized one. In this case, remove this index fron the FrameType array so that + // when we try to update the binding array, we dont have duplicates. + // + if (ntStatus == STATUS_NOT_SUPPORTED) { + ULONG j; + + // + // Remove this frametype from the FrameType array. + // + for (j = i+1; j < ConfigBinding.FrameTypeCount; j++) { + ConfigBinding.FrameType[j-1] = ConfigBinding.FrameType[j]; + } + + --ConfigBinding.FrameTypeCount; + + // + // Decrement so we see the one just moved up. + // + --i; + +#if DBG + for (j = 0; j < ISN_FRAME_TYPE_MAX; j++) { + IPX_DEBUG (AUTO_DETECT, ("%d: type %d, net %d, auto %d\n", + j, ConfigBinding.FrameType[j], ConfigBinding.NetworkNumber[j], ConfigBinding.AutoDetect[j])); + } +#endif + continue; + } + + if (ntStatus != STATUS_SUCCESS) { + continue; + } + + if (ConfigBinding.AutoDetect[i]) { + Device->AutoDetect = TRUE; + } + + CTEAssert(Adapter); + + ++SuccessfulOpens; + + // + // Even for WAN adapters, the FrameTypeCount is set to 4. We only need to + // allocate one binding for WAN; the others come later. + // + if (Adapter->MacInfo.MediumAsync) { + break; + } + } + + if (SuccessfulOpens == 0) { + goto InitFailed; + } + + // + // Place all the bindings corresponding to this adapter in the binding array + // Also resolve binding sets for non-autodetect bindings. + // + + // + // Obtain lock to the Binding related stuff. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + IpxPnPUpdateBindingArray (Device, Adapter, &ConfigBinding); + + // + // Release access to the Binding related stuff. + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // If at least one card appeared here, set our state + // to open + // + // [BUGBUGZZ]: what if all these bindings are eliminated - then + // the state is not open... + // + if (Device->ValidBindings > 0) { + if (Device->State == DEVICE_STATE_LOADED) { + FirstDevice = TRUE; + Device->State = DEVICE_STATE_OPEN; + } + } + + // + // We don't do auto-detect/bindingsets for WAN lines: skip over. + // + if (Adapter->MacInfo.MediumAsync) { + goto jump_wan; + } + + // + // Auto-detect the network number. Update the results for only the + // bindings corresponding to this adapter + // + + // + // Queue a request to discover our locally attached + // adapter addresses. This must succeed because we + // just allocated our send packet pool. We need + // to wait for this, either because we are + // auto-detecting or because we need to determine + // if there are multiple cards on the same network. + // + + KeInitializeEvent( + &Device->AutoDetectEvent, + NotificationEvent, + FALSE + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_RUNNING; + + // + // Make this 0; after we are done waiting, which means + // the packet has been completed, we set it to the + // correct value. + // + + // Device->IncludedHeaderOffset = 0; + + IPX_BEGIN_SYNC (&SyncContext); + ntStatus = RipQueueRequest (0xffffffff, RIP_REQUEST); + IPX_END_SYNC (&SyncContext); + + CTEAssert (ntStatus == STATUS_PENDING); + + // + // This is set when this rip send completes. + // + + IPX_DEBUG (AUTO_DETECT, ("Waiting for AutoDetectEvent\n")); + + KeWaitForSingleObject( + &Device->AutoDetectEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + Device->AutoDetectState = AUTO_DETECT_STATE_PROCESSING; + + // + // Now that we are done receiving responses, insert the + // current network number for every auto-detect binding + // to the rip database. + // + + // + // Obtain exclusive access to the Binding related stuff. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Note, here we go thru' only the bindings corresponding to this adapter + // + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + + Binding = Adapter->Bindings[i]; + + // + // Skip empty binding slots or bindings that were configured + // for a certain network number, we inserted those above. + // If no network number was detected, also skip it. + // + + if ((!Binding) || + (Binding->ConfiguredNetworkNumber != 0) || + (Binding->TentativeNetworkAddress == 0)) { + + continue; + } + + IPX_DEBUG (AUTO_DETECT, ("Final score for %lx on %lx is %d - %d\n", + REORDER_ULONG(Binding->TentativeNetworkAddress), + Binding, + Binding->MatchingResponses, + Binding->NonMatchingResponses)); + + // + // We don't care about the status. + // + + ntStatus = RipInsertLocalNetwork( + Binding->TentativeNetworkAddress, + Binding->NicId, + Binding->Adapter->NdisBindingHandle, + (USHORT)((839 + Binding->MediumSpeed) / Binding->MediumSpeed)); + + if ((ntStatus != STATUS_SUCCESS) && + (ntStatus != STATUS_DUPLICATE_NAME)) { + + // + // We failed to insert, keep it at zero, hopefully + // we will be able to update later. + // + +#if DBG + DbgPrint ("IPX: Could not insert net %lx for binding %lx\n", + REORDER_ULONG(Binding->LocalAddress.NetworkAddress), + Binding); +#endif + CTEAssert (Binding->LocalAddress.NetworkAddress == 0); + + } else { + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + Binding->LocalAddress.NetworkAddress = Binding->TentativeNetworkAddress; + } + + // ValidBindings = Device->BindingCount; + + ValidBindings = Device->ValidBindings; + + // [BUGBUGZZ] if (Device->AutoDetect) { + + ValidBindings = IpxResolveAutoDetect (Device, ValidBindings, &LockHandle1, &Device->RegistryPath); + + //} + + // + // Adjust all the indices by the number of AutoDetect bindings thrown away + // + // AutoDetectReject = (USHORT)(Device->BindingCount - ValidBindings); + + AutoDetectReject = (USHORT)(Device->ValidBindings - ValidBindings); + + Device->HighestLanNicId -= AutoDetectReject; + Device->HighestExternalNicId -= AutoDetectReject; + Device->HighestType20NicId -= AutoDetectReject; + Device->SapNicCount -= AutoDetectReject; + + Device->ValidBindings = (USHORT)ValidBindings; + + // + // Now see if any bindings are actually on the same + // network. This updates the Device->HighestExternalNicId + // and Device->HighestType20NicId, SapNicCount, HighestLanNicId + // + + // + // Do this only for the auto-detect bindings + // [BUGBUGZZ] check this + // + + //if (Device->AutoDetect) { + IpxResolveBindingSets (Device, Device->HighestExternalNicId); + //} + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +jump_wan: + + IPX_DEBUG(PNP, ("BindingCount: %lu\n", Device->BindingCount)); + IPX_DEBUG(PNP, ("ValidBindings: %lu\n", Device->ValidBindings)); + IPX_DEBUG(PNP, ("HighestLanNicId: %lu\n", Device->HighestLanNicId)); + IPX_DEBUG(PNP, ("HighestExternalNicId: %lu\n", Device->HighestExternalNicId)); + IPX_DEBUG(PNP, ("HighestType20NicId: %lu\n", Device->HighestType20NicId)); + IPX_DEBUG(PNP, ("SapNicCount: %lu\n", Device->SapNicCount)); + IPX_DEBUG(PNP, ("BindingArray: %lx\n", Device->Bindings)); + + // + // Enable this regardless of whether any of our clients enabled b'cast. + // NB always enables it, so we are fine. + // + // Since we dont increment the Broadcast count in the device, we will disable b'casts + // correctly if the count drops to 0. + // + // If the ISN clients appear before the adapters, they increment the BCount, but + // since the ValidBindings is 0, all works. Then, when the adapters appear, we enable + // the broadcasts here. + // + // If the adapters appear before the ISN clients, then the broadcast is enabled on + // the adapters here and the adapter's flag is set to indicate this, which will prevent + // any further calls to NDIS when the ISN clients force an IpxAddBroadcast. + // + Device->EnableBroadcastPending = TRUE; + IpxBroadcastOperation((PVOID)TRUE); + + // + // For multiple adapters, use the offset of the first...why not. + // + +#if 0 + Device->IncludedHeaderOffset = Device->Bindings[1]->DefHeaderSize; +#endif + + Device->IncludedHeaderOffset = MAC_HEADER_SIZE; + + // + // This function updates flags like RipResponder, MultiCardZeroVirtual, etc. + // If the VirtualNetwork number changed (NewVirtualNetwork is TRUE), it updates + // the Device structure and the RIP tables accordingly. + // It returns a boolean to indicate if SPX's reserved address changed. + // + ReservedAddrChanged = IpxNewVirtualNetwork(Device, NewVirtualNetwork); + + // + // Update the values once the auto-detect bindings have been thrown away... + // + IpxPnPUpdateDevice(Device); + + Device->AutoDetectState = AUTO_DETECT_STATE_DONE; + + IPX_DEBUG (DEVICE, ("Node is %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x, ", + Device->SourceAddress.NodeAddress[0], Device->SourceAddress.NodeAddress[1], + Device->SourceAddress.NodeAddress[2], Device->SourceAddress.NodeAddress[3], + Device->SourceAddress.NodeAddress[4], Device->SourceAddress.NodeAddress[5])); + IPX_DEBUG (DEVICE, ("Network is %lx\n", + REORDER_ULONG (Device->SourceAddress.NetworkAddress))); + + // + // Start the timer which updates the RIP database + // periodically. For the first one we do a ten + // second timeout (hopefully this is enough time + // for RIP to start if it is going to). + // + if (FirstDevice) { + UNICODE_STRING devicename; + + // + // Inform TDI clients about the open of our device object. + // + devicename.MaximumLength = (USHORT)Device->DeviceNameLength; + devicename.Length = (USHORT)Device->DeviceNameLength - sizeof(WCHAR); + devicename.Buffer = Device->DeviceName; + + if ((ntStatus = TdiRegisterDeviceObject( + &devicename, + &Device->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterDeviceObject failed: %lx", ntStatus)); + } + + IpxReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->RipLongTimer, + 10000, + RipLongTimeout, + (PVOID)Device); + + } + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Inform NB and TDI of all the bindings corresponding to this adapter + // + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Binding = Adapter->Bindings[i]; + + // + // If a NULL binding or a binding set slave, dont inform NB about it. + // + if (!Binding || (Binding->NicId > Device->HighestExternalNicId)) { +#if DBG + if (Binding) { + IPX_DEBUG(PNP, ("Binding: %lx, Binding set slave\n", Binding)); + } +#endif + continue; + } + + // + // Register this address with the TDI clients. + // + RtlCopyMemory (Device->TdiRegistrationAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Lock taken to check the UpperDriverBound flag. + // We already have the BindAccessLock at this point. + // + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // We could have informed the upper driver from IpxPnPIsnIndicate + // Ensure that we dont do it twice. + // + if (!Binding->IsnInformed[IDENTIFIER_NB]) { + + // + // Also, to ensure that the indications are done in the right order, + // check if the first card has been indicated yet. + // + if ((Binding->NicId != 1) && + !NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_NB]) { + + break; + } + + Binding->IsnInformed[IDENTIFIER_NB] = TRUE; + + if (Binding->NicId == 1) { + IpxPnPInfo.NewReservedAddress = TRUE; + + if (FirstDevice) { + IpxPnPInfo.FirstORLastDevice = TRUE; + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + } + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + IpxPnPInfo.NewReservedAddress = FALSE; + } + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to NB add: %lx\n", Binding)); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // Always true for SPX + // + IpxPnPInfo.NewReservedAddress = TRUE; + + if (FirstDevice) { + + IpxPnPInfo.FirstORLastDevice = TRUE; + + // + // We could have informed the upper driver from IpxPnPIsnIndicate + // + if (!NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX]) { + + NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX] = TRUE; + // + // Inform SPX - the network/node address is the Virtual one if it exists + // else the address of the first binding + // + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to SPX add: %lx\n", Binding)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + + // + // Not the first device - inform if the reserved address changed. + // + if (ReservedAddrChanged) { + if (!NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX]) { + NIC_ID_TO_BINDING_NO_ILOCK(Device, 1)->IsnInformed[IDENTIFIER_SPX] = TRUE; + IPX_DEBUG(PNP, ("Reserved addr changed; SPX not told of first one yet\n")); + } + + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + // + // new one appeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + // + // Old one disappeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("PnP to SPX add (res. addr change): %lx\n", Binding)); + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Release access to the Binding related stuff. + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + +InitFailed: + *Status = NDIS_STATUS_SUCCESS; + return; + +} /* IpxBindAdapter */ + + +VOID +IpxUnbindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_HANDLE UnbindContext + ) + +/*++ + +Routine Description: + + This routine receives a Plug and Play notification about the removal + of an existing adapter from the machine. We are called here only if + this adapter is to be bound to us, so we don't make any checks for this. + +Arguments: + + Status - NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING. + + ProtocolBindingContext - the adapter that got removed. + + UnbindContext - context to represent this bind indication. + +Return Value: + + Void - return thru' Status above. + +--*/ +{ + NTSTATUS ntStatus; + PADAPTER Adapter=(PADAPTER)ProtocolBindingContext; + CONFIG Config; + PBINDING Binding; + PDEVICE Device=IpxDevice; + ULONG i, Temp; + BOOLEAN NewVirtualNetwork = FALSE; + BOOLEAN NBReservedAddrChanged = FALSE; + BOOLEAN SPXInformed = FALSE; + IPX_PNP_INFO IpxPnPInfo; + PBINDING newMasterBinding; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // Used for error logging + // + Config.DriverObject = (PDRIVER_OBJECT)Device->DeviceObject; + + Config.RegistryPathBuffer = Device->RegistryPathBuffer; + + // + // Read the registry to see if a virtual network number appeared/disappeared + // + ntStatus = IpxPnPGetVirtualNetworkNumber(&Config); + + if (ntStatus != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("Could not read the vnet#: registrypathbuffer: %lx\n", Device->RegistryPathBuffer)); + *Status = NDIS_STATUS_SUCCESS; + return; + } + + Temp = REORDER_ULONG (Config.Parameters[CONFIG_VIRTUAL_NETWORK]); + + // + // If the VirtualNetwork number changed, record it. + // + if (Device->VirtualNetworkNumber != Temp) { + NewVirtualNetwork = TRUE; + } + + Device->VirtualNetworkOptional = (BOOLEAN)(Config.Parameters[CONFIG_VIRTUAL_OPTIONAL] != 0); + + IPX_DEBUG(PNP, ("Virtual net # is: %lx\n", Temp)); + + // + // If the WAN adapter disappeared, we can simply remove all the WAN bindings since + // all of them correspond to this single WAN adapter. Since we tell NB only about + // the first one of these, we need to indicate removal of only one binding to NB. + // + if (Adapter->MacInfo.MediumAsync) { + USHORT wanLineCount = (USHORT)Adapter->WanNicIdCount; + + CTEAssert(wanLineCount == (Device->HighestExternalNicId - Device->HighestLanNicId)); + + // + // If no more bindings remain, tell upper driver of the same. + // We go back to the loaded state. + // + + if ((Device->ValidBindings - wanLineCount) == 0) { + IpxPnPInfo.FirstORLastDevice = TRUE; + Device->State = DEVICE_STATE_LOADED; + + // + // Shut down RIP timers, complete address notify requests, etc. + // + IpxPnPToLoad(); + } else { + CTEAssert(Device->State == DEVICE_STATE_OPEN); + IpxPnPInfo.FirstORLastDevice = FALSE; + } + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // Get to the first WAN binding - this is always the one after the last LAN binding. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + + // + // DeRegister this address with the TDI clients. + // + + CTEAssert(Binding->TdiRegistrationHandle); + + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Give the PnP indication to indicate the deletion only if it was + // added before. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: delete WAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("PnP to NB delete: %lx\n", Binding)); + } +#if DBG + else { + DbgPrint("WAN adapter id: %lx not indicated to NB\n", Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#endif + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Inform SPX only if this is the last device. + // + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + if (IpxPnPInfo.FirstORLastDevice && Binding->IsnInformed[IDENTIFIER_SPX]) { + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEAssert(Device->HighestLanNicId == 0); + + // + // Get to the first WAN binding - this is always the one after the last LAN binding. + // + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, Device->HighestLanNicId+1); + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform SPX: delete WAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + } + + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Now remove these WAN bindings from the array. Move all the Slave bindings + // up to where the WAN bindings were. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + for (i = Device->HighestLanNicId+1; i <= Device->HighestExternalNicId; i++) { + // + // Unbind from the adapter - if it is not referenced by any other thread, it will + // be deleted at this point. + // + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(NIC_ID_TO_BINDING_NO_ILOCK(Device, i)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + // + // Move the slave binding here. + // + INSERT_BINDING(Device, i, NIC_ID_TO_BINDING_NO_ILOCK(Device, i+wanLineCount)); + } + + /* + RtlCopyMemory( Device->Bindings[Device->HighestLanNicId+1], + Device->Bindings[Device->HighestExternalNicId+1], + (Device->ValidBindings - Device->HighestExternalNicId) * sizeof(PBIND_ARRAY_ELEM)); + */ + + // + // Update the indices + // + Device->HighestExternalNicId -= wanLineCount; + Device->ValidBindings -= wanLineCount; + Device->BindingCount -= wanLineCount; + Device->SapNicCount = Device->HighestType20NicId = Device->HighestLanNicId; + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + CTEAssert(Device->HighestLanNicId == Device->HighestExternalNicId); + + } else { + // + // LAN adapter disappeared. + // + + // + // Set up the LineInfo struct. + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + // + // For each binding corresponding to this adapter, inform NB only + // if the binding addition was indicated. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + for (i = 0; i < ISN_FRAME_TYPE_MAX; i++) { + Binding = Adapter->Bindings[i]; + + if (!Binding) { + continue; + } + + // + // We cannot receive on this binding anymore + // + Adapter->Bindings[i] = NULL; + + // + // If this was a slave binding, dont inform of the deletion. + // Just remove the binding from the binding array and the bindingset list. + // + + if (Binding->NicId > Device->HighestExternalNicId) { + PBINDING MasterBinding, tempBinding; + + CTEAssert(Binding->BindingSetMember); + CTEAssert(Binding->CurrentSendBinding == NULL); + + // + // Traverse the bindingset list and remove this binding from there. + // + tempBinding = MasterBinding = Binding->MasterBinding; + + while (tempBinding->NextBinding != MasterBinding) { + if (tempBinding->NextBinding == Binding) { + tempBinding->NextBinding = tempBinding->NextBinding->NextBinding; + break; + } + tempBinding = tempBinding->NextBinding; + } + + // + // If no more slaves, this is no longer a bindingset. + // + if (MasterBinding->NextBinding == MasterBinding) { + MasterBinding->BindingSetMember = FALSE; + MasterBinding->CurrentSendBinding = NULL; + MasterBinding->ReceiveBroadcast = TRUE; + + IPX_DEBUG(PNP, ("Slave binding: %lx removed, no master: %lx\n", Binding, MasterBinding)); + } + + // + // Change the slave binding entries to have the master's NicId + // + RipAdjustForBindingChange (Binding->NicId, MasterBinding->NicId, IpxBindingMoved); + IPX_DEBUG(PNP, ("RipAdjustForBindingChange (%d, %d, IpxBindingMoved)\n", Binding->NicId, MasterBinding->NicId)); + + // + // Null out the Slave binding. + // + INSERT_BINDING(Device, Binding->NicId, NULL); + + --Device->ValidBindings; + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(Binding); + + continue; + } + + // + // If this was the last binding, go back to loaded state and shut down the RIP timers. + // + if (Device->ValidBindings == 1) { + CTEAssert(Device->HighestExternalNicId == 1); + CTEAssert(Device->HighestLanNicId == 1); + CTEAssert(Device->SapNicCount == 1); + CTEAssert(Device->HighestType20NicId == 1); + + Device->State = DEVICE_STATE_LOADED; + IpxPnPInfo.FirstORLastDevice = TRUE; + + // + // Shut down RIP timers, complete address notify requests, etc. + // + IpxPnPToLoad(); + + } else { + CTEAssert(Device->State == DEVICE_STATE_OPEN); + IpxPnPInfo.FirstORLastDevice = FALSE; + } + + // + // If this was a master binding, promote a slave binding to master. + // + if (Binding->BindingSetMember) { + + CTEAssert(Binding->CurrentSendBinding); + CTEAssert(Binding->MasterBinding == Binding); + + // + // Promote the next slave to Master. + // + newMasterBinding = Binding->NextBinding; + INSERT_BINDING(Device, Binding->NicId, newMasterBinding); + newMasterBinding->CurrentSendBinding = newMasterBinding; + newMasterBinding->MasterBinding = newMasterBinding; + + // + // If this is the only binding remaining out of its set, + // it is no longer part of a set. + // + if (newMasterBinding->NextBinding == Binding) { + newMasterBinding->NextBinding = newMasterBinding->CurrentSendBinding = NULL; + newMasterBinding->BindingSetMember = FALSE; + newMasterBinding->ReceiveBroadcast = TRUE; + + IPX_DEBUG(PNP, ("Master binding: %lx removed, no master: %lx\n", Binding, newMasterBinding)); + } + + // + // Change the slave binding entries to have the master's NicId + // + RipAdjustForBindingChange (newMasterBinding->NicId, Binding->NicId, IpxBindingMoved); + IPX_DEBUG(PNP, ("RipAdjustForBindingChange (%d, %d, IpxBindingMoved)\n", newMasterBinding->NicId, Binding->NicId)); + + // + // Register slave's address with the TDI clients. + // + CTEAssert(!newMasterBinding->TdiRegistrationHandle); + + RtlCopyMemory ( Device->TdiRegistrationAddress->Address, + &newMasterBinding->LocalAddress, + sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &newMasterBinding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + + // + // Null out the slave binding + // + INSERT_BINDING(Device, newMasterBinding->NicId, NULL); + + newMasterBinding->NicId = Binding->NicId; + + IPX_DEBUG(PNP, ("Promoted a master binding: %lx, old master: %lx\n", newMasterBinding, Binding)); + } else { + + ULONG j; + + // + // Remove the binding from the array + // + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDeleted); + + for (j = Binding->NicId+1; j <= Device->HighestExternalNicId; j++) { + INSERT_BINDING(Device, j-1, NIC_ID_TO_BINDING_NO_ILOCK(Device, j)); + --NIC_ID_TO_BINDING_NO_ILOCK(Device, j)->NicId; + } + + INSERT_BINDING(Device, Device->HighestExternalNicId, NULL); + + --Device->HighestExternalNicId; + --Device->HighestLanNicId; + --Device->HighestType20NicId; + --Device->SapNicCount; + } + + --Device->ValidBindings; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + + // + // If this is the first binding, NB's reserved will change. + // When we inform SPX of an address change later, we dont have + // this binding to know if this binding was indicated to SPX earlier. + // So, set SPXInformed, which is used later to determine if an address + // change is to be indicated to SPX later. + // + // Since NB is informed of all adapters, we inform of the reserved address + // change to NB if the new Binding (now at NicId 1) was indicated earlier. + // + if (Binding->NicId == 1) { + NBReservedAddrChanged = TRUE; + if (Binding->IsnInformed[IDENTIFIER_SPX]) { + SPXInformed = TRUE; + } + } + + CTEAssert(Binding->TdiRegistrationHandle); + + // + // DeRegister this address with the TDI clients. + // + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + // + // If this binding's addition was indicated earlier, indicate its deletion to NB. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: delete LAN device: %lx\n", Binding)); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // If this was a Master, indicate the addition of the (promoted) slave + // + if (Binding->BindingSetMember) { + IpxPnPInfo.NetworkAddress = newMasterBinding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, newMasterBinding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, newMasterBinding->NicId); + + // + // In this case, we set the ReservedAddrChanged bit here itself so dont need + // to indicate a separate address changed. + // + IpxPnPInfo.NewReservedAddress = (NBReservedAddrChanged) ? TRUE : FALSE; + NBReservedAddrChanged = FALSE; + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: add slave device: NicId: %lx\n", Binding->NicId)); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + newMasterBinding->IsnInformed[IDENTIFIER_NB] = TRUE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + } + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + + // + // Last device - inform SPX if it is bound and this device was added earlier. + // + if (IpxPnPInfo.FirstORLastDevice) { + IPX_DEBUG(PNP, ("Last device - inform SPX\n")); + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + if (Binding->IsnInformed[IDENTIFIER_SPX]) { + + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform SPX: last LAN device\n")); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + + // + // Unbind from the adapter so it can be deleted + // + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IpxUnBindFromAdapter(Binding); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + + // + // Update the Device and RIP tables if this is not the last device. + // If the reserved address changed, inform NB and SPX of this change. + // + if (!IpxPnPInfo.FirstORLastDevice) { + + Binding = NIC_ID_TO_BINDING_NO_ILOCK(Device, 1); + + if (IpxNewVirtualNetwork(Device, NewVirtualNetwork)) { + + IPX_DEBUG(PNP, ("SPX's reserved address changed\n")); + + // + // SPX's reserved address changed + // + IpxPnPInfo.NewReservedAddress = TRUE; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + + // + // If this binding's addition was indicated earlier, indicate change of address. + // + if (SPXInformed) { + Binding->IsnInformed[IDENTIFIER_SPX] = TRUE; + + IPX_DEBUG(PNP, ("Inform SPX: reserved address changed\n")); + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + + if (Device->VirtualNetwork) { + // + // new one appeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + // + // Old one disappeared + // + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } else { + + // + // Set the first binding's flag so that when this binding goes away, we remember + // to inform SPX of this device's removal. + // + + IPX_DEBUG(PNP, ("Transfer SPX informed flag to NicId: %lx\n", Binding->NicId)); + Binding->IsnInformed[IDENTIFIER_SPX] = TRUE; + } + + if (NBReservedAddrChanged) { + // + // NB's reserved address changed. + // + IpxPnPInfo.NewReservedAddress = TRUE; + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + // + // If this binding's addition was indicated earlier, indicate the change of reserved address. + // + if (Binding->IsnInformed[IDENTIFIER_NB]) { + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IPX_DEBUG(PNP, ("Inform NB: reserved address changed\n")); + + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } else { + IPX_FREE_LOCK(&Device->Lock, LockHandle); + } + } + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + // + // Re-calculate the values of datagram sizes in the Device. + // + IpxPnPUpdateDevice(Device); + + IPX_DEBUG(PNP, ("BindingCount: %lu\n", Device->BindingCount)); + IPX_DEBUG(PNP, ("ValidBindings: %lu\n", Device->ValidBindings)); + IPX_DEBUG(PNP, ("HighestLanNicId: %lu\n", Device->HighestLanNicId)); + IPX_DEBUG(PNP, ("HighestExternalNicId: %lu\n", Device->HighestExternalNicId)); + IPX_DEBUG(PNP, ("HighestType20NicId: %lu\n", Device->HighestType20NicId)); + IPX_DEBUG(PNP, ("SapNicCount: %lu\n", Device->SapNicCount)); + IPX_DEBUG(PNP, ("BindingArray: %lx\n", Device->Bindings)); +} /* IpxUnbindAdapter */ + + +VOID +IpxTranslate( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + OUT PNET_PNP_ID IdList, + IN ULONG IdListLength, + OUT PULONG BytesReturned + ) +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + The packet passed up from NDIS can be held on to by the TDI clients + that request TDI_EVENT_RECEIVE_EX_DATAGRAM events with us. + +Arguments: + + ProtocolBindingContext - The Adapter Binding specified at initialization time. + + ReceivedPacket - The packet received + + MediaSpecificInformation - Used for media such as Irda, wireless, etc. Not used here. + + HeaderBufferSize - Size of the MAC header + +Return Value: + + return of IpxReceiveIndicationNew(), + +--*/ +{ +} /* IpxTranslate */ + +#endif _PNP_POWER + + diff --git a/private/ntos/tdi/isnp/ipx/internal.c b/private/ntos/tdi/isnp/ipx/internal.c new file mode 100644 index 000000000..f11790158 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/internal.c @@ -0,0 +1,1233 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + internal.c + +Abstract: + + This module contains the code to handle the internal + binding of the upper drivers to IPX. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 25-August-1995 + Bug Fixes - tagged [SA] +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +NTSTATUS +IpxInternalBind( + IN PDEVICE Device, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is used when one of the upper drivers submits + a request to bind to IPX. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + PIPX_INTERNAL_BIND_INPUT BindInput; + PIPX_INTERNAL_BIND_OUTPUT BindOutput; + PIPX_INTERNAL_BIND_RIP_OUTPUT BindRipOutput; + CTELockHandle LockHandle; + PIPX_NIC_DATA NicData; + PBINDING Binding, LastRealBinding; + PADAPTER Adapter; + ULONG Identifier; + ULONG BindOutputSize; + BOOLEAN BroadcastEnable; + UINT i; +#if DBG + PUCHAR IdStrings[] = { "NB", "SPX", "RIP" }; +#endif + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < + (sizeof(IPX_INTERNAL_BIND_INPUT) - sizeof(ULONG))) { + + IPX_DEBUG (BIND, ("Bind received, bad input length %d/%d\n", + IrpSp->Parameters.DeviceIoControl.InputBufferLength, + sizeof (IPX_INTERNAL_BIND_INPUT))); + return STATUS_INVALID_PARAMETER; + + } + + BindInput = (PIPX_INTERNAL_BIND_INPUT)(Irp->AssociatedIrp.SystemBuffer); + + if (BindInput->Identifier >= UPPER_DRIVER_COUNT) { + IPX_DEBUG (BIND, ("Bind received, bad id %d\n", BindInput->Identifier)); + return STATUS_INVALID_PARAMETER; + } + + IPX_DEBUG (BIND, ("Bind received from id %d (%s)\n", + BindInput->Identifier, + IdStrings[BindInput->Identifier])); + +#ifdef _PNP_POWER +// RIP gives us version == 1 whereas the others give us 2 (ISN_VERSION). +// [BUGBUGZZ] - have RIP change? +// + if (BindInput->Identifier == IDENTIFIER_RIP) { + if (BindInput->Version != 1) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } + } else { + if (BindInput->Version != ISN_VERSION) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } + } + +#else + if (BindInput->Version != 1) { + IPX_DEBUG (BIND, ("Bind: bad version %d/%d\n", + BindInput->Version, 1)); + return STATUS_INVALID_PARAMETER; + } +#endif + + if (BindInput->Identifier != IDENTIFIER_RIP) { + BindOutputSize = sizeof(IPX_INTERNAL_BIND_OUTPUT); + } else { + BindOutputSize = FIELD_OFFSET (IPX_INTERNAL_BIND_RIP_OUTPUT, NicInfoBuffer.NicData[0]) + + (MIN (Device->MaxBindings, Device->HighestExternalNicId) * sizeof(IPX_NIC_DATA)); + } + + Irp->IoStatus.Information = BindOutputSize; + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + BindOutputSize) { + + IPX_DEBUG (BIND, ("Bind: bad output length %d/%d\n", + IrpSp->Parameters.DeviceIoControl.OutputBufferLength, + BindOutputSize)); + + // + // Fail this request with BUFFER_TOO_SMALL. Since the + // I/O system may not copy the status block back to + // the user's status block, do that here so that + // he gets IoStatus.Information. + // + + try { + *Irp->UserIosb = Irp->IoStatus; + } except(EXCEPTION_EXECUTE_HANDLER) { + NOTHING; + } + + return STATUS_BUFFER_TOO_SMALL; + } + + // + // We have verified the length, make sure we are not + // already bound. + // + + Identifier = BindInput->Identifier; + + CTEGetLock (&Device->Lock, &LockHandle); + + if (Device->UpperDriverBound[Identifier]) { + IPX_DEBUG (BIND, ("Bind: already bound\n")); + CTEFreeLock (&Device->Lock, LockHandle); + return STATUS_REQUEST_NOT_ACCEPTED; + } + + { + LARGE_INTEGER ControlChId; + + CCID_FROM_REQUEST(ControlChId, Irp); + + IPX_DEBUG (BIND, ("Control ChId: (%d, %d) for Id: %d\n", ControlChId.HighPart, ControlChId.LowPart, Identifier)); + Device->UpperDriverControlChannel[Identifier].QuadPart = ControlChId.QuadPart; + } + + RtlCopyMemory( + &Device->UpperDrivers[Identifier], + BindInput, + sizeof (IPX_INTERNAL_BIND_INPUT) + ); + + BroadcastEnable = BindInput->BroadcastEnable; + + // + // Now construct the output buffer. + // + + if (Identifier != IDENTIFIER_RIP) { + + BindOutput = (PIPX_INTERNAL_BIND_OUTPUT)Irp->AssociatedIrp.SystemBuffer; + + BindOutput->Version = 1; + + // + // Tell netbios our first binding's net/node instead of the + // virtual one. + // +#ifdef _PNP_POWER +// +// Fill the fields in only if the adapters have already appeared +// Else, set NodeNumber to 0 so NB/SPX know of it. +// + if ((*(UNALIGNED USHORT *)(Device->SourceAddress.NodeAddress+4) != 0) || + (*(UNALIGNED ULONG *)Device->SourceAddress.NodeAddress != 0)) { + + IPX_DEBUG(BIND, ("Device already opened\n")); + CTEAssert(Device->ValidBindings); + + if (Identifier == IDENTIFIER_SPX) { + + // + // For SPX, inform directly. + // + IPX_FREE_LOCK(&Device->Lock, LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (!NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = TRUE; + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + IpxPnPIsnIndicate((PVOID)Identifier); + + } else { + CTEAssert(FALSE); + + IPX_FREE_LOCK(&Device->Lock, LockHandle); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + + IPX_GET_LOCK(&Device->Lock, &LockHandle); + } else { + // + // For NB, queue a work item which will go thru' the adapters list and + // inform the upper drivers about each of them. + // + ExInitializeWorkItem( + &Device->PnPIndicationsQueueItem, + IpxPnPIsnIndicate, + (PVOID)Identifier); + ExQueueWorkItem(&Device->PnPIndicationsQueueItem, DelayedWorkQueue); + } + + } else { + IPX_DEBUG(BIND, ("Device not open\n")); + *((UNALIGNED ULONG *)BindOutput->Node) = 0; + *((UNALIGNED USHORT *)(BindOutput->Node+4)) = 0; + RtlZeroMemory(&BindOutput->LineInfo, sizeof(BindOutput->LineInfo)); + } + + BindOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + BindOutput->IncludedHeaderOffset = MAC_HEADER_SIZE; // (USHORT)Device->IncludedHeaderOffset; + + BindOutput->SendHandler = IpxSendFramePreFwd; + BindOutput->FindRouteHandler = IpxInternalFindRoute; + BindOutput->QueryHandler = IpxInternalQuery; + +#else + if ((Identifier == IDENTIFIER_NB) && + (Device->VirtualNetwork)) { + RtlCopyMemory(BindOutput->Node, Device->Bindings[1]->LocalAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)(BindOutput->Network) = Device->Bindings[1]->LocalAddress.NetworkAddress; + } else { + + RtlCopyMemory(BindOutput->Node, Device->SourceAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)(BindOutput->Network) = Device->SourceAddress.NetworkAddress; + } + + BindOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + + BindOutput->IncludedHeaderOffset = (USHORT)Device->IncludedHeaderOffset; + + BindOutput->LineInfo.LinkSpeed = Device->LinkSpeed; + BindOutput->LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + BindOutput->LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + BindOutput->LineInfo.MacOptions = Device->MacOptions; + + BindOutput->SendHandler = IpxSendFrame; + BindOutput->FindRouteHandler = IpxInternalFindRoute; + BindOutput->QueryHandler = IpxInternalQuery; +#endif + BindOutput->TransferDataHandler = IpxTransferData; + } else { + // + // Set this so we stop RIPping for our virtual network (if + // we have one). + // + + Device->RipResponder = FALSE; + + // + // See if he wants a single wan network number. + // + + if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(IPX_INTERNAL_BIND_INPUT)) || + ((BindInput->RipParameters & IPX_RIP_PARAM_GLOBAL_NETWORK) == 0)) { + + Device->WanGlobalNetworkNumber = FALSE; + Device->SapNicCount = Device->HighestExternalNicId; + + } else { + + Device->WanGlobalNetworkNumber = TRUE; + + } + + BindRipOutput = (PIPX_INTERNAL_BIND_RIP_OUTPUT)Irp->AssociatedIrp.SystemBuffer; + + BindRipOutput->Version = 1; + BindRipOutput->MaximumNicCount = MIN (Device->MaxBindings, Device->HighestExternalNicId) + 1; + + BindRipOutput->MacHeaderNeeded = MAC_HEADER_SIZE; //40; + BindRipOutput->IncludedHeaderOffset = (USHORT)Device->IncludedHeaderOffset; + + BindRipOutput->SendHandler = IpxSendFrame; + + BindRipOutput->SegmentCount = Device->SegmentCount; + BindRipOutput->SegmentLocks = Device->SegmentLocks; + + BindRipOutput->GetSegmentHandler = RipGetSegment; + BindRipOutput->GetRouteHandler = RipGetRoute; + BindRipOutput->AddRouteHandler = RipAddRoute; + BindRipOutput->DeleteRouteHandler = RipDeleteRoute; + BindRipOutput->GetFirstRouteHandler = RipGetFirstRoute; + BindRipOutput->GetNextRouteHandler = RipGetNextRoute; + + BindRipOutput->IncrementWanInactivityHandler = IpxInternalIncrementWanInactivity; + BindRipOutput->QueryWanInactivityHandler = IpxInternalQueryWanInactivity; + + BindRipOutput->TransferDataHandler = IpxTransferData; + + BindRipOutput->NicInfoBuffer.NicCount = (USHORT)MIN (Device->MaxBindings, Device->HighestExternalNicId); + BindRipOutput->NicInfoBuffer.VirtualNicId = 0; + if (Device->VirtualNetwork || Device->MultiCardZeroVirtual) { + *(UNALIGNED ULONG *)(BindRipOutput->NicInfoBuffer.VirtualNetwork) = Device->SourceAddress.NetworkAddress; + } else if (Device->DedicatedRouter) { + *(UNALIGNED ULONG *)(BindRipOutput->NicInfoBuffer.VirtualNetwork) = 0x0; + } + + NicData = &BindRipOutput->NicInfoBuffer.NicData[0]; + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + + // + // NULL bindings are WAN bindings, so we return the + // information from the last non-NULL binding found, + // which will be the first one on this adapter. + // Otherwise we save this as the last non-NULL one. + // + + if (Binding == NULL) { + Binding = LastRealBinding; + } else { + LastRealBinding = Binding; + } + + Adapter = Binding->Adapter; + NicData->NicId = i; + RtlCopyMemory (NicData->Node, Binding->LocalAddress.NodeAddress, 6); + *(UNALIGNED ULONG *)NicData->Network = Binding->LocalAddress.NetworkAddress; + NicData->LineInfo.LinkSpeed = Binding->MediumSpeed; + NicData->LineInfo.MaximumPacketSize = + Binding->MaxLookaheadData + sizeof(IPX_HEADER); + NicData->LineInfo.MaximumSendSize = + Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER); + NicData->LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + NicData->DeviceType = Adapter->MacInfo.RealMediumType; + NicData->EnableWanRouter = Adapter->EnableWanRouter; + + ++NicData; + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + } + if (BroadcastEnable) { + IpxAddBroadcast (Device); + } + + Device->UpperDriverBound[Identifier] = TRUE; + Device->AnyUpperDriverBound = TRUE; + CTEFreeLock (&Device->Lock, LockHandle); + + return STATUS_SUCCESS; + +} /* IpxInternalBind */ + + +NTSTATUS +IpxInternalUnbind( + IN PDEVICE Device, + IN UINT Identifier + ) + +/*++ + +Routine Description: + + This routine is used when one of the upper drivers submits + a request to unbind from IPX. It does this by closing the + control channel on which the bind ioctl was submitted. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; +#if DBG + PUCHAR IdStrings[] = { "NB", "SPX", "RIP" }; +#endif + + IPX_DEBUG (BIND, ("Unbind received from id %d (%s)\n", + Identifier, + IdStrings[Identifier])); + + CTEGetLock (&Device->Lock, &LockHandle); + + if (!Device->UpperDriverBound[Identifier]) { + CTEFreeLock (&Device->Lock, LockHandle); + IPX_DEBUG (BIND, ("No existing binding\n")); + return STATUS_SUCCESS; + } + + Device->UpperDriverBound[Identifier] = FALSE; + Device->AnyUpperDriverBound = (BOOLEAN) + (Device->UpperDriverBound[IDENTIFIER_RIP] || + Device->UpperDriverBound[IDENTIFIER_SPX] || + Device->UpperDriverBound[IDENTIFIER_NB]); + + if (Device->UpperDrivers[Identifier].BroadcastEnable) { + IpxRemoveBroadcast (Device); + } + +#ifdef _PNP_POWER + if (Device->ValidBindings > 0) { + // + // If SPX went away, reset the IsnIndicate flag in the first binding + // + if (Identifier == IDENTIFIER_SPX) { + CTEAssert(NIC_ID_TO_BINDING(Device, 1)); + + if (NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = FALSE; + IPX_DEBUG(PNP, ("SPX unbound: IsnInformed turned off\n")); + } + } + + // + // If NB went away, reset all the Binding's flags + // + if (Identifier == IDENTIFIER_SPX) { + PBINDING Binding; + UINT i; + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i < Index; i++) { + Binding = NIC_ID_TO_BINDING(Device, i); + if (Binding && Binding->IsnInformed[Identifier]) { + Binding->IsnInformed[Identifier] = FALSE; + IPX_DEBUG(PNP, ("NB unbound: IsnInformed off for NicId: %lx\n", i)); + } + } + } + } +#endif + + CTEFreeLock (&Device->Lock, LockHandle); + + // + // BUGBUG: Ensure that no calls are made to bogus + // handlers. + // + + return STATUS_SUCCESS; + +} /* IpxInternalUnbind */ + + +VOID +IpxInternalFindRoute ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest + ) + +/*++ + +Routine Description: + + This routine is the entry point for upper drivers to submit + requests to find a remote network, which is contained in + FindRouteRequest->Network. FindRouteRequest->Identifier must + contain the identifier of the upper driver. + + This request is always asynchronous and is completed by + a call to the FindRouteComplete handler of the upper driver. + + NOTE: As a currently unspecified extension to this call, + we returns the tick and hop counts as two USHORTs in the + PVOID Reserved2 structure of the request. + +Arguments: + + FindRouteRequest - Describes the request and contains + storage for IPX to use while processing it. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + ULONG Segment; + TDI_ADDRESS_IPX TempAddress; + PBINDING Binding, MasterBinding; + NTSTATUS Status; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // First see if we have a route to this network in our + // table. + // + + TempAddress.NetworkAddress = *(UNALIGNED ULONG *)(FindRouteRequest->Network); + // + // [SA] Bug #15094 Copy over the Node address so it can be used in WAN cases + // + + // RtlZeroMemory (TempAddress.NodeAddress, 6); + + *((UNALIGNED ULONG *)TempAddress.NodeAddress) = *((UNALIGNED ULONG *)FindRouteRequest->Node); + *((UNALIGNED USHORT *)(TempAddress.NodeAddress+4)) = *((UNALIGNED USHORT *)(FindRouteRequest->Node+4)); + + Segment = RipGetSegment(FindRouteRequest->Network); +#ifdef _PNP_POWER + // + // Since we maintain the order of locks as Bind > Device > RIP table + // Get the lock up-front. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + IPX_BEGIN_SYNC (&SyncContext); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // This call will return STATUS_PENDING if we need to + // RIP for the packet. + // + + CTEAssert ((sizeof(USHORT)*2) <= sizeof(PVOID)); + + Status = RipGetLocalTarget( + Segment, + &TempAddress, + FindRouteRequest->Type, + &FindRouteRequest->LocalTarget, + (PUSHORT)&FindRouteRequest->Reserved2); + + if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this find route request for completion when the + // RIP response arrives. + // + + CTEAssert (FindRouteRequest->Type != IPX_FIND_ROUTE_NO_RIP); // should never pend + + InsertTailList( + &Device->Segments[Segment].FindWaitingForRoute, + &FindRouteRequest->Linkage); + + } + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IPX_END_SYNC (&SyncContext); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + if (Status != STATUS_PENDING) { + + if (Status == STATUS_SUCCESS && FindRouteRequest->LocalTarget.NicId) { +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_HANDLE_TO_BINDING(Device, &FindRouteRequest->LocalTarget.NicHandle); + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FILL_LOCAL_TARGET(&FindRouteRequest->LocalTarget, Binding->NicId); + + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[FindRouteRequest->LocalTarget.NicId]; + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + + FindRouteRequest->LocalTarget.NicId = Binding->NicId; + + } +#endif + } + + (*Device->UpperDrivers[FindRouteRequest->Identifier].FindRouteCompleteHandler)( + FindRouteRequest, + (BOOLEAN)((Status == STATUS_SUCCESS) ? TRUE : FALSE)); + + } + +} /* IpxInternalFindRoute */ + + +NTSTATUS +IpxInternalQuery( + IN ULONG InternalQueryType, +#ifdef _PNP_POWER + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +) + +/*++ + +Routine Description: + + This routine is the entry point for upper drivers to query + information from us. + +Arguments: + + InternalQueryType - Identifies the type of the query. + + NicId - The ID to query, if needed + + Buffer - Input or output buffer for the query. + + BufferLength - The length of the buffer. + + BufferLengthNeeded - If the buffer is too short, this returns + the length needed. + +Return Value: + + None. + +--*/ + +{ + PBINDING Binding; + BOOLEAN BindingNeeded; + ULONG LengthNeeded; + PIPX_LINE_INFO LineInfo; + PUSHORT MaximumNicId; + PULONG ReceiveBufferSpace; + TDI_ADDRESS_IPX UNALIGNED * IpxAddress; + IPX_SOURCE_ROUTING_INFO UNALIGNED * SourceRoutingInfo; + ULONG SourceRoutingLength; + UINT MaxUserData; + PDEVICE Device = IpxDevice; +#ifdef _PNP_POWER + USHORT NicId = NicHandle->NicId; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // First verify the parameters. + // + + switch (InternalQueryType) { + + case IPX_QUERY_LINE_INFO: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(IPX_LINE_INFO); + break; + + case IPX_QUERY_MAXIMUM_NIC_ID: + case IPX_QUERY_MAX_TYPE_20_NIC_ID: + + BindingNeeded = FALSE; + LengthNeeded = sizeof(USHORT); + break; + + case IPX_QUERY_IS_ADDRESS_LOCAL: + + BindingNeeded = FALSE; // for now we don't need it + LengthNeeded = sizeof(TDI_ADDRESS_IPX); + break; + + case IPX_QUERY_RECEIVE_BUFFER_SPACE: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(ULONG); + break; + + case IPX_QUERY_IPX_ADDRESS: + + if ((NicId == 0) && + (BufferLength >= sizeof(TDI_ADDRESS_IPX))) { + + RtlCopyMemory (Buffer, &Device->SourceAddress, sizeof(TDI_ADDRESS_IPX)); + return STATUS_SUCCESS; + + } + + BindingNeeded = TRUE; + LengthNeeded = sizeof(TDI_ADDRESS_IPX); + break; + + case IPX_QUERY_SOURCE_ROUTING: + + BindingNeeded = TRUE; + LengthNeeded = sizeof(IPX_SOURCE_ROUTING_INFO); + break; + +#ifdef _PNP_POWER + // + // These are moved down from NB/SPX to IPX. LengthNeeded is set to 0 + // so we dont return BUFFER_TOO_SMALL here; we assume here that + // Bufferlength is also 0. + // Buffer is actually the IRP here. + // + case IPX_QUERY_DATA_LINK_ADDRESS: + case IPX_QUERY_NETWORK_ADDRESS: + + BindingNeeded = FALSE; + LengthNeeded = 0; + break; +#endif + default: + + return STATUS_NOT_SUPPORTED; + + } + + + if (LengthNeeded > BufferLength) { + if (BufferLengthNeeded != NULL) { + *BufferLengthNeeded = LengthNeeded; + } + return STATUS_BUFFER_TOO_SMALL; + } + + if (BindingNeeded) { + + if (NicId == 0) { + NicId = 1; + } + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + if ((Binding == NULL) || + (!Binding->LineUp)) { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + return STATUS_INVALID_PARAMETER; + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = IpxDevice->Bindings[NicId]; + if ((Binding == NULL) || + (!Binding->LineUp)) { + return STATUS_INVALID_PARAMETER; + } +#endif + } + + + // + // Now return the data. + // + + switch (InternalQueryType) { + + case IPX_QUERY_LINE_INFO: + + LineInfo = (PIPX_LINE_INFO)Buffer; + LineInfo->LinkSpeed = Binding->MediumSpeed; + LineInfo->MaximumPacketSize = Binding->MaxLookaheadData + sizeof(IPX_HEADER); + LineInfo->MaximumSendSize = Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER); + LineInfo->MacOptions = Binding->Adapter->MacInfo.MacOptions; + break; + + case IPX_QUERY_MAXIMUM_NIC_ID: + + MaximumNicId = (PUSHORT)Buffer; + *MaximumNicId = MIN (Device->MaxBindings, IpxDevice->HighestExternalNicId); + break; + + case IPX_QUERY_IS_ADDRESS_LOCAL: + + IpxAddress = (TDI_ADDRESS_IPX UNALIGNED *)Buffer; + if (!IpxIsAddressLocal(IpxAddress)) { + return STATUS_NO_SUCH_DEVICE; + } + break; + + case IPX_QUERY_RECEIVE_BUFFER_SPACE: + + ReceiveBufferSpace = (PULONG)Buffer; + *ReceiveBufferSpace = Binding->Adapter->ReceiveBufferSpace; + break; + + case IPX_QUERY_IPX_ADDRESS: + + RtlCopyMemory (Buffer, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + break; + + case IPX_QUERY_SOURCE_ROUTING: + + SourceRoutingInfo = (IPX_SOURCE_ROUTING_INFO UNALIGNED *)Buffer; + + MacLookupSourceRouting( + SourceRoutingInfo->Identifier, + Binding, + SourceRoutingInfo->RemoteAddress, + SourceRoutingInfo->SourceRouting, + &SourceRoutingLength); + + // + // Reverse the direction of the source routing since it + // is returned in the outgoing order. + // + + if (SourceRoutingLength > 0) { + SourceRoutingInfo->SourceRouting[0] &= 0x7f; + } + SourceRoutingInfo->SourceRoutingLength = (USHORT)SourceRoutingLength; + + MacReturnMaxDataSize( + &Binding->Adapter->MacInfo, + SourceRoutingInfo->SourceRouting, + SourceRoutingLength, + Binding->MaxSendPacketSize, + &MaxUserData); + + // + // MaxUserData does not include the MAC header but does include + // any extra 802.2 etc. headers, so we adjust for that to get the + // size starting at the IPX header. + // + + SourceRoutingInfo->MaximumSendSize = + MaxUserData - + (Binding->DefHeaderSize - Binding->Adapter->MacInfo.MinHeaderLength); + + break; + + case IPX_QUERY_MAX_TYPE_20_NIC_ID: + + MaximumNicId = (PUSHORT)Buffer; + *MaximumNicId = MIN (Device->MaxBindings, IpxDevice->HighestType20NicId); + break; + +#ifdef _PNP_POWER + case IPX_QUERY_DATA_LINK_ADDRESS: + case IPX_QUERY_NETWORK_ADDRESS: + // + // Call the TDI query equivalent here. + // + return IpxTdiQueryInformation(Device, (PREQUEST)Buffer); + +#endif + } + +#ifdef _PNP_POWER + // + // If Binding was needed earlier, it was referenced, deref it now. + // + if (BindingNeeded) { + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + } +#endif + + // + // If we haven't returned failure by now, succeed. + // + + return STATUS_SUCCESS; + +} /* IpxInternalQuery */ + + +VOID +IpxInternalIncrementWanInactivity( +#ifdef _PNP_LATER +// RIP not converted yet... +// + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +) + +/*++ + +Routine Description: + + This routine is the entry point where rip calls us to increment + the inactivity counter on a wan binding. This is done every + minute. + +Arguments: + + NicId - The NIC ID of the wan binding. + +Return Value: + + None. + +--*/ + +{ +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + // + // [BUGBUGZZ] Change to NIC_HANDLE_TO_BINDING later. Not done yet since RIP not changed to + // use NICHANDLE instead of NicId + // + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + ++Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + + } + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); +#else + PBINDING Binding = IpxDevice->Bindings[NicId]; + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + ++Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + + } +#endif + +} /* IpxInternalIncrementWanInactivity */ + + +ULONG +IpxInternalQueryWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +) + +/*++ + +Routine Description: + + This routine is the entry point where rip calls us to query + the inactivity counter on a wan binding. + +Arguments: + + NicId - The NIC ID of the wan binding. + +Return Value: + + The inactivity counter for this binding. + +--*/ + +{ +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + // Binding = NIC_HANDLE_TO_BINDING(IpxDevice, &NicHandle); + + Binding = NIC_ID_TO_BINDING(IpxDevice, NicId); + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + return Binding->WanInactivityCounter; + + } else { + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + CTEAssert (FALSE); + return 0; + + } + +#else + PBINDING Binding = IpxDevice->Bindings[NicId]; + + if ((Binding != NULL) && + (Binding->Adapter->MacInfo.MediumAsync)) { + + return Binding->WanInactivityCounter; + + } else { + + CTEAssert (FALSE); + return 0; + + } +#endif + +} /* IpxInternalQueryWanInactivity */ + +#ifdef _PNP_POWER + +VOID +IpxPnPIsnIndicate( + IN PVOID Param +) + +/*++ + +Routine Description: + + This routine goes through the list of adapters and informs (thru' PnP indications) + the ISN drivers bound to IPX about any new adapters that have appeared before the + bind took place. + + This is queued as a work item in the InternalBind routine. + +Arguments: + + Param - the upper driver identifier. + +Return Value: + + None. + +--*/ +{ + ULONG Identifier = (ULONG)Param; + PDEVICE Device=IpxDevice; + ULONG i; + PBINDING Binding; + IPX_PNP_INFO IpxPnPInfo; + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + // + // Set up the LineInfo struct. + // + + // + // BUGBUG: Do we give Binding-specific information here? + // + IpxPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + IpxPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + IpxPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + switch(Identifier) { + case IDENTIFIER_NB: + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + // + // Inform about all the adapters + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + Binding = NIC_ID_TO_BINDING(Device, i); + + if (!Binding) { + continue; + } + + // + // We could have informed the upper driver from IpxBindAdapter + // + if (!Binding->IsnInformed[Identifier]) { + Binding->IsnInformed[Identifier] = TRUE; + + // + // Inform NB - the reserved network/node address is always that of the first + // binding + // + if (i==1) { + IpxPnPInfo.FirstORLastDevice = TRUE; + IpxPnPInfo.NewReservedAddress = TRUE; + } else { + IpxPnPInfo.FirstORLastDevice = FALSE; + IpxPnPInfo.NewReservedAddress = FALSE; + } + + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, (USHORT)i); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[Identifier].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("IpxPnPIsnIndicate: PnP to NB add: %lx\n", Binding)); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + } + } + } + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + break; + + case IDENTIFIER_SPX: + // + // For SPX this is called directly, with the IsnInformed flag appropriately set. + // This is done so that the IsnInformed flag cannot be changed under + // us by the BindAdapter routine. + // +#if 0 + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + if (!NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier]) { + NIC_ID_TO_BINDING(Device, 1)->IsnInformed[Identifier] = TRUE; +#endif + IpxPnPInfo.FirstORLastDevice = TRUE; + // + // Inform of the reserved address only + // + if (Device->VirtualNetwork) { + IpxPnPInfo.NetworkAddress = Device->SourceAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, Device->SourceAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 0); + } else { + IpxPnPInfo.NetworkAddress = NIC_ID_TO_BINDING(Device, 1)->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxPnPInfo.NodeAddress, NIC_ID_TO_BINDING(Device, 1)->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, 1); + } + + IpxPnPInfo.NewReservedAddress = TRUE; + + // IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + (*Device->UpperDrivers[Identifier].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &IpxPnPInfo); + + IPX_DEBUG(PNP, ("IpxPnPIsnIndicate: PnP to SPX add: %lx\n", NIC_ID_TO_BINDING(Device, 1))); +#if 0 + } else { + CTEAssert(FALSE); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } +#endif + + } +} +#endif diff --git a/private/ntos/tdi/isnp/ipx/ipxprocs.h b/private/ntos/tdi/isnp/ipx/ipxprocs.h new file mode 100644 index 000000000..abfcf6ec3 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ipxprocs.h @@ -0,0 +1,1525 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ipxprocs.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + 1. Added new functions - IpxReceivePacket, IpxReceiveIndicationNew + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + CTEPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow CTEPrints that disappear when +// DBG is 0. +// + +#if DBG +#define IpxPrint0(fmt) DbgPrint(fmt) +#define IpxPrint1(fmt,v0) DbgPrint(fmt,v0) +#define IpxPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define IpxPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define IpxPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define IpxPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define IpxPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define IpxPrint0(fmt) +#define IpxPrint1(fmt,v0) +#define IpxPrint2(fmt,v0,v1) +#define IpxPrint3(fmt,v0,v1,v2) +#define IpxPrint4(fmt,v0,v1,v2,v3) +#define IpxPrint5(fmt,v0,v1,v2,v3,v4) +#define IpxPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + + +// +// Routines to log packets to a buffer. +// + +#if DBG +#define IPX_PACKET_LOG 1 +#endif + +#ifdef IPX_PACKET_LOG + +// +// The size of this is 64 bytes for easy display. +// + +typedef struct _IPX_PACKET_LOG_ENTRY { + UCHAR SendReceive; + UCHAR TimeStamp[5]; // low 5 digits of tick count. + UCHAR DestMac[6]; + UCHAR SrcMac[6]; + UCHAR Length[2]; + IPX_HEADER IpxHeader; + UCHAR Data[14]; +} IPX_PACKET_LOG_ENTRY, *PIPX_PACKET_LOG_ENTRY; + +#define IPX_PACKET_LOG_LENGTH 128 +extern ULONG IpxPacketLogDebug; +extern USHORT IpxPacketLogSocket; +EXTERNAL_LOCK(IpxPacketLogLock); +extern IPX_PACKET_LOG_ENTRY IpxPacketLog[IPX_PACKET_LOG_LENGTH]; +extern PIPX_PACKET_LOG_ENTRY IpxPacketLogLoc; +extern PIPX_PACKET_LOG_ENTRY IpxPacketLogEnd; + +// +// Bit fields in IpxPacketLogDebug +// + +#define IPX_PACKET_LOG_RCV_RIP 0x0001 // All RIP packets +#define IPX_PACKET_LOG_RCV_SPX 0x0002 // All SPX packets +#define IPX_PACKET_LOG_RCV_NB 0x0004 // All Netbios packets +#define IPX_PACKET_LOG_RCV_OTHER 0x0008 // All TDI client packets +#define IPX_PACKET_LOG_RCV_SOCKET 0x0010 // All packets to IpxPacketLogSocket +#define IPX_PACKET_LOG_RCV_ALL 0x0020 // All packets (even non-IPX) + +#define IPX_PACKET_LOG_SEND_RIP 0x0001 // All RIP packets +#define IPX_PACKET_LOG_SEND_SPX 0x0002 // All SPX packets +#define IPX_PACKET_LOG_SEND_NB 0x0004 // All Netbios packets +#define IPX_PACKET_LOG_SEND_OTHER 0x0008 // All TDI client packets +#define IPX_PACKET_LOG_SEND_SOCKET 0x0010 // All packets from IpxPacketLogSocket + +VOID +IpxLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID IpxHeader, + IN PVOID Data + ); + +#define PACKET_LOG(_Bit) (IpxPacketLogDebug & (_Bit)) + +#else // IPX_PACKET_LOG + +#define IpxLogPacket(_MacHeader,_Length,_IpxHeader,_Data) +#define PACKET_LOG(_Bit) 0 + +#endif // IPX_PACKET_LOG + +#ifdef _PNP_POWER +// +// In load-only PnP, references are not needed on adapters. This should be changed +// to actually take the reference post 4.0. +// +// BUGBUG: Revisit Post 4.0 - Keep the actual instructions around for ease of activation later. +// +#define IpxReferenceAdapter(_adapter) + // InterlockedIncrement(&(_adapter)->ReferenceCount) + +#define IpxDereferenceAdapter(_adapter) +/* + if (InterlockedDecrement(&(_adapter)->ReferenceCount) == 0) {\ + IpxCloseNdis(_adapter); \ + IpxDestroyAdapter(_adapter);\ + }\ +*/ +#endif + +// +// In load-only PnP case, we dont need the references on bindings. All such references +// have been changed to this macro. +// +#define IpxReferenceBinding1(_Binding, _Type) + +#define IpxDereferenceBinding1(_Binding, _Type) + +#if DBG + +#define IpxReferenceBinding(_Binding, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Binding)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefBinding (_Binding) + +#define IpxDereferenceBinding(_Binding, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Binding)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefBinding (_Binding) + +#define IpxReferenceDevice(_Device, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Device)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefDevice (_Device) + +#define IpxDereferenceDevice(_Device, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Device)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefDevice (_Device) + + +#define IpxReferenceAddress(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddress (_Address) + +#define IpxReferenceAddressLock(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressLock (_Address) + +#define IpxDereferenceAddress(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddress (_Address) + +#define IpxDereferenceAddressSync(_Address, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressSync (_Address) + + +#define IpxReferenceAddressFile(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFile (_AddressFile) + +#define IpxReferenceAddressFileLock(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFileLock (_AddressFile) + +#define IpxReferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &IpxGlobalInterlock); \ + IpxRefAddressFileSync (_AddressFile) + +#define IpxDereferenceAddressFile(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressFile (_AddressFile) + +#define IpxDereferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &IpxGlobalInterlock); \ + IpxDerefAddressFileSync (_AddressFile) + +#define IpxTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_NewType], \ + 1, \ + &IpxGlobalInterlock); \ + (VOID)IPX_ADD_ULONG ( \ + &(_AddressFile)->RefTypes[_OldType], \ + (ULONG)-1, \ + &IpxGlobalInterlock); + +#else // DBG + +#define IpxReferenceBinding(_Binding, _Type) \ + InterlockedIncrement(&(_Binding)->ReferenceCount) + +#define IpxDereferenceBinding(_Binding, _Type) \ + IpxDerefBinding (_Binding) + +#define IpxReferenceDevice(_Device, _Type) \ + InterlockedIncrement(&(_Device)->ReferenceCount) + +#define IpxDereferenceDevice(_Device, _Type) \ + IpxDerefDevice (_Device) + +#define IpxReferenceAddress(_Address, _Type) \ + InterlockedIncrement(&(_Address)->ReferenceCount) + +#define IpxReferenceAddressLock(_Address, _Type) \ + InterlockedIncrement(&(_Address)->ReferenceCount) + +#define IpxDereferenceAddress(_Address, _Type) \ + IpxDerefAddress (_Address) + +#define IpxDereferenceAddressSync(_Address, _Type) \ + IpxDerefAddressSync (_Address) + +#define IpxReferenceAddressFile(_AddressFile, _Type) \ + InterlockedIncrement(&(_AddressFile)->ReferenceCount) + +#define IpxReferenceAddressFileLock(_AddressFile, _Type) \ + InterlockedIncrement(&(_AddressFile)->ReferenceCount) + +#define IpxReferenceAddressFileSync(_AddressFile, _Type) \ + (VOID)IPX_ADD_ULONG( \ + &(_AddressFile)->ReferenceCount, \ + 1, \ + (_AddressFile)->AddressLock) + +#define IpxDereferenceAddressFile(_AddressFile, _Type) \ + if (InterlockedDecrement(&(_AddressFile)->ReferenceCount) == 0) { \ + IpxDestroyAddressFile (_AddressFile); \ + } + +#define IpxDereferenceAddressFileSync(_AddressFile, _Type) \ + if (InterlockedDecrement(&(_AddressFile)->ReferenceCount) == 0) { \ + IpxDestroyAddressFile (_AddressFile); \ + } + +#define IpxTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) + +#endif // DBG + + + +#if DBG + +#define IpxAllocateMemory(_BytesNeeded,_Tag,_Description) \ + IpxpAllocateTaggedMemory(_BytesNeeded,_Tag,_Description) + +#define IpxFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + IpxpFreeTaggedMemory(_Memory,_BytesAllocated,_Tag,_Description) + +#else // DBG + +#define IpxAllocateMemory(_BytesNeeded,_Tag,_Description) \ + IpxpAllocateMemory(_BytesNeeded,_Tag,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + +#define IpxFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + IpxpFreeMemory(_Memory,_BytesAllocated,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + + +#endif // DBG + + +// +// This routine compares two node addresses. +// + +#define IPX_NODE_EQUAL(_A,_B) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == *(UNALIGNED ULONG *)((PUCHAR)(_B))) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == *(UNALIGNED USHORT *)(((PUCHAR)(_B))+4))) + +// +// This routine checks if an address is the broadcast address. +// + +#define IPX_NODE_BROADCAST(_A) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == 0xffffffff) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == 0xffff)) + +// +// This routine does an ordered compare of two node addresses. It +// can handle the first address having the source-routing bit on. +// + +#define IPX_NODE_COMPARE(_A,_B,_R) \ + if ((*(_R) = (*(UNALIGNED SHORT *)(((PUCHAR)(_A))+4) - *(UNALIGNED SHORT *)(((PUCHAR)(_B))+4))) == 0) { \ + *(_R) = ((*(UNALIGNED LONG *)((PUCHAR)(_A)) & 0xffffff7f) - *(UNALIGNED LONG *)((PUCHAR)(_B))); \ + } + + + +// +// Routines in action.c +// + +NTSTATUS +IpxTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +IpxCancelAction( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +IpxAbortLineChanges( + IN PVOID ControlChannelContext + ); + + +// +// Routines in adapter.c +// + +VOID +IpxRefBinding( + IN PBINDING Binding + ); + +VOID +IpxDerefBinding( + IN PBINDING Binding + ); + +NTSTATUS +IpxCreateAdapter( + IN PDEVICE Device, + IN PUNICODE_STRING AdapterName, + IN OUT PADAPTER *AdapterPtr + ); + +VOID +IpxDestroyAdapter( + IN PADAPTER Adapter + ); + +NTSTATUS +IpxCreateBinding( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigBinding OPTIONAL, + IN ULONG NetworkNumberIndex, + IN PWCHAR AdapterName, + IN OUT PBINDING *BindingPtr + ); + +VOID +IpxDestroyBinding( + IN PBINDING Binding + ); + +#ifdef _PNP_POWER +VOID +IpxAllocateBindingPool( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +IpxPopBinding( + PDEVICE Device + ); +#endif + +// +// Routines in address.c +// + +TDI_ADDRESS_IPX UNALIGNED * +IpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ); + +BOOLEAN +IpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ); + +#if DBG + +VOID +IpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG Network, + IN UCHAR Node[6], + IN USHORT Socket + ); + +#else + +#define IpxBuildTdiAddress(_AddressBuffer,_Network,_Node,_Socket) { \ + TA_IPX_ADDRESS UNALIGNED * _IpxAddress = (TA_IPX_ADDRESS UNALIGNED *)(_AddressBuffer); \ + _IpxAddress->TAAddressCount = 1; \ + _IpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); \ + _IpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; \ + _IpxAddress->Address[0].Address[0].NetworkAddress = (_Network); \ + _IpxAddress->Address[0].Address[0].Socket = (_Socket); \ + RtlCopyMemory(_IpxAddress->Address[0].Address[0].NodeAddress, (_Node), 6); \ +} + +#endif + +NTSTATUS +IpxOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +USHORT +IpxAssignSocket( + IN PDEVICE Device + ); + +PADDRESS +IpxCreateAddress( + IN PDEVICE Device, + IN USHORT Socket + ); + +NTSTATUS +IpxVerifyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDestroyAddress( + IN PVOID Parameter + ); + +#if DBG + +VOID +IpxRefAddress( + IN PADDRESS Address + ); + +VOID +IpxRefAddressLock( + IN PADDRESS Address + ); + +#endif + +VOID +IpxDerefAddress( + IN PADDRESS Address + ); + +VOID +IpxDerefAddressSync( + IN PADDRESS Address + ); + +PADDRESS_FILE +IpxCreateAddressFile( + IN PDEVICE Device + ); + +NTSTATUS +IpxDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if DBG + +VOID +IpxRefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxRefAddressFileSync( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDerefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +IpxDerefAddressFileSync( + IN PADDRESS_FILE AddressFile + ); + +#endif + +PADDRESS +IpxLookupAddress( + IN PDEVICE Device, + IN USHORT Socket + ); + +NTSTATUS +IpxStopAddressFile( + IN PADDRESS_FILE AddressFile + ); + +NTSTATUS +IpxCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in device.c +// + +VOID +IpxRefDevice( + IN PDEVICE Device + ); + +VOID +IpxDerefDevice( + IN PDEVICE Device + ); + +NTSTATUS +IpxCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN ULONG SegmentCount, + IN OUT PDEVICE *DevicePtr + ); + +VOID +IpxDestroyDevice( + IN PDEVICE Device + ); + + +// +// Routines in driver.c +// +#ifdef _PNP_POWER +VOID +IpxPnPUpdateDevice( + IN PDEVICE Device + ); +#endif + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ); + +PVOID +IpxpAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ); + +VOID +IpxpFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ); + +#if DBG + +PVOID +IpxpAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ); + +VOID +IpxpFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ); + +#endif + +VOID +IpxWriteResourceErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +IpxWriteGeneralErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +IpxWriteOidErrorLog( + IN PDEVICE_OBJECT DeviceObject, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + +#ifdef _PNP_POWER +ULONG +IpxResolveAutoDetect( + IN PDEVICE Device, + IN ULONG ValidBindings, + IN CTELockHandle *LockHandle1, + IN PUNICODE_STRING RegistryPath + ); + +VOID +IpxResolveBindingSets( + IN PDEVICE Device, + IN ULONG ValidBindings + ); + +NTSTATUS +IpxBindToAdapter( + IN PDEVICE Device, + IN PBINDING_CONFIG ConfigAdapter, + IN PADAPTER *AdapterPtr, + IN ULONG FrameTypeIndex + ); + +NTSTATUS +IpxUnBindFromAdapter( + IN PBINDING Binding + ); + +VOID +IpxPnPUpdateBindingArray( + IN PDEVICE Device, + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ); + +VOID +IpxPnPToLoad(); + +NTSTATUS +IpxPnPReallocateBindingArray( + IN PDEVICE Device, + IN ULONG Size + ); + +#endif _PNP_POWER + +// +// Routines in event.c +// + +NTSTATUS +IpxTdiSetEventHandler( + IN PREQUEST Request + ); + + +// +// Routines in ind.c +// + +// +// [CH] Added these two functions +// +INT +IpxReceivePacket ( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET Packet + ); + +NDIS_STATUS +IpxReceiveIndicationNew( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize, + IN PMDL pMdl, + IN PINT pTdiClientCount + ); + +NDIS_STATUS +IpxReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ); + +VOID +IpxReceiveComplete( + IN NDIS_HANDLE BindingContext + ); + +NTSTATUS +IpxUpdateBindingNetwork( + IN PDEVICE Device, + IN PBINDING Binding, + IN ULONG Network + ); + + +// +// Routines in internal.c +// + +NTSTATUS +IpxInternalBind( + IN PDEVICE Device, + IN PIRP Irp + ); + +NTSTATUS +IpxInternalUnbind( + IN PDEVICE Device, + IN UINT Identifier + ); + +VOID +IpxInternalFindRoute( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest + ); + +NTSTATUS +IpxInternalQuery( + IN ULONG InternalQueryType, +#ifdef _PNP_POWER + IN PNIC_HANDLE NicHandle OPTIONAL, +#else + IN USHORT NicId OPTIONAL, +#endif + IN OUT PVOID Buffer, + IN ULONG BufferLength, + OUT PULONG BufferLengthNeeded OPTIONAL +); + +VOID +IpxInternalIncrementWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +ULONG +IpxInternalQueryWanInactivity( +#ifdef _PNP_LATER + IN NIC_HANDLE NicHandle +#else + IN USHORT NicId +#endif +); + +#ifdef _PNP_POWER +VOID +IpxPnPIsnIndicate( + IN PVOID Param +); +#endif + +// +// Routines in ndis.c +// + +NTSTATUS +IpxRegisterProtocol( + IN PNDIS_STRING NameString + ); + +VOID +IpxDeregisterProtocol( + VOID + ); + +NTSTATUS +IpxInitializeNdis( + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ); + +VOID +IpxAddBroadcast( + IN PDEVICE Device + ); + +VOID +IpxRemoveBroadcast( + IN PDEVICE Device + ); + +VOID +IpxBroadcastOperation( + IN PVOID Parameter + ); + +BOOLEAN +IpxIsAddressLocal( + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress + ); + +VOID +IpxCloseNdis( + IN PADAPTER Adapter + ); + +VOID +IpxOpenAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ); + +VOID +IpxCloseAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxResetComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxRequestComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ); + +VOID +IpxStatus( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ); + +VOID +IpxStatusComplete( + IN NDIS_HANDLE NdisBindingContext + ); + + +#ifdef _PNP_POWER +VOID +IpxBindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ); + +VOID +IpxUnbindAdapter( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_HANDLE UnbindContext + ); + +VOID +IpxTranslate( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE ProtocolBindingContext, + OUT PNET_PNP_ID IdList, + IN ULONG IdListLength, + OUT PULONG BytesReturned + ); +#endif // _PNP_POWER + +// +// Routines in mac.c +// + +VOID +MacInitializeBindingInfo( + IN struct _BINDING * Binding, + IN struct _ADAPTER * Adapter + ); + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PNDIS_INFORMATION MacInfo + ); + +VOID +MacMapFrameType( + IN NDIS_MEDIUM MacType, + IN ULONG FrameType, + OUT ULONG * MappedFrameType + ); + +VOID +MacReturnMaxDataSize( + IN PNDIS_INFORMATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ); + +#ifdef _PNP_POWER +NDIS_STATUS +IpxSendFramePreFwd( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); +#endif + +NDIS_STATUS +IpxSendFrame( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3EthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_3Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_5802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrame802_5Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddi802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddi802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameFddiSnap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameArcnet878_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +NDIS_STATUS +IpxSendFrameWanEthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + +VOID +MacUpdateSourceRouting( + IN ULONG Database, + IN PADAPTER Adapter, + IN PUCHAR MacHeader, + IN ULONG MacHeaderLength + ); + +VOID +MacLookupSourceRouting( + IN ULONG Database, + IN PBINDING Binding, + IN UCHAR NextRouter[6], + IN OUT UCHAR SourceRouting[18], + OUT PULONG SourceRoutingLength + ); + +VOID +MacSourceRoutingTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +MacSourceRoutingRemove( + IN PBINDING Binding, + IN UCHAR MacAddress[6] + ); + +VOID +MacSourceRoutingClear( + IN PBINDING Binding + ); + + +// +// Routines in packet.c +// + +NTSTATUS +IpxInitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ); + +#if BACK_FILL +NTSTATUS +IpxInitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ); +#endif + +NTSTATUS +IpxInitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ); + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ); + +NTSTATUS +IpxInitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxDeinitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ); + +#if BACK_FILL +VOID +IpxDeinitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ); +#endif + +VOID +IpxDeinitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ); + +VOID +IpxDeinitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxDeinitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ); + +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ); + +#if BACK_FILL +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ); +#endif + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ); + +VOID +IpxAllocateReceiveBufferPool( + IN PADAPTER Adapter + ); + +PSINGLE_LIST_ENTRY +IpxPopSendPacket( + IN PDEVICE Device + ); + +#if BACK_FILL +PSINGLE_LIST_ENTRY +IpxPopBackFillPacket( + IN PDEVICE Device + ); +#endif + +PSINGLE_LIST_ENTRY +IpxPopReceivePacket( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +IpxPopReceiveBuffer( + IN PADAPTER Adapter + ); + +PIPX_PADDING_BUFFER +IpxAllocatePaddingBuffer( + IN PDEVICE Device + ); + +VOID +IpxFreePaddingBuffer( + IN PDEVICE Device + ); + + + +// +// Routines in query.c +// + +NTSTATUS +IpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +IpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in receive.c +// + +VOID +IpxTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ); + + +VOID +IpxTransferData( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + +NTSTATUS +IpxTdiReceiveDatagram( + IN PREQUEST Request + ); + +VOID +IpxCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in rip.c +// + +NTSTATUS +RipGetLocalTarget( + IN ULONG Segment, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN UCHAR Type, + OUT PIPX_LOCAL_TARGET LocalTarget, + OUT USHORT Counts[2] OPTIONAL + ); + +NTSTATUS +RipQueueRequest( + IN ULONG Network, + IN USHORT Operation + ); + +VOID +RipSendResponse( + IN PBINDING Binding, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget + ); + +VOID +RipShortTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +RipLongTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +RipCleanupPacket( + IN PDEVICE Device, + IN PIPX_SEND_RESERVED RipReserved + ); + +VOID +RipProcessResponse( + IN PDEVICE Device, + IN PIPX_LOCAL_TARGET LocalTarget, + IN RIP_PACKET UNALIGNED * RipPacket + ); + +VOID +RipHandleRoutePending( + IN PDEVICE Device, + IN UCHAR Network[4], + IN CTELockHandle LockHandle, + IN BOOLEAN Success, + IN OPTIONAL PIPX_LOCAL_TARGET LocalTarget, + IN OPTIONAL USHORT HopCount, + IN OPTIONAL USHORT TickCount + ); + +NTSTATUS +RipInsertLocalNetwork( + IN ULONG Network, + IN USHORT NicId, + IN NDIS_HANDLE NdisBindingContext, + IN USHORT Count + ); + +VOID +RipAdjustForBindingChange( + IN USHORT NicId, + IN USHORT NewNicId, + IN IPX_BINDING_CHANGE_TYPE ChangeType + ); + +UINT +RipGetSegment( + IN UCHAR Network[4] + ); + +PIPX_ROUTE_ENTRY +RipGetRoute( + IN UINT Segment, + IN UCHAR Network[4] + ); + +BOOLEAN +RipAddRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ); + +BOOLEAN +RipDeleteRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ); + +PIPX_ROUTE_ENTRY +RipGetFirstRoute( + IN UINT Segment + ); + +PIPX_ROUTE_ENTRY +RipGetNextRoute( + IN UINT Segment + ); + +VOID +RipDropRemoteEntries( + VOID + ); + + +// +// Routines in send.c +// + +VOID +IpxSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +NTSTATUS +IpxTdiSendDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PREQUEST Request + ); + +#if DBG +VOID +IpxConstructHeader( + IN PUCHAR Header, + IN USHORT PacketLength, + IN UCHAR PacketType, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PTDI_ADDRESS_IPX LocalAddress + ); +#else +#define IpxConstructHeader(_Header,_PacketLength,_PacketType,_RemoteAddress,_LocalAddress) { \ + PIPX_HEADER _IpxHeader = (PIPX_HEADER)(_Header); \ + _IpxHeader->CheckSum = 0xffff; \ + _IpxHeader->PacketLength[0] = (UCHAR)((_PacketLength) / 256); \ + _IpxHeader->PacketLength[1] = (UCHAR)((_PacketLength) % 256); \ + _IpxHeader->TransportControl = 0; \ + _IpxHeader->PacketType = (_PacketType); \ + RtlCopyMemory(_IpxHeader->DestinationNetwork, (PVOID)(_RemoteAddress), 12); \ + RtlCopyMemory(_IpxHeader->SourceNetwork, (_LocalAddress), 12); \ +} +#endif + +// +// Routines in loopback.c +// + +VOID +IpxDoLoopback( + IN CTEEvent *Event, + IN PVOID Context + ); + +VOID +IpxInitLoopback(); + +VOID +IpxLoopbackEnque( + IN PNDIS_PACKET Packet, + IN PVOID Context + ); + diff --git a/private/ntos/tdi/isnp/ipx/ipxtypes.h b/private/ntos/tdi/isnp/ipx/ipxtypes.h new file mode 100644 index 000000000..0cc788a8f --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ipxtypes.h @@ -0,0 +1,1999 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ipxtypes.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports - tagged [CH] + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + +--*/ + + +// +// Definition of the protocol reserved field of a send packet. +// + +typedef struct _IPX_SEND_RESERVED { + UCHAR Identifier; // 0 for IPX packets + BOOLEAN SendInProgress; // used in an NdisSend + BOOLEAN OwnedByAddress; // packet is owned by an address + UCHAR DestinationType; // one of DEF, BCAST, MCAST + struct _IPX_PADDING_BUFFER * PaddingBuffer; // if one was allocated + PNDIS_BUFFER PreviousTail; // if padding buffer was appended +#ifdef _PNP_POWER + IPX_LOCAL_TARGET LocalTarget; + USHORT CurrentNicId; // current binding being tried for net 0 sends + ULONG PacketLength; // length that comes into IpxSendFrame initially + BOOLEAN Net0SendSucceeded; // at least one NdisSend succeeded for net 0 sends +#endif + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY GlobalLinkage; // all packets are on this + LIST_ENTRY WaitLinkage; // when on WaitingForRoute/WaitingRipPackets +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + struct _ADDRESS * Address; // that owns this packet, if ones does + + // + // The next fields are used differently depending on whether + // the packet is being used for a datagram send or a rip request. + // + + union { + struct { + PREQUEST Request; // send datagram request + struct _ADDRESS_FILE * AddressFile; // that this send is on + USHORT CurrentNicId; // current binding being tried for net 0 sends + BOOLEAN Net0SendSucceeded; // at least one NdisSend succeeded for net 0 sends + BOOLEAN OutgoingSap; // packet is sent from the SAP socket + } SR_DG; + struct { + ULONG Network; // net we are looking for + USHORT CurrentNicId; // current binding being tried + UCHAR RetryCount; // number of times sent; 0xfe = response, 0xff = down + BOOLEAN RouteFound; // network has been found + USHORT SendTime; // timer expirations when sent. + BOOLEAN NoIdAdvance; // don't advance CurrentNicId this time. + } SR_RIP; + } u; + + PUCHAR Header; // points to the MAC/IPX header + PNDIS_BUFFER HeaderBuffer; // the NDIS_BUFFER describing Header; +#if BACK_FILL + BOOLEAN BackFill; // 1 if we are using SMB's extended header + PNDIS_BUFFER IpxHeader; // Place holder for our IpxHeader + PNDIS_BUFFER MacHeader; // Place holder for our mac header + PVOID MappedSystemVa; + PVOID ByteOffset; + LONG UserLength; +#endif +} IPX_SEND_RESERVED, *PIPX_SEND_RESERVED; + +// +// Values for the DestinationType field. +// + +#define DESTINATION_DEF 1 +#define DESTINATION_BCAST 2 +#define DESTINATION_MCAST 3 + +// +// Used to indicate to IpxReceiveIndication that this is a loopback packet +// Assumption: Ndis cannot return this as the NdisBindingHandle value since +// that is a pointer (our pointers shd in kernel space, if not in Nonpaged pool). +// +#define IPX_LOOPBACK_COOKIE 0x00460007 + +// +// MIN/MAX macros +// +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#ifdef _PNP_POWER + +// +// In order to avoid a lock to read a value, this is used. +// As long as the final value has made it to _b by the time +// the check is made, this works fine. +// + +#define ASSIGN_LOOP(_a, _b) \ + do { \ + _a = _b; \ + } while ( _a != _b ); + +// +// Gets the value of a Ulong (possibly a pointer) by adding 0 in an interlocked manner. +// This relies on the fact that the return of the ExchangeAdd will be the value prior to +// addition. Since the value added is 0, the final value stays the same. +// +#define GET_VALUE(x) \ + InterlockedExchangeAdd((PULONG)&(x), 0) + +#define SET_VALUE(x,y) \ + InterlockedExchange((PLONG)&(x), (LONG)(y)) + +/* +PBINDING +NIC_ID_TO_BINDING ( + IN PDEVICE _device, + IN USHORT _nicid + ); +*/ +// +// We need to ensure that the binding array pointer is valid hence use the interlocked operation. +// Also, the binding pointer read out of the array should be valid. Since the bindings are never +// freed (IPX maintains a pool of bindings), the pointer thus retrieved will always point to +// memory that belongs to us, which in the worst case could point to a re-claimed binding block. +// +// BUGBUGZZ: we can eliminate the second interlock if we always ensure that the bindings in an array +// dont change i.e. when we move around bindings, do them in a copy and make that the master (thru' +// a single ulong exchange). +// +// A problem that still remains here is that even if we get a valid (IPX owned non-paged) ptr out of +// the array, we still cannot atomically get a ref on the binding +// We might need those locks after all.... (revisit post SUR when the delete is enabled). +// +#define NIC_ID_TO_BINDING(_device, _nicid) \ + ((PBINDING)GET_VALUE( ((PBIND_ARRAY_ELEM) GET_VALUE( (_device)->Bindings) )[_nicid].Binding )) + +/* +PBINDING +NIC_ID_TO_BINDING_NO_ILOCK ( + IN PDEVICE _device, + IN USHORT _nicid + ); +*/ +// +// No interlocked operations are used here to get to the binding. This is used in the PnP add/delete +// adapter paths on the assumption that NDIS will serialize the addition/deletion of cards. [JammelH: 5/15/96] +// +#define NIC_ID_TO_BINDING_NO_ILOCK(_device, _nicid) \ + ((_device)->Bindings[_nicid].Binding) + +/* +VOID +INSERT_BINDING( + IN PDEVICE _device, + IN USHORT _nicid, + IN PBINDING _binding + ) +*/ +// +// We dont do a get_value for the first arg of the macro since we are the writer and +// this value cannot change from under us here (NDIS will not give us two PnP Add adapter +// indications simultaneously). +// +#define INSERT_BINDING(_device, _nicid, _binding) \ + SET_VALUE((_device)->Bindings[_nicid].Binding, (_binding)); + +/* +VOID +SET_VERSION( + IN PDEVICE _device, + IN USHORT _nicid + ) +*/ +#define SET_VERSION(_device, _nicid) \ + SET_VALUE((_device)->Bindings[_nicid].Version, ++(_device)->BindingVersionNumber); + +/* +PBINDING +NIC_HANDLE_TO_BINDING ( + IN PDEVICE _device, + IN PNIC_HANDLE _nichandle, + ); +*/ +#ifdef _PNP_LATER +#define NIC_HANDLE_TO_BINDING(_device, _nichandle) \ + (((_nichandle)->Signature == IPX_BINDING_SIGNATURE) && \ + ((_nichandle)->Version == (_device)->Bindings[(_nichandle)->NicId].Version)) ? \ + (_device)->Bindings[(_nichandle)->NicId].Binding : NULL; +#else + +#define NIC_HANDLE_TO_BINDING(_device, _nichandle) \ + NIC_ID_TO_BINDING(_device, (_nichandle)->NicId); +#endif + +/* +VOID +FILL_LOCAL_TARGET( + IN PLOCAL_TARGET _localtarget, + IN USHORT _nicid + ) +*/ + +#define FILL_LOCAL_TARGET(_localtarget, _nicid) \ + NIC_HANDLE_FROM_NIC((_localtarget)->NicHandle, _nicid) + +#ifdef _PNP_LATER +#define NIC_HANDLE_FROM_NIC(_nichandle, _nic) \ + _nichandle.NicId = _nic; \ + _nichandle.Signature = IPX_BINDING_SIGNATURE; \ + if (_nic == 0) { \ + _nichandle.Version = 0; \ + } else { \ + _nichandle.Version = IpxDevice->Bindings[_nic].Version; \ + } + +#else + +#define NIC_HANDLE_FROM_NIC(_nichandle, _nic) \ + _nichandle.NicId = _nic; + +#endif + +#define NIC_FROM_LOCAL_TARGET(_localtarget) \ + (_localtarget)->NicHandle.NicId + +#endif _PNP_POWER + +// +// Definition of the protocol reserved field of a receive packet. +// + +typedef struct _IPX_RECEIVE_RESERVED { + UCHAR Identifier; // 0 for IPX packets + BOOLEAN TransferInProgress; // used in an NdisTransferData + BOOLEAN OwnedByAddress; // packet is owned by an address +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + struct _ADDRESS * Address; // that owns this packet, if ones does + PREQUEST SingleRequest; // if transfer is for one only + struct _IPX_RECEIVE_BUFFER * ReceiveBuffer; // if transfer is for multiple requests + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY GlobalLinkage; // all packets are on this + LIST_ENTRY Requests; // waiting on this transfer +} IPX_RECEIVE_RESERVED, *PIPX_RECEIVE_RESERVED; + +// +// The amount of data we need in our standard header, rounded up +// to the next longword bounday. +// +// [BUGBUGZZ] Make this declaration in one place +// +#define PACKET_HEADER_SIZE (MAC_HEADER_SIZE + IPX_HEADER_SIZE + RIP_PACKET_SIZE) + +// +// Types to abstract NDIS packets. This is to allow us to +// switch from using our own memory for packets to using +// authentically allocated NDIS packets. +// + +// #define IPX_OWN_PACKETS 1 + +#define IpxAllocateSendPacket(_Device,_SendPacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)(PACKET(_SendPacket))); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define IpxAllocateReceivePacket(_Device,_ReceivePacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)(PACKET(_ReceivePacket))); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#ifdef IPX_OWN_PACKETS + +#define NDIS_PACKET_SIZE 48 +// #define NDIS_PACKET_SIZE FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + +typedef struct _IPX_SEND_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(IPX_SEND_RESERVED)]; +} IPX_SEND_PACKET, *PIPX_SEND_PACKET; + +typedef struct _IPX_RECEIVE_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(IPX_RECEIVE_RESERVED)]; +} IPX_RECEIVE_PACKET, *PIPX_RECEIVE_PACKET; + +#define PACKET(_Packet) ((PNDIS_PACKET)((_Packet)->Data)) + +#define IpxFreeSendPacket(_Device,_Packet) + +#define IpxFreeReceivePacket(_Device,_Packet) + +#else // IPX_OWN_PACKETS + +typedef struct _IPX_SEND_PACKET { + PNDIS_PACKET Packet; + NDIS_HANDLE PoolHandle; +} IPX_SEND_PACKET, *PIPX_SEND_PACKET; + +typedef struct _IPX_RECEIVE_PACKET { + PNDIS_PACKET Packet; + NDIS_HANDLE PoolHandle; +} IPX_RECEIVE_PACKET, *PIPX_RECEIVE_PACKET; + +#define PACKET(_Packet) ((_Packet)->Packet) + +#define IpxAllocateSingleSendPacket(_Device,_SendPacket,_Status) { \ + NdisAllocatePacketPool(_Status, &(_SendPacket)->PoolHandle,1,sizeof(IPX_SEND_RESERVED)); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_SendPacket)->Packet, (_SendPacket)->PoolHandle); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + (_Device)->MemoryUsage += \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_SEND_RESERVED)); \ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis packet memory\n"));\ + }\ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n"));\ + }\ +} + +#define IpxAllocateSingleReceivePacket(_Device,_ReceivePacket,_Status) { \ + NdisAllocatePacketPool(_Status, &(_ReceivePacket)->PoolHandle,1,sizeof(IPX_RECEIVE_RESERVED)); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_ReceivePacket)->Packet, (_ReceivePacket)->PoolHandle); \ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + (_Device)->MemoryUsage += \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_RECEIVE_RESERVED)); \ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis packet memory\n"));\ + }\ + } else {\ + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n"));\ + }\ +} + +#define IpxFreeSingleSendPacket(_Device,_Packet) { \ + NdisFreePacket((_Packet).Packet); \ + NdisFreePacketPool((_Packet).PoolHandle); \ + (_Device)->MemoryUsage -= \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_SEND_RESERVED)); \ +} +#define IpxFreeSingleReceivePacket(_Device,_Packet) { \ + NdisFreePacket((_Packet).Packet); \ + NdisFreePacketPool((_Packet).PoolHandle); \ + (_Device)->MemoryUsage -= \ + (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0])+ \ + FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0])+ \ + sizeof(IPX_RECEIVE_RESERVED)); \ +} + +#define IpxFreeSendPacket(_Device,_Packet) NdisFreePacket(PACKET(_Packet)) + +#define IpxFreeReceivePacket(_Device,_Packet) NdisFreePacket(PACKET(_Packet)) + +#endif // IPX_OWN_PACKETS + +#define SEND_RESERVED(_Packet) ((PIPX_SEND_RESERVED)((PACKET(_Packet))->ProtocolReserved)) +#define RECEIVE_RESERVED(_Packet) ((PIPX_RECEIVE_RESERVED)((PACKET(_Packet))->ProtocolReserved)) + + +// +// This is the structure that contains a receive buffer for +// datagrams that are going to multiple recipients. +// + +typedef struct _IPX_RECEIVE_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this +#ifdef IPX_TRACK_POOL + PVOID Pool; // receive buffer pool was allocated from +#endif + SINGLE_LIST_ENTRY PoolLinkage; // when on free list + PNDIS_BUFFER NdisBuffer; // length of the NDIS buffer + ULONG DataLength; // length of the data + PUCHAR Data; // the actual data +} IPX_RECEIVE_BUFFER, *PIPX_RECEIVE_BUFFER; + + +// +// This is the structure that contains a padding buffer for +// padding ethernet frames out to an even number of bytes. +// + +typedef struct _IPX_PADDING_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free list + PNDIS_BUFFER NdisBuffer; // length of the NDIS buffer + ULONG DataLength; // length of the data + UCHAR Data[1]; // the actual pad data +} IPX_PADDING_BUFFER, *PIPX_PADDING_BUFFER; + +#ifdef IPX_OWN_PACKETS + +typedef struct _IPX_SEND_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + IPX_SEND_PACKET Packets[1]; +} IPX_SEND_POOL, *PIPX_SEND_POOL; + +typedef struct _IPX_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + IPX_RECEIVE_PACKET Packets[1]; +} IPX_RECEIVE_POOL, *PIPX_RECEIVE_POOL; +#else + +typedef struct _IPX_PACKET_POOL { + LIST_ENTRY Linkage; + PUCHAR Header; + NDIS_HANDLE PoolHandle; +} IPX_PACKET_POOL, *PIPX_PACKET_POOL; + +typedef IPX_PACKET_POOL IPX_RECEIVE_POOL, *PIPX_RECEIVE_POOL; +typedef IPX_PACKET_POOL IPX_SEND_POOL, *PIPX_SEND_POOL; + +#endif // IPX_OWN_PACKETS + +typedef struct _IPX_RECEIVE_BUFFER_POOL { + LIST_ENTRY Linkage; + UINT BufferCount; + UINT BufferFree; + IPX_RECEIVE_BUFFER Buffers[1]; + // after the packets the data buffers are allocated also. +} IPX_RECEIVE_BUFFER_POOL, *PIPX_RECEIVE_BUFFER_POOL; + +// +// Number of upper drivers we support. +// + +#define UPPER_DRIVER_COUNT 3 + + + +// +// Tags for memory allocation. +// + +#define MEMORY_CONFIG 0 +#define MEMORY_ADAPTER 1 +#define MEMORY_ADDRESS 2 +#define MEMORY_PACKET 3 +#define MEMORY_RIP 4 +#define MEMORY_SOURCE_ROUTE 5 +#define MEMORY_BINDING 6 +#define MEMORY_QUERY 7 + +#define MEMORY_MAX 8 + +#if DBG + +// +// Holds the allocations for a specific memory type. +// + +typedef struct _MEMORY_TAG { + ULONG Tag; + ULONG BytesAllocated; +} MEMORY_TAG, *PMEMORY_TAG; + +EXTERNAL_LOCK(IpxMemoryInterlock); +extern MEMORY_TAG IpxMemoryTag[MEMORY_MAX]; + +#endif + + +// +// This defines the reasons we delete rip entries for a binding. +// + +typedef enum _IPX_BINDING_CHANGE_TYPE { + IpxBindingDeleted, + IpxBindingMoved, + IpxBindingDown +} IPX_BINDING_CHANGE_TYPE, *PIPX_BINDING_CHANGE_TYPE; + + +// +// This structure contains information about a single +// source routing entry. +// + +typedef struct _SOURCE_ROUTE { + + struct _SOURCE_ROUTE * Next; // next in hash list + + UCHAR MacAddress[6]; // remote MAC address + UCHAR TimeSinceUsed; // timer expirations since last used + UCHAR SourceRoutingLength; // length of the data + + UCHAR SourceRouting[1]; // source routing data, stored as received in + +} SOURCE_ROUTE, *PSOURCE_ROUTE; + +#define SOURCE_ROUTE_SIZE(_SourceRoutingLength) \ + (FIELD_OFFSET(SOURCE_ROUTE, SourceRouting[0]) + (_SourceRoutingLength)) + +#define SOURCE_ROUTE_HASH_SIZE 16 + +// +// ULONG +// MacSourceRoutingHash( +// IN PUCHAR MacAddress +// ) +// +// /*++ +// +// Routine Description: +// +// This routine returns a hash value based on the MAC address +// that is pointed to. It will be between 0 and SOURCE_ROUTE_HASH_SIZE. +// +// Arguments: +// +// MacAddress - The MAC address. NOTE: The source-routing bit may +// or may not be on in the first byte, this routine will handle +// that. +// +// Return Value: +// +// The hash value. +// +// --*/ +// + +#define MacSourceRoutingHash(_MacAddress) \ + ((ULONG)((_MacAddress)[5] % SOURCE_ROUTE_HASH_SIZE)) + + + +// +// this structure describes a single NDIS adapter that IPX is +// bound to. +// + +struct _DEVICE; + +typedef struct _ADAPTER { + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IAD1" +#endif + +#ifdef _PNP_POWER + ULONG ReferenceCount; +#endif + + ULONG BindingCount; // number bound to this adapter + + // + // Handle returned by the NDIS wrapper after we bind to it. + // + + NDIS_HANDLE NdisBindingHandle; + + // + // The queue of (currently receive only) requests waiting to complete. + // + + LIST_ENTRY RequestCompletionQueue; + + // + // IPX header normal offsets for directed and + // broadcast/multicast frames. + // + + ULONG DefHeaderSizes[ISN_FRAME_TYPE_MAX]; + ULONG BcMcHeaderSizes[ISN_FRAME_TYPE_MAX]; + + // + // List of buffers to be used for transfers. + // + + ULONG AllocatedReceiveBuffers; + LIST_ENTRY ReceiveBufferPoolList; + SLIST_HEADER ReceiveBufferList; + + // + // List of ethernet padding buffers. + // + + ULONG AllocatedPaddingBuffers; + SINGLE_LIST_ENTRY PaddingBufferList; + + struct _BINDING * Bindings[ISN_FRAME_TYPE_MAX]; // the binding for each frame type. + + // + // TRUE if broadcast reception is enabled on this adapter. + // + + BOOLEAN BroadcastEnabled; + + // + // TRUE if we have enabled an auto-detected frame type + // on this adapter -- used to prevent multiple ones. + // + + BOOLEAN AutoDetectFound; + + // + // TRUE if we got a response to at least one of our + // auto-detect frames. + // + + BOOLEAN AutoDetectResponse; + + // + // This is TRUE if we are auto-detecting and we have + // found the default auto-detect type on the net. + // + + BOOLEAN DefaultAutoDetected; + + // + // For WAN adapters, we support multiple bindings per + // adapter, all with the same frame type. For them we + // demultiplex using the local mac address. This stores + // the range of device NIC IDs associated with this + // particular address. + // + + USHORT FirstWanNicId; + USHORT LastWanNicId; + ULONG WanNicIdCount; + + // + // This is based on the configuration. + // + + USHORT BindSap; // usually 0x8137 + USHORT BindSapNetworkOrder; // usually 0x3781 + BOOLEAN SourceRouting; + BOOLEAN EnableFunctionalAddress; + BOOLEAN EnableWanRouter; + ULONG ConfigMaxPacketSize; + + // + // TRUE if the tree is empty, so we can check quickly. + // + + BOOLEAN SourceRoutingEmpty[IDENTIFIER_TOTAL]; + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR AdapterName; + ULONG AdapterNameLength; + + struct _DEVICE * Device; + + CTELock Lock; + CTELock * DeviceLock; + + // + // some MAC addresses we use in the transport + // + + HARDWARE_ADDRESS LocalMacAddress; // our local hardware address. + + // + // The value of Device->SourceRoutingTime the last time + // we checked the list for timeouts (this is so we can + // tell in the timeout code when two bindings point to the + // same adapter). + // + + CHAR LastSourceRoutingTime; + + // + // These are used while initializing the MAC driver. + // + + KEVENT NdisRequestEvent; // used for pended requests. + NDIS_STATUS NdisRequestStatus; // records request status. + NDIS_STATUS OpenErrorStatus; // if Status is NDIS_STATUS_OPEN_FAILED. + + // + // This is the Mac type we must build the packet header for and know the + // offsets for. + // + + NDIS_INFORMATION MacInfo; + + ULONG MaxReceivePacketSize; // does not include the MAC header + ULONG MaxSendPacketSize; // includes the MAC header + ULONG ReceiveBufferSpace; // as queried from the card + + // + // This information is used to keep track of the speed of + // the underlying medium. + // + + ULONG MediumSpeed; // in units of 100 bytes/sec + + // + // The source routing tree for each of the identifiers + // + + PSOURCE_ROUTE SourceRoutingHeads[IDENTIFIER_TOTAL][SOURCE_ROUTE_HASH_SIZE]; + +} ADAPTER, * PADAPTER; + +#define ASSERT_ADAPTER(_Adapter) \ + CTEAssert (((_Adapter)->Type == IPX_ADAPTER_SIGNATURE) && ((_Adapter)->Size == sizeof(ADAPTER))) + + +// +// These are the media and frame type specific MAC header +// constructors that we call in the main TDI send path. +// + +typedef NDIS_STATUS +(*IPX_SEND_FRAME_HANDLER) ( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ); + + +#define BREF_BOUND 1 +#ifdef _PNP_POWER +#define BREF_DEVICE_ACCESS 2 +#define BREF_ADAPTER_ACCESS 3 +#endif +#define BREF_TOTAL 4 + +typedef struct _BINDING { + +#if DBG + ULONG RefTypes[BREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IBI1" +#endif + + ULONG ReferenceCount; + +#ifdef _PNP_POWER + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue +#endif + + // + // Adapter this binding is on. + // + + PADAPTER Adapter; + + // + // ID identifying us to the system (will be the index + // in Device->Bindings[]). + // + + USHORT NicId; + + // + // For LANs these will be the same as the adapter's, for WANs + // they change on line up indications. + // + + ULONG MaxSendPacketSize; + ULONG MediumSpeed; // in units of 100 bytes/sec + HARDWARE_ADDRESS LocalMacAddress; // our local hardware address. + + // + // This is used for WAN lines, all sends go to this address + // which is given on line up. + // + + HARDWARE_ADDRESS RemoteMacAddress; + + // + // For WAN lines, holds the remote address indicated to us + // in the IPXCP_CONFIGURATION structure -- this is used to + // select a binding to send to when WanGlobalNetworkNumber + // is TRUE. + // + + UCHAR WanRemoteNode[6]; + + // + // TRUE if this binding was set up to allow auto-detection, + // instead of being configured explicitly in the registry. + // + + BOOLEAN AutoDetect; + + // + // TRUE if this binding was set up for auto-detection AND + // was the default in the registry. + // + + BOOLEAN DefaultAutoDetect; + + // + // During auto-detect when we are processing responses from + // various networks, these keep track of how many responses + // we have received that match the current guess at the + // network number, and how many don't (the current guess + // is stored in TentativeNetworkAddress). + // + + USHORT MatchingResponses; + USHORT NonMatchingResponses; + + // + // During auto-detect, stores the current guess at the + // network number. + // + + ULONG TentativeNetworkAddress; + + // + // TRUE if this binding is part of a binding set. + // + + BOOLEAN BindingSetMember; + + // + // TRUE if this binding should receive broadcasts (this + // rotates through the members of a binding set). + // + + BOOLEAN ReceiveBroadcast; + + // + // TRUE for WAN lines if we are up. + // + + BOOLEAN LineUp; + + // + // TRUE if this is a WAN line and is dialout. + // + + BOOLEAN DialOutAsync; + + union { + + // + // Used when a binding is active, if it is a member + // of a binding set. + // + + struct { + + // + // Used to link members of a binding set in a circular list. + // NULL for non-set members. + // + + struct _BINDING * NextBinding; + + // + // If this binding is a master of a binding set, this points + // to the binding to use for the next send. For other members + // of a binding set it is NULL. We use this to determine + // if a binding is a master or not. + // + + struct _BINDING * CurrentSendBinding; + + // + // For binding set members, points to the master binding + // (if this is the master it points to itself). + // + + struct _BINDING * MasterBinding; + + }; + + // + // This is used when we are first binding to adapters, + // and the device's Bindings array is not yet allocated. + // + + LIST_ENTRY InitialLinkage; + + }; + + // + // Used by rip to keep track of unused wan lines. + // + + ULONG WanInactivityCounter; + + // + // Our local address, we don't use the socket but we keep + // it here so we can do quick copies. It contains the + // real network that we are bound to and our node + // address on that net (typically the adapter's MAC + // address but it will change for WANs). + // + + TDI_ADDRESS_IPX LocalAddress; + + IPX_SEND_FRAME_HANDLER SendFrameHandler; + + struct _DEVICE * Device; + + CTELock * DeviceLock; + + ULONG DefHeaderSize; // IPX header offset for directed frames + ULONG BcMcHeaderSize; // IPX header offset for broadcast/multicast + + ULONG AnnouncedMaxDatagramSize; // what we advertise -- assumes worst-case SR + ULONG RealMaxDatagramSize; // what will really break the card + ULONG MaxLookaheadData; + + // + // Configuration parameters. We overlay all of them except + // FrameType over the worker thread item we use to delay + // deletion -- all the others are not needed once the + // binding is up. Some of the config parameters are stored + // in the adapter, these are the ones that are modified + // per-binding. + // + + ULONG FrameType; + union { + struct { + ULONG ConfiguredNetworkNumber; + BOOLEAN AllRouteDirected; + BOOLEAN AllRouteBroadcast; + BOOLEAN AllRouteMulticast; + }; + WORK_QUEUE_ITEM WanDelayedQueueItem; + }; + +#ifdef _PNP_POWER + // + // Indicates whether this binding was indicated to the ISN driver + // + BOOLEAN IsnInformed[UPPER_DRIVER_COUNT]; + + // + // Keeps the NetAddressRegistrationHandle. + // + HANDLE TdiRegistrationHandle; +#endif +} BINDING, * PBINDING; + + +#ifdef _PNP_POWER +typedef struct _IPX_BINDING_POOL { + LIST_ENTRY Linkage; + UINT BindingCount; + BINDING Bindings[1]; +} IPX_BINDING_POOL, *PIPX_BINDING_POOL; +#endif + +// +// This structure defines the control structure for a single +// router table segment. +// + +typedef struct _ROUTER_SEGMENT { + LIST_ENTRY WaitingForRoute; // packets waiting for a route in this segment + LIST_ENTRY FindWaitingForRoute; // find route requests waiting for a route in this segment + LIST_ENTRY WaitingLocalTarget; // QUERY_IPX_LOCAL_TARGETs waiting for a route in this segment + LIST_ENTRY WaitingReripNetnum; // MIPX_RERIPNETNUMs waiting for a route in this segment + LIST_ENTRY Entries; + PLIST_ENTRY EnumerateLocation; +} ROUTER_SEGMENT, *PROUTER_SEGMENT; + + +// +// Number of buckets in the address hash table. This is +// a multiple of 2 so hashing is quick. +// + +#define IPX_ADDRESS_HASH_COUNT 16 + +// +// Routine to convert a socket to a hash index. We use the +// high bits because it is stored reversed. +// + +#define IPX_HASH_SOCKET(_S) ((((_S) & 0xff00) >> 8) % IPX_ADDRESS_HASH_COUNT) + +// +// This macro gets the socket hash right out of the IPX header. +// + +#define IPX_DEST_SOCKET_HASH(_IpxHeader) (((PUCHAR)&(_IpxHeader)->DestinationSocket)[1] % IPX_ADDRESS_HASH_COUNT) + + +// +// This structure defines the per-device structure for IPX +// (one of these is allocated globally). +// + +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_SR_TIMER 4 +#define DREF_RIP_TIMER 5 +#define DREF_LONG_TIMER 6 +#define DREF_RIP_PACKET 7 +#define DREF_ADDRESS_NOTIFY 8 +#define DREF_LINE_CHANGE 9 + +#define DREF_TOTAL 12 + +#ifdef _PNP_POWER +// +// Pre-allocated binding array size +// +#define MAX_BINDINGS 50 +#endif _PNP_POWER + +#ifdef _PNP_POWER +// +// Our new binding array is composed of the following binding +// array element +// +typedef struct _BIND_ARRAY_ELEM { + PBINDING Binding; + ULONG Version; +} BIND_ARRAY_ELEM, *PBIND_ARRAY_ELEM; + +#endif _PNP_POWER + +typedef struct _DEVICE { + +#if DBG + ULONG RefTypes[DREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IDC1" +#endif + + CTELock Interlock; // GLOBAL lock for reference count. + // (used in ExInterlockedXxx calls) + + // + // These are temporary versions of these counters, during + // timer expiration we update the real ones. + // + + ULONG TempDatagramBytesSent; + ULONG TempDatagramsSent; + ULONG TempDatagramBytesReceived; + ULONG TempDatagramsReceived; + + // + // Configuration parameters. + // + + BOOLEAN EthernetPadToEven; + BOOLEAN SingleNetworkActive; + BOOLEAN DisableDialoutSap; + + // + // TRUE if we have multiple cards but a virtual network of 0. + // + + BOOLEAN MultiCardZeroVirtual; + + CTELock Lock; + + // + // Lock to access the sequenced lists in the device. + // + CTELock SListsLock; + + LONG ReferenceCount; // activity count/this provider. + +#ifdef _PNP_POWER + + // + // Lock used to control the access to a binding (either from the + // binding array in the device or from the binding array in the + // adapter. + // + CTELock BindAccessLock; + + // + // Registry Path for use when PnP adapters appear. + // + PWSTR RegistryPathBuffer; + + UNICODE_STRING RegistryPath; + + // + // Binding array has the Version number too + // + PBIND_ARRAY_ELEM Bindings; // allocated when number is determined. + ULONG BindingCount; // total allocated in Bindings. + + // + // Monotonically increasing version number kept in bindings. + // Hopefully this will not wrap around... + // + ULONG BindingVersionNumber; +#else + // + // During init we hold all bindings in a queue, but after we + // know the approximate number we allocate an array. + // + + union { + LIST_ENTRY InitialBindingList; // only used during init. + struct { + PBINDING * Bindings; // allocated when number is determined. + ULONG BindingCount; // total allocated in Bindings. + }; + }; +#endif _PNP_POWER + + + // + // ValidBindings is the number of bindings in the array which may + // be valid (they are lan bindings or down wan binding placeholders). + // It will be less than BindingCount by the number of auto-detect + // bindings that are thrown away. HighestExternalNicId is ValidBindings + // minus any binding set slaves which are moved to the end of the + // array. SapNicCount is like HighestExternalNicId except that + // if WanGlobalNetworkNumber is TRUE it will count all WAN bindings + // as one. HighestExternalType20NicId is like HighestExternalNicId + // except it stops when all the remaining bindings are down wan + // lines, or dialin wan lines if DisableDialinNetbios bit 1 is on. + // + + USHORT ValidBindings; + USHORT HighestExternalNicId; + USHORT SapNicCount; + USHORT HighestType20NicId; +#ifdef _PNP_POWER + // + // Keeps track of the last LAN binding's position in the binding array + // + USHORT HighestLanNicId; + + // + // This keeps track of the current size of the binding array + // + USHORT MaxBindings; +#endif _PNP_POWER + + LIST_ENTRY GlobalSendPacketList; + LIST_ENTRY GlobalReceivePacketList; + LIST_ENTRY GlobalReceiveBufferList; + +#if BACK_FILL + LIST_ENTRY GlobalBackFillPacketList; +#endif + + // + // Action requests from SAP waiting for an adapter status to change. + // + + LIST_ENTRY AddressNotifyQueue; + + // + // Action requests from nwrdr waiting for the WAN line + // to go up/down. + // + + LIST_ENTRY LineChangeQueue; + + // + // All packet pools are chained on these lists. + // + + LIST_ENTRY SendPoolList; + LIST_ENTRY ReceivePoolList; + + +#if BACK_FILL + LIST_ENTRY BackFillPoolList; + SLIST_HEADER BackFillPacketList; +#endif + +#ifdef _PNP_POWER + LIST_ENTRY BindingPoolList; + SLIST_HEADER BindingList; +#endif + + SLIST_HEADER SendPacketList; + SLIST_HEADER ReceivePacketList; + PIPX_PADDING_BUFFER PaddingBuffer; + + UCHAR State; + + UCHAR FrameTypeDefault; + + // + // This holds state if SingleNetworkActive is TRUE. If + // it is TRUE then WAN nets are active; if it is FALSE + // then LAN nets are active. + // + + BOOLEAN ActiveNetworkWan; + + // + // TRUE if we have a virtual network. + // + + BOOLEAN VirtualNetwork; + + // + // If we are set up for SingleNetworkActive, we may have + // to start our broadcast of net 0 frames somewhere other + // than NIC ID 1, so that we don't send to the wrong type. + // + + USHORT FirstLanNicId; + USHORT FirstWanNicId; + + // + // This holds the total memory allocated for the above structures. + // + + LONG MemoryUsage; + LONG MemoryLimit; + + // + // How many of various resources have been allocated. + // + + ULONG AllocatedDatagrams; + ULONG AllocatedReceivePackets; + ULONG AllocatedPaddingBuffers; + + // + // Other configuration parameters. + // + + ULONG InitDatagrams; + ULONG MaxDatagrams; + ULONG RipAgeTime; + ULONG RipCount; + ULONG RipTimeout; + ULONG RipUsageTime; + ULONG SourceRouteUsageTime; + USHORT SocketStart; + USHORT SocketEnd; + ULONG SocketUniqueness; + ULONG VirtualNetworkNumber; + ULONG EthernetExtraPadding; + BOOLEAN DedicatedRouter; + BOOLEAN VirtualNetworkOptional; + UCHAR DisableDialinNetbios; + + // + // These are currently not read from the registry. + // + + ULONG InitReceivePackets; + ULONG InitReceiveBuffers; + ULONG MaxReceivePackets; + ULONG MaxReceiveBuffers; + +#ifdef _PNP_POWER + ULONG MaxPoolBindings; + ULONG AllocatedBindings; + ULONG InitBindings; +#endif + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + LARGE_INTEGER ControlChannelIdentifier; + + // + // This registry parameter controls whether IPX checks (and discards) + // packets with mismatched Source addresses in the receive path. + // + BOOLEAN VerifySourceAddress; + + // + // Where the current socket allocation is. + // + USHORT CurrentSocket; + + // + // Number of segments in the RIP database. + // + + ULONG SegmentCount; + + // + // Points to an array of locks for the RIP database (these + // are stored outside of the ROUTER_SEGMENT so the array + // can be exposed to the RIP upper driver as one piece). + // + + CTELock *SegmentLocks; + + // + // Points to an array of ROUTER_SEGMENT fields for + // various RIP control fields. + // + + ROUTER_SEGMENT *Segments; + + // + // Queue of RIP packets waiting to be sent. + // + + LIST_ENTRY WaitingRipPackets; + ULONG RipPacketCount; + + // + // Timer that keeps RIP requests RIP_GRANULARITY ms apart. + // + + BOOLEAN RipShortTimerActive; + USHORT RipSendTime; + CTETimer RipShortTimer; + + // + // Timer that runs to age out unused rip entries (if the + // router is not bound) and re-rip every so often for + // active entries. + // + + CTETimer RipLongTimer; + + // + // This controls the source routing timeout code. + // + + BOOLEAN SourceRoutingUsed; // TRUE if any 802.5 bindings exist. + CHAR SourceRoutingTime; // incremented each time timer fires. + CTETimer SourceRoutingTimer; // runs every minute. + + // + // These are the merging of the binding values. + // + + ULONG LinkSpeed; + ULONG MacOptions; + + // + // Where we tell upper drivers to put their headers. + // + + ULONG IncludedHeaderOffset; + + // + // A pre-allocated header containing our node and network, + // plus an unused socket (so the structure is a known size + // for easy copying). + // + + TDI_ADDRESS_IPX SourceAddress; + + // + // The following field is an array of list heads of ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabases[IPX_ADDRESS_HASH_COUNT]; // list of defined transport addresses. + + // + // Holds the last address we looked up. + // + + PVOID LastAddress; + + NDIS_HANDLE NdisBufferPoolHandle; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + + // + // Information.MaxDatagramSize is the minimum size we can + // send to all bindings assuming worst-case source routing; + // this is the value that won't break any network drivers. + // + + ULONG RealMaxDatagramSize; + +#if DBG + UCHAR Signature2[4]; // contains "IDC2" +#endif + + // + // Indicates whether each upper driver is bound + // (Netbios = 0, SPX = 1, RIP = 2). + // + + BOOLEAN UpperDriverBound[UPPER_DRIVER_COUNT]; + + // + // TRUE if any driver is bound. + // + + BOOLEAN AnyUpperDriverBound; + + // + // Whether a receive complete should be indicated to + // this upper driver. + // + + BOOLEAN ReceiveCompletePending[UPPER_DRIVER_COUNT]; + + // + // Control channel identifier for each of the upper + // drivers' bindings. + // + + LARGE_INTEGER UpperDriverControlChannel[UPPER_DRIVER_COUNT]; + + // + // Entry points and other information for each of the + // upper drivers. + // + + IPX_INTERNAL_BIND_INPUT UpperDrivers[UPPER_DRIVER_COUNT]; + + // + // How many upper drivers want broadcast enabled. + // + + ULONG EnableBroadcastCount; + + // + // Indicates if an enable broadcast operation is in + // progress. + // + + BOOLEAN EnableBroadcastPending; + + // + // Indicates if a disable broadcast operation is in + // progress. + // + + BOOLEAN DisableBroadcastPending; + + // + // Indicates if the current operation should be + // reversed when it is finished. + // + + BOOLEAN ReverseBroadcastOperation; + + // + // TRUE if RIP wants a single network number for all WANs + // + + BOOLEAN WanGlobalNetworkNumber; + + // + // If WanGlobalNetworkNumber is TRUE, then this holds the + // actual value of the network number, once we know it. + // + + ULONG GlobalWanNetwork; + + // + // Set to TRUE if WanGlobalNetworkNumber is TRUE and we + // have already completed a queued notify from SAP. In + // this case GlobalWanNetwork will be set correctly. + // + + BOOLEAN GlobalNetworkIndicated; + + // + // TRUE if we need to act as a RIP announcer/responder + // for our virtual net. + // + + BOOLEAN RipResponder; + + // + // TRUE if we have already logged an error because someone + // sent a SAP response but we have multiple cards with no + // virtual network. + // + + BOOLEAN SapWarningLogged; + + // + // Used to queue up a worker thread to perform + // broadcast operations. + // + + WORK_QUEUE_ITEM BroadcastOperationQueueItem; + +#ifdef _PNP_POWER + // + // Used to queue up a worker thread to perform + // PnP indications to upper drivers. + // + + WORK_QUEUE_ITEM PnPIndicationsQueueItem; +#endif + + // + // This event is used when unloading to signal that + // the reference count is now 0. + // + + KEVENT UnloadEvent; + BOOLEAN UnloadWaiting; + + // + // Counters for most of the statistics that IPX maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + + // + // This is TRUE if we have any adapters where we are + // auto-detecting the frame type. + // + + BOOLEAN AutoDetect; + + // + // This is TRUE if we are auto-detecting and we have + // found the default auto-detect type on the net. + // + + BOOLEAN DefaultAutoDetected; + + // + // Our state during auto-detect. After we are done this + // will stay at AutoDetectDone; + // + + UCHAR AutoDetectState; + + // + // If we are auto-detecting, this event is used to stall + // our initialization code while we do auto-detection -- + // this is so we have a constant view of the world once + // we return from DriverEntry. + // + + KEVENT AutoDetectEvent; + + // + // Counters for "active" time. + // + + LARGE_INTEGER IpxStartTime; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // Points back to the system device object. + // + + PDEVICE_OBJECT DeviceObject; + +#ifdef _PNP_POWER + // + // Used to store the Tdi registration handle for deviceobject notifications. + // + HANDLE TdiRegistrationHandle; + + // + // Used to store the TA_ADDRESS which is indicated up to Tdi clients as adapters appear. + // + PTA_ADDRESS TdiRegistrationAddress; +#endif + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; + ULONG DeviceNameLength; + +} DEVICE, * PDEVICE; + + +extern PDEVICE IpxDevice; +extern PIPX_PADDING_BUFFER IpxPaddingBuffer; +#if DBG +EXTERNAL_LOCK(IpxGlobalInterlock); +#endif + + +// +// device state definitions +// + +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 + +#ifdef _PNP_POWER + +// +// New state which comes between CLOSED and OPEN. At this state, +// there are no adapters in the system and so no network activity +// is possible. +// +#define DEVICE_STATE_LOADED 0x03 +#endif _PNP_POWER + +// +// This is the state of our auto-detect if we do it. +// + +#define AUTO_DETECT_STATE_INIT 0x00 // still initializing the device +#define AUTO_DETECT_STATE_RUNNING 0x01 // sent ffffffff query, waiting for responses +#define AUTO_DETECT_STATE_PROCESSING 0x02 // processing the responses +#define AUTO_DETECT_STATE_DONE 0x03 // detection is done, IPX is active + + + +#define IPX_TDI_RESOURCES 9 + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. +// + +#define AFREF_CREATE 0 +#define AFREF_RCV_DGRAM 1 +#define AFREF_SEND_DGRAM 2 +#define AFREF_VERIFY 3 +#define AFREF_INDICATION 4 + +#define AFREF_TOTAL 8 + +typedef struct _ADDRESS_FILE { + +#if DBG + ULONG RefTypes[AFREF_TOTAL]; +#endif + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + ULONG ReferenceCount; // number of references to this object. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + CTELock * AddressLock; + + // + // The following fields are kept for housekeeping purposes. + // + + PREQUEST Request; // the request used for open or close + struct _ADDRESS *Address; // address to which we are bound. +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + struct _DEVICE *Device; // device to which we are attached. + + // + // + // TRUE if ExtendedAddressing, ReceiveIpxHeader, + // FilterOnPacketType, or ReceiveFlagAddressing is TRUE. + // + + BOOLEAN SpecialReceiveProcessing; + + // + // The remote address of a send datagram includes the + // packet type. and on a receive datagram includes + // the packet type AND a flags byte indicating information + // about the frame (was it broadcast, was it sent from + // this machine). + // + + BOOLEAN ExtendedAddressing; + + // + // TRUE if the address on a receive datagram includes + // the packet type and a flags byte (like ExtendedAddressing), + // but on send the address is normal (no packet type). + // + + BOOLEAN ReceiveFlagsAddressing; + + // + // Is the IPX header received with the data. + // + + BOOLEAN ReceiveIpxHeader; + + // + // The packet type to use if it is unspecified in the send. + // + + UCHAR DefaultPacketType; + + // + // TRUE if packet type filtering is enabled. + // + + BOOLEAN FilterOnPacketType; + + // + // The packet type to filter on. + // + + UCHAR FilteredType; + + // + // Does this address file want broadcast packets. + // + + BOOLEAN EnableBroadcast; + + // + // This is set to TRUE if this is the SAP socket -- we + // put this under SpecialReceiveProcessing to avoid + // hitting the main path. + // + + BOOLEAN IsSapSocket; + + // + // The following queue is used to queue receive datagram requests + // on this address file. Send datagram requests are queued on the + // address itself. These queues are managed by the EXECUTIVE interlocked + // list management routines. The actual objects which get queued to this + // structure are request control blocks (RCBs). + // + + LIST_ENTRY ReceiveDatagramQueue; // FIFO of outstanding TdiReceiveDatagrams. + + // + // This holds the request used to close this address file, + // for pended completion. + // + + PREQUEST CloseRequest; + + // + // handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + // + // [CH] Added the chained receive handlers. + // + + BOOLEAN RegisteredReceiveDatagramHandler; + BOOLEAN RegisteredChainedReceiveDatagramHandler; + BOOLEAN RegisteredErrorHandler; + + // + // The following function pointer always points to a TDI_IND_RECEIVE_DATAGRAM + // event handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which does + // not accept the incoming data. + // + + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramHandlerContext; + PTDI_IND_CHAINED_RECEIVE_DATAGRAM ChainedReceiveDatagramHandler; + PVOID ChainedReceiveDatagramHandlerContext; + + // + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorHandlerContext; + +} ADDRESS_FILE, *PADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#define AREF_ADDRESS_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 + +#define AREF_TOTAL 4 + +typedef struct _ADDRESS { + +#if DBG + ULONG RefTypes[AREF_TOTAL]; +#endif + + USHORT Size; + CSHORT Type; + +/* ULONGs to allow for Interlocked operations. + + BOOLEAN SendPacketInUse; // put these after so header is aligned. + + BOOLEAN ReceivePacketInUse; +#if BACK_FILL + BOOLEAN BackFillPacketInUse; +#endif +*/ + + ULONG SendPacketInUse; // put these after so header is aligned. + + ULONG ReceivePacketInUse; +#if BACK_FILL + ULONG BackFillPacketInUse; +#endif + + LIST_ENTRY Linkage; // next address/this device object. + ULONG ReferenceCount; // number of references to this object. + + CTELock Lock; + + // + // The following fields comprise the actual address itself. + // + + PREQUEST Request; // pointer to address creation request. + + USHORT Socket; // the socket this address corresponds to. + USHORT SendSourceSocket; // used for sends; may be == Socket or 0 + + // + // The following fields are used to maintain state about this address. + // + + BOOLEAN Stopping; + ULONG Flags; // attributes of the address. + struct _DEVICE *Device; // device context to which we are attached. + CTELock * DeviceLock; + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + // + // Holds our source address, used for construcing datagrams + // quickly. + // + + TDI_ADDRESS_IPX LocalAddress; + + IPX_SEND_PACKET SendPacket; + IPX_RECEIVE_PACKET ReceivePacket; + +#if BACK_FILL + IPX_SEND_PACKET BackFillPacket; +#endif + + + UCHAR SendPacketHeader[IPX_MAXIMUM_MAC + sizeof(IPX_HEADER)]; + +#ifdef ISN_NT + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying IpxDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + +#endif + +} ADDRESS, *PADDRESS; + +#define ADDRESS_FLAGS_STOPPING 0x00000001 + +// +// In order to increase the range of ControlChannelIds, we have a large integer to represent +// monotonically increasing ControlChannelIdentifiers. This large integer is packed into the +// 6 Bytes as follows: +// +// REQUEST_OPEN_CONTEXT(_Request) - 4 bytes +// Upper 2 bytes of REQUEST_OPEN_TYPE(_Request) - 2 bytes +// +// IPX_CC_MASK is used to mask out the upper 2 bytes of the OPEN_TYPE. +// MAX_CCID is 2^48. +// +#define IPX_CC_MASK 0x0000ffff + +#define MAX_CCID 0xffffffffffff + +#define CCID_FROM_REQUEST(_ccid, _Request) \ + (_ccid).LowPart = (ULONG)(REQUEST_OPEN_CONTEXT(_Request)); \ + (_ccid).HighPart = ((ULONG)(REQUEST_OPEN_TYPE(_Request)) >> 16); + diff --git a/private/ntos/tdi/isnp/ipx/isnipx.h b/private/ntos/tdi/isnp/ipx/isnipx.h new file mode 100644 index 000000000..df947d439 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/isnipx.h @@ -0,0 +1,531 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnipx.h + +Abstract: + + This module contains definitions specific to the + IPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#ifndef _ISNIPX_ +#define _ISNIPX_ + +#define MAC_HEADER_SIZE ((IPX_MAXIMUM_MAC + 3) & ~3) +#define RIP_PACKET_SIZE ((sizeof(RIP_PACKET) + 3) & ~3) +#define IPX_HEADER_SIZE ((sizeof(IPX_HEADER) + 3) & ~3) + +// +// Frame type definitions +// + +#define ISN_FRAME_TYPE_ETHERNET_II 0 +#define ISN_FRAME_TYPE_802_3 1 +#define ISN_FRAME_TYPE_802_2 2 +#define ISN_FRAME_TYPE_SNAP 3 +#define ISN_FRAME_TYPE_ARCNET 4 // we ignore this +#define ISN_FRAME_TYPE_MAX 4 // of the four standard ones + +#define ISN_FRAME_TYPE_AUTO 0xff + + +// +// This defines the size of the maximum MAC header required +// (token-ring: MAC 14 bytes, RI 18 bytes, LLC 3 bytes, SNAP 5 bytes). +// + +#define IPX_MAXIMUM_MAC 40 + +// +// This is an internal identifier used for RIP query packets. +// + +#define IDENTIFIER_RIP_INTERNAL 4 + +// +// This is an internal identifier used for RIP response packets. +// + +#define IDENTIFIER_RIP_RESPONSE 5 + + +// +// This is the total number of "real" identifiers. +// + +#define IDENTIFIER_TOTAL 4 + + +// +// Some definitions (in the correct on-the-wire order). +// + +#define RIP_PACKET_TYPE 0x01 +#define RIP_SOCKET 0x5304 +#define RIP_REQUEST 0x0100 +#define RIP_RESPONSE 0x0200 +#define RIP_DOWN 0x8200 // use high bit to indicate it + +#define SAP_PACKET_TYPE 0x04 +#define SAP_SOCKET 0x5204 + +#define SPX_PACKET_TYPE 0x05 + +#define NB_SOCKET 0x5504 + + +#include <packon.h> + +// +// Definition of the IPX header. +// + +typedef struct _IPX_HEADER { + USHORT CheckSum; + UCHAR PacketLength[2]; + UCHAR TransportControl; + UCHAR PacketType; + UCHAR DestinationNetwork[4]; + UCHAR DestinationNode[6]; + USHORT DestinationSocket; + UCHAR SourceNetwork[4]; + UCHAR SourceNode[6]; + USHORT SourceSocket; +} IPX_HEADER, *PIPX_HEADER; + + +// +// Definition of a RIP network entry. +// + +typedef struct _RIP_NETWORK_ENTRY { + ULONG NetworkNumber; + USHORT HopCount; + USHORT TickCount; +} RIP_NETWORK_ENTRY, *PRIP_NETWORK_ENTRY; + +// +// Definition of a single entry rip packet. +// + +typedef struct _RIP_PACKET { + USHORT Operation; + RIP_NETWORK_ENTRY NetworkEntry; +} RIP_PACKET, *PRIP_PACKET; + +#include <packoff.h> + + +#define IPX_DEVICE_SIGNATURE 0x1401 +#define IPX_ADAPTER_SIGNATURE 0x1402 +#define IPX_BINDING_SIGNATURE 0x1403 +#define IPX_ADDRESS_SIGNATURE 0x1404 +#define IPX_ADDRESSFILE_SIGNATURE 0x1405 + +#define IPX_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + + +// +// Defined granularity of RIP timeouts in milliseconds +// + +#define RIP_GRANULARITY 55 + + +// +// The default number of segments in the RIP table. +// + +#define RIP_SEGMENTS 7 + + + +// +// Convert a ushort netware order <-> machine order +// + +#define REORDER_USHORT(_Ushort) ((((_Ushort) & 0xff00) >> 8) | (((_Ushort) & 0x00ff) << 8)) + +// +// Convert a ulong netware order <-> machine order +// + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + + +#if DBG + +extern ULONG IpxDebug; +extern ULONG IpxMemoryDebug; + +#define IPX_MEMORY_LOG_SIZE 128 +extern UCHAR IpxDebugMemory[IPX_MEMORY_LOG_SIZE][64]; +extern PUCHAR IpxDebugMemoryLoc; +extern PUCHAR IpxDebugMemoryEnd; + +VOID +IpxDebugMemoryLog( + IN PUCHAR FormatString, + ... +); + +#define IPX_DEBUG(_Flag, _Print) { \ + if (IpxDebug & (IPX_DEBUG_ ## _Flag)) { \ + DbgPrint ("IPX: "); \ + DbgPrint _Print; \ + } \ + if (IpxMemoryDebug & (IPX_DEBUG_ ## _Flag)) { \ + IpxDebugMemoryLog _Print; \ + } \ +} + +#else + +#define IPX_DEBUG(_Flag, _Print) + +#endif + + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; + + +// +// PREQUEST +// IpxAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define IpxAllocateRequest(_Device,_Irp) \ + (_Irp) + + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// IpxFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define IpxFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// VOID +// REQUEST_OPEN_CONTEXT_AND_PARAMS( +// IN PREQUEST Request +// OUT PVOID * OpenContext, +// OUT PTDI_REQUEST_KERNEL * Parameters +// ); +// +// Simultaneously returns the open context and the parameters +// for a request (this is an optimization since the send +// datagram code needs them both). +// + +#define REQUEST_OPEN_CONTEXT_AND_PARAMS(_Request,_OpenContext,_Parameters) { \ + PIO_STACK_LOCATION _IrpSp = IoGetCurrentIrpStackLocation(_Request); \ + *(_OpenContext) = _IrpSp->FileObject->FsContext; \ + *(_Parameters) = (PTDI_REQUEST_KERNEL)(&_IrpSp->Parameters); \ +} + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// IpxCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define IpxCompleteRequest(_Request) \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT) + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + + +#define IPX_INCREMENT(_Long, _Lock) InterlockedIncrement(_Long) +#define IPX_DECREMENT(_Long, _Lock) InterlockedDecrement(_Long) + +#define IPX_ADD_ULONG(_Pulong, _Ulong, _Lock) InterlockedExchangeAdd(_Pulong, _Ulong) + +#define IPX_DEFINE_SYNC_CONTEXT(_SyncContext) +#define IPX_BEGIN_SYNC(_SyncContext) +#define IPX_END_SYNC(_SyncContext) + +#define IPX_DEFINE_LOCK_HANDLE(_LockHandle) CTELockHandle _LockHandle; +#define IPX_DEFINE_LOCK_HANDLE_PARAM(_LockHandle) CTELockHandle _LockHandle; + +#define IPX_GET_LOCK(_Lock, _LockHandle) \ + CTEGetLock(_Lock, _LockHandle) + +#define IPX_FREE_LOCK(_Lock, _LockHandle) \ + CTEFreeLock(_Lock, _LockHandle) + +#define IPX_GET_LOCK1(_Lock, _LockHandle) + +#define IPX_FREE_LOCK1(_Lock, _LockHandle) + +#define IPX_REMOVE_HEAD_LIST(_Queue, _Lock) ExInterlockedRemoveHeadList(_Queue, _Lock) +#define IPX_LIST_WAS_EMPTY(_Queue, _OldHead) ((_OldHead) == NULL) +#define IPX_INSERT_HEAD_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertHeadList(_Queue, _Entry, _Lock) +#define IPX_INSERT_TAIL_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertTailList(_Queue, _Entry, _Lock) + +#define IPX_POP_ENTRY_LIST(_Queue, _Lock) ExInterlockedPopEntrySList(_Queue, _Lock) +#define IPX_PUSH_ENTRY_LIST(_Queue, _Entry, _Lock) ExInterlockedPushEntrySList(_Queue, _Entry, _Lock) + +// +// This macro adds a ULONG to a LARGE_INTEGER. +// + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger),(ULONG)(_Ulong)) + +#define IPX_DEBUG_DEVICE 0x00000001 +#define IPX_DEBUG_ADAPTER 0x00000002 +#define IPX_DEBUG_ADDRESS 0x00000004 +#define IPX_DEBUG_SEND 0x00000008 +#define IPX_DEBUG_NDIS 0x00000010 +#define IPX_DEBUG_RECEIVE 0x00000020 +#define IPX_DEBUG_CONFIG 0x00000040 +#define IPX_DEBUG_PACKET 0x00000080 +#define IPX_DEBUG_RIP 0x00000100 +#define IPX_DEBUG_BIND 0x00000200 +#define IPX_DEBUG_ACTION 0x00000400 +#define IPX_DEBUG_BAD_PACKET 0x00000800 +#define IPX_DEBUG_SOURCE_ROUTE 0x00001000 +#define IPX_DEBUG_WAN 0x00002000 +#define IPX_DEBUG_AUTO_DETECT 0x00004000 + +#ifdef _PNP_POWER +#define IPX_DEBUG_PNP 0x00008000 +#endif + +#define IPX_DEBUG_LOOPB 0x00010000 + +#endif diff --git a/private/ntos/tdi/isnp/ipx/loopback.c b/private/ntos/tdi/isnp/ipx/loopback.c new file mode 100644 index 000000000..be44bae5b --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/loopback.c @@ -0,0 +1,280 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + loopback.c + +Abstract: + + This module contains the routines to implement loopback + +Author: + + Sanjay Anand (SanjayAn) 2/6/96 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Global lock to control access to the Loopback queue +// +DEFINE_LOCK_STRUCTURE(LoopLock) + +// +// Head and tail of the Loopback queue +// +PNDIS_PACKET LoopXmitHead = (PNDIS_PACKET)NULL; +PNDIS_PACKET LoopXmitTail = (PNDIS_PACKET)NULL; + +CTEEvent LoopXmitEvent; +BOOLEAN LoopXmitRtnRunning = 0; + +// +// MaximumPacket sized buffer to hold the lookahead data. +// +// ZZBUGBUG: In PnP this value can change +// +// PUCHAR LookaheadBuffer=NULL; +#define LOOP_LOOKAHEAD_SIZE 128 + sizeof(IPX_HEADER) + 8 + 34 + + +VOID +IpxDoLoopback( + IN CTEEvent *Event, + IN PVOID Context + ) +/*++ + +Routine Description: + + Does the actual loopback. + +Arguments: + + Event - Pointer to event structure. + + Context - Pointer to ZZ + +Return Value: + + None. + +--*/ +{ + PNDIS_PACKET Packet; // Pointer to packet being transmitted + PNDIS_BUFFER Buffer; // Current NDIS buffer being processed. + ULONG TotalLength; // Total length of send. + ULONG LookaheadLength; // Bytes in lookahead. + ULONG Copied; // Bytes copied so far. + PUCHAR CopyPtr; // Pointer to buffer being copied into. + PUCHAR SrcPtr; // Pointer to buffer being copied from. + ULONG SrcLength; // Length of src buffer. + BOOLEAN Rcvd = FALSE; + PIPX_SEND_RESERVED Reserved; + ULONG MacSize; + PNDIS_PACKET *PacketPtr; + UCHAR LookaheadBuffer[LOOP_LOOKAHEAD_SIZE]; + + IPX_DEFINE_LOCK_HANDLE(Handle) + + KIRQL OldIrql; + + CTEAssert(KeGetCurrentIrql() < DISPATCH_LEVEL); + + // + // Raise IRQL so we can acquire locks at DPC level in the receive code. + // Also to be able to ReceiveIndicate at DPC + // + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + IPX_GET_LOCK(&LoopLock, &Handle); + + if (LoopXmitRtnRunning) { + IPX_FREE_LOCK(&LoopLock, Handle); + KeLowerIrql(OldIrql); + return; + } + + LoopXmitRtnRunning = 1; + + for (;;) { + + // + // Get the next packet from the list. + // + Packet = LoopXmitHead; + + if (Packet != (PNDIS_PACKET)NULL) { + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + LoopXmitHead = (PNDIS_PACKET)(Reserved->PaddingBuffer); + IPX_FREE_LOCK(&LoopLock, Handle); + } else { // Nothing left to do. + LoopXmitRtnRunning = 0; + IPX_FREE_LOCK(&LoopLock, Handle); + break; + } + + // + // We use the PaddingBuffer section as the next ptr. + // + Reserved->PaddingBuffer = NULL; + + IPX_DEBUG(LOOPB, ("Packet: %lx\n", Packet)); + + NdisQueryPacket(Packet, NULL, NULL, &Buffer, &TotalLength); + + NdisQueryBuffer(Buffer, NULL, &MacSize); + + IPX_DEBUG(LOOPB, ("Buffer: %lx Totalpktlen: %lx MacSize: %lx\n", Buffer, TotalLength, MacSize)); + + LookaheadLength = MIN(LOOP_LOOKAHEAD_SIZE, TotalLength); + Copied = 0; + CopyPtr = LookaheadBuffer; + while (Copied < LookaheadLength) { + ULONG ThisCopy; // Bytes to copy this time. + +#ifdef DBG + if (!Buffer) { + DbgBreakPoint(); + IPX_GET_LOCK(&LoopLock, &Handle); + LoopXmitRtnRunning = 0; + IPX_FREE_LOCK(&LoopLock, Handle); + KeLowerIrql(OldIrql); + return; + } +#endif + + NdisQueryBuffer(Buffer, &SrcPtr, &SrcLength); + ThisCopy = MIN(SrcLength, LookaheadLength - Copied); + CTEMemCopy(CopyPtr, SrcPtr, ThisCopy); + Copied += ThisCopy; + CopyPtr += ThisCopy; + NdisGetNextBuffer(Buffer, &Buffer); + } + + Rcvd = TRUE; + +#ifdef BACK_FILL + // + // For Backfill packets, the MAC header is not yet set up; for others, it is the size + // of the first MDL (17). + // + if ((Reserved->Identifier == IDENTIFIER_IPX) && + (Reserved->BackFill)) { + MacSize = 0; + } +#endif + IpxReceiveIndication( (NDIS_HANDLE)IPX_LOOPBACK_COOKIE, // BindingContext + Packet, // ReceiveContext + (MacSize) ? LookaheadBuffer : NULL, // HeaderBuffer + MacSize, // HeaderBufferSize + LookaheadBuffer+MacSize, // LookAheadBuffer + LookaheadLength-MacSize, // LookAheadBufferSize + TotalLength-MacSize); // PacketSize + + IpxSendComplete(Context, Packet, NDIS_STATUS_SUCCESS); + + // + // Give other threads a chance to run. + // + KeLowerIrql(OldIrql); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + IPX_GET_LOCK(&LoopLock, &Handle); + } + + if (Rcvd) { + IpxReceiveComplete(Context); + } + + KeLowerIrql(OldIrql); +} + + +VOID +IpxInitLoopback() +/*++ + +Routine Description: + + Initializes various loopback structures. + +Arguments: + +Return Value: + + None. + +--*/ +{ + CTEInitLock(&LoopLock); + CTEInitEvent(&LoopXmitEvent, IpxDoLoopback); + return; +} + + +VOID +IpxLoopbackEnque( + IN PNDIS_PACKET Packet, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Enqueues a packet to the loopbackQ + +Arguments: + + Packet - The packet to be enqueued. + + Context - Pointer to the adapter corresp to the first binding. + +Return Value: + + None. + +--*/ +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + // + // We use the PaddingBuffer as the next ptr. + // + Reserved->PaddingBuffer = NULL; + + IPX_GET_LOCK(&LoopLock, &LockHandle); + + // + // LoopbackQ is empty + // + if (LoopXmitHead == (PNDIS_PACKET)NULL) { + LoopXmitHead = Packet; + } else { + Reserved = (PIPX_SEND_RESERVED)(LoopXmitTail->ProtocolReserved); + (PNDIS_PACKET)(Reserved->PaddingBuffer) = Packet; + } + LoopXmitTail = Packet; + + IPX_DEBUG(LOOPB, ("Enqued packet: %lx, Reserved: %lx\n", Packet, Reserved)); + + // + // If this routine is not already running, schedule it as a work item. + // + if (!LoopXmitRtnRunning) { + CTEScheduleEvent(&LoopXmitEvent, Context); + } + + IPX_FREE_LOCK(&LoopLock, LockHandle); +} diff --git a/private/ntos/tdi/isnp/ipx/mac.c b/private/ntos/tdi/isnp/ipx/mac.c new file mode 100644 index 000000000..93f9e8a89 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mac.c @@ -0,0 +1,3793 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + mac.c + +Abstract: + + This module contains code which implements Mac type dependent code for + the IPX transport. + +Environment: + + Kernel mode (Actually, unimportant) + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#define TR_LENGTH_MASK 0x1F // low 5 bits in byte +#define TR_DIRECTION_MASK 0x80 // returns direction bit +#define TR_DEFAULT_LENGTH 0x70 // default for outgoing +#define TR_MAX_SIZE_MASK 0x70 + +#define TR_PREAMBLE_AC 0x10 +#define TR_PREAMBLE_FC 0x40 + +#define FDDI_HEADER_BYTE 0x57 + + +static UCHAR AllRouteSourceRouting[2] = { 0x82, TR_DEFAULT_LENGTH }; +static UCHAR SingleRouteSourceRouting[2] = { 0xc2, TR_DEFAULT_LENGTH }; + +#define ROUTE_EQUAL(_A,_B) { \ + (*(UNALIGNED USHORT *)(_A) == *(UNALIGNED USHORT *)(_B)) \ +} + +// +// For back-fillable packets, chains the back-fill space as a MAC header +// to the packet and sets the header pointer. +// + +// +// BUGBUG: We dont need to test for IDENTIFIER_IPX since it will always be +// true for the mediumframe specific send handlers. +// +#define BACK_FILL_HEADER(_header, _reserved, _headerlength, _packet) \ + if ((_reserved)->Identifier == IDENTIFIER_IPX) { \ + if((_reserved)->BackFill) { \ + CTEAssert ((_reserved)->HeaderBuffer); \ + CTEAssert ((_reserved)->HeaderBuffer->MdlFlags & MDL_NETWORK_HEADER); \ + _header = (PCHAR)(_reserved)->HeaderBuffer->MappedSystemVa - _headerlength; \ + (_reserved)->HeaderBuffer->MappedSystemVa = (PCHAR)(_reserved)->HeaderBuffer->MappedSystemVa - _headerlength; \ + (_reserved)->HeaderBuffer->ByteOffset -= _headerlength; \ + NdisChainBufferAtFront(_packet,(PNDIS_BUFFER)(_reserved)->HeaderBuffer); \ + } \ + } + +// +// In case of back-fillable packets, the adjusted length should include +// the prev. bytecount of the headerbuffer. +// +#define BACK_FILL_ADJUST_BUFFER_LENGTH(_reserved, _headerlength) \ + if((_reserved)->BackFill){ \ + NdisAdjustBufferLength ((_reserved)->HeaderBuffer, _headerlength+(_reserved)->HeaderBuffer->ByteCount); \ + IPX_DEBUG(SEND,("mac user mdl %x\n", (_reserved)->HeaderBuffer)); \ + } else { \ + NdisAdjustBufferLength ((_reserved)->HeaderBuffer, _headerlength); \ + } + +// +// This is the interpretation of the length bits in +// the 802.5 source-routing information. +// + +ULONG SR802_5Lengths[8] = { 516, 1500, 2052, 4472, + 8144, 11407, 17800, 17800 }; + +#ifndef _PNP_POWER + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,MacInitializeMacInfo) +#endif + +#endif + + +VOID +MacInitializeBindingInfo( + IN struct _BINDING * Binding, + IN struct _ADAPTER * Adapter + ) + +/*++ + +Routine Description: + + Fills in the binding info based on the adapter's MacInfo + and the frame type of the binding. + +Arguments: + + Binding - The newly created binding. + + Adapter - The adapter. + +Return Value: + + None. + +--*/ + +{ + ULONG MaxUserData; + + Binding->DefHeaderSize = Adapter->DefHeaderSizes[Binding->FrameType]; + Binding->BcMcHeaderSize = Adapter->BcMcHeaderSizes[Binding->FrameType]; + + MacReturnMaxDataSize( + &Adapter->MacInfo, + NULL, + 0, + Binding->MaxSendPacketSize, + &MaxUserData); + + Binding->MaxLookaheadData = + Adapter->MaxReceivePacketSize - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + + Binding->AnnouncedMaxDatagramSize = + MaxUserData - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + + Binding->RealMaxDatagramSize = + Binding->MaxSendPacketSize - + Adapter->MacInfo.MaxHeaderLength - + sizeof(IPX_HEADER) - + (Binding->DefHeaderSize - Adapter->MacInfo.MinHeaderLength); + +} /* MacInitializeBindingInfo */ + + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PNDIS_INFORMATION MacInfo + ) + +/*++ + +Routine Description: + + Fills in the MacInfo table based on MacType. + +Arguments: + + MacType - The MAC type we wish to decode. + + MacInfo - The MacInfo structure to fill in. + +Return Value: + + None. + +--*/ + +{ + switch (MacType) { + case NdisMedium802_3: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 14; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + case NdisMedium802_5: + MacInfo->SourceRouting = TRUE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x80; + MacInfo->MaxHeaderLength = 32; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_5; + break; + case NdisMediumFddi: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 13; + MacInfo->MinHeaderLength = 13; + MacInfo->MediumType = NdisMediumFddi; + break; + case NdisMediumArcnet878_2: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = FALSE; + MacInfo->BroadcastMask = 0x00; + MacInfo->MaxHeaderLength = 3; + MacInfo->MinHeaderLength = 3; + MacInfo->MediumType = NdisMediumArcnet878_2; + break; + case NdisMediumWan: + MacInfo->SourceRouting = FALSE; + MacInfo->MediumAsync = TRUE; + MacInfo->BroadcastMask = 0x01; + MacInfo->MaxHeaderLength = 14; + MacInfo->MinHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + default: + CTEAssert(FALSE); + } + MacInfo->RealMediumType = MacType; + +} /* MacInitializeMacInfo */ + + +VOID +MacMapFrameType( + IN NDIS_MEDIUM MacType, + IN ULONG FrameType, + OUT ULONG * MappedFrameType + ) + +/*++ + +Routine Description: + + Maps the specified frame type to a value which is + valid for the medium. + +Arguments: + + MacType - The MAC type we wish to map for. + + FrameType - The frame type in question. + + MappedFrameType - Returns the mapped frame type. + +Return Value: + +--*/ + +{ + switch (MacType) { + + // + // Ethernet accepts all values, the default is 802.2. + // + + case NdisMedium802_3: + if (FrameType >= ISN_FRAME_TYPE_MAX) { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } else { + *MappedFrameType = FrameType; + } + break; + + // + // Token-ring supports SNAP and 802.2 only. + // + + case NdisMedium802_5: + if (FrameType == ISN_FRAME_TYPE_SNAP) { + *MappedFrameType = ISN_FRAME_TYPE_SNAP; + } else { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } + break; + + // + // FDDI supports SNAP, 802.2, and 802.3 only. + // + + case NdisMediumFddi: + if ((FrameType == ISN_FRAME_TYPE_SNAP) || (FrameType == ISN_FRAME_TYPE_802_3)) { + *MappedFrameType = FrameType; + } else { + *MappedFrameType = ISN_FRAME_TYPE_802_2; + } + break; + + // + // On arcnet there is only one frame type, use 802.3 + // (it doesn't matter what we use). + // + + case NdisMediumArcnet878_2: + *MappedFrameType = ISN_FRAME_TYPE_802_3; + break; + + // + // WAN uses ethernet II because it includes the ethertype. + // + + case NdisMediumWan: + *MappedFrameType = ISN_FRAME_TYPE_ETHERNET_II; + break; + + default: + CTEAssert(FALSE); + } + +} /* MacMapFrameType */ + +// +// BUGBUG -- use symbols instead of hardcoded values for mac header lengths +// --pradeepb +// + +VOID +MacReturnMaxDataSize( + IN PNDIS_INFORMATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ) + +/*++ + +Routine Description: + + This routine returns the space available for user data in a MAC packet. + This will be the available space after the MAC header; all headers + headers will be included in this space. + +Arguments: + + MacInfo - Describes the MAC we wish to decode. + + SourceRouting - If we are concerned about a reply to a specific + frame, then this information is used. + + SourceRouting - The length of SourceRouting. + + MaxFrameSize - The maximum frame size as returned by the adapter. + + MaxDataSize - The maximum data size computed. + +Return Value: + + None. + +--*/ + +{ + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + // + // For 802.3, we always have a 14-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 14; + break; + + case NdisMedium802_5: + + // + // For 802.5, if we have source routing information then + // use that, otherwise assume the worst. + // + + if (SourceRouting && SourceRoutingLength >= 2) { + + UINT SRLength; + + SRLength = SR802_5Lengths[(SourceRouting[1] & TR_MAX_SIZE_MASK) >> 4]; + DeviceMaxFrameSize -= (SourceRoutingLength + 14); + + if (DeviceMaxFrameSize < SRLength) { + *MaxFrameSize = DeviceMaxFrameSize; + } else { + *MaxFrameSize = SRLength; + } + + } else { + +#if 0 + if (DeviceMaxFrameSize < 608) { + *MaxFrameSize = DeviceMaxFrameSize - 32; + } else { + *MaxFrameSize = 576; + } +#endif + // + // bug # 6192. There is no point in assuming the worst. It only + // leads to lower throughput. Packets can get dropped by an + // an intermediate router for both cases (this one and the one + // above where 576 is chosen). In the above case, they will + // get dropped if two ethernet machines are communicating via + // a token ring. In this case, they will if two token ring + // machines with a frame size > max ethernet frame size are + // going over an ethernet. To fix the packet drop case, one + // should adjust the MaxPktSize Parameter of the card. + // + *MaxFrameSize = DeviceMaxFrameSize - 32; + } + + break; + + case NdisMediumFddi: + + // + // For FDDI, we always have a 13-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 13; + break; + + case NdisMediumArcnet878_2: + + // + // For Arcnet, we always have a 3-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 3; + break; + + } + +} /* MacReturnMaxDataSize */ + +#if 0 + +VOID +IpxUpdateWanInactivityCounter( + IN PBINDING Binding, + IN IPX_HEADER UNALIGNED * IpxHeader, + IN ULONG IncludedHeaderLength, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength + ) + +/*++ + +Routine Description: + + This routine is called when a frame is being sent on a WAN + line. It updates the inactivity counter for this binding + unless: + + - The frame is from the RIP socket + - The frame is from the SAP socket + - The frame is a netbios keep alive + - The frame is an NCP keep alive + + BUGBUG: Take the identifier as a parameter to optimize. + +Arguments: + + Binding - The binding the frame is sent on. + + IpxHeader - May contain the first bytes of the packet. + + IncludedHeaderLength - The number of packet bytes at IpxHeader. + + Packet - The full NDIS packet. + + PacketLength - The length of the packet. + +Return Value: + + None, but in some cases we return without resetting the + inactivity counter. + +Comments: THIS FUNCTION IS REAL HACKY AND NEEDS TO BE WORKED AT. WE CAN + Improve the instruction count here - pradeepb + +--*/ + +{ + PNDIS_BUFFER SecondBuffer = NULL; + PUCHAR SecondBufferData; + UINT SecondBufferLength; + USHORT SourceSocket; + PNDIS_BUFFER ThirdBuffer = NULL; + PUCHAR ThirdBufferData; + UINT ThirdBufferLength; + + UNREFERENCED_PARAMETER (PacketLength); + + // + // First get the source socket. + // + +#if 0 + // + // Only time IncludedHeaderLength is less than the offset of + // SourceSocket in IPX header is when it 0 (from rip) + // + if (IncludedHeaderLength <= FIELD_OFFSET (IPX_HEADER, SourceSocket)) { +#endif + + // + // Get the second buffer in the packet (the ipx header is always in + // the second buffer - pradeepb. + // + // In this case + // there must be a second buffer or the packet is too + // short, so we don't check for NULL. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + SecondBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (SecondBuffer, (PVOID *)&SecondBufferData, &SecondBufferLength); + + SourceSocket = *(UNALIGNED USHORT *) + (&SecondBufferData[FIELD_OFFSET(IPX_HEADER, SourceSocket) - IncludedHeaderLength]); + +#if 0 + } +else { + + SourceSocket = IpxHeader->SourceSocket; + } +#endif + if ((SourceSocket == RIP_SOCKET) || + (SourceSocket == SAP_SOCKET)) { + + return; + + } + + if (SourceSocket == NB_SOCKET) { + + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT TotalDataLength; + +#if 0 + // + // We assume the connection control flag and data stream type + // are in the same buffer. + // + + if (IncludedHeaderLength < sizeof(IPX_HEADER) + 2) { + + if (SecondBuffer == NULL) { + + // + // Get the second buffer in the packet. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + SecondBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (ThirdBuffer, (PVOID *)&SecondBufferData, &SecondBufferLength); + + } +#endif + + ConnectionControlFlag = *(SecondBufferData + (sizeof(IPX_HEADER) - IncludedHeaderLength)); + DataStreamType = *(SecondBufferData + (sizeof(IPX_HEADER) + 1 - IncludedHeaderLength)); + + +#if 0 + } else { + + ConnectionControlFlag = ((PUCHAR)(IpxHeader+1))[0]; + DataStreamType = ((PUCHAR)(IpxHeader+1))[1]; + } + +#endif + + // + // If this is a SYS packet with or without a request for ACK and + // has session data in it. + // + if (((ConnectionControlFlag == 0x80) || (ConnectionControlFlag == 0xc0)) && + (DataStreamType == 0x06)) { + + // + // This would be from the rip driver, if IncludedHeaderLength is + // 0. -- pradeepb + // + // At this point, we assume that total data length is in + // the same buffer as the others. + // + // + // real hacky way of doing things. One should be using + // FIELD_OFFSET(NB_SESSION, TotalDataLength) instead of 8. + // -- pradeepb + // + + if (IncludedHeaderLength < sizeof(IPX_HEADER) + 2) { + TotalDataLength = *(USHORT UNALIGNED *)(SecondBufferData + (sizeof(IPX_HEADER) + 8 - IncludedHeaderLength)); + } else { + TotalDataLength = ((USHORT UNALIGNED *)(IpxHeader+1))[4]; + } + + if (TotalDataLength == 0) { + return; + } + } + + } else { + + UCHAR KeepAliveSignature; + + + // + // Now see if it is an NCP keep alive. + // + + if (PacketLength == sizeof(IPX_HEADER) + 2) { + + // + // if from rip + // + if (IncludedHeaderLength <= sizeof(IPX_HEADER) + 1) { + + + // + // Get the second buffer + // +#if 0 + if (SecondBuffer == NULL) { + + // + // Get the second buffer in the packet. + // + + NdisQueryPacket(Packet, NULL, NULL, &SecondBuffer, NULL); + ThirdBuffer = NDIS_BUFFER_LINKAGE(SecondBuffer); + NdisQueryBuffer (ThirdBuffer, (PVOID *)&ThirdBufferData, &ThirdBufferLength); + + } +#endif + KeepAliveSignature = SecondBufferData[sizeof(IPX_HEADER) + 1 - IncludedHeaderLength]; + + } else { + + // + // will we ever come here - pradeepb? + // + KeepAliveSignature = SecondBufferData[sizeof(IPX_HEADER) + 1 - IncludedHeaderLength]; +#if 0 + KeepAliveSignature = ((PUCHAR)(IpxHeader+1))[1]; +#endif + + } + + if ((KeepAliveSignature == '?') || + (KeepAliveSignature == 'Y')) { + return; + } + + } + + } + + // + // This was a normal packet, so reset this. + // + + Binding->WanInactivityCounter = 0; + +} /* IpxUpdateWanInactivityCounter */ +#endif + + +VOID +IpxUpdateWanInactivityCounter( + IN PBINDING Binding, + IN IPX_HEADER UNALIGNED * IpxHeader, + IN ULONG IncludedHeaderLength, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength + ) + +/*++ + +Routine Description: + + This routine is called when a frame is being sent on a WAN + line. It updates the inactivity counter for this binding + unless: + + - The frame is from the RIP socket + - The frame is from the SAP socket + - The frame is a netbios keep alive + - The frame is an NCP keep alive + + BUGBUG: Take the identifier as a parameter to optimize. + +Arguments: + + Binding - The binding the frame is sent on. + + IpxHeader - May contain the first bytes of the packet. + + IncludedHeaderLength - The number of packet bytes at IpxHeader. + + Packet - The full NDIS packet. + + PacketLength - The length of the packet. + +Return Value: + + None, but in some cases we return without resetting the + inactivity counter. + +Comments: Improve the instruction count here - pradeepb + +--*/ + +{ + USHORT SourceSocket; + PNDIS_BUFFER DataBuffer = NULL; + PUCHAR DataBufferData; + UINT DataBufferLength; + + + // + // First get the source socket. + // + SourceSocket = IpxHeader->SourceSocket; + if ((SourceSocket == RIP_SOCKET) || + (SourceSocket == SAP_SOCKET)) { + + return; + + } + + if (SourceSocket == NB_SOCKET) { + + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT TotalDataLength; + + // + // ConnectionControlFlag and DataStreamType will always follow + // IpxHeader + // + ConnectionControlFlag = ((PUCHAR)(IpxHeader+1))[0]; + DataStreamType = ((PUCHAR)(IpxHeader+1))[1]; + + // + // If this is a SYS packet with or without a request for ACK and + // has session data in it. + // + if (((ConnectionControlFlag == 0x80) || (ConnectionControlFlag == 0xc0)) && + (DataStreamType == 0x06)) { + + // + // TotalDataLength is in the same buffer. + // + TotalDataLength = ((USHORT UNALIGNED *)(IpxHeader+1))[4]; + + // + // No need to update the WAN activity counter + // + if (TotalDataLength == 0) { + return; + } + } + + } else { + + UCHAR KeepAliveSignature; + + + // + // Now see if it is an NCP keep alive. It can be from rip or from + // NCP on this machine + // + // NOTE: We cannot come here for an SMB packet - [IsaacHe - 12/15]. + // + if (PacketLength == sizeof(IPX_HEADER) + 2) { + + // + // Get the client data buffer + // + NdisQueryPacket(Packet, NULL, NULL, &DataBuffer, NULL); + + // + // If the included header length is 0, it is from rip + // + if (IncludedHeaderLength == 0) { + + // + // Get the second buffer in the packet. The second buffer + // contains the IPX header + other stuff + // + DataBuffer = NDIS_BUFFER_LINKAGE(DataBuffer); + } else { + // + // Get the third buffer in the packet. + // + DataBuffer = NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE(DataBuffer)); + } + + NdisQueryBuffer (DataBuffer, (PVOID *)&DataBufferData, &DataBufferLength); + CTEAssert(DataBufferData); + + if (IncludedHeaderLength == 0) { + KeepAliveSignature = DataBufferData[sizeof(IPX_HEADER) + 1]; + } else { + KeepAliveSignature = DataBufferData[1]; + } + + if ((KeepAliveSignature == '?') || + (KeepAliveSignature == 'Y')) { + return; + } + } + } + + + // + // This was a normal packet, so reset this. + // + + Binding->WanInactivityCounter = 0; + +} /* IpxUpdateWanInactivityCounter */ + +#if DBG +ULONG IpxPadCount = 0; +#endif + +#ifdef _PNP_POWER + +NDIS_STATUS +IpxSendFramePreFwd( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine is called by NB/SPX to send a frame. + +Arguments: + + LocalTarget - The local target of the send - NB will have the LocalTarget in the Send_Reserved part + of the packet; SPX will not now, but will later. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + Return of IpxSendFrame + + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + PDEVICE Device = IpxDevice; + PIPX_HEADER TempHeader; + + NdisQueryPacket (Packet, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + + // + // Set this now, will change later + // + Reserved->CurrentNicId = 0; + + // + // Copy the LocalTarget into the send reserved area of the packet. + // + Reserved->LocalTarget = *LocalTarget; + + // + // If the NicId in the handle is 0, then this could be a send + // over all NICs in the case of NB/SPX. + // + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == 0) { + CTEAssert(Reserved->Identifier == IDENTIFIER_NB || + Reserved->Identifier == IDENTIFIER_SPX); + + // + // Check the destination network in the IPX header. If this is 0, + // then we need to iterate the send over all NICs. + // + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + if ((*(UNALIGNED ULONG *)(TempHeader->DestinationNetwork) == 0) && + ((IPX_NODE_BROADCAST(TempHeader->DestinationNode)) || + (Reserved->Identifier == IDENTIFIER_SPX))) { + + // + // Start with the first NIC + // BUGBUG: Search for the first NIC + // + + IPX_DEBUG(SEND, ("Iteration over NICs started, reserved: %lx\n", Reserved)); + Reserved->CurrentNicId = 1; + Reserved->Net0SendSucceeded = FALSE; + + FILL_LOCAL_TARGET(&Reserved->LocalTarget, 1); + Reserved->PacketLength = PacketLength; + } else { + // + // If this is on the loopback adapter (Nic 0), queue it on the LoopbackQueue; + // if the LoopbackRtn is not started, start it now. + // + + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Mac.c: Packet: %x\n", Packet)); + + // + // Recalculate packet counts here. + // + + // + // Assume an 802_3802_2 header and use that length. + // + + // + // Adjust the MAC header's length to the right value + // + NdisAdjustBufferLength (HeaderBuffer, 17); + NdisRecalculatePacketCounts (Packet); + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); + + // + // The upper driver waits for the SendComplete. + // + return STATUS_PENDING; + } + + return IpxSendFrame ( + &Reserved->LocalTarget, + Packet, + PacketLength, + IncludedHeaderLength); + + } else { + return IpxSendFrame ( + LocalTarget, + Packet, + PacketLength, + IncludedHeaderLength); + } +} +#endif + + +NDIS_STATUS +IpxSendFrame( + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + BUGBUG: Check that Binding is not NULL. + +Arguments: + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PDEVICE Device = IpxDevice; + PUCHAR Header; + PBINDING Binding, MasterBinding; + PADAPTER Adapter; + ULONG TwoBytes; + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + ULONG HeaderLength=0; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + UCHAR SourceRoutingIdentifier; + ULONG HeaderSizeRequired; + PIPX_HEADER TempHeader; + USHORT PktLength; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + // + // Get the lock on the binding array + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + Binding = NIC_HANDLE_TO_BINDING(Device, &LocalTarget->NicHandle); + + if (Binding == NULL) { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IPX_DEBUG(PNP, ("Invalid NIC handle: %lx\n", LocalTarget->NicHandle)); + // + // [BUGBUGZZ] Return a unique error that NB/SPX see and re-query the NicId. + // + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + Adapter = Binding->Adapter; + + IpxReferenceAdapter(Adapter); + + // + // Release the lock + // + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + + // + // If this is on the loopback adapter (Nic 0), queue it on the LoopbackQueue; + // if the LoopbackRtn is not started, start it now. + // + if (LocalTarget->NicId == 0) { + + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Mac.c: Packet: %x\n", Packet)); + + // + // Assume an 802_3802_2 header and use that length. + // + + // + // Adjust the MAC header's length to the right value + // + // NdisAdjustBufferLength (HeaderBuffer, 17); + + NdisRecalculatePacketCounts (Packet); + + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); + + // + // The upper driver waits for the SendComplete. + // + return STATUS_PENDING; + } + + Binding = Device->Bindings[LocalTarget->NicId]; + + if (Binding == NULL) { + return STATUS_DEVICE_DOES_NOT_EXIST; // BUGBUG: Make this a separate switch that generally falls through? + } + Adapter = Binding->Adapter; +#endif _PNP_POWER + + // + // For IPX and other protocols that are guaranteed to have allocated + // the header from non-paged pool, use the buffer directly. For others, + // query the packet for the pointer to the MDL. + // + if (Reserved->Identifier >= IDENTIFIER_IPX) { + HeaderBuffer = Reserved->HeaderBuffer; + Header = Reserved->Header; + + } else { + NdisQueryPacket (Packet, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + } + + CTEAssert (Reserved->PaddingBuffer == NULL); + + // + // First move the packet around if needed. + // + + if (Reserved->Identifier < IDENTIFIER_IPX) { + + // + // Only RIP will have IncludedHeaderLength as 0. I don't know + // why we have the comment about RIP inside this if statement. + // + if (IncludedHeaderLength > 0) { + + // + // Spx can handle a virtual net as long as it is + // not 0. Netbios always needs to use the real address. + // We need to hack the ipx source address for packets + // which are sent by spx if we have a fake virtual + // net, and packets sent by netbios unless we are + // bound to only one card. + // + + // + // We handle binding sets as follows, based on who + // sent the frame to us: + // + // RIP: Since we only tell RIP about the masters at + // bind time, and hide slaves on indications, it should + // never be sending on a slave binding. Since RIP knows + // the real net and node of every binding we don't + // need to modify the packet at all. + // + // NB: For broadcasts we want to put the first card's + // address in the IPX source but round-robin the + // actual sends over all cards (broadcasts shouldn't + // be passed in with a slave's NIC ID). For directed + // packets, which may come in on a slave, we should + // put the slave's address in the IPX source. + // + // SPX: SPX does not send broadcasts. For directed + // frames we want to use the slave's net and node + // in the IPX source. + // + + if (Reserved->Identifier == IDENTIFIER_NB) { + + CTEAssert (IncludedHeaderLength >= sizeof(IPX_HEADER)); + + // + // Get the packet length from the ipx header. Compare with + // the max. allowed datagram size. + // + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + PktLength = ((TempHeader->PacketLength[0] << 8) | + (TempHeader->PacketLength[1])); + +// +// BUGBUG - Not the most efficient way to do this. NWLNKNB should do this. +// Doing it in ipx means doing it for all packets (even those sent on +// connections). Will remove this later when nwlnknb change has been +// tested. +// + + + if (PktLength > (Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER))) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + PktLength, + Binding->AnnouncedMaxDatagramSize + sizeof(IPX_HEADER))); + +#ifdef _PNP_POWER + // + // Dereference the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + return STATUS_INVALID_BUFFER_SIZE; + } + + if (Device->ValidBindings > 1) { + + + // + // Store this now, since even if we round-robin the + // actual send we want the binding set master's net + // and node in the IPX source address. + // + + *(UNALIGNED ULONG *)TempHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (TempHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + if (Binding->BindingSetMember) { + + if (IPX_NODE_BROADCAST(LocalTarget->MacAddress)) { + + // + // This is a broadcast, so we round-robin the + // sends through the binding set. + // +#ifdef _PNP_POWER + // + // [BUGBUGZZ]: We dont have a lock here - the masterbinding could be bogus + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + Adapter = Binding->Adapter; + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxReferenceAdapter(Adapter); +#endif + } + + } + } + + } else if (Reserved->Identifier == IDENTIFIER_SPX) { + + // + // Need to update this if we have multiple cards but + // a zero virtual net. + // + + if (Device->MultiCardZeroVirtual) { + + CTEAssert (IncludedHeaderLength >= sizeof(IPX_HEADER)); + + TempHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + *(UNALIGNED ULONG *)TempHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (TempHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + } + + } else { + + // + // For a rip packet it should not be in a binding set, + // or if it is it should be the master. + // +#if DBG + CTEAssert ((!Binding->BindingSetMember) || + (Binding->CurrentSendBinding)); +#endif + } + + +#if 0 + // + // There is a header included, we need to adjust it. + // The header will be at Device->IncludedHeaderOffset. + // + + if (LocalTarget->MacAddress[0] & Adapter->MacInfo.BroadcastMask) { + HeaderSizeRequired = Adapter->BcMcHeaderSizes[Binding->FrameType]; + } else { + HeaderSizeRequired = Adapter->DefHeaderSizes[Binding->FrameType]; + } + + if (HeaderSizeRequired != Device->IncludedHeaderOffset) { + + RtlMoveMemory( + &Header[HeaderSizeRequired], + &Header[Device->IncludedHeaderOffset], + IncludedHeaderLength); + } +#endif + } + } + + + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + + if (!Binding->LineUp) { + // + // Bug #17273 return proper error message + // + // return STATUS_DEVICE_DOES_NOT_EXIST; // BUGBUG: Make this a separate switch that generally falls through? +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + return STATUS_NETWORK_UNREACHABLE; + } + + if (Adapter->MacInfo.MediumAsync) { + + IPX_HEADER UNALIGNED * IpxHeader; + PNDIS_BUFFER IpxNdisBuff; + UINT IpxHeaderLen; + +#if 0 + // + // The header should have been moved here. + // + + CTEAssert(Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] == + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II]); + + + IpxHeader = (IPX_HEADER UNALIGNED *) + (&Header[Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II]]); +#endif + // + // The Ipx header is always the second ndis buffer in the mdl + // chain. Get it and then query the va of the same. + // + IpxNdisBuff = NDIS_BUFFER_LINKAGE(HeaderBuffer); + NdisQueryBuffer (IpxNdisBuff, (PVOID *)&IpxHeader, &IpxHeaderLen); +// IpxHeader = (IPX_HEADER UNALIGNED *) (&Header[MAC_HEADER_SIZE]); + + // + // If this is a type 20 name frame from Netbios and we are + // on a dialin WAN line, drop it if configured to. + // + // The 0x01 bit of DisableDialinNetbios controls + // internal->WAN packets, which we handle here. + // + // + + // + // SS# 33592: In case of iterative sends, the IncludedHeaderLength is not set properly + // since we dont keep track of the length that came in the first time (we track the PacketLength + // however). The included length field is used here for checking for NB_NAME_FRAMES, but elsewhere + // used only to distinguish between whether RIP or NB/SPX sent the packet (IncludedHeaderLen ==0 for RIP) + // The ideal solution here is to do way with this field altogether, but for the beta we will just use the + // PacketLength field for comparison here since we are assured that this will be equal to the InclHeaderLen + // for any type 0x14 packet that comes down from NB. + // + // BUGBUGZZ: Remove the IncludedHeaderLength field. + // + if ((!Binding->DialOutAsync) && + (Reserved->Identifier == IDENTIFIER_NB) && + // (IncludedHeaderLength == sizeof(IPX_HEADER) + 50) && // 50 == sizeof(NB_NAME_FRAME) + (PacketLength == sizeof(IPX_HEADER) + 50) && // 50 == sizeof(NB_NAME_FRAME) + ((Device->DisableDialinNetbios & 0x01) != 0) && + (IpxHeader->PacketType == 0x14)) { +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + return STATUS_SUCCESS; + } + + + // + // We do checks to see if we should reset the inactivity + // counter. We normally need to check for netbios + // session alives, packets from rip, packets from + // sap, and ncp keep alives. In fact sap and ncp + // packets don't come through here. + // + + IpxUpdateWanInactivityCounter( + Binding, + IpxHeader, + IncludedHeaderLength, + Packet, + PacketLength); + + RtlCopyMemory (Header, Binding->RemoteMacAddress.Address, 6); + + } else { + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + } + + RtlCopyMemory (Header+6, Binding->LocalMacAddress.Address, 6); + + switch (Binding->FrameType) { + + case ISN_FRAME_TYPE_802_2: + TwoBytes = PacketLength + 3; + Header[14] = 0xe0; + Header[15] = 0xe0; + Header[16] = 0x03; + HeaderLength = 17; + break; + case ISN_FRAME_TYPE_802_3: + TwoBytes = PacketLength; + HeaderLength = 14; + break; + case ISN_FRAME_TYPE_ETHERNET_II: + TwoBytes = Adapter->BindSap; + HeaderLength = 14; + break; + case ISN_FRAME_TYPE_SNAP: + TwoBytes = PacketLength + 8; + Header[14] = 0xaa; + Header[15] = 0xaa; + Header[16] = 0x03; + Header[17] = 0x00; + Header[18] = 0x00; + Header[19] = 0x00; + *(UNALIGNED USHORT *)(&Header[20]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + break; + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + + //BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + // + // Pad odd-length packets if needed. + // + + if ((((PacketLength + HeaderLength) & 1) != 0) && + (Device->EthernetPadToEven) && + (!Adapter->MacInfo.MediumAsync)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + if ( CurBuffer == HeaderBuffer ) { + BufferLength++; // Just bump this length + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + if (TwoBytes != Adapter->BindSap) { + CTEAssert(TwoBytes & 1); + TwoBytes += 1; + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + } + +#if DBG + ++IpxPadCount; +#endif + } + + break; + + case NdisMedium802_5: + + if (Reserved->Identifier >= IDENTIFIER_IPX) { + + DestinationType = Reserved->DestinationType; + SourceRoutingIdentifier = IDENTIFIER_IPX; + + } else { + + if (LocalTarget->MacAddress[0] & 0x80) { + if (*(UNALIGNED ULONG *)(&LocalTarget->MacAddress[2]) != 0xffffffff) { + DestinationType = DESTINATION_MCAST; + } else { + DestinationType = DESTINATION_BCAST; + } + } else { + DestinationType = DESTINATION_DEF; + } + SourceRoutingIdentifier = Reserved->Identifier; + + } + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + SourceRoutingIdentifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + +// PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; +// RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + } + +#if 0 + if (SourceRoutingLength != 0) { + + // PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } +#endif + + } + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Binding->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + switch (Binding->FrameType) { + case ISN_FRAME_TYPE_802_2: + case ISN_FRAME_TYPE_802_3: + case ISN_FRAME_TYPE_ETHERNET_II: + Header[0] = 0xe0; + Header[1] = 0xe0; + Header[2] = 0x03; + HeaderLength = 17; + break; + case ISN_FRAME_TYPE_SNAP: + Header[0] = 0xaa; + Header[1] = 0xaa; + Header[2] = 0x03; + Header[3] = 0x00; + Header[4] = 0x00; + Header[5] = 0x00; + *(UNALIGNED USHORT *)(&Header[6]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + break; + } + +// BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + + break; + + case NdisMediumFddi: + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Binding->LocalMacAddress.Address, 6); + + switch (Binding->FrameType) { + case ISN_FRAME_TYPE_802_3: + HeaderLength = 13; + break; + case ISN_FRAME_TYPE_802_2: + case ISN_FRAME_TYPE_ETHERNET_II: + Header[13] = 0xe0; + Header[14] = 0xe0; + Header[15] = 0x03; + HeaderLength = 16; + break; + case ISN_FRAME_TYPE_SNAP: + Header[13] = 0xaa; + Header[14] = 0xaa; + Header[15] = 0x03; + Header[16] = 0x00; + Header[17] = 0x00; + Header[18] = 0x00; + *(UNALIGNED USHORT *)(&Header[19]) = Adapter->BindSapNetworkOrder; + HeaderLength = 21; + break; + } + +// BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + break; + + case NdisMediumArcnet878_2: + + // + // Convert broadcast address to 0 (the arcnet broadcast). + // + + Header[0] = Binding->LocalMacAddress.Address[5]; + if (LocalTarget->MacAddress[5] == 0xff) { + Header[1] = 0x00; + } else { + Header[1] = LocalTarget->MacAddress[5]; + } + Header[2] = ARCNET_PROTOCOL_ID; + + // + // Binding->FrameType is not used. + // + + HeaderLength = 3; +// BufferLength = IncludedHeaderLength + HeaderLength; + BufferLength = HeaderLength; + + break; + + } + + // + // Adjust the MAC header's length to the right value + // + NdisAdjustBufferLength (HeaderBuffer, BufferLength); + NdisRecalculatePacketCounts (Packet); + +#if 0 + { + PMDL mdl; + mdl = (PMDL)NDIS_BUFFER_LINKAGE(HeaderBuffer); + if (mdl) { + + KdPrint(("**Bytecount %x %x\n",mdl->ByteCount, mdl)); + if ((LONG)mdl->ByteCount < 0) { + DbgBreakPoint(); + } + } + } +#endif + +#if DBG + { + ULONG SendFlag; + ULONG Temp; + PNDIS_BUFFER FirstPacketBuffer; + PNDIS_BUFFER SecondPacketBuffer; + IPX_HEADER DumpHeader; + UCHAR DumpData[14]; + + NdisQueryPacket (Packet, NULL, NULL, &FirstPacketBuffer, NULL); + SecondPacketBuffer = NDIS_BUFFER_LINKAGE(FirstPacketBuffer); + TdiCopyMdlToBuffer(SecondPacketBuffer, 0, &DumpHeader, 0, sizeof(IPX_HEADER), &Temp); + if (Reserved->Identifier == IDENTIFIER_NB) { + SendFlag = IPX_PACKET_LOG_SEND_NB; + } else if (Reserved->Identifier == IDENTIFIER_SPX) { + SendFlag = IPX_PACKET_LOG_SEND_SPX; + } else if (Reserved->Identifier == IDENTIFIER_RIP) { + SendFlag = IPX_PACKET_LOG_SEND_RIP; + } else { + if (DumpHeader.SourceSocket == IpxPacketLogSocket) { + SendFlag = IPX_PACKET_LOG_SEND_SOCKET | IPX_PACKET_LOG_SEND_OTHER; + } else { + SendFlag = IPX_PACKET_LOG_SEND_OTHER; + } + } + +#if 0 + if (PACKET_LOG(SendFlag)) { + + TdiCopyMdlToBuffer(SecondPacketBuffer, sizeof(IPX_HEADER), &DumpData, 0, 14, &Temp); + + IpxLogPacket( + TRUE, + LocalTarget->MacAddress, + Binding->LocalMacAddress.Address, + (USHORT)PacketLength, + &DumpHeader, + DumpData); + + } +#endif + } +#endif + + ++Device->Statistics.PacketsSent; + + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + +#ifdef _PNP_POWER + if (Status != STATUS_PENDING) { + + if (Reserved->PaddingBuffer) { + + // + // Remove padding if it was done. + // + + if ( Reserved->PreviousTail ) { + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT LastBufferLength; + + NdisQueryBuffer( LastBuffer, NULL, &LastBufferLength ); + NdisAdjustBufferLength( LastBuffer, (LastBufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (Packet); + } + } + + // + // If this was an NB/SPX packet, and there was an + // iterative send going on, then call the SendComplete + // handler. + // + if ((Reserved->Identifier == IDENTIFIER_NB || + Reserved->Identifier == IDENTIFIER_SPX) && + (Reserved->CurrentNicId)) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + Status); + + Status = STATUS_PENDING; + } + } +#else + if ((Status != STATUS_PENDING) && + (Reserved->PaddingBuffer)) { + + // + // Remove padding if it was done. + // + + if ( Reserved->PreviousTail ) { + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT LastBufferLength; + + NdisQueryBuffer( LastBuffer, NULL, &LastBufferLength ); + NdisAdjustBufferLength( LastBuffer, (LastBufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (Packet); + } + } +#endif + +#ifdef _PNP_POWER + // + // Derefernce the binding and adapter + // + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IpxDereferenceAdapter(Adapter); +#endif _PNP_POWER + + return Status; + +} /* IpxSendFrame */ + + +NDIS_STATUS +IpxSendFrame802_3802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_802_3 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + + // + // BUGBUG: Remove the IncludedHeaderLength parameter from here + // +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + LONG HeaderLength; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); + IPX_DEBUG(SEND,("Backfill request 802_3802_3!! %x %x %x\n", Packet, Reserved, Reserved->HeaderBuffer)); +#endif + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) != 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++PacketLength; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(PacketLength / 256); + Header[13] = (UCHAR)(PacketLength % 256); + + //NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3802_3 */ + + +NDIS_STATUS +IpxSendFrame802_3802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + ULONG TwoBytes; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 17, Packet); + IPX_DEBUG(SEND, ("Backfill request 802_3802_3!! %x %x %x\n", Packet, Reserved, Reserved->HeaderBuffer)); + IPX_DEBUG(SEND, ("packet=%x, usermdl %x\n",Packet,Reserved->HeaderBuffer)); +#endif + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + TwoBytes = PacketLength + 3; + Header[14] = 0xe0; + Header[15] = 0xe0; + Header[16] = 0x03; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) == 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0 ) { +#if BACK_FILL + if (0) { +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++TwoBytes; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 17); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 17); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3802_2 */ + + +NDIS_STATUS +IpxSendFrame802_3EthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_ETHERNET_II FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); +#endif BACK_FILL + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + *(UNALIGNED USHORT *)(&Header[12]) = Adapter->BindSapNetworkOrder; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) != 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + +#if DBG + ++IpxPadCount; +#endif + + } + + // NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3EthernetII */ + + +NDIS_STATUS +IpxSendFrame802_3Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_3 FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + ULONG TwoBytes; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 22, Packet); +#endif BACK_FILL + + RtlCopyMemory (Header, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+6, Adapter->LocalMacAddress.Address, 6); + + TwoBytes = PacketLength + 8; + Header[14] = 0xaa; + Header[15] = 0xaa; + Header[16] = 0x03; + Header[17] = 0x00; + Header[18] = 0x00; + Header[19] = 0x00; + *(UNALIGNED USHORT *)(&Header[20]) = Adapter->BindSapNetworkOrder; + + // + // Pad odd-length packets if needed. + // + + if (((PacketLength & 1) == 0) && + (IpxDevice->EthernetPadToEven)) { + + PNDIS_BUFFER CurBuffer; + PIPX_PADDING_BUFFER PaddingBuffer = IpxPaddingBuffer; + UINT Offset; + UINT LastBufferLength; + + // + // Find the tail of the current packet. + // + + CurBuffer = Reserved->HeaderBuffer; + while (NDIS_BUFFER_LINKAGE(CurBuffer) != NULL) { + CurBuffer = NDIS_BUFFER_LINKAGE(CurBuffer); + } + + // + // If the last byte of the last NDIS_BUFFER is not at the end of + // the page, then we can simply increase the NDIS_BUFFER ByteCount + // by one. + // Otherwise, we must use the global padding buffer. + // + + NdisQueryBufferOffset( CurBuffer, &Offset, &LastBufferLength ); + + if ( ((Offset + LastBufferLength) & (PAGE_SIZE - 1)) != 0) { + +#if BACK_FILL + if (0) { + +#else + if ( CurBuffer == Reserved->HeaderBuffer ) { + IncludedHeaderLength++; // Just bump this length +#endif + } else { + NdisAdjustBufferLength( CurBuffer, (LastBufferLength + 1) ); + + Reserved->PreviousTail = NULL; + Reserved->PaddingBuffer = (PIPX_PADDING_BUFFER)CurBuffer; + } + + } else { + + CTEAssert (NDIS_BUFFER_LINKAGE(PaddingBuffer->NdisBuffer) == NULL); + + Reserved->PreviousTail = CurBuffer; + NDIS_BUFFER_LINKAGE (CurBuffer) = PaddingBuffer->NdisBuffer; + Reserved->PaddingBuffer = PaddingBuffer; + + } + + ++TwoBytes; +#if DBG + ++IpxPadCount; +#endif + + } + + Header[12] = (UCHAR)(TwoBytes / 256); + Header[13] = (UCHAR)(TwoBytes % 256); + + // NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 22); +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 22); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 22); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_3Snap */ + + +NDIS_STATUS +IpxSendFrame802_5802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_5 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PBINDING Binding = Adapter->Bindings[ISN_FRAME_TYPE_802_2]; + PUCHAR Header; + ULONG HeaderLength; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 17, Packet); +#endif BACK_FILL + + DestinationType = Reserved->DestinationType; + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + Reserved->Identifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + + //PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + PUCHAR IpxHeader = Header + MAC_HEADER_SIZE; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; +// RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + } + +#if 0 + if (SourceRoutingLength != 0) { + + PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } +#endif + } + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Adapter->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + Header[0] = 0xe0; + Header[1] = 0xe0; + Header[2] = 0x03; + HeaderLength = 17; + + //BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, BufferLength); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, BufferLength); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_5802_2 */ + + +NDIS_STATUS +IpxSendFrame802_5Snap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUM802_5 FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PBINDING Binding = Adapter->Bindings[ISN_FRAME_TYPE_SNAP]; + PUCHAR Header; + ULONG HeaderLength; + UCHAR SourceRoutingBuffer[18]; + PUCHAR SourceRouting; + ULONG SourceRoutingLength; + NDIS_STATUS Status; + ULONG BufferLength; + UCHAR DestinationType; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 22, Packet); +#endif BACK_FILL + + DestinationType = Reserved->DestinationType; + + if (DestinationType == DESTINATION_DEF) { + + MacLookupSourceRouting( + Reserved->Identifier, + Binding, + LocalTarget->MacAddress, + SourceRoutingBuffer, + &SourceRoutingLength); + + if (SourceRoutingLength != 0) { + +// PUCHAR IpxHeader = Header + Binding->DefHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + SourceRouting = SourceRoutingBuffer; + // RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + + } else { + + // + // For these packets we assume that the header is in the + // right place. + // + + if (!Adapter->SourceRouting) { + + SourceRoutingLength = 0; + + } else { + + if (DestinationType == DESTINATION_BCAST) { + + if (Binding->AllRouteBroadcast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } else { + + CTEAssert (DestinationType == DESTINATION_MCAST); + + if (Binding->AllRouteMulticast) { + SourceRouting = AllRouteSourceRouting; + } else { + SourceRouting = SingleRouteSourceRouting; + } + SourceRoutingLength = 2; + + } + + if (SourceRoutingLength != 0) { + + // PUCHAR IpxHeader = Header + Binding->BcMcHeaderSize; + + // + // Need to slide the header down to accomodate the SR. + // + + // RtlMoveMemory (IpxHeader+SourceRoutingLength, IpxHeader, IncludedHeaderLength); + } + } + } + + Header[0] = TR_PREAMBLE_AC; + Header[1] = TR_PREAMBLE_FC; + RtlCopyMemory (Header+2, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+8, Adapter->LocalMacAddress.Address, 6); + + if (SourceRoutingLength != 0) { + Header[8] |= TR_SOURCE_ROUTE_FLAG; + RtlCopyMemory (Header+14, SourceRouting, SourceRoutingLength); + } + + Header += (14 + SourceRoutingLength); + + Header[0] = 0xaa; + Header[1] = 0xaa; + Header[2] = 0x03; + Header[3] = 0x00; + Header[4] = 0x00; + Header[5] = 0x00; + *(UNALIGNED USHORT *)(&Header[6]) = Adapter->BindSapNetworkOrder; + HeaderLength = 22; + + //BufferLength = IncludedHeaderLength + HeaderLength + SourceRoutingLength; + BufferLength = HeaderLength + SourceRoutingLength; + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, BufferLength); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, BufferLength); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrame802_5Snap */ + + +NDIS_STATUS +IpxSendFrameFddi802_3( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_802_3 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 13, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 13); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 13); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 13); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddi802_3 */ + + +NDIS_STATUS +IpxSendFrameFddi802_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 16, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + + Header[13] = 0xe0; + Header[14] = 0xe0; + Header[15] = 0x03; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 16); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 16); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 16); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddi802_2 */ + + +NDIS_STATUS +IpxSendFrameFddiSnap( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMFDDI FRAMES IN + THE ISN_FRAME_TYPE_SNAP FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 21, Packet); +#endif BACK_FILL + + Header[0] = FDDI_HEADER_BYTE; + RtlCopyMemory (Header+1, LocalTarget->MacAddress, 6); + RtlCopyMemory (Header+7, Adapter->LocalMacAddress.Address, 6); + + Header[13] = 0xaa; + Header[14] = 0xaa; + Header[15] = 0x03; + Header[16] = 0x00; + Header[17] = 0x00; + Header[18] = 0x00; + *(UNALIGNED USHORT *)(&Header[19]) = Adapter->BindSapNetworkOrder; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 21); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 21); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 21); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddiSnap */ + + +NDIS_STATUS +IpxSendFrameArcnet878_2( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMARCNET878_2 FRAMES IN + THE ISN_FRAME_TYPE_802_2 FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 3, Packet); +#endif BACK_FILL + // + // Convert broadcast address to 0 (the arcnet broadcast). + // + + Header[0] = Adapter->LocalMacAddress.Address[5]; + if (LocalTarget->MacAddress[5] == 0xff) { + Header[1] = 0x00; + } else { + Header[1] = LocalTarget->MacAddress[5]; + } + Header[2] = ARCNET_PROTOCOL_ID; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 3); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 3); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 3); +#endif + + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); + + return Status; + +} /* IpxSendFrameFddiArcnet878_2 */ + + +NDIS_STATUS +IpxSendFrameWanEthernetII( + IN PADAPTER Adapter, + IN PIPX_LOCAL_TARGET LocalTarget, + IN PNDIS_PACKET Packet, + IN ULONG PacketLength, + IN ULONG IncludedHeaderLength + ) + +/*++ + +Routine Description: + + This routine constructs a MAC header in a packet and submits + it to the appropriate NDIS driver. + + It is assumed that the first buffer in the packet contains + an IPX header at an offset based on the media type. This + IPX header is moved around if needed. + + THIS FUNCTION ONLY CONSTRUCT NDISMEDIUMWAN FRAMES IN + THE ISN_FRAME_TYPE_ETHERNET_II FORMAT. + +Arguments: + + Adapter - The adapter on which we are sending. + + LocalTarget - The local target of the send. + + Packet - The NDIS packet. + + PacketLength - The length of the packet, starting at the IPX header. + + IncludedHeaderLength - The length of the header included in the + first buffer that needs to be moved if it does not wind up + MacHeaderOffset bytes into the packet. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + PUCHAR Header; + NDIS_STATUS Status; + +#ifdef _PNP_POWER + PBINDING Binding; + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&IpxDevice->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(IpxDevice, NIC_FROM_LOCAL_TARGET(LocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + IPX_FREE_LOCK1(&IpxDevice->BindAccessLock, LockHandle1); + +#else + PBINDING Binding = IpxDevice->Bindings[LocalTarget->NicId]; +#endif _PNP_POWER + + if (Binding->LineUp) { + + Header = Reserved->Header; + +#if BACK_FILL + BACK_FILL_HEADER(Header, Reserved, 14, Packet); + + // + // Call UpdateWanInactivity only if this is not a backfill packet, since + // SMB server does not do KeepAlives. In case, of backfilled packets, reset + // the counter regardless. + // + if (!Reserved->BackFill) { + IpxUpdateWanInactivityCounter( + Binding, + (IPX_HEADER UNALIGNED *)(Header + IpxDevice->IncludedHeaderOffset), + IncludedHeaderLength, + Packet, + PacketLength); + } else { + Binding->WanInactivityCounter = 0; + } + +#else + // + // We do checks to see if we should reset the inactivity + // counter. We normally need to check for netbios + // session alives, packets from rip, packets from + // sap, and ncp keep alives. In fact netbios packets + // and rip packets don't come through here. + // + + IpxUpdateWanInactivityCounter( + Binding, + (IPX_HEADER UNALIGNED *)(Header + IpxDevice->IncludedHeaderOffset), + IncludedHeaderLength, + Packet, + PacketLength); +#endif BACK_FILL + + RtlCopyMemory (Header, Binding->RemoteMacAddress.Address, 6); + RtlCopyMemory (Header+6, Binding->LocalMacAddress.Address, 6); + + *(UNALIGNED USHORT *)(&Header[12]) = Adapter->BindSapNetworkOrder; + +// NdisAdjustBufferLength (Reserved->HeaderBuffer, IncludedHeaderLength + 14); + +#if BACK_FILL + BACK_FILL_ADJUST_BUFFER_LENGTH(Reserved, 14); +#else + NdisAdjustBufferLength (Reserved->HeaderBuffer, 14); +#endif + NdisRecalculatePacketCounts (Packet); + + NdisSend( + &Status, + Adapter->NdisBindingHandle, + Packet); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return Status; + + } else { + + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return STATUS_NETWORK_UNREACHABLE; + } + +} /* IpxSendFrameWanEthernetII */ + + +VOID +MacUpdateSourceRouting( + IN ULONG Database, + IN PADAPTER Adapter, + IN PUCHAR MacHeader, + IN ULONG MacHeaderLength + ) + +/*++ + +Routine Description: + + This routine is called when a valid IPX frame is received from + a remote. It gives the source routing database a change to + update itself to include information about this remote. + +Arguments: + + Database - The "database" to use (IPX, SPX, NB, RIP). + + Adapter - The adapter the frame was received on. + + MacHeader - The MAC header of the received frame. + + MacHeaderLength - The length of the MAC header. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + ULONG Hash; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + CTEAssert ((Database >= 0) && (Database <= 3)); + + // + // If this adapter is configured for no source routing, don't + // need to do anything. + // + + if (!Adapter->SourceRouting) { + return; + } + + // + // See if this source routing is relevant. We don't + // care about two-byte source routing since that + // indicates it did not cross a router. If there + // is nothing in the database, then don't add + // this if it is minimal (if it is not, we need + // to add it so we will find it on sending). + // + + if ((Adapter->SourceRoutingEmpty[Database]) && + (MacHeaderLength <= 16)) { + return; + } + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + // + // Try to find this address in the database. + // + + Hash = MacSourceRoutingHash (MacHeader+8); + Current = Adapter->SourceRoutingHeads[Database][Hash]; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacHeader+8, Current->MacAddress, &Result); + + if (Result == 0) { + + // + // We found routing for this node. If the data is the + // same as what we have, update the time since used to + // prevent aging. + // + + if ((Current->SourceRoutingLength == MacHeaderLength-14) && + (RtlEqualMemory (Current->SourceRouting, MacHeader+14, MacHeaderLength-14))) { + + Current->TimeSinceUsed = 0; + } + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + + } else { + + Current = Current->Next; + } + + } + + // + // Not found, insert a new node at the front of the list. + // + + Current = (PSOURCE_ROUTE)IpxAllocateMemory (SOURCE_ROUTE_SIZE(MacHeaderLength-14), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + if (Current == (PSOURCE_ROUTE)NULL) { + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + } + + Current->Next = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current; + + Adapter->SourceRoutingEmpty[Database] = FALSE; + + RtlCopyMemory (Current->MacAddress, MacHeader+8, 6); + Current->MacAddress[0] &= 0x7f; + Current->SourceRoutingLength = (UCHAR)(MacHeaderLength - 14); + RtlCopyMemory (Current->SourceRouting, MacHeader+14, MacHeaderLength - 14); + + Current->TimeSinceUsed = 0; + + IPX_DEBUG (SOURCE_ROUTE, ("Adding source route %lx for %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + Current, Current->MacAddress[0], Current->MacAddress[1], + Current->MacAddress[2], Current->MacAddress[3], + Current->MacAddress[4], Current->MacAddress[5])); + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacUpdateSourceRouting */ + + +VOID +MacLookupSourceRouting( + IN ULONG Database, + IN PBINDING Binding, + IN UCHAR MacAddress[6], + IN OUT UCHAR SourceRouting[18], + OUT PULONG SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine looks up a target address in the adapter's + source routing database to see if source routing information + needs to be added to the frame. + +Arguments: + + Database - The "database" to use (IPX, SPX, NB, RIP). + + Binding - The binding the frame is being sent on. + + MacAddress - The destination address. + + SourceRouting - Buffer to hold the returned source routing info. + + SourceRoutingLength - The returned source routing length. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + PADAPTER Adapter = Binding->Adapter; + ULONG Hash; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + // + // If this adapter is configured for no source routing, don't + // insert any. + // + + if (!Adapter->SourceRouting) { + *SourceRoutingLength = 0; + return; + } + + // + // See if source routing has not been important so far. + // + // BUGBUG: This is wrong because we may be sending a directed + // packet to somebody on the other side of a router, without + // ever having received a routed packet. We fix this for the + // moment by only setting SourceRoutingEmpty for netbios + // which uses broadcasts for discovery. + // + + if (Adapter->SourceRoutingEmpty[Database]) { + *SourceRoutingLength = 0; + return; + } + + Hash = MacSourceRoutingHash (MacAddress); + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + Current = Adapter->SourceRoutingHeads[Database][Hash]; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacAddress, Current->MacAddress, &Result); + + if (Result == 0) { + + // + // We found routing for this node. + // + + if (Current->SourceRoutingLength <= 2) { + *SourceRoutingLength = 0; + } else { + RtlCopyMemory (SourceRouting, Current->SourceRouting, Current->SourceRoutingLength); + SourceRouting[0] = (SourceRouting[0] & TR_LENGTH_MASK); + SourceRouting[1] = (SourceRouting[1] ^ TR_DIRECTION_MASK); + *SourceRoutingLength = Current->SourceRoutingLength; + } + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + return; + + } else { + + Current = Current->Next; + + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + + // + // We did not find this node, use the default. + // + + if (Binding->AllRouteDirected) { + RtlCopyMemory (SourceRouting, AllRouteSourceRouting, 2); + } else { + RtlCopyMemory (SourceRouting, SingleRouteSourceRouting, 2); + } + *SourceRoutingLength = 2; + +} /* MacLookupSourceRouting */ + + +VOID +MacSourceRoutingTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the source routing timer expires. + It is called every minute. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PADAPTER Adapter; + PBINDING Binding; + PSOURCE_ROUTE Current, OldCurrent, Previous; + UINT i, j, k; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + +#ifdef _PNP_POWER + + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + // + // Get a lock on the access path. + // + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + ++Device->SourceRoutingTime; + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + if (Binding = NIC_ID_TO_BINDING(Device, i)) { +#else + ++Device->SourceRoutingTime; + + for (i = 1; i <= Device->ValidBindings; i++) { + + if (Binding = Device->Bindings[i]) { +#endif _PNP_POWER + + Adapter = Binding->Adapter; + + if (Adapter->LastSourceRoutingTime != Device->SourceRoutingTime) { + + // + // We need to scan this adapter's source routing + // tree for stale routes. To simplify the scan we + // only delete entries that have at least one + // child that is NULL. + // + + Adapter->LastSourceRoutingTime = Device->SourceRoutingTime; + + for (j = 0; j < IDENTIFIER_TOTAL; j++) { + + for (k = 0; k < SOURCE_ROUTE_HASH_SIZE; k++) { + + if (Adapter->SourceRoutingHeads[j][k] == (PSOURCE_ROUTE)NULL) { + continue; + } + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + Current = Adapter->SourceRoutingHeads[j][k]; + Previous = (PSOURCE_ROUTE)NULL; + + while (Current != (PSOURCE_ROUTE)NULL) { + + ++Current->TimeSinceUsed; + + if (Current->TimeSinceUsed >= Device->SourceRouteUsageTime) { + + // + // A stale entry needs to be aged. + // + + if (Previous) { + Previous->Next = Current->Next; + } else { + Adapter->SourceRoutingHeads[j][k] = Current->Next; + } + + OldCurrent = Current; + Current = Current->Next; + + IPX_DEBUG (SOURCE_ROUTE, ("Aging out source-route entry %lx\n", OldCurrent)); + IpxFreeMemory (OldCurrent, SOURCE_ROUTE_SIZE (OldCurrent->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + } else { + + Previous = Current; + Current = Current->Next; + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + + } // for loop through the database's hash list + + } // for loop through the adapter's four databases + + } // if adapter's database needs to be checked + + } // if binding exists + + } // for loop through every binding + } + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + + // + // Now restart the timer unless we should not (which means + // we are being unloaded). + // + + if (Device->SourceRoutingUsed) { + + CTEStartTimer( + &Device->SourceRoutingTimer, + 60000, // one minute timeout + MacSourceRoutingTimeout, + (PVOID)Device); + + } else { + + IpxDereferenceDevice (Device, DREF_SR_TIMER); + } + +} /* MacSourceRoutingTimeout */ + + +VOID +MacSourceRoutingRemove( + IN PBINDING Binding, + IN UCHAR MacAddress[6] + ) + +/*++ + +Routine Description: + + This routine is called by the IPX action handler when an + IPXROUTE use has specified that source routing for a given + MAC address should be removed. + +Arguments: + + Binding - The binding to modify. + + MacAddress - The MAC address to remove. + +Return Value: + + None. + +--*/ + +{ + + PSOURCE_ROUTE Current, Previous; + PADAPTER Adapter = Binding->Adapter; + ULONG Hash; + ULONG Database; + LONG Result; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Scan through to find the matching entry in each database. + // + + Hash = MacSourceRoutingHash (MacAddress); + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Previous = NULL; + + while (Current != (PSOURCE_ROUTE)NULL) { + + IPX_NODE_COMPARE (MacAddress, Current->MacAddress, &Result); + + if (Result == 0) { + + if (Previous) { + Previous->Next = Current->Next; + } else { + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + } + + IPX_DEBUG (SOURCE_ROUTE, ("IPXROUTE freeing source-route entry %lx\n", Current)); + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + break; + + } else { + + Previous = Current; + Current = Current->Next; + + } + + } + + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacSourceRoutingRemove */ + + +VOID +MacSourceRoutingClear( + IN PBINDING Binding + ) + +/*++ + +Routine Description: + + This routine is called by the IPX action handler when an + IPXROUTE use has specified that source routing for a given + binding should be cleared entirely. + +Arguments: + + Binding - The binding to be cleared. + + MacAddress - The MAC address to remove. + +Return Value: + + None. + +--*/ + +{ + PSOURCE_ROUTE Current; + PADAPTER Adapter = Binding->Adapter; + ULONG Database, Hash; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Scan through and remove every entry in the database. + // + + IPX_GET_LOCK (&Adapter->Lock, &LockHandle); + + for (Database = 0; Database < IDENTIFIER_TOTAL; Database++) { + + for (Hash = 0; Hash < SOURCE_ROUTE_HASH_SIZE; Hash++) { + + while (Adapter->SourceRoutingHeads[Database][Hash]) { + + Current = Adapter->SourceRoutingHeads[Database][Hash]; + Adapter->SourceRoutingHeads[Database][Hash] = Current->Next; + + IpxFreeMemory (Current, SOURCE_ROUTE_SIZE (Current->SourceRoutingLength), MEMORY_SOURCE_ROUTE, "SourceRouting"); + + } + } + } + + IPX_FREE_LOCK (&Adapter->Lock, LockHandle); + +} /* MacSourceRoutingClear */ + + + diff --git a/private/ntos/tdi/isnp/ipx/mac.h b/private/ntos/tdi/isnp/ipx/mac.h new file mode 100644 index 000000000..a88e77ecd --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mac.h @@ -0,0 +1,44 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + mac.h + +Abstract: + + This header file defines manifest constants and necessary macros for use + by transports dealing with multiple MAC cards through the NDIS interface. + +Revision History: + +--*/ + + +// +// We need this to define information about the MAC. Note that +// it is a strange structure in that the first four elements +// are for use internally by the mac.c routines, while the +// DeviceContext knows about and uses the last two. +// + +typedef struct _NDIS_INFORMATION { + + NDIS_MEDIUM MediumType; + NDIS_MEDIUM RealMediumType; + BOOLEAN SourceRouting; + BOOLEAN MediumAsync; + UCHAR BroadcastMask; + ULONG CopyLookahead; + ULONG MacOptions; + ULONG MinHeaderLength; + ULONG MaxHeaderLength; + +} NDIS_INFORMATION, * PNDIS_INFORMATION; + + +#define TR_SOURCE_ROUTE_FLAG 0x80 + +#define ARCNET_PROTOCOL_ID 0xFA + diff --git a/private/ntos/tdi/isnp/ipx/mp/makefile b/private/ntos/tdi/isnp/ipx/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf b/private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf new file mode 100644 index 000000000..0c4359235 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mp/nwlnkipx.prf @@ -0,0 +1,89 @@ +IpxTdiSendDatagram@8 +IpxReceiveIndicationNew@36 +IpxSendFrame@16 +IpxReceiveComplete@4 +IpxSendFrame802_3802_2@20 +IpxReceivePacket@8 +IpxDerefAddressSync@4 +IpxSendComplete@12 +IpxSendFramePreFwd@16 +IpxReceiveIndication@28 +IpxInitializeBackFillPacket@12 +IpxpAllocateMemory@12 +IpxPopBackFillPacket@4 +IpxAllocateBackFillPool@4 +RipLongTimeout@8 +CTEStartTimer@16 +TdiCopyBufferToMdl@24 +IpxTdiQueryInformation@8 +IpxVerifyAddressFile@4 +IpxDispatchInternal@8 +IpxTransferData@28 +IpxInitLoopback@0 +RipGetFirstRoute@4 +IpxCreateAddress@8 +MacReturnMaxDataSize@20 +IpxLookupAddress@8 +IpxAbortLineChanges@4 +MacMapFrameType@12 +IpxInitializeReceiveBuffer@16 +IpxDispatchOpenClose@8 +IpxTdiSetEventHandler@4 +IpxCreateAddressFile@4 +IpxInternalBind@8 +IpxInitializeReceivePacket@8 +IpxIsAddressLocal@4 +IpxOpenAddress@8 +IpxAllocateReceiveBufferPool@4 +IpxSubmitNdisRequest@12 +IpxAddBroadcast@4 +IpxDestroyBinding@4 +RipAdjustForBindingChange@12 +IpxDispatchDeviceControl@8 +IpxAllocatePaddingBuffer@4 +TdiMapUserRequest@12 +CTEInitialize@0 +IpxInitializeSendPacket@12 +IpxpFreeMemory@12 +IpxInitializePaddingBuffer@12 +IpxAllocateSendPool@4 +RipCleanupPacket@8 +TdiRegisterDeviceObject@8 +TdiRegisterNetAddress@8 +IpxTdiAction@8 +IpxCreateBinding@20 +IpxDerefDevice@4 +MacInitializeBindingInfo@8 +IpxRegisterProtocol@4 +IpxInitializeNdis@8 +IpxGetConfigValue@24 +IpxCreateAdapter@12 +IpxResolveBindingSets@8 +IpxGetFrameType@24 +MacInitializeMacInfo@8 +IpxGetBindingValue@24 +RipQueueRequest@8 +RipShortTimeout@8 +IpxPopSendPacket@4 +IpxAllocateBindingPool@4 +IpxInternalQuery@20 +CTEInitTimer@4 +IpxRequestComplete@12 +IpxBroadcastOperation@4 +CTEInitEvent@8 +IpxPnPGetVirtualNetworkNumber@4 +IpxPnPGetAdapterParameters@12 +IpxOpenAdapterComplete@12 +IpxResolveAutoDetect@16 +IpxBindToAdapter@16 +TdiInitialize@0 +IpxPnPIsnIndicate@4 +IpxPnPUpdateBindingArray@12 +IpxBindAdapter@20 +IpxPnPUpdateDevice@4 +IpxGetConfiguration@12 +IpxAddExport@24 +IpxCreateDevice@16 +DriverEntry@8 +IpxReadLinkageInformation@4 +IpxFreeConfiguration@4 diff --git a/private/ntos/tdi/isnp/ipx/mp/sources b/private/ntos/tdi/isnp/ipx/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/ipx/ndis.c b/private/ntos/tdi/isnp/ipx/ndis.c new file mode 100644 index 000000000..14066a786 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/ndis.c @@ -0,0 +1,2204 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ndis.c + +Abstract: + + This module contains code which implements the routines used to + initialize the IPX <-> NDIS interface, as well as most of the + interface routines. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 3-Oct-1995 + Changes to support transfer of buffer ownership to transports + 1. Added the ReceivePacketHandler to the ProtChars. + + Sanjay Anand (SanjayAn) 27-Oct-1995 + Changes to support Plug and Play (in _PNP_POWER) + + Tony Bell (TonyBe) 10-Dec-1995 + Changes to support new NdisWan Lineup. + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This is a one-per-driver variable used in binding +// to the NDIS interface. +// + +NDIS_HANDLE IpxNdisProtocolHandle = (NDIS_HANDLE)NULL; + +NDIS_STATUS +IpxSubmitNdisRequest( + IN PADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ); + +#ifndef _PNP_POWER +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,IpxRegisterProtocol) +#pragma alloc_text(INIT,IpxInitializeNdis) +#endif +#endif + + +NTSTATUS +IpxRegisterProtocol( + IN PNDIS_STRING NameString + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface. + +Arguments: + + NameString - The name of the transport. + +Return Value: + + The function value is the status of the operation. + STATUS_SUCCESS if all goes well, + Failure status if we tried to register and couldn't, + STATUS_INSUFFICIENT_RESOURCES if we couldn't even try to register. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + NDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register + + + // + // Set up the characteristics of this protocol + // +#if NDIS40 + ProtChars.MajorNdisVersion = 4; + + ProtChars.ReceivePacketHandler = IpxReceivePacket; +#else + ProtChars.MajorNdisVersion = 3; +#endif + ProtChars.MinorNdisVersion = 0; + + ProtChars.Name = *NameString; + + ProtChars.OpenAdapterCompleteHandler = IpxOpenAdapterComplete; + ProtChars.CloseAdapterCompleteHandler = IpxCloseAdapterComplete; + ProtChars.ResetCompleteHandler = IpxResetComplete; + ProtChars.RequestCompleteHandler = IpxRequestComplete; + + ProtChars.SendCompleteHandler = IpxSendComplete; + ProtChars.TransferDataCompleteHandler = IpxTransferDataComplete; + + ProtChars.ReceiveHandler = IpxReceiveIndication; + ProtChars.ReceiveCompleteHandler = IpxReceiveComplete; + ProtChars.StatusHandler = IpxStatus; + ProtChars.StatusCompleteHandler = IpxStatusComplete; + +#ifdef _PNP_POWER + ProtChars.BindAdapterHandler = IpxBindAdapter; + ProtChars.UnbindAdapterHandler = IpxUnbindAdapter; + ProtChars.TranslateHandler = IpxTranslate; +#endif // _PNP_POWER + + NdisRegisterProtocol ( + &ndisStatus, + &IpxNdisProtocolHandle, + &ProtChars, + (UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) { + return (NTSTATUS)ndisStatus; + } + + return STATUS_SUCCESS; + +} /* IpxRegisterProtocol */ + + +VOID +IpxDeregisterProtocol ( + VOID + ) + +/*++ + +Routine Description: + + This routine removes this transport to the NDIS interface. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + if (IpxNdisProtocolHandle != (NDIS_HANDLE)NULL) { + NdisDeregisterProtocol ( + &ndisStatus, + IpxNdisProtocolHandle); + IpxNdisProtocolHandle = (NDIS_HANDLE)NULL; + } + +} /* IpxDeregisterProtocol */ + + +NDIS_STATUS +IpxSubmitNdisRequest( + IN PADAPTER Adapter, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ) + +/*++ + +Routine Description: + + This routine passed an NDIS_REQUEST to the MAC and waits + until it has completed before returning the final status. + +Arguments: + + Adapter - Pointer to the device context for this driver. + + NdisRequest - Pointer to the NDIS_REQUEST to submit. + + AdapterString - The name of the adapter, in case an error needs + to be logged. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS NdisStatus; + + NdisRequest( + &NdisStatus, + Adapter->NdisBindingHandle, + NdisRequest); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = Adapter->NdisRequestStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (NDIS, ("%s on OID %8.8lx failed %lx\n", + NdisRequest->RequestType == NdisRequestSetInformation ? "Set" : "Query", + NdisRequest->DATA.QUERY_INFORMATION.Oid, + NdisStatus)); + + IpxWriteOidErrorLog( + Adapter->Device->DeviceObject, + NdisRequest->RequestType == NdisRequestSetInformation ? + EVENT_TRANSPORT_SET_OID_FAILED : EVENT_TRANSPORT_QUERY_OID_FAILED, + NdisStatus, + AdapterString->Buffer, + NdisRequest->DATA.QUERY_INFORMATION.Oid); + + } else { + + IPX_DEBUG (NDIS, ("%s on OID %8.8lx succeeded\n", + NdisRequest->RequestType == NdisRequestSetInformation ? "Set" : "Query", + NdisRequest->DATA.QUERY_INFORMATION.Oid)); + } + + return NdisStatus; + +} /* IpxSubmitNdisRequest */ + + +NTSTATUS +IpxInitializeNdis( + IN PADAPTER Adapter, + IN PBINDING_CONFIG ConfigBinding + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface and sets up + any necessary NDIS data structures (Buffer pools and such). It will be + called for each adapter opened by this transport. + +Arguments: + + Adapter - Structure describing this binding. + + ConfigAdapter - Configuration information for this binding. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_STATUS NdisStatus; + NDIS_STATUS OpenErrorStatus; + NDIS_MEDIUM IpxSupportedMedia[] = { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumArcnet878_2, NdisMediumWan }; + UINT SelectedMedium; + NDIS_REQUEST IpxRequest; + ULONG MinimumLookahead; + UCHAR WanProtocolId[6] = { 0x80, 0x00, 0x00, 0x00, 0x81, 0x37 }; + UCHAR FunctionalAddress[4] = { 0x00, 0x80, 0x00, 0x00 }; + ULONG WanHeaderFormat = NdisWanHeaderEthernet; + NDIS_OID IpxOid; + ULONG MacOptions; + ULONG PacketFilter; + PNDIS_STRING AdapterString = &ConfigBinding->AdapterName; + + // + // Initialize this adapter for IPX use through NDIS + // + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &Adapter->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + Adapter->NdisBindingHandle = NULL; + + OpenErrorStatus = 0; + + NdisOpenAdapter ( + &NdisStatus, + &OpenErrorStatus, + &Adapter->NdisBindingHandle, + &SelectedMedium, + IpxSupportedMedia, + sizeof (IpxSupportedMedia) / sizeof(NDIS_MEDIUM), + IpxNdisProtocolHandle, + (NDIS_HANDLE)Adapter, + &ConfigBinding->AdapterName, + 0, + NULL); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = Adapter->NdisRequestStatus; + OpenErrorStatus = Adapter->OpenErrorStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (NDIS, ("Open %ws failed %lx\n", ConfigBinding->AdapterName.Buffer, NdisStatus)); + + IpxWriteGeneralErrorLog( + Adapter->Device->DeviceObject, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 807, + NdisStatus, + AdapterString->Buffer, + 1, + &OpenErrorStatus); + return STATUS_INSUFFICIENT_RESOURCES; + + } else { + + IPX_DEBUG (NDIS, ("Open %ws succeeded\n", ConfigBinding->AdapterName.Buffer)); + } + + + // + // Get the information we need about the adapter, based on + // the media type. + // + + MacInitializeMacInfo( + IpxSupportedMedia[SelectedMedium], + &Adapter->MacInfo); + + + switch (Adapter->MacInfo.RealMediumType) { + + case NdisMedium802_3: + + IpxOid = OID_802_3_CURRENT_ADDRESS; + break; + + case NdisMedium802_5: + + IpxOid = OID_802_5_CURRENT_ADDRESS; + break; + + case NdisMediumFddi: + + IpxOid = OID_FDDI_LONG_CURRENT_ADDR; + break; + + case NdisMediumArcnet878_2: + + IpxOid = OID_ARCNET_CURRENT_ADDRESS; + break; + + case NdisMediumWan: + + IpxOid = OID_WAN_CURRENT_ADDRESS; + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = IpxOid; + + if (IpxOid != OID_ARCNET_CURRENT_ADDRESS) { + + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = Adapter->LocalMacAddress.Address; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + } else { + + // + // We take the arcnet single-byte address and right-justify + // it in a field of zeros. + // + + RtlZeroMemory (Adapter->LocalMacAddress.Address, 5); + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &Adapter->LocalMacAddress.Address[5]; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 1; + + } + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the maximum packet sizes. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_FRAME_SIZE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MaxReceivePacketSize); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MaxSendPacketSize); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Query the receive buffer space. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_RECEIVE_BUFFER_SPACE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->ReceiveBufferSpace); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now set the minimum lookahead size. The value we choose + // here is the 128 needed for TDI indications, plus the size + // of the IPX header, plus the largest extra header possible + // (a SNAP header, 8 bytes), plus the largest higher-level + // header (I think it is a Netbios datagram, 34 bytes). + // + // BETABUGBUG: Adapt this based on higher-level bindings and + // configured frame types. + // + + MinimumLookahead = 128 + sizeof(IPX_HEADER) + 8 + 34; + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MinimumLookahead; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the link speed + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(Adapter->MediumSpeed); + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // For wan, specify our protocol ID and header format. + // We don't query the medium subtype because we don't + // case (since we require ethernet emulation). + // + + if (Adapter->MacInfo.MediumAsync) { + + if (Adapter->BindSap != 0x8137) { + *(UNALIGNED USHORT *)(&WanProtocolId[4]) = Adapter->BindSapNetworkOrder; + } + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_PROTOCOL_TYPE; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = WanProtocolId; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_HEADER_FORMAT; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &WanHeaderFormat; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Now query the line count. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_LINE_COUNT; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &Adapter->WanNicIdCount; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + Adapter->WanNicIdCount = 1; + } + + if (Adapter->WanNicIdCount == 0) { + + IPX_DEBUG (NDIS, ("OID_WAN_LINE_COUNT returned 0 lines\n")); + + IpxWriteOidErrorLog( + Adapter->Device->DeviceObject, + EVENT_TRANSPORT_QUERY_OID_FAILED, + NDIS_STATUS_INVALID_DATA, + AdapterString->Buffer, + OID_WAN_LINE_COUNT); + + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + + } + } + + + // + // For 802.5 adapter's configured that way, we enable the + // functional address (C0-00-00-80-00-00). + // + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && + (Adapter->EnableFunctionalAddress)) { + + // + // For token-ring, we pass the last four bytes of the + // Netbios functional address. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = FunctionalAddress; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + + // + // Now query the MAC's optional characteristics. + // + + IpxRequest.RequestType = NdisRequestQueryInformation; + IpxRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS; + IpxRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MacOptions; + IpxRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Adapter->MacInfo.CopyLookahead = + ((MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0; + Adapter->MacInfo.MacOptions = MacOptions; + + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 14; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 14; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 14; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 14; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + break; + + case NdisMedium802_5: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 17; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 17; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 22; + break; + + case NdisMediumFddi: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 16; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 13; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 16; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 21; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 16; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 13; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 16; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 21; + break; + + case NdisMediumArcnet878_2: + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_2] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_802_3] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 3; + Adapter->DefHeaderSizes[ISN_FRAME_TYPE_SNAP] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_2] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_802_3] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_ETHERNET_II] = 3; + Adapter->BcMcHeaderSizes[ISN_FRAME_TYPE_SNAP] = 3; + break; + + } + + // + // BUGBUG: If functional filtering is set, set the address + // for the appropriate binding. + // + + // + // Now that everything is set up, we enable the filter + // for packet reception. + // + + switch (Adapter->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumFddi: + case NdisMedium802_5: + case NdisMediumArcnet878_2: + + // + // If we have a virtual network number we need to receive + // broadcasts (either the router will be bound in which + // case we want them, or we need to respond to rip requests + // ourselves). + // + + PacketFilter = NDIS_PACKET_TYPE_DIRECTED; + + if (Adapter->Device->VirtualNetworkNumber != 0) { + + Adapter->BroadcastEnabled = TRUE; + Adapter->Device->EnableBroadcastCount = 1; + PacketFilter |= NDIS_PACKET_TYPE_BROADCAST; + + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && (Adapter->EnableFunctionalAddress)) { + PacketFilter |= NDIS_PACKET_TYPE_FUNCTIONAL; + } + + } else { + + Adapter->BroadcastEnabled = FALSE; + Adapter->Device->EnableBroadcastCount = 0; + + } + + break; + + default: + + CTEAssert (FALSE); + break; + + } + + // + // Now fill in the NDIS_REQUEST. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + IpxRequest.DATA.SET_INFORMATION.InformationBuffer = &PacketFilter; + IpxRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + NdisStatus = IpxSubmitNdisRequest (Adapter, &IpxRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxCloseNdis (Adapter); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + return STATUS_SUCCESS; + +} /* IpxInitializeNdis */ + + +VOID +IpxAddBroadcast( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine is called when another reason for enabling + broadcast reception is added. If it is the first, then + reception on the card is enabled by queueing a call to + IpxBroadcastOperation. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD. + +Arguments: + + Device - The IPX device. + +Return Value: + + None. + +--*/ + +{ + + ++Device->EnableBroadcastCount; + + if (Device->EnableBroadcastCount == 1) { + + // + // Broadcasts should be enabled. + // + + if (!Device->EnableBroadcastPending) { + + if (Device->DisableBroadcastPending) { + Device->ReverseBroadcastOperation = TRUE; + } else { + Device->EnableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)TRUE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + } + } + +} /* IpxAddBroadcast */ + + +VOID +IpxRemoveBroadcast( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine is called when a reason for enabling + broadcast reception is removed. If it is the last, then + reception on the card is disabled by queueing a call to + IpxBroadcastOperation. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD. + +Arguments: + + Device - The IPX device. + +Return Value: + + None. + +--*/ + +{ + + --Device->EnableBroadcastCount; + + if (Device->EnableBroadcastCount == 0) { + + // + // Broadcasts should be disabled. + // + + if (!Device->DisableBroadcastPending) { + + if (Device->EnableBroadcastPending) { + Device->ReverseBroadcastOperation = TRUE; + } else { + Device->DisableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)FALSE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + } + } + +} /* IpxRemoveBroadcast */ + + +VOID +IpxBroadcastOperation( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine is used to change whether broadcast reception + is enabled or disabled. It performs the requested operation + on every adapter bound to by IPX. + + This routine is called by a worker thread queued when a + bind/unbind operation changes the broadcast state. + +Arguments: + + Parameter - TRUE if broadcasts should be enabled, FALSE + if they should be disabled. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + BOOLEAN Enable = (BOOLEAN)Parameter; + UINT i; + PBINDING Binding; + PADAPTER Adapter; + ULONG PacketFilter; + NDIS_REQUEST IpxRequest; + NDIS_STRING AdapterName; + CTELockHandle LockHandle; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + IPX_DEBUG (NDIS, ("%s operation started\n", Enable ? "Enable" : "Disable")); + + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + Binding = NIC_ID_TO_BINDING(Device, i); +#else + IPX_DEBUG (NDIS, ("%s operation started\n", Enable ? "Enable" : "Disable")); + + for (i = 1; i <= Device->ValidBindings; i++) { + + Binding = Device->Bindings[i]; +#endif + if (Binding == NULL) { + continue; + } + + Adapter = Binding->Adapter; + if (Adapter->BroadcastEnabled == Enable) { + continue; + } + + if (Enable) { + if ((Adapter->MacInfo.MediumType == NdisMedium802_5) && (Adapter->EnableFunctionalAddress)) { + PacketFilter = (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_FUNCTIONAL); + } else { + PacketFilter = (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST); + } + } else { + PacketFilter = NDIS_PACKET_TYPE_DIRECTED; + } + + // + // Now fill in the NDIS_REQUEST. + // + + IpxRequest.RequestType = NdisRequestSetInformation; + IpxRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + IpxRequest.DATA.SET_INFORMATION.InformationBuffer = &PacketFilter; + IpxRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + AdapterName.Buffer = Adapter->AdapterName; + AdapterName.Length = (USHORT)Adapter->AdapterNameLength; + AdapterName.MaximumLength = (USHORT)(Adapter->AdapterNameLength + sizeof(WCHAR)); + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + (VOID)IpxSubmitNdisRequest (Adapter, &IpxRequest, &AdapterName); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + Adapter->BroadcastEnabled = Enable; + + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + CTEGetLock (&Device->Lock, &LockHandle); + + if (Enable) { + + CTEAssert (Device->EnableBroadcastPending); + Device->EnableBroadcastPending = FALSE; + + if (Device->ReverseBroadcastOperation) { + Device->ReverseBroadcastOperation = FALSE; + Device->DisableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)FALSE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + + } else { + + CTEAssert (Device->DisableBroadcastPending); + Device->DisableBroadcastPending = FALSE; + + if (Device->ReverseBroadcastOperation) { + Device->ReverseBroadcastOperation = FALSE; + Device->EnableBroadcastPending = TRUE; + ExInitializeWorkItem( + &Device->BroadcastOperationQueueItem, + IpxBroadcastOperation, + (PVOID)TRUE); + ExQueueWorkItem(&Device->BroadcastOperationQueueItem, DelayedWorkQueue); + } + + } + + CTEFreeLock (&Device->Lock, LockHandle); + +}/* IpxBroadcastOperation */ + + +VOID +IpxCloseNdis( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine unbinds the transport from the NDIS interface and does + any other work required to undo what was done in IpxInitializeNdis. + It is written so that it can be called from within IpxInitializeNdis + if it fails partway through. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + // + // Close the NDIS binding. + // + + if (Adapter->NdisBindingHandle != (NDIS_HANDLE)NULL) { + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &Adapter->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + Adapter->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &Adapter->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = Adapter->NdisRequestStatus; + + KeResetEvent( + &Adapter->NdisRequestEvent + ); + + } + + // + // We ignore ndisStatus. + // + + } + +#if 0 + if (Adapter->SendPacketPoolHandle != NULL) { + NdisFreePacketPool (Adapter->SendPacketPoolHandle); + } + + if (Adapter->ReceivePacketPoolHandle != NULL) { + NdisFreePacketPool (Adapter->ReceivePacketPoolHandle); + } + + if (Adapter->NdisBufferPoolHandle != NULL) { + NdisFreeBufferPool (Adapter->NdisBufferPoolHandle); + } +#endif + +} /* IpxCloseNdis */ + + +VOID +IpxOpenAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that an open adapter + is complete. Since we only ever have one outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + + OpenErrorStatus - More status information. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + Adapter->OpenErrorStatus = OpenErrorStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxOpenAdapterComplete */ + +VOID +IpxCloseAdapterComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a close adapter + is complete. Currently we don't close adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxCloseAdapterComplete */ + + +VOID +IpxResetComplete( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a reset adapter + is complete. Currently we don't reset adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(BindingContext); + UNREFERENCED_PARAMETER(NdisStatus); + +} /* IpxResetComplete */ + + +VOID +IpxRequestComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a request is complete. + Since we only ever have one request outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisRequest - The object describing the request. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + + Adapter->NdisRequestStatus = NdisStatus; + + KeSetEvent( + &Adapter->NdisRequestEvent, + 0L, + FALSE); + +} /* IpxRequestComplete */ + + +VOID +IpxStatus( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) + +{ + PADAPTER Adapter, TmpAdapter; + + PNDIS_WAN_LINE_UP LineUp; + PNDIS_WAN_LINE_DOWN LineDown; + PIPXCP_CONFIGURATION Configuration; // contains ipx net and node + + BOOLEAN UpdateLineUp; + PBINDING Binding, TmpBinding; + PDEVICE Device; + PADDRESS Address; + ULONG CurrentHash; + PIPX_ROUTE_ENTRY RouteEntry; + PNDIS_BUFFER NdisBuffer; + PNWLINK_ACTION NwlinkAction; + PIPX_ADDRESS_DATA IpxAddressData; + PREQUEST Request; + UINT BufferLength; + IPX_LINE_INFO LineInfo; + ULONG Segment; + ULONG LinkSpeed; + PLIST_ENTRY p; + NTSTATUS Status; + UINT i, j; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + NTSTATUS ntStatus; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + Adapter = (PADAPTER)NdisBindingContext; + + IpxReferenceAdapter(Adapter); +#else + Adapter = (PADAPTER)NdisBindingContext; +#endif + + Device = Adapter->Device; + + switch (NdisStatus) { + + case NDIS_STATUS_WAN_LINE_UP: + + + // + // If the line is already up, then we are just getting + // a change in line conditions, and the IPXCP_CONFIGURATION + // information is not included. If it turns out we need + // all the info, we check the size again later. + // + + if (StatusBufferSize < sizeof(NDIS_WAN_LINE_UP)) { + IPX_DEBUG (WAN, ("Line up, status buffer size wrong %d/%d\n", StatusBufferSize, sizeof(NDIS_WAN_LINE_UP))); +#ifdef _PNP_POWER + goto error_no_lock; +#else + return; +#endif + } + + LineUp = (PNDIS_WAN_LINE_UP)StatusBuffer; + + // + // We scan through the adapter's NIC ID range looking + // for an active binding with the same remote address. + // + + UpdateLineUp = FALSE; + + // + // See if this is a new lineup or not + // + *((ULONG UNALIGNED *)(&Binding)) = + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])); + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + if (Binding != NULL) { + UpdateLineUp = TRUE; + } + + if (LineUp->ProtocolType != Adapter->BindSap) { + IPX_DEBUG (WAN, ("Line up, wrong protocol type %lx\n", LineUp->ProtocolType)); + +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto error_no_lock; +#else + return; +#endif + } + + Configuration = (PIPXCP_CONFIGURATION)LineUp->ProtocolBuffer; + + // + // PNP_POWER - We hold the exclusive lock to the binding array (thru both the device and adapter) + // and the reference to the adapter at this point. + // + + // + // If this line was previously down, create a new binding + // if needed. + // + + if (!UpdateLineUp) { + + // + // We look for a binding that is allocated but down, if + // we can't find that then we look for any empty spot in + // the adapter's NIC ID range and allocate a binding in it. + // Since we always allocate this way, the allocated + // bindings are all clumped at the beginning and once + // we find a NULL spot we know there are no more + // allocated ones. + // + // We keep track of the first binding on this adapter + // in TmpBinding in case we need config info from it. + // + + TmpBinding = NULL; + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (i = Adapter->FirstWanNicId; + i <= Adapter->LastWanNicId; + i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if (TmpBinding == NULL) { + TmpBinding = Binding; + } + + if ((Binding == NULL) || + (!Binding->LineUp)) { + break; + } + } + + if (i > Adapter->LastWanNicId) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_DEBUG (WAN, ("Line up, no WAN binding available\n")); + return; + } + + if (Binding == NULL) { + + // + // We need to allocate one. + // + + CTEAssert (TmpBinding != NULL); + +#ifdef _PNP_POWER + // + // CreateBinding does an InterLockedPop with the DeviceLock. + // So, release the lock here. + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + Status = IpxCreateBinding( + Device, + NULL, + 0, + Adapter->AdapterName, + &Binding); + + if (Status != STATUS_SUCCESS) { +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + IPX_DEBUG (WAN, ("Line up, could not create WAN binding\n")); + goto error_no_lock; +#else + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_DEBUG (WAN, ("Line up, could not create WAN binding\n")); + return; +#endif + } + +#ifdef _PNP_POWER + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + // + // Binding->AllRouteXXX doesn't matter for WAN. + // + + Binding->FrameType = ISN_FRAME_TYPE_ETHERNET_II; + Binding->SendFrameHandler = IpxSendFrameWanEthernetII; + ++Adapter->BindingCount; + Binding->Adapter = Adapter; + + Binding->NicId = i; +#ifdef _PNP_POWER + INSERT_BINDING(Device, i, Binding); +#else + Device->Bindings[i] = Binding; +#endif + + // + // Other fields are filled in below. + // + + } + + // + // This is not an update, so note that the line is active. + // + + Binding->LineUp = TRUE; + + if (Configuration->ConnectionClient == 1) { + Binding->DialOutAsync = TRUE; + } else { + Binding->DialOutAsync = FALSE; + } + + // + // Keep track of the highest NIC ID that we should + // send type 20s out on. + // + + if (i > (UINT)MIN (Device->MaxBindings, Device->HighestType20NicId)) { + + if ((Binding->DialOutAsync) || + ((Device->DisableDialinNetbios & 0x01) == 0)) { + + Device->HighestType20NicId = i; + } + } + + // + // We could error out below, trying to insert this network number. In RipShortTimeout + // we dont check for LineUp when calculating the tick counts; set this before the insert + // attempt. + // + Binding->MediumSpeed = LineUp->LinkSpeed; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + + // + // Add a router entry for this net if there is no router. + // We want the number of ticks for a 576-byte frame, + // given the link speed in 100 bps units, so we calculate + // as: + // + // seconds 18.21 ticks 4608 bits + // --------------------- * ----------- * --------- + // link_speed * 100 bits second frame + // + // to get the formula + // + // ticks/frame = 839 / link_speed. + // + // We add link_speed to the numerator also to ensure + // that the value is at least 1. + // + + if ((!Device->UpperDriverBound[IDENTIFIER_RIP]) && + (*(UNALIGNED ULONG *)Configuration->Network != 0)) { + if (RipInsertLocalNetwork( + *(UNALIGNED ULONG *)Configuration->Network, + Binding->NicId, + Adapter->NdisBindingHandle, + (USHORT)((839 + LineUp->LinkSpeed) / LineUp->LinkSpeed)) != STATUS_SUCCESS) { + // + // This means we couldn't allocate memory, or + // the entry already existed. If it already + // exists we can ignore it for the moment. + // + // BUGBUG: Now it will succeed if the network + // exists. + // + + IPX_DEBUG (WAN, ("Line up, could not insert local network\n")); + Binding->LineUp = FALSE; +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + goto error_no_lock; +#else + return; +#endif + } + } + + + // + // Update our addresses. + // + Binding->LocalAddress.NetworkAddress = *(UNALIGNED ULONG *)Configuration->Network; + RtlCopyMemory (Binding->LocalAddress.NodeAddress, Configuration->LocalNode, 6); + RtlCopyMemory (Binding->WanRemoteNode, Configuration->RemoteNode, 6); + + // + // Return the binding context for this puppy! + // + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])) = + *((ULONG UNALIGNED *)(&Binding)); + + RtlCopyMemory (Binding->LocalMacAddress.Address, LineUp->LocalAddress, 6); + RtlCopyMemory (Binding->RemoteMacAddress.Address, LineUp->RemoteAddress, 6); + + // + // Update the device node and all the address + // nodes if we have only one bound, or this is + // binding one. + // + + if (!Device->VirtualNetwork) { + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + Device->SourceAddress.NetworkAddress = *(UNALIGNED ULONG *)(Configuration->Network); + RtlCopyMemory (Device->SourceAddress.NodeAddress, Configuration->LocalNode, 6); + } + + // + // Scan through all the addresses that exist and modify + // their pre-constructed local IPX address to reflect + // the new local net and node. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (CurrentHash = 0; CurrentHash < IPX_ADDRESS_HASH_COUNT; CurrentHash++) { + + for (p = Device->AddressDatabases[CurrentHash].Flink; + p != &Device->AddressDatabases[CurrentHash]; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + Address->LocalAddress.NetworkAddress = *(UNALIGNED ULONG *)Configuration->Network; + RtlCopyMemory (Address->LocalAddress.NodeAddress, Configuration->LocalNode, 6); + } + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + } + + // + // Reset this since the line just came up. + // + + Binding->WanInactivityCounter = 0; + + } + + LinkSpeed = LineUp->LinkSpeed; + + // + // Scan through bindings to update Device->LinkSpeed. + // If SingleNetworkActive is set, we only count WAN + // bindings when doing this (although it is unlikely + // a LAN binding would be the winner). + // + // BUGBUG: Update other device information? + // + + for (i = 1; i <= Device->ValidBindings; i++) { +#ifdef _PNP_POWER + if (TmpBinding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (TmpBinding = Device->Bindings[i]) { +#endif + TmpAdapter = TmpBinding->Adapter; + if (TmpBinding->LineUp && + (!Device->SingleNetworkActive || TmpAdapter->MacInfo.MediumAsync) && + (TmpBinding->MediumSpeed < LinkSpeed)) { + LinkSpeed = TmpBinding->MediumSpeed; + } + } + } + +#ifdef _PNP_POWER + // + // Release the lock after incrementing the reference count + // + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + Device->LinkSpeed = LinkSpeed; + + if ((Adapter->ConfigMaxPacketSize == 0) || + (LineUp->MaximumTotalSize < Adapter->ConfigMaxPacketSize)) { + Binding->MaxSendPacketSize = LineUp->MaximumTotalSize; + } else { + Binding->MaxSendPacketSize = Adapter->ConfigMaxPacketSize; + } + MacInitializeBindingInfo (Binding, Adapter); + +#ifdef _PNP_POWER + + // + // We dont give lineups; instead indicate only if the PnP reserved address + // changed to SPX. NB gets all PnP indications with the reserved address case + // marked out. + // + { + IPX_PNP_INFO NBPnPInfo; + + if ((!Device->MultiCardZeroVirtual) || (Binding->NicId == 1)) { + + // + // NB's reserved address changed. + // + NBPnPInfo.NewReservedAddress = TRUE; + + if (!Device->VirtualNetwork) { + // + // Let SPX know because it fills in its own headers. + // + if (Device->UpperDriverBound[IDENTIFIER_SPX]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_PNP_INFO IpxPnPInfo; + + IpxPnPInfo.NewReservedAddress = TRUE; + IpxPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + IpxPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(IpxPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(IpxPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_SPX].PnPHandler) ( + IPX_PNP_ADDRESS_CHANGE, + &IpxPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADDRESS_CHANGED to SPX: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + } + } else { + NBPnPInfo.NewReservedAddress = FALSE; + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + NBPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + NBPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + NBPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + NBPnPInfo.FirstORLastDevice = FALSE; + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + RtlCopyMemory(NBPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(NBPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_ADD_DEVICE, + &NBPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_ADD_DEVICE (lineup) to NB: net addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } + + // + // Register this address with the TDI clients. + // + RtlCopyMemory (Device->TdiRegistrationAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + + if ((ntStatus = TdiRegisterNetAddress( + Device->TdiRegistrationAddress, + &Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + + IPX_DEBUG(PNP, ("TdiRegisterNetAddress failed: %lx", ntStatus)); + } + } + + // + // Indicate to the upper drivers. + // + LineInfo.LinkSpeed = LineUp->LinkSpeed; + LineInfo.MaximumPacketSize = LineUp->MaximumTotalSize - 14; + LineInfo.MaximumSendSize = LineUp->MaximumTotalSize - 14; + LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + + // + // Give line up to RIP as it is not PnP aware. + // + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + (*Device->UpperDrivers[IDENTIFIER_RIP].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + UpdateLineUp ? NULL : Configuration); + } +#else + // + // Indicate to the upper drivers. + // + LineInfo.LinkSpeed = LineUp->LinkSpeed; + LineInfo.MaximumPacketSize = LineUp->MaximumTotalSize - 14; + LineInfo.MaximumSendSize = LineUp->MaximumTotalSize - 14; + LineInfo.MacOptions = Adapter->MacInfo.MacOptions; + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + + if (Device->UpperDriverBound[i]) { + (*Device->UpperDrivers[i].LineUpHandler)( + Binding->NicId, + &LineInfo, + NdisMediumWan, + UpdateLineUp ? NULL : Configuration); + } + } +#endif + if (!UpdateLineUp) { + + if ((Device->SingleNetworkActive) && + (Configuration->ConnectionClient == 1)) { + // + // Drop all entries in the database if rip is not bound. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + RipDropRemoteEntries(); + } + + Device->ActiveNetworkWan = TRUE; + + // + // Find a queued line change and complete it. + // + + if ((p = ExInterlockedRemoveHeadList( + &Device->LineChangeQueue, + &Device->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + } + + // + // If we have a virtual net, do a broadcast now so + // the router on the other end will know about us. + // + // BUGBUG: Use RipSendResponse, and do it even + // if SingleNetworkActive is FALSE?? + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + } + + // + // Find a queued address notify and complete it. + // If WanGlobalNetworkNumber is TRUE, we only do + // this when the first dialin line comes up. + // + + if ((!Device->WanGlobalNetworkNumber || + (!Device->GlobalNetworkIndicated && !Binding->DialOutAsync)) + && + ((p = ExInterlockedRemoveHeadList( + &Device->AddressNotifyQueue, + &Device->Lock)) != NULL)) { + + if (Device->WanGlobalNetworkNumber) { + Device->GlobalWanNetwork = Binding->LocalAddress.NetworkAddress; + Device->GlobalNetworkIndicated = TRUE; + } + + Request = LIST_ENTRY_TO_REQUEST(p); + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + + if (Device->WanGlobalNetworkNumber) { + IpxAddressData->adapternum = Device->SapNicCount - 1; + } else { + IpxAddressData->adapternum = Binding->NicId - 1; + } + *(UNALIGNED ULONG *)IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + IpxAddressData->wan = TRUE; + IpxAddressData->status = TRUE; + IpxAddressData->maxpkt = Binding->AnnouncedMaxDatagramSize; // BUGBUG: Use real? + IpxAddressData->linkspeed = Binding->MediumSpeed; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + break; + + case NDIS_STATUS_WAN_LINE_DOWN: + + if (StatusBufferSize < sizeof(NDIS_WAN_LINE_DOWN)) { + IPX_DEBUG (WAN, ("Line down, status buffer size wrong %d/%d\n", StatusBufferSize, sizeof(NDIS_WAN_LINE_DOWN))); + return; + } + + LineDown = (PNDIS_WAN_LINE_DOWN)StatusBuffer; + + *((ULONG UNALIGNED*)(&Binding)) = *((ULONG UNALIGNED*)(&LineDown->LocalAddress[2])); + + CTEAssert(Binding != NULL); + + // + // Note that the WAN line is down. + // +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + + Binding->LineUp = FALSE; + + // + // PNP_POWER - we hold the exclusive lock to the binding + // and reference to the adapter at this point. + // + + // + // Keep track of the highest NIC ID that we should + // send type 20s out on. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if (Binding->NicId == MIN (Device->MaxBindings, Device->HighestType20NicId)) { + + // + // This was the old limit, so we have to scan + // backwards to update it -- we stop when we hit + // a non-WAN binding, or a wan binding that is up and + // dialout, or any wan binding if bit 1 in + // DisableDialinNetbios is off. + // + + for (i = Binding->NicId-1; i >= 1; i--) { +#ifdef _PNP_POWER + TmpBinding = NIC_ID_TO_BINDING(Device, i); +#else + TmpBinding = Device->Bindings[i]; +#endif + + if ((TmpBinding != NULL) && + ((!TmpBinding->Adapter->MacInfo.MediumAsync) || + (TmpBinding->LineUp && + ((Binding->DialOutAsync) || + ((Device->DisableDialinNetbios & 0x01) == 0))))) { + + break; + } + } + + Device->HighestType20NicId = i; + + } + + + // + // Scan through bindings to update Device->LinkSpeed. + // If SingleNetworkActive is set, we only count LAN + // bindings when doing this. + // + // BUGBUG: Update other device information? + // + + LinkSpeed = 0xffffffff; + for (i = 1; i <= Device->ValidBindings; i++) { +#ifdef _PNP_POWER + if (TmpBinding = NIC_ID_TO_BINDING(Device, i)) { +#else + if (TmpBinding = Device->Bindings[i]) { +#endif + TmpAdapter = TmpBinding->Adapter; + if (TmpBinding->LineUp && + (!Device->SingleNetworkActive || !TmpAdapter->MacInfo.MediumAsync) && + (TmpBinding->MediumSpeed < LinkSpeed)) { + LinkSpeed = TmpBinding->MediumSpeed; + } + } + } + + if (LinkSpeed != 0xffffffff) { + Device->LinkSpeed = LinkSpeed; + } + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + // + // Remove our router entry for this net. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + + Segment = RipGetSegment ((PUCHAR)&Binding->LocalAddress.NetworkAddress); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + RouteEntry = RipGetRoute (Segment, (PUCHAR)&Binding->LocalAddress.NetworkAddress); + + if (RouteEntry != (PIPX_ROUTE_ENTRY)NULL) { + + RipDeleteRoute (Segment, RouteEntry); + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + } + + RipAdjustForBindingChange (Binding->NicId, 0, IpxBindingDown); + + } + + // + // Indicate to the upper drivers. + // +#ifdef _PNP_POWER + + // + // DeRegister this address with the TDI clients. + // + { + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + + CTEAssert(Binding->TdiRegistrationHandle); + + if ((ntStatus = TdiDeregisterNetAddress(Binding->TdiRegistrationHandle)) != STATUS_SUCCESS) { + IPX_DEBUG(PNP, ("TdiDeRegisterNetAddress failed: %lx", ntStatus)); + } + + if (Device->UpperDriverBound[IDENTIFIER_NB]) { + IPX_PNP_INFO NBPnPInfo; + + CTEAssert(Binding->IsnInformed[IDENTIFIER_NB]); + + NBPnPInfo.LineInfo.LinkSpeed = Device->LinkSpeed; + NBPnPInfo.LineInfo.MaximumPacketSize = + Device->Information.MaximumLookaheadData + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MaximumSendSize = + Device->Information.MaxDatagramSize + sizeof(IPX_HEADER); + NBPnPInfo.LineInfo.MacOptions = Device->MacOptions; + + NBPnPInfo.NewReservedAddress = FALSE; + NBPnPInfo.FirstORLastDevice = FALSE; + + NBPnPInfo.NetworkAddress = Binding->LocalAddress.NetworkAddress; + + RtlCopyMemory(NBPnPInfo.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + NIC_HANDLE_FROM_NIC(NBPnPInfo.NicHandle, Binding->NicId); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // give the PnP indication + // + (*Device->UpperDrivers[IDENTIFIER_NB].PnPHandler) ( + IPX_PNP_DELETE_DEVICE, + &NBPnPInfo); + + IPX_DEBUG(AUTO_DETECT, ("IPX_PNP_DELETE_DEVICE (linedown) to NB: addr: %lx\n", Binding->LocalAddress.NetworkAddress)); + } else { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + } + } + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + (*Device->UpperDrivers[IDENTIFIER_RIP].LineDownHandler)( + Binding->NicId); + } +#else + for (i = 0; i < UPPER_DRIVER_COUNT; i++) { + + if (Device->UpperDriverBound[i]) { + (*Device->UpperDrivers[i].LineDownHandler)( + Binding->NicId); + } + } +#endif + + if ((Device->SingleNetworkActive) && + (Binding->DialOutAsync)) { + + // + // Drop all entries in the database if rip is not bound. + // + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + RipDropRemoteEntries(); + } + + Device->ActiveNetworkWan = FALSE; + + // + // Find a queued line change and complete it. + // + + if ((p = ExInterlockedRemoveHeadList( + &Device->LineChangeQueue, + &Device->Lock)) != NULL) { + + Request = LIST_ENTRY_TO_REQUEST(p); + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_LINE_CHANGE); + + } + + } + + // + // Find a queued address notify and complete it. + // + + if ((!Device->WanGlobalNetworkNumber) && + ((p = ExInterlockedRemoveHeadList( + &Device->AddressNotifyQueue, + &Device->Lock)) != NULL)) { + + Request = LIST_ENTRY_TO_REQUEST(p); + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + IpxAddressData = (PIPX_ADDRESS_DATA)(NwlinkAction->Data); + + IpxAddressData->adapternum = Binding->NicId - 1; + *(UNALIGNED ULONG *)IpxAddressData->netnum = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory(IpxAddressData->nodenum, Binding->LocalAddress.NodeAddress, 6); + IpxAddressData->wan = TRUE; + IpxAddressData->status = FALSE; + IpxAddressData->maxpkt = Binding->AnnouncedMaxDatagramSize; // BUGBUG: Use real? + IpxAddressData->linkspeed = Binding->MediumSpeed; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + REQUEST_STATUS(Request) = STATUS_SUCCESS; + IpxCompleteRequest (Request); + IpxFreeRequest (Device, Request); + + IpxDereferenceDevice (Device, DREF_ADDRESS_NOTIFY); + } + +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + break; + + case NDIS_STATUS_WAN_FRAGMENT: + + // + // No response needed, IPX is a datagram service. + // + // BUGBUG: What about telling Netbios/SPX? + // + + break; + + default: + + break; + + } + +#ifdef _PNP_POWER +error_no_lock: + IpxDereferenceAdapter(Adapter); +#endif + +} /* IpxStatus */ + + +VOID +IpxStatusComplete( + IN NDIS_HANDLE NdisBindingContext + ) +{ + UNREFERENCED_PARAMETER (NdisBindingContext); + +} /* IpxStatusComplete */ + + + diff --git a/private/ntos/tdi/isnp/ipx/nwlnkipx.ini b/private/ntos/tdi/isnp/ipx/nwlnkipx.ini new file mode 100644 index 000000000..416f0c6b7 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/nwlnkipx.ini @@ -0,0 +1,191 @@ +\Registry\Machine\System\CurrentControlSet\Services\NwlnkIpx + Type = REG_DWORD 0x00000001 + Start = REG_DWORD 0x00000003 + ErrorControl = REG_DWORD 0x00000001 + ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwlnkipx.sys + DisplayName = NWLINK2 IPX Protocol + Group = TDI + DependOnService = REG_MULTI_SZ + DependOnGroup = REG_MULTI_SZ "NDIS" + Linkage + Bind = REG_MULTI_SZ "\Device\Elnkii1" + Export = REG_MULTI_SZ "\Device\NwlnkIpx" + Route = REG_MULTI_SZ ""Elnkii" "Elnkii1"" + Disabled + Bind = REG_MULTI_SZ + Export = REG_MULTI_SZ + Route = REG_MULTI_SZ + NetConfig + Elnkii1 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard2 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard3 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard4 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Netcard5 + NetworkNumber = REG_MULTI_SZ "0" + PktType = REG_MULTI_SZ "1" + BindSap = REG_DWORD 0x00008137 + SourceRouting = REG_DWORD 0x00000001 + SourceRouteDef = REG_DWORD 0x00000000 + SourceRouteBcast = REG_DWORD 0x00000000 + SourceRouteMcast = REG_DWORD 0x00000000 + EnableFuncaddr = REG_DWORD 0x00000001 + MaxPktSize = REG_DWORD 0x00000000 + Parameters + DedicatedRouter = REG_DWORD 0x00000000 + InitDatagrams = REG_DWORD 0x0000000a + MaxDatagrams = REG_DWORD 0x00000032 + RipAgeTime = REG_DWORD 0x00000005 + RipCount = REG_DWORD 0x00000005 + RipTimeout = REG_DWORD 0x00000001 + RipUsageTime = REG_DWORD 0x0000000f + SourceRouteUsageTime = REG_DWORD 0x0000000a + SocketUniqueness = REG_DWORD 0x00000008 + VirtualNetworkNumber = REG_DWORD 0x00000000 + VirtualNetworkOptional = REG_DWORD 0x00000001 + Winsock + Mapping = REG_BINARY 0x00000c08 + 0x00000100 0x00000003 0x00000006 0x00000002 0x000003e8 0x00000006 0x00000002 0x000003e9 + 0x00000006 0x00000002 0x000003ea 0x00000006 0x00000002 0x000003eb 0x00000006 0x00000002 + 0x000003ec 0x00000006 0x00000002 0x000003ed 0x00000006 0x00000002 0x000003ee 0x00000006 + 0x00000002 0x000003ef 0x00000006 0x00000002 0x000003f0 0x00000006 0x00000002 0x000003f1 + 0x00000006 0x00000002 0x000003f2 0x00000006 0x00000002 0x000003f3 0x00000006 0x00000002 + 0x000003f4 0x00000006 0x00000002 0x000003f5 0x00000006 0x00000002 0x000003f6 0x00000006 + 0x00000002 0x000003f7 0x00000006 0x00000002 0x000003f8 0x00000006 0x00000002 0x000003f9 + 0x00000006 0x00000002 0x000003fa 0x00000006 0x00000002 0x000003fb 0x00000006 0x00000002 + 0x000003fc 0x00000006 0x00000002 0x000003fd 0x00000006 0x00000002 0x000003fe 0x00000006 + 0x00000002 0x000003ff 0x00000006 0x00000002 0x00000400 0x00000006 0x00000002 0x00000401 + 0x00000006 0x00000002 0x00000402 0x00000006 0x00000002 0x00000403 0x00000006 0x00000002 + 0x00000404 0x00000006 0x00000002 0x00000405 0x00000006 0x00000002 0x00000406 0x00000006 + 0x00000002 0x00000407 0x00000006 0x00000002 0x00000408 0x00000006 0x00000002 0x00000409 + 0x00000006 0x00000002 0x0000040a 0x00000006 0x00000002 0x0000040b 0x00000006 0x00000002 + 0x0000040c 0x00000006 0x00000002 0x0000040d 0x00000006 0x00000002 0x0000040e 0x00000006 + 0x00000002 0x0000040f 0x00000006 0x00000002 0x00000410 0x00000006 0x00000002 0x00000411 + 0x00000006 0x00000002 0x00000412 0x00000006 0x00000002 0x00000413 0x00000006 0x00000002 + 0x00000414 0x00000006 0x00000002 0x00000415 0x00000006 0x00000002 0x00000416 0x00000006 + 0x00000002 0x00000417 0x00000006 0x00000002 0x00000418 0x00000006 0x00000002 0x00000419 + 0x00000006 0x00000002 0x0000041a 0x00000006 0x00000002 0x0000041b 0x00000006 0x00000002 + 0x0000041c 0x00000006 0x00000002 0x0000041d 0x00000006 0x00000002 0x0000041e 0x00000006 + 0x00000002 0x0000041f 0x00000006 0x00000002 0x00000420 0x00000006 0x00000002 0x00000421 + 0x00000006 0x00000002 0x00000422 0x00000006 0x00000002 0x00000423 0x00000006 0x00000002 + 0x00000424 0x00000006 0x00000002 0x00000425 0x00000006 0x00000002 0x00000426 0x00000006 + 0x00000002 0x00000427 0x00000006 0x00000002 0x00000428 0x00000006 0x00000002 0x00000429 + 0x00000006 0x00000002 0x0000042a 0x00000006 0x00000002 0x0000042b 0x00000006 0x00000002 + 0x0000042c 0x00000006 0x00000002 0x0000042d 0x00000006 0x00000002 0x0000042e 0x00000006 + 0x00000002 0x0000042f 0x00000006 0x00000002 0x00000430 0x00000006 0x00000002 0x00000431 + 0x00000006 0x00000002 0x00000432 0x00000006 0x00000002 0x00000433 0x00000006 0x00000002 + 0x00000434 0x00000006 0x00000002 0x00000435 0x00000006 0x00000002 0x00000436 0x00000006 + 0x00000002 0x00000437 0x00000006 0x00000002 0x00000438 0x00000006 0x00000002 0x00000439 + 0x00000006 0x00000002 0x0000043a 0x00000006 0x00000002 0x0000043b 0x00000006 0x00000002 + 0x0000043c 0x00000006 0x00000002 0x0000043d 0x00000006 0x00000002 0x0000043e 0x00000006 + 0x00000002 0x0000043f 0x00000006 0x00000002 0x00000440 0x00000006 0x00000002 0x00000441 + 0x00000006 0x00000002 0x00000442 0x00000006 0x00000002 0x00000443 0x00000006 0x00000002 + 0x00000444 0x00000006 0x00000002 0x00000445 0x00000006 0x00000002 0x00000446 0x00000006 + 0x00000002 0x00000447 0x00000006 0x00000002 0x00000448 0x00000006 0x00000002 0x00000449 + 0x00000006 0x00000002 0x0000044a 0x00000006 0x00000002 0x0000044b 0x00000006 0x00000002 + 0x0000044c 0x00000006 0x00000002 0x0000044d 0x00000006 0x00000002 0x0000044e 0x00000006 + 0x00000002 0x0000044f 0x00000006 0x00000002 0x00000450 0x00000006 0x00000002 0x00000451 + 0x00000006 0x00000002 0x00000452 0x00000006 0x00000002 0x00000453 0x00000006 0x00000002 + 0x00000454 0x00000006 0x00000002 0x00000455 0x00000006 0x00000002 0x00000456 0x00000006 + 0x00000002 0x00000457 0x00000006 0x00000002 0x00000458 0x00000006 0x00000002 0x00000459 + 0x00000006 0x00000002 0x0000045a 0x00000006 0x00000002 0x0000045b 0x00000006 0x00000002 + 0x0000045c 0x00000006 0x00000002 0x0000045d 0x00000006 0x00000002 0x0000045e 0x00000006 + 0x00000002 0x0000045f 0x00000006 0x00000002 0x00000460 0x00000006 0x00000002 0x00000461 + 0x00000006 0x00000002 0x00000462 0x00000006 0x00000002 0x00000463 0x00000006 0x00000002 + 0x00000464 0x00000006 0x00000002 0x00000465 0x00000006 0x00000002 0x00000466 0x00000006 + 0x00000002 0x00000467 0x00000006 0x00000002 0x00000468 0x00000006 0x00000002 0x00000469 + 0x00000006 0x00000002 0x0000046a 0x00000006 0x00000002 0x0000046b 0x00000006 0x00000002 + 0x0000046c 0x00000006 0x00000002 0x0000046d 0x00000006 0x00000002 0x0000046e 0x00000006 + 0x00000002 0x0000046f 0x00000006 0x00000002 0x00000470 0x00000006 0x00000002 0x00000471 + 0x00000006 0x00000002 0x00000472 0x00000006 0x00000002 0x00000473 0x00000006 0x00000002 + 0x00000474 0x00000006 0x00000002 0x00000475 0x00000006 0x00000002 0x00000476 0x00000006 + 0x00000002 0x00000477 0x00000006 0x00000002 0x00000478 0x00000006 0x00000002 0x00000479 + 0x00000006 0x00000002 0x0000047a 0x00000006 0x00000002 0x0000047b 0x00000006 0x00000002 + 0x0000047c 0x00000006 0x00000002 0x0000047d 0x00000006 0x00000002 0x0000047e 0x00000006 + 0x00000002 0x0000047f 0x00000006 0x00000002 0x00000480 0x00000006 0x00000002 0x00000481 + 0x00000006 0x00000002 0x00000482 0x00000006 0x00000002 0x00000483 0x00000006 0x00000002 + 0x00000484 0x00000006 0x00000002 0x00000485 0x00000006 0x00000002 0x00000486 0x00000006 + 0x00000002 0x00000487 0x00000006 0x00000002 0x00000488 0x00000006 0x00000002 0x00000489 + 0x00000006 0x00000002 0x0000048a 0x00000006 0x00000002 0x0000048b 0x00000006 0x00000002 + 0x0000048c 0x00000006 0x00000002 0x0000048d 0x00000006 0x00000002 0x0000048e 0x00000006 + 0x00000002 0x0000048f 0x00000006 0x00000002 0x00000490 0x00000006 0x00000002 0x00000491 + 0x00000006 0x00000002 0x00000492 0x00000006 0x00000002 0x00000493 0x00000006 0x00000002 + 0x00000494 0x00000006 0x00000002 0x00000495 0x00000006 0x00000002 0x00000496 0x00000006 + 0x00000002 0x00000497 0x00000006 0x00000002 0x00000498 0x00000006 0x00000002 0x00000499 + 0x00000006 0x00000002 0x0000049a 0x00000006 0x00000002 0x0000049b 0x00000006 0x00000002 + 0x0000049c 0x00000006 0x00000002 0x0000049d 0x00000006 0x00000002 0x0000049e 0x00000006 + 0x00000002 0x0000049f 0x00000006 0x00000002 0x000004a0 0x00000006 0x00000002 0x000004a1 + 0x00000006 0x00000002 0x000004a2 0x00000006 0x00000002 0x000004a3 0x00000006 0x00000002 + 0x000004a4 0x00000006 0x00000002 0x000004a5 0x00000006 0x00000002 0x000004a6 0x00000006 + 0x00000002 0x000004a7 0x00000006 0x00000002 0x000004a8 0x00000006 0x00000002 0x000004a9 + 0x00000006 0x00000002 0x000004aa 0x00000006 0x00000002 0x000004ab 0x00000006 0x00000002 + 0x000004ac 0x00000006 0x00000002 0x000004ad 0x00000006 0x00000002 0x000004ae 0x00000006 + 0x00000002 0x000004af 0x00000006 0x00000002 0x000004b0 0x00000006 0x00000002 0x000004b1 + 0x00000006 0x00000002 0x000004b2 0x00000006 0x00000002 0x000004b3 0x00000006 0x00000002 + 0x000004b4 0x00000006 0x00000002 0x000004b5 0x00000006 0x00000002 0x000004b6 0x00000006 + 0x00000002 0x000004b7 0x00000006 0x00000002 0x000004b8 0x00000006 0x00000002 0x000004b9 + 0x00000006 0x00000002 0x000004ba 0x00000006 0x00000002 0x000004bb 0x00000006 0x00000002 + 0x000004bc 0x00000006 0x00000002 0x000004bd 0x00000006 0x00000002 0x000004be 0x00000006 + 0x00000002 0x000004bf 0x00000006 0x00000002 0x000004c0 0x00000006 0x00000002 0x000004c1 + 0x00000006 0x00000002 0x000004c2 0x00000006 0x00000002 0x000004c3 0x00000006 0x00000002 + 0x000004c4 0x00000006 0x00000002 0x000004c5 0x00000006 0x00000002 0x000004c6 0x00000006 + 0x00000002 0x000004c7 0x00000006 0x00000002 0x000004c8 0x00000006 0x00000002 0x000004c9 + 0x00000006 0x00000002 0x000004ca 0x00000006 0x00000002 0x000004cb 0x00000006 0x00000002 + 0x000004cc 0x00000006 0x00000002 0x000004cd 0x00000006 0x00000002 0x000004ce 0x00000006 + 0x00000002 0x000004cf 0x00000006 0x00000002 0x000004d0 0x00000006 0x00000002 0x000004d1 + 0x00000006 0x00000002 0x000004d2 0x00000006 0x00000002 0x000004d3 0x00000006 0x00000002 + 0x000004d4 0x00000006 0x00000002 0x000004d5 0x00000006 0x00000002 0x000004d6 0x00000006 + 0x00000002 0x000004d7 0x00000006 0x00000002 0x000004d8 0x00000006 0x00000002 0x000004d9 + 0x00000006 0x00000002 0x000004da 0x00000006 0x00000002 0x000004db 0x00000006 0x00000002 + 0x000004dc 0x00000006 0x00000002 0x000004dd 0x00000006 0x00000002 0x000004de 0x00000006 + 0x00000002 0x000004df 0x00000006 0x00000002 0x000004e0 0x00000006 0x00000002 0x000004e1 + 0x00000006 0x00000002 0x000004e2 0x00000006 0x00000002 0x000004e3 0x00000006 0x00000002 + 0x000004e4 0x00000006 0x00000002 0x000004e5 0x00000006 0x00000002 0x000004e6 0x00000006 + 0x00000002 0x000004e7 + + HelperDllName = REG_EXPAND_SZ %SystemRoot%\system32\wshisn.dll + MinSockaddrLength = REG_DWORD 0x0000000e + MaxSockaddrLength = REG_DWORD 0x00000010 + Performance + Library = Perfctrs.dll + Open = OpenNbfPerformanceData + Collect = CollectNbfPerformanceData + Close = CloseNbfPerformanceData +\Registry\Machine\System\CurrentControlSet\Services\EventLog\System\NwlnkIpx + EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\netevent.dll + TypesSupported = REG_DWORD 0x00000007 diff --git a/private/ntos/tdi/isnp/ipx/nwlnkipx.rc b/private/ntos/tdi/isnp/ipx/nwlnkipx.rc new file mode 100644 index 000000000..0f437a15d --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/nwlnkipx.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 IPX Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkipx.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkipx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/ipx/packet.c b/private/ntos/tdi/isnp/ipx/packet.c new file mode 100644 index 000000000..f70154b03 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/packet.c @@ -0,0 +1,1560 @@ +/*++ +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the SEND_PACKET and + RECEIVE_PACKET objects, which describe NDIS packets used + by the transport. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +IpxInitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + + Header - Points to storage for the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisMacBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + + IpxAllocateSendPacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisMacBuffer, + Device->NdisBufferPoolHandle, + Header, + MAC_HEADER_SIZE); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisIpxBuffer, + Device->NdisBufferPoolHandle, + Header + MAC_HEADER_SIZE, + IPX_HEADER_SIZE + RIP_PACKET_SIZE); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + IpxFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisChainBufferAtFront (PACKET(Packet), NdisMacBuffer); + NdisChainBufferAtBack (PACKET(Packet), NdisIpxBuffer); + + // + // This flag optimizes the virtual to physical address X-ln + // in the MAC drivers on x86 + // + NdisMacBuffer->MdlFlags|=MDL_NETWORK_HEADER; + NdisIpxBuffer->MdlFlags|=MDL_NETWORK_HEADER; + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->SendInProgress = FALSE; + Reserved->Header = Header; + Reserved->HeaderBuffer = NdisMacBuffer; + Reserved->PaddingBuffer = NULL; +#if BACK_FILL + Reserved->BackFill = FALSE; +#endif + + ExInterlockedInsertHeadList( + &Device->GlobalSendPacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeSendPacket */ + +#if BACK_FILL +NTSTATUS +IpxInitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet, + IN PUCHAR Header + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + + Header - Points to storage for the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisMacBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + + + IPX_DEBUG (PACKET, ("Initializing backfill packet\n")); + IpxAllocateSendPacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->SendInProgress = FALSE; + Reserved->Header = NULL; + Reserved->HeaderBuffer = NULL; + Reserved->PaddingBuffer = NULL; + Reserved->BackFill = TRUE; + + ExInterlockedInsertHeadList( + &Device->GlobalBackFillPacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + IPX_DEBUG (PACKET, ("Initializing backfill packet Done\n")); + return STATUS_SUCCESS; + +} /* IpxInitializeBackFillPacket */ +#endif + + +NTSTATUS +IpxInitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + PIPX_RECEIVE_RESERVED Reserved; + + IpxAllocateReceivePacket (Device, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_IPX; + Reserved->TransferInProgress = FALSE; + Reserved->SingleRequest = NULL; + Reserved->ReceiveBuffer = NULL; + InitializeListHead (&Reserved->Requests); + + ExInterlockedInsertHeadList( + &Device->GlobalReceivePacketList, + &Reserved->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeReceivePacket */ + + +NTSTATUS +IpxInitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a receive buffer by allocating + an NDIS_BUFFER to describe the data buffer. + +Arguments: + + Adapter - The adapter. + + ReceiveBuffer - The receive buffer to initialize. + + DataBuffer - The data buffer. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + PDEVICE Device = Adapter->Device; + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + DataBuffer, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReceiveBuffer->NdisBuffer = NdisBuffer; + ReceiveBuffer->Data = DataBuffer; + ReceiveBuffer->DataLength = 0; + + ExInterlockedInsertHeadList( + &Device->GlobalReceiveBufferList, + &ReceiveBuffer->GlobalLinkage, + &Device->Lock); + + return STATUS_SUCCESS; + +} /* IpxInitializeReceiveBuffer */ + + +NTSTATUS +IpxInitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a padding buffer by allocating + an NDIS_BUFFER to describe the data buffer. + +Arguments: + + Adapter - The adapter. + + PaddingBuffer - The receive buffer to initialize. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + PaddingBuffer->Data, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NDIS_BUFFER_LINKAGE(NdisBuffer) = (PNDIS_BUFFER)NULL; + PaddingBuffer->NdisBuffer = NdisBuffer; + PaddingBuffer->DataLength = DataBufferLength; + RtlZeroMemory (PaddingBuffer->Data, DataBufferLength); + + return STATUS_SUCCESS; + +} /* IpxInitializePaddingBuffer */ + + +VOID +IpxDeinitializeSendPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine deinitializes a send packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + + + Reserved = SEND_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + // + // Free the packet in a slightly unconventional way; this + // allows us to not have to NULL out HeaderBuffer's linkage + // field during normal operations when we put it back in + // the free pool. + // + + NdisBuffer = Reserved->HeaderBuffer; + NdisIpxBuffer = NDIS_BUFFER_LINKAGE(NdisBuffer); + NDIS_BUFFER_LINKAGE (NdisBuffer) = NULL; + NDIS_BUFFER_LINKAGE (NdisIpxBuffer) = NULL; + +#if 0 + NdisAdjustBufferLength (NdisBuffer, PACKET_HEADER_SIZE); +#endif + NdisAdjustBufferLength (NdisBuffer, MAC_HEADER_SIZE); + NdisAdjustBufferLength (NdisIpxBuffer, IPX_HEADER_SIZE + RIP_PACKET_SIZE); + + NdisFreeBuffer (NdisBuffer); + NdisFreeBuffer (NdisIpxBuffer); + + NdisReinitializePacket (PACKET(Packet)); + IpxFreeSendPacket (Device, Packet); + +} /* IpxDeinitializeSendPacket */ + +#if BACK_FILL +VOID +IpxDeinitializeBackFillPacket( + IN PDEVICE Device, + IN PIPX_SEND_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine deinitializes a back fill packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisIpxBuffer; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + + IPX_DEBUG (PACKET, ("DeInitializing backfill packet\n")); + + Reserved = SEND_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + + + NdisReinitializePacket (PACKET(Packet)); + IpxFreeSendPacket (Device, Packet); + IPX_DEBUG (PACKET, ("DeInitializing backfill packet Done\n")); + + +} /* IpxDeinitializeBackFillPacket */ +#endif + + +VOID +IpxDeinitializeReceivePacket( + IN PDEVICE Device, + IN PIPX_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + Reserved = RECEIVE_RESERVED(Packet); + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + IpxFreeReceivePacket (Device, Packet); + +} /* IpxDeinitializeReceivePacket */ + + +VOID +IpxDeinitializeReceiveBuffer( + IN PADAPTER Adapter, + IN PIPX_RECEIVE_BUFFER ReceiveBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a receive buffer. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer. + + DataBufferLength - The allocated length of the receive buffer. + +Return Value: + + None. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = Adapter->Device; + + CTEGetLock (&Device->Lock, &LockHandle); + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); + CTEFreeLock (&Device->Lock, LockHandle); + + NdisAdjustBufferLength (ReceiveBuffer->NdisBuffer, DataBufferLength); + NdisFreeBuffer (ReceiveBuffer->NdisBuffer); + +} /* IpxDeinitializeReceiveBuffer */ + + +VOID +IpxDeinitializePaddingBuffer( + IN PDEVICE Device, + IN PIPX_PADDING_BUFFER PaddingBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a padding buffer. + +Arguments: + + Device - The device. + + PaddingBuffer - The padding buffer. + + DataBufferLength - The allocated length of the padding buffer. + +Return Value: + + None. + +--*/ + +{ + + NdisAdjustBufferLength (PaddingBuffer->NdisBuffer, DataBufferLength); + NdisFreeBuffer (PaddingBuffer->NdisBuffer); + +} /* IpxDeinitializePaddingBuffer */ + + + +#ifdef IPX_OWN_PACKETS +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PIPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + CTELockHandle LockHandle; + + + SendPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams) + + (PACKET_HEADER_SIZE * Device->InitDatagrams); + + + SendPool = (PIPX_SEND_POOL)IpxAllocateMemory (SendPoolSize, MEMORY_PACKET, "SendPool"); + if (SendPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + + IPX_DEBUG (PACKET, ("Initializing send pool %lx, %d packets\n", + SendPool, Device->InitDatagrams)); + + Header = (PUCHAR)(&SendPool->Packets[Device->InitDatagrams]); + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + + if (IpxInitializeSendPacket (Device, Packet, Header) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + Header += PACKET_HEADER_SIZE; + + } + + SendPool->PacketCount = PacketNum; + SendPool->PacketFree = PacketNum; + + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < SendPool->PacketCount; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + Device->AllocatedDatagrams += SendPool->PacketCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateSendPool */ + +#if BACK_FILL + +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PIPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + CTELockHandle LockHandle; + + PIPX_SEND_POOL BackFillPool; + UINT BackFillPoolSize; + + IPX_DEBUG (PACKET, ("Allocating backfill pool\n")); + + + BackFillPoolSize = FIELD_OFFSET (IPX_SEND_POOL, Packets[0]) + + (sizeof(IPX_SEND_PACKET) * Device->InitDatagrams); + + + // Allocate pool for back fillable packets + + BackFillPool = (PIPX_SEND_POOL)IpxAllocateMemory (BackFillPoolSize, MEMORY_PACKET, "BafiPool"); + + if (BackFillPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate BackFill pool memory\n")); + return; + } + + + + + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + Packet = &BackFillPool->Packets[PacketNum]; + + if (IpxInitializeBackFillPacket (Device, Packet, NULL) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = BackFillPool; +#endif + + + } + + BackFillPool->PacketCount = PacketNum; + BackFillPool->PacketFree = PacketNum; + + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < BackFillPool->PacketCount; PacketNum++) { + + Packet = &BackFillPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->BackFillPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->BackFillPoolList, &BackFillPool->Linkage); + + + IPX_DEBUG (PACKET, ("Allocation of backfill pool done\n")); + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateBackFillPool */ + +#endif + + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_POOL ReceivePool; + UINT ReceivePoolSize; + UINT PacketNum; + PIPX_RECEIVE_PACKET Packet; + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + ReceivePoolSize = FIELD_OFFSET (IPX_RECEIVE_POOL, Packets[0]) + + (sizeof(IPX_RECEIVE_PACKET) * Device->InitReceivePackets); + + ReceivePool = (PIPX_RECEIVE_POOL)IpxAllocateMemory (ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + if (ReceivePool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitReceivePackets)); + + for (PacketNum = 0; PacketNum < Device->InitReceivePackets; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + + if (IpxInitializeReceivePacket (Device, Packet) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + } + + ReceivePool->PacketCount = PacketNum; + ReceivePool->PacketFree = PacketNum; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (PacketNum = 0; PacketNum < ReceivePool->PacketCount; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + Reserved = RECEIVE_RESERVED(Packet); + IPX_PUSH_ENTRY_LIST (&Device->ReceivePacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + Device->AllocatedReceivePackets += ReceivePool->PacketCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateReceivePool */ + + +#else // IPX_OWN_PACKETS +VOID +IpxAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_POOL SendPool; + UINT HeaderSize; + UINT PacketNum; + IPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PUCHAR Header; + NDIS_STATUS Status; + + CTELockHandle LockHandle; + + SendPool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_SEND_POOL), MEMORY_PACKET, "SendPool"); + + if (SendPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + HeaderSize = PACKET_HEADER_SIZE * Device->InitDatagrams; + + Header = (PUCHAR)IpxAllocateMemory (HeaderSize, MEMORY_PACKET, "SendPool"); + + if (Header == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate header memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &SendPool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n")); + return; + } + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitDatagrams * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED))); + + IPX_DEBUG (PACKET, ("Initializing send pool %lx, %d packets\n", + SendPool, Device->InitDatagrams)); + + SendPool->Header = Header; + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), SendPool->PoolHandle); + + if (IpxInitializeSendPacket (Device, &Packet, Header) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + Header += PACKET_HEADER_SIZE; + + } + + CTEGetLock (&Device->Lock, &LockHandle); + + Device->AllocatedDatagrams += PacketNum; + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateSendPool */ + + +#if BACK_FILL + +VOID +IpxAllocateBackFillPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + UINT PacketNum; + IPX_SEND_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + PIPX_SEND_POOL BackFillPool; + NDIS_STATUS Status; + + IPX_DEBUG (PACKET, ("Allocating backfill pool\n")); + + // Allocate pool for back fillable packets + + BackFillPool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_SEND_POOL), MEMORY_PACKET, "BafiPool"); + + if (BackFillPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate backfill pool memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &BackFillPool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate Ndis pool memory\n")); + return; + } + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitDatagrams * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_SEND_RESERVED))); + + for (PacketNum = 0; PacketNum < Device->InitDatagrams; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), BackFillPool->PoolHandle); + + if (IpxInitializeBackFillPacket (Device, &Packet, NULL) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = BackFillPool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->BackFillPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + } + + CTEGetLock (&Device->Lock, &LockHandle); + + InsertTailList (&Device->BackFillPoolList, &BackFillPool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateBackFillPool */ + +#endif + + +VOID +IpxAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive packets to the pool for this device. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_POOL ReceivePool; + UINT PacketNum; + IPX_RECEIVE_PACKET Packet; + PIPX_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + NDIS_STATUS Status; + + ReceivePool = (PIPX_SEND_POOL)IpxAllocateMemory (sizeof(IPX_RECEIVE_POOL), MEMORY_PACKET, "ReceivePool"); + + if (ReceivePool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + NdisAllocatePacketPool(&Status, &ReceivePool->PoolHandle, Device->InitDatagrams, sizeof(IPX_SEND_RESERVED)); + + if (Status == NDIS_STATUS_RESOURCES) { + IPX_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitReceivePackets)); + + Device->MemoryUsage += (FIELD_OFFSET(NDIS_PACKET_POOL,Buffer[0]) + + Device->InitReceivePackets * (FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + sizeof(IPX_RECEIVE_RESERVED))); + + for (PacketNum = 0; PacketNum < Device->InitReceivePackets; PacketNum++) { + + NdisAllocatePacket(&Status, &PACKET(&Packet), ReceivePool->PoolHandle); + + if (IpxInitializeReceivePacket (Device, &Packet) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(&Packet); + Reserved->Address = NULL; + Reserved->OwnedByAddress = FALSE; +#ifdef IPX_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + IPX_PUSH_ENTRY_LIST (&Device->ReceivePacketList, &Reserved->PoolLinkage, &Device->SListsLock); + + } + + CTEGetLock (&Device->Lock, &LockHandle); + + Device->AllocatedReceivePackets += PacketNum; + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + CTEFreeLock (&Device->Lock, LockHandle); +} /* IpxAllocateReceivePool */ +#endif // IPX_OWN_PACKETS + +VOID +IpxAllocateReceiveBufferPool( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this adapter. + +Arguments: + + Adapter - The adapter. + +Return Value: + + None. + +--*/ + +{ + PIPX_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PIPX_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PDEVICE Device = Adapter->Device; + UINT DataLength; + PUCHAR Data; + CTELockHandle LockHandle; + + DataLength = Adapter->MaxReceivePacketSize; + + ReceiveBufferPoolSize = FIELD_OFFSET (IPX_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(IPX_RECEIVE_BUFFER) * Device->InitReceiveBuffers) + + (DataLength * Device->InitReceiveBuffers); + + ReceiveBufferPool = (PIPX_RECEIVE_BUFFER_POOL)IpxAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + IPX_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + IPX_DEBUG (PACKET, ("Init recv buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitReceiveBuffers, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitReceiveBuffers]); + + + for (BufferNum = 0; BufferNum < Device->InitReceiveBuffers; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (IpxInitializeReceiveBuffer (Adapter, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + IPX_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + +#ifdef IPX_TRACK_POOL + ReceiveBuffer->Pool = ReceiveBufferPool; +#endif + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + + CTEGetLock (&Device->Lock, &LockHandle); + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + IPX_PUSH_ENTRY_LIST (&Adapter->ReceiveBufferList, &ReceiveBuffer->PoolLinkage, &Device->SListsLock); + + } + + InsertTailList (&Adapter->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Adapter->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + + CTEFreeLock (&Device->Lock, LockHandle); + +} /* IpxAllocateReceiveBufferPool */ + + +PSINGLE_LIST_ENTRY +IpxPopSendPacket( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedDatagrams < Device->MaxDatagrams) { + + // + // Allocate a pool and try again. + // + + IpxAllocateSendPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopSendPacket */ + +#if BACK_FILL + +PSINGLE_LIST_ENTRY +IpxPopBackFillPacket( + PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + IPX_DEBUG (PACKET, ("Popping backfill packet\n")); + + + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedDatagrams < Device->MaxDatagrams) { + + // + // Allocate a pool and try again. + // + + IpxAllocateBackFillPool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + + IPX_DEBUG (PACKET, ("Popping backfill packet done\n")); + return s; + + } else { + + return NULL; + + } + +} /* IpxPopBackFillPacket */ +#endif //BackFill + + +PSINGLE_LIST_ENTRY +IpxPopReceivePacket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + + s = IPX_POP_ENTRY_LIST( + &Device->ReceivePacketList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceivePackets < Device->MaxReceivePackets) { + + // + // Allocate a pool and try again. + // + + IpxAllocateReceivePool (Device); + s = IPX_POP_ENTRY_LIST( + &Device->ReceivePacketList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopReceivePacket */ + + +PSINGLE_LIST_ENTRY +IpxPopReceiveBuffer( + IN PADAPTER Adapter + ) + +/*++ + +Routine Description: + + This routine allocates a receive buffer from the adapter's pool. + If there are no buffers in the pool, it allocates one up to + the configured limit. + +Arguments: + + Adapter - Pointer to our adapter to charge the buffer to. + +Return Value: + + The pointer to the Linkage field in the allocated receive buffer. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PDEVICE Device = Adapter->Device; + + s = IPX_POP_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Device->SListsLock); + + if (s != NULL) { + return s; + } + + // + // No buffer in the pool, see if we can allocate more. + // + + if (Adapter->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + IpxAllocateReceiveBufferPool (Adapter); + s = IPX_POP_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Device->SListsLock); + + return s; + + } else { + + return NULL; + + } + +} /* IpxPopReceiveBuffer */ + + +PIPX_PADDING_BUFFER +IpxAllocatePaddingBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a padding buffer for use by all devices. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the allocated padding buffer. + +--*/ + +{ + PIPX_PADDING_BUFFER PaddingBuffer; + ULONG PaddingBufferSize; + + // + // We are assuming that we can use 1 global padding buffer for ALL + // transmits! We must therefore test to make sure that EthernetExtraPadding + // is not greater than 1. Otherwise, we must assume that the extra padding + // is being used for something and we therefore cannot share across all + // transmit requests. + // + + // + // We cannot support more than 1 byte padding space, since we allocate only + // one buffer for all transmit requests. + // + + if ( Device->EthernetExtraPadding > 1 ) { + IPX_DEBUG (PACKET, ("Padding buffer cannot be more than 1 byte\n")); + DbgBreakPoint(); + } + + // + // Allocate a padding buffer if possible. + // + + PaddingBufferSize = FIELD_OFFSET (IPX_PADDING_BUFFER, Data[0]) + Device->EthernetExtraPadding; + + PaddingBuffer = IpxAllocateMemory (PaddingBufferSize, MEMORY_PACKET, "PaddingBuffer"); + + if (PaddingBuffer != NULL) { + + if (IpxInitializePaddingBuffer (Device, PaddingBuffer, Device->EthernetExtraPadding) != + STATUS_SUCCESS) { + IpxFreeMemory (PaddingBuffer, PaddingBufferSize, MEMORY_PACKET, "Padding Buffer"); + } else { + IPX_DEBUG (PACKET, ("Allocate padding buffer %lx\n", PaddingBuffer)); + return PaddingBuffer; + } + } + + return NULL; + +} /* IpxAllocatePaddingBuffer */ + + +VOID +IpxFreePaddingBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine deallocates the padding buffer. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + None + +--*/ + +{ + ULONG PaddingBufferSize; + + if ( IpxPaddingBuffer == (PIPX_PADDING_BUFFER)NULL ) { + return; + } + + PaddingBufferSize = FIELD_OFFSET (IPX_PADDING_BUFFER, Data[0]) + Device->EthernetExtraPadding; + IpxFreeMemory( IpxPaddingBuffer, PaddingBufferSize, MEMORY_PACKET, "Padding Buffer" ); + IpxPaddingBuffer = (PIPX_PADDING_BUFFER)NULL; + +} /* IpxFreePaddingBuffer */ + diff --git a/private/ntos/tdi/isnp/ipx/precomp.h b/private/ntos/tdi/isnp/ipx/precomp.h new file mode 100644 index 000000000..818629e5e --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/precomp.h @@ -0,0 +1,44 @@ +/*++ + +Copyright (c) 1993-1995 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompilation header file. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + +#include <ntos.h> +#include <tdikrnl.h> +#include <ndis.h> +#include <cxport.h> +#include <bind.h> +#include "isnipx.h" +#include "config.h" +#include "mac.h" +#include "ipxtypes.h" +#include "ipxprocs.h" +#include <wsnwlink.h> diff --git a/private/ntos/tdi/isnp/ipx/query.c b/private/ntos/tdi/isnp/ipx/query.c new file mode 100644 index 000000000..28b38df5c --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/query.c @@ -0,0 +1,297 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + query.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Useful macro to obtain the total length of an MDL chain. +// + +#define IpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + + +NTSTATUS +IpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PADDRESS_FILE AddressFile; + ULONG ElementSize, TransportAddressSize; + PTRANSPORT_ADDRESS TransportAddress; + TA_ADDRESS UNALIGNED * CurAddress; + PBINDING Binding; + union { + struct { + ULONG ActivityCount; + TA_IPX_ADDRESS IpxAddress; + } AddressInfo; + TDI_DATAGRAM_INFO DatagramInfo; + TDI_ADDRESS_IPX IpxAddress; + } TempBuffer; + UINT i; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // what type of status do we want? + // + + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (query->QueryType) { + + case TDI_QUERY_ADDRESS_INFO: + + // + // The caller wants the exact address value. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + status = IpxVerifyAddressFile (AddressFile); + + if (status == STATUS_SUCCESS) { + + TempBuffer.AddressInfo.ActivityCount = 0; + + IpxBuildTdiAddress( + &TempBuffer.AddressInfo.IpxAddress, + Device->SourceAddress.NetworkAddress, + Device->SourceAddress.NodeAddress, + AddressFile->Address->Socket); + + status = TdiCopyBufferToMdl( + &TempBuffer.AddressInfo, + 0, + sizeof(TempBuffer.AddressInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + IpxDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_PROVIDER_INFO: + + status = TdiCopyBufferToMdl ( + &(Device->Information), + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + status = TdiCopyBufferToMdl ( + &Device->Statistics, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; + TempBuffer.DatagramInfo.MaximumDatagramCount = 0; + + status = TdiCopyBufferToMdl ( + &TempBuffer.DatagramInfo, + 0, + sizeof(TempBuffer.DatagramInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS: + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + ElementSize = (2 * sizeof(USHORT)) + 6; + } else { + ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); + } + + TransportAddress = IpxAllocateMemory(sizeof(int) + (ElementSize * MIN (Device->MaxBindings, Device->ValidBindings)), MEMORY_QUERY, "NetworkAddress"); + + if (TransportAddress == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + TransportAddress->TAAddressCount = 0; + TransportAddressSize = sizeof(int); + CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->ValidBindings); + + for (i = 1; i <= Index; i++) { + + Binding = NIC_ID_TO_BINDING(Device, i); + if ((Binding == NULL) || + (!Binding->LineUp)) { + continue; + } + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = Binding->Adapter->MacInfo.RealMediumType; + RtlCopyMemory (CurAddress->Address, Binding->LocalAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } + } + + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + for (i = 1; i <= Device->ValidBindings; i++) { + + Binding = Device->Bindings[i]; + if ((Binding == NULL) || + (!Binding->LineUp)) { + continue; + } + + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = Binding->Adapter->MacInfo.RealMediumType; + RtlCopyMemory (CurAddress->Address, Binding->LocalAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &Binding->LocalAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } +#endif + status = TdiCopyBufferToMdl ( + TransportAddress, + 0, + TransportAddressSize, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + CTEFreeMem (TransportAddress); + + } + + break; + + default: + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} /* IpxTdiQueryInformation */ + + +NTSTATUS +IpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} /* IpxTdiSetInformation */ + + diff --git a/private/ntos/tdi/isnp/ipx/receive.c b/private/ntos/tdi/isnp/ipx/receive.c new file mode 100644 index 000000000..ff9c68fbd --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/receive.c @@ -0,0 +1,466 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiReceiveDatagram + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +IpxTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that an NdisTransferData has completed. We use this indication + to complete any pended requests to our clients. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + NdisPacket/RequestHandle - An identifier for the request that completed. + + NdisStatus - The completion status for the request. + + BytesTransferred - Number of bytes actually transferred. + + +Return Value: + + None. + +--*/ + +{ + PADAPTER Adapter = (PADAPTER)BindingContext; + PIPX_RECEIVE_RESERVED Reserved = (PIPX_RECEIVE_RESERVED)(NdisPacket->ProtocolReserved); + PREQUEST Request, LastRequest; + PADDRESS_FILE AddressFile; + ULONG ByteOffset; + PLIST_ENTRY p; + PDEVICE Device; + + + switch (Reserved->Identifier) { + + case IDENTIFIER_IPX: + + if (Reserved->SingleRequest) { + + // + // The transfer was directly into the client buffer, + // so simply complete the request. + // + + Request = Reserved->SingleRequest; + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + IPX_DEBUG (RECEIVE, ("Transferred %d bytes\n", BytesTransferred)); + REQUEST_INFORMATION(Request) = BytesTransferred; + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RECEIVE, ("Transfer failed\n")); + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR; + + } + + LastRequest = Request; + Reserved->SingleRequest = NULL; + + } else { + + // + // Multiple clients requested this datagram. Save + // the last one to delay queueing it for completion. + // + + LastRequest = LIST_ENTRY_TO_REQUEST (Reserved->Requests.Blink); + + while (TRUE) { + + p = RemoveHeadList (&Reserved->Requests); + if (p == &Reserved->Requests) { + break; + } + + Request = LIST_ENTRY_TO_REQUEST(p); + AddressFile = REQUEST_OPEN_CONTEXT(Request); + + if (AddressFile->ReceiveIpxHeader) { + ByteOffset = 0; + } else { + ByteOffset = sizeof(IPX_HEADER); + } + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + REQUEST_STATUS(Request) = + TdiCopyBufferToMdl( + Reserved->ReceiveBuffer->Data, + ByteOffset + REQUEST_INFORMATION(Request), + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))->ReceiveLength, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } else { + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR; + + } + + if (Request != LastRequest) { + + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(Request), + Adapter->DeviceLock); + + } + + } + + // + // Now free the receive buffer back. + // + + IPX_PUSH_ENTRY_LIST( + &Adapter->ReceiveBufferList, + &Reserved->ReceiveBuffer->PoolLinkage, + &Adapter->Device->SListsLock); + + Reserved->ReceiveBuffer = NULL; + + } + + + // + // Now free the packet. + // + + NdisReinitializePacket (NdisPacket); + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->ReceivePacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->ReceivePacketInUse); + + } else { + + Device = Adapter->Device; + + IPX_PUSH_ENTRY_LIST( + &Device->ReceivePacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + + + // + // We Delay inserting the last request (or the only one) + // until after we have put the packet back, to keep the + // address around if needed (the address won't go away + // until the last address file does, and the address file + // won't go away until the datagram is completed). + // + + IPX_INSERT_TAIL_LIST( + &Adapter->RequestCompletionQueue, + REQUEST_LINKAGE(LastRequest), + Adapter->DeviceLock); + + IpxReceiveComplete ((NDIS_HANDLE)Adapter); + + break; + + default: + + Device = Adapter->Device; + + (*Device->UpperDrivers[Reserved->Identifier].TransferDataCompleteHandler)( + NdisPacket, + NdisStatus, + BytesTransferred); + + break; + + } + +} /* IpxTransferDataComplete */ + + +VOID +IpxTransferData( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called by all tightly bound clients instead of NdisTransferData. + If this is a loopback packet, the transfer is done directly here, else NdisTransferData + is called. + +Arguments: + + Status - status of operation + NdisBindingHandle - Loopback cookie or Ndis context + MacReceiveContext - Loopback packet or Mac context + ByteOffset - Source offset + BytesToTransfer - length of the transfer desired + Packet - dest packet + BytesTransferred - length of successful transfer + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + // + // If this is a loopback packet, copy the data directly + // + if (NdisBindingHandle == (PVOID)IPX_LOOPBACK_COOKIE) { + + IPX_DEBUG (LOOPB, ("LoopbXfer: src: %lx, dest: %lx, bytestoxfer: %lx\n", + MacReceiveContext, Packet, BytesToTransfer)); + + NdisCopyFromPacketToPacket( + Packet, // Destination + 0, // DestinationOffset + BytesToTransfer, // BytesToCopy + (PNDIS_PACKET)MacReceiveContext, // Source + ByteOffset, // SourceOffset + BytesTransferred); // BytesCopied + + *Status = NDIS_STATUS_SUCCESS; + } else { + NdisTransferData( + Status, + NdisBindingHandle, + MacReceiveContext, + ByteOffset, + BytesToTransfer, + Packet, + BytesTransferred); + } +} + + + +NTSTATUS +IpxTdiReceiveDatagram( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PADDRESS Address; + PADDRESS_FILE AddressFile; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + // + // Do a quick check of the validity of the address. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != IPX_ADDRESSFILE_SIGNATURE)) { + + return STATUS_INVALID_HANDLE; + } + + Address = AddressFile->Address; + + if ((Address == NULL) || + (Address->Size != sizeof (ADDRESS)) || + (Address->Type != IPX_ADDRESS_SIGNATURE)) { + + return STATUS_INVALID_HANDLE; + } + + IPX_BEGIN_SYNC (&SyncContext); + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IPX_END_SYNC (&SyncContext); + return STATUS_INVALID_HANDLE; + } + + + InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request)); + + IoSetCancelRoutine (Request, IpxCancelReceiveDatagram); + + if (Request->Cancel) { + + (VOID)RemoveTailList (&AddressFile->ReceiveDatagramQueue); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IPX_END_SYNC (&SyncContext); + return STATUS_CANCELLED; + } + + IPX_DEBUG (RECEIVE, ("RDG posted on %lx\n", AddressFile)); + + IpxReferenceAddressFileLock (AddressFile, AFREF_RCV_DGRAM); + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + IPX_END_SYNC (&SyncContext); + + return STATUS_PENDING; + +} /* IpxTdiReceiveDatagram */ + + +VOID +IpxCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The datagram is found on the address file's receive + datagram queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN Found; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Address = AddressFile->Address; + + Found = FALSE; + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + IPX_DEBUG(RECEIVE, ("Cancelled datagram on %lx\n", AddressFile)); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + IpxCompleteRequest (Request); + ASSERT( DeviceObject->DeviceExtension == IpxDevice ); + IpxFreeRequest(IpxDevice, Request); + + IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + +} /* IpxCancelReceiveDatagram */ + + diff --git a/private/ntos/tdi/isnp/ipx/rip.c b/private/ntos/tdi/isnp/ipx/rip.c new file mode 100644 index 000000000..d30770223 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/rip.c @@ -0,0 +1,2655 @@ +/*++ + + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rip.c + +Abstract: + + This module contains code that implements the client-side + RIP support and simple router table support. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +NTSTATUS +RipGetLocalTarget( + IN ULONG Segment, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN UCHAR Type, + OUT PIPX_LOCAL_TARGET LocalTarget, + OUT USHORT Counts[2] OPTIONAL + ) + +/*++ + +Routine Description: + + This routine looks up the proper route for the specified remote + address. If a RIP request needs to be generated it does so. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD. + NOTE: IN THE CASE OF PnP, THIS COMES WITH THE BIND LOCK SHARED. + +Arguments: + + Segment - The segment associate with the remote address. + + RemoteAddress - The IPX address of the remote. + + Type - One of IPX_FIND_ROUTE_NO_RIP, IPX_FIND_ROUTE_RIP_IF_NEEDED, + or IPX_FIND_ROUTE_FORCE_RIP. + + LocalTarget - Returns the next router information. + + Counts - If specified, used to return the tick and hop count. + +Return Value: + + STATUS_SUCCESS if a route is found, STATUS_PENDING if a + RIP request needs to be generated, failure status if a + RIP request packet cannot be allocated. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + PBINDING Binding; + UINT i; + + + // + // Packets sent to network 0 go on the first adapter also. + // + + if (RemoteAddress->NetworkAddress == 0) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 1); + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed) / + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#else + LocalTarget->NicId = 1; + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + Device->Bindings[1]->MediumSpeed) / + Device->Bindings[1]->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#endif + return STATUS_SUCCESS; + } + + // + // See if this is a packet sent to our virtual network. + // + + if (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)) { + + // + // Send it through adapter 1. + // BUGBUG: Do real loopback. + // +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 0); + RtlCopyMemory (LocalTarget->MacAddress, NIC_ID_TO_BINDING(Device, 1)->LocalMacAddress.Address, 6); +#else + // + // Loopback this packet + // + LocalTarget->NicId = 0; + RtlCopyMemory (LocalTarget->MacAddress, Device->Bindings[1]->LocalMacAddress.Address, 6); +#endif + + IPX_DEBUG (LOOPB, ("Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = 1; // tick count + Counts[1] = 1; // hop count + } + return STATUS_SUCCESS; + + } + + // + // Look up the route in the table. If the net is one + // of the ones we are directly attached to, this will + // return an entry with the correct flag set. + // + + RouteEntry = RipGetRoute(Segment, (PUCHAR)&(RemoteAddress->NetworkAddress)); + + if (RouteEntry != NULL) { + + RouteEntry->Timer = 0; +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, RouteEntry->NicId); +#else + LocalTarget->NicId = RouteEntry->NicId; +#endif + if (RouteEntry->Flags & IPX_ROUTER_LOCAL_NET) { + + // + // The machine is on the same net, so send it directly. + // + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + + if (RouteEntry->Flags & IPX_ROUTER_GLOBAL_WAN_NET) { + + // + // The NicId here is bogus, we have to scan through + // our bindings until we find one whose indicated + // IPX remote node matches the destination node of + // this frame. We don't scan into the duplicate + // binding set members since they won't be WANs. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != (PBINDING)NULL) && + (Binding->Adapter->MacInfo.MediumAsync) && + (RtlEqualMemory( + Binding->WanRemoteNode, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + break; + + } + } + } + + if (i > (UINT)MIN (Device->MaxBindings, Device->HighestExternalNicId)) { + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + } + + } else { + // + // Find out if this is a loopback packet. If so, return NicId 0 + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + // + // Self-directed - loopback + // + if ((Binding != (PBINDING)NULL) && + (RtlEqualMemory( + Binding->LocalAddress.NodeAddress, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 0); +#else + LocalTarget->NicId = 0; +#endif + + IPX_DEBUG (LOOPB, ("2.Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + break; + + } + } + } + } + + } else { + + CTEAssert ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0); + + // + // This is not a locally attached net, so if the caller + // is forcing a re-RIP then do that. + // + + if (Type == IPX_FIND_ROUTE_FORCE_RIP) { + goto QueueUpRequest; + } + + // + // Fill in the address of the next router in the route. + // + + RtlCopyMemory (LocalTarget->MacAddress, RouteEntry->NextRouter, 6); + + } + + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = RouteEntry->TickCount; + Counts[1] = RouteEntry->HopCount; + } + + return STATUS_SUCCESS; + + } + +QueueUpRequest: + + if (Type == IPX_FIND_ROUTE_NO_RIP) { + + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + + } else { + + return RipQueueRequest (RemoteAddress->NetworkAddress, RIP_REQUEST); + + } + +} /* RipGetLocalTarget */ + + +NTSTATUS +RipQueueRequest( + IN ULONG Network, + IN USHORT Operation + ) + +/*++ + +Routine Description: + + This routine queues up a request for a RIP route. It can be + used to find a specific route or to discover the locally + attached network (if Network is 0). It can also be used + to do a periodic announcement of the virtual net, which + we do once a minute if the router is not bound. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD + IF IT IS A REQUEST AND THE NETWORK IS NOT 0xffffffff. + +Arguments: + + Network - The network to discover. + + Operation - One of RIP_REQUEST, RIP_RESPONSE, or RIP_DOWN. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_SEND_RESERVED Reserved; + PSINGLE_LIST_ENTRY s; + PLIST_ENTRY p; + PRIP_PACKET RipPacket; + TDI_ADDRESS_IPX RemoteAddress; + TDI_ADDRESS_IPX LocalAddress; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PNDIS_BUFFER pNdisIpxBuff; + + + // + // Make sure we only queue a request for net 0xffffffff if we + // are auto-detecting, because we assume that in other places. + // + + if ((Network == 0xffffffff) && + (Device->AutoDetectState != AUTO_DETECT_STATE_RUNNING)) { + + return STATUS_BAD_NETWORK_PATH; + + } + + // + // Try to get a packet to use for the RIP request. We + // allocate this now, but check if it succeeded later, + // to make the locking work better (we need to keep + // the lock between when we check for an existing + // request on this network and when we queue this + // request). + // + + s = IpxPopSendPacket (Device); + + // + // There was no router table entry for this network, first see + // if there is already a pending request for this route. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if (Operation == RIP_REQUEST) { + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + // + // Skip responses. + // + + if (Reserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (Reserved->u.SR_RIP.Network == Network && + !Reserved->u.SR_RIP.RouteFound) { + + // + // There is already one pending, put back the packet if + // we got one (we hold the lock already). + // + + if (s != NULL) { + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, s, &Device->SListsLock); + } + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + } + } + + } + + + if (s == NULL) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_INTERNAL; + Reserved->SendInProgress = FALSE; + Reserved->DestinationType = DESTINATION_BCAST; + Reserved->u.SR_RIP.CurrentNicId = 0; + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + switch (Operation) { + case RIP_REQUEST: Reserved->u.SR_RIP.RetryCount = 0; break; + case RIP_RESPONSE: Reserved->u.SR_RIP.RetryCount = 0xfe; break; + case RIP_DOWN: Reserved->u.SR_RIP.RetryCount = 0xff; break; + } + Reserved->u.SR_RIP.RouteFound = FALSE; + Reserved->u.SR_RIP.Network = Network; + Reserved->u.SR_RIP.SendTime = Device->RipSendTime; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // Fill in the IPX header at the standard offset (for sending + // to actual bindings it will be moved around if needed). We + // have to construct the local and remote addresses so they + // are in the format that IpxConstructHeader expects. + // + + RemoteAddress.NetworkAddress = Network; + RtlCopyMemory (RemoteAddress.NodeAddress, BroadcastAddress, 6); + RemoteAddress.Socket = RIP_SOCKET; + + RtlCopyMemory (&LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + LocalAddress.Socket = RIP_SOCKET; + + IpxConstructHeader( +// &Reserved->Header[Device->IncludedHeaderOffset], + &Reserved->Header[MAC_HEADER_SIZE], + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + &RemoteAddress, + &LocalAddress); + + // + // Fill in the RIP request also. + // + +#if 0 + RipPacket = (PRIP_PACKET)(&Reserved->Header[Device->IncludedHeaderOffset + sizeof(IPX_HEADER)]); +#endif + RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + RipPacket->Operation = Operation & 0x7fff; + RipPacket->NetworkEntry.NetworkNumber = Network; + + if (Operation == RIP_REQUEST) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(0xffff); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(0xffff); + } else if (Operation == RIP_RESPONSE) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(2); // will be modified when sent + } else { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(16); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(16); + } + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now insert this packet in the queue of pending RIP + // requests and start the timer if needed (this is done + // to ensure the RIP_GRANULARITY milliseconds inter-RIP-packet + // delay). + // + + IPX_DEBUG (RIP, ("RIP %s for network %lx\n", + (Operation == RIP_REQUEST) ? "request" : ((Operation == RIP_RESPONSE) ? "announce" : "down"), + REORDER_ULONG(Network))); + + InsertHeadList( + &Device->WaitingRipPackets, + &Reserved->WaitLinkage); + + ++Device->RipPacketCount; + + if (!Device->RipShortTimerActive) { + + Device->RipShortTimerActive = TRUE; + IpxReferenceDevice (Device, DREF_RIP_TIMER); + + CTEStartTimer( + &Device->RipShortTimer, + 1, // 1 ms, i.e. expire immediately + RipShortTimeout, + (PVOID)Device); + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + +} /* RipQueueRequest */ + + +VOID +RipSendResponse( + IN PBINDING Binding, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget + ) + +/*++ + +Routine Description: + + This routine sends a respond to a RIP request from a client -- + this is only used if we have a virtual network and the router + is not bound, and somebody queries on the virtual network. + +Arguments: + + Binding - The binding on which the request was received. + + RemoteAddress - The IPX source address of the request. + + LocalTarget - The local target of the received packet. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PIPX_SEND_RESERVED Reserved; + TDI_ADDRESS_IPX LocalAddress; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + PRIP_PACKET RipPacket; + PDEVICE Device = IpxDevice; + PBINDING MasterBinding; + NDIS_STATUS NdisStatus; + USHORT TickCount; + PNDIS_BUFFER pNdisIpxBuff; + + // + // Get a packet to use for the RIP response. + // + + s = IpxPopSendPacket (Device); + + if (s == NULL) { + return; + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_RESPONSE; + Reserved->DestinationType = DESTINATION_DEF; + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // If this binding is a binding set member, round-robin through + // the various bindings when responding. We will get some natural + // round-robinning because broadcast requests are received on + // binding set members in turn, but they are only rotated once + // a second. + // + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + + // + // Fill in the IPX header at the correct offset. + // + + LocalAddress.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (LocalAddress.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + LocalAddress.Socket = RIP_SOCKET; +#if 0 + IpxHeader = (PIPX_HEADER)(&Reserved->Header[Binding->DefHeaderSize]); +#endif + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + IpxConstructHeader( + (PUCHAR)IpxHeader, + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + RemoteAddress, + &LocalAddress); + + // + // In case the request comes from net 0, fill that in too. + // + + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + + + // + // Fill in the RIP request. + // + + RipPacket = (PRIP_PACKET)(IpxHeader+1); + + RipPacket->Operation = RIP_RESPONSE; + RipPacket->NetworkEntry.NetworkNumber = Device->VirtualNetworkNumber; + + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + TickCount = (USHORT)(((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + IPX_DEBUG (RIP, ("RIP response for virtual network %lx\n", + REORDER_ULONG(Device->VirtualNetworkNumber))); + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + +#ifdef _PNP_POWER + if (Binding->BindingSetMember) { + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + } +#endif + return; + +} /* RipSendResponse */ + + +VOID +RipShortTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP short timer expires. + It is called every RIP_GRANULARITY milliseconds unless there + is nothing to do. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p; + PIPX_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + USHORT OldNicId, NewNicId; + ULONG OldOffset, NewOffset; + PIPX_HEADER IpxHeader; + PBINDING Binding, MasterBinding; + NDIS_STATUS NdisStatus; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + +#ifdef _PNP_LATER + static IPX_LOCAL_TARGET BroadcastTarget = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, {0, 0, 0} }; +#else + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif + + static ULONG ZeroNetwork = 0; +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->RipSendTime; + + if (Device->RipPacketCount == 0) { + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_TIMER); + + return; + } + + // + // Check what is on the queue; this is set up as a + // loop but in fact it rarely does (under no + // circumstances can we send more than one packet + // each time this function executes). + // + + while (TRUE) { + + p = Device->WaitingRipPackets.Flink; + if (p == &Device->WaitingRipPackets) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if ((Reserved->u.SR_RIP.RouteFound) && (!Reserved->SendInProgress)) { + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + Reserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + } + + if ((((SHORT)(Device->RipSendTime - Reserved->u.SR_RIP.SendTime)) < 0) || + Reserved->SendInProgress) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + + // + // Find the right binding to send to. If NoIdAdvance + // is set, then the binding doesn't need to be changed + // this time (this means we wrapped last time). + // + + OldNicId = Reserved->u.SR_RIP.CurrentNicId; + + if (!Reserved->u.SR_RIP.NoIdAdvance) { + + BOOLEAN FoundNext = FALSE; + +#ifdef _PNP_POWER +// +// To maintain the lock order, release Device lock here and re-acquire later +// + USHORT StartId; + + if (Device->ValidBindings == 0) { + IPX_DEBUG(PNP, ("ValidBindings 0 in RipShortTimeOut\n")); + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_TIMER); + return; + } + + StartId = (USHORT)((OldNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); + + NewNicId = StartId; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#else + + USHORT StartId = (USHORT)((OldNicId % Device->BindingCount) + 1); + + NewNicId = StartId; +#endif + do { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NewNicId); +#else + Binding = Device->Bindings[NewNicId]; +#endif + if (Reserved->u.SR_RIP.Network != 0xffffffff) { + + // + // We are looking for a real net; check that + // the next binding is valid. If it is a WAN + // binding, we don't send queries if the router + // is bound. If it is a LAN binding, we don't + // send queries if we are configured for + // SingleNetworkActive and the WAN is up. + // We also don't send queries on binding set + // members which aren't masters. + // + + if ((Binding != NULL) + && + ((!Binding->Adapter->MacInfo.MediumAsync) || + (!Device->UpperDriverBound[IDENTIFIER_RIP])) + && + ((Binding->Adapter->MacInfo.MediumAsync) || + (!Device->SingleNetworkActive) || + (!Device->ActiveNetworkWan)) + && + ((!Binding->BindingSetMember) || + (Binding->CurrentSendBinding))) { + + FoundNext = TRUE; + break; + } + + } else { + + // + // We are sending out the initial request to net + // 0xffffffff, to generate traffic so we can figure + // out our real network number. We don't do this + // to nets that already have a number and we don't + // do it on WAN links. We also don't do it on + // auto-detect nets if we have found the default. + // + + + if ((Binding != NULL) && + (Binding->TentativeNetworkAddress == 0) && + (!Binding->Adapter->MacInfo.MediumAsync) && + (!Binding->AutoDetect || !Binding->Adapter->DefaultAutoDetected)) { + FoundNext = TRUE; + break; + } + } +#ifdef _PNP_POWER + // + // [BUGBUGZZ] Why cycle thru the entire list? + // + NewNicId = (USHORT)((NewNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); +#else + NewNicId = (USHORT)((NewNicId % Device->BindingCount) + 1); +#endif + } while (NewNicId != StartId); + + if (!FoundNext) { + + // + // Nothing more needs to be done with this packet, + // leave it off the queue and since we didn't send + // a packet we can check for more. + // +#ifndef _PNP_POWER + // + // This was released above (before the BindAccessLock was taken + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + RipCleanupPacket(Device, Reserved); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + + } + +#ifdef _PNP_POWER + + IPX_DEBUG(RIP, ("RIP: FoundNext: %lx, StartId: %lx, OldNicId: %lx, NewNicId: %lx\n", FoundNext, StartId, OldNicId, NewNicId)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // Re-acquire the Device lock + // + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + + Reserved->u.SR_RIP.CurrentNicId = NewNicId; + + // + // Move the data around if needed. + // + +#if 0 + if (OldNicId != NewNicId) { + + if (OldNicId == 0) { + OldOffset = Device->IncludedHeaderOffset; + } else { + OldOffset = Device->Bindings[OldNicId]->BcMcHeaderSize; + } + + NewOffset = Binding->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + + } + + } +#endif + + if (NewNicId <= OldNicId) { + + // + // We found a new binding but we wrapped, so increment + // the counter. If we have done all the resends, or + // this is a response (indicated by retry count of 0xff; + // they are only sent once) then clean up. + // + + if ((Reserved->u.SR_RIP.RetryCount >= 0xfe) || + ((++Reserved->u.SR_RIP.RetryCount) == Device->RipCount)) { + + // + // This packet is stale, clean it up and continue. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + RipCleanupPacket(Device, Reserved); + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + // + // We wrapped, so put ourselves back in the queue + // at the end. + // + + Reserved->u.SR_RIP.SendTime = (USHORT)(Device->RipSendTime + Device->RipTimeout - 1); + Reserved->u.SR_RIP.NoIdAdvance = TRUE; + InsertTailList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + +#ifdef _PNP_POWER + // + // Free the Device lock before deref'ing the Binding so we maintain + // the lock order: BindingAccess > GlobalInterLock > Device + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + } + + continue; + + } +#ifdef _PNP_POWER +// +// To prevent the re-acquire of the device lock, this is moved up... +// + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + } else { + + // + // Next time we need to advance the binding. + // + + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + NewNicId = OldNicId; +#ifdef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NewNicId); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[NewNicId]; +#endif + + } +#ifndef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + // + // This packet should be sent on binding NewNicId; first + // move the data to the right location for the current + // binding. + // +#ifdef _PNP_POWER + CTEAssert (Binding == NIC_ID_TO_BINDING(Device, NewNicId)); // temp, just to make sure +#else + CTEAssert (Binding == Device->Bindings[NewNicId]); // temp, just to make sure +#endif +// NewOffset = Binding->BcMcHeaderSize; + + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&BroadcastTarget, NewNicId); +#else + BroadcastTarget.NicId = NewNicId; +#endif + + // + // Modify the header so the packet comes from this + // specific adapter, not the virtual network. + // + + // IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + if (Reserved->u.SR_RIP.Network == 0xffffffff) { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = 0; + } else { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + } + + if (Reserved->u.SR_RIP.RetryCount < 0xfe) { + + // + // This is an outgoing query. We round-robin these through + // binding sets. + // + + if (Binding->BindingSetMember) { + + // + // Shouldn't have any binding sets during initial + // discovery. + // + + CTEAssert (Reserved->u.SR_RIP.Network != 0xffffffff); + + // + // If we are in a binding set, then use the current binding + // in the set for this send, and advance the current binding. + // The places we have used Binding before here will be fine + // since the binding set members all have the same media + // and frame type. + // + + CTEAssert (Binding->CurrentSendBinding); // should be a master. + MasterBinding = Binding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; +#ifdef _PNP_POWER + // + // [BUGBUGZZ]: We dont have a lock here - the masterbinding could be bogus + // + IpxDereferenceBinding1(MasterBinding, BREF_DEVICE_ACCESS); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + } + + + RtlCopyMemory (IpxHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + // + // Bug# 6485 + // Rip request, general or specific, is putting the network of the + // node to which the route has to be found in the ipx header remote + // network field. Some novell routers don't like that. This network + // field should be 0. + // + { + PRIP_PACKET RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + + if (RipPacket->Operation != RIP_REQUEST) { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + } else { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = 0; + } + } + + // + // If this is a RIP_RESPONSE, set the tick count for this + // binding. + // + + if (Reserved->u.SR_RIP.RetryCount == 0xfe) { + + PRIP_PACKET RipPacket = (PRIP_PACKET)(IpxHeader+1); + USHORT TickCount = (USHORT) + (((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + } + + if ((NdisStatus = IpxSendFrame( + &BroadcastTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + + break; + + } + + CTEStartTimer( + &Device->RipShortTimer, + RIP_GRANULARITY, + RipShortTimeout, + (PVOID)Device); + +} /* RipShortTimeout */ + + +VOID +RipLongTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP long timer expires. + It is called every minute and handles periodic re-RIPping + to ensure that entries are accurate, as well as aging out + of entries if the rip router is not bound. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + UINT i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + + // + // Rotate the broadcast receiver on all binding sets. + // We can loop up to HighestExternal only since we + // are only interested in finding binding set masters. + // +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != NULL) && + (Binding->CurrentSendBinding)) { + + // + // It is a master, so find the current broadcast + // receiver, then advance it. + // + + while (TRUE) { + if (Binding->ReceiveBroadcast) { + Binding->ReceiveBroadcast = FALSE; + Binding->NextBinding->ReceiveBroadcast = TRUE; + break; + } else { + Binding = Binding->NextBinding; + } + } + } + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + + // + // If RIP is bound, we don't do any of this, and + // we stop the timer from running. + // + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + return; + } + + + // + // If we have a virtual net, do our periodic broadcast. + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + + // + // Update the real counters from the temp ones. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Device->TempDatagramBytesSent); + Device->Statistics.DatagramsSent += Device->TempDatagramsSent; + + Device->TempDatagramBytesSent = 0; + Device->TempDatagramsSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + Device->TempDatagramBytesReceived); + Device->Statistics.DatagramsReceived += Device->TempDatagramsReceived; + + Device->TempDatagramBytesReceived = 0; + Device->TempDatagramsReceived = 0; + + + // + // We need to scan each hash bucket to see if there + // are any active entries which need to be re-RIPped + // for. We also scan for entries that should be timed + // out. + // + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + RouterSegment = &IpxDevice->Segments[Segment]; + + // + // Don't take the lock if the bucket is empty. + // + + if (RouterSegment->Entries.Flink == &RouterSegment->Entries) { + continue; + } + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry looking for ones to age. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) { + continue; + } + + ++RouteEntry->Timer; + if (RouteEntry->Timer >= Device->RipUsageTime) { + + RipDeleteRoute (Segment, RouteEntry); + IpxFreeMemory(RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + continue; + + } + + // + // See if we should re-RIP for this segment. It has + // to have been around for RipAgeTime, and we also + // make sure that the Timer is not too high to + // prevent us from re-RIPping on unused routes. + // + + ++RouteEntry->PRIVATE.Reserved[0]; + + if ((RouteEntry->PRIVATE.Reserved[0] >= Device->RipAgeTime) && + (RouteEntry->Timer <= Device->RipAgeTime)) { + + // + // If we successfully queue a request, then reset + // Reserved[0] so we don't re-RIP for a while. + // + + if (RipQueueRequest (*(UNALIGNED ULONG *)RouteEntry->Network, RIP_REQUEST) == STATUS_PENDING) { + RouteEntry->PRIVATE.Reserved[0] = 0; + } + } + } + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + } + + + // + // Now restart the timer for the next timeout. + // + + if (Device->State == DEVICE_STATE_OPEN) { + + CTEStartTimer( + &Device->RipLongTimer, + 60000, // one minute timeout + RipLongTimeout, + (PVOID)Device); + + } else { + + // + // Send a DOWN packet if needed, then stop ourselves. + // + + if (Device->RipResponder) { + + if (RipQueueRequest (Device->VirtualNetworkNumber, RIP_DOWN) != STATUS_PENDING) { + + // + // We need to kick this event because the packet completion + // won't. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + } + + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + + } + +} /* RipLongTimeout */ + + +VOID +RipCleanupPacket( + IN PDEVICE Device, + IN PIPX_SEND_RESERVED RipReserved + ) + +/*++ + +Routine Description: + + This routine cleans up when a RIP packet times out. + +Arguments: + + Device - The device. + + RipReserved - The ProtocolReserved section of the RIP packet. + +Return Value: + + None. + +--*/ + +{ + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + if (RipReserved->u.SR_RIP.RetryCount < 0xfe) { + + if (RipReserved->u.SR_RIP.Network != 0xffffffff) { + + IPX_DEBUG (RIP, ("Timing out RIP for network %lx\n", + REORDER_ULONG(RipReserved->u.SR_RIP.Network))); + + Segment = RipGetSegment ((PUCHAR)&RipReserved->u.SR_RIP.Network); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipReserved->u.SR_RIP.Network), + LockHandle, + FALSE, + NULL, + 0, + 0); + + } else { + + // + // This was the initial query looking for networks -- + // signal the init thread which is waiting. + // + + IPX_DEBUG (AUTO_DETECT, ("Signalling auto-detect event\n")); + KeSetEvent( + &Device->AutoDetectEvent, + 0L, + FALSE); + + } + + } else if (RipReserved->u.SR_RIP.RetryCount == 0xff) { + + // + // This is a DOWN message, set the device event that + // is waiting for it to complete. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + + // + // Put the RIP packet back in the pool. + // + + RipReserved->Identifier = IDENTIFIER_IPX; + +} /* RipCleanupPacket */ + + +VOID +RipProcessResponse( + IN PDEVICE Device, + IN PIPX_LOCAL_TARGET LocalTarget, + IN RIP_PACKET UNALIGNED * RipPacket + ) + +/*++ + +Routine Description: + + This routine processes a RIP response from the specified + local target, indicating a route to the network in the RIP + header. + +Arguments: + + Device - The device. + + LocalTarget - The router that the frame was received from. + + RipPacket - The RIP response header. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED RipReserved; // ProtocolReserved of RIP packet + ULONG Segment; + PIPX_ROUTE_ENTRY RouteEntry, OldRouteEntry; + PLIST_ENTRY p; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + // + // Since we have received a RIP response for this network. + // kill the waiting RIP packets for it if it exists. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + RipReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if (RipReserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (RipReserved->u.SR_RIP.Network == + RipPacket->NetworkEntry.NetworkNumber) { + break; + } + + } + + if (p == &Device->WaitingRipPackets) { + + // + // No packets pending on this, return. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Put the RIP packet back in the pool. + // + + IPX_DEBUG (RIP, ("Got RIP response for network %lx\n", + REORDER_ULONG(RipPacket->NetworkEntry.NetworkNumber))); + + RipReserved->u.SR_RIP.RouteFound = TRUE; + if (!RipReserved->SendInProgress) { + + // + // If the send is done destroy it now, otherwise + // when it pops up in RipShortTimeout it will get + // destroyed because RouteFound is TRUE. + // + + RemoveEntryList (p); + RipReserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &RipReserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + } + + + // + // Try to allocate and add a router segment unless the + // RIP router is active...if we don't that is fine, we'll + // just re-RIP later. + // + + Segment = RipGetSegment ((PUCHAR)&RipPacket->NetworkEntry.NetworkNumber); + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry != (PIPX_ROUTE_ENTRY)NULL) { + + *(UNALIGNED LONG *)RouteEntry->Network = RipPacket->NetworkEntry.NetworkNumber; +#ifdef _PNP_POWER + RouteEntry->NicId = NIC_FROM_LOCAL_TARGET(LocalTarget); + RouteEntry->NdisBindingContext = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)->Adapter->NdisBindingHandle; + // BUGBUG: What if this is NULL?? +#else + RouteEntry->NicId = LocalTarget->NicId; + RouteEntry->NdisBindingContext = Device->Bindings[LocalTarget->NicId]->Adapter->NdisBindingHandle; // BUGBUG: What if this is NULL?? +#endif + RouteEntry->Flags = 0; + RouteEntry->Timer = 0; + RouteEntry->PRIVATE.Reserved[0] = 0; + RouteEntry->Segment = Segment; + RouteEntry->HopCount = REORDER_USHORT(RipPacket->NetworkEntry.HopCount); + RouteEntry->TickCount = REORDER_USHORT(RipPacket->NetworkEntry.TickCount); + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + RtlCopyMemory (RouteEntry->NextRouter, LocalTarget->MacAddress, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Replace any existing routes. This is OK because once + // we get the first response to a RIP packet on a given + // route, we will take the packet out of the queue and + // ignore further responses. We will only get a bad route + // if we do two requests really quickly and there + // are two routes, and the second response to the first + // request is picked up as the first response to the second + // request. + // + + if ((OldRouteEntry = RipGetRoute (Segment, (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber))) != NULL) { + + // + // These are saved so timeouts etc. happen right. + // + + RouteEntry->Flags = OldRouteEntry->Flags; + RouteEntry->Timer = OldRouteEntry->Timer; + + RipDeleteRoute (Segment, OldRouteEntry); + IpxFreeMemory(OldRouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + + } + + RipAddRoute (Segment, RouteEntry); + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + // + // Complete all datagrams etc. that were waiting + // for this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber), + LockHandle, + TRUE, + LocalTarget, + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.HopCount)), + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.TickCount)) + ); + +} /* RipProcessResponse */ + +VOID +RipHandleRoutePending( + IN PDEVICE Device, + IN UCHAR Network[4], + IN CTELockHandle LockHandle, + IN BOOLEAN Success, + IN OPTIONAL PIPX_LOCAL_TARGET LocalTarget, + IN OPTIONAL USHORT HopCount, + IN OPTIONAL USHORT TickCount + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams, find route + requests, and GET_LOCAL_TARGET ioctls that were + waiting for a route to be found. + + THIS ROUTINE IS CALLED WITH THE SEGMENT LOCK HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + Network - The network in question. + + LockHandle - The handle used to acquire the lock. + + Success - TRUE if the route was successfully found. + + LocalTarget - If Success is TRUE, the local target for the route. + + HopCount - If Success is TRUE, the hop count for the route, + in machine order. + + TickCount - If Success is TRUE, the tick count for the route, + in machine order. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY FindRouteList; + LIST_ENTRY GetLocalTargetList; + LIST_ENTRY ReripNetnumList; + PIPX_SEND_RESERVED WaitReserved; // ProtocolReserved of waiting packet + PIPX_FIND_ROUTE_REQUEST FindRouteRequest; + PREQUEST GetLocalTargetRequest; + PREQUEST ReripNetnumRequest; + PISN_ACTION_GET_LOCAL_TARGET GetLocalTarget; + PIPX_NETNUM_DATA NetnumData; + ULONG Segment; + PBINDING Binding, SendBinding; + PLIST_ENTRY p; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + ULONG HeaderSize; + NDIS_STATUS NdisStatus; + ULONG NetworkUlong = *(UNALIGNED ULONG *)Network; + + + InitializeListHead (&DatagramList); + InitializeListHead (&FindRouteList); + InitializeListHead (&GetLocalTargetList); + InitializeListHead (&ReripNetnumList); + + + // + // Put all packets that were waiting for a route to + // this network on DatagramList. They will be sent + // or failed later in the routine. + // + + Segment = RipGetSegment (Network); + + p = Device->Segments[Segment].WaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].WaitingForRoute) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; +#if 0 + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[Device->IncludedHeaderOffset]))->DestinationNetwork) == + NetworkUlong) { +#endif + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[MAC_HEADER_SIZE]))->DestinationNetwork) == + NetworkUlong) { + + RemoveEntryList (&WaitReserved->WaitLinkage); + InsertTailList (&DatagramList, &WaitReserved->WaitLinkage); + } + + } + + // + // Put all find route requests for this network on + // FindRouteList. They will be completed later in the + // routine. + // + + p = Device->Segments[Segment].FindWaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].FindWaitingForRoute) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + if (*(UNALIGNED ULONG *)(FindRouteRequest->Network) == + NetworkUlong) { + + RemoveEntryList (&FindRouteRequest->Linkage); + InsertTailList (&FindRouteList, &FindRouteRequest->Linkage); + } + + } + + // + // Put all get local target action requests for this + // network on GetLocalTargetList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingLocalTarget.Flink; + + while (p != &Device->Segments[Segment].WaitingLocalTarget) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + if (GetLocalTarget->IpxAddress.NetworkAddress == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(GetLocalTargetRequest)); + InsertTailList (&GetLocalTargetList, REQUEST_LINKAGE(GetLocalTargetRequest)); + } + + } + + // + // Put all MIPX_RERIPNETNUM action requests for this + // network on ReripNetnumList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingReripNetnum.Flink; + + while (p != &Device->Segments[Segment].WaitingReripNetnum) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + if (*(UNALIGNED ULONG *)NetnumData->netnum == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(ReripNetnumRequest)); + InsertTailList (&ReripNetnumList, REQUEST_LINKAGE(ReripNetnumRequest)); + } + + } + + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + // + // For sends we will use the master binding of a binding + // set, but we'll return the real NicId for people who + // want that. + // + + if (Success) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, SendBinding->NicId)); + } else { + SendBinding = Binding; + } +#else + Binding = Device->Bindings[LocalTarget->NicId]; + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + LocalTarget->NicId = SendBinding->NicId; + } else { + SendBinding = Binding; + } +#endif + } + + + // + // Now that the lock is free, process all packets on + // DatagramList. + // + // NOTE: May misorder packets if they come in right now... + // + + for (p = DatagramList.Flink; p != &DatagramList ; ) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; + Packet = CONTAINING_RECORD (WaitReserved, NDIS_PACKET, ProtocolReserved[0]); + +#if DBG + CTEAssert (!WaitReserved->SendInProgress); + WaitReserved->SendInProgress = TRUE; +#endif + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued packet %lx\n", WaitReserved)); + + if (REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) > + SendBinding->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Queued send %d bytes too large (%d)\n", + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request), + SendBinding->RealMaxDatagramSize)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_INVALID_BUFFER_SIZE); + + } else { + +#if 0 + if (WaitReserved->DestinationType == DESTINATION_DEF) { + HeaderSize = SendBinding->DefHeaderSize; + } else { + HeaderSize = SendBinding->BcMcHeaderSize; + } + + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[HeaderSize]); +#endif + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[MAC_HEADER_SIZE]); + + // + // Move the header to the correct location now that + // we know the NIC ID to send to. + // +#if 0 + if (HeaderSize != Device->IncludedHeaderOffset) { + + RtlMoveMemory( + IpxHeader, + &WaitReserved->Header[Device->IncludedHeaderOffset], + sizeof(IPX_HEADER)); + + } +#endif + + if (Device->MultiCardZeroVirtual || + (IpxHeader->DestinationSocket == SAP_SOCKET)) { + + // + // These frames need to look like they come from the + // local network, not the virtual one. + // + + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = SendBinding->LocalAddress.NetworkAddress; + RtlCopyMemory (IpxHeader->SourceNode, SendBinding->LocalAddress.NodeAddress, 6); + } + + // + // Fill in the MAC header and submit the frame to NDIS. + // + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)SendBinding->Adapter, + Packet, + NdisStatus); + } + + } + + } else { + + IPX_DEBUG (RIP, ("Timing out packet %lx\n", WaitReserved)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_BAD_NETWORK_PATH); + + } + + } + + + // + // Since we round-robin outgoing rip packets, we just use the + // real NicId here for find route and get local target requests. + // We changed LocalTarget->NicId to be the master above. + // + + if (Success) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + } + + for (p = FindRouteList.Flink; p != &FindRouteList ; ) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + + if (Success) { + + PUSHORT Counts; + + IPX_DEBUG (RIP, ("Found queued find route %lx\n", FindRouteRequest)); + FindRouteRequest->LocalTarget = *LocalTarget; + + Counts = (PUSHORT)&FindRouteRequest->Reserved2; + Counts[0] = TickCount; + Counts[1] = HopCount; + + } else { + + IPX_DEBUG (RIP, ("Timing out find route %lx\n", FindRouteRequest)); + + } + + (*Device->UpperDrivers[FindRouteRequest->Identifier].FindRouteCompleteHandler)( + FindRouteRequest, + Success); + + } + + for (p = GetLocalTargetList.Flink; p != &GetLocalTargetList ; ) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + GetLocalTarget->LocalTarget = *LocalTarget; + REQUEST_INFORMATION(GetLocalTargetRequest) = sizeof(ISN_ACTION_GET_LOCAL_TARGET); + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + REQUEST_INFORMATION(GetLocalTargetRequest) = 0; + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(GetLocalTargetRequest); + IpxFreeRequest(Device, GetLocalTargetRequest); + + } + + // + // NOTE: LocalTarget->NicId now points to the real binding + // not the master, so we use SendBinding->NicId below. + // + + for (p = ReripNetnumList.Flink; p != &ReripNetnumList ; ) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + NetnumData->hopcount = HopCount; + NetnumData->netdelay = TickCount; + NetnumData->cardnum = (INT)(MIN( Device->MaxBindings, SendBinding->NicId) - 1); + RtlMoveMemory (NetnumData->router, LocalTarget->MacAddress, 6); + + REQUEST_INFORMATION(ReripNetnumRequest) = + FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_NETNUM_DATA); + REQUEST_STATUS(ReripNetnumRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + REQUEST_INFORMATION(ReripNetnumRequest) = 0; + REQUEST_STATUS(ReripNetnumRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(ReripNetnumRequest); + IpxFreeRequest(Device, ReripNetnumRequest); + + } + +} /* RipHandleRoutePending */ + + +NTSTATUS +RipInsertLocalNetwork( + IN ULONG Network, + IN USHORT NicId, + IN NDIS_HANDLE NdisBindingContext, + IN USHORT Count + ) + +/*++ + +Routine Description: + + This routine creates a router entry for a local network + and inserts it in the table. + +Arguments: + + Network - The network. + + NicId - The NIC ID used to route packets + + NdisBindingHandle - The binding handle used for NdisSend + + Count - The tick and hop count for this network (will be + 0 for the virtual net and 1 for attached nets) + +Return Value: + + The status of the operation. + +--*/ + +{ + PIPX_ROUTE_ENTRY RouteEntry; + PDEVICE Device = IpxDevice; + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // BUGBUG: We should allocate the memory in the binding/device + // structure itself. + // + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry == (PIPX_ROUTE_ENTRY)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Segment = RipGetSegment ((PUCHAR)&Network); + + *(UNALIGNED LONG *)RouteEntry->Network = Network; + RouteEntry->NicId = NicId; + RouteEntry->NdisBindingContext = NdisBindingContext; + + if (NicId == 0) { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY; + } else { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY | IPX_ROUTER_LOCAL_NET; + } + RouteEntry->Segment = Segment; + RouteEntry->TickCount = Count; + RouteEntry->HopCount = 1; + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + + // + // RouteEntry->NextRouter is not used for the virtual net or + // when LOCAL_NET is set (i.e. every net that we will add here). + // + + RtlZeroMemory (RouteEntry->NextRouter, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Make sure one doesn't exist. + // + + if (RipGetRoute(Segment, (PUCHAR)&Network) != NULL) { + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_DUPLICATE_NAME; + } + + // + // Add this new entry. + // + + if (RipAddRoute (Segment, RouteEntry)) { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + return STATUS_SUCCESS; + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_INSUFFICIENT_RESOURCES; + + } + +} /* RipInsertLocalNetwork */ + + +VOID +RipAdjustForBindingChange( + IN USHORT NicId, + IN USHORT NewNicId, + IN IPX_BINDING_CHANGE_TYPE ChangeType + ) + +/*++ + +Routine Description: + + This routine is called when an auto-detect binding is + deleted or moved, or a WAN line goes down. + + It scans the RIP database for routes equal to this NIC ID + and modifies them appropriately. If ChangeType is + IpxBindingDeleted it will subract one from any NIC IDs + in the database that are higher than NicId. It is assumed + that other code is readjusting the Device->Bindings + array. + +Arguments: + + NicId - The NIC ID of the deleted binding. + + NewNicId - The new NIC ID, for IpxBindingMoved changes. + + ChangeType - Either IpxBindingDeleted, IpxBindingMoved, + or IpxBindingDown. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry comparing the NIC ID. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->NicId == NicId) { + + if (ChangeType != IpxBindingMoved) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, binding deleted\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Changing NIC ID for route entry %lx\n", RouteEntry)); + RouteEntry->NicId = NewNicId; + + } +#ifdef _PNP_POWER + // + // If the NicId is 0, we dont adjust the other entries' NicId's - this is to support the removal + // of the Virtual Net # which resides at NicId=0. + // + } else if (NicId && (ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#else + } else if ((ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#endif + IPX_DEBUG (AUTO_DETECT, ("Decrementing NIC ID for route entry %lx\n", RouteEntry)); + --RouteEntry->NicId; + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipAdjustForBindingChange */ + + +UINT +RipGetSegment( + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the correct segment for the specified + network. + +Arguments: + + Network - The network. + +Return Value: + + The segment. + +--*/ + +{ + + ULONG Total; + + Total = Network[0] ^ Network[1] ^ Network[2] ^ Network[3]; + return (Total % IpxDevice->SegmentCount); + +} /* RipGetSegment */ + + +PIPX_ROUTE_ENTRY +RipGetRoute( + IN UINT Segment, + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the router table entry for the given + network, which is in the specified segment of the table. + THE SEGMENT LOCK MUST BE HELD. The returned data is valid + until the segment lock is released or other operations + (add/delete) are performed on the segment. + +Arguments: + + Segment - The segment corresponding to the network. + + Network - The network. + +Return Value: + + The router table entry, or NULL if none exists for this network. + +--*/ + +{ + PLIST_ENTRY p; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + + RouterSegment = &IpxDevice->Segments[Segment]; + + for (p = RouterSegment->Entries.Flink; + p != &RouterSegment->Entries; + p = p->Flink) { + + RouteEntry = CONTAINING_RECORD( + p, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + if ((*(UNALIGNED LONG *)RouteEntry->Network) == + (*(UNALIGNED LONG *)Network)) { + return RouteEntry; + } + } + + return NULL; + +} /* RipGetRoute */ + + +BOOLEAN +RipAddRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine stores a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is allocated and filled in by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully inserted. + +--*/ + +{ + + IPX_DEBUG (RIP, ("Adding route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + InsertTailList( + &IpxDevice->Segments[Segment].Entries, + &RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipAddRoute */ + + +BOOLEAN +RipDeleteRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine deletes a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is freed by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully deleted. + +--*/ + +{ + + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + IPX_DEBUG (RIP, ("Deleting route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + + // + // If the current enumeration point for this segment is here, + // adjust the pointer before deleting the entry. We make it + // point to the previous entry so GetNextRoute will work. + // + + if (RouterSegment->EnumerateLocation == &RouteEntry->PRIVATE.Linkage) { + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Blink; + } + + RemoveEntryList (&RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipDeleteRoute */ + + +PIPX_ROUTE_ENTRY +RipGetFirstRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the first router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetNextRoute to enumerate all the + entries in a segment. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The first router table entry, or NULL if the segment is empty. + +--*/ + +{ + PIPX_ROUTE_ENTRY FirstEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->Entries.Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + FirstEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return FirstEntry; + + } + +} /* RipGetFirstRoute */ + + +PIPX_ROUTE_ENTRY +RipGetNextRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the next router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetFirstRoute to enumerate all the + entries in a segment. + + It is illegal to call RipGetNextRoute on a segment + without first calling RipGetFirstRoute. The segment + lock must be held for the duration of the enumeration + of a single segment. It is legal to stop enumerating + the segment in the middle. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The next router table entry, or NULL if the end of the + segment is reached. + +--*/ + +{ + PIPX_ROUTE_ENTRY NextEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + NextEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return NextEntry; + + } + +} /* RipGetNextRoute */ + + +VOID +RipDropRemoteEntries( + VOID + ) + +/*++ + +Routine Description: + + This routine deletes all non-local entries from the + RIP database. It is called when the WAN line goes up + or down and we want to remove all existing entries. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through, deleting everything but local entries. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, dropping remote entries\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipDropRemoteEntries */ + diff --git a/private/ntos/tdi/isnp/ipx/send.c b/private/ntos/tdi/isnp/ipx/send.c new file mode 100644 index 000000000..fd9a62a7d --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/send.c @@ -0,0 +1,1651 @@ + +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains code that implements the send engine for the + IPX transport provider. + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) - August-25-1995 + Bug Fixes - tagged [SA] + Sanjay Anand (SanjayAn) - 22-Sept-1995 + BackFill optimization changes added under #if BACK_FILL + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// BUGBUG Using the macro for performance reasons. Should be taken out +// when NdisQueryPacket is optimized. In the near future (after PPC release) +// move this to a header file and use it at other places. +// +#define IPX_PACKET_HEAD(Pkt) (Pkt)->Private.Head + +#if 0 +#define IpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} +#endif + +VOID +IpxSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + ProtocolBindingContext - The ADAPTER structure for this binding. + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + + PIPX_SEND_RESERVED Reserved = (PIPX_SEND_RESERVED)(NdisPacket->ProtocolReserved); + PADAPTER Adapter = (PADAPTER)ProtocolBindingContext; + PREQUEST Request; + PADDRESS_FILE AddressFile; + PDEVICE Device = IpxDevice; + PBINDING Binding; + USHORT NewId, OldId; + ULONG NewOffset, OldOffset; + PIPX_HEADER IpxHeader; + IPX_LOCAL_TARGET LocalTarget; + PIO_STACK_LOCATION irpSp; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + +#if DBG + if (Adapter != NULL) { + ASSERT_ADAPTER(Adapter); + } +#endif + + // + // See if this send was padded. + // + + if (Reserved->PaddingBuffer) { + + UINT Offset; + // + // Check if we simply need to re-adjust the buffer length. This will + // happen if we incremented the buffer length in MAC.C. + // + + if (Reserved->PreviousTail) { + CTEAssert (NDIS_BUFFER_LINKAGE(Reserved->PaddingBuffer->NdisBuffer) == NULL); + NDIS_BUFFER_LINKAGE (Reserved->PreviousTail) = (PNDIS_BUFFER)NULL; + } else { + PNDIS_BUFFER LastBuffer = (PNDIS_BUFFER)Reserved->PaddingBuffer; + UINT BufferLength; + + NdisQueryBufferOffset( LastBuffer, &Offset, &BufferLength ); + NdisAdjustBufferLength( LastBuffer, (BufferLength - 1) ); + } + + Reserved->PaddingBuffer = NULL; + + if (Reserved->Identifier < IDENTIFIER_IPX) { + NdisRecalculatePacketCounts (NdisPacket); + } + } + +FunctionStart:; + + switch (Reserved->Identifier) { + + case IDENTIFIER_IPX: + +// #if DBG + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +// #endif + + // + // Check if this packet should be sent to all + // networks. + // + + if (Reserved->u.SR_DG.CurrentNicId) { + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + Reserved->u.SR_DG.Net0SendSucceeded = TRUE; + } + + OldId = Reserved->u.SR_DG.CurrentNicId; + +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (NewId = OldId+1; NewId <= Index; NewId++) { + if ((Binding = NIC_ID_TO_BINDING(Device, NewId)) +#else + for (NewId = OldId+1; NewId <= Device->HighestExternalNicId; NewId++) { + if ((Binding = Device->Bindings[NewId]) +#endif _PNP_POWER + && + ((!Device->SingleNetworkActive) || + (Device->ActiveNetworkWan == Binding->Adapter->MacInfo.MediumAsync)) + && + ((!Device->DisableDialoutSap) || + (!Binding->DialOutAsync) || + (!Reserved->u.SR_DG.OutgoingSap))) { + + // + // The binding exists, and we either are not configured + // for "SingleNetworkActive", or we are and this binding + // is the right type (i.e. the active network is wan and + // this is a wan binding, or the active network is not + // wan and this is not a wan binding), and this is not + // an outgoing sap that we are trying to send with + // "DisableDialoutSap" set. + // + + break; + } + } + } + + if (NewId <= MIN (Device->MaxBindings, Device->HighestExternalNicId)) { +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + + // + // Yes, we found another net to send it on, so + // move the header around if needed and do so. + // + + Reserved->u.SR_DG.CurrentNicId = NewId; + CTEAssert ((Reserved->DestinationType == DESTINATION_BCAST) || + (Reserved->DestinationType == DESTINATION_MCAST)); + +#if 0 + NewOffset = Binding->BcMcHeaderSize; + OldOffset = Device->Bindings[OldId]->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER)); + + } + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); +#endif + + + +#if BACK_FILL + // This should be a normal packet. Backfill packet is never used for + // reserved other than IPX type + + CTEAssert(!Reserved->BackFill); +#endif + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&LocalTarget, NewId); +#else + LocalTarget.NicId = NewId; +#endif + RtlCopyMemory(LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + if (Device->MultiCardZeroVirtual || + (IpxHeader->DestinationSocket == SAP_SOCKET)) { + + // + // SAP frames need to look like they come from the + // local network, not the virtual one. The same is + // true if we are running multiple nets without + // a virtual net. + // + + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (IpxHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + } + + // + // Fill in the MAC header and submit the frame to NDIS. + // + +// #if DBG + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; +// #endif + + if ((NdisStatus = IpxSendFrame( + &LocalTarget, + NdisPacket, + REQUEST_INFORMATION(Reserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + Adapter = Binding->Adapter; +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + goto FunctionStart; + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif _PNP_POWER + + return; + + } else { +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif _PNP_POWER + // + // If any of the sends succeeded then return + // success on the datagram send, otherwise + // use the most recent failure status. + // + + if (Reserved->u.SR_DG.Net0SendSucceeded) { + NdisStatus = NDIS_STATUS_SUCCESS; + } + + } + + } + + +#if 0 + // + // NOTE: We don't NULL out the linkage field of the + // HeaderBuffer, which will leave the old buffer chain + // hanging off it; but that is OK because if we reuse + // this packet we will replace that chain with the new + // one, and before we free it we NULL it out. + // + // I.e. we don't do this: + // + + NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer) = NULL; + NdisRecalculatePacketCounts (NdisPacket); +#endif + +#if 0 + { + ULONG ActualLength; + IpxGetMdlChainLength(NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer), &ActualLength); + if (ActualLength != REQUEST_INFORMATION(Reserved->u.SR_DG.Request)) { + DbgPrint ("IPX: At completion, IRP %lx has parameter length %d, buffer chain length %d\n", + Reserved->u.SR_DG.Request, REQUEST_INFORMATION(Reserved->u.SR_DG.Request), ActualLength); + DbgBreakPoint(); + } + } +#endif + + // + // Save these so we can free the packet. + // + + Request = Reserved->u.SR_DG.Request; + AddressFile = Reserved->u.SR_DG.AddressFile; + + +#if BACK_FILL + // Check if this is backfilled. If so restore users Mdl back to its original shape + // Also, push the packet on to backfillpacket queue if the packet is not owned by the address + + if (Reserved->BackFill) { + + Reserved->HeaderBuffer->MappedSystemVa = Reserved->MappedSystemVa; + Reserved->HeaderBuffer->ByteCount = Reserved->UserLength; + Reserved->HeaderBuffer->StartVa = (PCHAR)((ULONG)Reserved->HeaderBuffer->MappedSystemVa & ~(PAGE_SIZE-1)); + Reserved->HeaderBuffer->ByteOffset = (ULONG)Reserved->HeaderBuffer->MappedSystemVa & (PAGE_SIZE-1); + + IPX_DEBUG(SEND, ("completeing back filled userMdl %x\n",Reserved->HeaderBuffer)); + + NdisPacket->Private.ValidCounts = FALSE; + + NdisPacket->Private.Head = NULL; + NdisPacket->Private.Tail = NULL; + + Reserved->HeaderBuffer = NULL; + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->BackFillPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->BackFillPacketInUse); + + IPX_DEBUG(SEND, ("Freeing owned backfill %x\n", Reserved)); + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->BackFillPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } + } + // not a back fill packet. Push it on sendpacket pool + else { + + if (Reserved->OwnedByAddress) { + + // Reserved->Address->SendPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->SendPacketInUse); + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + + + } + +#else + + if (Reserved->OwnedByAddress) { + + + Reserved->Address->SendPacketInUse = FALSE; + + } else { + + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } +#endif + + ++Device->Statistics.PacketsSent; + + // + // If this is a fast send irp, we bypass the file system and + // call the completion routine directly. + // + + REQUEST_STATUS(Request) = NdisStatus; + irpSp = IoGetCurrentIrpStackLocation( Request ); + + if ( irpSp->MinorFunction == TDI_DIRECT_SEND_DATAGRAM ) { + + Request->CurrentLocation++, + Request->Tail.Overlay.CurrentStackLocation++; + + (VOID) irpSp->CompletionRoutine( + NULL, + Request, + irpSp->Context + ); + + } else { + IpxCompleteRequest (Request); + } + + IpxFreeRequest(Device, Request); + + IpxDereferenceAddressFileSync (AddressFile, AFREF_SEND_DGRAM); + + break; + + case IDENTIFIER_RIP_INTERNAL: + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + break; + + case IDENTIFIER_RIP_RESPONSE: + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Reserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + break; + +#ifdef _PNP_POWER + case IDENTIFIER_NB: + case IDENTIFIER_SPX: + + // + // See if this is an iterative send + // + if (OldId = Reserved->CurrentNicId) { + + PNDIS_BUFFER HeaderBuffer; + UINT TempHeaderBufferLength; + PUCHAR Header; + PIPX_HEADER IpxHeader; + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + Reserved->Net0SendSucceeded = TRUE; + } + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (NewId = OldId+1; NewId <= Index; NewId++) { + if (Binding = NIC_ID_TO_BINDING(Device, NewId)) { + // + // Found next NIC to send on + // + break; + } + } + } + + if (NewId <= MIN (Device->MaxBindings, Device->HighestExternalNicId)) { + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // Yes, we found another net to send it on, so + // move the header around if needed and do so. + // + IPX_DEBUG(SEND, ("ISN iteration: OldId: %lx, NewId: %lx\n", OldId, NewId)); + Reserved->CurrentNicId = NewId; +#if 0 + NewOffset = Binding->BcMcHeaderSize; + OldOffset = Device->Bindings[OldId]->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER)); + + } + + IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); + + +#if BACK_FILL + // This should be a normal packet. Backfill packet is never used for + // reserved other than IPX type + + CTEAssert(!Reserved->BackFill); +#endif +#endif + + NdisQueryPacket (NdisPacket, NULL, NULL, &HeaderBuffer, NULL); + NdisQueryBuffer(HeaderBuffer, &Header, &TempHeaderBufferLength); + + IpxHeader = (PIPX_HEADER)(&Header[Device->IncludedHeaderOffset]); + + IPX_DEBUG(SEND, ("SendComplete: IpxHeader: %lx\n", IpxHeader)); + FILL_LOCAL_TARGET(&Reserved->LocalTarget, NewId); + + // + // We don't need to so this since the macaddress is replaced in + // IpxSendFrame anyway. The LocalTarget is the same as the one on + // the original send - this is passed down for further sends. + // + // RtlCopyMemory(LocalTarget.MacAddress, IpxHeader->DestinationNode, 6); + + // + // Fill in the MAC header and submit the frame to NDIS. + // + + if ((NdisStatus = IpxSendFrame( + &Reserved->LocalTarget, + NdisPacket, + Reserved->PacketLength, + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + Adapter = Binding->Adapter; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + goto FunctionStart; + } + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + return; + + } else { + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // If any of the sends succeeded then return + // success on the datagram send, otherwise + // use the most recent failure status. + // + if (Reserved->Net0SendSucceeded) { + NdisStatus = NDIS_STATUS_SUCCESS; + } + + } + } + + // + // fall thru' + // +#endif + default: + + (*Device->UpperDrivers[Reserved->Identifier].SendCompleteHandler)( + NdisPacket, + NdisStatus); + break; + } + +} /* IpxSendComplete */ + + +NTSTATUS +IpxTdiSendDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSendDatagram request for the transport + provider. + +Arguments: + + Request - Pointer to the request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PADDRESS_FILE AddressFile; + PADDRESS Address; + PNDIS_PACKET Packet; + PIPX_SEND_RESERVED Reserved; + PSINGLE_LIST_ENTRY s; + TDI_ADDRESS_IPX UNALIGNED * RemoteAddress; + TDI_ADDRESS_IPX TempAddress; + TA_ADDRESS UNALIGNED * AddressName; + PTDI_CONNECTION_INFORMATION Information; + PTDI_REQUEST_KERNEL_SENDDG Parameters; + PBINDING Binding; + IPX_LOCAL_TARGET TempLocalTarget; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = IpxDevice; + UCHAR PacketType; + NTSTATUS Status; + PIPX_HEADER IpxHeader; + NDIS_STATUS NdisStatus; + USHORT LengthIncludingHeader; + IPX_DEFINE_SYNC_CONTEXT (SyncContext) + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PIO_STACK_LOCATION irpSp; \ + BOOLEAN IsLoopback = FALSE; + +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + + // + // Do a quick check of the validity of the address. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + IPX_BEGIN_SYNC (&SyncContext); + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == IPX_ADDRESSFILE_SIGNATURE) && + ((Address = AddressFile->Address) != NULL)) { + + IPX_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_CLOSING) { + + Parameters = (PTDI_REQUEST_KERNEL_SENDDG)REQUEST_PARAMETERS(Request); + Information = Parameters->SendDatagramInformation; + + // + // Do a quick check if this address has only one entry. + // + + AddressName = &((TRANSPORT_ADDRESS UNALIGNED *)(Information->RemoteAddress))->Address[0]; + + if ((AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) && + (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX))) { + + RemoteAddress = (TDI_ADDRESS_IPX UNALIGNED *)(AddressName->Address); + + } else if ((RemoteAddress = IpxParseTdiAddress (Information->RemoteAddress)) == NULL) { + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_ADDRESS; + goto error_send_no_packet; + } + + IPX_DEBUG (SEND, ("Send on %lx, network %lx socket %lx\n", + Address, RemoteAddress->NetworkAddress, RemoteAddress->Socket)); + +#if 0 + if (Parameters->SendLength > IpxDevice->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + IpxDevice->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_no_packet; + } +#endif + // + // Every address has one packet committed to it, use that + // if possible, otherwise take one out of the pool. + // + + +#if BACK_FILL + + // If the request is coming from the server, which resrves transport header space + // build the header in its space. Allocate a special packet to which does not contain + // mac and ipx headers in its reserved space. + + if ((PMDL)REQUEST_NDIS_BUFFER(Request) && + (((PMDL)REQUEST_NDIS_BUFFER(Request))->MdlFlags & MDL_NETWORK_HEADER) && + (!(Information->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS))) && + (RemoteAddress->NodeAddress[0] != 0xff)) { + + //if (!Address->BackFillPacketInUse) { + if (InterlockedExchangeAdd(&Address->BackFillPacketInUse, 0) == 0) { + //Address->BackFillPacketInUse = TRUE; + InterlockedIncrement(&Address->BackFillPacketInUse); + + Packet = PACKET(&Address->BackFillPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + IPX_DEBUG(SEND, ("Getting owned backfill %x %x \n", Packet,Reserved)); + + }else { + + s = IPX_POP_ENTRY_LIST( + &Device->BackFillPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotBackFillPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopBackFillPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto error_send_no_packet; + } + +GotBackFillPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + IPX_DEBUG(SEND, ("getting backfill packet %x %x %x\n", s, Reserved, RemoteAddress->NodeAddress)); + if(!Reserved->BackFill)DbgBreakPoint(); + + } + + }else { + + // if (!Address->SendPacketInUse) { + if (InterlockedExchangeAdd(&Address->SendPacketInUse, 0) == 0) { + // Address->SendPacketInUse = TRUE; + InterlockedIncrement(&Address->SendPacketInUse); + + Packet = PACKET(&Address->SendPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopSendPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto error_send_no_packet; + } + +GotPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + Reserved->BackFill = FALSE; + + } + + } + + +#else + + if (!Address->SendPacketInUse) { + + Address->SendPacketInUse = TRUE; + Packet = PACKET(&Address->SendPacket); + Reserved = (PIPX_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = IPX_POP_ENTRY_LIST( + &Device->SendPacketList, + &Device->SListsLock); + + if (s != NULL) { + goto GotPacket; + } + + // + // This function tries to allocate another packet pool. + // + + s = IpxPopSendPacket(Device); + + // + // Possibly we should queue the packet up to wait + // for one to become free. + // + + if (s == NULL) { + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto error_send_no_packet; + } + +GotPacket: + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + +#endif + + IpxReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + + // + // Save this now while we have Parameters available. + // + + REQUEST_INFORMATION(Request) = Parameters->SendLength; + LengthIncludingHeader = (USHORT)(Parameters->SendLength + sizeof(IPX_HEADER)); + +#if 0 + { + ULONG ActualLength; + IpxGetMdlChainLength(REQUEST_NDIS_BUFFER(Request), &ActualLength); + if (ActualLength != Parameters->SendLength) { + DbgPrint ("IPX: IRP %lx has parameter length %d, buffer chain length %d\n", + Request, Parameters->SendLength, ActualLength); + DbgBreakPoint(); + } + } +#endif + + Reserved->u.SR_DG.AddressFile = AddressFile; + Reserved->u.SR_DG.Request = Request; + CTEAssert (Reserved->Identifier == IDENTIFIER_IPX); + + + // + // Set this to 0; this means the packet is not one that + // should be broadcast on all nets. We will change it + // later if it turns out this is the case. + // + + Reserved->u.SR_DG.CurrentNicId = 0; + + // + // We need this to track these packets specially. + // + + Reserved->u.SR_DG.OutgoingSap = AddressFile->IsSapSocket; + + // + // Add the MDL chain after the pre-allocated header buffer. + // NOTE: THIS WILL ONLY WORK IF WE EVENTUALLY CALL + // NDISRECALCULATEPACKETCOUNTS (which we do in IpxSendFrame). + // + // +#if BACK_FILL + + if (Reserved->BackFill) { + Reserved->HeaderBuffer = REQUEST_NDIS_BUFFER(Request); + + //remove the ipx mdl from the packet. + Reserved->UserLength = Reserved->HeaderBuffer->ByteCount; + + IPX_DEBUG(SEND, ("back filling userMdl Reserved %x %x\n", Reserved->HeaderBuffer, Reserved)); + } else { + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = REQUEST_NDIS_BUFFER(Request); + } +#else + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = REQUEST_NDIS_BUFFER(Request); +#endif + + + if (Information->OptionsLength < sizeof(IPX_DATAGRAM_OPTIONS)) { + + // + // The caller did not supply the local target for this + // send, so we look it up ourselves. + // + + UINT Segment; + + // + // We calculate this now since we need to know + // if it is directed below. + // + + if (RemoteAddress->NodeAddress[0] == 0xff) { + // BUGBUG: What about multicast? + if ((*(UNALIGNED ULONG *)(RemoteAddress->NodeAddress) != 0xffffffff) || + (*(UNALIGNED USHORT *)(RemoteAddress->NodeAddress+4) != 0xffff)) { + Reserved->DestinationType = DESTINATION_MCAST; + } else { + Reserved->DestinationType = DESTINATION_BCAST; + } + } else { + Reserved->DestinationType = DESTINATION_DEF; // directed send + } + + // + // If there are no options, then check if the + // caller is passing the packet type as a final byte + // in the remote address; if not use the default. + // + + if (Information->OptionsLength == 0) { + if (AddressFile->ExtendedAddressing) { + PacketType = ((PUCHAR)(RemoteAddress+1))[0]; + } else { + PacketType = AddressFile->DefaultPacketType; + } + } else { + PacketType = ((PUCHAR)(Information->Options))[0]; + } + + if ((Reserved->DestinationType != DESTINATION_DEF) && + ((RemoteAddress->NetworkAddress == 0) || + (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)))) { + + // + // This packet needs to be broadcast to all networks. + // Make sure it is not too big for any of them. + // + + if (Parameters->SendLength > Device->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, Device->RealMaxDatagramSize)); + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + // + // If this is a broadcast to the virtual net, we + // need to construct a fake remote address which + // has network 0 in there instead. + // + + if (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)) { + + RtlCopyMemory (&TempAddress, (PVOID)RemoteAddress, sizeof(TDI_ADDRESS_IPX)); + TempAddress.NetworkAddress = 0; + RemoteAddress = (TDI_ADDRESS_IPX UNALIGNED *)&TempAddress; + } + + // + // If someone is sending to the SAP socket and + // we are running with multiple cards without a + // virtual network, AND this packet is a SAP response, + // then we log an error to warn them that the + // system may not work as they like (since there + // is no virtual network to advertise, we use + // the first card's net/node as our local address). + // We only do this once per boot, using the + // SapWarningLogged variable to control that. + // + + if ((RemoteAddress->Socket == SAP_SOCKET) && + (!Device->SapWarningLogged) && + (Device->MultiCardZeroVirtual)) { + + PNDIS_BUFFER FirstBuffer; + UINT FirstBufferLength; + USHORT UNALIGNED * FirstBufferData; + + if ((FirstBuffer = REQUEST_NDIS_BUFFER(Request)) != NULL) { + + NdisQueryBuffer( + FirstBuffer, + (PVOID *)&FirstBufferData, + &FirstBufferLength); + + // + // The first two bytes of a SAP packet are the + // operation, 0x2 (in network order) is response. + // + + if ((FirstBufferLength >= sizeof(USHORT)) && + (*FirstBufferData == 0x0200)) { + + Device->SapWarningLogged = TRUE; + + IpxWriteGeneralErrorLog( + Device->DeviceObject, + EVENT_IPX_SAP_ANNOUNCE, + 777, + STATUS_NOT_SUPPORTED, + NULL, + 0, + NULL); + } + } + } + + + // + // In this case we do not RIP but instead set the + // packet up so it is sent to each network in turn. + // + // Special case: If this packet is from the SAP + // socket and we are running with multiple cards + // without a virtual network, we only send this + // on the card with NIC ID 1, so we leave + // CurrentNicId set to 0. + // + + // + // BUGBUG: What if NicId 1 is invalid? Should scan + // for first valid one, fail send if none. + // + + if ((Address->Socket != SAP_SOCKET) || + (!Device->MultiCardZeroVirtual)) { + + if (Device->SingleNetworkActive) { + + if (Device->ActiveNetworkWan) { + Reserved->u.SR_DG.CurrentNicId = Device->FirstWanNicId; + } else { + Reserved->u.SR_DG.CurrentNicId = Device->FirstLanNicId; + } + + } else { + + Reserved->u.SR_DG.CurrentNicId = 1; + + } + + Reserved->u.SR_DG.Net0SendSucceeded = FALSE; + + // + // In this case, we need to scan for the first + // non-dialout wan socket. + // + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET)) { + + PBINDING TempBinding; + + CTEAssert (Reserved->u.SR_DG.CurrentNicId <= Device->ValidBindings); + while (Reserved->u.SR_DG.CurrentNicId <= MIN (Device->MaxBindings, Device->ValidBindings)) { +#ifdef _PNP_POWER +// No need to lock the access path since he just looks at it +// + TempBinding = NIC_ID_TO_BINDING(Device, Reserved->u.SR_DG.CurrentNicId); +#else + TempBinding = Device->Bindings[Reserved->u.SR_DG.CurrentNicId]; +#endif _PNP_POWER + if ((TempBinding != NULL) && + (!TempBinding->DialOutAsync)) { + break; + } + ++Reserved->u.SR_DG.CurrentNicId; + } + if (Reserved->u.SR_DG.CurrentNicId > MIN (Device->MaxBindings, Device->ValidBindings)) { + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + + goto error_send_with_packet; + } + } +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&TempLocalTarget, Reserved->u.SR_DG.CurrentNicId); +#else + TempLocalTarget.NicId = Reserved->u.SR_DG.CurrentNicId; +#endif + + } else { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&TempLocalTarget, 1); +#else + TempLocalTarget.NicId = 1; +#endif + } + + RtlCopyMemory(TempLocalTarget.MacAddress, RemoteAddress->NodeAddress, 6); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&TempLocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + } else { + + Segment = RipGetSegment((PUCHAR)&RemoteAddress->NetworkAddress); + + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // This call will return STATUS_PENDING if we need to + // RIP for the packet. + // + + Status = RipGetLocalTarget( + Segment, + RemoteAddress, + IPX_FIND_ROUTE_RIP_IF_NEEDED, + &TempLocalTarget, + NULL); + + if (Status == STATUS_SUCCESS) { + + // + // We found the route, TempLocalTarget is filled in. + // + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); +#ifdef _PNP_POWER + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + if (NIC_FROM_LOCAL_TARGET(&TempLocalTarget) == 0) { + IPX_DEBUG(LOOPB, ("Loopback TDI packet: remoteaddr: %lx\n", RemoteAddress)); + IsLoopback = TRUE; + FILL_LOCAL_TARGET(&TempLocalTarget, 1); + } + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(&TempLocalTarget)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + if (Parameters->SendLength > + Binding->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Binding->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET) && + (Binding->DialOutAsync)) { + + REQUEST_INFORMATION(Request) = 0; + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + + goto error_send_with_packet; + } +#else + if (TempLocalTarget.NicId == 0) { + IPX_DEBUG(LOOPB, ("Loopback TDI packet: remoteaddr: %lx\n", RemoteAddress)); + IsLoopback = TRUE; + TempLocalTarget.NicId = 1; + } + + if (Parameters->SendLength > + Device->Bindings[TempLocalTarget.NicId]->RealMaxDatagramSize) { + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Device->Bindings[TempLocalTarget.NicId]->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + + if ((Device->DisableDialoutSap) && + (Address->Socket == SAP_SOCKET) && + (Device->Bindings[TempLocalTarget.NicId]->DialOutAsync)) { + + REQUEST_INFORMATION(Request) = 0; + // + // [SA] Bug #17273 return proper error mesg. + // + + // Status = STATUS_DEVICE_DOES_NOT_EXIST; + Status = STATUS_NETWORK_UNREACHABLE; + goto error_send_with_packet; + } +#endif _PNP_POWER + + } else if (Status == STATUS_PENDING) { + + // + // A RIP request went out on the network; we queue + // this packet for transmission when the RIP + // response arrives. First we fill in the IPX + // header; the only thing we don't know is where + // exactly to fill it in, so we choose + // the most common location. + // + + IpxConstructHeader( + &Reserved->Header[Device->IncludedHeaderOffset], + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Address->LocalAddress); + + // + // Adjust the 2nd mdl's size + // + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); + + IPX_DEBUG (RIP, ("Queueing packet %lx\n", Reserved)); + + InsertTailList( + &Device->Segments[Segment].WaitingForRoute, + &Reserved->WaitLinkage); + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IPX_END_SYNC (&SyncContext); + + return STATUS_PENDING; + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + goto error_send_with_packet; + + } + } + + LocalTarget = &TempLocalTarget; + + // + // Now we know the local target, we can figure out + // the offset for the IPX header. + // +#ifdef _PNP_POWER +// Remember that we have got the binding with ref above.... + +#else + Binding = Device->Bindings[LocalTarget->NicId]; +#endif + IpxHeader = (PIPX_HEADER)&Reserved->Header[MAC_HEADER_SIZE]; +#if 0 + if (Reserved->DestinationType == DESTINATION_DEF) { + IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->DefHeaderSize]; + } else { + IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->BcMcHeaderSize]; + } +#endif + + } else { + + PacketType = ((PUCHAR)(Information->Options))[0]; + LocalTarget = &((PIPX_DATAGRAM_OPTIONS)(Information->Options))->LocalTarget; + + // + // Calculate the binding and the correct location + // for the IPX header. We can do this at the same + // time as we calculate the DestinationType which + // saves an if like the one 15 lines up. + // + +#ifdef _PNP_POWER +// Get lock to ref. + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + // + // If a loopback packet, use the first binding as place holder + // + if (NIC_FROM_LOCAL_TARGET(LocalTarget) == 0) { + Binding = NIC_ID_TO_BINDING(Device, 1); + IsLoopback = TRUE; + } else { + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + } + + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + if (LocalTarget->NicId == 0) { + Binding = Device->Bindings[1]; + IsLoopback = TRUE; + } else { + Binding = Device->Bindings[LocalTarget->NicId]; + } +#endif _PNP_POWER + if (Parameters->SendLength > Binding->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Send %d bytes too large (%d)\n", + Parameters->SendLength, + Binding->RealMaxDatagramSize)); + + REQUEST_INFORMATION(Request) = 0; + Status = STATUS_INVALID_BUFFER_SIZE; + goto error_send_with_packet; + } + +#if 0 + // + // This shouldn't be needed because even WAN bindings + // don't go away once they are added. + // + + if (Binding == NULL) { + Status = STATUS_DEVICE_DOES_NOT_EXIST; + goto error_send_with_packet; + } +#endif + + if (RemoteAddress->NodeAddress[0] == 0xff) { + // BUGBUG: What about multicast? + if ((*(UNALIGNED ULONG *)(RemoteAddress->NodeAddress) != 0xffffffff) || + (*(UNALIGNED USHORT *)(RemoteAddress->NodeAddress+4) != 0xffff)) { + Reserved->DestinationType = DESTINATION_MCAST; + } else { + Reserved->DestinationType = DESTINATION_BCAST; + } +// IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->BcMcHeaderSize]; + } else { + Reserved->DestinationType = DESTINATION_DEF; // directed send +// IpxHeader = (PIPX_HEADER)&Reserved->Header[Binding->DefHeaderSize]; + } + IpxHeader = (PIPX_HEADER)&Reserved->Header[MAC_HEADER_SIZE]; + + } + + + ++Device->TempDatagramsSent; + Device->TempDatagramBytesSent += Parameters->SendLength; + + +#if BACK_FILL + + if (Reserved->BackFill) { + Reserved->MappedSystemVa = Reserved->HeaderBuffer->MappedSystemVa; + IpxHeader = (PIPX_HEADER)((PCHAR)Reserved->HeaderBuffer->MappedSystemVa - sizeof(IPX_HEADER)); + Reserved->HeaderBuffer->ByteOffset -= sizeof(IPX_HEADER); + (ULONG)Reserved->HeaderBuffer->MappedSystemVa-= sizeof(IPX_HEADER); + IPX_DEBUG(SEND, ("Adjusting backfill userMdl Ipxheader %x %x \n",Reserved->HeaderBuffer,IpxHeader)); + } +#endif + + if (Device->MultiCardZeroVirtual || + (Address->LocalAddress.Socket == SAP_SOCKET) || + (RemoteAddress->Socket == SAP_SOCKET)) { + + // + // SAP frames need to look like they come from the + // local network, not the virtual one. The same is + // true if we are running multiple nets without + // a virtual network number. + // + // If this is a binding set member and a local target + // was provided we will send using the real node of + // the binding, even if it was a slave. This is + // intentional. If no local target was provided then + // this will not be a binding slave. + // + + IpxConstructHeader( + (PUCHAR)IpxHeader, + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Binding->LocalAddress); + + IpxHeader->SourceSocket = Address->SendSourceSocket; + + } else { + + IpxConstructHeader( + (PUCHAR)IpxHeader, + LengthIncludingHeader, + PacketType, + RemoteAddress, + &Address->LocalAddress); + + } + + + // + // Fill in the MAC header and submit the frame to NDIS. + // + +// #if DBG + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; +// #endif + // + // Adjust the 2nd mdl's size + // +#if BACK_FILL + if (Reserved->BackFill) { + NdisAdjustBufferLength(Reserved->HeaderBuffer, (Reserved->HeaderBuffer->ByteCount+sizeof(IPX_HEADER))); + } else { + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); + } +#else + NdisAdjustBufferLength(NDIS_BUFFER_LINKAGE(IPX_PACKET_HEAD(Packet)), sizeof(IPX_HEADER)); +#endif + + IPX_DEBUG(SEND, ("Packet Head %x\n",IPX_PACKET_HEAD(Packet))); + + if (IsLoopback) { + // + // Enque this packet to the LoopbackQueue on the binding. + // If the LoopbackRtn is not already scheduled, schedule it. + // + + IPX_DEBUG(LOOPB, ("Packet: %lx, Addr: %lx, Addr->SendPacket: %lx\n", Packet, Address, Address->SendPacket)); + + // + // Recalculate packet counts here. + // + // NdisAdjustBufferLength (Reserved->HeaderBuffer, 17); +#if BACK_FILL + + if (Reserved->BackFill) { + // + // Set the Header pointer and chain the first MDL + // + Reserved->Header = (PCHAR)Reserved->HeaderBuffer->MappedSystemVa; + NdisChainBufferAtFront(Packet,(PNDIS_BUFFER)Reserved->HeaderBuffer); + } +#endif + NdisRecalculatePacketCounts (Packet); +#ifdef _PNP_POWER + IpxLoopbackEnque(Packet, NIC_ID_TO_BINDING(Device, 1)->Adapter); +#else + IpxLoopbackEnque(Packet, Device->Bindings[1]->Adapter); +#endif + + } else { + if ((NdisStatus = (*Binding->SendFrameHandler)( + Binding->Adapter, + LocalTarget, + Packet, + Parameters->SendLength + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + } + + IPX_END_SYNC (&SyncContext); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + return STATUS_PENDING; + + } else { + + // + // The address file state was closing. + // + + IPX_FREE_LOCK (&Address->Lock, LockHandle); + Status = STATUS_INVALID_HANDLE; + goto error_send_no_packet; + + } + + } else { + + // + // The address file didn't look like one. + // + + Status = STATUS_INVALID_HANDLE; + goto error_send_no_packet; + } + + // + // Jump here if we want to fail the send and we have already + // allocated the packet and ref'ed the address file. + // + +error_send_with_packet: + +#if BACK_FILL + // + // Check if this is backfilled. If so, set the headerbuffer to NULL. Note that we dont need + // restore to restore the user's MDL since it was never touched when this error occurred. + // Also, push the packet on to backfillpacket queue if the packet is not owned by the address + // + if (Reserved->BackFill) { + + Reserved->HeaderBuffer = NULL; + + if (Reserved->OwnedByAddress) { + // Reserved->Address->BackFillPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->BackFillPacketInUse); + + IPX_DEBUG(SEND, ("Freeing owned backfill %x\n", Reserved)); + } else { + IPX_PUSH_ENTRY_LIST( + &Device->BackFillPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } + } else { + // not a back fill packet. Push it on sendpacket pool + if (Reserved->OwnedByAddress) { + // Reserved->Address->SendPacketInUse = FALSE; + InterlockedDecrement(&Reserved->Address->SendPacketInUse); + + } else { + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + + } + } +#else + if (Reserved->OwnedByAddress) { + Reserved->Address->SendPacketInUse = FALSE; + } else { + IPX_PUSH_ENTRY_LIST( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &Device->SListsLock); + } +#endif + + IpxDereferenceAddressFileSync (AddressFile, AFREF_SEND_DGRAM); + +error_send_no_packet: + + // + // Jump here if we fail before doing any of that. + // + + IPX_END_SYNC (&SyncContext); + + irpSp = IoGetCurrentIrpStackLocation( Request ); + if ( irpSp->MinorFunction == TDI_DIRECT_SEND_DATAGRAM ) { + + REQUEST_STATUS(Request) = Status; + Request->CurrentLocation++, + Request->Tail.Overlay.CurrentStackLocation++; + + (VOID) irpSp->CompletionRoutine( + NULL, + Request, + irpSp->Context + ); + + IpxFreeRequest (DeviceObject, Request); + } + + return Status; + +} /* IpxTdiSendDatagram */ + + +#if DBG +VOID +IpxConstructHeader( + IN PUCHAR Header, + IN USHORT PacketLength, + IN UCHAR PacketType, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PTDI_ADDRESS_IPX LocalAddress + ) + +/*++ + +Routine Description: + + This routine constructs an IPX header in a packet. + +Arguments: + + Header - The location at which the header should be built. + + PacketLength - The length of the packet, including the IPX header. + + PacketType - The packet type of the frame. + + RemoteAddress - The remote IPX address. + + LocalAddress - The local IPX address. + +Return Value: + + None. + +--*/ + +{ + + PIPX_HEADER IpxHeader = (PIPX_HEADER)Header; + + IpxHeader->CheckSum = 0xffff; + IpxHeader->PacketLength[0] = (UCHAR)(PacketLength / 256); + IpxHeader->PacketLength[1] = (UCHAR)(PacketLength % 256); + IpxHeader->TransportControl = 0; + IpxHeader->PacketType = PacketType; + + // + // These copies depend on the fact that the destination + // network is the first field in the 12-byte address. + // + + RtlCopyMemory(IpxHeader->DestinationNetwork, (PVOID)RemoteAddress, 12); + RtlCopyMemory(IpxHeader->SourceNetwork, LocalAddress, 12); + +} /* IpxConstructHeader */ +#endif + + diff --git a/private/ntos/tdi/isnp/ipx/sources.inc b/private/ntos/tdi/isnp/ipx/sources.inc new file mode 100644 index 000000000..449026087 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/sources.inc @@ -0,0 +1,74 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnkipx + +TARGETNAME=nwlnkipx +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +NTPROFILEINPUT=yes + + + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -DBACK_FILL=1 -DNDIS40=1 -D_PNP_POWER=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -D_NTIFS_ +!ENDIF + +SOURCES= \ + ..\action.c \ + ..\adapter.c \ + ..\address.c \ + ..\config.c \ + ..\device.c \ + ..\driver.c \ + ..\event.c \ + ..\ind.c \ + ..\internal.c \ + ..\nwlnkipx.rc \ + ..\mac.c \ + ..\ndis.c \ + ..\packet.c \ + ..\query.c \ + ..\receive.c \ + ..\rip.c \ + ..\send.c \ + ..\loopback.c + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj + +SOURCES_USED=..\sources.inc + + diff --git a/private/ntos/tdi/isnp/ipx/up/makefile b/private/ntos/tdi/isnp/ipx/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf b/private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf new file mode 100644 index 000000000..0c4359235 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/up/nwlnkipx.prf @@ -0,0 +1,89 @@ +IpxTdiSendDatagram@8 +IpxReceiveIndicationNew@36 +IpxSendFrame@16 +IpxReceiveComplete@4 +IpxSendFrame802_3802_2@20 +IpxReceivePacket@8 +IpxDerefAddressSync@4 +IpxSendComplete@12 +IpxSendFramePreFwd@16 +IpxReceiveIndication@28 +IpxInitializeBackFillPacket@12 +IpxpAllocateMemory@12 +IpxPopBackFillPacket@4 +IpxAllocateBackFillPool@4 +RipLongTimeout@8 +CTEStartTimer@16 +TdiCopyBufferToMdl@24 +IpxTdiQueryInformation@8 +IpxVerifyAddressFile@4 +IpxDispatchInternal@8 +IpxTransferData@28 +IpxInitLoopback@0 +RipGetFirstRoute@4 +IpxCreateAddress@8 +MacReturnMaxDataSize@20 +IpxLookupAddress@8 +IpxAbortLineChanges@4 +MacMapFrameType@12 +IpxInitializeReceiveBuffer@16 +IpxDispatchOpenClose@8 +IpxTdiSetEventHandler@4 +IpxCreateAddressFile@4 +IpxInternalBind@8 +IpxInitializeReceivePacket@8 +IpxIsAddressLocal@4 +IpxOpenAddress@8 +IpxAllocateReceiveBufferPool@4 +IpxSubmitNdisRequest@12 +IpxAddBroadcast@4 +IpxDestroyBinding@4 +RipAdjustForBindingChange@12 +IpxDispatchDeviceControl@8 +IpxAllocatePaddingBuffer@4 +TdiMapUserRequest@12 +CTEInitialize@0 +IpxInitializeSendPacket@12 +IpxpFreeMemory@12 +IpxInitializePaddingBuffer@12 +IpxAllocateSendPool@4 +RipCleanupPacket@8 +TdiRegisterDeviceObject@8 +TdiRegisterNetAddress@8 +IpxTdiAction@8 +IpxCreateBinding@20 +IpxDerefDevice@4 +MacInitializeBindingInfo@8 +IpxRegisterProtocol@4 +IpxInitializeNdis@8 +IpxGetConfigValue@24 +IpxCreateAdapter@12 +IpxResolveBindingSets@8 +IpxGetFrameType@24 +MacInitializeMacInfo@8 +IpxGetBindingValue@24 +RipQueueRequest@8 +RipShortTimeout@8 +IpxPopSendPacket@4 +IpxAllocateBindingPool@4 +IpxInternalQuery@20 +CTEInitTimer@4 +IpxRequestComplete@12 +IpxBroadcastOperation@4 +CTEInitEvent@8 +IpxPnPGetVirtualNetworkNumber@4 +IpxPnPGetAdapterParameters@12 +IpxOpenAdapterComplete@12 +IpxResolveAutoDetect@16 +IpxBindToAdapter@16 +TdiInitialize@0 +IpxPnPIsnIndicate@4 +IpxPnPUpdateBindingArray@12 +IpxBindAdapter@20 +IpxPnPUpdateDevice@4 +IpxGetConfiguration@12 +IpxAddExport@24 +IpxCreateDevice@16 +DriverEntry@8 +IpxReadLinkageInformation@4 +IpxFreeConfiguration@4 diff --git a/private/ntos/tdi/isnp/ipx/up/sources b/private/ntos/tdi/isnp/ipx/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/nb/action.c b/private/ntos/tdi/isnp/nb/action.c new file mode 100644 index 000000000..9ff843a76 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/action.c @@ -0,0 +1,221 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains code which implements the TDI action + dispatch routines. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine handles action requests. + +Arguments: + + Device - The netbios device. + + Request - The request describing the action. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + union { + PNB_ACTION_GET_COUNTS GetCounts; + } u; // BUGBUG: Make these unaligned?? + PNWLINK_ACTION NwlinkAction; + UINT i; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + if (NdisBuffer == NULL) { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + + return STATUS_NOT_SUPPORTED; + } + + + // + // Make sure we have enough room for just the header not + // including the data. + // + + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { + NB_DEBUG (QUERY, ("Nwlink action failed, buffer too small\n")); + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + + // + // Make sure that the correct file object is being used. + // + + if (NwlinkAction->OptionType == NWLINK_OPTION_ADDRESS) { + + if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + NB_DEBUG (QUERY, ("Nwlink action failed, not address file\n")); + return STATUS_INVALID_HANDLE; + } + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != NB_ADDRESSFILE_SIGNATURE)) { + + NB_DEBUG (QUERY, ("Nwlink action failed, bad address file\n")); + return STATUS_INVALID_HANDLE; + } + + } else if (NwlinkAction->OptionType != NWLINK_OPTION_CONTROL) { + + NB_DEBUG (QUERY, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); + return STATUS_NOT_SUPPORTED; + } + + + // + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + // + + + Status = STATUS_SUCCESS; + + switch (NwlinkAction->Option) { + + case (I_MIPX | 351): + + // + // A request for details on every binding. + // + + if (DataLength < sizeof(NB_ACTION_GET_COUNTS)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetCounts = (PNB_ACTION_GET_COUNTS)(NwlinkAction->Data); + + u.GetCounts->MaximumNicId = NbiDevice->MaximumNicId; + + for (i = 0; i < 32 ; i++) { + u.GetCounts->NicIdCounts[i] = 0; + } + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[i].Connections; + + while (Connection != NULL) { +#if defined(_PNP_POWER) + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicHandle.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicHandle.NicId]; + } +#else + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicId]; + } +#endif _PNP_POWER + Connection = Connection->NextConnection; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + // + // The Option was not supported, so fail. + // + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (!NT_SUCCESS(Status)) { + NB_DEBUG (QUERY, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); + } +#endif + + return Status; + +} /* NbiTdiAction */ + diff --git a/private/ntos/tdi/isnp/nb/address.c b/private/ntos/tdi/isnp/nb/address.c new file mode 100644 index 000000000..2eb882b80 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/address.c @@ -0,0 +1,2406 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Map all generic accesses to the same one. +// + +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_NETBIOS. + +Arguments: + + Transport - The generic TDI address. + + BroadcastAddressOk - TRUE if we should return the broadcast + address if found. If so, a value of (PVOID)-1 indicates + the broadcast address. + +Return Value: + + A pointer to the Netbios address, or NULL if none is found, + or (PVOID)-1 if the broadcast address is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the Netbios one. + // + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if ((addressName->AddressLength == 0) && + BroadcastAddressOk) { + return (PVOID)-1; + } else if (addressName->AddressLength == sizeof(TDI_ADDRESS_NETBIOS)) { + return ((TDI_ADDRESS_NETBIOS UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* NbiParseTdiAddress */ + + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + NbiPrint0 ("NbfValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* NbiValidateTdiAddress */ + + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + Device - pointer to the device describing the Netbios transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress; + ULONG DesiredShareAccess; + CTELockHandle LockHandle; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + BOOLEAN found = FALSE; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif +#if 0 + TA_NETBIOS_ADDRESS FakeAddress; +#endif + + + // + // The network name is in the EA, passed in the request. + // + + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no EA\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; +#if 0 + TdiBuildNetbiosAddress( + "ADAMBA67 ", + FALSE, + &FakeAddress); + name = (PTRANSPORT_ADDRESS)&FakeAddress; +#endif + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one of type Netbios. This call returns (PVOID)-1 if the + // address is the broadcast address. + // + + NetbiosAddress = NbiParseTdiAddress (name, TRUE); + + if (NetbiosAddress == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no Netbios Address\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // get an address file structure to represent this address. + // + + AddressFile = NbiCreateAddressFile (Device); + + if (AddressFile == (PADDRESS_FILE)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); + +#if defined(_PNP_POWER) + + Address = NbiFindAddress ( + Device, + ( NetbiosAddress == (PVOID)-1 ) ? (PVOID)-1 : NetbiosAddress->NetbiosName + ); + + if (Address == NULL) { + +#else + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Address = NbiLookupAddress (Device, NetbiosAddress); + + if (Address == NULL) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif _PNP_POWER + + // + // This address doesn't exist. Create it. + // This initializes the address with a ref + // of type ADDRESS_FILE, so if we fail here + // we need to remove that. + // + + Address = NbiCreateAddress ( + Device, + NetbiosAddress); + + if (Address != (PADDRESS)NULL) { + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + +#ifdef ISN_NT + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &Address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + return status; + + } + +#endif + + ExReleaseResource (&Device->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { +#else + if (Device->State == DEVICE_STATE_STOPPING) { +#endif _PNP_POWER + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DEVICE_NOT_READY; + + } else { + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->Address = Address; + + NB_INSERT_TAIL_LIST( + &Address->AddressFileDatabase, + &AddressFile->Linkage, + &Address->Lock); + + if (NetbiosAddress == (PVOID)-1) { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } else { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + NbiStartRegistration (Address); + } + + } + + } else { + + ExReleaseResource (&Device->AddressResource); + + // + // If the address could not be created, and is not in the + // process of being created, then we can't open up an address. + // Since we can't use the AddressLock to deref, we just destroy + // the address file. + // + + NbiDestroyAddressFile (AddressFile); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + +#if !defined(_PNP_POWER) + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif !_PNP_POWER + NB_DEBUG2 (ADDRESS, ("Add to address %lx\n", Address)); + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + // + // Make sure the types do not conflict. + // + + if ((NetbiosAddress != (PVOID)-1) && + (NetbiosAddress->NetbiosNameType != Address->NetbiosAddress.NetbiosNameType)) { + + NB_DEBUG (ADDRESS, ("Address types conflict %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + Address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) { + + NB_DEBUG (ADDRESS, ("Address access not allowed %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + +#ifdef ISN_NT + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) { + + NB_DEBUG (ADDRESS, ("Address share access wrong %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + ExReleaseResource (&Device->AddressResource); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // Insert the address file on the address + // list; we will pend this open if the address + // is still registering. If the address has + // already failed as duplicate, then we + // fail the open. + // + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Address duplicated %lx\n", Address)); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + InsertTailList ( + &Address->AddressFileDatabase, + &AddressFile->Linkage); + + // + // Start registration unless it is registered or + // it is the broadcast address. + // + + if ((Address->State == ADDRESS_STATE_REGISTERING) && + (NetbiosAddress != (PVOID)-1)) { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + } else { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + } + + AddressFile->Address = Address; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + + NbiReferenceAddress (Address, AREF_ADDRESS_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } + + } + } + } + + // + // Remove the reference from NbiLookupAddress. + // + + NbiDereferenceAddress (Address, AREF_LOOKUP); + } + + return status; + +} /* NbiOpenAddress */ + + +VOID +NbiStartRegistration( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine starts the registration process for a netbios name + by sending out the first add name packet and starting the timer + so that NbiRegistrationTimeout is called after the correct timeout. + +Arguments: + + Address - The address which is to be registered. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NB_DEBUG2 (ADDRESS, ("StartRegistration of %lx\n", Address)); + + // + // First send out an add name packet. + // + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + Address->RegistrationCount = 0; + + // + // Now start the timer. + // + + NbiReferenceAddress (Address, AREF_TIMER); + + CTEInitTimer (&Address->RegistrationTimer); + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + +} /* NbiStartRegistration */ + + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the address registration + timer expires. It sends another add name if needed, or + checks the result if the correct number have been sent. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the address pointer. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Context; + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PLIST_ENTRY p; + + ++Address->RegistrationCount; + + if ((Address->RegistrationCount < Address->Device->BroadcastCount) && + ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0)) { + + NB_DEBUG2 (ADDRESS, ("Send add name %d for %lx\n", Address->RegistrationCount+1, Address)); + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + + } else { + + // + // The correct number of frames have been sent, see what + // happened. + // + + NB_DEBUG2 (ADDRESS, ("Done with add names for %lx\n", Address)); + + ReferencedAddressFile = NULL; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0) { + Address->State = ADDRESS_STATE_OPEN; + } else { + Address->State = ADDRESS_STATE_STOPPING; + } + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + CTEAssert (AddressFile->State == ADDRESSFILE_STATE_OPENING); + CTEAssert (AddressFile->OpenRequest != NULL); + + NbiReferenceAddressFileLock (AddressFile, AFREF_TIMEOUT); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + // + // Now see what to do with this address file. + // + + REQUEST_INFORMATION(AddressFile->OpenRequest) = 0; + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Open of address file %lx failed, duplicate\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_DUPLICATE_NAME; + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + NB_DEBUG2 (ADDRESS, ("Complete open of address file %lx\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_SUCCESS; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + + } + + NbiCompleteRequest (AddressFile->OpenRequest); + NbiFreeRequest (Address->Device, AddressFile->OpenRequest); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + ReferencedAddressFile = AddressFile; + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + NbiDereferenceAddress (Address, AREF_TIMER); + + } + +} /* NbiRegistrationTimeout */ + + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_FIND_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Quick check for any names starting with this character. + // + + if (Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] == 0) { + return; + } + + // + // Always respond to broadcast requests. + // +#if defined(_PNP_POWER) + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + NbiDereferenceAddress (Address, AREF_FIND); + + } else if ( NbiFindAdapterAddress( NbConnectionless->NameFrame.Name, LOCK_NOT_ACQUIRED ) ) { + + NbiSendNameFrame( + NULL, + (UCHAR)(NB_NAME_UNIQUE | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + } +#else + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + NbiDereferenceAddress (Address, AREF_FIND); + + } +#endif _PNP_POWER +} /* NbiProcessFindName */ + + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_ADD_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + BOOLEAN LocalFrame; + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Ignore any frame that came from us, except for the purpose + // of updating the cache. + // + + if ((Device->Bind.QueryHandler)( + IPX_QUERY_IS_ADDRESS_LOCAL, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + NbConnectionless->IpxHeader.SourceNetwork, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + LocalFrame = TRUE; + + } else { + + LocalFrame = FALSE; + + } + + if (!LocalFrame) { + + if ((Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] != 0) && + (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name))) { + + if (NB_NODE_BROADCAST(NbConnectionless->IpxHeader.DestinationNode)) { + + // + // If this frame is an add name (identified because it is a + // broadcast frame) then respond if we have it registered + // unique, or we have it group and someone is trying to add + // it unique. + // + + if ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) || + ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) && + ((NbConnectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0))) { + + // + // According to GeorgeJ's doc, on a name in use we just + // echo back the name type flags from the request. + // + + NbiSendNameFrame( + Address, + NbConnectionless->NameFrame.NameTypeFlag, + NB_CMD_NAME_IN_USE, + RemoteAddress, +#if defined(_PNP_POWER) + NbConnectionless); +#else + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); +#endif _PNP_POWER + } + + } else if ((*(UNALIGNED ULONG *)NbConnectionless->IpxHeader.DestinationNetwork == + *(UNALIGNED ULONG *)Device->Bind.Network) && + NB_NODE_EQUAL(NbConnectionless->IpxHeader.DestinationNode, Device->Bind.Node)) { + + // + // If this is an add name response (which will be sent + // directly to us) then we need to mark the address + // as such. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + Address->Flags |= ADDRESS_FLAGS_DUPLICATE_NAME; + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + } + + + // + // Pass this frame over to the netbios cache management + // routines to check if they need to update their cache. + // + + CacheUpdateFromAddName (RemoteAddress, NbConnectionless, LocalFrame); + +} /* NbiProcessAddName */ + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + NetbiosAddress - The name to assign to this address, or -1 if it + is the broadcast address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PADDRESS Address; + + Address = (PADDRESS)NbiAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + if (Address == NULL) { + NB_DEBUG (ADDRESS, ("Create address %.16s failed\n", + (NetbiosAddress == (PVOID)-1) ? "<broadcast>" : NetbiosAddress->NetbiosName)); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address %lx (%.16s)\n", Address, + (NetbiosAddress == (PVOID)-1) ? "<broadcast>" : NetbiosAddress->NetbiosName)); + RtlZeroMemory (Address, sizeof(ADDRESS)); + + Address->Type = NB_ADDRESS_SIGNATURE; + Address->Size = sizeof (ADDRESS); + Address->State = ADDRESS_STATE_REGISTERING; + Address->Flags = 0; + + Address->Device = Device; + Address->DeviceLock = &Device->Lock; + CTEInitLock (&Address->Lock.Lock); + + InitializeListHead (&Address->AddressFileDatabase); + + Address->ReferenceCount = 1; +#if DBG + Address->RefTypes[AREF_ADDRESS_FILE] = 1; +#endif + + if (NetbiosAddress == (PVOID)-1) { + Address->NetbiosAddress.Broadcast = TRUE; + } else { + Address->NetbiosAddress.Broadcast = FALSE; + Address->NetbiosAddress.NetbiosNameType = NetbiosAddress->NetbiosNameType; + RtlCopyMemory (Address->NetbiosAddress.NetbiosName, NetbiosAddress->NetbiosName, 16); + ++Device->AddressCounts[NetbiosAddress->NetbiosName[0]]; + } + + if (Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) { + Address->NameTypeFlag = NB_NAME_UNIQUE; + } else { + Address->NameTypeFlag = NB_NAME_GROUP; + } + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + InsertTailList (&Device->AddressDatabase, &Address->Linkage); + ++Device->AddressCount; + + NbiReferenceDevice (Device, DREF_ADDRESS); + + return Address; + +} /* NbiCreateAddress */ + + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a ADDRESS_FILE object + + ConflictIsOk - TRUE if we should succeed the verify even if the + corresponding address is in CONFLICT. ( For Close and + cleanup we return STATUS_SUCCESS even if we are in conflict + so that the addressfile can be destroyed) + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PADDRESS Address; + BOOLEAN LockHeld = FALSE; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == NB_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + Address = AddressFile->Address; + + if ((Address->Size == sizeof (ADDRESS)) && + (Address->Type == NB_ADDRESS_SIGNATURE) ) { + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + LockHeld = TRUE; + +#if defined(_PNP_POWER) + if (Address->State != ADDRESS_STATE_STOPPING && + ( ConflictIsOk || ( !(Address->Flags & ADDRESS_FLAGS_CONFLICT) )) ) { +#else + if (Address->State != ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + + NbiReferenceAddressFileLock (AddressFile, AFREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx closing\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx bad signature\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + NbiPrint1("NbiVerifyAddressFile: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyAddressFile: AF %lx exception\n", Address); + if (LockHeld) { + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyAddressFile */ + + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by NbiDerefAddress when + the reference count goes to 0. + + This thread is only queued by NbiDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Parameter; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle; + + NB_DEBUG2 (ADDRESS, ("Destroy address %lx <%.16s>\n", Address, + Address->NetbiosAddress.Broadcast ? "<broadcast>" : Address->NetbiosAddress.NetbiosName)); + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (!Address->NetbiosAddress.Broadcast) { + --Device->AddressCounts[Address->NetbiosAddress.NetbiosName[0]]; + } + --Device->AddressCount; + RemoveEntryList (&Address->Linkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + + NbiDereferenceDevice (Device, DREF_ADDRESS); + +} /* NbiDestroyAddress */ + + +#if DBG +VOID +NbiRefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); +} /* NbiRefAddress */ + + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); + +} /* NbiRefAddressLock */ +#endif + + +VOID +NbiDerefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &Address->ReferenceCount ); + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + NbiDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + NbiDestroyAddress(Address); +#endif + + } + +} /* NbiDerefAddress */ + + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile; + UINT i; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + AddressFile = (PADDRESS_FILE)NbiAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + if (AddressFile == NULL) { + NB_DEBUG (ADDRESS, ("Create address file failed\n")); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address file %lx\n", AddressFile)); + + RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE)); + + AddressFile->Type = NB_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + AddressFile->Address = NULL; +#ifdef ISN_NT + AddressFile->FileObject = NULL; +#endif + AddressFile->Device = Device; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + AddressFile->ReferenceCount = 1; +#if DBG + AddressFile->RefTypes[AFREF_CREATE] = 1; +#endif + AddressFile->CloseRequest = (PREQUEST)NULL; + + // + // Initialize the request handlers. + // + + for (i = 0; i < 6; i++) { + AddressFile->RegisteredHandler[i] = FALSE; + AddressFile->HandlerContexts[i] = NULL; + AddressFile->Handlers[i] = TdiDefaultHandlers[i]; + } + + CTEAssert (AddressFile->ConnectionHandler == TdiDefaultConnectHandler); + CTEAssert (AddressFile->DisconnectHandler == TdiDefaultDisconnectHandler); + CTEAssert (AddressFile->ErrorHandler == TdiDefaultErrorHandler); + CTEAssert (AddressFile->ReceiveHandler == TdiDefaultReceiveHandler); + CTEAssert (AddressFile->ReceiveDatagramHandler == TdiDefaultRcvDatagramHandler); + CTEAssert (AddressFile->ExpeditedDataHandler == TdiDefaultRcvExpeditedHandler); + + return AddressFile; + +} /* NbiCreateAddressFile */ + + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by NbiDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PADDRESS Address; + PDEVICE Device; + PREQUEST CloseRequest; + BOOLEAN StopAddress; + + NB_DEBUG2 (ADDRESS, ("Destroy address file %lx\n", AddressFile)); + + Address = AddressFile->Address; + Device = AddressFile->Device; + + if (Address) { + + // + // This addressfile was associated with an address. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (Address->AddressFileDatabase.Flink == &Address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle1); + Address->State = ADDRESS_STATE_STOPPING; + NB_FREE_LOCK (&Device->Lock, LockHandle1); + + StopAddress = TRUE; + + } else { + + StopAddress = FALSE; + } + + AddressFile->Address = NULL; + +#ifdef ISN_NT + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; +#endif + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + if (StopAddress && (!Address->NetbiosAddress.Broadcast)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | + NB_NAME_USED | NB_NAME_REGISTERED | NB_NAME_DEREGISTERED), + NB_CMD_DELETE_NAME, + NULL, + NULL); + } + + // + // Now dereference the owning address. + // + + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + + } + + // + // Save this for later completion. + // + + CloseRequest = AddressFile->CloseRequest; + + // + // return the addressFile to the pool of address files + // + + NbiFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + + if (CloseRequest != (PREQUEST)NULL) { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + NbiCompleteRequest (CloseRequest); + NbiFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} /* NbiDestroyAddressFile */ + + +#if DBG +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); +} /* NbiRefAddressFile */ + + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); + +} /* NbiRefAddressFileLock */ + +#endif + + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &AddressFile->ReferenceCount ); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + NbiDestroyAddressFile (AddressFile); + } + +} /* NbiDerefAddressFile */ + +#if !defined(_PNP_POWER) + +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosAddress - The name to look up, or -1 if the broadcast + address is being searched for. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if (Address->State == ADDRESS_STATE_STOPPING) { + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosAddress != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if (NetbiosAddress == (PVOID)-1) { + continue; + } + + if (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosAddress->NetbiosName, + 16)) { + continue; + } + } + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_LOOKUP); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + return NULL; + +} /* NbiLookupAddress */ +#endif !_PNP_POWER + + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NetbiosName + values. If a match is found, the address is referenced and the + pointer is returned. + + We ignore any addresses which are either STOPPING or are under + CONFLICT state. + + A name in CONFLICT is dead for all practical purposes + except Close. This routine is called by various name service, + datagram and session sevice routines. We hide any names in CONFLICT + from these routines. + + This routine is also called by NbiTdiOpenAddress(). + A name could have been marked in CONFLICT ages ago(but is not closed + yet). We must allow another open of the same name as that might + succeed now. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosName - The name to look up, or -1 for the broadcast name. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + +#if defined(_PNP_POWER) + if ( ( Address->State == ADDRESS_STATE_STOPPING ) || + ( Address->Flags & ADDRESS_FLAGS_CONFLICT ) ) { +#else + if (Address->State == ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosName != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if ((NetbiosName == (PVOID)-1) || + (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosName, + 16))) { + continue; + } + } + + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_FIND); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + +} /* NbiFindAddress */ + + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PLIST_ENTRY p; + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle1, LockHandle2; + LIST_ENTRY SendDatagramList; + PNB_SEND_RESERVED Reserved; + PREQUEST DatagramRequest; + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + LIST_ENTRY DatagramQ; + + + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + NB_FREE_LOCK (&Address->Lock, LockHandle1); + return STATUS_SUCCESS; + } + + + // + // This prevents anybody else from being put on the + // ConnectionDatabase. + // + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + CTEAssert (Connection->AddressFile == AddressFile); + Connection->AddressFileLinked = FALSE; + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->ReferenceCount == 0) { + + // + // The refcount is already 0, so we can just + // NULL out this field to complete the disassociate. + // + + Connection->AddressFile = NULL; + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + // + // Mark this so we know to disassociate when the + // count goes to 0, but that there is no specific + // request pending on it. We also stop the connection + // to shut it down. + // + + Connection->DisassociatePending = (PVOID)-1; + NbiReferenceConnectionLock (Connection, CREF_DISASSOC); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle3); + + // + // This call frees the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle3)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_DISASSOC); + + } + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Abort all pending send datagrams. + // + // BUGBUG: Also make them cancellable. + // + + InitializeListHead (&SendDatagramList); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + if (Reserved->u.SR_DG.AddressFile == AddressFile) { + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&SendDatagramList, &Reserved->WaitLinkage); + + } + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + for (p = SendDatagramList.Flink; p != &SendDatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Aborting datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + + // + // Abort all pending receive datagrams. + // + + InitializeListHead( &DatagramQ ); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + while (!IsListEmpty(&AddressFile->ReceiveDatagramQueue)) { + + p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue); + Request = LIST_ENTRY_TO_REQUEST (p); + + // Insert it on a private Q, so it can be completed later. + InsertTailList( &DatagramQ, p); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + for( p = DatagramQ.Flink; p != &DatagramQ; ) { + Request = LIST_ENTRY_TO_REQUEST ( p ); + + p = p->Flink; + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + } + + + return STATUS_SUCCESS; + +} /* NbiStopAddressFile */ + + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PADDRESS Address; + PADDRESS_FILE AddressFile; + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + AddressFile->CloseRequest = Request; + + // + // We assume that addressFile has already been verified + // at this point. + // + + Address = AddressFile->Address; + CTEAssert (Address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); +#ifdef ISN_NT + IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess); +#endif + ExReleaseResource (&Device->AddressResource); + + NbiStopAddressFile (AddressFile, Address); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + return STATUS_PENDING; + +} /* NbiCloseAddressFile */ + +#if defined(_PNP_POWER) + + +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ) + +/*++ + +Routine Description: + + This routine creates an adapter address sttuctures which stores + the netbios name of an adapter. the netbios name has 12 0's + followed by the mac address of the adapter. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + AdapterMacAddress - pointer to the adapter mac address given to us + by IPX. + +Return Value: + + The newly created address, or NULL if none can be allocated. + THIS ROUTINE MUST BE CALLED WITH THE DEVICE LOCK HELD. + +--*/ + +{ + PADAPTER_ADDRESS AdapterAddress; + CTELockHandle LockHandle; + PDEVICE Device = NbiDevice; + + AdapterAddress = (PADAPTER_ADDRESS)NbiAllocateMemory (sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "Adapter Address"); + if (AdapterAddress == NULL) { + NB_DEBUG (ADDRESS, ("Create Adapter Address %<2.2x><2.2x><2.2x><2.2x><2.2x><2.2x> failed\n", + AdapterMacAddress[0], + AdapterMacAddress[1], + AdapterMacAddress[2], + AdapterMacAddress[3], + AdapterMacAddress[4], + AdapterMacAddress[5] + )); + return NULL; + } + + AdapterAddress->Type = NB_ADAPTER_ADDRESS_SIGNATURE; + AdapterAddress->Size = sizeof (ADDRESS); + + RtlZeroMemory(AdapterAddress->NetbiosName, 10); + RtlCopyMemory(&AdapterAddress->NetbiosName[10], AdapterMacAddress, 6); + + + InsertTailList (&Device->AdapterAddressDatabase, &AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + return AdapterAddress; + +} /* NbiCreateAdapterAddress */ + + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine destroys the adapter address structure and removes it + from the list. + +Arguments: + + AdapterAddress - Pointer to an adapter address structure to be destroyed + NULL if AdapterMacAddress is given. + + AdapterMacAddress - Mac Address of the adapter which just got deleted. so find + the corresponding adapter address structure and remove it. + NULL if AdapterAddress is supplied. + +Return Value: + + STATUS_SUCCESS or STATUS_UNSUCCESSFUL if address not found. + + THIS ROUTINE ASSUMES THE THE DEVICE IS LOCK IS HELD BY THE CALLER + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + UCHAR NetbiosName[NB_NETBIOS_NAME_SIZE]; + + + // + + CTEAssert( AdapterAddress || AdapterMacAddress ); + if ( !AdapterAddress ) { + RtlZeroMemory( NetbiosName, 10); + RtlCopyMemory( &NetbiosName[10], AdapterMacAddress, 6 ); + + AdapterAddress = NbiFindAdapterAddress( NetbiosName, LOCK_ACQUIRED ); + + if ( !AdapterAddress ) { + return STATUS_UNSUCCESSFUL; + } + } + + NB_DEBUG2 (ADDRESS, ("Destroy Adapter address %lx <%.16s>\n", AdapterAddress,AdapterAddress->NetbiosName)); + RemoveEntryList (&AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + NbiFreeMemory (AdapterAddress, sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "AdapterAddress"); + + return STATUS_SUCCESS; +} /* NbiDestroyAdapterAddress */ + + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ) + +/*++ + +Routine Description: + + This routine finds an adapter address ( netbios name ) for the given + AdapterMacAddress and returns a pointer to it. Note that no reference + is done on this address, so if this routine is called without the device + lock, the caller must not use this pointer directly. + +Arguments: + + NetbiosName - NetbiosName to be found. + + LockHeld - is device lock already held or not. + +Return Value: + + Pointer to the adapter address if found, NULL otherwise. + +--*/ + +{ + + PLIST_ENTRY p; + CTELockHandle LockHandle; + PADAPTER_ADDRESS AdapterAddress; + PDEVICE Device = NbiDevice; + + + if ( !LockHeld ) { + NB_GET_LOCK( &Device->Lock, &LockHandle ); + } + for ( p = Device->AdapterAddressDatabase.Flink; + p != &Device->AdapterAddressDatabase; + p = p->Flink ) { + + AdapterAddress = CONTAINING_RECORD( p, ADAPTER_ADDRESS, Linkage ); + if ( RtlEqualMemory( + NetbiosName, + AdapterAddress->NetbiosName, + NB_NETBIOS_NAME_SIZE ) ) { + break; + } + } + + + if ( !LockHeld ) { + NB_FREE_LOCK( &Device->Lock, LockHandle ); + } + + if ( p == &Device->AdapterAddressDatabase ) { + return NULL; + } else { + return AdapterAddress; + } + +} /* NbiFindAdapterAddress */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/nb/autodial.c b/private/ntos/tdi/isnp/nb/autodial.c new file mode 100644 index 000000000..ec56e2351 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/autodial.c @@ -0,0 +1,526 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + NT specific routines for interfacing with the + RAS AutoDial driver (rasacd.sys). + +Author: + + Anthony Discolo (adiscolo) Aug 30, 1995 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + adiscolo 08-30-95 created + +Notes: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL + +#include <acd.h> +#include <acdapi.h> + +// +// Global variables +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbi '; + + + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PDEVICE pDevice = pArgs[0]; + PCONNECTION pConnection = pArgs[1]; + PREQUEST pRequest = pArgs[2]; + CTELockHandle ConnectionLH, DeviceLH; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // +#if notdef // DBG + DbgPrint("NbiRetryTdiConnect: fSuccess=%d, pConnection=0x%x\n", fSuccess, pConnection); +#endif + + status = NbiVerifyConnection(pConnection); + if (!NT_SUCCESS(status)) { + DbgPrint( + "NbiRetryTdiConnect: NbiVerifyConnection failed on connection 0x%x (status=0x%x)\n", + pConnection, + status); + return; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&pConnection->Lock, &ConnectionLH); + NB_GET_LOCK (&pDevice->Lock, &DeviceLH); + +#if notdef // DBG + DbgPrint( + "NbiRetryTdiConnect: AddressFile=0x%x, DisassociatePending=0x%x, ClosePending=0x%x\n", + pConnection->AddressFile, + pConnection->DisassociatePending, + pConnection->ClosePending); +#endif + + if ((pConnection->AddressFile != NULL) && + (pConnection->AddressFile != (PVOID)-1) && + (pConnection->DisassociatePending == NULL) && + (pConnection->ClosePending == NULL)) + { + NbiReferenceConnectionLock(pConnection, CREF_CONNECT); + // + // Clear the AUTOCONNECTING flag since we + // done with the automatic connection attempt. + // Set the AUTOCONNECTED flag to prevent us + // from attempting an automatic connection + // for this connection again. + // + pConnection->Flags &= ~CONNECTION_FLAGS_AUTOCONNECTING; + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTED; + + pConnection->State = CONNECTION_STATE_CONNECTING; + pConnection->Retries = pDevice->ConnectionCount; + status = NbiTdiConnectFindName( + pDevice, + pRequest, + pConnection, + CancelLH, + ConnectionLH, + DeviceLH, + &bLockFreed); + } + else { + DbgPrint("NbiRetryTdiConnect: Connect on invalid connection 0x%x\n", pConnection); + + pConnection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&pDevice->Lock, DeviceLH); + status = STATUS_INVALID_CONNECTION; + } + if (!bLockFreed) { + NB_FREE_LOCK (&pConnection->Lock, ConnectionLH); + NB_FREE_CANCEL_LOCK(CancelLH); + } + // + // Complete the irp if necessary. + // + if (status != STATUS_PENDING) { + REQUEST_INFORMATION(pRequest) = 0; + REQUEST_STATUS(pRequest) = status; + + NbiCompleteRequest(pRequest); + NbiFreeRequest(pDevice, pRequest); + } + NbiDereferenceConnection(pConnection, CREF_VERIFY); +} /* NbiRetryTdiConnect */ + + + +BOOLEAN +NbiCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ +#if notdef // DBG + DbgPrint("NbiCancelAutodialRequest: pArg=0x%x\n", pArg); +#endif + if (nArgs != 2) + return FALSE; + + return (pArgs[1] == pArg); +} // NbiCancelAutoDialRequest + + + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ) + +/*++ + +DESCRIPTION + This routine is called by the I/O system to cancel a connection + when we are attempting to restore an automatic connection. + +ARGUMENTS + pDevice: a pointer to the device object for this driver + + pRequest: a pointer to the irp to be cancelled + + pConnection: a pointer to the connnection to be cancelled + +RETURN VALUE + TRUE if the request was canceled; FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint( + "NbiCancelTdiConnect: pIrp=0x%x, RemoteName=%-15.15s, pConnection=0x%x\n", + pRequest, + addr.cNetbios, + pConnection); +#endif + // + // Cancel the autodial request. + // + return (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbiCancelAutoDialRequest, + pConnection); +} // NbiCancelTdiConnect + + + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. + +Arguments: + + pDevice - a pointer to the DEVICE structure for this connection + + pConnection - a pointer to the CONNECTION block for this connection + + ulFlags - connection flags to pass to the automatic + connection driver + + pProc - a callback procedure when the automatic connection completes + + pRequest - a pointer to the request irp + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + PVOID pArgs[3]; + BOOLEAN bSuccess; + + // + // If we've already attempted an automatic connection + // on this connection, don't try it again. + // + if (pConnection->Flags & CONNECTION_FLAGS_AUTOCONNECTED) + return FALSE; + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint("NbiAttemptAutoDial: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Attempt to start the connection. + // NbiRetryTdiConnect() will be called + // when the connection process has completed. + // + pArgs[0] = pDevice; + pArgs[1] = pConnection; + pArgs[2] = pRequest; + bSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 3, + pArgs); + if (bSuccess) { + // + // Set the AUTOCONNECTING flag so we know + // to also cancel the connection in the + // automatic connection driver if this + // request gets canceled. + // + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTING; + } +} // NbiAttemptAutoDial + + + +VOID +NbiNoteNewConnection( + IN PCONNECTION pConnection + ) +{ + NTSTATUS status; + ACD_ADDR addr; + ACD_ADAPTER adapter; + ULONG i; + TDI_ADDRESS_IPX tdiIpxAddress; + + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); + // + // Determine the mac address of the adapter + // over which the connection has been made. + // + status = (pConnection->Device->Bind.QueryHandler)( + IPX_QUERY_IPX_ADDRESS, +#if defined(_PNP_POWER) + &pConnection->LocalTarget.NicHandle, +#else + pConnection->LocalTarget.NicId, +#endif _PNP_POWER + &tdiIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + if (status != STATUS_SUCCESS) { +#if notdef // DBG + DbgPrint("NbiNoteNewConnection: QueryHandler(IPX_QUERY_IPX_ADDRESS) failed (status=0x%x)\n", status); + return; +#endif + } + // + // Copy the source mac address to identify + // the adapter. + // + adapter.fType = ACD_ADAPTER_MAC; + for (i = 0; i < 6; i++) + adapter.cMac[i] = tdiIpxAddress.NodeAddress[i]; +#if notdef // DBG + DbgPrint( + "NbiNoteNewConnection: address=%-15.15s, remote mac=%02x:%02x:%02x:%02x:%02x:%02x\n", + addr.cNetbios, + adapter.cMac[0], + adapter.cMac[1], + adapter.cMac[2], + adapter.cMac[3], + adapter.cMac[4], + adapter.cMac[5]); +#endif + // + // Simply notify the automatic connection driver + // that a successful connection has been made. + // + (*AcdDriverG.lpfnNewConnection)( + &addr, + &adapter); +} // NbiNoteNewConnection + + + +VOID +NbiAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdBind + + + +VOID +NbiAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdUnbind + +#endif // RASAUTODIAL diff --git a/private/ntos/tdi/isnp/nb/bind.c b/private/ntos/tdi/isnp/nb/bind.c new file mode 100644 index 000000000..7dc20d0d5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/bind.c @@ -0,0 +1,593 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiBind) +#endif + +#if defined(_PNP_POWER) +// +// local functions. +// +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine binds the Netbios module of ISN to the IPX + module, which provides the NDIS binding services. + +Arguments: + + Device - Pointer to the Netbios device. + + Config - Pointer to the configuration information. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; +/* union { + IPX_INTERNAL_BIND_INPUT Input; + IPX_INTERNAL_BIND_OUTPUT Output; + } Bind; +*/ + InitializeObjectAttributes( + &ObjectAttributes, + &Config->BindName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwCreateFile( + &Device->BindHandle, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(Status)) { + + NB_DEBUG (BIND, ("Could not open IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + return Status; + } + + // + // Fill in our bind data. + // + +#if defined(_PNP_POWER) + Device->BindInput.Version = ISN_VERSION; +#else + Device->BindInput.Version = 1; +#endif _PNP_POWER + Device->BindInput.Identifier = IDENTIFIER_NB; + Device->BindInput.BroadcastEnable = TRUE; + Device->BindInput.LookaheadRequired = 192; + Device->BindInput.ProtocolOptions = 0; + Device->BindInput.ReceiveHandler = NbiReceive; + Device->BindInput.ReceiveCompleteHandler = NbiReceiveComplete; + Device->BindInput.StatusHandler = NbiStatus; + Device->BindInput.SendCompleteHandler = NbiSendComplete; + Device->BindInput.TransferDataCompleteHandler = NbiTransferDataComplete; + Device->BindInput.FindRouteCompleteHandler = NbiFindRouteComplete; + Device->BindInput.LineUpHandler = NbiLineUp; + Device->BindInput.LineDownHandler = NbiLineDown; + Device->BindInput.ScheduleRouteHandler = NULL; +#if defined(_PNP_POWER) + Device->BindInput.PnPHandler = NbiPnPNotification; +#endif _PNP_POWER + + + Status = ZwDeviceIoControlFile( + Device->BindHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + &Device->BindInput, // Input Buffer + sizeof(Device->BindInput), // Input Buffer Length + &Device->Bind, // OutputBuffer + sizeof(Device->Bind)); // OutputBufferLength + + // + // We open synchronous, so this shouldn't happen. + // + + CTEAssert (Status != STATUS_PENDING); + + // + // Save the bind data. + // + + if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (BIND, ("Successfully bound to IPX (%ws)\n", + Config->BindName.Buffer)); +// RtlCopyMemory (&Device->Bind, &Bind.Output, sizeof(IPX_INTERNAL_BIND_OUTPUT)); + +#if !defined(_PNP_POWER) + RtlZeroMemory (Device->ReservedNetbiosName, 16); + RtlCopyMemory (&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAXIMUM_NIC_ID, + (USHORT)0, + &Device->MaximumNicId, + sizeof(Device->MaximumNicId), + NULL); + CTEAssert (Status == STATUS_SUCCESS); +#endif !_PNP_POWER + + } else { + + NB_DEBUG (BIND, ("Could not bind to IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_BINDING_FAILED, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + ZwClose(Device->BindHandle); + } + + return Status; + +} /* NbiBind */ + + +VOID +NbiUnbind( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This function closes the binding between the Netbios over + IPX module and the IPX module previously established by + NbiBind. + +Arguments: + + Device - The netbios device object. + +Return Value: + + None. + +--*/ + +{ + ZwClose (Device->BindHandle); + +} /* NbiUnbind */ + + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +/*++ + +Routine Description: + + This function receives a status indication from IPX, + corresponding to a status indication from an underlying + NDIS driver. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + GeneralStatus - The general status code. + + StatusBuffer - The status buffer. + + StatusBufferLength - The length of the status buffer. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiStatus */ + + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + + +/*++ + +Routine Description: + + This function receives line up indications from IPX, + indicating that the specified adapter is now up with + the characteristics shown. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + LineInfo - Information about the adapter's medium. + + DeviceType - The type of the adapter. + + ConfigurationData - IPX-specific configuration data. + +Return Value: + + None. + +--*/ + +{ + PIPXCP_CONFIGURATION Configuration = (PIPXCP_CONFIGURATION)ConfigurationData; + + // + // Update queries have NULL as the ConfigurationData. These + // only indicate changes in LineInfo. BUGBUG Ignore these + // for the moment. + // + + if (Configuration == NULL) { + return; + } + +#if !defined(_PNP_POWER) + // + // Since Netbios outgoing queries only go out on network 1, + // we ignore this (BUGBUG for the moment) unless that is + // the NIC it is on. + // + + if (NicId == 1) { + + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNetwork, Configuration->Network, 4); + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNode, Configuration->LocalNode, 6); + + } +#endif !_PNP_POWER +} /* NbiLineUp */ + + +VOID +NbiLineDown( + IN USHORT NicId + ) + + +/*++ + +Routine Description: + + This function receives line down indications from IPX, + indicating that the specified adapter is no longer + up. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiLineDown */ + +#if defined(_PNP_POWER) + +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX. + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + UCHAR PrevReservedName[NB_NETBIOS_NAME_SIZE]; + UNICODE_STRING UnicodeDeviceName; + + + NB_DEBUG2( DEVICE, ("Received a pnp notification, opcode %d\n",OpCode )); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + BOOLEAN ReallocReceiveBuffers = FALSE; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + if ( PnPInfo->NewReservedAddress ) { + + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + +// RtlZeroMemory(Device->ReservedNetbiosName, NB_NETBIOS_NAME_SIZE); +// RtlCopyMemory(&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + } + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->State != DEVICE_STATE_OPEN ); + + + // + // we must do this while we still have the device lock. + // + if ( !Device->LongTimerRunning ) { + Device->LongTimerRunning = TRUE; + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } + + Device->State = DEVICE_STATE_OPEN; + + CTEAssert( !Device->MaximumNicId ); + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + Device->Bind.LineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumSendSize; + ReallocReceiveBuffers = TRUE; + } else { + if ( PnPInfo->LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + ReallocReceiveBuffers = TRUE; + } + // + // MaxSendSize could become smaller. + // + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + } + + Device->MaximumNicId++; + + + // + // + NbiCreateAdapterAddress( PnPInfo->NodeAddress ); + + // + // And finally remove all the failed cache entries since we might + // find those routes using this new adapter + // + FlushFailedNetbiosCacheEntries(Device->NameCache); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + if ( ReallocReceiveBuffers ) { + PWORK_QUEUE_ITEM WorkItem; + + WorkItem = NbiAllocateMemory( sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buffer work item"); + + if ( WorkItem ) { + ExInitializeWorkItem( WorkItem, NbiReAllocateReceiveBufferPool, (PVOID) WorkItem ); + ExQueueWorkItem( WorkItem, DelayedWorkQueue ); + } else { + NB_DEBUG( DEVICE, ("Cannt schdule work item to realloc receive buffer pool\n")); + } + } + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->DeviceName; + UnicodeDeviceName.MaximumLength = Device->DeviceNameLength; + UnicodeDeviceName.Length = Device->DeviceNameLength - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to register nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + CTEAssert( Device->MaximumNicId ); + Device->MaximumNicId--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->State = DEVICE_STATE_LOADED; + Device->MaximumNicId = 0; + + } + + + // + // MaximumSendSize could change if the card with the smallest send size just + // got removed. MaximumPacketSize could only become smaller and we ignore that + // since we dont need to(want to) realloc ReceiveBuffers. + // + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + RemoveInvalidRoutesFromNetbiosCacheTable( Device->NameCache, &PnPInfo->NicHandle ); + + NbiDestroyAdapterAddress( NULL, PnPInfo->NodeAddress ); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + +/* // + // Now mark the previous reserved name in conflict if it has + // been registered by any of our client + // + if ( Address = NbiFindAddress( Device, PrevReservedName ) ) { + NB_GET_LOCK( &Address->Lock, &LockHandle ); + Address->Flags |= ADDRESS_FLAGS_CONFLICT; + NB_FREE_LOCK( &Address->Lock, LockHandle ); + + NB_DEBUG( ADDRESS, ("Reserved Address %lx<%.16s> is marked CONFLICT\n",Address,Address->NetbiosAddress.NetbiosName)); + // + // nbifindaddress added a reference, so deref + // + NbiDereferenceAddress( Address, AREF_FIND ); + } +*/ + + // + // inform tdi clients about the device deletion + // + if ( PnPInfo->FirstORLastDevice ) { + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to Deregister nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + PADDRESS Address; + BOOLEAN ReservedNameClosing = FALSE; + + CTEAssert( PnPInfo->NewReservedAddress ); + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/nb/cache.c b/private/ntos/tdi/isnp/nb/cache.c new file mode 100644 index 000000000..cbd27ad67 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/cache.c @@ -0,0 +1,2746 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + cache.c + +Abstract: + + This module contains the name cache routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 20-December-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> + +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ); + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); +#endif // RASAUTODIAL + +// +// BUGBUG: We should change to monitor add name packets better, +// so if we get an add for a different place we attempt to determine +// if it is real or bogus and update if possible. +// + + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +) + +/*++ + +Routine Description: + + This routine looks up a particular remote name in the + Netbios name cache. If it cannot find it, a find name + request is queued up. + + THIS REQUEST IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The netbios device. + + Type - Defines the type. The effect this has is: + FindNameConnect - On connects we will ignore an existing + cache entry if it got no response before. + FindNameNetbiosFindName - For these we ignore an existing + cache entry if it is for a group name -- this is + because the find name wants the address of every + machine, not just the network list. + FindNameOther - Normal handling is done. + + RemoteName - The name to be discovered -- will be NULL if it + is the broadcast address. + + CacheName - Returns the cache entry that was discovered. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE FoundCacheName; + PNB_SEND_RESERVED Reserved; + PUCHAR RealRemoteName; // RemoteName or NetbiosBroadcastName + + // + // First scan the netbios name cache to see if we know + // about this remote. + // + + if (RemoteName) { + RealRemoteName = RemoteName; + } else { + RealRemoteName = NetbiosBroadcastName; + } + + if ( FindInNetbiosCacheTable ( Device->NameCache, + RealRemoteName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + // + // If this is a netbios find name, we only can use unique + // names in the cache; for the group ones we need to requery + // because the cache only lists networks, not individual machines. + // For connect requests, if we find an empty cache entry we + // remove it and requery. + // + + if ( FoundCacheName->Unique || (Type != FindNameNetbiosFindName) ) { + + if (FoundCacheName->NetworksUsed > 0) { + + *CacheName = FoundCacheName; + NB_DEBUG2 (CACHE, ("Found cache name <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_SUCCESS; + + } else { + + if (Type != FindNameConnect) { + + if (FoundCacheName->FailedOnDownWan) { + NB_DEBUG2 (CACHE, ("Found cache name, but down wan <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_DEVICE_DOES_NOT_EXIST; + } else { + NB_DEBUG2 (CACHE, ("Found cache name, but no nets <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_BAD_NETWORK_PATH; + } + + } else { + + // + // This is a connect and the current cache entry + // has zero names; delete it. + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + CTEAssert (FoundCacheName->ReferenceCount == 1); + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free unneeded empty cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + } + + } + } + } + } + + + // + // There was no suitable cache entry for this network, first see + // if there is one pending. + // + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // For this purpose we ignore a packet if a route + // has been found and it was for a unique name. This + // is because the cache information has already been + // inserted for this name. Otherwise if the name has + // since been deleted from the cache, the request + // that is looking for this name will starve because + // FindNameTimeout will just destroy the packet. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + continue; + } + + if (RtlEqualMemory( + Reserved->u.SR_FN.NetbiosName, + RealRemoteName, 16)) { + + NB_DEBUG2 (CACHE, ("Cache name already pending <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + + // + // There is already one pending. If it is for a group + // name and this is a netbios find name, we make sure + // the retry count is such that at least one more + // query will be sent, so the netbios find name + // buffer can be filled with the responses from this. + // + + if ((Type == FindNameNetbiosFindName) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) && + (Reserved->u.SR_FN.RetryCount == Device->BroadcastCount)) { + + --Reserved->u.SR_FN.RetryCount; + } + + return STATUS_PENDING; + } + } + + s = NbiPopSendPacket(Device, TRUE); + + if (s == NULL) { + NB_DEBUG (CACHE, ("Couldn't get packet to find <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = FALSE; + Reserved->Type = SEND_TYPE_FIND_NAME; + RtlCopyMemory (Reserved->u.SR_FN.NetbiosName, RealRemoteName, 16); + Reserved->u.SR_FN.StatusAndSentOnUpLine = FNStatusNoResponse; // SentOnUpLine is FALSE + Reserved->u.SR_FN.RetryCount = 0; + Reserved->u.SR_FN.NewCache = NULL; + Reserved->u.SR_FN.SendTime = Device->FindNameTime; +#if !defined(_PNP_POWER) + Reserved->u.SR_FN.CurrentNicId = 1; + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAX_TYPE_20_NIC_ID, + (USHORT)0, + &Reserved->u.SR_FN.MaximumNicId, + sizeof(USHORT), + NULL); + + if (Reserved->u.SR_FN.MaximumNicId == 0) { + Reserved->u.SR_FN.MaximumNicId = 1; // code assumes at least one + } +#endif !_PNP_POWER + NB_DEBUG2 (CACHE, ("Queued FIND_NAME %lx for <%.16s>\n", + Reserved, RemoteName ? RemoteName : "<broadcast>")); + + + InsertHeadList( + &Device->WaitingFindNames, + &Reserved->WaitLinkage); + + ++Device->FindNamePacketCount; + + if (!Device->FindNameTimerActive) { + + Device->FindNameTimerActive = TRUE; + NbiReferenceDevice (Device, DREF_FN_TIMER); + + CTEStartTimer( + &Device->FindNameTimer, + 1, // 1 ms, i.e. expire immediately + FindNameTimeout, + (PVOID)Device); + } + + NbiReferenceDevice (Device, DREF_FIND_NAME); + + return STATUS_PENDING; + +} /* CacheFindName */ + + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the find name timer expires. + It is called every FIND_NAME_GRANULARITY milliseconds unless there + is nothing to do. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, q; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + PNETBIOS_CACHE FoundCacheName; + NDIS_STATUS NdisStatus; +#if !defined(_PNP_POWER) + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif !_PNP_POWER + NB_DEFINE_LOCK_HANDLE (LockHandle) + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->FindNameTime; + + if (Device->FindNamePacketCount == 0) { + + NB_DEBUG2 (CACHE, ("FindNameTimeout exiting\n")); + + Device->FindNameTimerActive = FALSE; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + NbiDereferenceDevice (Device, DREF_FN_TIMER); + + return; + } + + // + // Check what is on the queue; this is set up as a + // loop but in fact it rarely does (under no + // circumstances can we send more than one packet + // each time this function executes). + // +#if defined(_PNP_POWER) + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // + + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // If this is the first retry, we need to initialize the packet + // + if ( Reserved->u.SR_FN.RetryCount == 1 ) { + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + + } + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx\n", Reserved)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + + break; + + } +#else + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + // + // Save this now, then change it if needed. + // + + BroadcastTarget.NicId = Reserved->u.SR_FN.CurrentNicId; + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + } + + + // + // Increment the current NIC ID for next time. + // + + if (Reserved->u.SR_FN.CurrentNicId >= Reserved->u.SR_FN.MaximumNicId) { + Reserved->u.SR_FN.CurrentNicId = 1; + } else { + ++Reserved->u.SR_FN.CurrentNicId; + } + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // If we are going to wrap around the maximum NIC ID + // after sending this packet we wait the full configured + // amount, otherwise we wait almost the minimum (but still + // insert ourselves at the back of the queue to be fair). + // + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + } else { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + 2); + } + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx to %d\n", Reserved, BroadcastTarget.NicId)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + if (NdisStatus == STATUS_DEVICE_DOES_NOT_EXIST) { + + // + // This send was done on a down wan line. To avoid + // extensive delays sending find names on systems + // with a lot of wan lines, we loop around immediately + // and do the next send. We put this packet back on + // the head of the list and change its SendTime so + // it will be resent immediately. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + NB_DEBUG2 (CACHE, ("FindNameTimeout resending %lx, wan send failed\n", Reserved)); + + RemoveEntryList (&Reserved->WaitLinkage); + InsertHeadList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + Reserved->u.SR_FN.SendTime = Device->FindNameTime; + + continue; + + } else { + + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } + + + break; + + } +#endif _PNP_POWER + + // + // Since we did something this time, we restart the timer. + // + + CTEStartTimer( + &Device->FindNameTimer, + FIND_NAME_GRANULARITY, + FindNameTimeout, + (PVOID)Device); + +} /* FindNameTimeout */ + + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams and connects + that were waiting for a route to be discovered to a + given Netbios NAME. THIS ROUTINE IS CALLED WITH + DEVICE->LOCK ACQUIRED AND RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + RemoteName - The netbios name that was being searched for. + + Result - Indicates if the name was found, or not found due + to no response or wan lines being down. + + CacheName - If Result is NetbiosNameFound, the cache entry for this name. + This entry has been referenced and this routine will deref it. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY ConnectList; + LIST_ENTRY AdapterStatusList; + LIST_ENTRY NetbiosFindNameList; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + PLIST_ENTRY p; + PREQUEST ConnectRequest, DatagramRequest, AdapterStatusRequest, NetbiosFindNameRequest; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + InitializeListHead (&DatagramList); + InitializeListHead (&ConnectList); + InitializeListHead (&AdapterStatusList); + InitializeListHead (&NetbiosFindNameList); + + // + // Put all connect requests on ConnectList. They will + // be continued or failed later. + // + + p = Device->WaitingConnects.Flink; + + while (p != &Device->WaitingConnects) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + p = p->Flink; + + if (RtlEqualMemory (Connection->RemoteName, RemoteName, 16)) { + + RemoveEntryList (REQUEST_LINKAGE(ConnectRequest)); + InsertTailList (&ConnectList, REQUEST_LINKAGE(ConnectRequest)); + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + } + + } + + + // + // Put all the datagrams on Datagram list. They will be + // sent or failed later. + // + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + // + // Check differently based on whether we were looking for + // the broadcast address or not. + // + + if (Reserved->u.SR_DG.RemoteName == (PVOID)-1) { + if (!RtlEqualMemory (RemoteName, NetbiosBroadcastName, 16)) { + continue; + } + } else { + + if (!RtlEqualMemory (RemoteName, Reserved->u.SR_DG.RemoteName->NetbiosName, 16)) { + continue; + } + } + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&DatagramList, &Reserved->WaitLinkage); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the adapter status requests on AdapterStatus + // list. They will be sent or failed later. + // + + p = Device->WaitingAdapterStatus.Flink; + + while (p != &Device->WaitingAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(AdapterStatusRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the netbios find name requests on NetbiosFindName + // list. They will be completed later. + // + + p = Device->WaitingNetbiosFindName.Flink; + + while (p != &Device->WaitingNetbiosFindName) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(NetbiosFindNameRequest)); + InsertTailList (&NetbiosFindNameList, REQUEST_LINKAGE(NetbiosFindNameRequest)); + + } + + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + // + // Now that the lock is free, process all the packets on + // the various lists. + // + + for (p = ConnectList.Flink; p != &ConnectList; ) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (CONNECTION, ("Found queued connect %lx on %lx\n", ConnectRequest, Connection)); + + // + // Continue with the connection sequence. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!ConnectRequest->Cancel) { + + IoSetCancelRoutine (ConnectRequest, NbiCancelConnectWaitResponse); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1 ); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelling connect %lx on %lx\n", ConnectRequest, Connection)); + + goto AbortConnect; + + // + // Jumps down into the else below. + // + + } + + } else { + BOOLEAN bAutodialAttempt = FALSE; + + NB_DEBUG2 (CONNECTION, ("Timing out connect %lx on %lx\n", ConnectRequest, Connection)); +AbortConnect: + + ASSERT (Connection->ConnectRequest == ConnectRequest); + +#ifdef RASAUTODIAL + if (fAcdLoadedG) { + CTELockHandle adirql; + BOOLEAN fEnabled; + + // + // See if the automatic connection driver knows + // about this address before we search the + // network. If it does, we return STATUS_PENDING, + // and we will come back here via NbfRetryTdiConnect(). + // + CTEGetLock(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbiAttemptAutoDial( + Device, + Connection, + 0, + NbiRetryTdiConnect, + ConnectRequest)) + { + NB_SYNC_FREE_LOCK(&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + bAutodialAttempt = TRUE; + } + } +#endif // RASAUTODIAL + + if (!bAutodialAttempt) { + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + IoSetCancelRoutine( ConnectRequest, (PDRIVER_CANCEL)NULL ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS(ConnectRequest) = STATUS_BAD_NETWORK_PATH; + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + } + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + } else { + + // BUGBUG What happens to the IRP? Who completes it? + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + + } + + + for (p = DatagramList.Flink; p != &DatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (DATAGRAM, ("Found queued datagram %lx on %lx\n", Reserved->u.SR_DG.DatagramRequest, Reserved->u.SR_DG.AddressFile)); + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + + // + // CacheName was referenced above. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER( Reserved->u.SR_DG.DatagramRequest )) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Reserved->u.SR_DG.DatagramRequest)); + } + + NbiTransmitDatagram (Reserved); + + } else { + + // + // BETABUGBUG: Should we send it once as a broadcast + // on net 0, just in case?? + // + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Timing out datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // If the failure was due to a down wan line indicate + // that, otherwise return success (so the browser won't + // confuse this with a down wan line). + // + + if (Result == NetbiosNameNotFoundWanDown) { + REQUEST_STATUS(DatagramRequest) = STATUS_DEVICE_DOES_NOT_EXIST; + REQUEST_INFORMATION(DatagramRequest) = 0; + } else { + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + } + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + } + + } + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (QUERY, ("Found queued AdapterStatus %lx\n", AdapterStatusRequest)); + + // + // Continue with the AdapterStatus sequence. We put + // it in ActiveAdapterStatus, it will either get + // completed when a response is received or timed + // out by the long timeout. + // + + REQUEST_STATUS(AdapterStatusRequest) = (NTSTATUS)CacheName; + + // + // CacheName was referenced above. + // + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + + NB_INSERT_TAIL_LIST( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (AdapterStatusRequest), + &Device->Lock); + + NbiSendStatusQuery (AdapterStatusRequest); + + } else { + + NB_DEBUG2 (QUERY, ("Timing out AdapterStatus %lx\n", AdapterStatusRequest)); + + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + } + + + for (p = NetbiosFindNameList.Flink; p != &NetbiosFindNameList; ) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // + // In fact there is not much difference between success or + // failure, since in the successful case the information + // will already have been written to the buffer. Just + // complete the request with the appropriate status, + // which will already be stored in the request. + // + + if (Result == NetbiosNameFound) { + + if (CacheName->Unique) { + + NB_DEBUG2 (QUERY, ("Found queued unique NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } else { + + NB_DEBUG2 (QUERY, ("Found queued group NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + } else { + + CTEAssert (REQUEST_STATUS(NetbiosFindNameRequest) == STATUS_IO_TIMEOUT); + NB_DEBUG2 (QUERY, ("Timed out NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + // + // This sets REQUEST_INFORMATION(Request) to the correct value. + // + + NbiSetNetbiosFindNameInformation (NetbiosFindNameRequest); + + NbiCompleteRequest(NetbiosFindNameRequest); + NbiFreeRequest (Device, NetbiosFindNameRequest); + + NbiDereferenceDevice (Device, DREF_NB_FIND_NAME); + + } + + + // + // We referenced this temporarily so we could use it in here, + // deref and check if we need to delete it. + // + + if (Result == NetbiosNameFound) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free newly allocated cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free in CacheHandlePending"); + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + + } + +} /* CacheHandlePending */ + + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_NAME_RECOGNIZED frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + PNETBIOS_CACHE NameCache; + PREQUEST NetbiosFindNameRequest; + PNB_SEND_RESERVED Reserved; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteNetbiosAddress; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + +#if 0 + // + // BETABUGBUG: We should handle responses from network 0 + // differently -- if they are for a group name, we should + // keep them around but only until we get a non-zero + // response from the same card. + // + + if (*(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork) == 0) { + return; + } +#endif + + + // + // We need to scan our queue of pending find name packets + // to see if someone is waiting for this name. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // Find names which have already found unique names are + // "dead", waiting for FindNameTimeout to remove them, + // and should be ignored when scanning the list. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + continue; + } + + if (RtlEqualMemory (Reserved->u.SR_FN.NetbiosName, Connectionless->NameFrame.Name, 16)) { + break; + } + } + + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Scan for any netbios find name requests on the queue, and + // inform them about this remote. We need to do this on every + // response because group names need every computer recorded, + // but the normal cache only includes one entry per network. + // + + for (p = Device->WaitingNetbiosFindName.Flink; + p != &Device->WaitingNetbiosFindName; + p = p->Flink) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + RemoteNetbiosAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + Connectionless->NameFrame.Name, + RemoteNetbiosAddress->NetbiosName, + 16)) { + continue; + } + + // + // This will update the request status if needed. + // + + NbiUpdateNetbiosFindName( + NetbiosFindNameRequest, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + (TDI_ADDRESS_IPX UNALIGNED *)Connectionless->IpxHeader.SourceNetwork, + (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0)); + + } + + + // + // See what is up with this pending find name packet. + // + + if (Reserved->u.SR_FN.NewCache == NULL) { + + // + // This is the first response we have received, so we + // allocate the initial entry with room for a single + // entry. + // + + NameCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (NameCache == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Alloc new cache %lx for <%.16s>, net %lx\n", + NameCache, Reserved->u.SR_FN.NetbiosName, + *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork))); + + RtlCopyMemory (NameCache->NetbiosName, Connectionless->NameFrame.Name, 16); + NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0); + NameCache->ReferenceCount = 1; + RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + NameCache->NetworksAllocated = 1; + NameCache->NetworksUsed = 1; + NameCache->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + + if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16)) { + + NB_SET_SR_FN_STATUS (Reserved, FNStatusResponseGroup); + NameCache->Unique = FALSE; + + } else { + + NB_SET_SR_FN_STATUS( + Reserved, + NameCache->Unique ? FNStatusResponseUnique : FNStatusResponseGroup); + + } + + Reserved->u.SR_FN.NewCache = NameCache; + + // + // If this packet was not routed to us and is for a group name, + // rather than use whatever local target it happened to come + // from we set it up so that it is broadcast on that net. + // + + if ((RtlEqualMemory (RemoteAddress->MacAddress, Connectionless->IpxHeader.SourceNode, 6)) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup)) { +#if defined(_PNP_POWER) + NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[0].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6); + RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[0].LocalTarget = *RemoteAddress; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // Complete pending requests now, since it is a unique + // name we have all the information we will get. + // + + NameCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + NameCache); + + // + // Reference it since CacheHandlePending uses it + // with the lock released. CacheHandlePending + // will dereference it. + // + + ++NameCache->ReferenceCount; + + // + // This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + NameCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // We already have a response to this frame. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // BUGBUG: Should we check that the response is also + // unique? Not much to do since I don't know of an + // equivalent to the netbeui NAME_IN_CONFLICT. + // + + } else { + + // + // This is a group name. + // + + if (Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) { + + // + // Update our information about this network if needed. + // This may free the existing cache and allocate a new one. + // + + Reserved->u.SR_FN.NewCache = + CacheUpdateNameCache( + Reserved->u.SR_FN.NewCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *) + Connectionless->IpxHeader.SourceNetwork, + FALSE); + + } else { + + // + // BUGBUG: This respondent thinks it is a unique name + // but we think it is group, should we do something? + // + + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessNameRecognized */ + + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ) + +/*++ + +Routine Description: + + This routine is called to update a netbios cache entry + with a new network, if it is does not already contain + information about the network. It is called when a frame + is received advertising the appropriate cache entry, which + is either a group name or the broadcast name. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH IT HELD. + +Arguments: + + NameCache - The name cache entry to update. + + RemoteAddress - The remote address on which a frame was received. + + IpxAddress - The source IPX address of the frame. + + ModifyQueue - TRUE if we should update the queue which this + cache entry is in, if we reallocate it. + +Return Value: + + The netbios cache entry, either the original or a reallocated one. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT NewNetworks; + PNETBIOS_CACHE NewNameCache; + PLIST_ENTRY OldPrevious; + UINT i; + + // + // See if we already know about this network. + // + + for (i = 0; i < NameCache->NetworksUsed; i++) { + if (NameCache->Networks[i].Network == SourceAddress->NetworkAddress) { + return NameCache; + } + } + + // + // We need to add information about this network + // to the name cache entry. If we have to allocate + // a new one we do that. + // + + NB_DEBUG2 (CACHE, ("Got new net %lx for <%.16s>\n", + SourceAddress->NetworkAddress, + NameCache->NetbiosName)); + + if (NameCache->NetworksUsed == NameCache->NetworksAllocated) { + + // + // We double the number of entries allocated until + // we hit 16, then add 8 at a time. + // + + if (NameCache->NetworksAllocated < 16) { + NewNetworks = NameCache->NetworksAllocated * 2; + } else { + NewNetworks = NameCache->NetworksAllocated + 8; + } + + + NewNameCache = NbiAllocateMemory( + sizeof(NETBIOS_CACHE) + ((NewNetworks-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge cache entry"); + + if (NewNameCache == NULL) { + return NameCache; + } + + NB_DEBUG2 (CACHE, ("Expand cache %lx to %lx for <%.16s>\n", + NameCache, NewNameCache, NameCache->NetbiosName)); + + // + // Copy the new current data to the new one. + // + + RtlCopyMemory( + NewNameCache, + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK))); + + NewNameCache->NetworksAllocated = NewNetworks; + NewNameCache->ReferenceCount = 1; + + if (ModifyQueue) { + + // + // Insert at the same place as the old one. The time + // stamp is the same as the old one. + // + + + ReinsertInNetbiosCacheTable( Device->NameCache, NameCache, NewNameCache ); + + } + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + NameCache = NewNameCache; + + } + + NameCache->Networks[NameCache->NetworksUsed].Network = + SourceAddress->NetworkAddress; + + // + // If this packet was not routed to us, then store the local + // target for a correct broadcast. + // + + if (RtlEqualMemory (RemoteAddress->MacAddress, SourceAddress->NodeAddress, 6)) { +#if defined(_PNP_POWER) + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[NameCache->NetworksUsed].LocalTarget.MacAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[NameCache->NetworksUsed].LocalTarget = *RemoteAddress; + } + + ++NameCache->NetworksUsed; + + return NameCache; + +} /* CacheUpdateNameCache */ + + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ) + +/*++ + +Routine Description: + + This routine is called when an add name frame is received. + If it is for a group name it checks if our cache entry for + that group name needs to be updated to include a new network; + for all frames it checks if our broadcast cache entry needs + to be updated to include a new network. + +Arguments: + + RemoteAddress - The address the frame was received from. + + Connectionless - The header of the received add name. + + LocalFrame - TRUE if the frame was sent locally. + +Return Value: + + None. + +--*/ + +{ + PUCHAR NetbiosName; + PNETBIOS_CACHE NameCache; + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + // + // First look up the broadcast name. + // + // BUGBUG: We should cache a pointer to the cache name + // for the broadcast entry, if there is one. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if (!LocalFrame) { + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosBroadcastName, + &NameCache ) == STATUS_SUCCESS ) { + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + } + + } + + + // + // Now see if our database needs to be updated based on this. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Connectionless->NameFrame.Name, + &NameCache ) == STATUS_SUCCESS ) { + + + if (!NameCache->Unique) { + + if (!LocalFrame) { + + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + + } + + } else { + + // + // To be safe, delete any unique names we get add + // names for (we will requery next time we need it). + // BUGBUG: Update the database instead -- but then + // we may not get the best route?? + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, NameCache ); + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free add named cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + +} /* CacheUpdateFromAddName */ + + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_DELETE_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PUCHAR NetbiosName; + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // We want to update our netbios cache to reflect the + // fact that this name is no longer valid. + // + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosName, + &CacheName ) == STATUS_SUCCESS ) { + + // + // We don't track group names since we don't know if + // this is the last person that owns it. We also drop + // the frame if does not come from the person we think + // owns this name. + // + + if ((!CacheName->Unique) || + (CacheName->NetworksUsed == 0) || + (!RtlEqualMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12))) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Found cache name to delete <%.16s>\n", NetbiosName)); + + }else { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // We have a cache entry, take it out of the list. If no + // one else is using it, delete it; if not, they will delete + // it when they are done. + // + + + RemoveFromNetbiosCacheTable ( Device->NameCache, CacheName); + + if (--CacheName->ReferenceCount == 0) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessDeleteName */ + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry in the hash table + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + USHORT HashIndex; + + // + // Keep a threshold of how many entries do we keep in the table. + // If it crosses the threshold, just remove the oldest entry + // + if ( CacheTable->CurrentEntries >= CacheTable->MaxHashIndex * NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET ) { + PNETBIOS_CACHE OldestCacheEntry = NULL; + PNETBIOS_CACHE NextEntry; + PLIST_ENTRY p; + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + if ( (p = CacheTable->Bucket[ HashIndex ].Blink ) != &CacheTable->Bucket[ HashIndex ] ) { + NextEntry = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + if ( OldestCacheEntry ) { + if ( NextEntry->TimeStamp < OldestCacheEntry->TimeStamp ) { + OldestCacheEntry = NextEntry; + } + } else { + OldestCacheEntry = NextEntry; + } + } + } + + CTEAssert( OldestCacheEntry ); + + NB_DEBUG2 (CACHE, ("Threshold exceeded, removing oldest cache entry %lx\n", OldestCacheEntry)); + RemoveEntryList (&OldestCacheEntry->Linkage); + CacheTable->CurrentEntries--; + + if (--OldestCacheEntry->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed cache entry %lx\n", OldestCacheEntry)); + + NbiFreeMemory( + OldestCacheEntry, + sizeof(NETBIOS_CACHE) + ((OldestCacheEntry->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } + HashIndex = ( ( CacheEntry->NetbiosName[0] & 0x0f ) << 4 ) + ( CacheEntry->NetbiosName[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + InsertHeadList( &CacheTable->Bucket[HashIndex], &CacheEntry->Linkage ); + CacheTable->CurrentEntries++; +} /* InsertInNetbiosCacheTable */ + + +__inline +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry at the same place where + the old entry was. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY OldPrevious; + + OldPrevious = OldEntry->Linkage.Blink; + RemoveEntryList (&OldEntry->Linkage); + InsertHeadList (OldPrevious, &NewEntry->Linkage); +} /* ReinsertInNetbiosCacheTable */ + +__inline +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine removes an entry from the cache table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + RemoveEntryList( &CacheEntry->Linkage ); + CacheTable->CurrentEntries--; +} /* RemoveFromNetbiosCacheTable */ + + + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ) + +/*++ + +Routine Description: + + This routine removes all the old entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + AgeLimit - All the entries older than AgeLimit will be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // see if any entries have been around for more than agelimit + // + + if ((USHORT)(NbiDevice->CacheTimeStamp - CacheName->TimeStamp) >= AgeLimit ) { + + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Aging out name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } else { + + break; + + } + } // for loop + } // for loop +} /* FlushOldFromNetbiosCacheTable */ + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all the failed entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // flush all the failed cache entries. + // We do this when a new adapter appears, and there's a possiblity that + // the failed entries might succeed now on the new adapter. + // + + if (CacheName->NetworksUsed == 0) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + CTEAssert( CacheName->ReferenceCount == 1 ); + CTEAssert( CacheName->NetworksAllocated == 1 ); + + NB_DEBUG2 (CACHE, ("Flushing out failed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE), + MEMORY_CACHE, + "Aged out"); + + } + } // for loop + } // for loop +} /* FlushFailedNetbiosCacheEntries */ + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ) + +/*++ + +Routine Description: + + This routine removes all invalid route entries from the hash table. + Routes become invalid when the binding is deleted in Ipx due to PnP + event. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + InvalidRouteNicId - NicId of the invalid routes. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + USHORT HashIndex; + PDEVICE Device = NbiDevice; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + + for ( HashIndex = 0; HashIndex < Device->NameCache->MaxHashIndex; HashIndex++) { + for (p = Device->NameCache->Bucket[ HashIndex ].Flink; + p != &Device->NameCache->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Flink; + + + // + // Remove each of those routes which is using this NicId. + // if no routes left, then flush the cache entry also. + // ( unique names have only one route anyways ) + // + for ( i = 0, NetworksRemoved = 0; i < CacheName->NetworksUsed; i++ ) { + if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId == InvalidNicHandle->NicId ) { + CTEAssert( RtlEqualMemory( &CacheName->Networks[i].LocalTarget.NicHandle, InvalidNicHandle, sizeof(NIC_HANDLE))); + for ( j = i+1; j < CacheName->NetworksUsed; j++ ) { + CacheName->Networks[j-1] = CacheName->Networks[j]; + } + NetworksRemoved++; + } else if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId > InvalidNicHandle->NicId ) { + CacheName->Networks[i].LocalTarget.NicHandle.NicId--; + } + } + CTEAssert( NetworksRemoved <= CacheName->NetworksUsed ); + if ( ! ( CacheName->NetworksUsed -= NetworksRemoved ) ) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + NB_DEBUG2 (CACHE, ("Removed cache entry %lx bcoz route(NicId %d) deleted\n", CacheName, InvalidNicHandle->NicId )); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + } + } // for loop + } // for loop +} /* RemoveInvalidRoutesFromNetbiosCacheTable */ + + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ) + +/*++ + +Routine Description: + + This routine finds a netbios name in the Hash Table and returns + the corresponding cache entry. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Pointer to the netbios cache entry if found. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_UNSUCCESSFUL - otherwise. + +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE FoundCacheName; + + + HashIndex = ( ( NameToBeFound[0] & 0x0f ) << 4 ) + ( NameToBeFound[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + for (p = ( CacheTable->Bucket[ HashIndex ] ).Flink; + p != &CacheTable->Bucket[ HashIndex ]; + p = p->Flink) { + + FoundCacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + // + // See if this entry is for the same name we are looking for. + + if ( RtlEqualMemory (FoundCacheName->NetbiosName, NameToBeFound, 16) ) { + *CacheEntry = FoundCacheName; + return STATUS_SUCCESS; + } + } + + return STATUS_UNSUCCESSFUL; +} /* FindInNetbiosCacheTable */ + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ) + +/*++ + +Routine Description: + + This routine creates a new hash table for netbios cache + and initializes it. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + NewTable - The pointer of the table to be created. + + MaxHashIndex - Number of buckets in the hash table. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_INSUFFICIENT_RESOURCES - If cannot allocate memory. + +--*/ + +{ + USHORT i; + + *NewTable = NbiAllocateMemory (sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( MaxHashIndex - 1) , + MEMORY_CACHE, "Cache Table"); + + if ( *NewTable ) { + for ( i = 0; i < MaxHashIndex; i++ ) { + InitializeListHead(& (*NewTable)->Bucket[i] ); + } + + (*NewTable)->MaxHashIndex = MaxHashIndex; + (*NewTable)->CurrentEntries = 0; + return STATUS_SUCCESS; + } + else { + NB_DEBUG( CACHE, ("Cannot create Netbios Cache Table\n") ); + return STATUS_INSUFFICIENT_RESOURCES; + } + +} /* CreateNetbiosCacheTable */ + + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all entries from the hash table. + and free up the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + while (!IsListEmpty ( &( CacheTable->Bucket[ HashIndex ] ) ) ) { + + p = RemoveHeadList ( &( CacheTable->Bucket[ HashIndex ] )); + CacheTable->CurrentEntries--; + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + NB_DEBUG2 (CACHE, ("Free cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free entries"); + + } + } // for loop + + CTEAssert( CacheTable->CurrentEntries == 0 ); + + NbiFreeMemory (CacheTable, sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( CacheTable->MaxHashIndex - 1) , + MEMORY_CACHE, "Free Cache Table"); + +} /* DestroyNetbiosCacheTable */ + + diff --git a/private/ntos/tdi/isnp/nb/config.c b/private/ntos/tdi/isnp/nb/config.c new file mode 100644 index 000000000..8689c5d20 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/config.c @@ -0,0 +1,661 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN Netbios module. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Local functions used to access the registry. +// + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiGetConfiguration) +#pragma alloc_text(INIT,NbiFreeConfiguration) +#pragma alloc_text(INIT,NbiGetConfigValue) +#pragma alloc_text(INIT,NbiAddBind) +#pragma alloc_text(INIT,NbiAddExport) +#pragma alloc_text(INIT,NbiReadLinkageInformation) +#endif + + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + DriverObject - Used for logging errors. + + RegistryPath - The name of Netbios' node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + NTSTATUS Status; + ULONG One = 1; + ULONG Two = 2; + ULONG Three = 3; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG FortyEight = 48; + ULONG Sixty = 60; + ULONG TwoFifty = 250; + ULONG FiveHundred = 500; + ULONG MaxMTU = 0xffffffff; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"AckDelayTime", &TwoFifty } , // milliseconds + { L"AckWindow", &Two } , + { L"AckWindowThreshold", &FiveHundred } , // milliseconds + { L"EnablePiggyBackAck", &One } , + { L"Extensions", &One } , + { L"RcvWindowMax", &Four } , + { L"BroadcastCount", &Three } , + { L"BroadcastTimeout", &One } , // half-seconds + { L"ConnectionCount", &Five } , + { L"ConnectionTimeout", &Two } , // half-seconds + { L"InitPackets", &Eight } , + { L"MaxPackets", &FortyEight } , + { L"InitialRetransmissionTime", &FiveHundred } , // milliseconds + { L"Internet", &One } , + { L"KeepAliveCount", &Eight } , + { L"KeepAliveTimeout", &Sixty } , // half-seconds + { L"RetransmitMax", &Eight } , + { L"RouterMTU", &MaxMTU } }; + UINT i; + + + // + // Allocate memory for the main config structure. + // + + Config = NbiAllocateMemory (sizeof(CONFIG), MEMORY_CONFIG, "Config"); + if (Config == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, sizeof(CONFIG), MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->DeviceName.Buffer = NULL; + Config->BindName.Buffer = NULL; + Config->DriverObject = DriverObject; // save this to log errors + + // + // Read in the NDIS binding information (if none is present + // the array will be filled with all known drivers). + // + // NbiReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)NbiAllocateMemory(RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG, "RegistryPathBuffer"); + if (RegistryPathBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG); + NbiFreeConfiguration(Config); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->RegistryPathBuffer = RegistryPathBuffer; + + + // + // Determine what name to export and who to bind to. + // + + Status = NbiReadLinkageInformation (Config); + + if (Status != STATUS_SUCCESS) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + return Status; + } + + + // + // Read the per-transport (as opposed to per-binding) + // parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2-18) Call NbiSetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = NbiGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + + } + + // + // 19) Stop + // + + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + NbiFreeConfiguration(Config); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 701, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + NbiFreeMemory (RegistryPathBuffer, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG, "RegistryPathBuffer"); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} /* NbiGetConfiguration */ + + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get free any storage that was allocated + by NbiGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + if (Config->BindName.Buffer) { + NbiFreeMemory (Config->BindName.Buffer, Config->BindName.MaximumLength, MEMORY_CONFIG, "BindName"); + } + + if (Config->DeviceName.Buffer) { + NbiFreeMemory (Config->DeviceName.Buffer, Config->DeviceName.MaximumLength, MEMORY_CONFIG, "DeviceName"); + } + + NbiFreeMemory (Config, sizeof(CONFIG), MEMORY_CONFIG, "Config"); + +} /* NbiFreeConfig */ + + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + ULONG Data = *(UNALIGNED ULONG *)ValueData; + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + + switch ( (ULONG) EntryContext ) { + case CONFIG_ROUTER_MTU: + if ( ( Data - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER) ) <= 0 ) { + Config->Parameters[CONFIG_ROUTER_MTU] = 0xffffffff; + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 704, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_SUCCESS; + } + break; + default: + break; + } + + NB_DEBUG2 (CONFIG, ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, Data)); + Config->Parameters[(ULONG)EntryContext] = Data; + + return STATUS_SUCCESS; + +} /* NbiGetConfigValue */ + + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a Config structure. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read bind value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "BindName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->BindName.Buffer = NameBuffer; + Config->BindName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->BindName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddBind */ + + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string. It + saves the first callback string in the Config structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a ULONG that goes to 1 after the + first call to this routine (so we know to ignore other ones). + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read export value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "DeviceName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->DeviceName.Buffer = NameBuffer; + Config->DeviceName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->DeviceName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddExport */ + + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to read its linkage information + from the registry. + +Arguments: + + Config - The config structure which will have per-binding information + linked on to it. + +Return Value: + + The status of the operation. + +--*/ + +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[3]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG ValueReadOk; // set to TRUE when a value is read correctly + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 1) Call NbiAddExport for each string in "Export" + // + + QueryTable[1].QueryRoutine = NbiAddExport; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = Export; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[2].QueryRoutine = NULL; + QueryTable[2].Flags = 0; + QueryTable[2].Name = NULL; + + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 702, + Status, + Export, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + + // + // 1) Change to call NbiAddBind for each string in "Bind" + // + + QueryTable[1].QueryRoutine = NbiAddBind; + QueryTable[1].Flags = 0; // not required + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 703, + Status, + Bind, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; + +} /* NbiReadLinkageInformation */ + diff --git a/private/ntos/tdi/isnp/nb/config.h b/private/ntos/tdi/isnp/nb/config.h new file mode 100644 index 000000000..99b6c6357 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/config.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.h + +Abstract: + + Private include file for the ISN Netbios module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + + +// +// These are used to index into the Parameters array in CONFIG. +// + +#define CONFIG_ACK_DELAY_TIME 0 +#define CONFIG_ACK_WINDOW 1 +#define CONFIG_ACK_WINDOW_THRESHOLD 2 +#define CONFIG_ENABLE_PIGGYBACK_ACK 3 +#define CONFIG_EXTENSIONS 4 +#define CONFIG_RCV_WINDOW_MAX 5 +#define CONFIG_BROADCAST_COUNT 6 +#define CONFIG_BROADCAST_TIMEOUT 7 +#define CONFIG_CONNECTION_COUNT 8 +#define CONFIG_CONNECTION_TIMEOUT 9 +#define CONFIG_INIT_PACKETS 10 +#define CONFIG_MAX_PACKETS 11 +#define CONFIG_INIT_RETRANSMIT_TIME 12 +#define CONFIG_INTERNET 13 +#define CONFIG_KEEP_ALIVE_COUNT 14 +#define CONFIG_KEEP_ALIVE_TIMEOUT 15 +#define CONFIG_RETRANSMIT_MAX 16 +#define CONFIG_ROUTER_MTU 17 +#define CONFIG_PARAMETERS 18 + +// +// Main configuration structure. +// + +typedef struct _CONFIG { + + ULONG Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING DeviceName; // device name exported + NDIS_STRING BindName; // device to bind to + PWSTR RegistryPathBuffer; // path to config info + PDRIVER_OBJECT DriverObject; // used for logging errors + +} CONFIG, * PCONFIG; + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ); + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ); + diff --git a/private/ntos/tdi/isnp/nb/connect.c b/private/ntos/tdi/isnp/nb/connect.c new file mode 100644 index 000000000..7ee7204b5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/connect.c @@ -0,0 +1,3628 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This routine contains the code to handle connect requests + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ); +#endif // RASAUTODIAL + + + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +/*++ + +Routine Description: + + This routine is called when a find route request + previously issued to IPX completes. + +Arguments: + + FindRouteRequest - The find route request that was issued. + + FoundRoute - TRUE if the route was found. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + UINT i; + BOOLEAN LocalRoute; + USHORT TickCount; + PREQUEST RequestToComplete; + PUSHORT Counts; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + Connection = CONTAINING_RECORD (FindRouteRequest, CONNECTION, FindRouteRequest); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection->FindRouteInProgress = FALSE; + + if (FoundRoute) { + + // + // See if the route is local or not (for local routes + // we use the real MAC address in the local target, but + // the NIC ID may not be what we expect. + // + + LocalRoute = TRUE; + + for (i = 0; i < 6; i++) { + if (FindRouteRequest->LocalTarget.MacAddress[i] != 0x00) { + LocalRoute = FALSE; + } + } + + if (LocalRoute) { + +#if defined(_PNP_POWER) + Connection->LocalTarget.NicHandle = FindRouteRequest->LocalTarget.NicHandle; +#else + Connection->LocalTarget.NicId = FindRouteRequest->LocalTarget.NicId; +#endif _PNP_POWER + + } else { + + Connection->LocalTarget = FindRouteRequest->LocalTarget; + + } + + Counts = (PUSHORT)(&FindRouteRequest->Reserved2); + TickCount = Counts[0]; + + if (TickCount > 1) { + + // + // Each tick is 55 ms, and for our timeout we use 10 ticks + // worth (this makes tick count of 1 be about 500 ms, the + // default). + // + // We get 55 milliseconds from + // + // 1 second * 1000 milliseconds 55 ms + // -------- ----------------- = ----- + // 18.21 ticks 1 second tick + // + + Connection->TickCount = TickCount; + Connection->BaseRetransmitTimeout = (TickCount * 550) / SHORT_TIMER_DELTA; + if (Connection->State != CONNECTION_STATE_ACTIVE) { + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + } + + Connection->HopCount = Counts[1]; + + } + + // + // If the call failed we just use whatever route we had before + // (on a connect it will be from the name query response, on + // a listen from whatever the incoming connect frame had). + // + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_W_ROUTE)) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // Continue on with the session init frame. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + // Maximum packet size is the lower of RouterMtu and MaximumSendSize. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER) , Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION) ; + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + // + // Don't set RcvSequenceMax yet because we don't know + // if the connection is old or new netbios. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + + // + // We found a route, we need to start the connect + // process by sending out the session initialize + // frame. We start the timer to handle retries. + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiSendSessionInitialize (Connection); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ROUTE)) { + + if (Connection->ListenRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_ACTIVE); + RequestToComplete = Connection->ListenRequest; + Connection->ListenRequest = NULL; + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + + } else if (Connection->AcceptRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_ACCEPT, CREF_ACTIVE); + RequestToComplete = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } else { + + CTEAssert (FALSE); + RequestToComplete = NULL; + + } + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + + // Take the lowest of MaximumPacketSize ( set from the sessionInit + // frame ), MaximumSendSize and RouterMtu. + + if (Connection->MaximumPacketSize > Connection->LineInfo.MaximumSendSize - sizeof(NB_CONNECTION)) { + + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER), Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION); + + } else { + + // Connection->MaximumPacketSize is what was set by the sender so already + // accounts for the header. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER), Connection->MaximumPacketSize ) ; + + } + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + if (Connection->NewNetbios) { + CTEAssert (Connection->LocalRcvSequenceMax == 4); // should have been set + Connection->LocalRcvSequenceMax = Connection->ReceiveWindowSize; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + ++Device->Statistics.OpenConnections; + + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // StartWatchdog acquires TimerLock, so we have to + // free Lock first. + // + + + NbiStartWatchdog (Connection); + + // + // This releases the connection lock, so that SessionInitAckData + // can't be freed before it is copied. + // + + NbiSendSessionInitAck( + Connection, + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + &LockHandle1); + + if (RequestToComplete != NULL) { + + REQUEST_STATUS(RequestToComplete) = STATUS_SUCCESS; + + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_FIND_ROUTE); + +} /* NbiFindRouteComplete */ + + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PCONNECTION Connection; + PFILE_FULL_EA_INFORMATION ea; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful NbfCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + if (!(Connection = NbiCreateConnection (Device))) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &Connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)Connection; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONNECTION_FILE; +#ifdef ISN_NT + Connection->FileObject = IrpSp->FileObject; +#endif + + return STATUS_SUCCESS; + +} /* NbiOpenConnection */ + + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called to stop an active connection. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection to be stopped. + + DisconnectStatus - The reason for the disconnect. One of: + STATUS_LINK_FAILED: We timed out trying to probe the remote. + STATUS_REMOTE_DISCONNECT: The remote sent a session end. + STATUS_LOCAL_DISCONNECT: The local side disconnected. + STATUS_CANCELLED: A send or receive on this connection was cancelled. + STATUS_INVALID_CONNECTION: The local side closed the connection. + STATUS_INVALID_ADDRESS: The local side closed the address. + + LockHandle - The handle which the connection lock was acquired with. + +Return Value: + + None. + +--*/ + +{ + PREQUEST ListenRequest, AcceptRequest, SendRequest, ReceiveRequest, + DisconnectWaitRequest, ConnectRequest; + PREQUEST Request, TmpRequest; + BOOLEAN DerefForPacketize; + BOOLEAN DerefForWaitPacket; + BOOLEAN DerefForActive; + BOOLEAN DerefForWaitCache; + BOOLEAN SendSessionEnd; + BOOLEAN ActiveReceive; + BOOLEAN IndicateToClient; + BOOLEAN ConnectionWasActive; + PDEVICE Device = NbiDevice; + PADDRESS_FILE AddressFile; + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + + NB_DEBUG2 (CONNECTION, ("Stop connection %lx (%lx)\n", Connection, DisconnectStatus)); + + // + // These flags control our actions after we set the state to + // DISCONNECT. + // + + DerefForPacketize = FALSE; + DerefForWaitPacket = FALSE; + DerefForActive = FALSE; + DerefForWaitCache = FALSE; + SendSessionEnd = FALSE; + ActiveReceive = FALSE; + IndicateToClient = FALSE; + ConnectionWasActive = FALSE; + + // + // These contain requests or queues of request to complete. + // + + ListenRequest = NULL; + AcceptRequest = NULL; + SendRequest = NULL; + ReceiveRequest = NULL; + DisconnectWaitRequest = NULL; + ConnectRequest = NULL; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + --Device->Statistics.OpenConnections; + + ConnectionWasActive = TRUE; + + Connection->Status = DisconnectStatus; + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_LOCAL_DISCONNECT)) { + + // + // Send out session end frames, but fewer if + // we timed out. + // + // BUGBUG: What about STATUS_CANCELLED? + // + + Connection->Retries = (DisconnectStatus == STATUS_LOCAL_DISCONNECT) ? + Device->ConnectionCount : + (Device->ConnectionCount / 2); + + SendSessionEnd = TRUE; + Connection->SubState = CONNECTION_SUBSTATE_D_W_ACK; + + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + if (Connection->ReceiveState == CONNECTION_RECEIVE_TRANSFER) { + ActiveReceive = TRUE; + } + + Connection->State = CONNECTION_STATE_DISCONNECT; + DerefForActive = TRUE; + + if (Connection->DisconnectWaitRequest != NULL) { + DisconnectWaitRequest = Connection->DisconnectWaitRequest; + Connection->DisconnectWaitRequest = NULL; + } + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_REMOTE_DISCONNECT) || + (DisconnectStatus == STATUS_CANCELLED)) { + + IndicateToClient = TRUE; + + } + + // + // If we are inside NbiAssignSequenceAndSend, add + // a reference so the connection won't go away during it. + // + + if (Connection->NdisSendsInProgress > 0) { + *(Connection->NdisSendReference) = TRUE; + NB_DEBUG2 (SEND, ("Adding CREF_NDIS_SEND to %lx\n", Connection)); + NbiReferenceConnectionLock (Connection, CREF_NDIS_SEND); + } + + // + // Clean up some other stuff. + // + + Connection->ReceiveUnaccepted = 0; + Connection->CurrentIndicateOffset = 0; + + // + // Update our counters. BUGBUG: Some of these we + // never use. + // + + switch (DisconnectStatus) { + + case STATUS_LOCAL_DISCONNECT: + ++Device->Statistics.LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + ++Device->Statistics.RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + ++Device->Statistics.LinkFailures; + break; + case STATUS_IO_TIMEOUT: + ++Device->Statistics.SessionTimeouts; + break; + case STATUS_CANCELLED: + ++Device->Statistics.CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + ++Device->Statistics.RemoteResourceFailures; + break; + case STATUS_INVALID_CONNECTION: + case STATUS_INVALID_ADDRESS: + case STATUS_INSUFFICIENT_RESOURCES: + ++Device->Statistics.LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + case STATUS_REMOTE_NOT_LISTENING: + ++Device->Statistics.NotFoundFailures; + break; + default: + CTEAssert(FALSE); + break; + } + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // + // There is a connect in progress. We have to find ourselves + // in the pending connect queue if we are there. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) { + RemoveEntryList (REQUEST_LINKAGE(Connection->ConnectRequest)); + DerefForWaitCache = TRUE; + } + + if (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) { + + ConnectRequest = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + } + + } + + + // + // If we allocated this memory, free it. + // + + if (Connection->SessionInitAckDataLength > 0) { + + NbiFreeMemory( + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + MEMORY_CONNECTION, + "SessionInitAckData"); + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + + } + + + if (Connection->ListenRequest != NULL) { + + ListenRequest = Connection->ListenRequest; + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(ListenRequest)); // take out of Device->ListenQueue + + } + + if (Connection->AcceptRequest != NULL) { + + AcceptRequest = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } + + + // + // BUGBUG: Do we need to stop the connection timer? + // I don't think so. + // + + + + // + // A lot of this we only have to tear down if we were + // active before this, because once we are stopping nothing + // new will get started. BUGBUG: Some of the other stuff + // can be put inside this if also. + // + + if (ConnectionWasActive) { + + // + // Stop any receives. If there is one that is actively + // transferring we leave it and just run down the rest + // of the queue. If not, we queue the rest of the + // queue on the back of the current one and run + // down them all. + // + + if (ActiveReceive) { + + ReceiveRequest = Connection->ReceiveQueue.Head; + + // + // Connection->ReceiveRequest will get set to NULL + // when the transfer completes. + // + + } else { + + ReceiveRequest = Connection->ReceiveRequest; + if (ReceiveRequest) { + REQUEST_SINGLE_LINKAGE (ReceiveRequest) = Connection->ReceiveQueue.Head; + } else { + ReceiveRequest = Connection->ReceiveQueue.Head; + } + Connection->ReceiveRequest = NULL; + + } + + Connection->ReceiveQueue.Head = NULL; + + + if ((Request = Connection->FirstMessageRequest) != NULL) { + + // + // If the current request has some sends outstanding, then + // we dequeue it from the queue to let it complete when + // the sends complete. In that case we set SendRequest + // to be the rest of the queue, which will be aborted. + // If the current request has no sends, then we put + // queue everything to SendRequest to be aborted below. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + // + // NOTE: If this is a multi-request message, then + // the linkage of Request will already point to the + // send queue head, but we don't bother checking. + // + + SendRequest = Request; + REQUEST_SINGLE_LINKAGE (Request) = Connection->SendQueue.Head; + + } else { + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->FirstMessageRequest = NULL; + + } else { + + // + // This may happen if we were sending a probe when a + // send was submitted, and the probe timed out. + // + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->SendQueue.Head = NULL; + + } + + + if (Connection->OnWaitPacketQueue) { + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + DerefForWaitPacket = TRUE; + } + + if (Connection->OnPacketizeQueue) { + Connection->OnPacketizeQueue = FALSE; + RemoveEntryList (&Connection->PacketizeLinkage); + DerefForPacketize = TRUE; + } + + // + // BUGBUG: Should we check if DataAckPending is TRUE and + // send an ack?? + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // We can't acquire TimerLock with Lock held, since + // we sometimes call ReferenceConnection (which does an + // interlocked add using Lock) with TimerLock held. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle3); + + if (Connection->OnShortList) { + Connection->OnShortList = FALSE; + RemoveEntryList (&Connection->ShortList); + } + + if (Connection->OnLongList) { + Connection->OnLongList = FALSE; + RemoveEntryList (&Connection->LongList); + } + + if (Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = FALSE; + RemoveEntryList (&Connection->DataAckLinkage); + Device->DataAckQueueChanged = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle3); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + + if (IndicateToClient) { + + AddressFile = Connection->AddressFile; + + if (AddressFile->RegisteredHandler[TDI_EVENT_DISCONNECT]) { + + NB_DEBUG2 (CONNECTION, ("Session end indicated on connection %lx\n", Connection)); + + (*AddressFile->DisconnectHandler)( + AddressFile->HandlerContexts[TDI_EVENT_DISCONNECT], + Connection->Context, + 0, // DisconnectData + NULL, + 0, // DisconnectInformation + NULL, + TDI_DISCONNECT_RELEASE); // DisconnectReason. BUGBUG: Clean it up? + + } + + } + + + if (DisconnectWaitRequest != NULL) { + + // + // Make the TDI tester happy by returning CONNECTION_RESET + // here. + // + + if (DisconnectStatus == STATUS_REMOTE_DISCONNECT) { + REQUEST_STATUS(DisconnectWaitRequest) = STATUS_CONNECTION_RESET; + } else { + REQUEST_STATUS(DisconnectWaitRequest) = DisconnectStatus; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (DisconnectWaitRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (DisconnectWaitRequest); + NbiFreeRequest (Device, DisconnectWaitRequest); + + } + + if (ConnectRequest != NULL) { + + REQUEST_STATUS (ConnectRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ConnectRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION(ListenRequest) = 0; + REQUEST_STATUS(ListenRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest(Device, ListenRequest); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } + + if (AcceptRequest != NULL) { + + REQUEST_INFORMATION(AcceptRequest) = 0; + REQUEST_STATUS(AcceptRequest) = STATUS_LOCAL_DISCONNECT; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest(Device, AcceptRequest); + + NbiDereferenceConnection (Connection, CREF_ACCEPT); + + } + + while (ReceiveRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (ReceiveRequest); + + REQUEST_STATUS (ReceiveRequest) = DisconnectStatus; + REQUEST_INFORMATION (ReceiveRequest) = 0; + + NB_DEBUG2 (RECEIVE, ("StopConnection aborting receive %lx\n", ReceiveRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ReceiveRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ReceiveRequest); + NbiFreeRequest (Device, ReceiveRequest); + + ++Connection->ConnectionInfo.ReceiveErrors; + + ReceiveRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } + + while (SendRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + + REQUEST_STATUS (SendRequest) = DisconnectStatus; + REQUEST_INFORMATION (SendRequest) = 0; + + NB_DEBUG2 (SEND, ("StopConnection aborting send %lx\n", SendRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + + ++Connection->ConnectionInfo.TransmissionErrors; + + SendRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + if (SendSessionEnd) { + NbiSendSessionEnd (Connection); + } + + if (DerefForWaitCache) { + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + } + + if (DerefForPacketize) { + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + } + + if (DerefForWaitPacket) { + NbiDereferenceConnection (Connection, CREF_W_PACKET); + } + + if (DerefForActive) { + NbiDereferenceConnection (Connection, CREF_ACTIVE); + } + +} /* NbiStopConnection */ + + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (CONNECTION, ("Close connection %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (Connection->ReferenceCount == 0) { + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // Even if the ref count is zero and some thread has already done cleanup, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + if ( Connection->CanBeDestroyed && ( Connection->ThreadsInHandleConnectionZero == 0 ) ) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NbiDestroyConnection(Connection); + Status = STATUS_SUCCESS; + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + return Status; + +} /* NbiCloseConnection */ + + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and + the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; +#ifdef ISN_NT + PFILE_OBJECT FileObject; +#endif + PADDRESS_FILE AddressFile; + PADDRESS Address; + PTDI_REQUEST_KERNEL_ASSOCIATE Parameters; + CTELockHandle LockHandle; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + + // + // The request request parameters hold + // get a pointer to the address FileObject, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + Parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)REQUEST_PARAMETERS(Request); + +#ifdef ISN_NT + + Status = ObReferenceObjectByHandle ( + Parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *)&FileObject, + NULL); + + if (!NT_SUCCESS(Status)) { + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + AddressFile = (PADDRESS_FILE)(FileObject->FsContext); + +#else + + // + // I don't know how this works in a VxD. + // + + AddressFile = (PADDRESS_FILE)(Parameters->AddressHandle); + +#endif + + // + // Make sure the address file is valid, and reference it. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + +#ifdef ISN_NT + ObDereferenceObject (FileObject); +#endif + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Associate connection %lx with address file %lx\n", + Connection, AddressFile)); + + + // + // Now insert the connection into the database of the address. + // + + Address = AddressFile->Address; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFile != NULL) { + + // + // The connection is already associated with + // an address file. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_CONNECTION; + + } else { + + if (AddressFile->State == ADDRESSFILE_STATE_OPEN) { + + Connection->AddressFile = AddressFile; + Connection->AddressFileLinked = TRUE; + InsertHeadList (&AddressFile->ConnectionDatabase, &Connection->AddressFileLinkage); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_ADDRESS; + } + + } + +#ifdef ISN_NT + + // + // We don't need the reference to the file object, we just + // used it to get from the handle to the object. + // + + ObDereferenceObject (FileObject); + +#endif + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAssociateAddress */ + + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the disassociation of the connection + and the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Disassociate connection %lx\n", Connection)); + + + // + // First check if the connection is still active. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if (Connection->State != CONNECTION_STATE_INACTIVE) { + + // + // This releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + // + // BUGBUG: Keep the sync through the function?? + // + + NB_END_SYNC (&SyncContext); + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Make sure the connection is associated and is not in the + // middle of disassociating. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL)) { + + if (Connection->ReferenceCount == 0) { + + // + // Because the connection still has a reference to + // the address file, we know it is still valid. We + // set the connection address file to the temporary + // value of -1, which prevents somebody else from + // disassociating it and also prevents a new association. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + // + // Set this so when the count goes to 0 it will + // be disassociated and the request completed. + // + + Connection->DisassociatePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisassociateAddress */ + + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine posts a listen on a connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the listen. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_WAITING; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + + if (!Request->Cancel) { + + NB_DEBUG2 (CONNECTION, ("Queued listen %lx on %lx\n", Request, Connection)); + InsertTailList (&Device->ListenQueue, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelListen); + Connection->ListenRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_LISTEN); + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled listen %lx on %lx\n", Request, Connection)); + Connection->State = CONNECTION_STATE_INACTIVE; + Status = STATUS_CANCELLED; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiListen */ + + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine accepts a connection to a remote machine. The + connection must previously have completed a listen with + the TDI_QUERY_ACCEPT flag on. + +Arguments: + + Device - The netbios device. + + Request - The request describing the accept. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiTransferReferenceConnection (Connection, CREF_W_ACCEPT, CREF_ACCEPT); + Connection->AcceptRequest = Request; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->Retries = NbiDevice->KeepAliveCount; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + NB_DEBUG2 (CONNECTION, ("Accept received on %lx\n", Connection)); + + Status = STATUS_PENDING; + + } else { + + NB_DEBUG (CONNECTION, ("Accept received on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAccept */ + + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_CONNECT Parameters; +#if 0 + PLARGE_INTEGER RequestedTimeout; + LARGE_INTEGER RealTimeout; +#endif + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Parameters = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->RequestConnectionInformation->RemoteAddress), FALSE); + + if (RemoteName == NULL) { + + // + // There is no netbios remote address specified. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_BAD_NETWORK_PATH; + + } else { + + NbiReferenceConnectionLock (Connection, CREF_CONNECT); + Connection->State = CONNECTION_STATE_CONNECTING; + RtlCopyMemory (Connection->RemoteName, RemoteName->NetbiosName, 16); + + Connection->Retries = Device->ConnectionCount; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + Status = NbiTdiConnectFindName( + Device, + Request, + Connection, + CancelLH, + LockHandle1, + LockHandle2, + &bLockFreed); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Connect on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + if (!bLockFreed) { + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiConnect */ + + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ) +{ + NTSTATUS Status; + PNETBIOS_CACHE CacheName; + + // + // See what is up with this Netbios name. + // + + Status = CacheFindName( + Device, + FindNameConnect, + Connection->RemoteName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this connect + // request and processing will be resumed when + // we get a response. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_FIND_NAME; + + + if (!Request->Cancel) { + + InsertTailList( &Device->WaitingConnects, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelConnectFindName); + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_WAIT_CACHE); + NB_DEBUG2 (CONNECTION, ("Queueing up connect %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // We don't need to worry about referencing CacheName + // because we stop using it before we release the lock. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelConnectWaitResponse); + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, ConnectionLH); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_DEBUG2 (CONNECTION, ("Found connect cached %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NB_FREE_LOCK (&Connection->Lock, ConnectionLH); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + Status = STATUS_PENDING; + + // + // This jump is like falling out of the if, except + // it skips over freeing the connection lock since + // we just did that. + // + + *pbLockFreed = TRUE; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else { + + // + // We could not find or queue a request for + // this remote, fail it. When the refcount + // drops the state will go to INACTIVE and + // the connection ID will be deassigned. + // + + if (Status == STATUS_DEVICE_DOES_NOT_EXIST) { + Status = STATUS_BAD_NETWORK_PATH; + } + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + } + + return Status; +} /* NbiTdiConnectFindName */ + + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + BOOLEAN DisconnectWait; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + CTELockHandle CancelLH; + + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + DisconnectWait = (BOOLEAN) + ((((PTDI_REQUEST_KERNEL_DISCONNECT)(REQUEST_PARAMETERS(Request)))->RequestFlags & + TDI_DISCONNECT_WAIT) != 0); + + NB_GET_CANCEL_LOCK( &CancelLH ); + + // + // We need to be inside a sync because NbiStopConnection + // expects that. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (DisconnectWait) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // This disconnect wait will get completed by + // NbiStopConnection. + // + + if (Connection->DisconnectWaitRequest == NULL) { + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelDisconnectWait); + NB_DEBUG2 (CONNECTION, ("Disconnect wait queued on connection %lx\n", Connection)); + Connection->DisconnectWaitRequest = Request; + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled disconnect wait on connection %lx\n", Connection)); + Status = STATUS_CANCELLED; + } + + } else { + + // + // We got a second disconnect request and we already + // have one pending. + // + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, already queued on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + NB_DEBUG (CONNECTION, ("Disconnect wait submitted on disconnected connection %lx\n", Connection)); + Status = Connection->Status; + + } else { + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, bad state on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } else { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + + NB_DEBUG2 (CONNECTION, ("Disconnect of active connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + // + // This call releases the connection lock, sets + // the state to DISCONNECTING, and sends out + // the first session end. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // There is already a disconnect pending. Queue + // this one up so it completes when the refcount + // goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of disconnecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + // + // We were waiting for an accept, but instead we got + // a disconnect. Remove the reference and the teardown + // will proceed. The disconnect will complete when the + // refcount goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of accept pending connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_W_ACCEPT); + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // We are connecting, and got a disconnect. We call + // NbiStopConnection which will handle this case + // and abort the connect. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of connecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // This call releases the connection lock and + // aborts the connect request. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Disconnect of invalid connection (%d) %lx\n", + Connection->State, Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Status = STATUS_INVALID_CONNECTION; + + } + + } + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisconnect */ + + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to assign a connection ID. It picks + one whose hash table has the fewest entries. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. THE CONNECTION IS INSERTED INTO THE CORRECT HASH + ENTRY BY THIS CALL. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + TRUE if it could be successfully assigned. + +--*/ + +{ + UINT Hash; + UINT i; + USHORT ConnectionId, HashId; + PCONNECTION CurConnection; + + + CTEAssert (Connection->LocalConnectionId == 0xffff); + + // + // Find the hash bucket with the fewest entries. + // + + Hash = 0; + for (i = 1; i < CONNECTION_HASH_COUNT; i++) { + if (Device->ConnectionHash[i].ConnectionCount < Device->ConnectionHash[Hash].ConnectionCount) { + Hash = i; + } + } + + + // + // Now find a valid connection ID within that bucket. + // + + ConnectionId = Device->ConnectionHash[Hash].NextConnectionId; + + while (TRUE) { + + // + // Scan through the list to see if this ID is in use. + // + + HashId = (USHORT)(ConnectionId | (Hash << CONNECTION_HASH_SHIFT)); + + CurConnection = Device->ConnectionHash[Hash].Connections; + + while (CurConnection != NULL) { + if (CurConnection->LocalConnectionId != HashId) { + CurConnection = CurConnection->NextConnection; + } else { + break; + } + } + + if (CurConnection == NULL) { + break; + } + + if (ConnectionId >= CONNECTION_MAXIMUM_ID) { + ConnectionId = 1; + } else { + ++ConnectionId; + } + + // + // BUGBUG: What if we have 64K-1 sessions and loop forever? + // + } + + if (Device->ConnectionHash[Hash].NextConnectionId >= CONNECTION_MAXIMUM_ID) { + Device->ConnectionHash[Hash].NextConnectionId = 1; + } else { + ++Device->ConnectionHash[Hash].NextConnectionId; + } + + Connection->LocalConnectionId = HashId; + Connection->RemoteConnectionId = 0xffff; + NB_DEBUG2 (CONNECTION, ("Assigned ID %lx to %x\n", Connection->LocalConnectionId, Connection)); + + Connection->NextConnection = Device->ConnectionHash[Hash].Connections; + Device->ConnectionHash[Hash].Connections = Connection; + ++Device->ConnectionHash[Hash].ConnectionCount; + + return TRUE; + +} /* NbiAssignConnectionId */ + + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to deassign a connection ID. It removes + the connection from the hash bucket for its ID. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + None. + +--*/ + +{ + UINT Hash; + PCONNECTION CurConnection; + PCONNECTION * PrevConnection; + + // + // Make sure the connection has a valid ID. + // + + CTEAssert (Connection->LocalConnectionId != 0xffff); + + Hash = (Connection->LocalConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + CurConnection = Device->ConnectionHash[Hash].Connections; + PrevConnection = &Device->ConnectionHash[Hash].Connections; + + while (TRUE) { + + CTEAssert (CurConnection != NULL); + + // + // We can loop until we find it because it should be + // on here. + // + + if (CurConnection == Connection) { + *PrevConnection = Connection->NextConnection; + --Device->ConnectionHash[Hash].ConnectionCount; + break; + } + + PrevConnection = &CurConnection->NextConnection; + CurConnection = CurConnection->NextConnection; + + } + + Connection->LocalConnectionId = 0xffff; + +} /* NbiDeassignConnectionId */ + + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the connection timer expires. + This is either because we need to send the next session + initialize, or because our listen has timed out. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the connection. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection = (PCONNECTION)Context; + PDEVICE Device = NbiDevice; + PREQUEST Request; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (CancelLH) + + // + // Take the lock and see what we need to do. + // + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (--Connection->Retries == 0) { + + NB_DEBUG2 (CONNECTION, ("Timing out session initializes on %lx\n", Connection)); + + // + // We have just timed out this connect, we fail the + // request. When the reference count goes to 0 we + // will set the state to INACTIVE and deassign + // the connection ID. + // + + Request = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_BAD_NETWORK_PATH; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session initialize. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionInitialize (Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + if ((Connection->SubState != CONNECTION_SUBSTATE_D_W_ACK) || + (--Connection->Retries == 0)) { + + NB_DEBUG2 (CONNECTION, ("Timing out disconnect of %lx\n", Connection)); + + // + // Just dereference the connection, that will cause the + // disconnect to be completed, the state to be set + // to INACTIVE, and our connection ID deassigned. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session end. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionEnd(Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } + +} /* NbiConnectionTimeout */ + + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + listen. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_LISTEN)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_WAITING) && + (Connection->ListenRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled listen on %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel listen on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelListen */ + + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for the name to be found. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + PLIST_ENTRY p; + BOOLEAN fCanceled = TRUE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) && + (Connection->ConnectRequest == Request)) { + + // + // Make sure the request is still on the queue + // before cancelling it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + for (p = Device->WaitingConnects.Flink; + p != &Device->WaitingConnects; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + break; + } + } + + if (p != &Device->WaitingConnects) { + + NB_DEBUG2 (CONNECTION, ("Cancelled find name connect on %lx\n", Connection)); + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + Connection->ConnectRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + +#ifdef RASAUTODIAL + if (Connection->Flags & CONNECTION_FLAGS_AUTOCONNECTING) + fCanceled = NbiCancelTdiConnect(Device, Request, Connection); +#endif // RASAUTODIAL + + if (fCanceled) { + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect not found on queue %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectFindName */ + + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for a rip or session init response + from the remote. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN TimerWasStopped = FALSE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) && + (Connection->ConnectRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled wait response connect on %lx\n", Connection)); + + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectWaitResponse */ + + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + disconnect wait. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_DISCONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->DisconnectWaitRequest == Request) { + + Connection->DisconnectWaitRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelDisconnectWait */ + + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ) + +/*++ + +Routine Description: + + This routine looks up a connection based on the context. + The connection is assumed to be associated with the + specified address file. + +Arguments: + + AddressFile - Pointer to an address file. + + ConnectionContext - Connection context to find. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + CTELockHandle LockHandle1, LockHandle2; + PLIST_ENTRY p; + PADDRESS Address = AddressFile->Address; + PCONNECTION Connection; + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p=AddressFile->ConnectionDatabase.Flink; + p != &AddressFile->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + NB_GET_LOCK (&Connection->Lock, &LockHandle2); + + // + // BUGBUG: Does this spinlock ordering hurt us + // somewhere else? + // + + if (Connection->Context == ConnectionContext) { + + NbiReferenceConnection (Connection, CREF_BY_CONTEXT); + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return Connection; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return NULL; + +} /* NbiLookupConnectionByContext */ + + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates a transport connection and associates it with + the specified transport device context. The reference count in the + connection is automatically set to 1, and the reference count of the + device context is incremented. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + connection. + +Return Value: + + The newly created connection, or NULL if none can be allocated. + +--*/ + +{ + PCONNECTION Connection; + PNB_SEND_RESERVED SendReserved; + ULONG ConnectionSize; + ULONG HeaderLength; + NTSTATUS Status; + CTELockHandle LockHandle; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION); + ConnectionSize = FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + HeaderLength; + + Connection = (PCONNECTION)NbiAllocateMemory (ConnectionSize, MEMORY_CONNECTION, "Connection"); + if (Connection == NULL) { + NB_DEBUG (CONNECTION, ("Create connection failed\n")); + return NULL; + } + + NB_DEBUG2 (CONNECTION, ("Create connection %lx\n", Connection)); + RtlZeroMemory (Connection, ConnectionSize); + + +#if defined(NB_OWN_PACKETS) + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + +#else // !NB_OWN_PACKETS + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &Connection->SendPacketPoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (CONNECTION, ("Could not allocatee connection packet %lx\n", Status)); + Connection->SendPacketInUse = TRUE; + } else { + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(Connection->SendPacketPoolHandle); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + } + +#endif NB_OWN_PACKETS + + Connection->Type = NB_CONNECTION_SIGNATURE; + Connection->Size = (USHORT)ConnectionSize; + +#if 0 + Connection->AddressFileLinked = FALSE; + Connection->AddressFile = NULL; +#endif + + Connection->State = CONNECTION_STATE_INACTIVE; +#if 0 + Connection->SubState = 0; + Connection->ReferenceCount = 0; +#endif + + Connection->CanBeDestroyed = TRUE; + + Connection->TickCount = 1; + Connection->HopCount = 1; + + // + // Device->InitialRetransmissionTime is in milliseconds, as is + // SHORT_TIMER_DELTA. + // + + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + // + // Device->KeepAliveTimeout is in half-seconds, while LONG_TIMER_DELTA + // is in milliseconds. + // + + Connection->WatchdogTimeout = (Device->KeepAliveTimeout * 500) / LONG_TIMER_DELTA; + + + Connection->LocalConnectionId = 0xffff; + + // + // When the connection becomes active we will replace the + // destination address of this header with the correct + // information. + // + + RtlCopyMemory(&Connection->RemoteHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + Connection->Device = Device; + Connection->DeviceLock = &Device->Lock; + CTEInitLock (&Connection->Lock.Lock); + + CTEInitTimer (&Connection->Timer); + + InitializeListHead (&Connection->NdisSendQueue); +#if 0 + Connection->NdisSendsInProgress = 0; + Connection->DisassociatePending = NULL; + Connection->ClosePending = NULL; + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; +#endif + Connection->Flags = 0; + + NbiReferenceDevice (Device, DREF_CONNECTION); + + return Connection; + +} /* NbiCreateConnection */ + + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid connection object. We reference + it to keep it from disappearing while we use it. + +Arguments: + + Connection - potential pointer to a CONNECTION object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PDEVICE Device = NbiDevice; + BOOLEAN LockHeld = FALSE; + + try { + + if ((Connection->Size == FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) && + (Connection->Type == NB_CONNECTION_SIGNATURE)) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LockHeld = TRUE; + + if (Connection->State != CONNECTION_STATE_CLOSING) { + + NbiReferenceConnectionLock (Connection, CREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx closing\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx bad signature\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyConnection: C %lx exception\n", Connection); + if (LockHeld) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyConnection */ + + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine destroys a transport connection and removes all references + made by it to other objects in the transport. The connection structure + is returned to nonpaged system pool. + +Arguments: + + Connection - Pointer to a transport connection structure to be destroyed. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = Connection->Device; +#if 0 + CTELockHandle LockHandle; +#endif + + NB_DEBUG2 (CONNECTION, ("Destroy connection %lx\n", Connection)); + + if (!Connection->SendPacketInUse) { + NbiDeinitializeSendPacket (Device, &Connection->SendPacket, Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(Connection->SendPacketPoolHandle); +#endif + } + + NbiFreeMemory (Connection, (ULONG)Connection->Size, MEMORY_CONNECTION, "Connection"); + + NbiDereferenceDevice (Device, DREF_CONNECTION); + +} /* NbiDestroyConnection */ + + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + (VOID)ExInterlockedAddUlong ( + &Connection->ReferenceCount, + 1, + &Connection->DeviceLock->Lock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnection */ + + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when the device lock is already held. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + ++Connection->ReferenceCount; + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionLock */ + + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when we are in a sync routine. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + (VOID)NB_ADD_ULONG ( + &Connection->ReferenceCount, + 1, + Connection->DeviceLock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionSync */ + + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine dereferences a transport connection by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiHandleConnectionZero to complete any disconnect, disassociate, + or close requests that have pended on the connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + CTELockHandle LockHandle; + + NB_GET_LOCK( Connection->DeviceLock, &LockHandle ); + CTEAssert( Connection->ReferenceCount ); + if ( !(--Connection->ReferenceCount) ) { + + Connection->ThreadsInHandleConnectionZero++; + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + + // + // If the refcount has dropped to 0, then the connection can + // become inactive. We reacquire the spinlock and if it has not + // jumped back up then we handle any disassociates and closes + // that have pended. + // + + NbiHandleConnectionZero (Connection); + } else { + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + } + + +} /* NbiDerefConnection */ + + +#endif + + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine handles a connection's refcount going to 0. + + BUGBUG: If two threads are in this at the same time and + the close has already come through, one of them might + destroy the connection while the other one is looking + at it. We minimize the chance of this by not derefing + the connection after calling CloseConnection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST DisconnectPending; + PREQUEST DisassociatePending; + PREQUEST ClosePending; + + + Device = Connection->Device; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + +#if DBG + // + // Make sure if our reference count is zero, all the + // sub-reference counts are also zero. + // + + if (Connection->ReferenceCount == 0) { + + UINT i; + for (i = 0; i < CREF_TOTAL; i++) { + if (Connection->RefTypes[i] != 0) { + DbgPrint ("NBI: Connection reftype mismatch on %lx\n", Connection); + DbgBreakPoint(); + } + } + } +#endif + + // + // If the connection was assigned an ID, then remove it + // (it is assigned one when it leaves INACTIVE). + // + + if (Connection->LocalConnectionId != 0xffff) { + NbiDeassignConnectionId (Device, Connection); + } + + // + // Complete any pending disconnects. + // + + if (Connection->DisconnectRequest != NULL) { + + DisconnectPending = Connection->DisconnectRequest; + Connection->DisconnectRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_STATUS(DisconnectPending) = STATUS_SUCCESS; + NbiCompleteRequest (DisconnectPending); + NbiFreeRequest (Device, DisconnectPending); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // This should have been completed by NbiStopConnection, + // or else not allowed to be queued. + // + + CTEAssert (Connection->DisconnectWaitRequest == NULL); + + + Connection->State = CONNECTION_STATE_INACTIVE; + + // + // BUGBUG: Make NbiInitializeConnection() to take care of all this. + // + + RtlZeroMemory (&Connection->ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + Connection->TickCount = 1; + Connection->HopCount = 1; + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + + Connection->ConnectionInfo.TransmittedTsdus = 0; + Connection->ConnectionInfo.TransmissionErrors = 0; + Connection->ConnectionInfo.ReceivedTsdus = 0; + Connection->ConnectionInfo.ReceiveErrors = 0; + + // + // See if we need to do a disassociate now. + // + + if ((Connection->ReferenceCount == 0) && + (Connection->DisassociatePending != NULL)) { + + // + // A disassociate pended, now we complete it. + // + + DisassociatePending = Connection->DisassociatePending; + Connection->DisassociatePending = NULL; + + // + // Set this so nobody else tries to disassociate. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + if (DisassociatePending != (PVOID)-1) { + REQUEST_STATUS(DisassociatePending) = STATUS_SUCCESS; + NbiCompleteRequest (DisassociatePending); + NbiFreeRequest (Device, DisassociatePending); + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + + // + // If a close was pending, complete that. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if ((Connection->ReferenceCount == 0) && + (Connection->ClosePending)) { + + ClosePending = Connection->ClosePending; + Connection->ClosePending = NULL; + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + // + // Even if the ref count is zero and we just cleaned up everything, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + if (ExInterlockedAddUlong ( &Connection->ThreadsInHandleConnectionZero, (ULONG)-1, &Device->Lock.Lock) == 1) { + NbiDestroyConnection(Connection); + } + + REQUEST_STATUS(ClosePending) = STATUS_SUCCESS; + NbiCompleteRequest (ClosePending); + NbiFreeRequest (Device, ClosePending); + + } else { + + if ( Connection->ReferenceCount == 0 ) { + Connection->CanBeDestroyed = TRUE; + } + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + Connection->ThreadsInHandleConnectionZero--; + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiHandleConnectionZero */ + diff --git a/private/ntos/tdi/isnp/nb/datagram.c b/private/ntos/tdi/isnp/nb/datagram.c new file mode 100644 index 000000000..e81579d1b --- /dev/null +++ b/private/ntos/tdi/isnp/nb/datagram.c @@ -0,0 +1,1089 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + datagram.c + +Abstract: + + This module contains the code to handle datagram reception + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ) + +/*++ + +Routine Description: + + This routine handles datagram indications. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + + Broadcast - TRUE if the frame was a broadcast datagram. + +Return Value: + + None. + +--*/ + +{ + + PADDRESS Address; + NDIS_STATUS NdisStatus; + PUCHAR NetbiosName; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + PDEVICE Device = NbiDevice; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + ULONG DataOffset; + UINT BytesTransferred; + PNDIS_PACKET Packet; + CTELockHandle LockHandle; + + + // + // See if there is an address that might want this. + // + + if (Broadcast) { + NetbiosName = (PVOID)-1; + } else { + NetbiosName = (PUCHAR)Connectionless->Datagram.DestinationName; + if (Device->AddressCounts[NetbiosName[0]] == 0) { + return; + } + } + + DataOffset = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + +#if defined(_PNP_POWER) + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->CurMaxReceiveBufferSize)) { +#else + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->Bind.LineInfo.MaximumPacketSize)) { +#endif _PNP_POWER + + NB_DEBUG (DATAGRAM, ("Datagram length %d discarded\n", PacketSize)); + return; + } + + Address = NbiFindAddress (Device, NetbiosName); + + if (Address == NULL) { + return; + } + + // + // We need to cache the remote name if the packet came across the router. + // This allows this machine to get back to the RAS client which might + // have sent this datagram. We currently dont allow broadcasts to go out + // on the dial-in line. + // Dont cache some of the widely used group names, that would be too much + // to store in cache. + // + +#if 0 + if ( Connectionless->IpxHeader.TransportControl && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x0 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x01 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x1E ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { +#endif + if ( Connectionless->IpxHeader.TransportControl && + ( (Address->NetbiosAddress.NetbiosName[15] == 0x1c ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { + + PNETBIOS_CACHE CacheName; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + if ( FindInNetbiosCacheTable ( Device->NameCache, + Connectionless->Datagram.SourceName, + &CacheName ) != STATUS_SUCCESS ) { + + CacheName = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (CacheName ) { + RtlCopyMemory (CacheName->NetbiosName, Connectionless->Datagram.SourceName, 16); + CacheName->Unique = TRUE; + CacheName->ReferenceCount = 1; + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->NetworksAllocated = 1; + CacheName->NetworksUsed = 1; + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + NB_DEBUG2 (CACHE, ("Alloc new cache from Datagram %lx for <%.16s>\n", + CacheName, CacheName->NetbiosName)); + + CacheName->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + CacheName); + + } + } else if ( CacheName->Unique ) { + // + // We already have an entry for this remote. We should update + // the address. This is so that if the ras client dials-out + // then dials-in again and gets a new address, we dont end up + // caching the old address. + // + if ( !RtlEqualMemory( &CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12) ) { + + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + + } + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + // + // We need to allocate a packet and buffer for the transfer. + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + + + s = NbiPopReceiveBuffer (Device); + if (s == NULL) { + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveBuffer = CONTAINING_RECORD (s, NB_RECEIVE_BUFFER, PoolLinkage); + + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + ReceiveReserved->u.RR_DG.ReceiveBuffer = ReceiveBuffer; + + + // + // Now that we have a packet and a buffer, set up the transfer. + // The indication to the TDI clients will happen at receive + // complete time. + // + + NdisChainBufferAtFront (Packet, ReceiveBuffer->NdisBuffer); + ReceiveBuffer->Address = Address; + + ReceiveReserved->Type = RECEIVE_TYPE_DATAGRAM; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + TdiCopyLookaheadData( + &ReceiveBuffer->RemoteName, + Connectionless->Datagram.SourceName, + 16, + (MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + DataOffset, + PacketSize - DataOffset, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == PacketSize - DataOffset); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessDatagram */ + + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ) + +/*++ + +Routine Description: + + This routine indicates a datagram to clients on the specified + address. It is called from NbiReceiveComplete. + +Arguments: + + Address - The address the datagram was sent to. + + RemoteName - The source netbios address of the datagram. + + Data - The data. + + DataLength - The length of the data. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p, q; + PIRP Irp; + ULONG IndicateBytesCopied; + PREQUEST Request; + TA_NETBIOS_ADDRESS SourceName; + PTDI_CONNECTION_INFORMATION RemoteInformation; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * DatagramAddress; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // Update our statistics. + // + + ++Device->Statistics.DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + DataLength); + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + TdiBuildNetbiosAddress (RemoteName, FALSE, &SourceName); + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. We run through the receive datagram queue + // until we find a datagram with no remote address or with + // this sender's address as its remote address. + // + + for (q = AddressFile->ReceiveDatagramQueue.Flink; + q != &AddressFile->ReceiveDatagramQueue; + q = q->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (q); + DatagramInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReceiveDatagramInformation; + + if (DatagramInformation && + (DatagramInformation->RemoteAddress) && + (DatagramAddress = NbiParseTdiAddress(DatagramInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + RemoteName, + DatagramAddress->NetbiosName, + 16))) { + continue; + } + break; + } + + if (q != &AddressFile->ReceiveDatagramQueue) { + + RemoveEntryList (q); + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // Do this deref now, we hold another one so it + // will stick around. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + IndicateBytesCopied = 0; + + // + // Fall past the else to copy the data. + // + + } else { + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No receive datagram requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE_DATAGRAM]) { + + IndicateBytesCopied = 0; + + if ((*AddressFile->ReceiveDatagramHandler)( + AddressFile->HandlerContexts[TDI_EVENT_RECEIVE_DATAGRAM], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + DataLength, // indicated + DataLength, // available + &IndicateBytesCopied, + Data, + &Irp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + Request = NbiAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + } + + } else { + + // + // The client has nothing posted and no handler, + // go on to the next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + } + + // + // We have a request; copy the actual user data. + // + if ( REQUEST_NDIS_BUFFER (Request) ) { + + REQUEST_STATUS(Request) = + TdiCopyBufferToMdl ( + Data, + IndicateBytesCopied, + DataLength - IndicateBytesCopied, + REQUEST_NDIS_BUFFER (Request), + 0, + &REQUEST_INFORMATION (Request)); + + } else { + // + // No buffer specified in the request + // + REQUEST_INFORMATION (Request) = 0; + // + // If there was any data to be copied, return error o/w success + // + REQUEST_STATUS(Request) = ( (DataLength - IndicateBytesCopied) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ); + } + + // + // Copy the addressing information. + // + + RemoteInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReturnDatagramInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + +} /* NbiIndicateDatagram */ + + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends a datagram on an address. + +Arguments: + + Device - The netbios device. + + Request - The request describing the datagram send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS_FILE AddressFile; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_SENDDG Parameters; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle; + NTSTATUS Status; + + // + // Make sure that the address is valid. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status == STATUS_SUCCESS) { + + Parameters = (PTDI_REQUEST_KERNEL_SENDDG)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->SendDatagramInformation->RemoteAddress), TRUE); + + + // + // Check that datagram size is less than the maximum allowable + // by the adapters. In the worst case this would be + // 576 - 64 = 512. + // + +#if defined(_PNP_POWER) + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumSendSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumSendSize )); + return STATUS_INVALID_PARAMETER; + } +#else + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumPacketSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumPacketSize )); + return STATUS_INVALID_PARAMETER; + } +#endif _PNP_POWER + + if (RemoteName != NULL) { + + // + // Get a packet to use in this send. + // + + s = NbiPopSendPacket (Device, FALSE); + + if (s != NULL) { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Check on the cache status of this name. + // + + Reserved->u.SR_DG.DatagramRequest = Request; + Reserved->u.SR_DG.AddressFile = AddressFile; + Reserved->u.SR_DG.RemoteName = RemoteName; + + REQUEST_INFORMATION (Request) = Parameters->SendLength; + + ++Device->Statistics.DatagramsSent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Parameters->SendLength); + + if (Device->Internet) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + (RemoteName == (PVOID)-1) ? NULL : (PUCHAR)RemoteName->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this datagram + // request and processing will be resumed when + // we get a response. + // + + NB_DEBUG2 (CONNECTION, ("Queueing up datagram %lx on %lx\n", + Request, AddressFile)); + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + InsertTailList( + &Device->WaitingDatagrams, + &Reserved->WaitLinkage); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (CONNECTION, ("Found datagram cached %lx on %lx\n", + Request, AddressFile)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + ++CacheName->ReferenceCount; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } else { + + // + // Only this failure gets passed back up to + // the caller, to avoid confusing the browser. + // + + if (Status != STATUS_DEVICE_DOES_NOT_EXIST) { + + Status = STATUS_SUCCESS; + + } else { + + REQUEST_INFORMATION (Request) = 0; + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + + + + } + + } else { + + // + // We are not in internet mode, so we do not + // need to do the name discovery. + // + + NB_DEBUG2 (CONNECTION, ("Sending datagram direct %lx on %lx\n", + Request, AddressFile)); + + Reserved->u.SR_DG.Cache = NULL; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } + + } else { + + // + // Could not allocate a packet for the datagram. + // + + NB_DEBUG (DATAGRAM, ("Couldn't get packet to send DG %lx\n", Request)); + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + + } else { + + // + // There is no netbios remote address specified. + // + + NB_DEBUG (DATAGRAM, ("No netbios address in DG %lx\n", Request)); + Status = STATUS_BAD_NETWORK_PATH; + + } + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } else { + + NB_DEBUG (DATAGRAM, ("Invalid address file for DG %lx\n", Request)); + + } + + return Status; + +} /* NbiTdiSendDatagram */ + + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine sends a datagram to the next net in the + cache entry for the remote name. + +Arguments: + + Reserved - The reserved section of the packet that has + been allocated for this send. Reserved->u.SR_DG.Cache + will be NULL if Internet mode is off, otherwise it + will contain the cache entry to use when sending + this datagram. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_PACKET Packet; + PNETBIOS_CACHE CacheName; + NB_CONNECTIONLESS UNALIGNED * Header; + ULONG HeaderLength; + ULONG PacketLength; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DATAGRAM; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + if (CacheName == NULL) { + +#if defined(_PNP_POWER) + // + // IPX will send this on all the Nics. + // + TempLocalTarget.NicHandle.NicId = 0; +#else + TempLocalTarget.NicId = 1; +#endif _PNP_POWER + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + + } else { + + if (CacheName->Unique) { + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + } else { + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + } + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + } + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), HeaderLength); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiTransmitDatagram */ + + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Request - Describes this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + CTELockHandle LockHandle; + CTELockHandle CancelLH; + + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status != STATUS_SUCCESS) { + return Status; + } + + Address = AddressFile->Address; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_INVALID_HANDLE; + } + + + if (Request->Cancel) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_CANCELLED; + } + + InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request)); + + IoSetCancelRoutine (Request, NbiCancelReceiveDatagram); + + NB_DEBUG2 (DATAGRAM, ("RDG posted on %lx\n", AddressFile)); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_RCV_DGRAM); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + return STATUS_PENDING; + +} /* NbiTdiReceiveDatagram */ + + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The datagram is found on the address file's receive + datagram queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN Found; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Address = AddressFile->Address; + + Found = FALSE; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + NB_DEBUG (DATAGRAM, ("Cancelled datagram on %lx\n", AddressFile)); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest((PDEVICE)DeviceObject, Request); + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + +} /* NbiCancelReceiveDatagram */ + diff --git a/private/ntos/tdi/isnp/nb/device.c b/private/ntos/tdi/isnp/nb/device.c new file mode 100644 index 000000000..d1a9af781 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/device.c @@ -0,0 +1,461 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + device.c + +Abstract: + + This module contains code which implements the DEVICE object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiCreateDevice) +#endif + + +VOID +NbiRefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Device->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Device->ReferenceCount); + +} /* NbiRefDevice */ + + +VOID +NbiDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + NbiDestroyDevice (Device); + } + +} /* NbiDerefDevice */ + + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + UINT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors) and the RIP fields. + // + + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + NB_DEBUG(DEVICE, ("Create device %ws failed %lx\n", DeviceName->Buffer, status)); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + Device = (PDEVICE)deviceObject; + + NB_DEBUG2 (DEVICE, ("Create device %ws succeeded %lx\n", DeviceName->Buffer, Device)); + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + // + // Copy over the device name. + // + + Device->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + Device->DeviceName = (PWCHAR)(Device+1); + RtlCopyMemory( + Device->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + Device->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + Device->ReferenceCount = 1; +#if DBG + Device->RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->Signature1, "NDC1", 4); + RtlCopyMemory(Device->Signature2, "NDC2", 4); +#endif + + // + // BETABUGBUG: Clean this up a bit. + // + + Device->Information.Version = 0x0100; + Device->Information.MaxSendSize = 65535; + Device->Information.MaxConnectionUserData = 0; + Device->Information.MaxDatagramSize = 500; + Device->Information.ServiceFlags = + TDI_SERVICE_CONNECTION_MODE | TDI_SERVICE_ERROR_FREE_DELIVERY | + TDI_SERVICE_MULTICAST_SUPPORTED | TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_DELAYED_ACCEPTANCE | TDI_SERVICE_CONNECTIONLESS_MODE | + TDI_SERVICE_MESSAGE_MODE; + Device->Information.MinimumLookaheadData = 128; + Device->Information.MaximumLookaheadData = 1500; + Device->Information.NumberOfResources = 0; + KeQuerySystemTime (&Device->Information.StartTime); + + Device->Statistics.Version = 0x0100; + Device->Statistics.MaximumSendWindow = 4; + Device->Statistics.AverageSendWindow = 4; + + // + // Set this so we won't ignore the broadcast name. + // + + Device->AddressCounts['*'] = 1; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&Device->AddressResource); + + // + // initialize the various fields in the device context + // + + CTEInitLock (&Device->Interlock.Lock); + CTEInitLock (&Device->Lock.Lock); + + CTEInitTimer (&Device->FindNameTimer); + + Device->ControlChannelIdentifier = 1; + + InitializeListHead (&Device->GlobalSendPacketList); + InitializeListHead (&Device->GlobalReceivePacketList); + InitializeListHead (&Device->GlobalReceiveBufferList); + + InitializeListHead (&Device->AddressDatabase); +#if defined(_PNP_POWER) + InitializeListHead (&Device->AdapterAddressDatabase); +#endif _PNP_POWER + + InitializeListHead (&Device->WaitingFindNames); + + InitializeListHead (&Device->WaitingConnects); + InitializeListHead (&Device->WaitingDatagrams); + + InitializeListHead (&Device->WaitingAdapterStatus); + InitializeListHead (&Device->ActiveAdapterStatus); + + InitializeListHead (&Device->WaitingNetbiosFindName); + + InitializeListHead (&Device->ReceiveDatagrams); + InitializeListHead (&Device->ConnectIndicationInProgress); + + InitializeListHead (&Device->ListenQueue); + + InitializeListHead (&Device->ReceiveCompletionQueue); + + InitializeListHead (&Device->WaitPacketConnections); + InitializeListHead (&Device->PacketizeConnections); + InitializeListHead (&Device->DataAckConnections); + + Device->MemoryUsage = 0; + + InitializeListHead (&Device->SendPoolList); + InitializeListHead (&Device->ReceivePoolList); + InitializeListHead (&Device->ReceiveBufferPoolList); + + ExInitializeSListHead( &Device->SendPacketList ); + ExInitializeSListHead( &Device->ReceivePacketList ); + Device->ReceiveBufferList.Next = NULL; + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + Device->ConnectionHash[i].Connections = NULL; + Device->ConnectionHash[i].ConnectionCount = 0; + Device->ConnectionHash[i].NextConnectionId = 1; + } + + KeQuerySystemTime (&Device->NbiStartTime); + + Device->State = DEVICE_STATE_CLOSED; + + Device->Type = NB_DEVICE_SIGNATURE; + Device->Size - sizeof (DEVICE); + + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} /* NbiCreateDevice */ + + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PNB_SEND_POOL SendPool; + PNB_SEND_PACKET SendPacket; + UINT SendPoolSize; + PNB_RECEIVE_POOL ReceivePool; + PNB_RECEIVE_PACKET ReceivePacket; + UINT ReceivePoolSize; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + ULONG HeaderLength; + UINT i; + + NB_DEBUG2 (DEVICE, ("Destroy device %lx\n", Device)); + + // + // Take all the connectionless packets out of its pools. + // + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, NB_SEND_POOL, Linkage); + + for (i = 0; i < SendPool->PacketCount; i++) { + + SendPacket = &SendPool->Packets[i]; + NbiDeinitializeSendPacket (Device, SendPacket, HeaderLength); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", SendPool)); + +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(SendPool->PoolHandle); +#endif + + NbiFreeMemory (SendPool, SendPoolSize, MEMORY_PACKET, "SendPool"); + } + + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, NB_RECEIVE_POOL, Linkage); + + for (i = 0; i < ReceivePool->PacketCount; i++) { + + ReceivePacket = &ReceivePool->Packets[i]; + NbiDeinitializeReceivePacket (Device, ReceivePacket); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", ReceivePool)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(ReceivePool->PoolHandle); +#endif + NbiFreeMemory (ReceivePool, ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + } + +#if defined(_PNP_POWER) + NbiDestroyReceiveBufferPools( Device ); + + // + // Destroy adapter address list. + // + while(!IsListEmpty( &Device->AdapterAddressDatabase ) ){ + PADAPTER_ADDRESS AdapterAddress; + AdapterAddress = CONTAINING_RECORD( Device->AdapterAddressDatabase.Flink, ADAPTER_ADDRESS, Linkage ); + NbiDestroyAdapterAddress( AdapterAddress, NULL ); + } +#else + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (Device->Bind.LineInfo.MaximumPacketSize * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceiveBufferPoolList)) { + + p = RemoveHeadList (&Device->ReceiveBufferPoolList); + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + } +#endif _PNP_POWER + + NB_DEBUG (DEVICE, ("Final memory use is %d\n", Device->MemoryUsage)); + +#if DBG + for (i = 0; i < MEMORY_MAX; i++) { + if (NbiMemoryTag[i].BytesAllocated != 0) { + NB_DEBUG (DEVICE, ("Tag %d: %d bytes left\n", i, NbiMemoryTag[i].BytesAllocated)); + } + } +#endif + + // + // If we are being unloaded then someone is waiting for this + // event to finish the cleanup, since we may be at DISPATCH_LEVEL; + // otherwise it is during load and we can just kill ourselves here. + // + + if (Device->UnloadWaiting) { + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + + } else { + + CTEAssert (KeGetCurrentIrql() < DISPATCH_LEVEL); + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + } + +} /* NbiDestroyDevice */ + diff --git a/private/ntos/tdi/isnp/nb/dirs b/private/ntos/tdi/isnp/nb/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/nb/driver.c b/private/ntos/tdi/isnp/nb/driver.c new file mode 100644 index 000000000..c4df8cbe6 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/driver.c @@ -0,0 +1,1794 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + + +PDEVICE NbiDevice = NULL; +DEFINE_LOCK_STRUCTURE(NbiGlobalPoolInterlock); + +#ifdef RSRC_TIMEOUT_DBG + +ULONG NbiGlobalDebugResTimeout = 1; +LARGE_INTEGER NbiGlobalMaxResTimeout; + // the packet is allocated from ndis pool. +NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +UCHAR NbiGlobalDeathPacketHeader[100]; + +VOID +NbiInitDeathPacket() +{ + + NDIS_HANDLE PoolHandle; // poolhandle for sendpacket below when + NTSTATUS Status; + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &PoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + } else { + + if (NbiInitializeSendPacket( + NbiDevice, + PoolHandle, + &NbiGlobalDeathPacket, + NbiGlobalDeathPacketHeader, + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) != STATUS_SUCCESS) { + + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(PoolHandle); + } + } + +} +#endif //RSRC_TIMEOUT_DBG + +#if DBG + +ULONG NbiDebug = 0xffffffff; +ULONG NbiDebug2 = 0x00000000; +ULONG NbiMemoryDebug = 0x0002482c; + +UCHAR NbiTempDebugBuffer[150]; +UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +PUCHAR NbiDebugMemoryLoc = NbiDebugMemory[0]; +PUCHAR NbiDebugMemoryEnd = NbiDebugMemory[NB_MEMORY_LOG_SIZE]; +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (NbiTempDebugBuffer, 150); + ArgLen = vsprintf(NbiTempDebugBuffer,FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (NbiDebugMemoryLoc, 64); + RtlCopyMemory( NbiDebugMemoryLoc, NbiTempDebugBuffer, ArgLen ); + + NbiDebugMemoryLoc += 64; + if (NbiDebugMemoryLoc >= NbiDebugMemoryEnd) { + NbiDebugMemoryLoc = NbiDebugMemory[0]; + } + } + +} /* NbiDebugMemoryLog */ + + +DEFINE_LOCK_STRUCTURE(NbiMemoryInterlock); +MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// +DEFINE_LOCK_STRUCTURE(NbiGlobalInterlock); + + +#ifdef RASAUTODIAL +VOID +NbiAcdBind(); + +VOID +NbiAcdUnbind(); +#endif + +#ifdef NB_PACKET_LOG + +ULONG NbiPacketLogDebug = NB_PACKET_LOG_RCV_OTHER | NB_PACKET_LOG_SEND_OTHER; +USHORT NbiPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(NbiPacketLogLock); +NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +PNB_PACKET_LOG_ENTRY NbiPacketLogLoc = NbiPacketLog; +PNB_PACKET_LOG_ENTRY NbiPacketLogEnd = &NbiPacketLog[NB_PACKET_LOG_LENGTH]; + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PNB_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&NbiPacketLogLock, &LockHandle); + + PacketLog = NbiPacketLogLoc; + + ++NbiPacketLogLoc; + if (NbiPacketLogLoc >= NbiPacketLogEnd) { + NbiPacketLogLoc = NbiPacketLog; + } + *(UNALIGNED ULONG *)NbiPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&NbiPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(NB_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, Length); + } else { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* NbiLogPacket */ + +#endif // NB_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiFreeResources ( + IN PVOID Adapter + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#endif + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + + +// +// These two are used in various places in the driver. +// + +#if defined(_PNP_POWER) +IPX_LOCAL_TARGET BroadcastTarget = { {0}, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif _PNP_POWER + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +UCHAR NetbiosBroadcastName[16] = { '*', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +ULONG NbiFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the Netbios ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of Netbios's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("Netbios/IPX Transport"); + PDEVICE Device; + PIPX_HEADER IpxHeader; + CTELockHandle LockHandle; + + PCONFIG Config = NULL; + +#if 0 + DbgPrint ("NBI: FailLoad at %lx\n", &NbiFailLoad); + DbgBreakPoint(); + + if (NbiFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + NB_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&NbiGlobalInterlock); + CTEInitLock (&NbiMemoryInterlock); + { + UINT i; + for (i = 0; i < MEMORY_MAX; i++) { + NbiMemoryTag[i].Tag = i; + NbiMemoryTag[i].BytesAllocated = 0; + } + } +#endif +#ifdef NB_PACKET_LOG + CTEInitLock (&NbiPacketLogLock); +#endif + +#if defined(NB_OWN_PACKETS) + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + NB_DEBUG2 (DEVICE, ("ISN Netbios loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = NbiGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + PANIC (" Failed to initialize transport, ISN Netbios initialization failed.\n"); + return status; + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = NbiDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = NbiDispatchDeviceControl; + + DriverObject->DriverUnload = NbiUnload; + + + // + // Create the device object which exports our name. + // + + status = NbiCreateDevice (DriverObject, &Config->DeviceName, &Device); + + if (!NT_SUCCESS (status)) { + + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + NbiFreeConfiguration(Config); + return status; + } + + NbiDevice = Device; + + // + // Initialize the global pool interlock + // + CTEInitLock (&NbiGlobalPoolInterlock); + + + // + // Save the relevant configuration parameters. + // + + Device->AckDelayTime = (Config->Parameters[CONFIG_ACK_DELAY_TIME] / SHORT_TIMER_DELTA) + 1; + Device->AckWindow = Config->Parameters[CONFIG_ACK_WINDOW]; + Device->AckWindowThreshold = Config->Parameters[CONFIG_ACK_WINDOW_THRESHOLD]; + Device->EnablePiggyBackAck = Config->Parameters[CONFIG_ENABLE_PIGGYBACK_ACK]; + Device->Extensions = Config->Parameters[CONFIG_EXTENSIONS]; + Device->RcvWindowMax = Config->Parameters[CONFIG_RCV_WINDOW_MAX]; + Device->BroadcastCount = Config->Parameters[CONFIG_BROADCAST_COUNT]; + Device->BroadcastTimeout = Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500; + Device->ConnectionCount = Config->Parameters[CONFIG_CONNECTION_COUNT]; + Device->ConnectionTimeout = Config->Parameters[CONFIG_CONNECTION_TIMEOUT] * 500; + Device->InitPackets = Config->Parameters[CONFIG_INIT_PACKETS]; + Device->MaxPackets = Config->Parameters[CONFIG_MAX_PACKETS]; + Device->InitialRetransmissionTime = Config->Parameters[CONFIG_INIT_RETRANSMIT_TIME]; + Device->Internet = Config->Parameters[CONFIG_INTERNET]; + Device->KeepAliveCount = Config->Parameters[CONFIG_KEEP_ALIVE_COUNT]; + Device->KeepAliveTimeout = Config->Parameters[CONFIG_KEEP_ALIVE_TIMEOUT]; + Device->RetransmitMax = Config->Parameters[CONFIG_RETRANSMIT_MAX]; + Device->RouterMtu = Config->Parameters[CONFIG_ROUTER_MTU]; + + Device->FindNameTimeout = + ((Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500) + (FIND_NAME_GRANULARITY/2)) / + FIND_NAME_GRANULARITY; + + Device->MaxReceiveBuffers = 20; // BUGBUG: Make it configurable? + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + + NbiInitializeTimers (Device); +#endif _PNP_POWER + + // + // Now bind to IPX via the internal interface. + // + + status = NbiBind (Device, Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + +#ifdef RSRC_TIMEOUT_DBG + NbiInitDeathPacket(); + // NbiGlobalMaxResTimeout.QuadPart = 50; // 1*1000*10000; + NbiGlobalMaxResTimeout.QuadPart = 20*60*1000; + NbiGlobalMaxResTimeout.QuadPart *= 10000; +#endif // RSRC_TIMEOUT_DBG + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Create Hash Table to store netbios cache entries + // For server create a big table, for workstation a small one + // + + if ( MmIsThisAnNtAsSystem() ) { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_LARGE ); + } else { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_SMALL ); + } + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NB_FREE_LOCK(&Device->Lock, LockHandle); + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + + // + // Allocate our initial connectionless packet pool. + // + + NbiAllocateSendPool (Device); + + // + // Allocate our initial receive packet pool. + // + + NbiAllocateReceivePool (Device); + + // + // Allocate our initial receive buffer pool. + // + // +#if !defined(_PNP_POWER) + NbiAllocateReceiveBufferPool (Device); +#endif !_PNP_POWER + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == Device->State ) { + Device->State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#if !defined(_PNP_POWER) + // + // Start the timer system. + // + + NbiInitializeTimers (Device); +#endif !_PNP_POWER + + + // + // Fill in the default connnectionless header. + // + + IpxHeader = &Device->ConnectionlessHeader; + IpxHeader->CheckSum = 0xffff; + IpxHeader->PacketLength[0] = 0; + IpxHeader->PacketLength[1] = 0; + IpxHeader->TransportControl = 0; + IpxHeader->PacketType = 0; + *(UNALIGNED ULONG *)(IpxHeader->DestinationNetwork) = 0; + RtlCopyMemory(IpxHeader->DestinationNode, BroadcastAddress, 6); + IpxHeader->DestinationSocket = NB_SOCKET; + IpxHeader->SourceSocket = NB_SOCKET; +#if !defined(_PNP_POWER) + RtlCopyMemory(IpxHeader->SourceNetwork, Device->Bind.Network, 4); + RtlCopyMemory(IpxHeader->SourceNode, Device->Bind.Node, 6); + + Device->State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + + NbiFreeConfiguration(Config); + +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + NbiAcdBind(); +#endif + + return STATUS_SUCCESS; + +} /* DriverEntry */ + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has Netbios open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + + UNREFERENCED_PARAMETER (DriverObject); + + +#ifdef RASAUTODIAL + // + // Unbind from the + // automatic connection driver. + // + NbiAcdUnbind(); +#endif + + Device->State = DEVICE_STATE_STOPPING; + + // + // Free the cache of netbios names. + // + + DestroyNetbiosCacheTable( Device->NameCache ); + + // + // Cancel the long timer. + // + + if (CTEStopTimer (&Device->LongTimer)) { + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + + // + // Unbind from the IPX driver. + // + + NbiUnbind (Device); + + // + // This event will get set when the reference count + // drops to 0. + // + + KeInitializeEvent( + &Device->UnloadEvent, + NotificationEvent, + FALSE); + Device->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + NbiDereferenceDevice (Device, DREF_LOADED); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &Device->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} /* NbiUnload */ + + +VOID +NbiFreeResources ( + IN PVOID Adapter + ) +/*++ + +Routine Description: + + This routine is called by Netbios to clean up the data structures associated + with a given Device. When this routine exits, the Device + should be deleted as it no longer has any assocaited resources. + +Arguments: + + Device - Pointer to the Device we wish to clean up. + +Return Value: + + None. + +--*/ +{ +#if 0 + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PTP_PACKET packet; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; +#endif + + +#if 0 + // + // Clean up packet pool. + // + + while ( Device->PacketPool.Next != NULL ) { + s = PopEntryList( &Device->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + NbiDeallocateSendPacket (Device, packet); + } + + // + // Clean up receive packet pool + // + + while ( Device->ReceivePacketPool.Next != NULL) { + s = PopEntryList (&Device->ReceivePacketPool); + + // + // HACK: This works because Linkage is the first field in + // ProtocolReserved for a receive packet. + // + + ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); + + NbiDeallocateReceivePacket (Device, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( Device->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &Device->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + NbiDeallocateReceiveBuffer (Device, BufferTag); + } + +#endif + +} /* NbiFreeResources */ + + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPXNB device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST Request; + UINT i; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + +#if !defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenAddress (Device, Request); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenConnection (Device, Request); + break; + } + + } else { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier); + ++Device->ControlChannelIdentifier; + if (Device->ControlChannelIdentifier == 0) { + Device->ControlChannelIdentifier = 1; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONTROL_CHANNEL_FILE; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = NbiCloseAddressFile (Device, Request); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // We don't call VerifyConnection because the I/O + // system should only give us one close and the file + // object should be valid. This helps avoid a window + // where two threads call HandleConnectionZero at the + // same time. + // + + Status = NbiCloseConnection (Device, Request); + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NbiStopAddressFile (AddressFile, AddressFile->Address); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection(Connection); + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + // + // This call releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_CONNECTION + NB_LOCK_HANDLE_ARG (LockHandle1)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + Status = STATUS_SUCCESS; + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchOpenClose */ + + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = NbiDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + + return Status; + +} /* NbiDeviceControl */ + + +NB_TDI_DISPATCH_ROUTINE NbiDispatchInternalTable[] = { + NbiTdiAssociateAddress, + NbiTdiDisassociateAddress, + NbiTdiConnect, + NbiTdiListen, + NbiTdiAccept, + NbiTdiDisconnect, + NbiTdiSend, + NbiTdiReceive, + NbiTdiSendDatagram, + NbiTdiReceiveDatagram, + NbiTdiSetEventHandler, + NbiTdiQueryInformation, + NbiTdiSetInformation, + NbiTdiAction + }; + + +NTSTATUS +NbiDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request; + UCHAR MinorFunction; + + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + + // + // Branch to the appropriate request handler. + // + + MinorFunction = REQUEST_MINOR_FUNCTION(Request) - 1; + + if (MinorFunction <= (TDI_ACTION-1)) { + + Status = (*NbiDispatchInternalTable[MinorFunction]) ( + Device, + Request); + + } else { + + NB_DEBUG (DRIVER, ("Unsupported minor code %d\n", MinorFunction+1)); + if ((MinorFunction+1) == TDI_DISCONNECT) { + Status = STATUS_SUCCESS; + } else { + Status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchInternal */ + + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = NbiDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + NbiPrint1 ("Nbi: Could not allocate %d: limit\n", BytesNeeded); + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' IBN'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + NbiPrint1("Nbi: Could not allocate %d: no pool\n", BytesNeeded); + + if (ChargeDevice) { + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; + +} /* NbipAllocateMemory */ + + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* NbipFreeMemory */ + +#if DBG + + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = NbipAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &NbiMemoryInterlock); + } + + return Memory; + +} /* NbipAllocateTaggedMemory */ + + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &NbiMemoryInterlock); + + NbipFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* NbipFreeTaggedMemory */ + +#endif + + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + INT i; + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteResourceErrorLog */ + + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[8] = L"NwlnkNb"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (Device->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteGeneralErrorLog */ + + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + Device - Pointer to the device context. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteOidErrorLog */ + diff --git a/private/ntos/tdi/isnp/nb/event.c b/private/ntos/tdi/isnp/nb/event.c new file mode 100644 index 000000000..f6cff7105 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/event.c @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +PVOID TdiDefaultHandlers[6] = { + TdiDefaultConnectHandler, + TdiDefaultDisconnectHandler, + TdiDefaultErrorHandler, + TdiDefaultReceiveHandler, + TdiDefaultRcvDatagramHandler, + TdiDefaultRcvExpeditedHandler + }; + + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Device - The netbios device object. + + Request - Pointer to the request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + CTELockHandle LockHandle; + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PADDRESS_FILE AddressFile; + UINT EventType; + + UNREFERENCED_PARAMETER (Device); + + // + // Get the Address this is associated with; if there is none, get out. + // + + AddressFile = REQUEST_OPEN_CONTEXT(Request); +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&AddressFile->Address->Lock, &LockHandle); + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(Request); + EventType = (UINT)(Parameters->EventType); + + if (Parameters->EventType > TDI_EVENT_RECEIVE_EXPEDITED) { + + Status = STATUS_INVALID_PARAMETER; + + } else { + + if (Parameters->EventHandler == NULL) { + AddressFile->RegisteredHandler[EventType] = FALSE; + AddressFile->Handlers[EventType] = TdiDefaultHandlers[EventType]; + AddressFile->HandlerContexts[EventType] = NULL; + } else { + AddressFile->Handlers[EventType] = Parameters->EventHandler; + AddressFile->HandlerContexts[EventType] = Parameters->EventContext; + AddressFile->RegisteredHandler[EventType] = TRUE; + } + + } + + NB_FREE_LOCK (&AddressFile->Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + return Status; + +} /* NbiTdiSetEventHandler */ + diff --git a/private/ntos/tdi/isnp/nb/frame.c b/private/ntos/tdi/isnp/nb/frame.c new file mode 100644 index 000000000..bbc14fd56 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/frame.c @@ -0,0 +1,1095 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + frame.c + +Abstract: + + This module contains code which creates and sends various + types of frames. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if defined(_PNP_POWER) + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + ReqFrame - If specified, the request frame for which this + response is being sent. The reqframe contains the + destination ipx address and the netbios name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(ReqFrame)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)ReqFrame->IpxHeader.SourceNetwork, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : ReqFrame->NameFrame.Name, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + LocalTarget = &BroadcastTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#else + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + DestAddress - If specified, the destination IPX address to + use for the send (if not, it will be broadcast on net 0). + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.CurrentNicId = 1; + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } else { + Reserved->u.SR_NF.CurrentNicId = 0; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(DestAddress)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)DestAddress, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : NetbiosBroadcastName, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + TempLocalTarget.NicId = 1; // BUGBUG: What if 1 isn't valid? + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#endif _PNP_POWER + + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + sizeof(NB_SESSION_INIT)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Device->Extensions) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = 0xffff; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = sizeof(NB_SESSION_INIT); + Header->Session.Offset = 0; + Header->Session.DataLength = sizeof(NB_SESSION_INIT); + Header->Session.ReceiveSequence = 0; + if (Device->Extensions) { + Header->Session.ReceiveSequenceMax = 1; // low estimate for the moment + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + sizeof(NB_SESSION_INIT), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitialize */ + + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize ack + frame for the specified connection. If extra data was + specified in the session initialize frame it is echoed + back to the remote. + +Arguments: + + Connection - The connection on which the frame is sent. + + ExtraData - Any extra data (after the SESSION_INIT buffer) + in the frame. + + ExtraDataLength - THe length of the extra data. + + LockHandle - If specified, indicates the connection lock + is held and should be released. This is for cases + where the ExtraData is in memory which may be freed + once the connection lock is released. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + ULONG SessionInitBufferLength; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitBufferLength = sizeof(NB_SESSION_INIT) + ExtraDataLength; + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + // + // Save the extra data, now we can free the lock. + // + + if (ExtraDataLength != 0) { + RtlCopyMemory (SessionInitMemory+1, ExtraData, ExtraDataLength); + } + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + SessionInitBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Connection->NewNetbios) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; + } + CTEAssert (Connection->CurrentSend.SendSequence == 0); + CTEAssert (Connection->ReceiveSequence == 1); + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = (USHORT)SessionInitBufferLength; + Header->Session.Offset = 0; + Header->Session.DataLength = (USHORT)SessionInitBufferLength; + Header->Session.ReceiveSequence = 1; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + SessionInitBufferLength, + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitAck */ + + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ) + +/*++ + +Routine Description: + + This routine allocates and sends a data ack frame. + + THIS ROUTINE IS CALLED WITH THE LOCK HANDLE HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection on which the frame is sent. + + AckType - Indicates if this is a query to the remote, + a response to a received probe, or a request to resend. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, try for the connection + // packet. If that's not available, that's OK since data + // acks are connectionless anyway. + // + + if (s == NULL) { + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + } else { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.PacketLength = sizeof(NB_CONNECTION); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + switch (AckType) { + case NbiAckQuery: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_SEND_ACK; break; + case NbiAckResponse: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; break; + case NbiAckResend: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_RESEND; break; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentSend.MessageOffset; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + NbiReferenceConnectionSync(Connection, CREF_FRAME); + + // + // Set this so we will accept a probe from a remote without + // the send ack bit on. However if we receive such a request + // we turn this flag off until we get something else from the + // remote. + // + + Connection->IgnoreNextDosProbe = FALSE; + + Connection->ReceivesWithoutAck = 0; + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + +} /* NbiSendDataAck */ + + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. We don't advance the + // send pointer, since it is the last frame of the session + // and we want it to stay the same in the case of resends. + // + + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + Header->Session.DataStreamType = NB_CMD_SESSION_END; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + NbiReferenceConnection (Connection, CREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEnd */ + + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame. Generally it is sent on a connection but we + are not tied to that, to allow us to respond to + session ends from unknown remotes. + +Arguments: + + RemoteAddress - The remote IPX address. + + LocalTarget - The local target of the remote. + + SessionEnd - The received session end frame. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = NULL; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, (PVOID)RemoteAddress, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->Session.ConnectionControlFlag = 0x00; + Header->Session.DataStreamType = NB_CMD_SESSION_END_ACK; + Header->Session.SourceConnectionId = SessionEnd->DestConnectionId; + Header->Session.DestConnectionId = SessionEnd->SourceConnectionId; + Header->Session.SendSequence = SessionEnd->ReceiveSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + if (SessionEnd->BytesReceived != 0) { // BUGBUG: Will this detect new netbios? + Header->Session.ReceiveSequence = SessionEnd->SendSequence + 1; + Header->Session.ReceiveSequenceMax = SessionEnd->SendSequence + 3; + } else { + Header->Session.ReceiveSequence = SessionEnd->SendSequence; + Header->Session.BytesReceived = 0; + } + + NbiReferenceDevice (Device, DREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEndAck */ + diff --git a/private/ntos/tdi/isnp/nb/isnnb.h b/private/ntos/tdi/isnp/nb/isnnb.h new file mode 100644 index 000000000..2d142e346 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/isnnb.h @@ -0,0 +1,787 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnnb.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +#define NB_MAXIMUM_MAC 40 + +#define NB_SOCKET 0x5504 + +#if defined(_PNP_POWER) +#define NB_NETBIOS_NAME_SIZE 16 + +#define LOCK_ACQUIRED TRUE +#define LOCK_NOT_ACQUIRED FALSE +#endif _PNP_POWER + +// +// Defined granularity of find name timeouts in milliseconds -- +// we make this the same as the spec'ed RIP gap to avoid +// flooding routers. +// + +#define FIND_NAME_GRANULARITY 55 + + +// +// Defines the number of milliseconds between expirations of the +// short and long timers. +// + +#define MILLISECONDS 10000 // number of NT time units in one + +#define SHORT_TIMER_DELTA 100 +#define LONG_TIMER_DELTA 2000 + + +// +// Convert a ushort netware order <-> machine order +// + +#define REORDER_USHORT(_Ushort) ((((_Ushort) & 0xff00) >> 8) | (((_Ushort) & 0x00ff) << 8)) + +// +// Convert a ulong netware order <-> machine order +// + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + + +#include <packon.h> + +// +// Definition of the IPX header. +// + +typedef struct _IPX_HEADER { + USHORT CheckSum; + UCHAR PacketLength[2]; + UCHAR TransportControl; + UCHAR PacketType; + UCHAR DestinationNetwork[4]; + UCHAR DestinationNode[6]; + USHORT DestinationSocket; + UCHAR SourceNetwork[4]; + UCHAR SourceNode[6]; + USHORT SourceSocket; +} IPX_HEADER, *PIPX_HEADER; + + +// +// Definition of the Netbios header for name frames. +// + +typedef struct _NB_NAME_FRAME { + union { + struct { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + }; + UCHAR RoutingInfo[32]; + }; + UCHAR NameTypeFlag; + UCHAR DataStreamType2; + UCHAR Name[16]; +} NB_NAME_FRAME, *PNB_NAME_FRAME; + +// +// Definition of the Netbios header for directed datagrams. +// + +typedef struct _NB_DATAGRAM { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR SourceName[16]; + UCHAR DestinationName[16]; +} NB_DATAGRAM, *PNB_DATAGRAM; + +// +// Definition of the Netbios header for a status query. +// + +typedef struct _NB_STATUS_QUERY { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR Padding[14]; +} NB_STATUS_QUERY, *PNB_STATUS_QUERY; + +// +// Definition of the Netbios header for a status response +// (this does not include the status buffer itself). +// + +typedef struct _NB_STATUS_RESPONSE { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; +} NB_STATUS_RESPONSE, *PNB_STATUS_RESPONSE; + + +// +// Definition of the general Netbios connectionless header. +// + +typedef struct _NB_CONNECTIONLESS { + IPX_HEADER IpxHeader; + union { + NB_NAME_FRAME NameFrame; + NB_DATAGRAM Datagram; + NB_STATUS_QUERY StatusQuery; + NB_STATUS_RESPONSE StatusResponse; + }; +} NB_CONNECTIONLESS, *PNB_CONNECTIONLESS; + + +// +// Definition of the Netbios session frame. +// + +typedef struct _NB_SESSION { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT SourceConnectionId; + USHORT DestConnectionId; + USHORT SendSequence; + USHORT TotalDataLength; + USHORT Offset; + USHORT DataLength; + USHORT ReceiveSequence; + union { + USHORT BytesReceived; + USHORT ReceiveSequenceMax; + }; +} NB_SESSION, *PNB_SESSION; + + +// +// Definition of the extra fields in a Netbios +// session frame for session init and session init +// ack. +// + +typedef struct _NB_SESSION_INIT { + UCHAR SourceName[16]; + UCHAR DestinationName[16]; + USHORT MaximumDataSize; + USHORT MaximumPacketTime; + USHORT StartTripTime; +} NB_SESSION_INIT, *PNB_SESSION_INIT; + + +// +// Definition of the general Netbios connection-oriented header. +// + +typedef struct _NB_CONNECTION { + IPX_HEADER IpxHeader; + NB_SESSION Session; +} NB_CONNECTION, *PNB_CONNECTION; + + +// +// Definition of a Netbios packet. +// + +typedef union _NB_FRAME { + NB_CONNECTIONLESS Connectionless; + NB_CONNECTION Connection; +} NB_FRAME, *PNB_FRAME; + +#include <packoff.h> + + +// +// Definitions for the DataStreamType field, with the +// format used shown in the comment afterward. +// + +#define NB_CMD_FIND_NAME 0x01 // NAME_FRAME +#define NB_CMD_NAME_RECOGNIZED 0x02 // NAME_FRAME +#define NB_CMD_ADD_NAME 0x03 // NAME_FRAME +#define NB_CMD_NAME_IN_USE 0x04 // NAME_FRAME +#define NB_CMD_DELETE_NAME 0x05 // NAME_FRAME +#define NB_CMD_SESSION_DATA 0x06 // SESSION +#define NB_CMD_SESSION_END 0x07 // SESSION +#define NB_CMD_SESSION_END_ACK 0x08 // SESSION +#define NB_CMD_STATUS_QUERY 0x09 // STATUS_QUERY +#define NB_CMD_STATUS_RESPONSE 0x0a // STATUS_RESPONSE +#define NB_CMD_DATAGRAM 0x0b // DATAGRAM +#define NB_CMD_BROADCAST_DATAGRAM 0x0c // BROADCAST_DATAGRAM + +#ifdef RSRC_TIMEOUT_DBG +#define NB_CMD_DEATH_PACKET 0x99 // +#endif // RSRC_TIMEOUT_DBG + +// +// Bit values in the NameTypeFlag of NB_NAME_FRAME frames. +// + +#define NB_NAME_UNIQUE 0x00 +#define NB_NAME_GROUP 0x80 +#define NB_NAME_USED 0x40 +#define NB_NAME_REGISTERED 0x04 +#define NB_NAME_DUPLICATED 0x02 +#define NB_NAME_DEREGISTERED 0x01 + +// +// Bit values in the ConnectionControlFlag. +// + +#define NB_CONTROL_SYSTEM 0x80 +#define NB_CONTROL_SEND_ACK 0x40 +#define NB_CONTROL_ATTENTION 0x20 +#define NB_CONTROL_EOM 0x10 +#define NB_CONTROL_RESEND 0x08 +#define NB_CONTROL_NEW_NB 0x01 + + + +#define NB_DEVICE_SIGNATURE 0x1401 +#if defined(_PNP_POWER) +#define NB_ADAPTER_ADDRESS_SIGNATURE 0x1403 +#endif _PNP_POWER +#define NB_ADDRESS_SIGNATURE 0x1404 +#define NB_ADDRESSFILE_SIGNATURE 0x1405 +#define NB_CONNECTION_SIGNATURE 0x1406 + + +// +// Useful in various places. +// +#if defined(_PNP_POWER) +extern IPX_LOCAL_TARGET BroadcastTarget; +#endif _PNP_POWER +extern UCHAR BroadcastAddress[6]; +extern UCHAR NetbiosBroadcastName[16]; + + +// +// Contains the default handler for each of the TDI event types +// that are supported. +// + +extern PVOID TdiDefaultHandlers[6]; + + +// +// Define a structure that can track lock acquire/release. +// + +typedef struct _NB_LOCK { + CTELock Lock; +#if DBG + ULONG LockAcquired; + UCHAR LastAcquireFile[8]; + ULONG LastAcquireLine; + UCHAR LastReleaseFile[8]; + ULONG LastReleaseLine; +#endif +} NB_LOCK, *PNB_LOCK; + + + +#if DBG + +extern ULONG NbiDebug; +extern ULONG NbiDebug2; +extern ULONG NbiMemoryDebug; + +#define NB_MEMORY_LOG_SIZE 128 +extern UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +extern PUCHAR NbiDebugMemoryLoc; +extern PUCHAR NbiDebugMemoryEnd; + +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +); + +#define NB_DEBUG(_Flag, _Print) { \ + if (NbiDebug & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#define NB_DEBUG2(_Flag, _Print) { \ + if (NbiDebug2 & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#else + +#define NB_DEBUG(_Flag, _Print) +#define NB_DEBUG2(_Flag, _Print) + +#endif + + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; +typedef struct _REQUEST_LIST_HEAD { + PREQUEST Head; // list is empty if this is NULL + PREQUEST Tail; // undefined if the list is empty. +} REQUEST_LIST_HEAD, *PREQUEST_LIST_HEAD; + + +// +// PREQUEST +// NbiAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define NbiAllocateRequest(_Device,_Irp) \ + (_Irp) + + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// NbiFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define NbiFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// REQUEST_SINGLE_LINKAGE( +// IN PREQUEST Request +// ); +// +// Used to access a single list linkage field in the request. +// + +#define REQUEST_SINGLE_LINKAGE(_Request) \ + (*((PREQUEST *)&((_Request)->Tail.Overlay.ListEntry.Flink))) + + +// +// ULONG +// REQUEST_REFCOUNT( +// IN PREQUEST Request +// ); +// +// Used to access a field in the request which can be used for +// the reference count, as long as it is on a REQUEST_LIST. +// + +#define REQUEST_REFCOUNT(_Request) \ + (*((PULONG)&((_Request)->Tail.Overlay.ListEntry.Blink))) + + +// +// VOID +// REQUEST_LIST_INSERT_TAIL( +// IN PREQUEST_LIST_HEAD Head, +// IN PREQUEST Entry +// ); +// +// Inserts a request into a single list linkage queue. +// + +#define REQUEST_LIST_INSERT_TAIL(_Head,_Entry) { \ + if ((_Head)->Head == NULL) { \ + (_Head)->Head = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } else { \ + REQUEST_SINGLE_LINKAGE((_Head)->Tail) = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } \ +} + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// NbiCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define NbiCompleteRequest(_Request) \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT) + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +// +// some utility macros. + +// Minimum of two +// +#define NB_MIN( _a , _b ) ( ( (_a) < (_b) ) ? (_a) : (_b) ) + +// +// Swap the _s1 and _s2 of Type _T +// + +#define NB_SWAP(_s1, _s2, _T) { \ + _T _temp; \ + _temp = (_s1); \ + (_s1) = (_s2); \ + (_s2) = _temp; \ +} + +#define NB_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + +// +// Define our own spinlock routines. +// + +#if DBG + +#define NB_GET_LOCK(_Lock, _LockHandle) { \ + CTEGetLock(&(_Lock)->Lock, _LockHandle); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK(_Lock, _LockHandle) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + CTEFreeLock(&(_Lock)->Lock, _LockHandle); \ +} + +#define NB_GET_LOCK_DPC(_Lock) { \ + ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK_DPC(_Lock) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock); \ +} + +#else + +#define NB_GET_LOCK(_Lock, _LockHandle) CTEGetLock(&(_Lock)->Lock, _LockHandle) +#define NB_FREE_LOCK(_Lock, _LockHandle) CTEFreeLock(&(_Lock)->Lock, _LockHandle) +#define NB_GET_LOCK_DPC(_Lock) ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock) +#define NB_FREE_LOCK_DPC(_Lock) ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock) + +#endif + + +#define NB_GET_CANCEL_LOCK( _LockHandle ) IoAcquireCancelSpinLock( _LockHandle ) + +#define NB_FREE_CANCEL_LOCK( _LockHandle ) IoReleaseCancelSpinLock( _LockHandle ) + + +// +// Routines to optimize for a uni-processor environment. +// + + +#define NB_INCREMENT(_Long, _Lock) InterlockedIncrement(_Long) +#define NB_DECREMENT(_Long, _Lock) InterlockedDecrement(_Long) + +#define NB_ADD_ULONG(_Pulong, _Ulong, _Lock) ExInterlockedAddUlong(_Pulong, _Ulong, &(_Lock)->Lock) + +#define NB_DEFINE_SYNC_CONTEXT(_SyncContext) +#define NB_BEGIN_SYNC(_SyncContext) +#define NB_END_SYNC(_SyncContext) + +#define NB_DEFINE_LOCK_HANDLE(_LockHandle) CTELockHandle _LockHandle; + +// +// BUGBUG: Make these be NB_XXX_LOCK_DPC calls -- then the definitions +// of the NB_SYNC_XXX_LOCK calls can be changed to not need _LockHandle +// and many of the functions won't need that as a parameter. +// + +#define NB_SYNC_GET_LOCK(_Lock, _LockHandle) NB_GET_LOCK(_Lock, _LockHandle) +#define NB_SYNC_FREE_LOCK(_Lock, _LockHandle) NB_FREE_LOCK(_Lock, _LockHandle) + +#define NB_REMOVE_HEAD_LIST(_Queue, _Lock) ExInterlockedRemoveHeadList(_Queue, &(_Lock)->Lock) +#define NB_LIST_WAS_EMPTY(_Queue, _OldHead) ((_OldHead) == NULL) +#define NB_INSERT_HEAD_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertHeadList(_Queue, _Entry, &(_Lock)->Lock) +#define NB_INSERT_TAIL_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertTailList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_POP_ENTRY_LIST(_Queue, _Lock) ExInterlockedPopEntryList(_Queue, &(_Lock)->Lock) +#define NB_PUSH_ENTRY_LIST(_Queue, _Entry, _Lock) ExInterlockedPushEntryList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_LOCK_HANDLE_PARAM(_LockHandle) , IN CTELockHandle _LockHandle +#define NB_LOCK_HANDLE_ARG(_LockHandle) , (_LockHandle) + +#define NB_SYNC_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + + +// +// This macro adds a ULONG to a LARGE_INTEGER (should be +// called with a spinlock held). +// + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger),(ULONG)(_Ulong)) + +#define NB_DEBUG_DEVICE 0x00000001 +#define NB_DEBUG_ADDRESS 0x00000004 +#define NB_DEBUG_SEND 0x00000008 +#define NB_DEBUG_RECEIVE 0x00000020 +#define NB_DEBUG_CONFIG 0x00000040 +#define NB_DEBUG_PACKET 0x00000080 +#define NB_DEBUG_BIND 0x00000200 +#define NB_DEBUG_ADDRESS_FRAME 0x00000400 +#define NB_DEBUG_CONNECTION 0x00000800 +#define NB_DEBUG_QUERY 0x00001000 +#define NB_DEBUG_DRIVER 0x00002000 +#define NB_DEBUG_CACHE 0x00004000 +#define NB_DEBUG_DATAGRAM 0x00008000 +#define NB_DEBUG_TIMER 0x00010000 +#define NB_DEBUG_SEND_WINDOW 0x00020000 + + + +// +// NB_GET_NBHDR_BUFF - gets the nb header in the packet. It is always the +// second buffer. +// +#define NB_GET_NBHDR_BUFF(Packet) (NDIS_BUFFER_LINKAGE((Packet)->Private.Head)) + + diff --git a/private/ntos/tdi/isnp/nb/mp/makefile b/private/ntos/tdi/isnp/nb/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/mp/sources b/private/ntos/tdi/isnp/nb/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/nb/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/nb/nbcount/makefile b/private/ntos/tdi/isnp/nb/nbcount/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/nbcount/nbcount.c b/private/ntos/tdi/isnp/nb/nbcount/nbcount.c new file mode 100644 index 000000000..74a656f16 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/nbcount.c @@ -0,0 +1,177 @@ +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Compatible Source Routing Daemon for Windows NT +* +* Module: ipx/route/ipxroute.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +***************************************************************************** +* +* Functional Description: +* +* +****************************************************************************/ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <process.h> +#include <ntstapi.h> +#include <sys/stropts.h> +#include <windows.h> +#include "errno.h" +#include "tdi.h" +#include "isnkrnl.h" + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + +HANDLE isnnbfd; +wchar_t isnnbname[] = L"\\Device\\NwlnkNb"; +char pgmname[] = "NBCOUNT"; + +/** **/ + +#define INVALID_HANDLE (HANDLE)(-1) + +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen); + +/*page************************************************************* + m a i n + + This is the main routine that gets executed when a NET START + happens. + + Arguments - None + + Returns - Nothing +********************************************************************/ +void _CRTAPI1 main(int argc, char **argv) +{ + UNICODE_STRING FileString; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + NB_ACTION_GET_COUNTS GetCounts; + int rc; + int i; + + /** Open the nwlnknb driver **/ + + RtlInitUnicodeString (&FileString, isnnbname); + + InitializeObjectAttributes( + &ObjectAttributes, + &FileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &isnnbfd, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_ALERT); + + if (!NT_SUCCESS(Status)) { + isnnbfd = INVALID_HANDLE; + printf("Could not open transport\n"); + } + + if (isnnbfd == INVALID_HANDLE) { + exit(1); + } + + rc = do_isnnbioctl(isnnbfd, (I_MIPX | 351), (char *)&GetCounts, sizeof(NB_ACTION_GET_COUNTS)); + if (rc == 0) { + + printf("NB NIC count: %d\n", GetCounts.MaximumNicId); + for (i = 1; i <= GetCounts.MaximumNicId; i++) { + printf("NIC %d: %d sessions\n", i, GetCounts.NicIdCounts[i]); + } + } +} + + +/*page*************************************************************** + d o _ i s n i p x i o c t l + + Do the equivalent of a stream ioctl to isnnb + + Arguments - fd = Handle to put on + cmd = Command to send + datap = Ptr to ctrl buffer + dlen = Ptr to len of data buffer + + Returns - 0 = OK + else = Error +********************************************************************/ +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen) +{ + NTSTATUS Status; + UCHAR buffer[300]; + PNWLINK_ACTION action; + IO_STATUS_BLOCK IoStatusBlock; + int rc; + + /** Fill out the structure **/ + + action = (PNWLINK_ACTION)buffer; + + action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; + action->OptionType = NWLINK_OPTION_CONTROL; + action->BufferLength = sizeof(ULONG) + dlen; + action->Option = cmd; + RtlMoveMemory(action->Data, datap, dlen); + + /** Issue the ioctl **/ + + Status = NtDeviceIoControlFile( + fd, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_TDI_ACTION, + NULL, + 0, + action, + FIELD_OFFSET(NWLINK_ACTION,Data) + dlen); + + if (Status != STATUS_SUCCESS) { + if (Status == STATUS_INVALID_PARAMETER) { + rc = ERANGE; + } else { + rc = EINVAL; + } + } else { + if (dlen > 0) { + RtlMoveMemory (datap, action->Data, dlen); + } + rc = 0; + } + + return rc; + +} + diff --git a/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc b/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc new file mode 100644 index 000000000..ada219b24 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc @@ -0,0 +1,11 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "NWLink Netbios Session Count Application" +#define VER_INTERNALNAME_STR "nbcount.exe" +#define VER_ORIGINALFILENAME_STR "nbcount.exe" + +#include "common.ver" diff --git a/private/ntos/tdi/isnp/nb/nbcount/sources b/private/ntos/tdi/isnp/nb/nbcount/sources new file mode 100644 index 000000000..f9dfe3561 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1993 Micro Computer Systems, Inc. + +!ENDIF + +MAJORCOMP=nwlink +MINORCOMP=nbcount + +TARGETNAME=nbcount +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=UMAPPL_NOLIB + +USE_CRTDLL=1 + +C_DEFINES=$(C_DEFINES) + +!IF 0 +INCLUDES=..\h;..\..\..\..\..\inc;..\..\..\..\inc;..\..\..\inc +!ELSE +INCLUDES=..\h;$(BASEDIR)\private\inc;$(BASEDIR)\private\ntos\inc;$(BASEDIR)\private\ntos\streams\inc +!ENDIF + +SOURCES= nbcount.c nbcount.rc + +UMTYPE=console +UMAPPL=$(TARGETNAME) +UMLIBS=$(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + diff --git a/private/ntos/tdi/isnp/nb/nbiprocs.h b/private/ntos/tdi/isnp/nb/nbiprocs.h new file mode 100644 index 000000000..dff399019 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbiprocs.h @@ -0,0 +1,1530 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbiprocs.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + CTEPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow CTEPrints that disappear when +// DBG is 0. +// + +#if DBG +#define NbiPrint0(fmt) DbgPrint(fmt) +#define NbiPrint1(fmt,v0) DbgPrint(fmt,v0) +#define NbiPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define NbiPrint0(fmt) +#define NbiPrint1(fmt,v0) +#define NbiPrint2(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + + +// +// Routines to log packets to a buffer. +// + +#if DBG +#define NB_PACKET_LOG 1 +#endif + +#ifdef NB_PACKET_LOG + +// +// The size of this is 64 bytes for easy display. +// + +typedef struct _NB_PACKET_LOG_ENTRY { + UCHAR SendReceive; + UCHAR TimeStamp[5]; // low 5 digits of tick count. + UCHAR DestMac[6]; + UCHAR SrcMac[6]; + UCHAR Length[2]; + IPX_HEADER NbiHeader; + UCHAR Data[14]; +} NB_PACKET_LOG_ENTRY, *PNB_PACKET_LOG_ENTRY; + +#define NB_PACKET_LOG_LENGTH 128 +extern ULONG NbiPacketLogDebug; +extern USHORT NbiPacketLogSocket; +EXTERNAL_LOCK(NbiPacketLogLock); +extern NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogLoc; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogEnd; + +// +// Bit fields in NbiPacketLogDebug +// + +#define NB_PACKET_LOG_RCV_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_RCV_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_RCV_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_RCV_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_RCV_SOCKET 0x0010 // All packets to NbiPacketLogSocket +#define NB_PACKET_LOG_RCV_ALL 0x0020 // All packets (even non-NB) + +#define NB_PACKET_LOG_SEND_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_SEND_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_SEND_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_SEND_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_SEND_SOCKET 0x0010 // All packets from NbiPacketLogSocket + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ); + +#define PACKET_LOG(_Bit) (NbiPacketLogDebug & (_Bit)) + +#else // NB_PACKET_LOG + +#define NbiLogPacket(_MacHeader,_Length,_NbiHeader,_Data) +#define PACKET_LOG(_Bit) 0 + +#endif // NB_PACKET_LOG + + +#if DBG + +#define NbiReferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefDevice (_Device) + +#define NbiDereferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefDevice (_Device) + + +#define NbiReferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddress (_Address) + +#define NbiReferenceAddressLock(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressLock (_Address) + +#define NbiDereferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFile (_AddressFile) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFileLock (_AddressFile) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddressFile (_AddressFile) + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + + +#define NbiReferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnection (_Connection) + +#define NbiReferenceConnectionLock(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionLock (_Connection) + +#define NbiReferenceConnectionSync(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionSync (_Connection) + +#define NbiDereferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefConnection (_Connection) + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + +#else // DBG + +#define NbiReferenceDevice(_Device, _Type) \ + InterlockedIncrement(&(_Device)->ReferenceCount) + +#define NbiDereferenceDevice(_Device, _Type) \ + NbiDerefDevice (_Device) + + + +#define NbiReferenceAddress(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiReferenceAddressLock(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiDereferenceAddress(_Address, _Type) \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + if ( !InterlockedDecrement(&(_AddressFile)->ReferenceCount )) { \ + NbiDestroyAddressFile (_AddressFile); \ + } + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) + + +#define NbiReferenceConnection(_Connection, _Type) { \ + (VOID)ExInterlockedAddUlong( \ + &(_Connection)->ReferenceCount, \ + 1, \ + &(_Connection)->DeviceLock->Lock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionLock(_Connection, _Type) { \ + ++(_Connection)->ReferenceCount; \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionSync(_Connection, _Type) { \ + (VOID)NB_ADD_ULONG( \ + &(_Connection)->ReferenceCount, \ + 1, \ + (_Connection)->DeviceLock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiDereferenceConnection(_Connection, _Type) { \ + CTELockHandle _LockHandle; \ + NB_GET_LOCK( (_Connection)->DeviceLock, &_LockHandle ); \ + if ( !(--(_Connection)->ReferenceCount) ) { \ + (_Connection)->ThreadsInHandleConnectionZero++; \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + NbiHandleConnectionZero (_Connection); \ + } else { \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + } \ +} + + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) + +#endif // DBG + + + +#if DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateTaggedMemory(_BytesNeeded,_Tag,_Description) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeTaggedMemory(_Memory,_BytesAllocated,_Tag,_Description) + +#else // DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateMemory(_BytesNeeded,_Tag,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeMemory(_Memory,_BytesAllocated,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + + +#endif // DBG + + +// +// Definition of the callback routine where an NdisTransferData +// call is not needed. +// + +typedef VOID +(*NB_CALLBACK_NO_TRANSFER) ( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + + +// +// This routine compares two node addresses. +// + +#define NB_NODE_EQUAL(_A,_B) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == *(UNALIGNED ULONG *)((PUCHAR)(_B))) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == *(UNALIGNED USHORT *)(((PUCHAR)(_B))+4))) + +// +// This routine checks if an address is the broadcast address. +// + +#define NB_NODE_BROADCAST(_A) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == 0xffffffff) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == 0xffff)) + + +// +// Definition of the routine to handler a particular minor +// code for an IOCTL_MJ_INTERNAL_DEVICE_CONTROL IRP. +// + +typedef NTSTATUS +(*NB_TDI_DISPATCH_ROUTINE) ( + IN PDEVICE Device, + IN PREQUEST Request + ); + + + +// +// Routines in action.c +// + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in address.c +// + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ); + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ); + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStartRegistration( + IN PADDRESS Address + ); + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ); + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ); + +#if DBG + +VOID +NbiRefAddress( + IN PADDRESS Address + ); + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ); + +#endif + +VOID +NbiDerefAddress( + IN PADDRESS Address + ); + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ); + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if DBG + +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ); + +#endif + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if !defined(_PNP_POWER) +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); +#endif !_PNP_POWER + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ); + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ); + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ); + +#if defined(_PNP_POWER) +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ); + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ); + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ); +#endif _PNP_POWER + + +// +// Routines in bind.c +// + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ); + +VOID +NbiUnbind( + IN PDEVICE Device + ); + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ); + +VOID +NbiLineDown( + IN USHORT NicId + ); + + +// +// Routines in cache.c +// + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +); + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ); + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ); + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ); + +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ); + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ); + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ); + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ); + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +// +// Routines in connect.c +// + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ); + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ); + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ); + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ); + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ); + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnectionSync( + IN PCONNECTION Connection + ); +#endif + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ); + + +// +// Routines in datagram.c +// + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ); + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ); + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ); + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in device.c +// + +VOID +NbiRefDevice( + IN PDEVICE Device + ); + +VOID +NbiDerefDevice( + IN PDEVICE Device + ); + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ); + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ); + + +// +// Routines in driver.c +// + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ); + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ); + +#if DBG + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ); + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ); + +#endif + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + + +// +// Routines in event.c +// + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in frame.c +// + +VOID +NbiSendNameFrame( + IN PADDRESS Address, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, +#if defined(_PNP_POWER) + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL +#else + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL +#endif _PNP_POWER + ); + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ); + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ); + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ); + + +// +// Routines in packet.c +// + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ); + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ); + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ); + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ); + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ); + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ); + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ); + +#if defined(_PNP_POWER) +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ); + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ); + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ); + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); +#else +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ); +#endif _PNP_POWER + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ); + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ); + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ); + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ); + + +// +// Routines in query.c +// + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ); + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ); + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ); + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + + +// +// Routines in receive.c +// + + +VOID +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + +VOID +NbiReceiveComplete( + IN USHORT NicId + ); + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in send.c +// + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ); + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ); + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ); + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ); + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ); + + +// +// Routines in session.c +// + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + +// +// Routines in timer.c +// + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ); + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ); + +#else + +#define NbiStopRetransmit(_Connection) \ + (_Connection)->Retransmit = 0; + +#define NbiStopWatchdog(_Connection) \ + (_Connection)->Watchdog = 0; + +#endif + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ); + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ); + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ); + diff --git a/private/ntos/tdi/isnp/nb/nbitypes.h b/private/ntos/tdi/isnp/nb/nbitypes.h new file mode 100644 index 000000000..604df5fb5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbitypes.h @@ -0,0 +1,1511 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbitypes.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// For find name requests, defines the current status (SR_FN.Status). +// + +typedef enum { + FNStatusNoResponse, // no response has been received + FNStatusResponseUnique, // response received, is a unique name + FNStatusResponseGroup // response received, is a group name +}; + +// +// Defines the results we can get from sending a series of find +// names to locate a netbios name. +// + +typedef enum _NETBIOS_NAME_RESULT { + NetbiosNameFound, // name was located + NetbiosNameNotFoundNormal, // name not found, no response received + NetbiosNameNotFoundWanDown // name not found, all lines were down +} NETBIOS_NAME_RESULT, *PNETBIOS_NAME_RESULT; + + +// +// Definition of the protocol reserved field of a send packet. +// + +typedef struct _NB_SEND_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN SendInProgress; // used in an NdisSend + UCHAR Type; // what to do on completion + BOOLEAN OwnedByConnection; // if this is a connection's one packet +#if defined(_PNP_POWER) + PVOID Reserved[SEND_RESERVED_COMMON_SIZE]; // used by ipx for even-padding and local target etc. +#else + PVOID Reserved[2]; // used by ipx for even-padding +#endif _PNP_POWER + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY WaitLinkage; // when waiting on other queues +#ifdef NB_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + union { + struct { + UCHAR NetbiosName[16]; // name being searched for + UCHAR StatusAndSentOnUpLine; // low nibble: look at FNStatusXXX enum + // high nibble: TRUE if while sending, found lan or up wan line + UCHAR RetryCount; // number of times sent + USHORT SendTime; // based on Device->FindNameTime +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // current nic id it is being sent on + USHORT MaximumNicId; // highest one it will be sent on +#endif !_PNP_POWER + struct _NETBIOS_CACHE * NewCache; // new cache entry for group names + } SR_FN; + struct { + struct _ADDRESS * Address; // that owns this packet, if one does + PREQUEST Request; // send datagram request + struct _ADDRESS_FILE * AddressFile; // that this send is on +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // non-zero for frames that go to all +#endif !_PNP_POWER + UCHAR NameTypeFlag; // save these two values for frames + UCHAR DataStreamType; // that need to be sent to all nic id's + } SR_NF; + struct { + PREQUEST DatagramRequest; // holds the passed-in request + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; // will be -1 for broadcast + struct _ADDRESS_FILE * AddressFile; // that the datagram was sent on + struct _NETBIOS_CACHE * Cache; // how to route to the netbios address + ULONG CurrentNetwork; // within the cache entry + } SR_DG; + struct { + struct _CONNECTION * Connection; // that this frame was sent on. + PREQUEST Request; // that this frame was sent for. + ULONG PacketLength; // total packet length. + BOOLEAN NoNdisBuffer; // none allocate for send + } SR_CO; + struct { + ULONG ActualBufferLength; // real length of allocated buffer. + } SR_AS; + } u; + PUCHAR Header; // points to the MAC/IPX/NB header + PNDIS_BUFFER HeaderBuffer; // the NDIS_BUFFER describing Header +} NB_SEND_RESERVED, *PNB_SEND_RESERVED; + +// +// Values for Type. +// + +#define SEND_TYPE_NAME_FRAME 1 +#define SEND_TYPE_SESSION_INIT 2 +#define SEND_TYPE_FIND_NAME 3 +#define SEND_TYPE_DATAGRAM 4 +#define SEND_TYPE_SESSION_NO_DATA 5 +#define SEND_TYPE_SESSION_DATA 6 +#define SEND_TYPE_STATUS_QUERY 7 +#define SEND_TYPE_STATUS_RESPONSE 8 + +#ifdef RSRC_TIMEOUT_DBG +#define SEND_TYPE_DEATH_PACKET 9 +#endif //RSRC_TIMEOUT_DBG + +// +// Macros to access StatusAndSentOnUpLine. +// + +#define NB_GET_SR_FN_STATUS(_Reserved) \ + ((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) + +#define NB_SET_SR_FN_STATUS(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) | (_Value)); + +#define NB_GET_SR_FN_SENT_ON_UP_LINE(_Reserved) \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) != 0) + +#define NB_SET_SR_FN_SENT_ON_UP_LINE(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) | ((_Value) << 4)); + + +// +// Definition of the protocol reserved field of a receive packet. +// + +typedef struct _NB_RECEIVE_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN TransferInProgress; // used in an NdisTransferData + UCHAR Type; // what to do on completion +#if defined(_PNP_POWER) + PVOID Pool; // send pool it was allocated from +#else + +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + +#endif _PNP_POWER + union { + struct { + struct _CONNECTION * Connection; // that the transfer is for + BOOLEAN EndOfMessage; // this was the last part of a message + BOOLEAN CompleteReceive; // receive should be completed + BOOLEAN NoNdisBuffer; // user's mdl chain was used + BOOLEAN PartialReceive; // (new nb) don't ack this packet + } RR_CO; + struct { + struct _NB_RECEIVE_BUFFER * ReceiveBuffer; // datagram receive buffer + } RR_DG; + struct { + PREQUEST Request; // for this request + } RR_AS; + } u; + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue +} NB_RECEIVE_RESERVED, *PNB_RECEIVE_RESERVED; + +// +// Values for Type. +// + +#define RECEIVE_TYPE_DATAGRAM 1 +#define RECEIVE_TYPE_DATA 2 +#define RECEIVE_TYPE_ADAPTER_STATUS 3 + + + +typedef struct _NB_RECEIVE_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this +#if defined(_PNP_POWER) + PVOID Pool; // receive buffer pool was allocated from +#else +#ifdef NB_TRACK_POOL + PVOID Pool; // receive buffer pool was allocated from +#endif +#endif _PNP_POWER + struct _ADDRESS * Address; // that the datagram is for + SINGLE_LIST_ENTRY PoolLinkage; // when in free pool + LIST_ENTRY WaitLinkage; // when in ReceiveDatagrams queue + PNDIS_BUFFER NdisBuffer; // describes the data + UCHAR RemoteName[16]; // datagram was received from + ULONG DataLength; // length for current one, not allocated + PUCHAR Data; // points to data to hold packet +} NB_RECEIVE_BUFFER, *PNB_RECEIVE_BUFFER; + + +// +// Types to abstract NDIS packets. This is to allow us to +// switch from using our own memory for packets to using +// authentically allocated NDIS packets. +// + +//#define NB_OWN_PACKETS 1 + +#ifdef NB_OWN_PACKETS + +#define NDIS_PACKET_SIZE 48 +// #define NDIS_PACKET_SIZE FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + +typedef struct _NB_SEND_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_SEND_RESERVED)]; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_RECEIVE_RESERVED)]; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_SEND_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((PNDIS_PACKET)((_Packet)->Data)) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_SendPacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiAllocateReceivePacket(_Device,_PoolHandle, _ReceivePacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_ReceivePacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiFreeSendPacket(_Device,_Packet) + +#define NbiFreeReceivePacket(_Device,_Packet) + + +#else // NB_OWN_PACKETS + +typedef struct _NB_SEND_PACKET { + PNDIS_PACKET Packet; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + PNDIS_PACKET Packet; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_PACKET_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((_Packet)->Packet) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisAllocatePacket(_Status, &(_SendPacket)->Packet, _PoolHandle); \ +} + +#define NbiAllocateReceivePacket(_Device, _PoolHandle, _ReceivePacket,_Status) { \ + NdisAllocatePacket(_Status, &(_ReceivePacket)->Packet, _PoolHandle); \ +} + +#define NbiFreeSendPacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#define NbiFreeReceivePacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#endif // NB_OWN_PACKETS + +#define SEND_RESERVED(_Packet) ((PNB_SEND_RESERVED)((PACKET(_Packet))->ProtocolReserved)) +#define RECEIVE_RESERVED(_Packet) ((PNB_RECEIVE_RESERVED)((PACKET(_Packet))->ProtocolReserved)) + + + +typedef struct _NB_RECEIVE_BUFFER_POOL { + LIST_ENTRY Linkage; + UINT BufferCount; + UINT BufferFree; +#if defined(_PNP_POWER) + UINT BufferDataSize; // allocation size of each buffer data +#endif _PNP_POWER + NB_RECEIVE_BUFFER Buffers[1]; + // after the packets the data buffers are allocated also. +} NB_RECEIVE_BUFFER_POOL, *PNB_RECEIVE_BUFFER_POOL; + + +// +// Tags for memory allocation. +// + +#define MEMORY_CONFIG 0 +#define MEMORY_ADAPTER 1 +#define MEMORY_ADDRESS 2 +#define MEMORY_PACKET 3 +#define MEMORY_CACHE 4 +#define MEMORY_CONNECTION 5 +#define MEMORY_STATUS 6 +#define MEMORY_QUERY 7 +#if defined(_PNP_POWER) +#define MEMORY_WORK_ITEM 8 +#define MEMORY_ADAPTER_ADDRESS 9 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +#define MEMORY_MAX 10 +#else +#define MEMORY_MAX 8 +#endif _PNP_POWER + +#if DBG + +// +// Holds the allocations for a specific memory type. +// + +typedef struct _MEMORY_TAG { + ULONG Tag; + ULONG BytesAllocated; +} MEMORY_TAG, *PMEMORY_TAG; + +EXTERNAL_LOCK(NbiMemoryInterlock); +extern MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif + + + +// +// This structure holds a single remote network which a +// Netbios name exists on. +// + +typedef struct _NETBIOS_NETWORK { + ULONG Network; + IPX_LOCAL_TARGET LocalTarget; +} NETBIOS_NETWORK, *PNETBIOS_NETWORK; + +// +// This defines a netbios cache entry for a given name. +// + +typedef struct _NETBIOS_CACHE { + UCHAR NetbiosName[16]; + BOOLEAN Unique; + BOOLEAN FailedOnDownWan; // if NetworksUsed == 0, was it due to down wan lines? + USHORT TimeStamp; // in seconds - CacheTimeStamp when inserted + ULONG ReferenceCount; + LIST_ENTRY Linkage; + TDI_ADDRESS_IPX FirstResponse; + USHORT NetworksAllocated; + USHORT NetworksUsed; + NETBIOS_NETWORK Networks[1]; // may be more than one of these +} NETBIOS_CACHE, *PNETBIOS_CACHE; + +typedef struct _NETBIOS_CACHE_TABLE { + USHORT MaxHashIndex; + USHORT CurrentEntries; + LIST_ENTRY Bucket[1]; +} NETBIOS_CACHE_TABLE, *PNETBIOS_CACHE_TABLE; + +#define NB_NETBIOS_CACHE_TABLE_LARGE 26 // for server +#define NB_NETBIOS_CACHE_TABLE_SMALL 8 // for workstation +#define NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET 8 + +// +// This defines the different kind of requests that can be made +// to CacheFindName(). +// + +typedef enum _FIND_NAME_TYPE { + FindNameConnect, + FindNameNetbiosFindName, + FindNameOther +} FIND_NAME_TYPE, *PFIND_NAME_TYPE; + + +// +// The number of hash entries in the non-inactive connection +// database. +// + +#define CONNECTION_HASH_COUNT 8 + +// +// Mask and shift to retrieve the hash number from a connection +// ID. +// + +#define CONNECTION_HASH_MASK 0xe000 +#define CONNECTION_HASH_SHIFT 13 + +// +// The maximum connection ID we can assign, not counting the +// shifted-over hash id (which occupies the top 3 bits of the +// real id we use on the wire). We can use all the bits except +// the top one, to prevent an ID of 0xffff being used. +// + +#define CONNECTION_MAXIMUM_ID (USHORT)(~CONNECTION_HASH_MASK & ~1) + +// +// A single connection hash bucket. +// + +typedef struct _CONNECTION_HASH { + struct _CONNECTION * Connections; + USHORT ConnectionCount; + USHORT NextConnectionId; +} CONNECTION_HASH, *PCONNECTION_HASH; + + +// +// These are queued in the ConnectIndicationInProgress +// queue to track indications to TDI clients. +// + +typedef struct _CONNECT_INDICATION { + LIST_ENTRY Linkage; + UCHAR NetbiosName[16]; + TDI_ADDRESS_IPX RemoteAddress; + USHORT ConnectionId; +} CONNECT_INDICATION, *PCONNECT_INDICATION; + +// +// This structure defines the per-device structure for NB +// (one of these is allocated globally). +// + +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_CONNECTION 4 +#define DREF_FN_TIMER 5 +#define DREF_FIND_NAME 6 +#define DREF_SESSION_INIT 7 +#define DREF_NAME_FRAME 8 +#define DREF_FRAME 9 +#define DREF_SHORT_TIMER 10 +#define DREF_LONG_TIMER 11 +#define DREF_STATUS_QUERY 12 +#define DREF_STATUS_RESPONSE 13 +#define DREF_STATUS_FRAME 14 +#define DREF_NB_FIND_NAME 15 + +#define DREF_TOTAL 16 + +typedef struct _DEVICE { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + +#if DBG + ULONG RefTypes[DREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IDC1" +#endif + + NB_LOCK Interlock; // GLOBAL lock for reference count. + // (used in ExInterlockedXxx calls) + NB_LOCK Lock; + LONG ReferenceCount; // activity count/this provider. + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; +#if defined(_PNP_POWER) + USHORT DeviceNameLength; +#else + ULONG DeviceNameLength; +#endif _PNP_POWER + + LIST_ENTRY GlobalSendPacketList; + LIST_ENTRY GlobalReceivePacketList; + LIST_ENTRY GlobalReceiveBufferList; + + // + // All send packet pools are chained on this list. + // + + LIST_ENTRY SendPoolList; + LIST_ENTRY ReceivePoolList; + LIST_ENTRY ReceiveBufferPoolList; + + SLIST_HEADER SendPacketList; + SLIST_HEADER ReceivePacketList; + SINGLE_LIST_ENTRY ReceiveBufferList; + + // + // Receive requests waiting to be completed. + // + + LIST_ENTRY ReceiveCompletionQueue; + + // + // Connections waiting for send packets. + // + + LIST_ENTRY WaitPacketConnections; + + // + // Connections waiting to packetize. + // + + LIST_ENTRY PacketizeConnections; + + // + // Connections waiting to send a data ack. + // + + LIST_ENTRY DataAckConnections; + + // + // The list changed while we were processing it. + // + + BOOLEAN DataAckQueueChanged; + + // + // Information to manage the Netbios name cache. + // + + LIST_ENTRY WaitingConnects; // connect requests waiting for a name + LIST_ENTRY WaitingDatagrams; // datagram requests waiting for a name + LIST_ENTRY WaitingAdapterStatus; // adapter status requests waiting for a name + LIST_ENTRY WaitingNetbiosFindName; // netbios find name requests waiting for a name + + // + // Holds adapter status request which have a name and + // are waiting for a response. The long timeout aborts + // these after a couple of expirations (BUGBUG: currently we + // do not do resends). + // + + LIST_ENTRY ActiveAdapterStatus; + + // + // Receive datagrams waiting to be indicated. + // + + LIST_ENTRY ReceiveDatagrams; + + // + // In-progress connect indications (used to make + // sure we don't indicate the same packet twice). + // + + LIST_ENTRY ConnectIndicationInProgress; + + // + // Listens that have been posted to connections. + // + + LIST_ENTRY ListenQueue; + + UCHAR State; + + // + // The following fields control the timer system. + // The short timer is used for retransmission and + // delayed acks, and the long timer is used for + // watchdog timeouts. + // + BOOLEAN ShortListActive; // ShortList is not empty. + BOOLEAN DataAckActive; // DataAckConnections is not empty. + BOOLEAN TimersInitialized; // has the timer system been initialized. + BOOLEAN ProcessingShortTimer; // TRUE if we are in ScanShortTimer. +#if defined(_PNP_POWER) + BOOLEAN LongTimerRunning; // True if the long timer is running. +#endif _PNP_POWER + LARGE_INTEGER ShortTimerStart; // tick count when the short timer was set. + CTETimer ShortTimer; // controls the short timer. + ULONG ShortAbsoluteTime; // up-count timer ticks, short timer. + CTETimer LongTimer; // kernel DPC object, long timer. + ULONG LongAbsoluteTime; // up-count timer ticks, long timer. + NB_LOCK TimerLock; // lock for following timer queues + LIST_ENTRY ShortList; // list of waiting connections + LIST_ENTRY LongList; // list of waiting connections + + + // + // Hash table of non-inactive connections. + // + + CONNECTION_HASH ConnectionHash[CONNECTION_HASH_COUNT]; + + // + // Control the queue of waiting find names. + // + + USHORT FindNameTime; // incremented each time the timer runs + BOOLEAN FindNameTimerActive; // TRUE if the timer is queued + CTETimer FindNameTimer; // runs every FIND_NAME_GRANULARITY + ULONG FindNameTimeout; // the retry count in timer ticks + + ULONG FindNamePacketCount; // Count of packets on the queue + LIST_ENTRY WaitingFindNames; // FIND_NAME frames waiting to go out + + // + // The cache of NETBIOS_CACHE entries. + // + + PNETBIOS_CACHE_TABLE NameCache; + + // + // The current time stamp, incremented every second. + // + + USHORT CacheTimeStamp; + + // + // Maximum valid NIC ID we can use. + // + + USHORT MaximumNicId; + + + // + // Handle for our binding to the IPX driver. + // + + HANDLE BindHandle; + + // + // Holds the output from binding to IPX. + // + union { + IPX_INTERNAL_BIND_OUTPUT Bind; + IPX_INTERNAL_BIND_INPUT BindInput; + }; + + // + // Holds our reserved netbios name, which is 10 bytes + // of zeros followed by our node address. + // +#if !defined(_PNP_POWER) + UCHAR ReservedNetbiosName[16]; +#endif !_PNP_POWER + + // + // This holds the total memory allocated for the above structures. + // + + LONG MemoryUsage; + LONG MemoryLimit; + + // + // How many packets have been allocated. + // + + ULONG AllocatedSendPackets; + ULONG AllocatedReceivePackets; + ULONG AllocatedReceiveBuffers; + +#if defined(_PNP_POWER) + // + // This is the size of each buffer in the receive buffer pool. + // We reallocate buffer pool when the LineInfo.MaxPacketSize changes(increases) + // from IPX because of a new adapter. The LineInfo.MaxPacketSize could + // also change(decrease) when a adapter disappears but our buffer pool size + // will stay at this value. + // + ULONG CurMaxReceiveBufferSize; +#endif _PNP_POWER + + // + // Other configuration parameters. + // + + ULONG AckDelayTime; // converted to short timeouts, rounded up + ULONG AckWindow; + ULONG AckWindowThreshold; + ULONG EnablePiggyBackAck; + ULONG Extensions; + ULONG RcvWindowMax; + ULONG BroadcastCount; + ULONG BroadcastTimeout; + ULONG ConnectionCount; + ULONG ConnectionTimeout; + ULONG InitPackets; + ULONG MaxPackets; + ULONG InitialRetransmissionTime; + ULONG Internet; + ULONG KeepAliveCount; + ULONG KeepAliveTimeout; + ULONG RetransmitMax; + ULONG RouterMtu; + + ULONG MaxReceiveBuffers; + + + // + // Where we tell upper drivers to put their headers. + // + + ULONG IncludedHeaderOffset; + + // + // The following field is a head of a list of ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabase; // list of defined transport addresses. +#if defined(_PNP_POWER) + LIST_ENTRY AdapterAddressDatabase; // list of netbios names made from adapter addresses. +#endif _PNP_POWER + + ULONG AddressCount; // number of addresses in the database. + + NDIS_HANDLE NdisBufferPoolHandle; + +#if DBG + UCHAR Signature2[4]; // contains "IDC2" +#endif + + // + // This structure holds a pre-built IPX header which is used + // to quickly fill in common fields of outgoing connectionless + // frames. + // + + IPX_HEADER ConnectionlessHeader; + + // + // This event is used when unloading to signal that + // the reference count is now 0. + // + + KEVENT UnloadEvent; + BOOLEAN UnloadWaiting; + +#if defined(_PNP_POWER) + HANDLE TdiRegistrationHandle; +#endif _PNP_POWER + + // + // Counters for most of the statistics that NB maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + // + // These are "temporary" versions of the other counters. + // During normal operations we update these, then during + // the short timer expiration we update the real ones. + // + + ULONG TempFrameBytesSent; + ULONG TempFramesSent; + ULONG TempFrameBytesReceived; + ULONG TempFramesReceived; + + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + USHORT ControlChannelIdentifier; + + // + // Counters for "active" time. + // + + LARGE_INTEGER NbiStartTime; + + // + // This array is used to quickly dismiss connectionless frames + // that are not destined for us. The count is the number + // of addresses with that first letter that are registered + // on this device. + // + + UCHAR AddressCounts[256]; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + +} DEVICE, * PDEVICE; + + +extern PDEVICE NbiDevice; +EXTERNAL_LOCK(NbiGlobalPoolInterlock); + +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// + +EXTERNAL_LOCK(NbiGlobalInterlock); + + +// +// device state definitions +// +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +#define NB_TDI_RESOURCES 9 + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. +// + +#define AFREF_CREATE 0 +#define AFREF_RCV_DGRAM 1 +#define AFREF_SEND_DGRAM 2 +#define AFREF_VERIFY 3 +#define AFREF_INDICATION 4 +#define AFREF_TIMEOUT 5 +#define AFREF_CONNECTION 6 + +#define AFREF_TOTAL 8 + +typedef struct _ADDRESS_FILE { + +#if DBG + ULONG RefTypes[AFREF_TOTAL]; +#endif + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + ULONG ReferenceCount; // number of references to this object. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + PNB_LOCK AddressLock; + + // + // The following fields are kept for housekeeping purposes. + // + + PREQUEST OpenRequest; // the request used for open + struct _ADDRESS *Address; // address to which we are bound. +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + struct _DEVICE *Device; // device to which we are attached. + + LIST_ENTRY ConnectionDatabase; // associated with this address. + + LIST_ENTRY ReceiveDatagramQueue; // posted by the client. + + // + // This holds the request used to close this address file, + // for pended completion. + // + + PREQUEST CloseRequest; + + // + // Handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + BOOLEAN RegisteredHandler[6]; + + // + // This is a list of handlers for a given event. They can be + // accessed using the explicit names for type-checking, or the + // array (indexed by the event type) for speed. + // + + union { + struct { + PTDI_IND_CONNECT ConnectionHandler; + PTDI_IND_DISCONNECT DisconnectHandler; + PTDI_IND_ERROR ErrorHandler; + PTDI_IND_RECEIVE ReceiveHandler; + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PTDI_IND_RECEIVE_EXPEDITED ExpeditedDataHandler; + }; + PVOID Handlers[6]; + }; + + PVOID HandlerContexts[6]; + +} ADDRESS_FILE, *PADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines a NETBIOS name as a character array for use when +// passing preformatted NETBIOS names between internal routines. It is +// not a part of the external interface to the transport provider. +// + +typedef struct _NBI_NETBIOS_ADDRESS { + UCHAR NetbiosName[16]; + USHORT NetbiosNameType; + BOOLEAN Broadcast; +} NBI_NETBIOS_ADDRESS, *PNBI_NETBIOS_ADDRESS; + +// +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#define AREF_ADDRESS_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 +#define AREF_NAME_FRAME 3 +#define AREF_TIMER 4 +#define AREF_FIND 5 + +#define AREF_TOTAL 8 + + +typedef struct _ADDRESS { + +#if DBG + ULONG RefTypes[AREF_TOTAL]; +#endif + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + ULONG ReferenceCount; // number of references to this object. + + NB_LOCK Lock; + + // + // The following fields comprise the actual address itself. + // + + PREQUEST Request; // pointer to address creation request. + + UCHAR NameTypeFlag; // NB_NAME_UNIQUE or NB_NAME_GROUP + + NBI_NETBIOS_ADDRESS NetbiosAddress; // our netbios name. + + // + // The following fields are used to maintain state about this address. + // + + ULONG Flags; // attributes of the address. + ULONG State; // current state of the address. + struct _DEVICE *Device; // device context to which we are attached. + PNB_LOCK DeviceLock; + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + UCHAR SendPacketHeader[NB_MAXIMUM_MAC + sizeof(IPX_HEADER)]; + + // + // This timer is used for registering the name. + // + + CTETimer RegistrationTimer; + + // + // Number of times an add name frame has been sent. + // + + ULONG RegistrationCount; + +#ifdef ISN_NT + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying NbiDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + +#endif + +} ADDRESS, *PADDRESS; + +// +// Values for Flags +// + +#define ADDRESS_FLAGS_DUPLICATE_NAME 0x00000002 +#if defined(_PNP_POWER) +#define ADDRESS_FLAGS_CONFLICT 0x00000010 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// this booleans are passed to nbiverifyaddressfile calls. +// +#define CONFLICT_IS_OK TRUE +#define CONFLICT_IS_NOT_OK FALSE +#endif _PNP_POWER + +// +// Values for State +// + +#define ADDRESS_STATE_REGISTERING 1 +#define ADDRESS_STATE_OPEN 2 +#define ADDRESS_STATE_STOPPING 3 + +#if defined(_PNP_POWER) +// +// This holds the adapters names i.e netbios names which are +// created from adater node address to support adapter status +// queries using adapter node addresses. +// +typedef struct _ADAPTER_ADDRESS { + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + NIC_HANDLE NicHandle; // NicHandle corresponding to this address. + UCHAR NetbiosName[16]; +} ADAPTER_ADDRESS, *PADAPTER_ADDRESS; +#endif _PNP_POWER + +// +// This defines the types of probe packets we can send. +// + +typedef enum _NB_ACK_TYPE { + NbiAckQuery, + NbiAckResponse, + NbiAckResend +} NB_ACK_TYPE, *PNB_ACK_TYPE; + + +// +// This defines the a packetizing location in a +// send. +// + +typedef struct _SEND_POINTER { + ULONG MessageOffset; // up count, bytes sent this message. + PREQUEST Request; // current send request in chain. + PNDIS_BUFFER Buffer; // current buffer in send chain. + ULONG BufferOffset; // current byte offset in current buffer. + USHORT SendSequence; +} SEND_POINTER, *PSEND_POINTER; + +// +// This defines the current location in a receive. +// + +typedef struct _RECEIVE_POINTER { + ULONG MessageOffset; // up count, bytes received this message. + ULONG Offset; // up count, bytes received this request. + PNDIS_BUFFER Buffer; // current buffer in receive request. + ULONG BufferOffset; // current byte offset in current buffer. +} RECEIVE_POINTER, *PRECEIVE_POINTER; + + +// +// This structure defines a connection, which controls a +// session with a remote. +// + +#define CREF_VERIFY 0 +#define CREF_LISTEN 1 +#define CREF_CONNECT 2 +#define CREF_WAIT_CACHE 3 +#define CREF_TIMER 4 +#define CREF_INDICATE 5 +#define CREF_ACTIVE 6 +#define CREF_FRAME 7 +#define CREF_BY_CONTEXT 8 +#define CREF_W_ACCEPT 9 +#define CREF_SEND 10 +#define CREF_RECEIVE 11 +#define CREF_PACKETIZE 12 +#define CREF_DISASSOC 13 +#define CREF_W_PACKET 14 +#define CREF_CANCEL 15 +#define CREF_NDIS_SEND 16 +#define CREF_SHORT_D_ACK 17 +#define CREF_LONG_D_ACK 18 +#define CREF_FIND_ROUTE 19 +#define CREF_ACCEPT 20 + +#define CREF_TOTAL 24 + +typedef struct _CONNECTION { + +#if DBG + ULONG RefTypes[CREF_TOTAL]; +#endif + + CSHORT Type; + USHORT Size; + + NB_LOCK Lock; + PNB_LOCK DeviceLock; + + ULONG ReferenceCount; // number of references to this object. + + CONNECTION_CONTEXT Context; // client-specified value. + + ULONG State; + ULONG SubState; + ULONG ReceiveState; // SubState tracks sends when active. + ULONG NewNetbios; // 1 if we negotiated this. + + REQUEST_LIST_HEAD SendQueue; + REQUEST_LIST_HEAD ReceiveQueue; + + USHORT ReceiveSequence; + + USHORT LocalRcvSequenceMax; // we advertise to him (will be near SendSequence) + USHORT RemoteRcvSequenceMax; // he advertises to us (will be near ReceiveSequence) + USHORT SendWindowSequenceLimit; // when this send window ends (may send past it however) + + // + // RemoteRcvSequenceMax is the largest frame number that he expects to + // receive, while SendWindowSequenceLimit is one more than the max + // we can send. I.e. if he is advertising a window of 4 and we think + // the window should be 2, and the current send sequence is 7, + // RemoteRcvSequenceMax is 10 and SendWindowSequenceLimit is 9. + // + + USHORT ReceiveWindowSize; // when it is open, how big to make it + USHORT SendWindowSize; // what we'll send, may be less than what he advertises + USHORT MaxSendWindowSize; // maximum we allow it to grow to + + USHORT IncreaseWindowFailures; // how many windows after increase have had retransmits + BOOLEAN RetransmitThisWindow; // we had to retransmit in this send window + BOOLEAN SendWindowIncrease; // send window was just increased. + BOOLEAN ResponseTimeout; // we hit timeout in SEND_W or REMOTE_W + + BOOLEAN SendBufferInUse; // current send's already queued on packet + + ULONG Retries; + + // + // Tracks the current send. + // + + SEND_POINTER CurrentSend; + + // + // Tracks the unacked point in the send. + // + + SEND_POINTER UnAckedSend; + + PREQUEST FirstMessageRequest; // first one in the message. + PREQUEST LastMessageRequest; // last one in the message. + + ULONG CurrentMessageLength; // total length of current message. + + // + // Tracks the current receive. + // + + RECEIVE_POINTER CurrentReceive; // where to receive next data + RECEIVE_POINTER PreviousReceive; // stores it while transfer in progress + + PREQUEST ReceiveRequest; // current one; not in ReceiveQueue + ULONG ReceiveLength; // length of ReceiveRequest + + ULONG ReceiveUnaccepted; // by client...only indicate when == 0 + + ULONG CurrentIndicateOffset; // if previous frame was partially accepted. + + IPX_LINE_INFO LineInfo; // for the adapter this connect is on. + ULONG MaximumPacketSize; // as negotiated during session init/ack + + // + // Links us in the non-inactive connection hash bucket. + // + + struct _CONNECTION * NextConnection; + + // + // These are used to determine when to piggyback and when not to. + // + + BOOLEAN NoPiggybackHeuristic; // we have reason to assume it would be bad. + BOOLEAN PiggybackAckTimeout; // we got a timeout last time we tried. + ULONG ReceivesWithoutAck; // used to do an auto ack. + + // + // The following field is used as linkage in the device's + // PacketizeConnections queue. + // + + LIST_ENTRY PacketizeLinkage; + + // + // The following field is used as linkage in the device's + // WaitPacketConnections queue. + // + + LIST_ENTRY WaitPacketLinkage; + + // + // The following field is used as linkage in the device's + // DataAckConnections queue. + // + + LIST_ENTRY DataAckLinkage; + + // + // TRUE if we are on these queues. + // + + BOOLEAN OnPacketizeQueue; + BOOLEAN OnWaitPacketQueue; + BOOLEAN OnDataAckQueue; + + // + // TRUE if we have a piggyback ack pending. + // + + BOOLEAN DataAckPending; + + // + // TRUE if the current receive does not allow piggyback acks. + // + + BOOLEAN CurrentReceiveNoPiggyback; + + // + // Number of short timer expirations with the data ack queued. + // + + ULONG DataAckTimeouts; + + // + // Used to queue sends so that no two are outstanding at once. + // + + ULONG NdisSendsInProgress; + LIST_ENTRY NdisSendQueue; + + // + // This pointer is valid when NdisSendsInProgress is non-zero; + // it holds a pointer to a location on the stack of the thread + // which is inside NbiAssignSequenceAndSend. If this location + // is set to TRUE, it means the connection was stopped by another + // thread and a reference was added to keep the connection around. + // + + PBOOLEAN NdisSendReference; + + // + // These are used for timeouts. + // + + ULONG BaseRetransmitTimeout; // config # of short timeouts we wait. + ULONG CurrentRetransmitTimeout; // possibly backed-off number + ULONG WatchdogTimeout; // how many long timeouts we wait. + ULONG Retransmit; // timer; based on Device->ShortAbsoluteTime + ULONG Watchdog; // timer; based on Device->LongAbsoluteTime + USHORT TickCount; // 18.21/second, # for 576-byte packet. + USHORT HopCount; // As returned by ipx on find route. + BOOLEAN OnShortList; // are we inserted in the list + BOOLEAN OnLongList; // are we inserted in the list + LIST_ENTRY ShortList; // queues us on Device->ShortList + LIST_ENTRY LongList; // queues us on Device->LongList + + // + // These are valid when we have a connection established; + // + + USHORT LocalConnectionId; + USHORT RemoteConnectionId; + + PREQUEST DisassociatePending; // guarded by device lock. + PREQUEST ClosePending; + + PREQUEST ConnectRequest; + PREQUEST ListenRequest; + PREQUEST AcceptRequest; + PREQUEST DisconnectRequest; + PREQUEST DisconnectWaitRequest; + + ULONG CanBeDestroyed; // FALSE if reference is non-zero + ULONG ThreadsInHandleConnectionZero; // # of threads in HandleConnectionZero + + // + // These are used to hold extra data that was sent on a session + // init, for use in sending the ack. Generally will be NULL and 0. + // + + PUCHAR SessionInitAckData; + ULONG SessionInitAckDataLength; + + IPX_LOCAL_TARGET LocalTarget; // for the remote when active. + IPX_HEADER RemoteHeader; + + CTETimer Timer; + + PADDRESS_FILE AddressFile; // guarded by device lock if associated. + LIST_ENTRY AddressFileLinkage; // guarded by device lock + ULONG AddressFileLinked; // TRUE if queued using AddressFileLinkage + + PDEVICE Device; +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + + CHAR RemoteName[16]; // for an active connection. + + IPX_FIND_ROUTE_REQUEST FindRouteRequest; // use this to verify route. + + TDI_CONNECTION_INFO ConnectionInfo; // can be queried from above. + + BOOLEAN FindRouteInProgress; // we have a request pending. + + BOOLEAN SendPacketInUse; // put this here to align packet/header. + BOOLEAN IgnoreNextDosProbe; + + NTSTATUS Status; // status code for connection rundown. + +#ifdef RSRC_TIMEOUT_DBG + LARGE_INTEGER FirstMessageRequestTime; +#endif //RSRC_TIMEOUT_DBG + + NDIS_HANDLE SendPacketPoolHandle; // poolhandle for sendpacket below when + // the packet is allocated from ndis pool. + + NB_SEND_PACKET SendPacket; // try to use this first for sends + + ULONG Flags; // miscellaneous connection flags + + UCHAR SendPacketHeader[1]; // connection is extended to include this + + // + // NOTE: This is variable length structure! + // Do not add fields below this comment. + // +} CONNECTION, *PCONNECTION; + + +#define CONNECTION_STATE_INACTIVE 1 +#define CONNECTION_STATE_CONNECTING 2 +#define CONNECTION_STATE_LISTENING 3 +#define CONNECTION_STATE_ACTIVE 4 +#define CONNECTION_STATE_DISCONNECT 5 +#define CONNECTION_STATE_CLOSING 6 + + +#define CONNECTION_SUBSTATE_L_WAITING 1 // queued by a listen +#define CONNECTION_SUBSTATE_L_W_ACCEPT 2 // waiting for user to accept +#define CONNECTION_SUBSTATE_L_W_ROUTE 3 // waiting for rip response + +#define CONNECTION_SUBSTATE_C_FIND_NAME 1 // waiting for cache response +#define CONNECTION_SUBSTATE_C_W_ACK 2 // waiting for session init ack +#define CONNECTION_SUBSTATE_C_W_ROUTE 3 // waiting for rip response +#define CONNECTION_SUBSTATE_C_DISCONN 4 // disconnect was issued + +#define CONNECTION_SUBSTATE_A_IDLE 1 // no sends in progress +#define CONNECTION_SUBSTATE_A_PACKETIZE 2 // packetizing a send +#define CONNECTION_SUBSTATE_A_W_ACK 3 // waiting for an ack +#define CONNECTION_SUBSTATE_A_W_PACKET 4 // waiting for a packet +#define CONNECTION_SUBSTATE_A_W_EOR 5 // waiting for eor to start packetizing +#define CONNECTION_SUBSTATE_A_W_PROBE 6 // waiting for a keep-alive response +#define CONNECTION_SUBSTATE_A_REMOTE_W 7 // remote shut down our window + +#define CONNECTION_RECEIVE_IDLE 1 // no receives queued +#define CONNECTION_RECEIVE_ACTIVE 2 // receive is queued +#define CONNECTION_RECEIVE_W_RCV 3 // waiting for receive to be posted +#define CONNECTION_RECEIVE_INDICATE 4 // indication in progress +#define CONNECTION_RECEIVE_TRANSFER 5 // transfer is in progress +#define CONNECTION_RECEIVE_PENDING 6 // last request is queued for completion + +#define CONNECTION_SUBSTATE_D_W_ACK 1 +#define CONNECTION_SUBSTATE_D_GOT_ACK 2 + +// +// Bit values for Flags field in +// the CONNECTION structure. +// +#define CONNECTION_FLAGS_AUTOCONNECTING 0x00000001 // RAS autodial in progress +#define CONNECTION_FLAGS_AUTOCONNECTED 0x00000002 // RAS autodial connected + +#ifdef RSRC_TIMEOUT_DBG +extern ULONG NbiGlobalDebugResTimeout; +extern LARGE_INTEGER NbiGlobalMaxResTimeout; +extern NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +#endif //RSRC_TIMEOUT_DBG diff --git a/private/ntos/tdi/isnp/nb/nwlnknb.ini b/private/ntos/tdi/isnp/nb/nwlnknb.ini new file mode 100644 index 000000000..e2df88f54 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nwlnknb.ini @@ -0,0 +1,43 @@ +\Registry\Machine\System\CurrentControlSet\Services\NwlnkNb + Type = REG_DWORD 0x00000001 + Start = REG_DWORD 0x00000003 + ErrorControl = REG_DWORD 0x00000001 + ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwlnknb.sys + DisplayName = NWLINK2 IPX Netbios Protocol + Group = TDI + DependOnService = REG_MULTI_SZ "NwlnkIpx" + DependOnGroup = REG_MULTI_SZ "NDIS" + Linkage + Bind = REG_MULTI_SZ "\Device\NwlnkIpx" + Export = REG_MULTI_SZ "\Device\NwlnkNb" + Route = REG_MULTI_SZ ""NwlnkIpx"" + Disabled + Bind = REG_MULTI_SZ + Export = REG_MULTI_SZ + Route = REG_MULTI_SZ + Parameters + AckDelayTime = REG_DWORD 0x000000fa + AckWindow = REG_DWORD 0x00000002 + AckWindowThreshold = REG_DWORD 0x000001f4 + EnablePiggyBackAck = REG_DWORD 0x00000001 + Extensions = REG_DWORD 0x00000001 + RcvWindowMax = REG_DWORD 0x00000004 + BroadcastCount = REG_DWORD 0x00000003 + BroadcastTimeout = REG_DWORD 0x00000001 + ConnectionCount = REG_DWORD 0x00000005 + ConnectionTimeout = REG_DWORD 0x00000002 + InitPackets= REG_DWORD 0x00000005 + MaxPackets = REG_DWORD 0x0000001e + InitialRetransmissionTime = REG_DWORD 0x000001f4 + Internet = REG_DWORD 0x00000001 + KeepAliveCount = REG_DWORD 0x00000008 + KeepAliveTimeout = REG_DWORD 0x0000003c + RetransmitMax = REG_DWORD 0x00000008 + Performance + Library = Perfctrs.dll + Open = OpenNbfPerformanceData + Collect = CollectNbfPerformanceData + Close = CloseNbfPerformanceData +\Registry\Machine\System\CurrentControlSet\Services\EventLog\System\NwlnkNb + EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\netevent.dll + TypesSupported = REG_DWORD 0x00000007 diff --git a/private/ntos/tdi/isnp/nb/nwlnknb.rc b/private/ntos/tdi/isnp/nb/nwlnknb.rc new file mode 100644 index 000000000..1b0163cf3 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nwlnknb.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 IPX Netbios Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnknb.sys" +#define VER_ORIGINALFILENAME_STR "nwlnknb.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/nb/packet.c b/private/ntos/tdi/isnp/nb/packet.c new file mode 100644 index 000000000..7e22534a8 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/packet.c @@ -0,0 +1,1482 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the SEND_PACKET and + RECEIVE_PACKET objects, which describe NDIS packets used + by the transport. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Local Function Protos +// +#if defined(_PNP_POWER) +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ); +#endif _PNP_POWER + + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + + Header - Points to storage for the header. + + HeaderLength - The length of the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisNbBuffer; + PNB_SEND_RESERVED Reserved; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + NbiAllocateSendPacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } +// DbgPrint("NbiInitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + + // + // allocate the mac header. + // + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + Header, + MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisChainBufferAtFront (PACKET(Packet), NdisBuffer); + +// DbgPrint("NbiInitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + // + // Allocate the nb header + // + NdisAllocateBuffer( + &NdisStatus, + &NdisNbBuffer, + Device->NdisBufferPoolHandle, + Header + MacHeaderNeeded, + HeaderLength - MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + // DbgPrint("NbiInitializeSendPacket: IPX header address is (%x)\n", NdisNbBuffer); + NdisChainBufferAtBack (PACKET(Packet), NdisNbBuffer); + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->SendInProgress = FALSE; + Reserved->OwnedByConnection = FALSE; + Reserved->Header = Header; + Reserved->HeaderBuffer = NdisBuffer; + + Reserved->Reserved[0] = NULL; + Reserved->Reserved[1] = NULL; + + InsertHeadList( + &Device->GlobalSendPacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeSendPacket */ + + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + PNB_RECEIVE_RESERVED Reserved; + + NbiAllocateReceivePacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->TransferInProgress = FALSE; + + InsertHeadList( + &Device->GlobalReceivePacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceivePacket */ + + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a receive buffer by allocating + an NDIS_BUFFER to describe the data buffer. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer to initialize. + + DataBuffer - The data buffer. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + DataBuffer, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReceiveBuffer->NdisBuffer = NdisBuffer; + ReceiveBuffer->Data = DataBuffer; + ReceiveBuffer->DataLength = 0; + + InsertHeadList( + &Device->GlobalReceiveBufferList, + &ReceiveBuffer->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceiveBuffer */ + + + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a send packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + + HeaderLength - The length of the first buffer on the packet. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNB_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + CTEAssert(HeaderLength > MacHeaderNeeded); + Reserved = SEND_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Free the mac header + // + // DbgPrint("NbiDeinitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // Free the nb header + // + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: IPX header address is (%x)\n", NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, HeaderLength - MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // free the packet + // + NbiFreeSendPacket (Device, Packet); + +} /* NbiDeinitializeSendPacket */ + + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + PNB_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + Reserved = RECEIVE_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeReceivePacket (Device, Packet); + +} /* NbiDeinitializeReceivePacket */ + + + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine deinitializes a receive buffer. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer. + +Return Value: + + None. + + THIS ROUTINE SHOULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ + +{ +#if defined(_PNP_POWER) + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); +#else + CTELockHandle LockHandle; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER + + NdisFreeBuffer (ReceiveBuffer->NdisBuffer); + +} /* NbiDeinitializeReceiveBuffer */ + + + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PNB_SEND_PACKET Packet; + PNB_SEND_RESERVED Reserved; + PUCHAR Header; + ULONG HeaderLength; + NTSTATUS Status; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + SendPool = (PNB_SEND_POOL)NbiAllocateMemory (SendPoolSize, MEMORY_PACKET, "SendPool"); + if (SendPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + RtlZeroMemory (SendPool, SendPoolSize); + + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &SendPool->PoolHandle, Device->InitPackets, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( SendPool, SendPoolSize, MEMORY_PACKET, "Send Pool Freed"); + return; + } +#endif + + NB_DEBUG2 (PACKET, ("Initializing send pool %lx, %d packets, header %d\n", + SendPool, Device->InitPackets, HeaderLength)); + + Header = (PUCHAR)(&SendPool->Packets[Device->InitPackets]); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + + if (NbiInitializeSendPacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + SendPool->PoolHandle, +#endif + Packet, + Header, + HeaderLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->u.SR_NF.Address = NULL; +#ifdef NB_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + Header += HeaderLength; + + } + + SendPool->PacketCount = PacketNum; + SendPool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < SendPool->PacketCount; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + Device->AllocatedSendPackets += SendPool->PacketCount; + +} /* NbiAllocateSendPool */ + + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 5 receive packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_POOL ReceivePool; + UINT ReceivePoolSize; + UINT PacketNum; + PNB_RECEIVE_PACKET Packet; + PNB_RECEIVE_RESERVED Reserved; + NTSTATUS Status; + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + ReceivePool = (PNB_RECEIVE_POOL)NbiAllocateMemory (ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + if (ReceivePool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + RtlZeroMemory (ReceivePool, ReceivePoolSize); + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &ReceivePool->PoolHandle, Device->InitPackets, sizeof(NB_RECEIVE_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( ReceivePool, ReceivePoolSize, MEMORY_PACKET, "Receive Pool Freed"); + return; + } +#endif NB_OWN_PACKETS + + NB_DEBUG2 (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitPackets)); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + + if (NbiInitializeReceivePacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + ReceivePool->PoolHandle, +#endif + Packet) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(Packet); +#ifdef NB_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + } + + ReceivePool->PacketCount = PacketNum; + ReceivePool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < ReceivePool->PacketCount; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + Reserved = RECEIVE_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); +// PushEntryList (&Device->ReceivePacketList, &Reserved->PoolLinkage); + + } + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + Device->AllocatedReceivePackets += ReceivePool->PacketCount; + +} /* NbiAllocateReceivePool */ + + +#if defined(_PNP_POWER) + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + DataLength - Max length of the data in each buffer. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PUCHAR Data; + + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + + ReceiveBuffer->Pool = ReceiveBufferPool; + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + ReceiveBufferPool->BufferDataSize = DataLength; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + Device->CurMaxReceiveBufferSize = DataLength; + +} /* NbiAllocateReceiveBufferPool */ +#else + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + UINT DataLength; + PUCHAR Data; + + DataLength = Device->Bind.LineInfo.MaximumPacketSize; + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + +#ifdef NB_TRACK_POOL + ReceiveBuffer->Pool = ReceiveBufferPool; +#endif + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + +} /* NbiAllocateReceiveBufferPool */ +#endif _PNP_POWER + +#if defined(_PNP_POWER) + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ) + +/*++ + +Routine Description: + + This routines destroys all the existing Buffer Pools and creates + new one using the larger packet size given to us by IPX because + a new card was inserted with a larger packet size. + +Arguments: + + WorkItem - The work item that was allocated for this. + +Return Value: + + None. + +--*/ +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + + NB_GET_LOCK ( &Device->Lock, &LockHandle ); + + if ( Device->Bind.LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("Reallocating new pools due to new maxpacketsize\n"); +#endif + NbiDestroyReceiveBufferPools( Device ); + NbiAllocateReceiveBufferPool( Device, Device->Bind.LineInfo.MaximumPacketSize ); + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + NbiFreeMemory( WorkItem, sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buff Work Item freed"); +} + +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ) + +/*++ + +Routine Description: + + This routine frees the +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ +{ + PDEVICE Device = NbiDevice; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize,i; + + CTEAssert( ReceiveBufferPool->BufferDataSize ); + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (ReceiveBufferPool->BufferDataSize * Device->InitPackets); + + // + // Check if we can free this pool + // + CTEAssert(ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + RemoveEntryList( &ReceiveBufferPool->Linkage ); + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + +} + + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routines walks the ReceiveBufferPoolList and destroys the + pool which does not have any buffer in use. + +Arguments: + +Return Value: + + None. + + THIS ROUTINE COULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine is also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ +{ + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY Unused; + + + // + // Clean up this list before we call NbiFreeReceiveBufferPool bcoz that will + // simply destroy all the buffer which might be queue here on this list. + // At the end of this routine we must start with a fresh ReceiveBufferList. + // + do { + Unused = PopEntryList( &Device->ReceiveBufferList ); + } while( Unused ); + + // + // Now destroy each individual ReceiveBufferPool. + // + for ( p = Device->ReceiveBufferPoolList.Flink; + p != &Device->ReceiveBufferPoolList; + ) { + + + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + p = p->Flink; + + // + // This will destroy and unlink this Pool if none of its buffer is + // in use currently. + // + + if ( ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } else { + // + // When the device is stopping we must succeed in freeing the pool. + CTEAssert( Device->State != DEVICE_STATE_STOPPING ); + } + + } + +} + + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine returns the receive buffer back to the free list. + It checks the size of this buffer. If it is smaller than the + the CurMaxReceiveBufferSize, then it does not return this back + to the free list, instead it destroys it and possibly also + destroys the pool associated with it. O/w it simply returns this + to the free list. + +Arguments: + + ReceiveBuffer - Pointer to the buffer to be returned to the free list. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; +#if defined(DBG) + ULONG BufLen = 0; +#endif + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + +#if defined(DBG) + NdisQueryBuffer( ReceiveBuffer->NdisBuffer, NULL, &BufLen ); + CTEAssert( BufLen == ReceiveBufferPool->BufferDataSize ); +#endif + + // + // This is an old buffer which was in use when we changed + // the CurMaxReceiveBufferSize due to new adapter. We must not + // return this buffer back to free list. Infact, if the pool + // associated with this buffer does not have any other buffers + // in use, we should free the pool also. + CTEAssert( ReceiveBufferPool->BufferFree < ReceiveBufferPool->BufferCount ); + ReceiveBufferPool->BufferFree++; + + if ( ReceiveBufferPool->BufferDataSize < Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("ReceiveBuffer %lx, not returned to pool %lx( Free %d)\n", ReceiveBuffer, ReceiveBufferPool, ReceiveBufferPool->BufferFree); +#endif + + + if ( ReceiveBufferPool->BufferFree == ReceiveBufferPool->BufferCount ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } + } else { + + PushEntryList( &Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage ); + + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); +} +#endif _PNP_POWER + + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + + LockAcquired - TRUE if Device->Lock is acquired. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (!LockAcquired) { + NB_GET_LOCK (&Device->Lock, &LockHandle); + } + + if (Device->AllocatedSendPackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateSendPool (Device); + + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + return s; + } else { + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return NULL; + } + +} /* NbiPopSendPacket */ + + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine frees a packet back to the device context's pool. + If there are connections waiting for packets, it removes + one from the list and inserts it on the packetize queue. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // BUGBUG: Make this a function. Optimize for + // UP by not doing two checks? + // + + if (!IsListEmpty (&Device->WaitPacketConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = RemoveHeadList (&Device->WaitPacketConnections); + + // + // Take a connection off the WaitPacketQueue and put it + // on the PacketizeQueue. We don't worry about if the + // connection has stopped, that will get checked when + // the PacketizeQueue is run down. + // + // Since this is in send completion, we may not get + // a receive complete. We guard against this by calling + // NbiReceiveComplete from the long timer timeout. + // + + if (p != &Device->WaitPacketConnections) { + + Connection = CONTAINING_RECORD (p, CONNECTION, WaitPacketLinkage); + + CTEAssert (Connection->OnWaitPacketQueue); + Connection->OnWaitPacketQueue = FALSE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } + +} /* NbiPushSendPacket */ + + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine checks if a connection is on the wait packet + queue and if so takes it off and queues it to be packetized. + It is meant to be called when the connection's packet has + been freed. + +Arguments: + + Connection - The connection to check. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (Connection->OnWaitPacketQueue) { + + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + InsertTailList( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage); + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + return; + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiCheckForWaitPacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceivePackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceivePool (Device); + NB_FREE_LOCK (&Device->Lock, LockHandle); + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + + return s; + + } else { + + return NULL; + + } + +} /* NbiPopReceivePacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a receive buffer from the device context's pool. + If there are no buffers in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the buffer to. + +Return Value: + + The pointer to the Linkage field in the allocated receive buffer. + +--*/ + +{ +#if defined(_PNP_POWER) + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + CTELockHandle LockHandle; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + s = PopEntryList( &Device->ReceiveBufferList ); + + + if ( !s ) { + + // + // No buffer in the pool, see if we can allocate more. + // + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateReceiveBufferPool (Device, Device->CurMaxReceiveBufferSize ); + s = PopEntryList(&Device->ReceiveBufferList); + } + } + + if ( s ) { + + + // + // Decrement the BufferFree count on the corresponding ReceiveBufferPool. + // so that we know that + ReceiveBuffer = CONTAINING_RECORD( s, NB_RECEIVE_BUFFER, PoolLinkage ); + + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + + CTEAssert( ReceiveBufferPool->BufferFree && ( ReceiveBufferPool->BufferFree <= ReceiveBufferPool->BufferCount ) ); + CTEAssert( ReceiveBufferPool->BufferDataSize == Device->CurMaxReceiveBufferSize ); + + ReceiveBufferPool->BufferFree--; + + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; +#else + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntryList( + &Device->ReceiveBufferList, + &Device->Lock.Lock); + + if (s != NULL) { + return s; + } + + // + // No buffer in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceiveBufferPool (Device); + s = PopEntryList(&Device->ReceiveBufferList); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; + + } else { + + return NULL; + + } +#endif _PNP_POWER +} /* NbiPopReceiveBuffer */ + diff --git a/private/ntos/tdi/isnp/nb/precomp.h b/private/ntos/tdi/isnp/nb/precomp.h new file mode 100644 index 000000000..a024f2d3d --- /dev/null +++ b/private/ntos/tdi/isnp/nb/precomp.h @@ -0,0 +1,42 @@ +/*++ + +Copyright (c) 1993-1995 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompilation header file. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + +#include <ntos.h> +#include <tdikrnl.h> +#include <ndis.h> +#include <cxport.h> +#include <bind.h> +#include "isnnb.h" +#include "config.h" +#include "nbitypes.h" +#include "nbiprocs.h" +#include "zwapi.h" diff --git a/private/ntos/tdi/isnp/nb/query.c b/private/ntos/tdi/isnp/nb/query.c new file mode 100644 index 000000000..6ee33adf3 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/query.c @@ -0,0 +1,1817 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + query.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Remove the warning -- this is defined in windef also. +// + +#ifdef FAR +#undef FAR +#endif + +#include <windef.h> +#include <nb30.h> + + +// +// Useful macro to obtain the total length of a buffer chain. +// BUGBUG: Make this use NDIS macros. +// + +#define NbiGetBufferChainLength(Buffer, Length) { \ + PNDIS_BUFFER _Buffer = (Buffer); \ + *(Length) = 0; \ + while (_Buffer) { \ + *(Length) += MmGetMdlByteCount(_Buffer); \ + _Buffer = _Buffer->Next; \ + } \ +} + + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + The status of operation. + +--*/ + +{ + NTSTATUS Status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PCONNECTION Connection; + union { + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS NbiAddress; + } AddressInfo; + TA_NETBIOS_ADDRESS BroadcastAddress; + TDI_ADDRESS_IPX IpxAddress; + TDI_DATAGRAM_INFO DatagramInfo; + struct { + FIND_NAME_HEADER Header; + FIND_NAME_BUFFER Buffer; + } FindNameInfo; + } TempBuffer; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + PADAPTER_STATUS AdapterStatus; + BOOLEAN RemoteAdapterStatus; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + ULONG TargetBufferLength; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + ULONG ElementSize, TransportAddressSize; + PTRANSPORT_ADDRESS TransportAddress; + TA_ADDRESS UNALIGNED * CurAddress; + PNETBIOS_CACHE CacheName; + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + NTSTATUS QueryStatus; + CTELockHandle LockHandle; + PLIST_ENTRY p; + BOOLEAN UsedConnection; + UINT i; + + + // + // what type of status do we want? + // + + Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (Query->QueryType) { + + case TDI_QUERY_ADDRESS_INFO: + + // + // The caller wants the exact address value. + // + + if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = FALSE; + + } else if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE) { + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = TRUE; + + AddressFile = Connection->AddressFile; + + } else { + + Status = STATUS_INVALID_ADDRESS; + break; + + } + + Address = AddressFile->Address; + + NB_DEBUG2 (QUERY, ("Query address info on %lx\n", AddressFile)); + + TempBuffer.AddressInfo.ActivityCount = 0; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + if (CONTAINING_RECORD (p, ADDRESS_FILE, Linkage)->State == ADDRESSFILE_STATE_OPEN) { + ++TempBuffer.AddressInfo.ActivityCount; + } + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + TdiBuildNetbiosAddress( + AddressFile->Address->NetbiosAddress.NetbiosName, + (BOOLEAN)(AddressFile->Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempBuffer.AddressInfo.NbiAddress); + + Status = TdiCopyBufferToMdl( + &TempBuffer.AddressInfo, + 0, + sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + if (UsedConnection) { + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + } else { + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Status = STATUS_INVALID_CONNECTION; + + } else { + + // + // Assume 50 ms of delay for every hop after the + // first. The delay is returned as a negative number. + // + + if (Connection->HopCount > 1) { + Connection->ConnectionInfo.Delay.HighPart = (ULONG)-1; + Connection->ConnectionInfo.Delay.LowPart = + -((Connection->HopCount-1) * 50 * MILLISECONDS); + } else { + Connection->ConnectionInfo.Delay.HighPart = 0; + Connection->ConnectionInfo.Delay.LowPart = 0; + } + + // + // We have tick count; to convert to bytes/second we do: + // + // packet 576 bytes 18.21 ticks + // ---------------- * --------- * ----------- + // tick_count ticks packet seconds + // + // to get 10489/tick_count = bytes/second. We + // double this because routers tend to + // overestimate it. + // + // Since tick_count has such a low granularity, + // a tick count of 1 gives us a throughput of + // only 84 kbps, which is much too low. In + // that case we return twice the link speed + // which is in 100 bps units; that corresponds + // to about 1/6 of our bandwidth in bytes/sec. + // + + if (Connection->TickCount <= Connection->HopCount) { + + Connection->ConnectionInfo.Throughput.QuadPart = + UInt32x32To64 (Connection->LineInfo.LinkSpeed, 2); + + } else { + + Connection->ConnectionInfo.Throughput.HighPart = 0; + Connection->ConnectionInfo.Throughput.LowPart = + 20978 / (Connection->TickCount - Connection->HopCount); + + } + + Connection->ConnectionInfo.Unreliable = FALSE; + + Status = TdiCopyBufferToMdl ( + &Connection->ConnectionInfo, + 0, + sizeof(TDI_CONNECTION_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + break; + + case TDI_QUERY_PROVIDER_INFO: + + NB_DEBUG2 (QUERY, ("Query provider info\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Information, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + NB_DEBUG2 (QUERY, ("Query broadcast address\n")); + + TempBuffer.BroadcastAddress.TAAddressCount = 1; + TempBuffer.BroadcastAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + TempBuffer.BroadcastAddress.Address[0].AddressLength = 0; + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.BroadcastAddress, + 0L, + sizeof (TempBuffer.BroadcastAddress.TAAddressCount) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressType) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressLength), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + break; + + case TDI_QUERY_ADAPTER_STATUS: + + // + // Determine if this is a local or remote query. + // + + RemoteAdapterStatus = FALSE; + + if (Query->RequestConnectionInformation != NULL) { + + RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (RemoteAddress == NULL) { + return STATUS_BAD_NETWORK_PATH; + } + +#if defined(_PNP_POWER) + if ( !NbiFindAdapterAddress( + RemoteAddress->NetbiosName, + LOCK_NOT_ACQUIRED ) ) { + + RemoteAdapterStatus = TRUE; + } +#else + if (!RtlEqualMemory( + RemoteAddress->NetbiosName, + Device->ReservedNetbiosName, + 16)) { + + RemoteAdapterStatus = TRUE; + + } +#endif _PNP_POWER + + } + + if (RemoteAdapterStatus) { + + // + // See if we have this name cached. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this status + // request and processing will be resumed when + // we get a response. + // + // The status field in the request will hold + // the cache entry for the remote. The information + // field will hold the remote netbios name while + // it is in the WaitingAdapterStatus queue, and + // will hold a timeout value while we it is in + // the ActiveAdapterStatus queue. + // + + NB_DEBUG2 (QUERY, ("Queueing up adapter status %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + InsertTailList( + &Device->WaitingAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found adapter status cached %lx\n", Request)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + REQUEST_STATUS(Request) = (NTSTATUS)CacheName; + ++CacheName->ReferenceCount; + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = 0; + + InsertTailList( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiSendStatusQuery (Request); + + Status = STATUS_PENDING; + + } else { + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // Local adapter status. + // + + NbiGetBufferChainLength (REQUEST_NDIS_BUFFER(Request), &TargetBufferLength); + + Status = NbiStoreAdapterStatus( + TargetBufferLength, + 1, // NIC ID, was 0, changed to 1 for Bug #18026 + // because for NicId = 0, Ipx returns virtual + // address. Netbios uses that to register the + // name (00...01) and fails. + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + + // + // This should succeed since we know the length + // will fit. + // + + (VOID)TdiCopyBufferToMdl( + AdapterStatus, + 0, + ValidStatusLength, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + + } + + } + + break; + + case TDI_QUERY_FIND_NAME: + + // + // Check that there is a valid Netbios remote address. + // + + if ((Query->RequestConnectionInformation == NULL) || + ((RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE)) == NULL)) { + + return STATUS_BAD_NETWORK_PATH; + } + + // + // We assume the entire request buffer is in the first + // piece of the MDL chain (BUGBUG: Can we do this?). + // Make sure there is room for at least the header. + // + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER)) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // See if we have this name cached. We specify that this is + // a netbios name query, so this will only succeed if this is a + // unique name -- for a group name it will queue up a find + // name query and when we get the response we will fill in + // the request's buffer based on it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameNetbiosFindName, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this find + // name request and processing will be resumed when + // we get a response. + // + // The information field will hold the remote + // netbios name while it is in the WaitingNetbiosFindName + // queue. The status will hold the current status -- + // initially failure, then success, then overflow + // if the buffer is too small. + // + + NB_DEBUG2 (QUERY, ("Queueing up find name %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_NB_FIND_NAME); + + FindNameHeader->node_count = 0; + FindNameHeader->reserved = 0; + FindNameHeader->unique_group = 0; + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + // + // Assume it fails, we update the status to + // SUCCESS or BUFFER_OVERFLOW if needed. + // + + REQUEST_STATUS (Request) = STATUS_IO_TIMEOUT; + + InsertTailList( + &Device->WaitingNetbiosFindName, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found find name cached %lx\n", Request)); + + // + // We don't need to reference the cache entry since + // we only use it here with the lock still held. + // + + // + // Query the local address, which we will return as + // the destination address of this query. Since we + // use TempBuffer.IpxAddress for this query, we have + // to immediately copy it to its correct place in + // TempBuffer.FindNameInfo.Buffer. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + &CacheName->Networks[0].LocalTarget.NicHandle, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicHandle.NicId )); + + goto QueryFindNameFailed; + } +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicId, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + RtlMoveMemory (TempBuffer.FindNameInfo.Buffer.destination_addr, TempBuffer.IpxAddress.NodeAddress, 6); + TempBuffer.FindNameInfo.Buffer.access_control = 0x10; // standard token-ring values + TempBuffer.FindNameInfo.Buffer.frame_control = 0x40; + RtlCopyMemory (TempBuffer.FindNameInfo.Buffer.source_addr, CacheName->FirstResponse.NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, CacheName->FirstResponse.NodeAddress, 6); + + QueryStatus = (*Device->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + &CacheName->Networks[0].LocalTarget.NicHandle, +#else + CacheName->Networks[0].LocalTarget.NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(TempBuffer.FindNameInfo.Buffer.routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + TempBuffer.FindNameInfo.Buffer.routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + TempBuffer.FindNameInfo.Buffer.length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + TempBuffer.FindNameInfo.Header.node_count = 1; + TempBuffer.FindNameInfo.Header.reserved = 0; + TempBuffer.FindNameInfo.Header.unique_group = 0; // unique + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.FindNameInfo, + 0, + sizeof(FIND_NAME_HEADER) + 33, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } else { + +#if defined(_PNP_POWER) +QueryFindNameFailed: +#endif _PNP_POWER + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + // + // BETABUGBUG: Keep track of more of these. + // + + NB_DEBUG2 (QUERY, ("Query provider statistics\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Statistics, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + NB_DEBUG2 (QUERY, ("Query datagram info\n")); + + TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; + TempBuffer.DatagramInfo.MaximumDatagramCount = 0; + + Status = TdiCopyBufferToMdl ( + &TempBuffer.DatagramInfo, + 0, + sizeof(TempBuffer.DatagramInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS:{ +#if defined(_PNP_POWER) + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS + ? IPX_QUERY_DATA_LINK_ADDRESS + : IPX_QUERY_NETWORK_ADDRESS ), + NULL, + Request, + 0, + NULL); +#else + ULONG TransportAddressAllocSize; + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + ElementSize = (2 * sizeof(USHORT)) + 6; + } else { + ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); + } + +// TransportAddress = CTEAllocMem(sizeof(int) + (ElementSize * Device->MaximumNicId)); + TransportAddressAllocSize = sizeof(int) + ( ElementSize * Device->MaximumNicId); + TransportAddress = NbiAllocateMemory( TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + if (TransportAddress == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + TransportAddress->TAAddressCount = 0; + TransportAddressSize = sizeof(int); + CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; + + for (i = 1; i <= Device->MaximumNicId; i++) { + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + (USHORT)i, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + if (Status != STATUS_SUCCESS) { + continue; + } + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = TDI_ADDRESS_TYPE_UNSPEC; + RtlCopyMemory (CurAddress->Address, TempBuffer.IpxAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } + + Status = TdiCopyBufferToMdl ( + TransportAddress, + 0, + TransportAddressSize, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + +// CTEFreeMem (TransportAddress); + NbiFreeMemory( TransportAddress, TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + } +#endif _PNP_POWER + break; + } + default: + + NB_DEBUG (QUERY, ("Invalid query type %d\n", Query->QueryType)); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; + +} /* NbiTdiQueryInformation */ + + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ) + +/*++ + +Routine Description: + + This routine allocates an ADAPTER_STATUS buffer and + fills it in. The buffer will be allocated at most + MaximumLength size. The caller is responsible for + freeing the buffer. + +Arguments: + + MaximumLength - The maximum length to allocate. + + NicId - The NIC ID the query was received on, or 0 for + a local query. + + StatusBuffer - Returns the allocated buffer. + + StatusBufferLength - Returns the length of the buffer. + + ValidBufferLength - Returns the length of the buffer which + contains valid adapter status data. + +Return Value: + + STATUS_SUCCESS - The buffer was written successfully. + STATUS_BUFFER_OVERFLOW - The buffer was written but not all + data could fit in MaximumLength bytes. + STATUS_INSUFFICIENT_RESOURCES - The buffer could not be allocated. + +--*/ + +{ + + PADAPTER_STATUS AdapterStatus; + PNAME_BUFFER NameBuffer; + ADAPTER_STATUS TempAdapterStatus; +#if !defined(_PNP_POWER) + TDI_ADDRESS_IPX IpxAddress; +#endif !_PNP_POWER + PDEVICE Device = NbiDevice; + PADDRESS Address; + UCHAR NameCount; + ULONG LengthNeeded; + ULONG BytesWritten; + NTSTATUS Status; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + // + // First fill in the basic adapter status structure, to make + // it easier to copy over if the target buffer is really short. + // + + RtlZeroMemory ((PVOID)&TempAdapterStatus, sizeof(ADAPTER_STATUS)); + +#if defined(_PNP_POWER) + RtlCopyMemory (TempAdapterStatus.adapter_address, Device->Bind.Node, 6); +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + RtlCopyMemory (TempAdapterStatus.adapter_address, IpxAddress.NodeAddress, 6); +#endif _PNP_POWER + + + // + // Some of the fields mean different things for Novell Netbios, + // as described in the comments. + // + + TempAdapterStatus.rev_major = 0; // Jumpers + TempAdapterStatus.reserved0 = 0; // SelfTest + TempAdapterStatus.adapter_type = 0; // MajorVersion + TempAdapterStatus.rev_minor = 0; // MinorVersion + + TempAdapterStatus.duration = 0; // ReportingPeriod + TempAdapterStatus.frmr_recv = 0; // ReceiveCRCErrors + TempAdapterStatus.frmr_xmit = 0; // ReceiveAlignErrors + + TempAdapterStatus.iframe_recv_err = 0; // XmitCollisions + TempAdapterStatus.xmit_aborts = 0; // XmitAbort + + TempAdapterStatus.xmit_success = Device->Statistics.DataFramesSent; // SuccessfulXmits + TempAdapterStatus.recv_success = Device->Statistics.DataFramesReceived; // SuccessfulReceive + + TempAdapterStatus.iframe_xmit_err = (WORD)Device->Statistics.DataFramesResent; // XmitRetries + TempAdapterStatus.recv_buff_unavail = (WORD)Device->Statistics.DataFramesRejected; // ExhaustedResource + + // t1_timeouts, ti_timeouts, and reserved1 are unused. + + TempAdapterStatus.free_ncbs = 0xffff; // FreeBlocks + TempAdapterStatus.max_cfg_ncbs = 0xffff; // ConfiguredNCB + TempAdapterStatus.max_ncbs = 0xffff; // MaxNCB + + // xmit_bug_unavail and max_dgram_size are unused. + + TempAdapterStatus.pending_sess = (WORD)Device->Statistics.OpenConnections; // CurrentSessions + TempAdapterStatus.max_cfg_sess = 0xffff; // MaxSessionConfigured + TempAdapterStatus.max_sess = 0xffff; // MaxSessionPossible + TempAdapterStatus.max_sess_pkt_size = + Device->Bind.LineInfo.MaximumSendSize - sizeof(NB_CONNECTION); // MaxSessionPacketSize + + TempAdapterStatus.name_count = 0; + + + // + // Do a quick estimate of how many names we need room for. + // This includes stopping addresses and the broadcast + // address, for the moment. BUGBUG: Fix this? + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LengthNeeded = sizeof(ADAPTER_STATUS) + (Device->AddressCount * sizeof(NAME_BUFFER)); + + if (LengthNeeded > MaximumLength) { + LengthNeeded = MaximumLength; + } + + AdapterStatus = NbiAllocateMemory(LengthNeeded, MEMORY_STATUS, "Adapter Status"); + if (AdapterStatus == NULL) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + *StatusBuffer = AdapterStatus; + *StatusBufferLength = LengthNeeded; + + if (LengthNeeded < sizeof(ADAPTER_STATUS)) { + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, LengthNeeded); + *ValidBufferLength = LengthNeeded; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, sizeof(ADAPTER_STATUS)); + + BytesWritten = sizeof(ADAPTER_STATUS); + NameBuffer = (PNAME_BUFFER)(AdapterStatus+1); + NameCount = 0; + + // + // Scan through the device's address database, filling in + // the NAME_BUFFERs. + // + + Status = STATUS_SUCCESS; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + +#if defined(_PNP_POWER) + if ((Address->State != ADDRESS_STATE_OPEN) || + (Address->Flags & ADDRESS_FLAGS_CONFLICT)) { + continue; + } +#else + if ((Address->State != ADDRESS_STATE_OPEN) != 0) { + continue; + } +#endif _PNP_POWER + + // + // Ignore the broadcast address. + // + + if (Address->NetbiosAddress.Broadcast) { + continue; + } + + // + // Ignore our reserved address. + // +#if defined(_PNP_POWER) + if ( NbiFindAdapterAddress( + Address->NetbiosAddress.NetbiosName, + LOCK_ACQUIRED + )) { + continue; + } +#else + if (RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + Device->ReservedNetbiosName, + 16)) { + continue; + } + +#endif _PNP_POWER + // + // Make sure we still have room. + // + + if (BytesWritten + sizeof(NAME_BUFFER) > LengthNeeded) { + Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlCopyMemory( + NameBuffer->name, + Address->NetbiosAddress.NetbiosName, + 16); + + ++NameCount; + NameBuffer->name_num = NameCount; + + NameBuffer->name_flags = REGISTERED; + if (Address->NameTypeFlag == NB_NAME_GROUP) { + NameBuffer->name_flags |= GROUP_NAME; + } + + // + // BUGBUG: name_flags should be done more accurately. + // + + BytesWritten += sizeof(NAME_BUFFER); + ++NameBuffer; + + } + + AdapterStatus->name_count = (WORD)NameCount; + *ValidBufferLength = BytesWritten; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Status; + +} /* NbiStoreAdapterStatus */ + + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ) + +/*++ + +Routine Description: + + This routine updates the find name request with the + new information received. It updates the status in + the request if needed. + +Arguments: + + Request - The netbios find name request. + + NicId - The NIC ID the response was received on. + + RemoteIpxAddress - The IPX address of the remote. + + Unique - TRUE if the name is unique. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + FIND_NAME_BUFFER UNALIGNED * FindNameBuffer; + UINT FindNameBufferLength; + TDI_ADDRESS_IPX LocalIpxAddress; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + NTSTATUS QueryStatus; + UINT i; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // Scan through the names saved so far and see if this one + // is there. + // + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *)(FindNameHeader+1); + + for (i = 0; i < FindNameHeader->node_count; i++) { + + if (RtlEqualMemory( + FindNameBuffer->source_addr, + RemoteIpxAddress->NodeAddress, + 6)) { + + // + // This remote already responded, ignore it. + // + + return; + + } + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *) + (((PUCHAR)FindNameBuffer) + 33); + + } + + // + // Make sure there is room for this new node. 33 is + // sizeof(FIND_NAME_BUFFER) without padding. + // + + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER) + ((FindNameHeader->node_count+1) * 33)) { + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + return; + } + + // + // Query the local address, which we will return as + // the destination address of this query. + // + +#if defined(_PNP_POWER) + if( (*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicHandle, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + // + // Ignore this response if the query fails. maybe the NicHandle + // is bad or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + NicHandle->NicId )); + return; + } +#else + (VOID)(*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + FindNameBuffer->access_control = 0x10; // standard token-ring values + FindNameBuffer->frame_control = 0x40; + RtlMoveMemory (FindNameBuffer->destination_addr, LocalIpxAddress.NodeAddress, 6); + RtlCopyMemory (FindNameBuffer->source_addr, RemoteIpxAddress->NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, RemoteIpxAddress->NodeAddress, 6); + + QueryStatus = (*NbiDevice->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + NicHandle, +#else + NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(FindNameBuffer->routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + FindNameBuffer->routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + FindNameBuffer->length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + ++FindNameHeader->node_count; + if (!Unique) { + FindNameHeader->unique_group = 1; // group + } + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + +} /* NbiUpdateNetbiosFindName */ + + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sets the REQUEST_INFORMATION field to the right + value based on the number of responses recorded in the netbios + find name request's buffer. + +Arguments: + + Request - The netbios find name request. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + REQUEST_INFORMATION(Request) = sizeof(FIND_NAME_HEADER) + (FindNameHeader->node_count * 33); + +} /* NbiSetNetbiosFindNameInformation */ + + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} /* NbiTdiSetInformation */ + + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_QUERY frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LINE_INFO LineInfo; + ULONG ResponseSize; + NTSTATUS Status; + PNDIS_BUFFER AdapterStatusBuffer; + PADAPTER_STATUS AdapterStatus; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + PDEVICE Device = NbiDevice; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + + + // + // The old stack does not include the 14 bytes of padding in + // the 802.3 or IPX length of the packet. + // + + if (PacketSize < (sizeof(IPX_HEADER) + 2)) { + return; + } + + // + // Get the maximum size we can send. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + &RemoteAddress->NicHandle, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL) != STATUS_SUCCESS ) { + // + // Bad NicHandle or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_LINE_INFO, + RemoteAddress->NicHandle.NicId )); + + return; + } + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } +#else + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + // + // Get the maximum size we can send. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + RemoteAddress->NicId, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL); +#endif _PNP_POWER + + ResponseSize = LineInfo.MaximumSendSize - sizeof(IPX_HEADER) - sizeof(NB_STATUS_RESPONSE); + + // + // Get the local adapter status (this allocates a buffer). + // + + Status = NbiStoreAdapterStatus( + ResponseSize, +#if defined(_PNP_POWER) + RemoteAddress->NicHandle.NicId, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &AdapterStatusBuffer, + Device->NdisBufferPoolHandle, + AdapterStatus, + ValidStatusLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + NB_DEBUG2 (QUERY, ("Reply to AdapterStatus from %lx %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + *(UNALIGNED ULONG *)Connectionless->IpxHeader.SourceNetwork, + Connectionless->IpxHeader.SourceNode[0], + Connectionless->IpxHeader.SourceNode[1], + Connectionless->IpxHeader.SourceNode[2], + Connectionless->IpxHeader.SourceNode[3], + Connectionless->IpxHeader.SourceNode[4], + Connectionless->IpxHeader.SourceNode[5])); + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_RESPONSE; + Reserved->u.SR_AS.ActualBufferLength = AdapterStatusLength; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, Connectionless->IpxHeader.SourceNetwork, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_RESPONSE; + + NbiReferenceDevice (Device, DREF_STATUS_RESPONSE); + + NdisChainBufferAtBack (Packet, AdapterStatusBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + RemoteAddress, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + ValidStatusLength, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends NB_CMD_STATUS_QUERY frames. + +Arguments: + + Request - Holds the request describing the remote adapter + status query. REQUEST_STATUS(Request) points + to the netbios cache entry for the remote name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNETBIOS_CACHE CacheName; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_QUERY; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(Request); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + LocalTarget = &CacheName->Networks[0].LocalTarget; + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_QUERY; + + NbiReferenceDevice (Device, DREF_STATUS_FRAME); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY), + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_RESPONSE frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + PREQUEST AdapterStatusRequest; + PNETBIOS_CACHE CacheName; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNDIS_BUFFER TargetBuffer; + ULONG TargetBufferLength, BytesToTransfer; + ULONG BytesTransferred; + NDIS_STATUS NdisStatus; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + BOOLEAN Found; + PNAME_BUFFER NameBuffer; + UINT i,NameCount = 0; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) { + return; + } + + // + // Find out how many names are there. + // + NameBuffer = (PNAME_BUFFER)(LookaheadBuffer + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)); + if ( LookaheadBufferSize > sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS) ) { + NameCount = (LookaheadBufferSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)) ) / + sizeof(NAME_BUFFER); + } + // + // Find a request queued to this remote. If there are + // multiple requests outstanding for the same name we + // should get multiple responses, so we only need to + // find one. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Found = FALSE; + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if ( CacheName->Unique ) { + if (RtlEqualMemory( + &CacheName->FirstResponse, + Connectionless->IpxHeader.SourceNetwork, + 12)) { + Found = TRUE; + break; + } + } else if ( RtlEqualMemory( CacheName->NetbiosName,NetbiosBroadcastName,16)){ + // + // It's a broadcast name. Any response is fine. + // + Found = TRUE; + break; + } else { + // + // It's group name. Make sure that this remote + // has this group name registered with him. + // + for (i =0;i<NameCount;i++) { + if ( (RtlEqualMemory( + CacheName->NetbiosName, + NameBuffer[i].name, + 16)) && + + (NameBuffer[i].name_flags & GROUP_NAME) ) { + + Found = TRUE; + break; + } + } + } + + } + + if (!Found) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (QUERY, ("Got response to AdapterStatus %lx\n", AdapterStatusRequest)); + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + REQUEST_STATUS (AdapterStatusRequest) = STATUS_INSUFFICIENT_RESOURCES; + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->Type = RECEIVE_TYPE_ADAPTER_STATUS; + ReceiveReserved->u.RR_AS.Request = AdapterStatusRequest; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_SUCCESS; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // Now that we have a packet and a buffer, set up the transfer. + // We will complete the request when the transfer completes. + // + + TargetBuffer = REQUEST_NDIS_BUFFER (AdapterStatusRequest); + + NdisChainBufferAtFront (Packet, TargetBuffer); + + NbiGetBufferChainLength (TargetBuffer, &TargetBufferLength); + BytesToTransfer = PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if (TargetBufferLength < BytesToTransfer) { + BytesToTransfer = TargetBufferLength; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_BUFFER_OVERFLOW; + } + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)), + BytesToTransfer, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessStatusResponse */ + diff --git a/private/ntos/tdi/isnp/nb/receive.c b/private/ntos/tdi/isnp/nb/receive.c new file mode 100644 index 000000000..54ce78944 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/receive.c @@ -0,0 +1,1303 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains the code to handle receive indication + and posted receives for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This routine is a no-op to put in the NbiCallbacks table so +// we can avoid checking for runt session frames (this is because +// of how the if is structure below). +// + +VOID +NbiProcessSessionRunt( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) +{ + return; +} + +NB_CALLBACK_NO_TRANSFER NbiCallbacksNoTransfer[] = { + NbiProcessFindName, + NbiProcessNameRecognized, + NbiProcessAddName, + NbiProcessAddName, // processes name in use frames also + NbiProcessDeleteName, + NbiProcessSessionRunt, // in case get a short session packet + NbiProcessSessionEnd, + NbiProcessSessionEndAck, + NbiProcessStatusQuery + }; + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiProcessDeathPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + DbgPrint("******Received death packet - connid %x\n",Sess->DestConnectionId); + + if ( !NbiGlobalDebugResTimeout ) { + return; + } + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + DbgPrint("********No Connection found with %x id\n",Sess->DestConnectionId); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + DbgPrint("******Received death packet on conn %lx from <%.16s>\n",Connection,Connection->RemoteName); + DbgBreakPoint(); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } +} +#endif //RSRC_TIMEOUT_DBG + + +VOID +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles receive indications from IPX. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PNB_FRAME NbFrame = (PNB_FRAME)LookaheadBuffer; + UCHAR DataStreamType; + + // + // We know that this is a frame with a valid IPX header + // because IPX would not give it to use otherwise. However, + // it does not check the source socket. + // + + if (NbFrame->Connectionless.IpxHeader.SourceSocket != NB_SOCKET) { + return; + } + + ++NbiDevice->Statistics.PacketsReceived; + + // First assume that the DataStreamType is at the normal place i.e 2nd byte + // + + // Now see if this is a name frame. + // + if ( PacketSize == sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME) ) { + // In the internet mode, the DataStreamType2 becomes DataStreamType + if (NbFrame->Connectionless.IpxHeader.PacketType == 0x14 ) { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType2; + } else { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + } + + // Is this a name frame? + // NB_CMD_FIND_NAME = 1 .... NB_CMD_DELETE_NAME = 5 + // + if ((DataStreamType >= NB_CMD_FIND_NAME) && (DataStreamType <= NB_CMD_DELETE_NAME)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + return; + } + + } + +#ifdef RSRC_TIMEOUT_DBG + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_DEATH_PACKET)) { + + NbiProcessDeathPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } +#endif //RSRC_TIMEOUT_DBG + + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_SESSION_DATA)) { + + NbiProcessSessionData( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else { + + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + // Handle NB_CMD_SESSION_END = 7 ... NB_CMD_STATUS_QUERY = 9 + // + if ((DataStreamType >= NB_CMD_SESSION_END ) && (DataStreamType <= NB_CMD_STATUS_QUERY)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + + } else if (DataStreamType == NB_CMD_STATUS_RESPONSE) { + + NbiProcessStatusResponse( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else if ((DataStreamType == NB_CMD_DATAGRAM) || + (DataStreamType == NB_CMD_BROADCAST_DATAGRAM)) { + + NbiProcessDatagram( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize, + (BOOLEAN)(DataStreamType == NB_CMD_BROADCAST_DATAGRAM)); + + } + + } + +} /* NbiReceive */ + + +VOID +NbiReceiveComplete( + IN USHORT NicId + ) + +/*++ + +Routine Description: + + This routine handles receive complete indications from IPX. + +Arguments: + + NicId - The NIC ID on which a receive was previously indicated. + +Return Value: + + None. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS Address; + PREQUEST Request; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PDEVICE Device = NbiDevice; + LIST_ENTRY LocalList; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + // + // Complete any pending receive requests. + // + + + if (!IsListEmpty (&Device->ReceiveCompletionQueue)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveCompletionQueue, p)) { + + Request = LIST_ENTRY_TO_REQUEST (p); + + // + // BUGBUG: Cache the connection somewhere easier + // to retrieve? + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + } + + } + + + // + // Indicate any datagrams to clients. + // + + if (!IsListEmpty (&Device->ReceiveDatagrams)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveDatagrams, p)) { + + ReceiveBuffer = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER, WaitLinkage); + Address = ReceiveBuffer->Address; + + NbiIndicateDatagram( + Address, + ReceiveBuffer->RemoteName, + ReceiveBuffer->Data, + ReceiveBuffer->DataLength); + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + } + } + + + // + // Start packetizing connections. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Check again because it may just have become + // empty, and the code below depends on it being + // non-empty. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + // + // We copy the list locally, in case someone gets + // put back on it. We have to hack the end so + // it points to LocalList instead of PacketizeConnections. + // + + LocalList = Device->PacketizeConnections; + LocalList.Flink->Blink = &LocalList; + LocalList.Blink->Flink = &LocalList; + + InitializeListHead (&Device->PacketizeConnections); + + // + // Set all these connections to not be on the list, so + // NbiStopConnection won't try to take them off. + // + + for (p = LocalList.Flink; p != &LocalList; p = p->Flink) { + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + CTEAssert (Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = FALSE; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + while (TRUE) { + + p = RemoveHeadList (&LocalList); + if (p == &LocalList) { + break; + } + + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + } + } + +} /* NbiReceiveComplete */ + + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine handles a transfer data complete indication from + IPX, indicating that a previously issued NdisTransferData + call has completed. + +Arguments: + + Packet - The packet associated with the transfer. + + Status - The status of the transfer. + + BytesTransferred - The number of bytes transferred. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PADDRESS Address; + PCONNECTION Connection; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PREQUEST AdapterStatusRequest; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + ReceiveReserved = (PNB_RECEIVE_RESERVED)(Packet->ProtocolReserved); + + switch (ReceiveReserved->Type) { + + case RECEIVE_TYPE_DATA: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + Connection = ReceiveReserved->u.RR_CO.Connection; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Status != NDIS_STATUS_SUCCESS) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive = Connection->PreviousReceive; + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // BUGBUG: Send a resend ack? + // + + } else { + + // + // This aborts the current receive and + // releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } else { + + + Connection->CurrentReceive.Offset += BytesTransferred; + Connection->CurrentReceive.MessageOffset += BytesTransferred; + + if (ReceiveReserved->u.RR_CO.CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (ReceiveReserved->u.RR_CO.EndOfMessage) { + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (ReceiveReserved->u.RR_CO.PartialReceive) { + Connection->CurrentIndicateOffset += BytesTransferred; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + } + + // + // Free the NDIS buffer chain if we allocated one. + // + + if (!ReceiveReserved->u.RR_CO.NoNdisBuffer) { + + NdisQueryPacket (Packet, NULL, NULL, &CurBuffer, NULL); + + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + break; + + case RECEIVE_TYPE_DATAGRAM: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + ReceiveBuffer = ReceiveReserved->u.RR_DG.ReceiveBuffer; + + // + // Free the packet used for the transfer. + // + + ReceiveReserved->u.RR_DG.ReceiveBuffer = NULL; + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // If it succeeded then queue it for indication, + // otherwise free the receive buffer also. + // + + if (Status == STATUS_SUCCESS) { + + ReceiveBuffer->DataLength = BytesTransferred; + NB_INSERT_HEAD_LIST( + &Device->ReceiveDatagrams, + &ReceiveBuffer->WaitLinkage, + &Device->Lock); + + } else { + + Address = ReceiveBuffer->Address; + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + break; + + case RECEIVE_TYPE_ADAPTER_STATUS: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + AdapterStatusRequest = ReceiveReserved->u.RR_AS.Request; + + // + // Free the packet used for the transfer. + // + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // Complete the request. + // + + if (Status == STATUS_SUCCESS) { + + // + // REQUEST_STATUS() is already to set to SUCCESS or + // BUFFER_OVERFLOW based on whether the buffer was + // big enough. + // + + REQUEST_INFORMATION(AdapterStatusRequest) = BytesTransferred; + + } else { + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_UNEXPECTED_NETWORK_ERROR; + + } + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + break; + + } + +} /* NbiTransferDataComplete */ + + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when a receive needs to be acked to + the remote. It either sends a data ack or queues up a piggyback + ack request. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - Pointer to the connection. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + + if (Connection->NewNetbios) { + + // + // CurrentReceiveNoPiggyback is based on the bits he + // set in his frame, NoPiggybackHeuristic is based on + // guesses about the traffic pattern, it is set to + // TRUE if we think we should not piggyback. + // + + if ((!Device->EnablePiggyBackAck) || + (Connection->CurrentReceiveNoPiggyback) || + (Connection->PiggybackAckTimeout) || + (Connection->NoPiggybackHeuristic)) { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (!Connection->DataAckPending) { + + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + // + // Some stacks can have multiple messages + // outstanding, so we may already have an + // ack queued. + // + + Connection->DataAckTimeouts = 0; + Connection->DataAckPending = TRUE; + + ++Device->Statistics.PiggybackAckQueued; + + if (!Connection->OnDataAckQueue) { + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle1); + + if (!Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = TRUE; + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + } + + if (!Device->DataAckActive) { + NbiStartShortTimer (Device); + Device->DataAckActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle1); + } + + // + // Clear this, since a message ack resets the count. + // + + Connection->ReceivesWithoutAck = 0; + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + +} + + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have filled up a receive request + and need to complete it. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + + THIS ROUTINE ALSO HOLDS CANCEL SPIN LOCK WHEN IT IS CALLED + AND RELEASES IT WHEN IT RETURNS. +Arguments: + + Connection - Pointer to the connection. + + EndOfMessage - BOOLEAN set to true if the message end was received. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PDEVICE Device = NbiDevice; + + // + // Complete the current receive request. If the connection + // has shut down then we complete it right here, otherwise + // we queue it for completion in the receive complete + // handler. + // + + Request = Connection->ReceiveRequest; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveRequest = NULL; // StopConnection won't do this + + REQUEST_STATUS(Request) = Connection->Status; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + ++Connection->ConnectionInfo.ReceiveErrors; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } else { + + REQUEST_INFORMATION (Request) = Connection->CurrentReceive.Offset; + + if (EndOfMessage) { + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + } else { + + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + + } + + // + // If we indicated to the client, adjust this down by the + // amount of data taken, when it hits zero we can reindicate. + // + + if (Connection->ReceiveUnaccepted) { + NB_DEBUG2 (RECEIVE, ("Moving Unaccepted %d down by %d\n", + Connection->ReceiveUnaccepted, Connection->CurrentReceive.Offset)); + if (Connection->CurrentReceive.Offset >= Connection->ReceiveUnaccepted) { + Connection->ReceiveUnaccepted = 0; + } else { + Connection->ReceiveUnaccepted -= Connection->CurrentReceive.Offset; + } + } + + // + // BUGBUG: Check whether to activate another receive? + // + + Connection->ReceiveState = CONNECTION_RECEIVE_PENDING; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + if (EndOfMessage) { + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (Connection->CurrentIndicateOffset != 0) { + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + } + + } else { + + NbiSendDataAck( + Connection, + EndOfMessage ? NbiAckResponse : NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // This will complete the request inside ReceiveComplete, + // dereference the connection, and set the state to IDLE. + // + + NB_INSERT_TAIL_LIST( + &Device->ReceiveCompletionQueue, + REQUEST_LINKAGE (Request), + &Device->Lock); + + } + +} /* NbiCompleteReceive */ + + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a receive on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the receive. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PCONNECTION Connection; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelReceive); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + // + // Insert this in our queue, then see if we need + // to wake up the remote. + // + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->ReceiveQueue, Request); + + if (Connection->ReceiveState != CONNECTION_RECEIVE_W_RCV) { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx idle\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx awakened\n", Request, Connection)); + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = (USHORT) + (Connection->ReceiveSequence + Connection->ReceiveWindowSize - 1); + + } + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (RECEIVE, ("Receive connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiReceive */ + + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive. + The request is found on the connection's receive queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + + // + // Just stop the connection, that will tear down any + // receives. + // + // BUGBUG: Do we care about cancelling non-active + // receives without stopping the connection?? + // + // BUGBUG: This routine is the same as NbiCancelSend, + // so if we don't make it more specific, merge the two. + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelReceive */ + diff --git a/private/ntos/tdi/isnp/nb/send.c b/private/ntos/tdi/isnp/nb/send.c new file mode 100644 index 000000000..a4443c73c --- /dev/null +++ b/private/ntos/tdi/isnp/nb/send.c @@ -0,0 +1,2886 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains the send routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +) + +/*++ + +Routine Description: + + This routine handles a send completion call from IPX. + +Arguments: + + Packet - The packet which has been completed. + + Status - The status of the send. + +Return Value: + + None. + +--*/ + + + +{ + PDEVICE Device = NbiDevice; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST DatagramRequest; + PREQUEST SendRequest, TmpRequest; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PNETBIOS_CACHE CacheName; + PNDIS_BUFFER SecondBuffer; + PVOID SecondBufferMemory; + UINT SecondBufferLength; + ULONG oldvalue; + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + CTELockHandle CancelLH; +#if defined(_PNP_POWER) + CTELockHandle LockHandle; +#endif _PNP_POWER + + // + // We jump back here if we re-call send from inside this + // function and it doesn't pend (to avoid stack overflow). + // + +FunctionStart:; + + ++Device->Statistics.PacketsSent; + + switch (Reserved->Type) { + + case SEND_TYPE_SESSION_DATA: + + // + // This was a send on a session. This references the + // IRP. + // + + NB_DEBUG2 (SEND, ("Complete NDIS packet %lx\n", Reserved)); + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + SendRequest = Reserved->u.SR_CO.Request; + + if (!Reserved->u.SR_CO.NoNdisBuffer) { + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + // + // If NoNdisBuffer is TRUE, then we could set + // Connection->SendBufferInUse to FALSE here. The + // problem is that a new send might be in progress + // by the time this completes and it may have + // used the user buffer, then if we need to + // retransmit that packet we would use the buffer + // twice. We instead rely on the fact that whenever + // we make a new send active we set SendBufferInUse + // to FALSE. The net effect is that the user's buffer + // can be used the first time a send is packetize + // but not on resends. + // + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + +#if DBG + if (REQUEST_REFCOUNT(SendRequest) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, SendRequest); + DbgBreakPoint(); + } +#endif + +#if defined(__PNP) + NB_GET_LOCK( &Connection->Lock, &LockHandle ); + oldvalue = REQUEST_REFCOUNT(SendRequest)--; + if ( DEVICE_NETWORK_PATH_NOT_FOUND == Status ) { + Connection->LocalTarget = Reserved->LocalTarget; + } + NB_FREE_LOCK( &Connection->Lock, LockHandle ); +#else + oldvalue = NB_ADD_ULONG( + &REQUEST_REFCOUNT (SendRequest), + (ULONG)-1, + &Connection->Lock); +#endif __PNP + + if (oldvalue == 1) { + + // + // If the refcount on this request is now zero then + // we already got the ack for it, which means + // that the ack-processing code has unlinked the + // request from Connection->SendQueue. So we + // can just run the queue of connections here + // and complete them. + // + // We dereference the connection for all but one + // of the requests, we hang on to that until a bit + // later so everything stays around. + // + + while (TRUE) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + NB_DEBUG2 (SEND, ("Completing request %lx from send complete\n", SendRequest)); + REQUEST_STATUS (SendRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + ++Connection->ConnectionInfo.TransmittedTsdus; + SendRequest = TmpRequest; + + if (SendRequest == NULL) { + break; + } + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } + + if (Reserved->OwnedByConnection) { + + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (oldvalue == 1) { + NbiDereferenceConnection (Connection, CREF_SEND); + } + + break; + + case SEND_TYPE_NAME_FRAME: + + // + // The frame is an add name/delete name; put it back in + // the pool and deref the address. + // + + CTEAssert (Reserved->SendInProgress); + + Address = Reserved->u.SR_NF.Address; + +#if !defined(_PNP_POWER) + if ((Reserved->u.SR_NF.CurrentNicId) && + (Reserved->u.SR_NF.CurrentNicId < Device->MaximumNicId)) { + + NB_CONNECTIONLESS UNALIGNED * Header; + IPX_LOCAL_TARGET TempLocalTarget; + + // + // This is a name frame being sent to every address, so + // resent it to the next NIC ID. We hold the address + // reference through this send. + // + + CTEAssert (Address != NULL); + + ++Reserved->u.SR_NF.CurrentNicId; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = Reserved->u.SR_NF.DataStreamType; + Header->NameFrame.NameTypeFlag = Reserved->u.SR_NF.NameTypeFlag; + + // + // This is not a name in use frame so DataStreamType2 + // is the same as DataStreamType. + // + + Header->NameFrame.DataStreamType2 = Reserved->u.SR_NF.DataStreamType; + + RtlCopyMemory( + Header->NameFrame.Name, + Address->NetbiosAddress.NetbiosName, + 16); + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + TempLocalTarget.NicId = Reserved->u.SR_NF.CurrentNicId; + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)); + if ((Status = + (*Device->Bind.SendHandler)( + &TempLocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + goto FunctionStart; + + } + + return; + + } +#endif !_PNP_POWER + + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + if (Address) { + NbiDereferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_NAME_FRAME); + } + + break; + + case SEND_TYPE_SESSION_INIT: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + CTEAssert (SecondBufferLength == sizeof(NB_SESSION_INIT)); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_SESSION_INIT); + + break; + + case SEND_TYPE_SESSION_NO_DATA: + + // + // This is a frame which was sent on a connection but + // has no data (ack, session end, session end ack). + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + + if (Reserved->OwnedByConnection) { + + CTEAssert (Connection != NULL); + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (Connection != NULL) { + NbiDereferenceConnection (Connection, CREF_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_FRAME); + } + + break; + + case SEND_TYPE_FIND_NAME: + + // + // The frame is a find name; just set SendInProgress to + // FALSE and FindNameTimeout will clean it up. + // +#if defined(_PNP_POWER) + NB_GET_LOCK( &Device->Lock, &LockHandle); + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + if ( STATUS_SUCCESS == Status ) { + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } else { + NB_DEBUG( CACHE, ("Send complete of find name with failure %lx\n",Status )); + } + NB_FREE_LOCK(&Device->Lock, LockHandle); +#else + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +#endif _PNP_POWER + break; + + case SEND_TYPE_DATAGRAM: + + // + // If there are any more networks to send this on then + // do so, otherwise put it back in the pool and complete + // the request. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + if ((Reserved->u.SR_DG.Cache == NULL) || + (++Reserved->u.SR_DG.CurrentNetwork >= + Reserved->u.SR_DG.Cache->NetworksUsed)) { + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Completing datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // Remove any user buffers chained on this packet. + // + + NdisReinitializePacket (Packet); + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisChainBufferAtFront (Packet, Reserved->HeaderBuffer); + + // + // Complete the request. + // + + REQUEST_STATUS(DatagramRequest) = Status; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + CacheName = Reserved->u.SR_DG.Cache; + + NbiPushSendPacket (Reserved); + + // + // Since we are no longer referencing the cache + // name, see if we should delete it (this will + // happen if the cache entry was aged out while + // the datagram was being processed). + // + + if (CacheName != NULL) { + + oldvalue = NB_ADD_ULONG( + &CacheName->ReferenceCount, + (ULONG)-1, + &Device->Lock); + + if (oldvalue == 1) { + + NB_DEBUG2 (CACHE, ("Free aged cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free old cache"); + + } + } + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + } else { + + NB_CONNECTIONLESS UNALIGNED * Header; + PIPX_LOCAL_TARGET LocalTarget; + ULONG HeaderLength; + ULONG PacketLength; + + // send the datagram on the next net. + CTEAssert (!Reserved->u.SR_DG.Cache->Unique); + Reserved->SendInProgress = TRUE; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + if ((Status = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + goto FunctionStart; + } + + } + + break; + + case SEND_TYPE_STATUS_QUERY: + + // + // This is an adapter status query, which is a simple + // packet. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_FRAME); + + break; + + case SEND_TYPE_STATUS_RESPONSE: + + // + // This is an adapter status response, we have to free the + // second buffer. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, Reserved->u.SR_AS.ActualBufferLength, MEMORY_STATUS, "Adapter Status"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_RESPONSE); + + break; + +#ifdef RSRC_TIMEOUT_DBG + case SEND_TYPE_DEATH_PACKET: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + DbgPrint("********Death packet send completed status %lx\n",Status); + DbgBreakPoint(); + break; +#endif //RSRC_TIMEOUT_DBG + + default: + + CTEAssert (FALSE); + break; + + } + +} /* NbiSendComplete */ + +#if 0 +ULONG NbiLoudSendQueue = 1; +#endif + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is used to ensure that receive sequence numbers on + packets are numbered correctly. It is called in place of the lower-level + send handler; after assigning the receive sequence number it locks out + other sends until the NdisSend call has returned (not necessarily completed), + insuring that the packets with increasing receive sequence numbers + are queue in the right order by the MAC. + + NOTE: THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD, AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection the send is on. + + Packet - The packet to send. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PNB_SEND_RESERVED Reserved; + PLIST_ENTRY p; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + BOOLEAN NdisSendReference; + ULONG result; + + + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + CTEAssert (Connection->State == CONNECTION_STATE_ACTIVE); + + // + // If there is a send in progress, then queue this packet + // and return. + // + + if (Connection->NdisSendsInProgress > 0) { + + NB_DEBUG2 (SEND, ("Queueing send packet %lx on %lx\n", Reserved, Connection)); + InsertTailList (&Connection->NdisSendQueue, &Reserved->WaitLinkage); + ++Connection->NdisSendsInProgress; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // No send in progress. Set the flag to true, and fill in the + // receive sequence fields in the packet. + // + + Connection->NdisSendsInProgress = 1; + NdisSendReference = FALSE; + Connection->NdisSendReference = &NdisSendReference; + + while (TRUE) { + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; + } + + // + // Since we are acking as much as we know, we can clear + // this flag. The connection will eventually get removed + // from the queue by the long timeout. + // + + Connection->DataAckPending = FALSE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + NdisStatus = (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + Reserved->u.SR_CO.PacketLength, + sizeof(NB_CONNECTION)); + + if (NdisStatus != NDIS_STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + // + // Take the ref count down, which may allow others + // to come through. + // + + result = NB_ADD_ULONG( + &Connection->NdisSendsInProgress, + (ULONG)-1, + &Connection->Lock); + + // + // We have now sent a packet, see if any queued up while we + // were doing it. If the count was zero after removing ours, + // then anything else queued is being processed, so we can + // exit. If the connection was stopped while we were sending, + // a special reference was added which we remove (NbiStopConnection + // sets NdisSendReference to TRUE, using the pointer saved + // in Connection->NdisSendReference). + // + + if (result == 1) { + if (NdisSendReference) { + NB_DEBUG2 (SEND, ("Remove CREF_NDIS_SEND from %lx\n", Connection)); + NbiDereferenceConnection (Connection, CREF_NDIS_SEND); + } + return; + } + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + p = RemoveHeadList(&Connection->NdisSendQueue); + + // + // If the refcount was not zero, then nobody else should + // have taken packets off since they would have been + // blocked by us. So, the queue should not be empty. + // + + ASSERT (p != &Connection->NdisSendQueue); + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } // while loop + + // + // We should never reach here. + // + + CTEAssert (FALSE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiAssignSequenceAndSend */ + + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + PTDI_REQUEST_KERNEL_SEND Parameters; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + + // + // For old netbios, don't allow sends greater than 64K-1. + // + + if ((Connection->NewNetbios) || + (Parameters->SendLength <= 0xffff)) { + + IoSetCancelRoutine (Request, NbiCancelSend); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_INFORMATION (Request) = Parameters->SendLength; // assume it succeeds. + + REQUEST_REFCOUNT (Request) = 1; // refcount starts at 1. + NbiReferenceConnectionSync (Connection, CREF_SEND); + + // + // NOTE: The connection send queue is managed such + // that the current send being packetized is not on + // the queue. For multiple-request messages, the + // first one is not on the queue, but its linkage + // field points to the next request in the message + // (which will be on the head of the queue). + // + + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a final send. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx idle\n", Request, Connection)); + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have been collecting partial sends waiting + // for a final one, which we have now received, + // so start packetizing. + // + // We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx got eor\n", Request, Connection)); + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + + Connection->UnAckedSend = Connection->CurrentSend; +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + // + // The state is PACKETIZE, W_ACK, or W_PACKET. + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx busy\n", Request, Connection)); + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // This is a partial send. We queue them up without + // packetizing until we get a final (this is because + // we have to put a correct Connection->CurrentMessageLength + // in the frames. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + // + // Start collecting partial sends. NOTE: Partial sends + // are always inserted in the send queue + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have got another partial send to add to our + // list. We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } else { + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, too long for connection %lx (%d)\n", Request, Connection, Parameters->SendLength)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_PARAMETER; + + } + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiSend */ + + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + PNB_SEND_RESERVED Reserved; + PDEVICE Device = NbiDevice; + NB_CONNECTION UNALIGNED * Header; + ULONG PacketLength; + ULONG PacketSize; + ULONG DesiredLength; + ULONG ActualLength; + NTSTATUS Status; + PSINGLE_LIST_ENTRY s; + USHORT ThisSendSequence; + USHORT ThisOffset; + BOOLEAN ExitAfterSend; + UCHAR ConnectionControlFlag; + CTELockHandle DeviceLockHandle; + + // + // We jump back here if we are talking new Netbios and it + // is OK to packetize another send. + // + +SendAnotherPacket: + + // + // If we decide to packetize another send after this, we + // change ExitAfterSend to FALSE and SubState to PACKETIZE. + // Right now we don't change SubState in case it is W_PACKET. + // + + ExitAfterSend = TRUE; + + CTEAssert (Connection->CurrentSend.Request != NULL); + + if (Connection->NewNetbios) { + + // + // Check that we have send window, both that advertised + // by the remote and our own locally-decided window which + // may be smaller. + // + + if (((USHORT)(Connection->CurrentSend.SendSequence-1) == Connection->RemoteRcvSequenceMax) || + (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize)) { + + // + // Keep track of whether we are waiting because of his window + // or because of our local window. If it is because of our local + // window then we may want to adjust it after this window + // is acked. + // + + if ((USHORT)(Connection->CurrentSend.SendSequence-1) != Connection->RemoteRcvSequenceMax) { + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + NB_DEBUG2 (SEND, ("Connection %lx local shut down at %lx, %lx\n", Connection, Connection->CurrentSend.SendSequence, Connection->UnAckedSend.SendSequence)); + } else { + Connection->SubState = CONNECTION_SUBSTATE_A_REMOTE_W; + NB_DEBUG2 (SEND, ("Connection %lx remote shut down at %lx\n", Connection, Connection->CurrentSend.SendSequence)); + } + + // + // Start the timer so we will keep bugging him about + // this. BUGBUG: What if he doesn't get a receive down + // quickly -- but this is better than losing his ack + // and then dying. We won't really back off our timer + // because we will keep getting acks, and resetting it. + // + + NbiStartRetransmit (Connection); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + + } + + Request = Connection->CurrentSend.Request; + + // + // If we are in this routine then we know that + // we are coming out of IDLE, W_ACK, or W_PACKET + // and we still have the lock held. We also know + // that there is a send request in progress. If + // an ack for none or part of the last packet was + // received, then our send pointers have been + // adjusted to reflect that. + // + + // + // First get a packet for the current send. + // + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s == NULL) { + + // + // This function tries to allocate another packet pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + if (s == NULL) { + + // + // It is possible to come in here and already be in + // W_PACKET state -- this is because we may packetize + // when in that state, and rather than always be + // checking that we weren't in W_PACKET, we go + // ahead and check again here. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PACKET) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_PACKET; + + NB_GET_LOCK (&Device->Lock, &DeviceLockHandle); + if (!Connection->OnWaitPacketQueue) { + + NbiReferenceConnectionLock (Connection, CREF_W_PACKET); + + Connection->OnWaitPacketQueue = TRUE; + + InsertTailList( + &Device->WaitPacketConnections, + &Connection->WaitPacketLinkage + ); + +// NB_INSERT_TAIL_LIST( +// &Device->WaitPacketConnections, +// &Connection->WaitPacketLinkage, +// &Device->Lock); + + } + NB_FREE_LOCK (&Device->Lock, DeviceLockHandle); + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + // + // Set this now, we will change it later if needed. + // + + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + + + // + // Save these since they go in this next packet. + // + + ThisSendSequence = Connection->CurrentSend.SendSequence; + ThisOffset = (USHORT)Connection->CurrentSend.MessageOffset; + + + // + // Now see if we need to copy the buffer chain. + // + + PacketSize = Connection->MaximumPacketSize; + + if (Connection->CurrentSend.MessageOffset + PacketSize >= Connection->CurrentMessageLength) { + + PacketSize = Connection->CurrentMessageLength - Connection->CurrentSend.MessageOffset; + + if ((Connection->CurrentSend.MessageOffset == 0) && + (!Connection->SendBufferInUse)) { + + // + // If the entire send remaining fits in one packet, + // and this is also the first packet in the send, + // then the entire send fits in one packet and + // we don't need to build a duplicate buffer chain. + // + + BufferChain = Connection->CurrentSend.Buffer; + Reserved->u.SR_CO.NoNdisBuffer = TRUE; + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + Connection->CurrentSend.MessageOffset = Connection->CurrentMessageLength; + Connection->CurrentSend.Request = NULL; + ++Connection->CurrentSend.SendSequence; + Connection->SendBufferInUse = TRUE; + if (Connection->NewNetbios) { + if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + } else { + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + } else { + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + + if (BufferChain != NULL) { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), user buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + NdisChainBufferAtBack (Packet, BufferChain); + } else { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + } + + goto GotBufferChain; + + } + + } + + // + // We need to build a partial buffer chain. In the case + // where the current request is a partial one, we may + // build this from the ndis buffer chains of several + // requests. + // + + if (PacketSize > 0) { + + DesiredLength = PacketSize; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), allocate buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + while (TRUE) { + + Status = NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentSend.Buffer, + Connection->CurrentSend.BufferOffset, + DesiredLength, + &BufferChain, + &Connection->CurrentSend.Buffer, + &Connection->CurrentSend.BufferOffset, + &ActualLength); + + if (Status != STATUS_SUCCESS) { + + PNDIS_BUFFER CurBuffer, TmpBuffer; + + NB_DEBUG2 (SEND, ("Allocate buffer chain failed for packet %lx\n", Reserved)); + + // + // We could not allocate resources for this send. + // We'll put the connection on the packetize + // queue and hope we get more resources later. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + // + // Connection->CurrentSend can stay where it is. + // + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Free any buffers we have allocated on previous calls + // to BuildBufferChain inside this same while(TRUE) loop, + // then free the packet. + // + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + + if (Reserved->OwnedByConnection) { + Connection->SendPacketInUse = FALSE; + } else { + NbiPushSendPacket(Reserved); + } + + return; + + } + + NdisChainBufferAtBack (Packet, BufferChain); + Connection->CurrentSend.MessageOffset += ActualLength; + + DesiredLength -= ActualLength; + + if (DesiredLength == 0) { + + // + // We have gotten enough data for our packet. + // + + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + Connection->CurrentSend.Request = NULL; + } + break; + } + + // + // We ran out of buffer chain on this send, which means + // that we must have another one behind it (since we + // don't start packetizing partial sends until all of + // them are queued). + // + + Request = REQUEST_SINGLE_LINKAGE(Request); + if (Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + + } + + } else { + + // + // This is a zero-length send (in general we will go + // through the code before the if that uses the user's + // buffer, but not on a resend). + // + + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + CTEAssert (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength); + Connection->CurrentSend.Request = NULL; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no alloc buf\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + } + + Reserved->u.SR_CO.NoNdisBuffer = FALSE; + + if (Connection->NewNetbios) { + + ++Connection->CurrentSend.SendSequence; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + + if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + + } else if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else if (ThisSendSequence == Connection->RemoteRcvSequenceMax) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = 0; + ExitAfterSend = FALSE; + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } + + } else { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + ++Connection->CurrentSend.SendSequence; + } + } + +GotBufferChain: + + // + // We have a packet and a buffer chain, there are + // no other resources required for a send so we can + // fill in the header and go. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.Request = Connection->FirstMessageRequest; + + PacketLength = PacketSize + sizeof(NB_CONNECTION); + Reserved->u.SR_CO.PacketLength = PacketLength; + + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. BUGBUG: Put this in + // a contiguous buffer in the connection. + // + + Header->Session.ConnectionControlFlag = ConnectionControlFlag; + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = ThisSendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentMessageLength; + Header->Session.Offset = ThisOffset; + Header->Session.DataLength = (USHORT)PacketSize; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + // + // Reference the request to account for this send. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + ++REQUEST_REFCOUNT (Request); + + ++Device->TempFramesSent; + Device->TempFrameBytesSent += PacketSize; + + // + // Start the timer. + // + + NbiStartRetransmit (Connection); + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + + if (!ExitAfterSend) { + + // + // BUGBUG: Did we need to reference the connection until we + // get the lock back?? + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + // + // Jump back to the beginning of the function to + // repacketize. + + goto SendAnotherPacket; + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + +} /* NbiPacketizeSend */ + + +VOID +NbiAdjustSendWindow( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine adjusts a connection's send window if needed. It is + assumed that we just got an ack for a full send window. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + + if (Connection->RetransmitThisWindow) { + + // + // Move it down. Check if this keeps happening. + // + + if (Connection->SendWindowSize > 2) { + --Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lower window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + if (Connection->SendWindowIncrease) { + + // + // We just increased the window. + // + + ++Connection->IncreaseWindowFailures; + NB_DEBUG2 (SEND_WINDOW, ("%d consecutive increase failues on %lx (%lx)\n", Connection->IncreaseWindowFailures, Connection, Connection->CurrentSend.SendSequence)); + + if (Connection->IncreaseWindowFailures >= 2) { + + if (Connection->MaxSendWindowSize > 2) { + + // + // Lock ourselves at a smaller window. + // + + Connection->MaxSendWindowSize = Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lock send window at %d on %lx (%lx)\n", Connection->MaxSendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + Connection->IncreaseWindowFailures = 0; + } + + Connection->SendWindowIncrease = FALSE; + } + + } else { + + // + // Increase it if allowed, and make a note + // in case this increase causes problems in + // the next window. + // + + if (Connection->SendWindowSize < Connection->MaxSendWindowSize) { + + ++Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Raise window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + Connection->SendWindowIncrease = TRUE; + + } else { + + if (Connection->SendWindowIncrease) { + + // + // We just increased it and nothing failed, + // which is good. + // + + Connection->SendWindowIncrease = FALSE; + Connection->IncreaseWindowFailures = 0; + NB_DEBUG2 (SEND_WINDOW, ("Raised window OK on %lx (%lx)\n", Connection, Connection->CurrentSend.SendSequence)); + } + } + } + + + // + // This controls when we'll check this again. + // + + Connection->SendWindowSequenceLimit += Connection->SendWindowSize; + +} /* NbiAdjustSendWindow */ + + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have gotten an ack + for some data. It completes any sends that have + been acked, and if needed modifies the current send + pointer and queues the connection for repacketizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + ReceiveSequence - The receive sequence from the remote. + + BytesReceived - The number of bytes received in this message. + + Resend - If it is OK to resend based on this packet. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + PREQUEST RequestToComplete; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + + + // + // BUGBUG: We should change to stop the timer + // only if we go idle, since otherwise we still + // want it running, or will restart it when we + // packetize. + // + + // + // See how much is acked here. + // + + if ((Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) && + (ReceiveSequence == (USHORT)(Connection->CurrentSend.SendSequence)) && + (Connection->FirstMessageRequest != NULL)) { + + // Special check for 0 length send which was not accepted by the remote. + // In this case it will pass the above 3 conditions yet, nothing + // is acked. BUG#10395 + if (!Connection->CurrentSend.MessageOffset && Connection->CurrentSend.SendSequence == Connection->UnAckedSend.SendSequence ) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // This acks the entire message. + // + + NB_DEBUG2 (SEND, ("Got ack for entire message on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + Connection->CurrentSend.MessageOffset = 0; // BUGBUG: Needed? + Connection->UnAckedSend.MessageOffset = 0; + + // + // We don't adjust the send window since we likely stopped + // packetizing before we hit it. + // + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + if (Connection->NewNetbios) { + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + + } + + Connection->RetransmitThisWindow = FALSE; + + Request = Connection->FirstMessageRequest; + + // + // We dequeue these requests from the connection's + // send queue. + // + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + RequestToComplete = Request; + + } else { + + // + // There are still sends pending, this will get + // completed when the last send completes. Since + // we have already unlinked the request from the + // connection's send queue we can do this without + // any locks. + // + + RequestToComplete = NULL; + + } + + // + // Now see if there is a send to activate. + // + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Now complete any requests we need to. + // + + while (RequestToComplete != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (RequestToComplete); + REQUEST_STATUS (RequestToComplete) = STATUS_SUCCESS; + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + ++Connection->ConnectionInfo.TransmittedTsdus; + RequestToComplete = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } else if ((ReceiveSequence == Connection->CurrentSend.SendSequence) && + (Connection->NewNetbios || (BytesReceived == Connection->CurrentSend.MessageOffset)) && + (Connection->CurrentSend.Request != NULL)) { + + // + // This acks whatever we sent last time, and we are + // not done packetizing this send, so we can repacketize. + // BUGBUG: With SendSequence changing as it does now, + // don't need the CurrentSend.Request check??? + // + + NB_DEBUG2 (SEND, ("Got full ack on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + if (Connection->NewNetbios) { + + // + // If we are waiting for a window, and this does not open it + // anymore, then we don't reset our timers/retries. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) { + + if (Connection->RemoteRcvSequenceMax != BytesReceived) { + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + } + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + Connection->RetransmitThisWindow = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + // + // We may be on if this ack is duplicated. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if( Connection->FirstMessageRequest ) { + + // + // This acked part of the current send. If the + // remote is requesting a resend then we advance + // the current send location by the amount + // acked and resend from there. If he does + // not want a resend, just ignore this. + // + // We repacketize immediately because we have + // backed up the pointer, and this would + // cause us to ignore an ack for the amount + // sent. Since we don't release the lock + // until we have packetized, the current + // pointer will be advanced past there. + // + // BUGBUG: If he is acking more than we sent, we + // ignore this -- the remote is confused and there + // is nothing much we can do. + // + + if (Resend) { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RetransmitThisWindow = TRUE; + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else if (!Connection->NewNetbios && + ((ReceiveSequence == Connection->UnAckedSend.SendSequence) && + (BytesReceived <= Connection->CurrentSend.MessageOffset))) { + + ULONG BytesAcked = + BytesReceived - Connection->UnAckedSend.MessageOffset; + + // + // Old netbios. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedByBytes( + Connection, + BytesAcked); + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, reset the retry count + // + if ( BytesAcked ) { + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. We advance + // the back of our send window, but we don't repacketize. + // + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if ((Connection->CurrentSend.Request != NULL) && + (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } + } + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +} /* NbiReframeConnection */ + + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when have gotten an ack for + a full message, or received a response to a watchdog + probe, and need to check if the connection should + start packetizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + ULONG TempCount; + PTDI_REQUEST_KERNEL_SEND Parameters; + PDEVICE Device = NbiDevice; + + // + // See if there is a send to activate. + // + + if (Connection->SendQueue.Head != NULL) { + + // + // Take the first send off the queue and make + // it current. + // + + Request = Connection->SendQueue.Head; + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Request); + + // + // BUGBUG: Cache the information about being EOM + // in a more easily accessible location? + // + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a one-request message. + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } else { + + // + // This is a multiple-request message. We scan + // to see if we have the end of message received + // yet. + // + + TempCount = Parameters->SendLength; + TmpRequest = Request; + Request = REQUEST_SINGLE_LINKAGE(Request); + + while (Request != NULL) { + + TempCount += Parameters->SendLength; + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + Connection->CurrentSend.Request = TmpRequest; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (TmpRequest); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = TmpRequest; + Connection->LastMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = TempCount; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + break; + + } + + Request = REQUEST_SINGLE_LINKAGE(Request); + + } + + if (Request == NULL) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } + + } + + } else { + + Connection->FirstMessageRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + + NbiStartWatchdog (Connection); + + } + +} /* NbiRestartConnection */ + + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer by the specified number of bytes. It + assumes that there are enough send requests to + handle the number specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + + BytesAcked - The number of bytes acked. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + ULONG CurSendBufferLength; + ULONG BytesLeft = BytesAcked; + ULONG TempBytes; + + while (BytesLeft > 0) { + + NdisQueryBuffer (Connection->UnAckedSend.Buffer, NULL, &CurSendBufferLength); + + // + // See if bytes acked ends within the current buffer. + // + + if (Connection->UnAckedSend.BufferOffset + BytesLeft < + CurSendBufferLength) { + + Connection->UnAckedSend.BufferOffset += BytesLeft; + Connection->UnAckedSend.MessageOffset += BytesLeft; + break; + + } else { + + TempBytes = CurSendBufferLength - Connection->UnAckedSend.BufferOffset; + BytesLeft -= TempBytes; + Connection->UnAckedSend.MessageOffset += TempBytes; + + // + // No, so advance the buffer. + // + + Connection->UnAckedSend.BufferOffset = 0; + Connection->UnAckedSend.Buffer = + NDIS_BUFFER_LINKAGE (Connection->UnAckedSend.Buffer); + + // + // Is there a next buffer in this request? + // + + if (Connection->UnAckedSend.Buffer == NULL) { + + // + // No, so advance the request unless we are done. + // + + if (BytesLeft == 0) { + return; + } + + Connection->UnAckedSend.Request = + REQUEST_SINGLE_LINKAGE(Connection->UnAckedSend.Request); + + if (Connection->UnAckedSend.Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->UnAckedSend.Buffer = + REQUEST_NDIS_BUFFER (Connection->UnAckedSend.Request); + + } + } + } + +} /* NbiAdvanceUnAckedByBytes */ + + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer so that the next packet to send will be + the correct one for ReceiveSequence. UnAckedSend + must point to a known valid combination. It + assumes that there are enough send requests to + handle the sequence specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT PacketsAcked; + + // + // BUGBUG: Fix this to account for partial sends, where + // we might not have used the max. for all packets. + // + + PacketsAcked = ReceiveSequence - Connection->UnAckedSend.SendSequence; + + NbiAdvanceUnAckedByBytes( + Connection, + PacketsAcked * Connection->MaximumPacketSize); + + Connection->UnAckedSend.SendSequence += PacketsAcked; + +} /* NbiAdvanceUnAckedBySequence */ + + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a send + The request is found on the connection's send queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_SEND)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // Just stop the connection, that will tear down any + // sends. + // + // BUGBUG: Do we care about cancelling non-active + // sends without stopping the connection?? + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelSend */ + + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ) + +/*++ + +Routine Description: + + This routine is called to build an NDIS_BUFFER chain from a source + NDIS_BUFFER chain and offset into it. We assume we don't know the + length of the source Mdl chain, and we must allocate the NDIS_BUFFERs + for the destination chain, which we do from the NDIS buffer pool. + + If the system runs out of memory while we are building the destination + NDIS_BUFFER chain, we completely clean up the built chain and return with + NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl + and ByteOffset. + +Environment: + +Arguments: + + BufferPoolHandle - The buffer pool to allocate buffers from. + + CurrentSourceBuffer - Points to the start of the NDIS_BUFFER chain + from which to draw the packet. + + CurrentByteOffset - Offset within this NDIS_BUFFER to start the packet at. + + DesiredLength - The number of bytes to insert into the packet. + + DestinationBuffer - returned pointer to the NDIS_BUFFER chain describing + the packet. + + NewSourceBuffer - returned pointer to the NDIS_BUFFER that would + be used for the next byte of packet. NULL if the source NDIS_BUFFER + chain was exhausted. + + NewByteOffset - returned offset into the NewSourceBuffer for the next byte + of packet. NULL if the source NDIS_BUFFER chain was exhausted. + + ActualLength - The actual length of the data copied. + +Return Value: + + STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded + and was the correct length. + + STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while + building the destination chain. + +--*/ +{ + ULONG AvailableBytes; + ULONG CurrentByteCount; + ULONG BytesCopied; + PNDIS_BUFFER OldNdisBuffer; + PNDIS_BUFFER NewNdisBuffer; + NDIS_STATUS NdisStatus; + + + OldNdisBuffer = CurrentSourceBuffer; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + AvailableBytes = CurrentByteCount - CurrentByteOffset; + if (AvailableBytes > DesiredLength) { + AvailableBytes = DesiredLength; + } + + // + // Build the first NDIS_BUFFER, which could conceivably be the only one... + // + + NdisCopyBuffer( + &NdisStatus, + &NewNdisBuffer, + BufferPoolHandle, + OldNdisBuffer, + CurrentByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *DestinationBuffer = NewNdisBuffer; + BytesCopied = AvailableBytes; + + // + // Was the first NDIS_BUFFER enough data. + // + + if (BytesCopied == DesiredLength) { + if (CurrentByteOffset + AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = CurrentSourceBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset + AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + if (CurrentSourceBuffer->Next == NULL) { + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + + } + + // + // Need more data, so follow the in Mdl chain to create a packet. + // + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + while (OldNdisBuffer != NULL) { + + AvailableBytes = DesiredLength - BytesCopied; + if (AvailableBytes > CurrentByteCount) { + AvailableBytes = CurrentByteCount; + } + + NdisCopyBuffer( + &NdisStatus, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + BufferPoolHandle, + OldNdisBuffer, + 0, + AvailableBytes); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while (*DestinationBuffer != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE(*DestinationBuffer); + NdisFreeBuffer (*DestinationBuffer); + *DestinationBuffer = NewNdisBuffer; + } + + *NewByteOffset = CurrentByteOffset; + *NewSourceBuffer = CurrentSourceBuffer; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + + BytesCopied += AvailableBytes; + + if (BytesCopied == DesiredLength) { + if (AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = OldNdisBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = OldNdisBuffer; + *NewByteOffset = AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + } + + // + // We ran out of source buffer chain. + // + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + +} /* NbiBuildBufferChainFromBufferChain */ + diff --git a/private/ntos/tdi/isnp/nb/session.c b/private/ntos/tdi/isnp/nb/session.c new file mode 100644 index 000000000..fe998feb2 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/session.c @@ -0,0 +1,2450 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + session.c + +Abstract: + + This module contains the code to handle session frames + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL +#pragma hdrstop + +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +VOID +NbiNoteNewConnection( + PCONNECTION pConnection + ); +#endif + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiSendDeathPacket( + IN PCONNECTION Connection, + IN CTELockHandle LockHandle + ) +{ + PNDIS_PACKET Packet = PACKET(&NbiGlobalDeathPacket); + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + NDIS_STATUS NdisStatus; + + if ( Reserved->SendInProgress ) { + DbgPrint("***Could not send death packet - in use\n"); + NB_FREE_LOCK(&Connection->Lock, LockHandle); + return; + } + + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DEATH_PACKET; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + Header->Session.ConnectionControlFlag = 0; + Header->Session.DataStreamType = NB_CMD_DEATH_PACKET; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + + + NB_FREE_LOCK(&Connection->Lock, LockHandle); + + DbgPrint("*****Death packet is being sent for connection %lx, to <%.16s>\n",Connection, Connection->RemoteName); + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} +#endif //RSRC_TIMEOUT_DBG + + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = NbiDevice; + ULONG Hash; + ULONG ReceiveFlags; + ULONG IndicateBytesTransferred; + ULONG DataAvailable, DataIndicated; + ULONG DestBytes, BytesToTransfer; + PUCHAR DataHeader; + BOOLEAN Last, CompleteReceive, EndOfMessage, PartialReceive, CopyLookahead; + NTSTATUS Status; + NDIS_STATUS NdisStatus; + ULONG NdisBytesTransferred; + PIRP ReceiveIrp; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + ULONG BufferChainLength; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + +#ifdef RSRC_TIMEOUT_DBG + if ( Connection->FirstMessageRequest && NbiGlobalDebugResTimeout ) { + LARGE_INTEGER CurrentTime, ElapsedTime; + KeQuerySystemTime(&CurrentTime); + ElapsedTime.QuadPart = CurrentTime.QuadPart - Connection->FirstMessageRequestTime.QuadPart; +// DbgPrint("*****Elapsed %lx.%lx time\n",ElapsedTime.HighPart,ElapsedTime.LowPart); + if ( ElapsedTime.QuadPart > NbiGlobalMaxResTimeout.QuadPart ) { + + DbgPrint("*****Connection %lx is not copleting irp %lx for %lx.%lx time\n",Connection, Connection->FirstMessageRequest, + ElapsedTime.HighPart,ElapsedTime.LowPart); + DbgPrint("************irp arrived at %lx.%lx current time %lx.%lx\n", + Connection->FirstMessageRequestTime.HighPart,Connection->FirstMessageRequestTime.LowPart, + CurrentTime.HighPart, CurrentTime.LowPart); + + NbiSendDeathPacket( Connection, LockHandle ); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + } + } +#endif //RSRC_TIMEOUT_DBG + + // + // The connection is up, see if this is data should + // be received. + // + + if (Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) { + + // + // This is an ack. This call releases + // the lock. BUGBUG: Does this need to + // be a function? + // + + NbiProcessDataAck( + Connection, + Sess, + RemoteAddress + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // See if there is any piggyback ack here. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. Even the old netbios sometimes piggyback + // acks (and doesn't send the explicit ack). + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if ((Connection->NewNetbios) && + (Connection->CurrentSend.SendSequence != Connection->UnAckedSend.SendSequence)) { + + // + // For the new netbios, even if we are not waiting + // for an ack he may have acked something with this + // send and we should check, since it may allow + // us to open our send window. + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } + + // + // This is data on the connection. First make sure + // it is the data we expect next. + // + + if (Connection->NewNetbios) { + + if (Sess->SendSequence != Connection->ReceiveSequence) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp data on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving a packet we have already seen, just + // send a normal ack, otherwise force a resend. This test + // we do is equivalent to + // Sess->SendSequence < Connection->ReceiveSequence + // but rearranged so it works when the numbers wrap. + // + + if ((SHORT)(Sess->SendSequence - Connection->ReceiveSequence) < 0) { + + // + // Since this is a resend, check if the local + // target has changed. + // +#if defined(_PNP_POWER) + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, sizeof(IPX_LOCAL_TARGET))) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx, (%d,%d)\n", Connection, + Connection->LocalTarget.NicHandle.NicId, RemoteAddress->NicHandle.NicId); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#else + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#endif _PNP_POWER + AckType = NbiAckResponse; + + } else { + + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // Old netbios. + // + + if ((Sess->SendSequence != Connection->ReceiveSequence) || + (Sess->Offset != Connection->CurrentReceive.MessageOffset)) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving the last packet again, just + // send a normal ack, otherwise force a resend. + // + + if (((Sess->SendSequence == Connection->ReceiveSequence) && + ((ULONG)(Sess->Offset + Sess->DataLength) == Connection->CurrentReceive.MessageOffset)) || + (Sess->SendSequence == (USHORT)(Connection->ReceiveSequence-1))) { + AckType = NbiAckResponse; + } else { + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + + IndicateBytesTransferred = 0; + DataAvailable = PacketSize - sizeof(NB_CONNECTION); + DataIndicated = LookaheadBufferSize - sizeof(NB_CONNECTION); + DataHeader = LookaheadBuffer + sizeof(NB_CONNECTION); + + ++Device->TempFramesReceived; + Device->TempFrameBytesReceived += DataAvailable; + + if (Connection->CurrentIndicateOffset) { + CTEAssert (DataAvailable >= Connection->CurrentIndicateOffset); + DataAvailable -= Connection->CurrentIndicateOffset; + if (DataIndicated >= Connection->CurrentIndicateOffset) { + DataIndicated -= Connection->CurrentIndicateOffset; + } else { + DataIndicated = 0; + } + DataHeader += Connection->CurrentIndicateOffset; + } + + CopyLookahead = (BOOLEAN)(MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA); + + if (Connection->NewNetbios) { + Last = (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_EOM) != 0); + } else { + Last = (BOOLEAN)(Sess->Offset + Sess->DataLength == Sess->TotalDataLength); + } + + Connection->CurrentReceiveNoPiggyback = + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) != 0); + + if (Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) { + + // + // We don't have a receive posted, so see if we can + // get one from the queue or our client. + // + + if (Connection->ReceiveQueue.Head != NULL) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Request = Connection->ReceiveQueue.Head; + Connection->ReceiveQueue.Head = REQUEST_SINGLE_LINKAGE(Request); + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NB_DEBUG2 (RECEIVE, ("Activated receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + if ((Connection->ReceiveUnaccepted == 0) && + (Connection->AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE])) { + + Connection->ReceiveState = CONNECTION_RECEIVE_INDICATE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL; + if (Last) { + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + if (CopyLookahead) { + ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD; + } + + Status = (*Connection->AddressFile->ReceiveHandler)( + Connection->AddressFile->HandlerContexts[TDI_EVENT_RECEIVE], + Connection->Context, + ReceiveFlags, + DataIndicated, + DataAvailable, + &IndicateBytesTransferred, + DataHeader, + &ReceiveIrp); + + if (Status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // We got an IRP, activate it. + // + + Request = NbiAllocateRequest (Device, ReceiveIrp); + + IF_NOT_ALLOCATED(Request) { + + ReceiveIrp->IoStatus.Information = 0; + ReceiveIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (ReceiveIrp, IO_NETWORK_INCREMENT); + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + CTEAssert (REQUEST_OPEN_CONTEXT(Request) == Connection); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + Connection->ReceiveUnaccepted = DataAvailable - IndicateBytesTransferred; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + NB_DEBUG2 (RECEIVE, ("Indicate got receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + // + // The connection has been stopped. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // He accepted some or all of the data. + // + + NB_DEBUG2 (RECEIVE, ("Indicate took receive data %lx (%d)\n", Connection, Connection->ReceiveSequence)); + + if ( (IndicateBytesTransferred >= DataAvailable)) { + + CTEAssert (IndicateBytesTransferred == DataAvailable); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentIndicateOffset = 0; + if ( Last ) { + Connection->CurrentReceive.MessageOffset = 0; + } else { + Connection->CurrentReceive.MessageOffset+= IndicateBytesTransferred; + } + + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + Connection->NoPiggybackHeuristic = (BOOLEAN) + (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // We will do the easiest thing here, which + // is to send an ack for the amount he + // took, and force a retransmit on the + // remote. For net netbios we make a note + // of how many bytes were taken and ask + // for a resend. + // + // BUGBUG: Handle this better?? + // + +#if DBG + DbgPrint ("NBI: Client took partial indicate data\n"); +#endif + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive.MessageOffset += + IndicateBytesTransferred; + Connection->ReceiveUnaccepted = + DataAvailable - IndicateBytesTransferred; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + Connection->CurrentIndicateOffset = IndicateBytesTransferred; + // + // NOTE: We don't advance ReceiveSequence + // + } + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + Connection->NewNetbios ? + NbiAckResend : NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } else { + + // + // No IRP returned. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveUnaccepted = DataAvailable; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_DEBUG (RECEIVE, ("Indicate got no receive on %lx (%lx)\n", Connection, Status)); + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // No receive handler. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + if (Connection->ReceiveUnaccepted == 0) { + NB_DEBUG (RECEIVE, ("No receive, no handler on %lx\n", Connection)); + } else { + NB_DEBUG (RECEIVE, ("No receive, ReceiveUnaccepted %d on %lx\n", + Connection->ReceiveUnaccepted, Connection)); + } + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + } else if (Connection->ReceiveState != CONNECTION_RECEIVE_ACTIVE) { + + // + // If we have a transfer in progress, or are waiting for + // a receive to be posted, then ignore this frame. + // + + NB_DEBUG2 (RECEIVE, ("Got data on %lx, state %d (%d)\n", Connection, Connection->ReceiveState, Connection->ReceiveSequence)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + // + // At this point we have a receive and it is set to + // the correct current location. + // + + DestBytes = Connection->ReceiveLength - Connection->CurrentReceive.Offset; + BytesToTransfer = DataAvailable - IndicateBytesTransferred; + + if (DestBytes < BytesToTransfer) { + + // + // If the data overflows the current receive, then make a + // note that we should complete the receive at the end of + // transfer data, but with EOR false. + // + + EndOfMessage = FALSE; + CompleteReceive = TRUE; + PartialReceive = TRUE; + BytesToTransfer = DestBytes; + + } else if (DestBytes == BytesToTransfer) { + + // + // If the data just fills the current receive, then complete + // the receive; EOR depends on whether this is a DOL or not. + // + + EndOfMessage = Last; + CompleteReceive = TRUE; + PartialReceive = FALSE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + EndOfMessage = Last; + CompleteReceive = Last; + PartialReceive = FALSE; + + } + + // + // If we can copy the data directly, then update our + // pointers, send an ack, and do the copy. + // + + if ((BytesToTransfer > 0) && + (IndicateBytesTransferred + BytesToTransfer <= DataIndicated)) { + + ULONG BytesNow, BytesLeft; + PUCHAR CurTarget, CurSource; + ULONG CurTargetLen; + PNDIS_BUFFER CurBuffer; + ULONG CurByteOffset; + + NB_DEBUG2 (RECEIVE, ("Direct copy of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + CurBuffer = Connection->CurrentReceive.Buffer; + CurByteOffset = Connection->CurrentReceive.BufferOffset; + + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurTarget += CurByteOffset; + CurTargetLen -= CurByteOffset; + + CurSource = DataHeader + IndicateBytesTransferred; + BytesLeft = BytesToTransfer; + + while (TRUE) { + + if (CurTargetLen < BytesLeft) { + BytesNow = CurTargetLen; + } else { + BytesNow = BytesLeft; + } + TdiCopyLookaheadData( + CurTarget, + CurSource, + BytesNow, + CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + if (BytesNow == CurTargetLen) { + BytesLeft -= BytesNow; + CurBuffer = CurBuffer->Next; + CurByteOffset = 0; + if (BytesLeft > 0) { + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurSource += BytesNow; + } else { + break; + } + } else { + CurByteOffset += BytesNow; + CTEAssert (BytesLeft == BytesNow); + break; + } + + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->CurrentReceive.Buffer = CurBuffer; + Connection->CurrentReceive.BufferOffset = CurByteOffset; + + Connection->CurrentReceive.Offset += BytesToTransfer; + Connection->CurrentReceive.MessageOffset += BytesToTransfer; + + if (CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (EndOfMessage) { + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (PartialReceive) { + Connection->CurrentIndicateOffset += BytesToTransfer; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // and CANCEL Lock. + // + + NbiCompleteReceive( + Connection, + EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + // + // CompleteReceive is FALSE, so EndOfMessage is FALSE. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + + // + // We have to set up a call to transfer data and send + // the ack after it completes (if it succeeds). + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + DataAvailable); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->u.RR_CO.Connection = Connection; + ReceiveReserved->u.RR_CO.EndOfMessage = EndOfMessage; + ReceiveReserved->u.RR_CO.CompleteReceive = CompleteReceive; + ReceiveReserved->u.RR_CO.PartialReceive = PartialReceive; + + ReceiveReserved->Type = RECEIVE_TYPE_DATA; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("TransferData of 0 bytes %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiTransferDataComplete( + Packet, + NDIS_STATUS_SUCCESS, + 0); + + return; + } + + // + // If needed, build a buffer chain to describe this + // to NDIS. + // + + Connection->PreviousReceive = Connection->CurrentReceive; + + if ((Connection->CurrentReceive.Offset == 0) && + CompleteReceive) { + + BufferChain = Connection->CurrentReceive.Buffer; + BufferChainLength = BytesToTransfer; + Connection->CurrentReceive.Buffer = NULL; + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + + } else { + + if (NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentReceive.Buffer, + Connection->CurrentReceive.BufferOffset, + BytesToTransfer, + &BufferChain, + &Connection->CurrentReceive.Buffer, + &Connection->CurrentReceive.BufferOffset, + &BufferChainLength) != NDIS_STATUS_SUCCESS) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("Could not build receive buffer chain %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + ReceiveReserved->u.RR_CO.NoNdisBuffer = FALSE; + + } + + + NdisChainBufferAtFront (Packet, BufferChain); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("TransferData of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + sizeof(NB_CONNECTION) + + Connection->CurrentIndicateOffset + IndicateBytesTransferred, + BytesToTransfer, + Packet, + (PUINT)&NdisBytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (NdisBytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete ( + Packet, + NdisStatus, + NdisBytesTransferred); + + } + + return; + + } + + } else if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + // + // If this is the ack for the session initialize, then + // complete the pending connects. This routine releases + // the connection lock. + // + + NbiProcessSessionInitAck( + Connection, + Sess + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + // + // This is a session initialize frame. + // + // BUGBUG: If there is more data than in the lookahead + // buffer, we won't be able to echo it back in the + // response. + // + + NbiProcessSessionInitialize( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + + } + +} /* NbiProcessSessionData */ + + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine processes an ack on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The session frame. + + RemoteAddress - The local target this packet was received from. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + BOOLEAN Resend; + + // + // Make sure we expect an ack right now. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (((Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W)) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We are waiting for an ack (because we completed + // packetizing a send, or ran out of receive window). + // + // This will complete any sends that are acked by + // this receive, and if necessary readjust the + // send pointer and requeue the connection for + // packetizing. It release the connection lock. + // + + if (Connection->ResponseTimeout) { + Resend = TRUE; + Connection->ResponseTimeout = FALSE; + } else { + Resend = (BOOLEAN) + ((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0); + } + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + Resend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We had a probe outstanding and got a response. Restart + // the connection if needed (a send may have just been + // posted while the probe was outstanding). + // + // BUGBUG: We should check that the response is really + // correct. + // + + if (Connection->NewNetbios) { + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + } + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + if (Connection->NewNetbios) { + + // + // We are packetizing, reframe. In the unlikely + // event that this acks everything we may packetize + // in this call, but that is OK (the other thread + // will exit if we finish up). More normally we + // will just advance UnAcked send a bit. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0) + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +#if 0 + + // + // BUGBUG: Should handle this case (i.e. may be in W_PACKET). + // + + } else if ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0) { + + DbgPrint ("NWLNKNB: Ignoring ack, state is %d\n", Connection->SubState); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); +#endif + + } else { + + // + // We got a probe from the remote. Some old DOS clients + // send probes that do not have the send ack bit on, + // so we respond to any probe if none of the conditions + // above are true. This call releases the lock. + // + // We use the IgnoreNextDosProbe flag to ignore every + // second probe of this nature, to avoid a data ack + // war between two machines who each think they are + // responding to the other. This flag is set to FALSE + // whenever we send an ack or a probe. + // + + if (!Connection->IgnoreNextDosProbe) { + + // + // Since this is a probe, check if the local + // target has changed. + // + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + Connection->IgnoreNextDosProbe = TRUE; + + } else { + + Connection->IgnoreNextDosProbe = FALSE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + +} /* NbiProcessDataAck */ + + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION frames which have + a remote connection ID of 0xffff -- these are session + initialize frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + PacketBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + CONNECT_INDICATION TempConnInd; + PCONNECT_INDICATION ConnInd; + PCONNECTION Connection; + PADDRESS Address; + PREQUEST Request, ListenRequest, AcceptRequest; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + ULONG Hash; + TA_NETBIOS_ADDRESS SourceName; + PIRP AcceptIrp; + CONNECTION_CONTEXT ConnectionContext; + NTSTATUS AcceptStatus; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_REQUEST_KERNEL_LISTEN ListenParameters; + PTDI_CONNECTION_INFORMATION ListenInformation; + PTDI_CONNECTION_INFORMATION RemoteInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * ListenAddress; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + // + // Verify that the whole packet is there. + // + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT))) { +#if DBG + DbgPrint ("NBI: Got short session initialize, %d/%d\n", PacketSize, + sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); +#endif + return; + } + + // + // Verify that MaximumDataSize that remote can support is > 0 + // Bug # 19405 + // + if ( SessInit->MaximumDataSize == 0 ) { + NB_DEBUG(CONNECTION, ("Connect request with MaximumDataSize == 0\n" +)); + return; + } + + // + // Make sure this is for an address we care about. + // + + if (Device->AddressCounts[SessInit->DestinationName[0]] == 0) { + return; + } + + Address = NbiFindAddress (Device, (PUCHAR)SessInit->DestinationName); + + if (Address == NULL) { + return; + } + + // + // First see if we have a session to this remote. We check + // this in case our ack of the session initialize was dropped, + // we don't want to reindicate our client. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + for (Hash = 0; Hash < CONNECTION_HASH_COUNT; Hash++) { + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if ((RtlEqualMemory (&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12)) && + (Connection->RemoteConnectionId == Sess->SourceConnectionId) && + (Connection->State != CONNECTION_STATE_DISCONNECT)) { + + // + // Yes, we are talking to this remote, if it is active then + // respond, otherwise we are in the process of connecting + // and we will respond eventually. + // + +#if DBG + DbgPrint ("NBI: Got connect request on active connection %lx\n", Connection); +#endif + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + NbiSendSessionInitAck( + Connection, + (PUCHAR)(SessInit+1), + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)), + NULL); // lock is not held + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + Connection = Connection->NextConnection; + } + } + + + TdiBuildNetbiosAddress ((PUCHAR)SessInit->SourceName, FALSE, &SourceName); + + // + // Scan the queue of listens to see if there is one that + // satisfies this request. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ListenQueue.Flink; + p != &Device->ListenQueue; + p = p->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->AddressFile->Address != Address) { + continue; + } + + // + // Check that this listen is not specific to a different + // netbios name. + // + + ListenParameters = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(Request); + ListenInformation = ListenParameters->RequestConnectionInformation; + + if (ListenInformation && + (ListenInformation->RemoteAddress) && + (ListenAddress = NbiParseTdiAddress(ListenInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + SessInit->SourceName, + ListenAddress->NetbiosName, + 16))) { + continue; + } + + // + // This connection is valid, so we use it. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen %lx\n", Connection)); + + RemoveEntryList (REQUEST_LINKAGE(Request)); + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + // + // Save this information now for whenever we complete the listen. + // + + RemoteInformation = ListenParameters->ReturnConnectionInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + if (ListenParameters->RequestFlags & TDI_QUERY_ACCEPT) { + + // + // We have to wait for an accept before sending the + // session init ack, so we complete the listen and wait. + // + + ListenRequest = Request; + Connection->ListenRequest = NULL; + + NB_DEBUG2 (CONNECTION, ("Queued listen on %lx awaiting accept\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ACCEPT; + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_W_ACCEPT); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } else { + + // + // We are ready to go, so we send out the find route request + // for the remote. We keep the listen alive and the CREF_LISTEN + // reference on until this completes. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen on %lx\n", Connection)); + + ListenRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } + + // + // Complete the listen if needed. + // + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION (ListenRequest) = 0; + REQUEST_STATUS (ListenRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK ( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest (Device, ListenRequest); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + + return; + + } + + // + // We could not find a listen, so we indicate to every + // client. Make sure there is no session initialize for this + // remote being indicated. If there is not, we insert + // ourselves in the queue to block others. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ConnectIndicationInProgress.Flink; + p != &Device->ConnectIndicationInProgress; + p = p->Flink) { + + ConnInd = CONTAINING_RECORD (p, CONNECT_INDICATION, Linkage); + + if ((RtlEqualMemory(ConnInd->NetbiosName, SessInit->DestinationName, 16)) && + (RtlEqualMemory(&ConnInd->RemoteAddress, Conn->IpxHeader.SourceNetwork, 12)) && + (ConnInd->ConnectionId == Sess->SourceConnectionId)) { + + // + // We are processing a request from this remote for + // the same ID, to avoid confusion we just exit. + // + +#if DBG + DbgPrint ("NBI: Already processing connect to <%.16s>\n", SessInit->DestinationName); +#endif + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + } + + RtlCopyMemory (&TempConnInd.RemoteAddress, SessInit->DestinationName, 16); + RtlCopyMemory (&TempConnInd.RemoteAddress, Conn->IpxHeader.SourceNetwork, 12); + TempConnInd.ConnectionId = Sess->SourceConnectionId; + + InsertTailList (&Device->ConnectIndicationInProgress, &TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + + // + // Now scan through the address to find someone who has + // an indication routine registed and wants this connection. + // + + + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No posted listen requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_CONNECT]) { + + if ((*AddressFile->ConnectionHandler)( + AddressFile->HandlerContexts[TDI_EVENT_CONNECT], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, // user data + NULL, + 0, // options + NULL, + &ConnectionContext, + &AcceptIrp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + continue; + + } + + AcceptRequest = NbiAllocateRequest (Device, AcceptIrp); + + IF_NOT_ALLOCATED(AcceptRequest) { + + AcceptStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + // + // The client accepted the connect, so activate + // the connection and complete the accept. + // listen. This lookup references the connection + // so we know it will remain valid. + // + + Connection = NbiLookupConnectionByContext ( + AddressFile, + ConnectionContext); + + if (Connection != NULL) { + + ASSERT (Connection->AddressFile == AddressFile); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle2); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + NB_DEBUG2 (CONNECTION, ("Indication on %lx returned connection %lx\n", AddressFile, Connection)); + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + Connection->Retries = Device->KeepAliveCount; + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + NbiReferenceConnectionLock (Connection, CREF_ACCEPT); + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + Connection->AcceptRequest = AcceptRequest; + AcceptStatus = STATUS_PENDING; + + // + // Take us out of this list now, we will jump to + // FoundConnection which is past the removal below. + // + + RemoveEntryList (&TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned invalid connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + + } + + NbiDereferenceConnection (Connection, CREF_BY_CONTEXT); + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned unknown connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + + } + } + + // + // Complete the accept request in the failure case. + // + + if (AcceptStatus != STATUS_PENDING) { + + REQUEST_STATUS (AcceptRequest) = AcceptStatus; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest (Device, AcceptRequest); + + } else { + + // + // We found a connection, so we break; this is + // a jump since the while exit assumes the + // address lock is held. + // + + goto FoundConnection; + + } + + } + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Take us out of the list that blocks other indications + // from this remote to this address. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + RemoveEntryList (&TempConnInd.Linkage); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + +FoundConnection: + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + + NbiDereferenceAddress (Address, AREF_FIND); + +} /* NbiProcessSessionInitialize */ + + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine handles session init ack frames. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The netbios header for the received frame. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + BOOLEAN TimerWasStopped = FALSE; + CTELockHandle CancelLH; + + if ((Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) && + (Sess->SendSequence == 0x0000) && + (Sess->ReceiveSequence == 0x0001)) { + + NB_DEBUG2 (CONNECTION, ("Completing connect on %lx\n", Connection)); + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + if (Connection->Retries == NbiDevice->ConnectionCount) { + ++NbiDevice->Statistics.ConnectionsAfterNoRetry; + } else { + ++NbiDevice->Statistics.ConnectionsAfterRetry; + } + ++NbiDevice->Statistics.OpenConnections; + + Connection->Retries = NbiDevice->KeepAliveCount; + NbiStartWatchdog (Connection); + + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 1; + Connection->UnAckedSend.SendSequence = 1; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 0; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = NbiDevice->KeepAliveCount; + if (NbiDevice->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveWindowSize - 1); + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 3; + } else { + Connection->NewNetbios = FALSE; + } + + if (Connection->MaximumPacketSize > SessInit->MaximumDataSize) { + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + } + + Request = Connection->ConnectRequest; + +#ifdef RASAUTODIAL + // + // Check to see if we have to notify + // the automatic connection driver about + // this connection. + // + if (fAcdLoadedG) { + BOOLEAN fEnabled; + CTELockHandle AcdHandle; + + CTEGetLock(&AcdDriverG.SpinLock, &AcdHandle); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, AcdHandle); + if (fEnabled) + NbiNoteNewConnection(Connection); + } +#endif // RASAUTODIAL + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_SUCCESS; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiTransferReferenceConnection (Connection, CREF_CONNECT, CREF_ACTIVE); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiProcessSessionInitAck */ + + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + + // + // We reply to any session end, even if we don't know the + // connection, to speed up the disconnect on the remote. + // + + if (Connection == NULL) { + + NB_DEBUG (CONNECTION, ("Session end received on unknown id %lx\n", Sess->DestConnectionId)); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. We do this in case a full send has been + // received by the remote but he did not send an + // ack before the session went down -- this will + // prevent us from failing a send which actually + // succeeded. If we are not in W_ACK this may ack + // part of a send, but in that case we don't care + // since StopConnection will abort it anyway and + // the amount successfully received by the remote + // doesn't matter. + // + // This releases the lock. BUGBUG: Fix this. + // + + NB_DEBUG2 (CONNECTION, ("Session end at W_ACK, reframing %lx (%d)\n", Connection, Sess->ReceiveSequence)); + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle1)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on connection %lx\n", Connection)); + + } + + // + // This call sets the state to DISCONNECT and + // releases the connection lock. It will also + // complete a disconnect wait request if one + // is pending, and indicate to our client + // if needed. + // + + NbiStopConnection( + Connection, + STATUS_REMOTE_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on inactive connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEnd */ + + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END_ACK frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // Stop the timer, when the reference goes away it + // will shut down. We set the substate so if the + // timer is running it will not restart (BUGBUG: + // there is a small window here, but it is not + // harmful, we will just have to timeout one + // more time). + // + + NB_DEBUG2 (CONNECTION, ("Got session end ack on %lx\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_D_GOT_ACK; + if (CTEStopTimer (&Connection->Timer)) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEndAck */ + diff --git a/private/ntos/tdi/isnp/nb/sources.inc b/private/ntos/tdi/isnp/nb/sources.inc new file mode 100644 index 000000000..f54b4918b --- /dev/null +++ b/private/ntos/tdi/isnp/nb/sources.inc @@ -0,0 +1,69 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnknb + +TARGETNAME=nwlnknb +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\inc;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 -DRASAUTODIAL +#-DRSRC_TIMEOUT_DBG + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +SOURCES= \ + ..\action.c \ + ..\address.c \ + ..\autodial.c \ + ..\bind.c \ + ..\cache.c \ + ..\config.c \ + ..\connect.c \ + ..\datagram.c \ + ..\device.c \ + ..\driver.c \ + ..\event.c \ + ..\frame.c \ + ..\nwlnknb.rc \ + ..\packet.c \ + ..\query.c \ + ..\receive.c \ + ..\send.c \ + ..\session.c \ + ..\timer.c + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj +
\ No newline at end of file diff --git a/private/ntos/tdi/isnp/nb/timer.c b/private/ntos/tdi/isnp/nb/timer.c new file mode 100644 index 000000000..381b120e5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/timer.c @@ -0,0 +1,1233 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + This module contains code which implements the timers for + netbios. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +ULONG NbiTickIncrement = 0; +ULONG NbiShortTimerDeltaTicks = 0; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiInitializeTimers) +#endif + + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the retransmit timer for the given connection. + The connection is inserted on the short list if it isn't on already. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Insert us in the queue if we aren't in it. + // + + Connection->Retransmit = + Device->ShortAbsoluteTime + Connection->CurrentRetransmitTimeout; + + if (!Connection->OnShortList) { + + CTEAssert (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnShortList) { + Connection->OnShortList = TRUE; + InsertTailList (&Device->ShortList, &Connection->ShortList); + } + + if (!Device->ShortListActive) { + NbiStartShortTimer (Device); + Device->ShortListActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartRetransmit */ + + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the watchdog timer for a connection. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + Connection->Watchdog = Device->LongAbsoluteTime + Connection->WatchdogTimeout; + + if (!Connection->OnLongList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnLongList) { + Connection->OnLongList = TRUE; + InsertTailList (&Device->LongList, &Connection->LongList); + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartWatchdog */ + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the retransmit timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Retransmit = 0; + +} /* NbiStopRetransmit */ + + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the watchdog timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Watchdog = 0; + +} /* NbiStopWatchdog */ +#endif + + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's retransmit timer + expires. It is called from NbiShortTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = NbiDevice; + BOOLEAN SendFindRoute; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + SendFindRoute = FALSE; + + ++Device->Statistics.ResponseTimerExpirations; + + if (!(Connection->NewNetbios) && + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Wait for ack timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // Set our current packetize location back to the + // spot of the last ack, and start up again. + // + // BUGBUG: Should we send a probe here? + // + + Connection->CurrentSend = Connection->UnAckedSend; + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NB_DEBUG2 (SEND, ("Connection %lx retransmit timeout\n", Connection)); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // This releases the lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) || + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Probe timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NbiStartRetransmit (Connection); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // Set this so we know to retransmit when the ack + // is received. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PROBE) { + Connection->ResponseTimeout = TRUE; + } + + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + if (SendFindRoute) { + + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_FORCE_RIP; + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireRetansmit */ + + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's watchdog timer + expires. It is called from NbiLongTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // If we are not idle, then something else is happening + // so the watchdog is unnecessary. + // + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE)) { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->SubState = CONNECTION_SUBSTATE_A_W_PROBE; + NbiStartRetransmit (Connection); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireWatchdog */ + + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the short connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p, nextp; + PDEVICE Device = (PDEVICE)Context; + PCONNECTION Connection; + BOOLEAN RestartTimer = FALSE; + LARGE_INTEGER CurrentTick; + LARGE_INTEGER TickDifference; + ULONG TickDelta; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // This prevents anybody from starting the timer while we + // are in this routine (the main reason for this is that it + // makes it easier to determine whether we should restart + // it at the end of this routine). + // + + Device->ProcessingShortTimer = TRUE; + + // + // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + + KeQueryTickCount (&CurrentTick); + + TickDifference.QuadPart = CurrentTick.QuadPart - + Device->ShortTimerStart.QuadPart; + + TickDelta = TickDifference.LowPart / NbiShortTimerDeltaTicks; + if (TickDelta == 0) { + TickDelta = 1; + } + + Device->ShortAbsoluteTime += TickDelta; + + if (Device->ShortAbsoluteTime >= 0xf0000000) { + + ULONG Timeout; + + Device->ShortAbsoluteTime -= 0xe0000000; + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + Timeout = Connection->Retransmit; + if (Timeout) { + Connection->Retransmit = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + ASSERT (Connection->OnShortList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Retransmit && + (Device->ShortAbsoluteTime > Connection->Retransmit)) { + + Connection->Retransmit = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireRetransmit (Connection); // no locks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnShortList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + + break; + + } + + nextp = p->Flink; + + if (Connection->Retransmit == 0) { + + Connection->OnShortList = FALSE; + RemoveEntryList(p); + + // + // Do another check; that way if someone slipped in between + // the check of Connection->Tx and the OnShortList = FALSE and + // therefore exited without inserting, we'll catch that here. + // + + if (Connection->Retransmit != 0) { + InsertTailList(&Device->ShortList, &Connection->ShortList); + Connection->OnShortList = TRUE; + } + + } + + p = nextp; + + } + + // + // If the list is empty note that, otherwise ShortListActive + // remains TRUE. + // + + if (IsListEmpty (&Device->ShortList)) { + Device->ShortListActive = FALSE; + } + + + // + // Connection Data Ack timers. This queue is used to indicate + // that a piggyback ack is pending for this connection. We walk + // the queue, for each element we check if the connection has + // been on the queue for enough times through here, + // If so, we take it off and send an ack. Note that + // we have to be very careful how we walk the queue, since + // it may be changing while this is running. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + // + // Skip this connection if it is not queued or it is + // too recent to matter. We may skip incorrectly if + // the connection is just being queued, but that is + // OK, we will get it next time. + // + + if (!Connection->DataAckPending) { + continue; + } + + ++Connection->DataAckTimeouts; + + if (Connection->DataAckTimeouts < Device->AckDelayTime) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_SHORT_D_ACK); + + Device->DataAckQueueChanged = FALSE; + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + // + // Check the correct connection flag, to ensure that a + // send has not just taken him off the queue. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->DataAckPending) { + + // + // Yes, we were waiting to piggyback an ack, but no send + // has come along. Turn off the flags and send an ack. + // We set PiggybackAckTimeout to TRUE so that we won't try + // to piggyback a response until we get back traffic. + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = TRUE; + ++Device->Statistics.AckTimerExpirations; + ++Device->Statistics.PiggybackAckTimeouts; + + // + // This call releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_SHORT_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // If the list has changed, then we need to stop processing + // since p->Flink is not valid. + // + + if (Device->DataAckQueueChanged) { + break; + } + + } + + if (IsListEmpty (&Device->DataAckConnections)) { + Device->DataAckActive = FALSE; + } + + + // + // Update the real counters from the temp ones. We have + // TimerLock here, which is good enough. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesSent, + Device->TempFrameBytesSent); + Device->Statistics.DataFramesSent += Device->TempFramesSent; + + Device->TempFrameBytesSent = 0; + Device->TempFramesSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesReceived, + Device->TempFrameBytesReceived); + Device->Statistics.DataFramesReceived += Device->TempFramesReceived; + + Device->TempFrameBytesReceived = 0; + Device->TempFramesReceived = 0; + + + // + // Determine if we have to restart the timer. + // + + Device->ProcessingShortTimer = FALSE; + + if ((Device->ShortListActive || Device->DataAckActive) && + (Device->State != DEVICE_STATE_STOPPING)) { + + RestartTimer = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + if (RestartTimer) { + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } else { + + NbiDereferenceDevice (Device, DREF_SHORT_TIMER); + + } + +} /* NbiShortTimeout */ + + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the long connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, nextp; + LIST_ENTRY AdapterStatusList; + PREQUEST AdapterStatusRequest; + PCONNECTION Connection; + PNETBIOS_CACHE CacheName; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + // + // Advance the up-counter used to mark time in LONG_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (++Device->LongAbsoluteTime == 0xf0000000) { + + ULONG Timeout; + + Device->LongAbsoluteTime = 0x10000000; + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + Timeout = Connection->Watchdog; + if (Timeout) { + Connection->Watchdog = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + + if ((Device->LongAbsoluteTime % 4) == 0) { + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + ASSERT (Connection->OnLongList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Watchdog && (Device->LongAbsoluteTime > Connection->Watchdog)) { + + Connection->Watchdog = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireWatchdog (Connection); // no spinlocks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnLongList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBI: Stop processing LongList, %lx removed\n", Connection); +#endif + break; + + } + + nextp = p->Flink; + + if (Connection->Watchdog == 0) { + + Connection->OnLongList = FALSE; + RemoveEntryList(p); + + if (Connection->Watchdog != 0) { + InsertTailList(&Device->LongList, &Connection->LongList); + Connection->OnLongList = TRUE; + } + + } + + p = nextp; + + } + + } + + + // + // Now scan the data ack queue, looking for connections with + // no acks queued that we can get rid of. + // + // Note: The timer spinlock is held here. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + if (Connection->DataAckPending) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_LONG_D_ACK); + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Have to check again, because the connection might + // just have been stopped, and it also might just have + // had a data ack queued. + // + + if (Connection->OnDataAckQueue) { + + Connection->OnDataAckQueue = FALSE; + + RemoveEntryList (&Connection->DataAckLinkage); + + if (Connection->DataAckPending) { + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + Connection->OnDataAckQueue = TRUE; + } + + Device->DataAckQueueChanged = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiDereferenceConnection (Connection, CREF_LONG_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Since we have changed the list, we can't tell if p->Flink + // is valid, so break. The effect is that we gradually peel + // connections off the queue. + // + + break; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + + // + // Scan for any uncompleted receive IRPs, this may happen if + // the cable is pulled and we don't get any more ReceiveComplete + // indications. + + NbiReceiveComplete((USHORT)0); + + + // + // Check if any adapter status queries are getting old. + // + + InitializeListHead (&AdapterStatusList); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + if (REQUEST_INFORMATION(AdapterStatusRequest) == 1) { + + // + // BUGBUG: We should resend a certain number of times. + // + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // We are going to abort this request, so dereference + // the cache entry it used. + // + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + } else { + + ++REQUEST_INFORMATION(AdapterStatusRequest); + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + NB_DEBUG2 (QUERY, ("AdapterStatus %lx got name but no response\n", AdapterStatusRequest)); + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + // + // See if a minute has passed and we need to check for empty + // cache entries to age out. We check for 64 seconds to make + // the mod operation faster. + // + +#if defined(_PNP_POWER) + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); +#endif _PNP_POWER + + ++Device->CacheTimeStamp; + + if ((Device->CacheTimeStamp % 64) == 0) { + + + // + // flush all the entries which have been around for ten minutes + // (LONG_TIMER_DELTA is in milliseconds). + // + + FlushOldFromNetbiosCacheTable( Device->NameCache, (600000 / LONG_TIMER_DELTA) ); + + } + + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + if (Device->State != DEVICE_STATE_STOPPING) { + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } else { +#if defined(_PNP_POWER) + Device->LongTimerRunning = FALSE; +#endif _PNP_POWER + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + +#if defined(_PNP_POWER) + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER +} /* NbiLongTimeout */ + + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine starts the short timer, if it is not already running. + +Arguments: + + Device - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // Start the timer unless it the DPC is already running (in + // which case it will restart the timer itself if needed), + // or some list is active (meaning the timer is already + // queued up). + // + + if ((!Device->ProcessingShortTimer) && + (!(Device->ShortListActive)) && + (!(Device->DataAckActive))) { + + NbiReferenceDevice (Device, DREF_SHORT_TIMER); + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } + +} /* NbiStartShortTimer */ + + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine initializes the lightweight timer system for the transport + provider. + +Arguments: + + Device - Pointer to our device. + +Return Value: + + none. + +--*/ + +{ + + // + // NbiTickIncrement is the number of NT time increments + // which pass between each tick. NbiShortTimerDeltaTicks + // is the number of ticks which should happen in + // SHORT_TIMER_DELTA milliseconds (i.e. between each + // expiration of the short timer). + // + + NbiTickIncrement = KeQueryTimeIncrement(); + + if (NbiTickIncrement > (SHORT_TIMER_DELTA * MILLISECONDS)) { + NbiShortTimerDeltaTicks = 1; + } else { + NbiShortTimerDeltaTicks = (SHORT_TIMER_DELTA * MILLISECONDS) / NbiTickIncrement; + } + + // + // The AbsoluteTime cycles between 0x10000000 and 0xf0000000. + // + + Device->ShortAbsoluteTime = 0x10000000; + Device->LongAbsoluteTime = 0x10000000; + + CTEInitTimer (&Device->ShortTimer); + CTEInitTimer (&Device->LongTimer); + +#if !defined(_PNP_POWER) + // + // One reference for the long timer. + // + + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + +#endif !_PNP_POWER + + Device->TimersInitialized = TRUE; + Device->ShortListActive = FALSE; + Device->ProcessingShortTimer = FALSE; + + InitializeListHead (&Device->ShortList); + InitializeListHead (&Device->LongList); + + CTEInitLock (&Device->TimerLock.Lock); + +} /* NbiInitializeTimers */ + diff --git a/private/ntos/tdi/isnp/nb/up/makefile b/private/ntos/tdi/isnp/nb/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/up/sources b/private/ntos/tdi/isnp/nb/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/spx/dirs b/private/ntos/tdi/isnp/spx/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/spx/globals.c b/private/ntos/tdi/isnp/spx/globals.c new file mode 100644 index 000000000..51fc80803 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/globals.c @@ -0,0 +1,87 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Global values +PDEVICE SpxDevice = NULL; +UNICODE_STRING IpxDeviceName = {0}; +HANDLE IpxHandle = NULL; + +LARGE_INTEGER Magic100000 = { + 0x1b478424, + 0xa7c5ac47 + }; +// Line info +IPX_LINE_INFO IpxLineInfo = {0}; +USHORT IpxMacHdrNeeded = 0; +USHORT IpxInclHdrOffset= 0; + +// Entry Points into the IPX stack +IPX_INTERNAL_SEND IpxSendPacket = NULL; +IPX_INTERNAL_FIND_ROUTE IpxFindRoute = NULL; +IPX_INTERNAL_QUERY IpxQuery = NULL; +IPX_INTERNAL_TRANSFER_DATA IpxTransferData = NULL; + +#if DBG +ULONG SpxDebugDump = 0; +LONG SpxDumpInterval = DBG_DUMP_DEF_INTERVAL; +ULONG SpxDebugLevel = DBG_LEVEL_ERR; +ULONG SpxDebugSystems = DBG_COMP_MOST; +#endif + +// Unload event triggered when ref count on device goes to zero. +KEVENT SpxUnloadEvent = {0}; + +// Maximum packet size quanta used during packet size negotiation +ULONG SpxMaxPktSize[] = { + 576 - MIN_IPXSPX2_HDRSIZE, + 1024 - MIN_IPXSPX2_HDRSIZE, + 1474 - MIN_IPXSPX2_HDRSIZE, + 1492 - MIN_IPXSPX2_HDRSIZE, + 1500 - MIN_IPXSPX2_HDRSIZE, + 1954 - MIN_IPXSPX2_HDRSIZE, + 4002 - MIN_IPXSPX2_HDRSIZE, + 8192 - MIN_IPXSPX2_HDRSIZE, + 17314 - MIN_IPXSPX2_HDRSIZE, + 65535 - MIN_IPXSPX2_HDRSIZE + }; + +ULONG SpxMaxPktSizeIndex = sizeof(SpxMaxPktSize)/sizeof(ULONG); + + +// Global interlock +CTELock SpxGlobalInterlock = {0}; + +// Another one, used only for global queues for addr/conn +CTELock SpxGlobalQInterlock = {0}; +PSPX_CONN_FILE SpxGlobalConnList = NULL; +PSPX_ADDR_FILE SpxGlobalAddrList = NULL; + +SPX_CONNFILE_LIST SpxPktConnList = {NULL, NULL}; +SPX_CONNFILE_LIST SpxRecvConnList = {NULL, NULL}; + +// Timer globals +LONG SpxTimerCurrentTime = 0; diff --git a/private/ntos/tdi/isnp/spx/h/fwddecls.h b/private/ntos/tdi/isnp/spx/h/fwddecls.h new file mode 100644 index 000000000..feda4e76b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/fwddecls.h @@ -0,0 +1,28 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + fwddecls.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +struct _SPX_ADDR ; +struct _SPX_ADDR_FILE ; +struct _SPX_CONN_FILE ; +struct _SPX_SEND_RESD ; diff --git a/private/ntos/tdi/isnp/spx/h/globals.h b/private/ntos/tdi/isnp/spx/h/globals.h new file mode 100644 index 000000000..e4fcf39a8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/globals.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + globals.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +extern PDEVICE SpxDevice; +extern UNICODE_STRING IpxDeviceName; +extern HANDLE IpxHandle; + +extern LARGE_INTEGER Magic100000; + +#if 1 // DBG +extern ULONG SpxDebugDump; +extern LONG SpxDumpInterval; +extern ULONG SpxDebugLevel; +extern ULONG SpxDebugSystems; + +#endif + +// More IPX info. +extern IPX_LINE_INFO IpxLineInfo; +extern USHORT IpxMacHdrNeeded; +extern USHORT IpxInclHdrOffset; + +// Entry Points into the IPX stack +extern IPX_INTERNAL_SEND IpxSendPacket; +extern IPX_INTERNAL_FIND_ROUTE IpxFindRoute; +extern IPX_INTERNAL_QUERY IpxQuery; +extern IPX_INTERNAL_TRANSFER_DATA IpxTransferData; + +// Unload event +extern KEVENT SpxUnloadEvent; + +extern ULONG SpxMaxPktSize[]; +extern ULONG SpxMaxPktSizeIndex; + +extern CTELock SpxGlobalInterlock; + + +extern CTELock SpxGlobalQInterlock; +extern PSPX_CONN_FILE SpxGlobalConnList; +extern PSPX_ADDR_FILE SpxGlobalAddrList; + +extern SPX_CONNFILE_LIST SpxPktConnList; +extern SPX_CONNFILE_LIST SpxRecvConnList; + +extern LONG SpxTimerCurrentTime; diff --git a/private/ntos/tdi/isnp/spx/h/isnspx.h b/private/ntos/tdi/isnp/spx/h/isnspx.h new file mode 100644 index 000000000..6080b0423 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/isnspx.h @@ -0,0 +1,363 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnspx.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + + +#include <ntddk.h> +#include <tdikrnl.h> +#include <ndis.h> +#ifndef CTE_TYPEDEFS_DEFINED +#include <cxport.h> +#endif +#include <bind.h> + +#include "wsnwlink.h" + +#define SPX_DEVICE_SIGNATURE (USHORT)(*(PUSHORT)"SD") +#define SPX_ADDRESS_SIGNATURE (USHORT)(*(PUSHORT)"AD") +#define SPX_ADDRESSFILE_SIGNATURE (USHORT)(*(PUSHORT)"AF") +#define SPX_CONNFILE_SIGNATURE (USHORT)(*(PUSHORT)"CF") + +#define SPX_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + +#define SPX_ADD_ULONG(_Pulong, _Ulong, _Lock) InterlockedExchangeAdd(_Pulong, _Ulong) + +typedef UCHAR BYTE, *PBYTE; +typedef ULONG DWORD, *PDWORD; + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; + +// +// PREQUEST +// SpxAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define SpxAllocateRequest(_Device,_Irp) \ + (_Irp) + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// SpxFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define SpxFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_TDI_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the TDI buffer chain associated with a request. +// + +#define REQUEST_TDI_BUFFER(_Request) \ + ((PVOID)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// PUNICODE_STRING +// REQUEST_OPEN_NAME( +// IN PREQUEST Request +// ); +// +// Used to access the RemainingName field of a request. +// + +#define REQUEST_OPEN_NAME(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->FileObject->FileName)) + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// SpxCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define SpxCompleteRequest(_Request) \ + { \ + CTELockHandle _CancelIrql; \ + DBGPRINT(TDI, INFO, \ + ("SpxCompleteRequest: Completing %lx with %lx\n", \ + (_Request), REQUEST_STATUS(_Request))); \ + \ + IoAcquireCancelSpinLock( &_CancelIrql ); \ + (_Request)->CancelRoutine = NULL; \ + IoReleaseCancelSpinLock( _CancelIrql ); \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT); \ + } + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +#include "fwddecls.h" + +// BUGBUG: This should go in ntddk.h? +#ifndef _NTIOAPI_ +#include "spxntdef.h" +#endif + +#include "spxreg.h" +#include "spxdev.h" +#include "spxbind.h" +#include "spxtimer.h" +#include "spxpkt.h" +#include "spxerror.h" +#include "spxaddr.h" +#include "spxconn.h" +#include "spxrecv.h" +#include "spxsend.h" +#include "spxquery.h" +#include "spxmem.h" +#include "spxutils.h" + + +// Globals +#include "globals.h" + + + + diff --git a/private/ntos/tdi/isnp/spx/h/spxaddr.h b/private/ntos/tdi/isnp/spx/h/spxaddr.h new file mode 100644 index 000000000..b49a4791e --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxaddr.h @@ -0,0 +1,426 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.h + +Abstract: + + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define DYNSKT_RANGE_START 0x4000 +#define DYNSKT_RANGE_END 0x7FFF +#define SOCKET_UNIQUENESS 1 + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. + +#define AFREF_CREATE 0 +#define AFREF_VERIFY 1 +#define AFREF_INDICATION 2 +#define AFREF_CONN_ASSOC 3 + +#define AFREF_TOTAL 4 + +typedef struct _SPX_ADDR_FILE { + +#if DBG + ULONG saf_RefTypes[AFREF_TOTAL]; +#endif + + CSHORT saf_Type; + CSHORT saf_Size; + + // number of references to this object. + ULONG saf_RefCount; + + // Linkage in address list. + struct _SPX_ADDR_FILE * saf_Next; + struct _SPX_ADDR_FILE * saf_GlobalNext; + + // List of associated connection/active or otherwise + struct _SPX_CONN_FILE * saf_AssocConnList; + + // the current state of the address file structure; this is either open or + // closing + USHORT saf_Flags; + + // address to which we are bound, pointer to its lock. + struct _SPX_ADDR * saf_Addr; + CTELock * saf_AddrLock; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT saf_FileObject; +#endif + + // device to which we are attached. + struct _DEVICE * saf_Device; + + // This holds the request used to close this address file, + // for pended completion. + PREQUEST saf_CloseReq; + + // This function pointer points to a connection indication handler for this + // Address. Any time a connect request is received on the address, this + // routine is invoked. + PTDI_IND_CONNECT saf_ConnHandler; + PVOID saf_ConnHandlerCtx; + + // The following function pointer always points to a TDI_IND_DISCONNECT + // handler for the address. + PTDI_IND_DISCONNECT saf_DiscHandler; + PVOID saf_DiscHandlerCtx; + + // The following function pointer always points to a TDI_IND_RECEIVE + // event handler for connections on this address. + PTDI_IND_RECEIVE saf_RecvHandler; + PVOID saf_RecvHandlerCtx; + + // Send possible handler + PTDI_IND_SEND_POSSIBLE saf_SendPossibleHandler; + PVOID saf_SendPossibleHandlerCtx; + + // !!!We do not do datagrams or expedited data!!! + + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. + PTDI_IND_ERROR saf_ErrHandler; + PVOID saf_ErrHandlerCtx; + PVOID saf_ErrHandlerOwner; + + +} SPX_ADDR_FILE, *PSPX_ADDR_FILE; + +#define SPX_ADDRFILE_OPENING 0x0000 // not yet open for business +#define SPX_ADDRFILE_OPEN 0x0001 // open for business +#define SPX_ADDRFILE_CLOSING 0x0002 // closing +#define SPX_ADDRFILE_STREAM 0x0004 // Opened for stream mode operation +#define SPX_ADDRFILE_CONNIND 0x0008 // Connect ind in progress +#define SPX_ADDRFILE_SPX2 0x0010 // Attempt SPX2 address file +#define SPX_ADDRFILE_NOACKWAIT 0x0020 // Dont delay acks on assoc connections +#define SPX_ADDRFILE_IPXHDR 0x0040 // Pass ipx hdr on all assoc connections +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** +// If you are adding any more states to this beyond 0x0080, MAKE SURE to go +// in code and change statements like (Flags & SPX_***) to +// ((Flags & SPX_**) != 0)!!! I dont want to make that change that at this stage. +// ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** ***STOP*** + +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. + +#define AREF_ADDR_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 + +#define AREF_TOTAL 4 + +typedef struct _SPX_ADDR { + +#if DBG + ULONG sa_RefTypes[AREF_TOTAL]; +#endif + + USHORT sa_Size; + CSHORT sa_Type; + + // number of references to this object. + ULONG sa_RefCount; + + // next address/this device object. + struct _SPX_ADDR * sa_Next; + + // The following fields are used to maintain state about this address. + // attributes of the address. + ULONG sa_Flags; + + // Next addressfile for this address + struct _SPX_ADDR_FILE * sa_AddrFileList; + + // List of inactive connections and active connections on this address file. + struct _SPX_CONN_FILE * sa_InactiveConnList; + struct _SPX_CONN_FILE * sa_ActiveConnList; + + // This is the list of connections which have a POST_LISTEN on them. They + // do not have a local connection id at this point. But will, when they move + // from here to the ActiveConnList, when the listen is satisfied (no matter + // if the accept has not been posted yet, in the case of non-autoaccept listens) + struct _SPX_CONN_FILE * sa_ListenConnList; + + CTELock sa_Lock; + + // the socket this address corresponds to. + USHORT sa_Socket; + + // device context to which we are attached. + struct _DEVICE * sa_Device; + CTELock * sa_DeviceLock; + +#ifdef ISN_NT + + // These two can be a union because they are not used + // concurrently. + union { + + // This structure is used for checking share access. + SHARE_ACCESS sa_ShareAccess; + + // Used for delaying NbfDestroyAddress to a thread so + // we can access the security descriptor. + WORK_QUEUE_ITEM sa_DestroyAddrQueueItem; + + } u; + + // This structure is used to hold ACLs on the address. + PSECURITY_DESCRIPTOR sa_SecurityDescriptor; + +#endif + +} SPX_ADDR, *PSPX_ADDR; + +#define SPX_ADDR_CLOSING 0x00000001 + + +// ROUTINE PROTOTYPES + +VOID +SpxAddrRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address); + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address); + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile); + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile); + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest); + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile); + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address); + +NTSTATUS +SpxAddrFileCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request); + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT struct _SPX_CONN_FILE **ppSpxConnFile); + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile); + +VOID +SpxAddrDestroy( + IN PVOID Parameter); + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device); + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket); + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile); + +#if DBG +#define SpxAddrReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type],\ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrRef (_Address); \ + } + +#define SpxAddrLockReference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrLockRef (_Address); \ + } + +#define SpxAddrDereference(_Address, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Address)->sa_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + }\ + } + + +#define SpxAddrFileReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileRef (_AddressFile); \ + } + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxAddrFileLockRef (_AddressFile); \ + } + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxAddrFileDeref (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_AddressFile)->saf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxAddrReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock) + +#define SpxAddrLockReference(_Address, _Type) \ + SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + 1, \ + (_Address)->sa_DeviceLock); + +#define SpxAddrDereference(_Address, _Type) \ + if (SPX_ADD_ULONG( \ + &(_Address)->sa_RefCount, \ + (ULONG)-1, \ + &(_Address)->sa_Lock) == 1) { \ + SpxAddrDestroy (_Address); \ + } + +#define SpxAddrFileReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock) + +#define SpxAddrFileLockReference(_AddressFile, _Type) \ + SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + 1, \ + (_AddressFile)->saf_AddrLock); + +#define SpxAddrFileDereference(_AddressFile, _Type) \ + if (SPX_ADD_ULONG( \ + &(_AddressFile)->saf_RefCount, \ + (ULONG)-1, \ + (_AddressFile)->saf_AddrLock) == 1) { \ + SpxAddrFileDestroy (_AddressFile); \ + } + +#define SpxAddrFileTransferReference(_AddressFile, _OldType, _NewType) + +#endif // DBG diff --git a/private/ntos/tdi/isnp/spx/h/spxbind.h b/private/ntos/tdi/isnp/spx/h/spxbind.h new file mode 100644 index 000000000..81ad6ac58 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxbind.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +NTSTATUS +SpxInitBindToIpx( + VOID); + +VOID +SpxUnbindFromIpx( + VOID); + diff --git a/private/ntos/tdi/isnp/spx/h/spxconn.h b/private/ntos/tdi/isnp/spx/h/spxconn.h new file mode 100644 index 000000000..bb1173432 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxconn.h @@ -0,0 +1,1666 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxconn.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +// Minimum value for RTT in ms. +// BUGBUG: Have these be a derivate of registry values. +#define SPX_T1_MIN 200 +#define MAX_RETRY_DELAY 5000 // 5 seconds +#define SPX_DEF_RENEG_RETRYCOUNT 1 // All reneg pkts except min sent once + +// Some types +typedef enum +{ + SPX_CALL_RECVLEVEL, + SPX_CALL_TDILEVEL +} SPX_CALL_LEVEL; + +typedef enum +{ + SPX_REQ_DATA, + SPX_REQ_ORDREL, + SPX_REQ_DISC + +} SPX_SENDREQ_TYPE; + +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Connection. + +#define CFREF_CREATE 0 +#define CFREF_VERIFY 1 +#define CFREF_INDICATION 2 +#define CFREF_BYCTX 3 +#define CFREF_BYID 4 +#define CFREF_ADDR 5 +#define CFREF_REQ 6 +#define CFREF_TIMER 7 +#define CFREF_PKTIZE 8 +#define CFREF_RECV 9 +#define CFREF_ABORTPKT 10 +#define CFREF_ERRORSTATE 11 +#define CFREF_FINDROUTE 12 + +// +// New state added to reflect an SPXI connection which is waiting for +// a local disconnect after having indicated a RELEASE to AFD. +// +#define CFREF_DISCWAITSPX 13 + +#define CFREF_TOTAL 14 + +#define CFMAX_STATES 20 + +typedef struct _SPX_CONN_FILE +{ + +#if DBG + ULONG scf_RefTypes[CFREF_TOTAL]; + +#if 0 +// +// Disabled for now - to enable logging of states, move this array *after* the Type/Size; +// a change in their offset can cause problems since we assume the offset to be less than +// the size of an AddressFile structure. (see SpxTdiQueryInformation) +// + ULONG scf_StateBuffer[CFMAX_STATES]; + ULONG scf_NextStatePtr; +#endif + +#endif + + CSHORT scf_Type; + CSHORT scf_Size; + + // number of references to this object. + ULONG scf_RefCount; + + // Linkage in device address file list. The connection can be on the device + // connection list, address inactive/listen/active list. + struct _SPX_CONN_FILE * scf_Next; + struct _SPX_CONN_FILE * scf_AssocNext; + struct _SPX_CONN_FILE * scf_GlobalActiveNext; + + // Queued in a global list, stays here from creation to destroy. + struct _SPX_CONN_FILE * scf_GlobalNext; + struct _SPX_CONN_FILE * scf_PktNext; + struct _SPX_CONN_FILE * scf_ProcessRecvNext; + + // the current state of the connection. One main state and multiple substates. + ULONG scf_Flags; + + // More information + ULONG scf_Flags2; + +#if DBG + // Save the state of flags/flags2 before reinit. Overwritten every reinit. + ULONG scf_GhostFlags; + ULONG scf_GhostFlags2; + ULONG scf_GhostRefCount; + PREQUEST scf_GhostDiscReq; +#endif + + // Connection retry counts, or watchdog timer count when the connection goes + // active + union + { + LONG scf_CRetryCount; + LONG scf_WRetryCount; + }; + LONG scf_RRetryCount; + USHORT scf_RRetrySeqNum; + + union + { + ULONG scf_CTimerId; + ULONG scf_RTimerId; // Only after we turn active + }; + + ULONG scf_WTimerId; // Watchdog timer + ULONG scf_TTimerId; // TDI Connect/Disconnect timer + ULONG scf_ATimerId; // Ack timer id + + // Variables used to manage the Retry timer tick value + // Note our timer subsytem fires at 100ms granularity. + int scf_BaseT1; + int scf_AveT1; + int scf_DevT1; + + // Stored in HOST-ORDER + // LOCAL variables + USHORT scf_LocalConnId; + USHORT scf_SendSeqNum; // Debug dw +9a + USHORT scf_SentAllocNum; // dw +9c + + // REMOTE variables + USHORT scf_RecvSeqNum; // dw +9e + USHORT scf_RecdAckNum; // dw +a0 + USHORT scf_RecdAllocNum; // dw +a2 + + // RETRY sequence number + USHORT scf_RetrySeqNum; + + // Saved ack number to be used in building the reneg ack packet. + // Note that our RecvSeqNum which we normally use is overwritten + // when we receive a renegotiate request. + USHORT scf_RenegAckAckNum; + + // Stored in NETWORK-ORDER. scf_RemAckAddr contains the remote address + // for a data packet that had the ack bit set, buildAck will use this + // address. + BYTE scf_RemAddr[12]; + BYTE scf_RemAckAddr[12]; + USHORT scf_RemConnId; // Debug dw +be + + // Maximum packet size (or size of first) reneg packet. + USHORT scf_RenegMaxPktSize; + + // Local target to use in when sending acks. This is set to received + // data's indicated local target. + IPX_LOCAL_TARGET scf_AckLocalTarget; + + // Maximum packet size to use for this connection + USHORT scf_MaxPktSize; + UCHAR scf_DataType; + + // Local target to use in sends, initialized upon connect indication + // or when find_route completes + IPX_LOCAL_TARGET scf_LocalTarget; + + // Connection lock + CTELock scf_Lock; + + // address to which we are bound + struct _SPX_ADDR_FILE * scf_AddrFile; + + // Connection context + CONNECTION_CONTEXT scf_ConnCtx; + +#ifdef ISN_NT + // easy backlink to file object. + PFILE_OBJECT scf_FileObject; +#endif + + // LIST_ENTRY of disconnect irps waiting for completion. There could be + // multiple disconnect inform irps. + LIST_ENTRY scf_DiscLinkage; + + // LIST_ENTRY of send requests (intially contains connect/listen/accept also) + // on this connection. + LIST_ENTRY scf_ReqLinkage; + + // Queue for completed requests awaiting completion + LIST_ENTRY scf_ReqDoneLinkage; + LIST_ENTRY scf_RecvDoneLinkage; + + // Queue for pending receives + LIST_ENTRY scf_RecvLinkage; + PREQUEST scf_CurRecvReq; + ULONG scf_CurRecvOffset; + ULONG scf_CurRecvSize; + + // Current request packetize info + PREQUEST scf_ReqPkt; + ULONG scf_ReqPktOffset; + ULONG scf_ReqPktSize; + ULONG scf_ReqPktFlags; + SPX_SENDREQ_TYPE scf_ReqPktType; + + // Single linked list of sequenced send/disc packets + PSPX_SEND_RESD scf_SendSeqListHead; + PSPX_SEND_RESD scf_SendSeqListTail; + + // Single linked list of send (unsequenced) packets + PSPX_SEND_RESD scf_SendListHead; + PSPX_SEND_RESD scf_SendListTail; + + // Single linked list of buffered recv packets. + PSPX_RECV_RESD scf_RecvListHead; + PSPX_RECV_RESD scf_RecvListTail; + + // Connect request + PREQUEST scf_ConnectReq; + + // This holds the request used to close this address file, + // for pended completion. We also pend cleanup requests for connections. + PREQUEST scf_CleanupReq; + PREQUEST scf_CloseReq; + +#if DBG + + // Packet being indicated, seq num, flags/flags2 + USHORT scf_PktSeqNum; + ULONG scf_PktFlags; + ULONG scf_PktFlags2; + + ULONG scf_IndBytes; + ULONG scf_IndLine; +#endif + +#if DBG_WDW_CLOSE + + // Keep track of how long the window was closed on this connection. + ULONG scf_WdwCloseAve; + LARGE_INTEGER scf_WdwCloseTime; // Time when wdw was closed +#endif + + // device to which we are attached. + struct _DEVICE * scf_Device; + +} SPX_CONN_FILE, *PSPX_CONN_FILE; + + +// Basic states +// Least significant byte of flags is used. +// Mutually exclusive states are coded as numbers, others are bit flags. +// Only main states are currently in form of numbers. Also, send and receive. +// +// Once we go active, we need SEND/RECEIVE/DISC substates to be mutually +// exclusive with each other. As all three could be active at the same time. + +// Connection MAIN states. These are all mutually exclusive. +#define SPX_CONNFILE_MAINMASK 0x00000007 +#define SPX_CONNFILE_ACTIVE 0x00000001 +#define SPX_CONNFILE_CONNECTING 0x00000002 +#define SPX_CONNFILE_LISTENING 0x00000003 +#define SPX_CONNFILE_DISCONN 0x00000004 + +// Connecting states (VALID when CONNFILE_CONNECTING) +#define SPX_CONNECT_MASK 0x000000F0 +#define SPX_CONNECT_SENTREQ 0x00000010 +#define SPX_CONNECT_NEG 0x00000020 +#define SPX_CONNECT_W_SETUP 0x00000030 + +// Listening states (VALID when CONNFILE_LISTENING) +#define SPX_LISTEN_MASK 0x000000F0 +#define SPX_LISTEN_RECDREQ 0x00000010 +#define SPX_LISTEN_SENTACK 0x00000020 +#define SPX_LISTEN_NEGACK 0x00000030 +#define SPX_LISTEN_SETUP 0x00000040 + +// Connection SUB states +// Send machine states (VALID when CONNFILE_ACTIVE) +#define SPX_SEND_MASK 0x000000F0 +#define SPX_SEND_IDLE 0x00000000 +#define SPX_SEND_PACKETIZE 0x00000010 +#define SPX_SEND_RETRY 0x00000020 +#define SPX_SEND_RETRYWD 0x00000030 +#define SPX_SEND_RENEG 0x00000040 +#define SPX_SEND_RETRY2 0x00000050 +#define SPX_SEND_RETRY3 0x00000060 +#define SPX_SEND_WD 0x00000070 // We dont reneg pkt size on wdog + // Also we change to this state only + // 2nd time wdog fires w/out ack. +#define SPX_SEND_NAK_RECD 0x00000080 + +// Receive machine states (VALID when CONNFILE_ACTIVE) +#define SPX_RECV_MASK 0x00000F00 +#define SPX_RECV_IDLE 0x00000000 +#define SPX_RECV_POSTED 0x00000100 +#define SPX_RECV_PROCESS_PKTS 0x00000200 + +// Disconnect states (VALID when CONNFILE_DISCONN/CONNFILE_ACTIVE) +// These are valid when either ACTIVE/DISCONN is set. We use these when +// active for a orderly release, i.e. we receive pkt from remote, but we +// stay active (setting SPX_DISC_RECV_ORDREL) until our client posts a +// disconnect, which is when we move to disconnecting. +#define SPX_DISC_MASK 0x0000F000 +#define SPX_DISC_IDLE 0x00000000 +#define SPX_DISC_ABORT 0x00001000 +#define SPX_DISC_SENT_IDISC 0x00002000 +#define SPX_DISC_POST_ORDREL 0x00003000 +#define SPX_DISC_SENT_ORDREL 0x00004000 +#define SPX_DISC_ORDREL_ACKED 0x00005000 +#define SPX_DISC_POST_IDISC 0x00006000 + +// [SA] bug #14655 added flag to indicate that SpxConnInactivate already called for +// this disconnecting connection +// +#define SPX_DISC_INACTIVATED 0x00007000 + +// The following are not mutually exclusive. +#define SPX_CONNFILE_RECVQ 0x00010000 // Process completed receives/pkts +#define SPX_CONNFILE_RENEG_SIZE 0x00020000 // Size changed in renegotiate pkt +#define SPX_CONNFILE_ACKQ 0x00040000 // Waiting to piggyback ack queue +#define SPX_CONNFILE_PKTQ 0x00080000 // Waiting to packetize queue + +#define SPX_CONNFILE_ASSOC 0x00100000 // associated +#define SPX_CONNFILE_NEG 0x00200000 // CR had neg set (for delayed accept) +#define SPX_CONNFILE_SPX2 0x00400000 +#define SPX_CONNFILE_STREAM 0x00800000 +#define SPX_CONNFILE_R_TIMER 0x01000000 // Retry timer (only after ACTIVE) +#define SPX_CONNFILE_C_TIMER 0x01000000 // Connect timer +#define SPX_CONNFILE_W_TIMER 0x02000000 // Watchdog timer +#define SPX_CONNFILE_T_TIMER 0x04000000 // tdi connect/disc timer specified +#define SPX_CONNFILE_RENEG_PKT 0x08000000 // Renegotiate changed size, repacketize +#define SPX_CONNFILE_IND_IDISC 0x10000000 // Indicated abortive disc to afd +#define SPX_CONNFILE_IND_ODISC 0x20000000 // Indicated orderly release to afd + +#define SPX_CONNFILE_STOPPING 0x40000000 +#define SPX_CONNFILE_CLOSING 0x80000000 // closing + +#define SPX_CONNFILE2_PKT_NOIND 0x00000001 +#define SPX_CONNFILE2_RENEGRECD 0x00000002 // A renegotiate was received. + // scf_RenegAckAckNum set. +#define SPX_CONNFILE2_PKT 0x00000004 +#define SPX_CONNFILE2_FINDROUTE 0x00000010 // A find route in progress on conn. +#define SPX_CONNFILE2_NOACKWAIT 0x00000020 // Dont delay acks on connection, option +#define SPX_CONNFILE2_IMMED_ACK 0x00000040 // Send an immediate ack,no back traffic +#define SPX_CONNFILE2_IPXHDR 0x00000080 // Pass ipxhdr in receives + +// +// [SA] Saves the IDisc flag passed to AbortiveDisc; this is TRUE only if there was +// a remote disconnect on an SPX connection (in which case, we indicate TDI_DISCONNECT_RELEASE +// else we indicate TDI_DISCONNECT_ABORT) +// +#define SPX_CONNFILE2_IDISC 0x00000100 + +// +// Indicates an SPXI connfile waiting for a local disconnect in response +// to a TDI_DISCONNECT_RELEASE to AFD. +// +#define SPX_CONNFILE2_DISC_WAIT 0x00000200 + +// FindRoute request structure +typedef struct _SPX_FIND_ROUTE_REQUEST +{ + // !!!!This must be the first element in the structure + IPX_FIND_ROUTE_REQUEST fr_FindRouteReq; + PVOID fr_Ctx; + +} SPX_FIND_ROUTE_REQUEST, *PSPX_FIND_ROUTE_REQUEST; + +typedef struct _SPX_CONNFILE_LIST +{ + PSPX_CONN_FILE pcl_Head; + PSPX_CONN_FILE pcl_Tail; + +} SPX_CONNFILE_LIST, *PSPX_CONNFILE_LIST; + +// Exported routines + +NTSTATUS +SpxConnOpen( + IN PDEVICE pDevice, + IN CONNECTION_CONTEXT pConnCtx, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnCleanup( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnClose( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxConnDisAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +spxConnDisAssoc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnStop( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +SpxConnAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnListen( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAccept( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnAction( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnDisconnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnSend( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +NTSTATUS +SpxConnRecv( + IN PDEVICE pDevice, + IN PREQUEST pRequest); + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT NTSTATUS * pStatus); + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile); + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnConnectFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +VOID +SpxConnActiveFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle); + +BOOLEAN +SpxConnPacketize( + IN PSPX_CONN_FILE pSpxConnFile, + IN BOOLEAN fNormalState, + IN CTELockHandle LockHandleConn); + +#if DBG +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile); +#endif + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus); + +BOOLEAN +SpxConnDequeuePktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +VOID +SpxConnSendAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnSendNack( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT NumToSend, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxConnProcessAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pAckHdr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessRenegReq( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessIDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +VOID +SpxConnProcessOrdRel( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle); + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt); + +// LOCAL functions +VOID +spxConnHandleConnReq( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr); + +VOID +spxConnHandleSessPktFromClient( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnHandleSessPktFromSrv( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +ULONG +spxConnConnectTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnWatchdogTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnRetryTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +ULONG +spxConnAckTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + +VOID +spxConnCompletePended( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +SpxConnQWaitAck( + IN PSPX_CONN_FILE pSpxConnFile); + +USHORT +spxConnGetId( + VOID); + +VOID +spxConnInsertIntoActiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoInactiveList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile); + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnInsertIntoListenList( + IN PSPX_ADDR pSpxAddr, + IN PSPX_CONN_FILE pSpxConnFile); + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove); + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile); + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt); + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt); + +VOID +spxConnResendPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize); + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size); + +BOOLEAN +spxConnAcceptCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +spxConnCompleteConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn); + +VOID +SpxConnQueueRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest); + +NTSTATUS +spxConnProcessRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnProcessIndData( + IN PSPX_CONN_FILE pSpxConnFile, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnOrderlyDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +NTSTATUS +spxConnInformedDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN PREQUEST pRequest, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortiveDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn, + IN BOOLEAN Flag); // [SA] Bug #15249 + +VOID +spxConnAbortRecvs( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnAbortSends( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +VOID +spxConnResetSendQueue( + IN PSPX_CONN_FILE pSpxConnFile); + +VOID +spxConnAbortSendPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_SEND_RESD pSendResd, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn); + +// +// MACROS +// +#define SHIFT100000 16 + +#define SPX_CONVERT100NSTOCENTISEC(Li) \ + RtlExtendedMagicDivide((Li), Magic100000, SHIFT100000) + +#define UNSIGNED_BETWEEN_WITH_WRAP(Low, High, Target) \ + ((Low <= High) ? ((Target >= Low) && (Target <= High)) : \ + ((Target >= Low) || (Target <= High))) + +// This is with the assumption that the window size will never be greater +// than the difference of 0x8000 and 0x1000. If High is < 1000 and Low +// is > 8000 then we can assume a wrap happened. Otherwise, we assume no +// wrap and do a straight compare. +#define MAX_WINDOW_SIZE 0x6000 +#define DEFAULT_WINDOW_SIZE 8 + +#define UNSIGNED_GREATER_WITH_WRAP(High, Low) \ + (((High < 0x1000) && (Low > 0x8000)) ? TRUE : (High > Low)) + +#define SPX_SET_ACKNUM(pSpxConnFile, RecdAckNum, RecdAllocNum) \ + { \ + DBGPRINT(SEND, DBG, \ + ("SPX_SET_ACKNUM: %lx.%lx = %lx.%lx (%s.%d)\n", \ + (RecdAckNum), (RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAllocNum), \ + __FILE__, __LINE__)); \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAckNum), \ + ((pSpxConnFile)->scf_RecdAckNum))) \ + { \ + (pSpxConnFile)->scf_RecdAckNum = (RecdAckNum); \ + } \ + \ + if (UNSIGNED_GREATER_WITH_WRAP((RecdAllocNum), \ + ((pSpxConnFile)->scf_RecdAllocNum)))\ + { \ + (pSpxConnFile)->scf_RecdAllocNum = (RecdAllocNum); \ + } \ + } + +#define BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum) \ + { \ + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT); \ + } + +#define END_PROCESS_PACKET(pSpxConnFile, fBuffered, fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, \ + (SPX_CONNFILE2_PKT |SPX_CONNFILE2_RENEGRECD)); \ + if (fSuccess) \ + { \ + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); \ + SPX_SET_RECVNUM(pSpxConnFile, fBuffered); \ + } \ + } + +#define INCREMENT_WINDOW(pSpxConnFile) \ + ((pSpxConnFile)->scf_SentAllocNum++) + +#define ADD_TO_WINDOW(pSpxConnFile, numPkts) \ + ((pSpxConnFile)->scf_SentAllocNum += (numPkts)) + +#if DBG_WDW_CLOSE +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + \ + if (fBuffered && \ + (UNSIGNED_GREATER_WITH_WRAP( \ + (pSpxConnFile)->scf_RecvSeqNum, \ + (pSpxConnFile)->scf_SentAllocNum))) \ + { \ + KeQuerySystemTime( \ + (PLARGE_INTEGER)&pSpxConnFile->scf_WdwCloseTime); \ + } \ + } +#else +#define SPX_SET_RECVNUM(pSpxConnFile, fBuffered) \ + { \ + (pSpxConnFile)->scf_RecvSeqNum++; \ + if (!fBuffered) \ + (pSpxConnFile)->scf_SentAllocNum++; \ + } +#endif + + +#define SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest) \ + { \ + RemoveEntryList(REQUEST_LINKAGE((pRequest))); \ + pSpxConnFile->scf_CurRecvReq = NULL; \ + pSpxConnFile->scf_CurRecvOffset = 0; \ + pSpxConnFile->scf_CurRecvSize = 0; \ + if (!IsListEmpty(&(pSpxConnFile)->scf_RecvLinkage)) \ + { \ + PTDI_REQUEST_KERNEL_RECEIVE _p; \ + DBGPRINT(RECEIVE, DBG, \ + ("spxConnProcessRecv: CURRECV %lx\n", pRequest)); \ + \ + (pSpxConnFile)->scf_CurRecvReq = \ + LIST_ENTRY_TO_REQUEST( \ + (pSpxConnFile)->scf_RecvLinkage.Flink); \ + \ + _p = (PTDI_REQUEST_KERNEL_RECEIVE) \ + REQUEST_PARAMETERS((pSpxConnFile)->scf_CurRecvReq); \ + \ + (pSpxConnFile)->scf_CurRecvOffset = 0; \ + (pSpxConnFile)->scf_CurRecvSize = (_p)->ReceiveLength; \ + } \ + if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || \ + (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) \ + { \ + SPX_RECV_SETSTATE( \ + pSpxConnFile, \ + (pSpxConnFile->scf_CurRecvReq == NULL) ? \ + SPX_RECV_IDLE : SPX_RECV_POSTED); \ + } \ + } + +#define SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ActiveConnList; \ + (pSpxAddr)->sa_ActiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_INACTIVE(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_InactiveConnList; \ + (pSpxAddr)->sa_InactiveConnList = pSpxConnFile; \ + } + +#define SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile) \ + { \ + (pSpxConnFile)->scf_Next = (pSpxAddr)->sa_ListenConnList; \ + (pSpxAddr)->sa_ListenConnList = pSpxConnFile; \ + } + + +// +// STATE MANIPULATION +// + +#if 0 +// +// Disabled for now +// +#define SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_StateBuffer[(pSpxConnFile)->scf_NextStatePtr++] = \ + (pSpxConnFile)->scf_Flags; \ + (pSpxConnFile)->scf_NextStatePtr %= CFMAX_STATES; +#else + +#define SPX_STORE_LAST_STATE(pSpxConnFile) + +#endif + +#define SPX_MAIN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNFILE_MAINMASK) + +// #define SPX_CONN_IDLE(pSpxConnFile) \ +// ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == 0)) + +#define SPX_CONN_IDLE(pSpxConnFile) \ + ((BOOLEAN)((SPX_MAIN_STATE(pSpxConnFile) == 0) || \ + ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && \ + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)))) + +#define SPX_CONN_ACTIVE(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE)) + +#define SPX_CONN_CONNECTING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_CONNECTING)) + +#define SPX_CONN_LISTENING(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_LISTENING)) + +#define SPX_CONN_DISC(pSpxConnFile) \ + ((BOOLEAN)(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN)) + +#if DBG + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#else + +#define SPX_MAIN_SETSTATE(pSpxConnFile, newState) \ + { \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNFILE_MAINMASK) | (newState));\ + } + +#endif + +#define SPX_CONN_FLAG(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags & (Flag)) != 0)) + +#define SPX_CONN_FLAG2(pSpxConnFile, Flag) \ + ((BOOLEAN)(((pSpxConnFile)->scf_Flags2 & (Flag)) != 0)) + +#if DBG + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) +#else + +#define SPX_CONN_SETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags |= (Flag)) + +#endif + +#define SPX_CONN_SETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 |= (Flag)) + +#define SPX_CONN_RESETFLAG(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags &= ~(Flag)) + +#define SPX_CONN_RESETFLAG2(pSpxConnFile, Flag) \ + ((pSpxConnFile)->scf_Flags2 &= ~(Flag)) + +#define SPX2_CONN(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_SPX2)) + +#define SPX_CONN_STREAM(pSpxConnFile) \ + (SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_CONN_MSG(pSpxConnFile) \ + (!SPX_CONN_FLAG((pSpxConnFile), SPX_CONNFILE_STREAM)) + +#define SPX_LISTEN_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_LISTEN_MASK) + +#define SPX_CONNECT_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_CONNECT_MASK) + +#define SPX_SEND_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_SEND_MASK) + +#define SPX_RECV_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_RECV_MASK) + +#define SPX_DISC_STATE(pSpxConnFile) \ + ((pSpxConnFile)->scf_Flags & SPX_DISC_MASK) + +#if DBG + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + SPX_STORE_LAST_STATE(pSpxConnFile) \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } + +#else + +#define SPX_LISTEN_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("LISTEN: %x -> %x\n", \ + SPX_LISTEN_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + pSpxConnFile->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_LISTEN_MASK) | (newState)); \ + } + +#define SPX_CONNECT_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("CONNECT: %x -> %x\n", \ + SPX_CONNECT_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_CONNECT_MASK) | (newState)); \ + } + +#define SPX_SEND_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("SEND: %x -> %x\n", \ + SPX_SEND_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_SEND_MASK) | (newState)); \ + } + +#define SPX_RECV_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("RECV: %x -> %x\n", \ + SPX_RECV_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_RECV_MASK) | (newState)); \ + } + +#define SPX_DISC_SETSTATE(pSpxConnFile, newState) \ + { \ + DBGPRINT(STATE, INFO, \ + ("DISC: %x -> %x\n", \ + SPX_DISC_STATE(pSpxConnFile), (newState))); \ + DBGPRINT(STATE, INFO, \ + ("FILE: %s - %d\n", __FILE__, __LINE__)); \ + (pSpxConnFile)->scf_Flags = \ + (((pSpxConnFile)->scf_Flags & ~SPX_DISC_MASK) | (newState)); \ + } +#endif //DBG +#define SpxConnQueueSendPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendListTail->sr_Next = _pSendResd; \ + (pSpxConnFile)->scf_SendListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendListHead = _pSendResd; \ + } + +#define SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + (pSpxConnFile)->scf_SendSeqListTail->sr_Next = _pSendResd;\ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } \ + } + +#define SpxConnQueueSendSeqPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_SEND_RESD _pSendResd; \ + _pSendResd = (PSPX_SEND_RESD)((pPkt)->ProtocolReserved); \ + _pSendResd->sr_Next = NULL; \ + if ((pSpxConnFile)->scf_SendSeqListTail != NULL) \ + { \ + _pSendResd->sr_Next = (pSpxConnFile)->scf_SendSeqListHead;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_SendSeqListTail = _pSendResd; \ + } \ + (pSpxConnFile)->scf_SendSeqListHead = _pSendResd; \ + } + +#define SpxConnQueueRecvPktTail(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + (pSpxConnFile)->scf_RecvListTail->rr_Next = _pRecvResd; \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd;\ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } \ + } + +#define SpxConnQueueRecvPktHead(pSpxConnFile, pPkt) \ + { \ + PSPX_RECV_RESD _pRecvResd; \ + _pRecvResd = (PSPX_RECV_RESD)((pPkt)->ProtocolReserved); \ + _pRecvResd->rr_Next = NULL; \ + if ((pSpxConnFile)->scf_RecvListTail != NULL) \ + { \ + _pRecvResd->rr_Next = (pSpxConnFile)->scf_RecvListHead; \ + } \ + else \ + { \ + (pSpxConnFile)->scf_RecvListTail = _pRecvResd; \ + } \ + (pSpxConnFile)->scf_RecvListHead = _pRecvResd; \ + } + +#if DBG +#define SpxConnFileReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileRef (_ConnFile); \ + } + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + SpxConnFileLockRef (_ConnFile); \ + } + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxConnFileDeref (_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _l; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_l)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, _l); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_NewType], \ + 1, \ + &SpxGlobalInterlock); \ + (VOID)SPX_ADD_ULONG ( \ + &(_ConnFile)->scf_RefTypes[_OldType], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + } + +#else // DBG + +#define SpxConnFileReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock) + +#define SpxConnFileLockReference(_ConnFile, _Type) \ + SPX_ADD_ULONG( \ + &(_ConnFile)->scf_RefCount, \ + 1, \ + &(_ConnFile)->scf_Lock); + +#define SpxConnFileDereference(_ConnFile, _Type) \ + { \ + SpxConnFileDeref(_ConnFile); \ + } + +#define SpxConnFileReferenceByCtx(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock((_pAddrFile)->saf_AddrLock, &(_lockHandle)); \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus));\ + CTEFreeLock((_pAddrFile)->saf_AddrLock, (_lockHandle)); \ + } + +#define SpxConnFileReferenceByCtxLock(_pAddrFile, _Ctx, _ppConnFile, _pStatus) \ + SpxConnFileRefByCtxLock((_pAddrFile), (_Ctx), (_ppConnFile),(_pStatus)); + +#define SpxConnFileReferenceById(_ConnId, _ppConnFile, _pStatus) \ + { \ + CTELockHandle _lockHandle; \ + CTEGetLock(&SpxDevice->dev_Lock, &(_lockHandle)); \ + SpxConnFileRefByIdLock(_ConnId, _ppConnFile, _pStatus); \ + CTEFreeLock(&SpxDevice->dev_Lock, (_lockHandle)); \ + } + +#define SpxConnFileTransferReference(_ConnFile, _OldType, _NewType) + +#endif // DBG + + +// Set the packet size. If we are spx1 or spx2 and !neg, check if we are different +// nets, set to min then, else use the size indicated by IPX. If we are spx2, just +// set it to our local max. +// +// Also always even out packet size and round down. This solves an issue with +// data size needing to be even for some novell 802.2 clients. +// +// Fix after beta2 for tokring using receive size. Only if spx2 and neg. +#if defined(_PNP_POWER) +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + &(pSpxConnFile)->scf_LocalTarget.NicHandle, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#else +#define SPX_MAX_PKT_SIZE(pSpxConnFile, fSpx2Neg, fSpx2, pRemNet) \ + { \ + if (!fSpx2 && PARAM(CONFIG_BACKCOMP_SPX)) { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + else { \ + IPX_LINE_INFO _i; \ + \ + (VOID)(*IpxQuery)( \ + IPX_QUERY_LINE_INFO, \ + (pSpxConnFile)->scf_LocalTarget.NicId, \ + &(_i), \ + sizeof(IPX_LINE_INFO), \ + NULL); \ + \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumPacketSize; \ + if (!fSpx2Neg) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = (_i).MaximumSendSize; \ + } \ + \ + if ((pSpxConnFile)->scf_MaxPktSize < SPX_MAX_PACKET) \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("%s : %d.%d\n", __FILE__, __LINE__, fSpx2Neg)); \ + \ + if ((!fSpx2Neg) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != 0) && \ + ((*(UNALIGNED ULONG *)SpxDevice->dev_Network) != 0) && \ + ((*(UNALIGNED ULONG *)(pRemNet)) != \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network)) \ + { \ + if (PARAM(CONFIG_ROUTER_MTU) != 0) \ + { \ + DBGPRINT(CONNECT, ERR, \ + ("SPX_MAX_PKT_SIZE: PARAM %lx Max Pkt %lx\n", \ + PARAM(CONFIG_ROUTER_MTU), \ + (pSpxConnFile)->scf_MaxPktSize)); \ + \ + (pSpxConnFile)->scf_MaxPktSize = \ + (USHORT)(MIN(PARAM(CONFIG_ROUTER_MTU), \ + (ULONG)((pSpxConnFile)->scf_MaxPktSize)));\ + } \ + else \ + { \ + (pSpxConnFile)->scf_MaxPktSize = SPX_MAX_PACKET; \ + } \ + \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: Nets %lx.%lx Max Pkt %d\n", \ + (*(UNALIGNED ULONG *)(pRemNet)), \ + *(UNALIGNED ULONG *)SpxDevice->dev_Network, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: LineInfo Pkt %d\n", \ + (_i).MaximumSendSize)); \ + } \ + } \ + (pSpxConnFile)->scf_MaxPktSize &= ~((USHORT)1); \ + DBGPRINT(CONNECT, DBG, \ + ("SPX_MAX_PKT_SIZE: %lx.%d\n", \ + (pSpxConnFile)->scf_MaxPktSize, \ + (pSpxConnFile)->scf_MaxPktSize)); \ + } +#endif _PNP_POWER + +#if DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + if (_n != NDIS_STATUS_SUCCESS) \ + { \ + DBGPRINT(SEND, ERR, \ + ("SPX_SENDPACKET: Failed with %lx in %s.%lx\n", \ + _n, __FILE__, __LINE__)); \ + } \ + \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#else // DBG +#define SPX_SENDPACKET(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_LocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } +#define SPX_SENDACK(pSpxConnFile, pNdisPkt, pSendResd) \ + { \ + NDIS_STATUS _n; \ + \ + ++SpxDevice->dev_Stat.PacketsSent; \ + \ + _n = (*IpxSendPacket)( \ + &(pSpxConnFile)->scf_AckLocalTarget, \ + (pNdisPkt), \ + (pSendResd)->sr_Len, \ + (pSendResd)->sr_HdrLen); \ + \ + if (_n != NDIS_STATUS_PENDING) \ + { \ + SpxSendComplete( \ + (pNdisPkt), \ + _n); \ + } \ + } + +#endif // DBG + +#define SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile) \ + { \ + if (!SPX_CONN_FLAG( \ + (pSpxConnFile), \ + SPX_CONNFILE_RECVQ)) \ + { \ + SPX_CONN_SETFLAG((pSpxConnFile), SPX_CONNFILE_RECVQ); \ + SpxConnFileLockReference(pSpxConnFile, CFREF_RECV); \ + SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile); \ + } \ + } + +#define SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile) \ + { \ + if (SpxPktConnList.pcl_Tail) \ + { \ + SpxPktConnList.pcl_Tail->scf_PktNext = pSpxConnFile; \ + SpxPktConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxPktConnList.pcl_Tail = \ + SpxPktConnList.pcl_Head = pSpxConnFile; \ + } \ + } + +#define SPX_QUEUE_TAIL_RECVLIST(pSpxConnFile) \ + { \ + if (SpxRecvConnList.pcl_Tail) \ + { \ + SpxRecvConnList.pcl_Tail->scf_ProcessRecvNext = pSpxConnFile; \ + SpxRecvConnList.pcl_Tail = pSpxConnFile; \ + } \ + else \ + { \ + SpxRecvConnList.pcl_Tail = \ + SpxRecvConnList.pcl_Head = pSpxConnFile; \ + } \ + } + + diff --git a/private/ntos/tdi/isnp/spx/h/spxdev.h b/private/ntos/tdi/isnp/spx/h/spxdev.h new file mode 100644 index 000000000..30c0adae5 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxdev.h @@ -0,0 +1,204 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.h + +Abstract: + + This module contains definitions specific to the + SPX module of the ISN transport. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +// Hash buckets for SPX_ADDR done using socket number +#define NUM_SPXADDR_HASH_BUCKETS 8 +#define NUM_SPXADDR_HASH_MASK 7 +#define NUM_SPXCONN_HASH_BUCKETS 8 +#define NUM_SPXCONN_HASH_MASK 7 + +// This structure defines the per-device structure for SPX +// (one of these is allocated globally). +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_ORPHAN 4 + +#define DREF_TOTAL 5 + +typedef struct _DEVICE { + + DEVICE_OBJECT dev_DevObj; // the I/O system's device object. + +#if DBG + ULONG dev_RefTypes[DREF_TOTAL]; +#endif + + CSHORT dev_Type; // type of this structure + USHORT dev_Size; // size of this structure + +#if DBG + UCHAR dev_Signature1[4]; // contains "SPX1" +#endif + + // activity count/this provider. + LONG dev_RefCount; + UCHAR dev_State; + + // number of adapters IPX is bound to. + USHORT dev_Adapters; + + // GLOBAL lock for reference count (used in ExInterlockedXxx calls). + CTELock dev_Interlock; + CTELock dev_Lock; + + // Hash table of lists of addresses opened on this device + struct _SPX_ADDR * dev_AddrHashTable[NUM_SPXADDR_HASH_BUCKETS]; + + // List of all active connections, later this be a tree. + struct _SPX_CONN_FILE * dev_GlobalActiveConnList[NUM_SPXCONN_HASH_BUCKETS]; + USHORT dev_NextConnId; + + // Other configuration parameters. + // Where the current socket allocation is. + USHORT dev_CurrentSocket; + + // Our node and network. + UCHAR dev_Network[4]; + UCHAR dev_Node[6]; + + // Pointer to the config information from registry + PCONFIG dev_ConfigInfo; + + // Control channel identifier + ULONG dev_CcId; + + // These are kept around for error logging, and stored right + // after this structure. + PWCHAR dev_DeviceName; +#if defined(_PNP_POWER) + USHORT dev_DeviceNameLen; +#else + ULONG dev_DeviceNameLen; +#endif _PNP_POWER + +#if DBG + UCHAR dev_Signature2[4]; // contains "SPX2" +#endif + + // Handle to ndis buffer pool for spx stack. + NDIS_HANDLE dev_NdisBufferPoolHandle; + + // registration handle with tdi clients. +#if defined(_PNP_POWER) + HANDLE dev_TdiRegistrationHandle; +#endif _PNP_POWER + + // This interlock is used to guard access to the statistics + // define below. + KSPIN_LOCK dev_StatInterlock; // for ULONG quantities + KSPIN_LOCK dev_StatSpinLock; // for LARGE_INTEGER quantities + + // Counters for most of the statistics that SPX maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + TDI_PROVIDER_STATISTICS dev_Stat; + + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + ERESOURCE dev_AddrResource; + + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + TDI_PROVIDER_INFO dev_ProviderInfo; // information about this provider. + +} DEVICE, * PDEVICE; + +// device state definitions +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +// SPX device name +#define SPX_DEVICE_NAME L"\\Device\\NwlnkSpx" + +#define SPX_TDI_RESOURCES 9 + + +// MACROS +#if DBG + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + 1, \ + &SpxGlobalInterlock); \ + \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + { \ + (VOID)SPX_ADD_ULONG ( \ + &(_Device)->dev_RefTypes[_Type], \ + (ULONG)-1, \ + &SpxGlobalInterlock); \ + SpxDerefDevice (_Device); \ + } + +#else + +#define SpxReferenceDevice(_Device, _Type) \ + { \ + (VOID)InterlockedIncrement ( \ + &(_Device)->dev_RefCount); \ + } + +#define SpxDereferenceDevice(_Device, _Type) \ + SpxDerefDevice (_Device) + +#endif + +// EXPORTED ROUTINES + +VOID +SpxDestroyDevice( + IN PDEVICE Device); + +VOID +SpxDerefDevice( + IN PDEVICE Device); + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr); diff --git a/private/ntos/tdi/isnp/spx/h/spxerror.h b/private/ntos/tdi/isnp/spx/h/spxerror.h new file mode 100644 index 000000000..761342512 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxerror.h @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxerror.h + +Abstract: + + This module contains some error definitions for spx. + +Author: + + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + +Notes: Tab stop: 4 +--*/ + +// Define the modules names for SPX - use the high bits. +#define SPXDRVR 0x00010000 +#define SPXREG 0x00020000 +#define SPXDEV 0x00030000 +#define SPXBIND 0x00040000 +#define SPXRECV 0x00050000 +#define SPXSEND 0x00060000 +#define SPXTIMER 0x00070000 +#define SPXERROR 0x00080000 +#define SPXPKT 0x00090000 +#define SPXUTILS 0x000a0000 +#define SPXCPKT 0x000b0000 +#define SPXCONN 0x000c0000 +#define SPXADDR 0x000d0000 +#define SPXCUTIL 0x000e0000 +#define SPXINIT 0x000f0000 +#define SPXMEM 0x00100000 +#define SPXQUERY 0x00200000 + + +// DEBUGGING SUPPORT: +// Debugging messages are provided per-subsystem defined here, and within +// the subsystems, there are 4 levels of messages. +// +// The four levels of debug messages are: +// +// INFO: Informational messages, eg., entry exit in routines +// DBG: Used when debugging some msgs are turned from info to dbg +// WARN: Something went wrong, but its not an error, eg., packet was not ours +// ERR: Error situations, but we can still run if a retry happens +// FATAL: In this situation, the driver is not operational + +#define DBG_LEVEL_INFO 0x4000 +#define DBG_LEVEL_DBG 0x5000 +#define DBG_LEVEL_DBG1 0x5001 +#define DBG_LEVEL_DBG2 0x5002 +#define DBG_LEVEL_DBG3 0x5003 +#define DBG_LEVEL_WARN 0x6000 +#define DBG_LEVEL_ERR 0x7000 +#define DBG_LEVEL_FATAL 0x8000 + +// SUBSYSTEMS +#define DBG_COMP_DEVICE 0x00000001 +#define DBG_COMP_CREATE 0x00000002 +#define DBG_COMP_ADDRESS 0x00000004 +#define DBG_COMP_SEND 0x00000008 +#define DBG_COMP_NDIS 0x00000010 +#define DBG_COMP_RECEIVE 0x00000020 +#define DBG_COMP_CONFIG 0x00000040 +#define DBG_COMP_PACKET 0x00000080 +#define DBG_COMP_RESOURCES 0x00000100 +#define DBG_COMP_BIND 0x00000200 +#define DBG_COMP_UNLOAD 0x00000400 +#define DBG_COMP_DUMP 0x00000800 +#define DBG_COMP_REFCOUNTS 0x00001000 +#define DBG_COMP_SYSTEM 0x00002000 +#define DBG_COMP_CRITSEC 0x00004000 +#define DBG_COMP_UTILS 0x00008000 +#define DBG_COMP_TDI 0x00010000 +#define DBG_COMP_CONNECT 0x00020000 +#define DBG_COMP_DISC 0x00040000 +#define DBG_COMP_ACTION 0x00080000 +#define DBG_COMP_STATE 0x00100000 + +#define DBG_COMP_MOST (DBG_COMP_DEVICE | \ + DBG_COMP_CREATE | \ + DBG_COMP_ADDRESS | \ + DBG_COMP_SEND | \ + DBG_COMP_NDIS | \ + DBG_COMP_RECEIVE | \ + DBG_COMP_CONFIG | \ + DBG_COMP_PACKET | \ + DBG_COMP_RESOURCES | \ + DBG_COMP_BIND | \ + DBG_COMP_UNLOAD | \ + DBG_COMP_DUMP | \ + DBG_COMP_REFCOUNTS | \ + DBG_COMP_SYSTEM | \ + DBG_COMP_CRITSEC | \ + DBG_COMP_UTILS | \ + DBG_COMP_TDI | \ + DBG_COMP_CONNECT | \ + DBG_COMP_DISC | \ + DBG_COMP_ACTION | \ + DBG_COMP_STATE) + + +// More debugging support. These values define the dumping components. +// There are a max of 32 such components that can be defined. Each of +// these are associated with a dump routine. It one is specified and +// enabled, periodically it is called. It is upto that component to +// decide what it wants to do + +#define DBG_DUMP_DEF_INTERVAL 30 // In Seconds + +// This defines the number of times an error has to happen consecutively before +// it gets logged again. +#define ERROR_CONSEQ_FREQ 200 +#define ERROR_CONSEQ_TIME (60*30) // 30 minutes + +#ifdef DBG +typedef VOID (*DUMP_ROUTINE)(VOID); + +extern +BOOLEAN +SpxDumpComponents( + IN PVOID Context); + +#endif + +// +// PROTOTYPES +// + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue); + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); + + +// +// MACROS +// + +#if DBG +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#else + +#define LOG_ERROR(Error, NtStatus, SecondString, RawData, RawDataLen) \ + { \ + SpxWriteGeneralErrorLog( \ + SpxDevice, \ + Error, \ + FILENUM | __LINE__, \ + NtStatus, \ + SecondString, \ + RawData, \ + RawDataLen); \ + } + +#define RES_LOG_ERROR(BytesNeeded) \ + { \ + SpxWriteResourceErrorLog( \ + SpxDevice, \ + BytesNeeded, \ + FILENUM | __LINE__); \ + } + +#endif + + +#if DBG + +#define DBGPRINT(Component, Level, Fmt) \ + { \ + if (((DBG_LEVEL_ ## Level) >= SpxDebugLevel) && \ + (SpxDebugSystems & (DBG_COMP_ ## Component))) \ + { \ + DbgPrint("SPX: "); \ + DbgPrint Fmt; \ + } \ + } + +#define DBGBRK(Level) \ + { \ + if ((DBG_LEVEL_ ## Level) >= SpxDebugLevel) \ + DbgBreakPoint(); \ + } + +#define TMPLOGERR() \ + { \ + DBGPRINT(MOST, ERR, \ + ("TempErrLog: %s, Line %ld\n", __FILE__, __LINE__)); \ + } + +#else +#define DBGPRINT(Component, Level, Fmt) +#define DBGBRK(Level) +#define TMPLOGERR() +#endif + +extern +VOID +SpxWriteErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen); diff --git a/private/ntos/tdi/isnp/spx/h/spxmem.h b/private/ntos/tdi/isnp/spx/h/spxmem.h new file mode 100644 index 000000000..717c69a6b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxmem.h @@ -0,0 +1,142 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.h + +Abstract: + + This module contains memory management routines. + +Author: + + Nikhil Kamkolkar (nikhilk) 17-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +#define QWORDSIZEBLOCK(Size) (((Size)+sizeof(LARGE_INTEGER)-1) & ~(sizeof(LARGE_INTEGER)-1)) +#define SPX_MEMORY_SIGNATURE *(PULONG)"SPXM" +#define ZEROED_MEMORY_TAG 0xF0000000 +#define SPX_TAG *((PULONG)"SPX ") + +// +// Definitions for the block management package +// +typedef UCHAR BLKID; + +// Add a BLKID_xxx and an entry to atalkBlkSize for every block client +#define BLKID_TIMERLIST (BLKID)0 +#define BLKID_NDISSEND (BLKID)1 +#define BLKID_NDISRECV (BLKID)2 +#define NUM_BLKIDS (BLKID)3 + +typedef struct _BLK_CHUNK +{ + struct _BLK_CHUNK * bc_Next; // Pointer to next in the link + SHORT bc_NumFrees; // Number of free blocks in the chunk + UCHAR bc_Age; // Number of invocations since the chunk free + BLKID bc_BlkId; // Id of the block + struct _BLK_HDR * bc_FreeHead; // Head of the list of free blocks + +#ifndef SPX_OWN_PACKETS + PVOID bc_ChunkCtx; // Used to store pool header if not own + // packets +#else + PVOID bc_Padding; // Keep the header 16 bytes +#endif + + // This is followed by an array of N blks of size M such that the block header + // is exactly spxChunkSize[i] + +} BLK_CHUNK, *PBLK_CHUNK; + +typedef struct _BLK_HDR +{ + union + { + struct _BLK_HDR * bh_Next; // Valid when it is free + struct _BLK_CHUNK * bh_pChunk; // The parent chunk to which this blocks belong + // valid when it is allocated + }; + PVOID bh_Padding; // Make the header 8 bytes +} BLK_HDR, *PBLK_HDR; + +#define BC_OVERHEAD (8+8) // LARGE_INTEGER for SpxAllocMemory() header and + // POOL_HEADER for ExAllocatePool() header + +#define BLOCK_POOL_TIMER 1000 // Check interval (1 sec) +#define MAX_BLOCK_POOL_AGE 3 // # of timer invocations before free + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown); + + +#ifdef TRACK_MEMORY_USAGE + +#define SpxAllocateMemory(Size) SpxAllocMem((Size), FILENUM | __LINE__) + +extern +PVOID +SpxAllocMem( + IN ULONG Size, + IN ULONG FileLine +); + +extern +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine +); + +#else + +#define SpxAllocateMemory(Size) SpxAllocMem(Size) +#define SpxTrackMemoryUsage(pMem, Alloc, FileLine) + +extern +PVOID +SpxAllocMem( + IN ULONG Size +); + +#endif // TRACK_MEMORY_USAGE + +VOID +SpxFreeMemory( + IN PVOID pBuf); + +#define SpxAllocateZeroedMemory(Size) SpxAllocateMemory((Size) | ZEROED_MEMORY_TAG) + + +extern +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice); + +extern +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice); + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId); + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId); + diff --git a/private/ntos/tdi/isnp/spx/h/spxntdef.h b/private/ntos/tdi/isnp/spx/h/spxntdef.h new file mode 100644 index 000000000..60f9c9e54 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxntdef.h @@ -0,0 +1,72 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxntdef.h + +Abstract: + + Missing nt definitions in ntddk.h + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +NTSTATUS +NTAPI +NtCreateFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength + ); + +NTSTATUS +NTAPI +NtClose( + IN HANDLE Handle + ); + +NTSTATUS +NTAPI +NtDeviceIoControlFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG IoControlCode, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength + ); + +NTSTATUS +NTAPI +NtWaitForSingleObject( + IN HANDLE Handle, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout OPTIONAL + ); + + diff --git a/private/ntos/tdi/isnp/spx/h/spxpkt.h b/private/ntos/tdi/isnp/spx/h/spxpkt.h new file mode 100644 index 000000000..b643ed95b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxpkt.h @@ -0,0 +1,466 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// Use our own NDIS packets +#define SPX_OWN_PACKETS 1 + +// Offsets into the IPX header +#define IPX_HDRSIZE 30 // Size of the IPX header +#define IPX_CHECKSUM 0 // Checksum +#define IPX_LENGTH 2 // Length +#define IPX_XPORTCTL 4 // Transport Control +#define IPX_PKTTYPE 5 // Packet Type +#define IPX_DESTADDR 6 // Dest. Address (Total) +#define IPX_DESTNET 6 // Dest. Network Address +#define IPX_DESTNODE 10 // Dest. Node Address +#define IPX_DESTSOCK 16 // Dest. Socket Number +#define IPX_SRCADDR 18 // Source Address (Total) +#define IPX_SRCNET 18 // Source Network Address +#define IPX_SRCNODE 22 // Source Node Address +#define IPX_SRCSOCK 28 // Source Socket Number + +#define IPX_NET_LEN 4 +#define IPX_NODE_LEN 6 + + +#include <packon.h> + +// Definition of the IPX/SPX header. +typedef struct _IPXSPX_HEADER +{ + USHORT hdr_CheckSum; + USHORT hdr_PktLen; + UCHAR hdr_XportCtrl; + UCHAR hdr_PktType; + UCHAR hdr_DestNet[4]; + UCHAR hdr_DestNode[6]; + USHORT hdr_DestSkt; + UCHAR hdr_SrcNet[4]; + UCHAR hdr_SrcNode[6]; + USHORT hdr_SrcSkt; + + // SPX Header Elements + UCHAR hdr_ConnCtrl; + UCHAR hdr_DataType; + USHORT hdr_SrcConnId; + USHORT hdr_DestConnId; + USHORT hdr_SeqNum; + USHORT hdr_AckNum; + USHORT hdr_AllocNum; + + // For non-CR SPXII packets only + USHORT hdr_NegSize; + +} IPXSPX_HDR, *PIPXSPX_HDR; + +#include <packoff.h> + +// NDIS Packet size +#define NDIS_PACKET_SIZE 48 + +// Minimum header size (doesnt include neg size) +#define MIN_IPXSPX_HDRSIZE (sizeof(IPXSPX_HDR) - sizeof(USHORT)) +#define MIN_IPXSPX2_HDRSIZE sizeof(IPXSPX_HDR) +#define SPX_CR_PKTLEN 42 + +// SPX packet type +#define SPX_PKT_TYPE 0x5 + +// Connection control fields +#define SPX_CC_XHD 0x01 +#define SPX_CC_RES1 0x02 +#define SPX_CC_NEG 0x04 +#define SPX_CC_SPX2 0x08 +#define SPX_CC_EOM 0x10 +#define SPX_CC_ATN 0x20 +#define SPX_CC_ACK 0x40 +#define SPX_CC_SYS 0x80 + +#define SPX_CC_CR (SPX_CC_ACK | SPX_CC_SYS) + +// Data stream types +#define SPX2_DT_ORDREL 0xFD +#define SPX2_DT_IDISC 0xFE +#define SPX2_DT_IDISC_ACK 0xFF + +// Negotiation size +#define SPX_MAX_PACKET 576 +#define SPX_NEG_MIN SPX_MAX_PACKET +#define SPX_NEG_MAX 65535 + +// No packet references connection. But if the sends are being aborted, and +// the packet happens to be owned by ipx at the time, the pkt is dequeued from +// conn, the ABORT flag is set and conn is referenced for packet. +// +// Send packet states +// ABORT : Used for aborted packet. Calls AbortSendPkt(). +// IPXOWNS : Currently owned by ipx +// FREEDATA: Frees the data associated with second ndis buffer desc +// ACKREQ : Only for sequenced packets. Set by retry timer in packets it wants +// resent (1 for spx1, all pending for spx2) with ack bit set. +// DESTROY : Only for non-sequenced packets, dequeue packet from list and free. +// REQ : For both seq/non-seq. A request is associated with the packet +// SEQ : Packet is a sequenced packet. +// LASTPKT : Packet is last packet comprising the request, if acked req is done. +// EOM : Send EOM with the last packet for this request +// ACKEDPKT: Send completion must only deref req with pkt and complete if zero. +// + +#define SPX_SENDPKT_IDLE 0 +#define SPX_SENDPKT_ABORT 0x0002 +#define SPX_SENDPKT_IPXOWNS 0x0004 +#define SPX_SENDPKT_FREEDATA 0x0008 +#define SPX_SENDPKT_ACKREQ 0x0010 +#define SPX_SENDPKT_DESTROY 0x0020 +#define SPX_SENDPKT_REQ 0x0040 +#define SPX_SENDPKT_SEQ 0x0080 +#define SPX_SENDPKT_LASTPKT 0x0100 +#define SPX_SENDPKT_ACKEDPKT 0x0200 +#define SPX_SENDPKT_EOM 0x0400 +#define SPX_SENDPKT_REXMIT 0x0800 + +// Packet types +#define SPX_TYPE_CR 0x01 +#define SPX_TYPE_CRACK 0x02 +#define SPX_TYPE_SN 0x03 +#define SPX_TYPE_SNACK 0x04 +#define SPX_TYPE_SS 0x05 +#define SPX_TYPE_SSACK 0x06 +#define SPX_TYPE_RR 0x07 +#define SPX_TYPE_RRACK 0x08 +#define SPX_TYPE_IDISC 0x09 +#define SPX_TYPE_IDISCACK 0x0a +#define SPX_TYPE_ORDREL 0x0b +#define SPX_TYPE_ORDRELACK 0x0c +#define SPX_TYPE_DATA 0x0d +#define SPX_TYPE_DATAACK 0x0e +#define SPX_TYPE_DATANACK 0x0f +#define SPX_TYPE_PROBE 0x10 + +// Definition of the protocol reserved field of a send packet. +// BUGBUG: Make Len/HdrLen USHORTS, move to the end before the +// sr_SentTime so we dont use padding space. +typedef struct _SPX_SEND_RESD +{ + UCHAR sr_Id; // Set to SPX + UCHAR sr_Type; // What kind of packet + USHORT sr_State; // State of send packet + PVOID sr_Reserved1; // Needed by IPX + PVOID sr_Reserved2; // Needed by IPX +#if defined(_PNP_POWER) + PVOID sr_Reserved[SEND_RESERVED_COMMON_SIZE-2]; // needed by IPX for local target +#endif _PNP_POWER + ULONG sr_Len; // Length of packet + ULONG sr_HdrLen; // Included header length + + struct _SPX_SEND_RESD * sr_Next; // Points to next packet + // in send queue in conn. + PREQUEST sr_Request; // request associated + ULONG sr_Offset; // Offset in mdl for sends + +#ifndef SPX_OWN_PACKETS + PVOID sr_FreePtr; // Ptr to use in free chunk +#endif + + struct _SPX_CONN_FILE * sr_ConnFile; // that this send is on + USHORT sr_SeqNum; // Seq num for seq pkts + + // Quad word aligned. + LARGE_INTEGER sr_SentTime; // Time packet was sent + // Only valid for data pkt + // with ACKREQ set. + +} SPX_SEND_RESD, *PSPX_SEND_RESD; + + + +// Recv packet states +#define SPX_RECVPKT_IDLE 0 +#define SPX_RECVPKT_BUFFERING 0x0001 +#define SPX_RECVPKT_IDISC 0x0002 +#define SPX_RECVPKT_ORD_DISC 0x0004 +#define SPX_RECVPKT_INDICATED 0x0008 +#define SPX_RECVPKT_SENDACK 0x0010 +#define SPX_RECVPKT_EOM 0x0020 +#define SPX_RECVPKT_IMMEDACK 0x0040 + +#define SPX_RECVPKT_DISCMASK (SPX_RECVPKT_ORD_DISC | SPX_RECVPKT_IDISC) + +// Definition of the protocol reserved field of a receive packet. +typedef struct _SPX_RECV_RESD +{ + UCHAR rr_Id; // Set to SPX + USHORT rr_State; // State of receive packet + struct _SPX_RECV_RESD * rr_Next; // Points to next packet + ULONG rr_DataOffset; // To indicate/copy from + +#ifndef SPX_OWN_PACKETS + PVOID rr_FreePtr; // Ptr to use in free chunk +#endif + +#if DBG + USHORT rr_SeqNum; // Seq num of packet +#endif + + PREQUEST rr_Request; // request waiting on xfer + struct _SPX_CONN_FILE * rr_ConnFile; // that this recv is on + +} SPX_RECV_RESD, *PSPX_RECV_RESD; + + +// Destination built as an assign of 3 ulongs. +#define SpxBuildIpxHdr(pIpxSpxHdr, PktLen, pRemAddr, SrcSkt) \ + { \ + PBYTE pDestIpxAddr = (PBYTE)pIpxSpxHdr->hdr_DestNet; \ + (pIpxSpxHdr)->hdr_CheckSum = 0xFFFF; \ + PUTSHORT2SHORT((PUSHORT)(&(pIpxSpxHdr)->hdr_PktLen), (PktLen)); \ + (pIpxSpxHdr)->hdr_XportCtrl = 0; \ + (pIpxSpxHdr)->hdr_PktType = SPX_PKT_TYPE; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNet))= \ + *((UNALIGNED ULONG *)(SpxDevice->dev_Network)); \ + *((UNALIGNED ULONG *)((pIpxSpxHdr)->hdr_SrcNode)) = \ + *((UNALIGNED ULONG *)SpxDevice->dev_Node); \ + *((UNALIGNED USHORT *)((pIpxSpxHdr)->hdr_SrcNode+4)) = \ + *((UNALIGNED USHORT *)(SpxDevice->dev_Node+4)); \ + *((UNALIGNED USHORT *)&((pIpxSpxHdr)->hdr_SrcSkt)) = \ + SrcSkt; \ + } + +#define SpxCopyIpxAddr(pIpxSpxHdr, pDestIpxAddr) \ + { \ + PBYTE pRemAddr = (PBYTE)pIpxSpxHdr->hdr_SrcNet; \ + *((UNALIGNED ULONG *)pDestIpxAddr) = \ + *((UNALIGNED ULONG *)pRemAddr); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+4)) = \ + *((UNALIGNED ULONG *)(pRemAddr+4)); \ + *((UNALIGNED ULONG *)(pDestIpxAddr+8)) = \ + *((UNALIGNED ULONG *)(pRemAddr+8)); \ + } + +#ifndef SPX_OWN_PACKETS +#define SpxAllocSendPacket(_Device,_SendPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_SEND_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_SendPacket), (_Handle)); \ + SEND_RESD(_SendPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + NDIS_HANDLE _Handle; \ + NdisAllocatePacketPool(_Status, &(_Handle),1,sizeof(SPX_RECV_RESD));\ + if (*(_Status) == NDIS_STATUS_SUCCESS) { \ + NdisAllocatePacket(_Status, &(_RecvPacket), (_Handle)); \ + RECV_RESD(_RecvPacket)->sr_PoolHandle = (_Handle); \ + } \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; \ + NdisFreePacket(_Packet); \ + NdisFreePacketPool(_Handle); \ + } + +#define SpxReInitSendPacket(_Packet) + { + NDIS_HANDLE _Handle = SEND_RESD(_Packet)->sr_PoolHandle; + NdisReInitializePacket(_Packet); + SEND_RESD(_Packet)->sr_PoolHandle = (_Handle); + } + +#define SpxReInitRecvPacket(_Packet) + { + NDIS_HANDLE _Handle = RECV_RESD(_Packet)->rr_PoolHandle; + NdisReInitializePacket(_Packet); + RECV_RESD(_Packet)->rr_PoolHandle = (_Handle); + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#else + +#define SpxAllocSendPacket(_Device, _SendPacket, _Status) \ + { \ + if (*(_SendPacket) = SpxBPAllocBlock(BLKID_NDISSEND)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxAllocRecvPacket(_Device,_RecvPacket,_Status) \ + { \ + if (*(_RecvPacket) = SpxBPAllocBlock(BLKID_NDISRECV)) \ + *(_Status) = NDIS_STATUS_SUCCESS; \ + else \ + *(_Status) = NDIS_STATUS_RESOURCES; \ + } + +#define SpxFreeSendPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISSEND); \ + } + +#define SpxFreeRecvPacket(_Device,_Packet) \ + { \ + SpxBPFreeBlock(_Packet, BLKID_NDISRECV); \ + } + +#define SpxReInitSendPacket(_Packet) \ + { \ + } + +#define SpxReInitRecvPacket(_Packet) \ + { \ + } + +#define SEND_RESD(_Packet) ((PSPX_SEND_RESD)((_Packet)->ProtocolReserved)) +#define RECV_RESD(_Packet) ((PSPX_RECV_RESD)((_Packet)->ProtocolReserved)) + +#endif + + +// +// Routine Prototypes +// + +VOID +SpxPktBuildCr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildCrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN struct _SPX_ADDR * pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildSn( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSs( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSsAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildSnAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State); + +VOID +SpxPktBuildRr( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State); + +VOID +SpxPktBuildRrAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize); + +VOID +SpxPktBuildProbe( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2); + +VOID +SpxPktBuildData( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length); + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ); + +VOID +SpxPktBuildAck( + IN struct _SPX_CONN_FILE * pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend); + +VOID +SpxPktBuildDisc( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType); + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt); + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt); diff --git a/private/ntos/tdi/isnp/spx/h/spxquery.h b/private/ntos/tdi/isnp/spx/h/spxquery.h new file mode 100644 index 000000000..68e0a1ca8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxquery.h @@ -0,0 +1,54 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#define SPX_TDI_PROVIDERINFO_VERSION 0x0001 +#define SPX_PINFOSENDSIZE 0xFFFFFFFF +#define SPX_PINFOMINMAXLOOKAHEAD 128 + +// +// Bug #14498: Indicate to AFD that we are capable of orderly disc so AFD can follow +// these semantics. +// In order to have SPXI connections work correctly, we OR this bit in at query time. +// (see SpxTdiQueryInformation) +// +#define SPX_PINFOSERVICEFLAGS ( TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_DELAYED_ACCEPTANCE | \ + TDI_SERVICE_MESSAGE_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY) // | \ + // TDI_SERVICE_ORDERLY_RELEASE ) + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo); + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request); + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request); + diff --git a/private/ntos/tdi/isnp/spx/h/spxrecv.h b/private/ntos/tdi/isnp/spx/h/spxrecv.h new file mode 100644 index 000000000..0ce382990 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxrecv.h @@ -0,0 +1,89 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +VOID +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred); + +VOID +SpxReceiveComplete( + IN USHORT NicId); + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize); + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize); + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn); + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn); + diff --git a/private/ntos/tdi/isnp/spx/h/spxreg.h b/private/ntos/tdi/isnp/spx/h/spxreg.h new file mode 100644 index 000000000..4e0cb469b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxreg.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.h + +Abstract: + + Private include file for the ISN SPX module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + +#define HALFSEC_TO_MS_FACTOR 500 +#define IPX_REG_PATH L"NwlnkIpx\\Linkage" + +// These are used to index into the Parameters array in CONFIG. +#define CONFIG_CONNECTION_COUNT 0 +#define CONFIG_CONNECTION_TIMEOUT 1 +#define CONFIG_INIT_PACKETS 2 +#define CONFIG_MAX_PACKETS 3 +#define CONFIG_INITIAL_RETRANSMIT_TIMEOUT 4 +#define CONFIG_KEEPALIVE_COUNT 5 +#define CONFIG_KEEPALIVE_TIMEOUT 6 +#define CONFIG_WINDOW_SIZE 7 +#define CONFIG_SOCKET_RANGE_START 8 +#define CONFIG_SOCKET_RANGE_END 9 +#define CONFIG_SOCKET_UNIQUENESS 10 +#define CONFIG_MAX_PACKET_SIZE 11 +#define CONFIG_REXMIT_COUNT 12 + +// Hidden parameters +#define CONFIG_DISABLE_SPX2 13 +#define CONFIG_ROUTER_MTU 14 +#define CONFIG_BACKCOMP_SPX 15 + +#define CONFIG_PARAMETERS 16 + +// Main configuration structure. +typedef struct _CONFIG { + + ULONG cf_Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING cf_DeviceName; // device name exported + PWSTR cf_RegistryPathBuffer; // path to config info + +} CONFIG, * PCONFIG; + + +#define PARAM(x) (SpxDevice->dev_ConfigInfo->cf_Parameters[(x)]) + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr); + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config); + diff --git a/private/ntos/tdi/isnp/spx/h/spxsend.h b/private/ntos/tdi/isnp/spx/h/spxsend.h new file mode 100644 index 000000000..40ef810ea --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxsend.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +VOID +SpxSendComplete( + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus); + +VOID +SpxSendPktRelease( + IN PNDIS_PACKET pPkt, + IN UINT BufCount); diff --git a/private/ntos/tdi/isnp/spx/h/spxtimer.h b/private/ntos/tdi/isnp/spx/h/spxtimer.h new file mode 100644 index 000000000..225037bd2 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxtimer.h @@ -0,0 +1,101 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.h + +Abstract: + + This module contains routines to schedule timer events. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + +Revision History: + 19 Jun 1992 Initial Version + +Notes: Tab stop: 4 +--*/ + +#define TIMER_DONT_REQUEUE 0 +#define TIMER_REQUEUE_CUR_VALUE 1 + +typedef ULONG (*TIMER_ROUTINE)(IN PVOID Context, IN BOOLEAN TimerShuttingDown); + +extern +NTSTATUS +SpxTimerInit( + VOID); + +extern +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG DeltaTime, // Schedule after this much time + IN PVOID pContext); // Context to pass to the routine + +extern +VOID +SpxTimerFlushAndStop( + VOID); + +extern +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue); + +#define TMR_SIGNATURE *(PULONG)"ATMR" +#if DBG +#define VALID_TMR(pTmr) (((pTmr) != NULL) && \ + ((pTmr)->tmr_Signature == TMR_SIGNATURE)) +#else +#define VALID_TMR(pTmr) ((pTmr) != NULL) +#endif +typedef struct _TimerList +{ +#if DBG + ULONG tmr_Signature; +#endif + struct _TimerList * tmr_Next; // Link to next + struct _TimerList ** tmr_Prev; // Link to prev + struct _TimerList * tmr_Overflow; // Link to overflow entry in hash table + ULONG tmr_AbsTime; // Absolute time, for re-enqueue + ULONG tmr_RelDelta; // Relative to the previous entry + ULONG tmr_Id; // Unique Id for this event + BOOLEAN tmr_Cancelled; // Was the timer cancelled? + TIMER_ROUTINE tmr_Worker; // Real Worker + PVOID tmr_Context; // Real context +} TIMERLIST, *PTIMERLIST; + + +#define SpxGetCurrentTime() (SpxTimerCurrentTime/SPX_TIMER_FACTOR) +#define SpxGetCurrentTick() SpxTimerCurrentTime + +// Keep this at a ONE second level. +#define SPX_TIMER_FACTOR 10 // i.e. 10 ticks per second +#define SPX_MS_TO_TICKS 100 // Divide ms by this to get ticks +#define SPX_TIMER_TICK -1000000L // 100ms in 100ns units +#define SPX_TIMER_WAIT 50 // Time to wait in FlushAndStop in ms +#define TIMER_HASH_TABLE 32 + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); + +VOID +spxTimerWorker( + IN PTIMERLIST pList); + +VOID +spxTimerEnqueue( + PTIMERLIST pListNew); + + diff --git a/private/ntos/tdi/isnp/spx/h/spxutils.h b/private/ntos/tdi/isnp/spx/h/spxutils.h new file mode 100644 index 000000000..074a1649b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/h/spxutils.h @@ -0,0 +1,178 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.h + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +// For PROTO_SPX, i'd return a device name from the dll of the form +// \Device\NwlnkSpx\SpxStream (for SOCK_STREAM) or +// \Device\NwlnkSpx\Spx (for SOCK_SEQPKT) +// +// and for PROTO_SPXII (the more common case we hope, even if +// internally we degrade to SPX1 cause of the remote client's +// limitations) +// \Device\NwlnkSpx\Stream (for SOCK_STREAM) or +// \Device\NwlnkSpx (for SOCK_SEQPKT) + +#define SOCKET1STREAM_SUFFIX L"\\SpxStream" +#define SOCKET1_SUFFIX L"\\Spx" +#define SOCKET2STREAM_SUFFIX L"\\Stream" +#define SOCKET1_TYPE_SEQPKT 0 +#define SOCKET2_TYPE_SEQPKT 1 +#define SOCKET1_TYPE_STREAM 2 +#define SOCKET2_TYPE_STREAM 3 + +#define IN_RANGE(_S, _RangeStart, _RangeEnd) \ + ((_S >= _RangeStart) && (_S <= _RangeEnd)) + + +// +// The following macros deal with on-the-wire integer and long values +// +// On the wire format is big-endian i.e. a long value of 0x01020304 is +// represented as 01 02 03 04. Similarly an int value of 0x0102 is +// represented as 01 02. +// +// The host format is not assumed since it will vary from processor to +// processor. +// + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = (USHORT) (*(PBYTE)(SrcPtr)) + +// Get a byte from on-the-wire format to a short in the host format +#define GETBYTE2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = (ULONG) (*(PBYTE)(SrcPtr)) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a short from on-the-wire format to a dword in the host format +#define GETSHORT2SHORT(DstPtr, SrcPtr) \ + *(PUSHORT)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 8) + \ + (*((PBYTE)(SrcPtr)+1) )) + +// Get a dword from on-the-wire format to a dword in the host format +#define GETULONG2ULONG(DstPtr, SrcPtr) \ + *(PULONG)(DstPtr) = ((*((PBYTE)(SrcPtr)+0) << 24) + \ + (*((PBYTE)(SrcPtr)+1) << 16) + \ + (*((PBYTE)(SrcPtr)+2) << 8) + \ + (*((PBYTE)(SrcPtr)+3) )) + +// Get a dword from on-the-wire format to a dword in the same format but +// also watch out for alignment +#define GETULONG2ULONG_NOCONV(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0); \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1); \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2); \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3); + +// Put a dword from the host format to a short to on-the-wire format +#define PUTBYTE2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2BYTE(DstPtr, Src) \ + *((PBYTE)(DstPtr)) = ((USHORT)(Src) % 256) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTSHORT2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((USHORT)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE)(Src) + +// Put a dword from the host format to a byte to on-the-wire format +#define PUTULONG2BYTE(DstPtr, Src) \ + *(PBYTE)(DstPtr) = (BYTE)(Src) + +// Put a dword from the host format to a short to on-the-wire format +#define PUTULONG2SHORT(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+1) = (BYTE) (Src) + +// Put a dword from the host format to a dword to on-the-wire format +#define PUTULONG2ULONG(DstPtr, Src) \ + *((PBYTE)(DstPtr)+0) = (BYTE) ((ULONG)(Src) >> 24), \ + *((PBYTE)(DstPtr)+1) = (BYTE) ((ULONG)(Src) >> 16), \ + *((PBYTE)(DstPtr)+2) = (BYTE) ((ULONG)(Src) >> 8), \ + *((PBYTE)(DstPtr)+3) = (BYTE) (Src) + +// Put a BYTE[4] array into another BYTE4 array. +#define PUTBYTE42BYTE4(DstPtr, SrcPtr) \ + *((PBYTE)(DstPtr)+0) = *((PBYTE)(SrcPtr)+0), \ + *((PBYTE)(DstPtr)+1) = *((PBYTE)(SrcPtr)+1), \ + *((PBYTE)(DstPtr)+2) = *((PBYTE)(SrcPtr)+2), \ + *((PBYTE)(DstPtr)+3) = *((PBYTE)(SrcPtr)+3) + +// MIN/MAX macros +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + + + +// Exported prototypes + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr); + +LONG +SpxRandomNumber( + VOID); + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType); + +VOID +SpxSleep( + IN ULONG TimeInMs); + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket); + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr); + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress); + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength); + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1); diff --git a/private/ntos/tdi/isnp/spx/mp/makefile b/private/ntos/tdi/isnp/spx/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/spx/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/spx/mp/sources b/private/ntos/tdi/isnp/spx/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/spx/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/spx/nwlnkspx.rc b/private/ntos/tdi/isnp/spx/nwlnkspx.rc new file mode 100644 index 000000000..02175f21d --- /dev/null +++ b/private/ntos/tdi/isnp/spx/nwlnkspx.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 SPX Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnkspx.sys" +#define VER_ORIGINALFILENAME_STR "nwlnkspx.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/spx/precomp.h b/private/ntos/tdi/isnp/spx/precomp.h new file mode 100644 index 000000000..d227d02f9 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/precomp.h @@ -0,0 +1 @@ +#include "isnspx.h" diff --git a/private/ntos/tdi/isnp/spx/sources.inc b/private/ntos/tdi/isnp/spx/sources.inc new file mode 100644 index 000000000..419f580f1 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/sources.inc @@ -0,0 +1,65 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnkspx + +TARGETNAME=nwlnkspx +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\h;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=..\spxdrvr.c \ + ..\spxreg.c \ + ..\spxdev.c \ + ..\spxbind.c \ + ..\spxaddr.c \ + ..\spxconn.c \ + ..\spxcutil.c \ + ..\spxcpkt.c \ + ..\spxrecv.c \ + ..\spxsend.c \ + ..\spxquery.c \ + ..\spxutils.c \ + ..\spxmem.c \ + ..\spxtimer.c \ + ..\spxpkt.c \ + ..\globals.c \ + ..\spxerror.c \ + ..\nwlnkspx.rc + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/ntos/tdi/isnp/spx/spxaddr.c b/private/ntos/tdi/isnp/spx/spxaddr.c new file mode 100644 index 000000000..e9e85dc5c --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxaddr.c @@ -0,0 +1,1729 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxaddr.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Author: + + Adam Barr (adamba ) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, SpxAddrFileCreate) +#pragma alloc_text( PAGE, SpxAddrFileClose) +#endif + +// Define module number for event logging entries +#define FILENUM SPXADDR + +// Map all generic accesses to the same one. +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + +#define REORDER(_Socket) ((((_Socket) & 0xff00) >> 8) | (((_Socket) & 0x00ff) << 8)) + + + + +NTSTATUS +SpxAddrOpen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + DeviceObject - pointer to the device object describing the ST transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR pAddr; + PSPX_ADDR_FILE pAddrFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED * AddressName; + USHORT Socket, hostSocket; + ULONG DesiredShareAccess; + CTELockHandle LockHandle, LockHandleAddr; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = FALSE; + +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // The network name is in the EA, passed in the request. + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no EA\n", Request)); + + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // this may be a valid name; parse the name from the EA and use it if OK. + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + AddressName = (PTA_ADDRESS)&name->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the first one of type IPX. + for (i=0;i<name->TAAddressCount;i++) + { + if (AddressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (AddressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + Socket = + ((TDI_ADDRESS_IPX UNALIGNED *)&AddressName->Address[0])->Socket; + + GETSHORT2SHORT(&hostSocket, &Socket); + + DBGPRINT(CREATE, DBG, + ("SpxAddrOpen: Creating socket %lx.h%lx\n", + Socket, hostSocket )); + + found = TRUE; + } + break; + + } + else + { + + AddressName = (PTA_ADDRESS)(AddressName->Address + + AddressName->AddressLength); + } + } + + if (!found) + { + DBGPRINT(TDI, ERR, + ("OpenAddress: REQUEST %lx has no IPX Address\n", Request)); + + return STATUS_INVALID_ADDRESS_COMPONENT; + } + +#ifdef SOCKET_RANGE_OPEN_LIMITATION_REMOVED + // Is the socket in our range if its in the range 0x4000-0x7FFF + if (IN_RANGE(hostSocket, DYNSKT_RANGE_START, DYNSKT_RANGE_END)) + { + if (!IN_RANGE( + hostSocket, + PARAM(CONFIG_SOCKET_RANGE_START), + PARAM(CONFIG_SOCKET_RANGE_END))) + { + return(STATUS_INVALID_ADDRESS); + } + } +#endif + + // get an address file structure to represent this address. + status = SpxAddrFileCreate(Device, Request, &pAddrFile); + if (!NT_SUCCESS(status)) + return status; + + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + CTEGetLock (&Device->dev_Lock, &LockHandle); + + // We checkfor/create sockets within the critical section. + if (Socket == 0) + { + Socket = SpxAddrAssignSocket(Device); + + if (Socket == 0) + { + DBGPRINT(ADDRESS, ERR, + ("OpenAddress, no unique socket found\n")); + + CTEFreeLock (&Device->dev_Lock, LockHandle); + ExReleaseResource (&Device->dev_AddrResource); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + } + + pAddr = SpxAddrLookup(Device, Socket); + + if (pAddr == NULL) + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // This address doesn't exist. Create it. + // registering it. It also puts a ref of type ADDR_FILE on address. + pAddr = SpxAddrCreate( + Device, + Socket); + + if (pAddr != (PSPX_ADDR)NULL) + { +#ifdef ISN_NT + + // Initialize the shared access now. We use read access + // to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess); + + + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &pAddr->sa_SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) + { + // Error, return status. + IoRemoveShareAccess (IrpSp->FileObject, &pAddr->u.sa_ShareAccess); + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + return status; + } + +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + // if the adapter isn't ready, we can't do any of this; get out +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) +#else + if (Device->dev_State == DEVICE_STATE_STOPPING) +#endif _PNP_POWER + { + SpxAddrDereference (pAddr, AREF_ADDR_FILE); + + SpxAddrFileDestroy(pAddrFile); + status = STATUS_DEVICE_NOT_READY; + } + else + { + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; + + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + status = STATUS_SUCCESS; + } + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + + // If the address could not be created, and is not in the process of + // being created, then we can't open up an address. + + SpxAddrFileDestroy(pAddrFile); + } + } + else + { + CTEFreeLock (&Device->dev_Lock, LockHandle); + + DBGPRINT(ADDRESS, ERR, + ("Add to address %lx\n", pAddr)); + + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + pAddr->sa_SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { +#ifdef ISN_NT + + // Now check that we can obtain the desired share + // access. We use read access to control all access. + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &pAddr->u.sa_ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) + { + ExReleaseResource (&Device->dev_AddrResource); + SpxAddrFileDestroy(pAddrFile); + } + else + { + ExReleaseResource (&Device->dev_AddrResource); + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEGetLock (&pAddr->sa_Lock, &LockHandleAddr); + + pAddrFile->saf_Addr = pAddr; + pAddrFile->saf_AddrLock = &pAddr->sa_Lock; +#ifdef ISN_NT + pAddrFile->saf_FileObject = IrpSp->FileObject; +#endif + // Set flags appropriately, note spx/stream flags are set at this + // point. + pAddrFile->saf_Flags &= ~SPX_ADDRFILE_OPENING; + pAddrFile->saf_Flags |= SPX_ADDRFILE_OPEN; + + SpxAddrLockReference (pAddr, AREF_ADDR_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)pAddrFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + // Queue in the address list, removed in destroy. + pAddrFile->saf_Next = pAddr->sa_AddrFileList; + pAddr->sa_AddrFileList = pAddrFile; + + CTEFreeLock (&pAddr->sa_Lock, LockHandleAddr); + CTEFreeLock (&Device->dev_Lock, LockHandle); + + status = STATUS_SUCCESS; + } + } + + // Remove the reference from SpxLookupAddress. + SpxAddrDereference (pAddr, AREF_LOOKUP); + } + + return status; + +} // SpxAddrOpen + + + + +NTSTATUS +SpxAddrSetEventHandler( + IN PDEVICE Device, + IN PREQUEST pRequest + ) +{ + CTELockHandle lockHandle; + NTSTATUS status = STATUS_SUCCESS; + + PSPX_ADDR_FILE + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); + PTDI_REQUEST_KERNEL_SET_EVENT + pParam = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(pRequest); + + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + return(status); + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + switch (pParam->EventType) + { + + case TDI_EVENT_ERROR: + + break; + + case TDI_EVENT_CONNECT: + + pSpxAddrFile->saf_ConnHandler = + (PTDI_IND_CONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_ConnHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE: + + pSpxAddrFile->saf_RecvHandler = + (PTDI_IND_RECEIVE)(pParam->EventHandler); + pSpxAddrFile->saf_RecvHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_DISCONNECT: + + pSpxAddrFile->saf_DiscHandler = + (PTDI_IND_DISCONNECT)(pParam->EventHandler); + pSpxAddrFile->saf_DiscHandlerCtx = + pParam->EventContext; + + break; + + + case TDI_EVENT_SEND_POSSIBLE : + + pSpxAddrFile->saf_SendPossibleHandler = + (PTDI_IND_SEND_POSSIBLE)(pParam->EventHandler); + pSpxAddrFile->saf_SendPossibleHandlerCtx = + pParam->EventContext; + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + case TDI_EVENT_RECEIVE_EXPEDITED: + default: + + status = STATUS_INVALID_PARAMETER; + } + + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return(status); +} + + + +PSPX_ADDR +SpxAddrCreate( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Socket - The socket to assign to this address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PSPX_ADDR pAddr; + int index; + CTELockHandle lockHandle; + + pAddr = (PSPX_ADDR)SpxAllocateZeroedMemory (sizeof(SPX_ADDR)); + if (pAddr == NULL) + { + DBGPRINT(ADDRESS, INFO, + ("Create address %lx failed\n", (ULONG)Socket)); + + return NULL; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address %lx (%lx)\n", pAddr, (ULONG)Socket)); + + pAddr->sa_Type = SPX_ADDRESS_SIGNATURE; + pAddr->sa_Size = sizeof (SPX_ADDR); + pAddr->sa_Flags = 0; + + pAddr->sa_Device = Device; + pAddr->sa_DeviceLock = &Device->dev_Lock; + CTEInitLock (&pAddr->sa_Lock); + + // This reference is for the address file that will associated with this addr. + pAddr->sa_RefCount = 1; + +#if DBG + pAddr->sa_RefTypes[AREF_ADDR_FILE] = 1; +#endif + + pAddr->sa_Socket = Socket; + + // Insert address into the device hash table. + index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + CTEGetLock (&Device->dev_Lock, &lockHandle); + pAddr->sa_Next = Device->dev_AddrHashTable[index]; + Device->dev_AddrHashTable[index] = pAddr; + CTEFreeLock (&Device->dev_Lock, lockHandle); + + SpxReferenceDevice (Device, DREF_ADDRESS); + + return pAddr; + +} // SpxAddrCreate + + + + +NTSTATUS +SpxAddrFileVerify( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a SPX_ADDR_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PSPX_ADDR Address; + + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + + try + { + if ((pAddrFile->saf_Size == sizeof (SPX_ADDR_FILE)) && + (pAddrFile->saf_Type == SPX_ADDRESSFILE_SIGNATURE) ) + { + Address = pAddrFile->saf_Addr; + + if ((Address->sa_Size == sizeof (SPX_ADDR)) && + (Address->sa_Type == SPX_ADDRESS_SIGNATURE) ) + { + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if ((Address->sa_Flags & SPX_ADDR_CLOSING) == 0) + { + SpxAddrFileLockReference(pAddrFile, AFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx closing\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: A %lx bad signature\n", Address)); + + status = STATUS_INVALID_ADDRESS; + } + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pAddrFile)); + + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxAddrFileVerify: AF %lx exception\n", Address)); + + return GetExceptionCode(); + } + + return status; + +} // SpxAddrFileVerify + + + + +VOID +SpxAddrDestroy( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by SpxDerefAddress when + the reference count goes to 0. + + This thread is only queued by SpxDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PSPX_ADDR pAddr, *ppAddr; + CTELockHandle LockHandle; + + PSPX_ADDR Address = (PSPX_ADDR)Parameter; + PDEVICE Device = Address->sa_Device; + int index = (int)(Address->sa_Socket & NUM_SPXADDR_HASH_MASK); + + DBGPRINT(ADDRESS, INFO, + ("Destroy address %lx\n", Address)); + + SeDeassignSecurity (&Address->sa_SecurityDescriptor); + + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + CTEGetLock (&Device->dev_Lock, &LockHandle); + for (ppAddr = &Device->dev_AddrHashTable[index]; (pAddr = *ppAddr) != NULL;) + { + if (pAddr == Address) + { + *ppAddr = pAddr->sa_Next; + break; + } + + ppAddr = &pAddr->sa_Next; + } + CTEFreeLock (&Device->dev_Lock, LockHandle); + + SpxFreeMemory (Address); + SpxDereferenceDevice (Device, DREF_ADDRESS); + +} + + + + +#if DBG + +VOID +SpxAddrRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} + + + + +VOID +SpxAddrLockRef( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->sa_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &Address->sa_RefCount, + 1, + Address->sa_DeviceLock); +} +#endif + + + + +VOID +SpxAddrDeref( + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &Address->sa_RefCount, + (ULONG)-1, + Address->sa_DeviceLock); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (oldvalue != 0); + + if (oldvalue == 1) + { +#if ISN_NT + ExInitializeWorkItem( + &Address->u.sa_DestroyAddrQueueItem, + SpxAddrDestroy, + (PVOID)Address); + ExQueueWorkItem(&Address->u.sa_DestroyAddrQueueItem, DelayedWorkQueue); +#else + SpxAddrDestroy(Address); +#endif + + } + +} + + + + +NTSTATUS +SpxAddrFileCreate( + IN PDEVICE Device, + IN PREQUEST Request, + OUT PSPX_ADDR_FILE * ppAddrFile + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + NTSTATUS status; + BYTE socketType; + CTELockHandle LockHandle; + PSPX_ADDR_FILE pAddrFile; + + // What is the address file type? + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) + { + return(status); + } + + pAddrFile = (PSPX_ADDR_FILE)SpxAllocateZeroedMemory (sizeof(SPX_ADDR_FILE)); + if (pAddrFile == NULL) + { + DBGPRINT(ADDRESS, ERR, + ("Create address file failed\n")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + DBGPRINT(ADDRESS, INFO, + ("Create address file %lx\n", pAddrFile)); + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + pAddrFile->saf_Type = SPX_ADDRESSFILE_SIGNATURE; + pAddrFile->saf_Size = sizeof (SPX_ADDR_FILE); + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject = NULL; +#endif + + pAddrFile->saf_Device = Device; + pAddrFile->saf_Flags = SPX_ADDRFILE_OPENING; + if ((socketType == SOCKET1_TYPE_SEQPKT) || + (socketType == SOCKET1_TYPE_STREAM)) + { + if (socketType == SOCKET1_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_SPX2; + if (socketType == SOCKET2_TYPE_STREAM) + { + pAddrFile->saf_Flags |= SPX_ADDRFILE_STREAM; + } + } + + pAddrFile->saf_RefCount = 1; + +#if DBG + pAddrFile->saf_RefTypes[AFREF_CREATE] = 1; +#endif + + pAddrFile->saf_CloseReq = (PREQUEST)NULL; + + // Initialize the request handlers. + pAddrFile->saf_ConnHandler = + pAddrFile->saf_ConnHandlerCtx = NULL; + pAddrFile->saf_DiscHandler = + pAddrFile->saf_DiscHandlerCtx = NULL; + pAddrFile->saf_RecvHandler = + pAddrFile->saf_RecvHandlerCtx = NULL; + pAddrFile->saf_ErrHandler = + pAddrFile->saf_ErrHandlerCtx = NULL; + + // Release lock + CTEFreeLock (&Device->dev_Lock, LockHandle); + + // Put in the global list for our reference + spxAddrInsertIntoGlobalList(pAddrFile); + + *ppAddrFile = pAddrFile; + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileDestroy( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by SpxAddrFileDereference. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + pAddrFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PSPX_ADDR Address; + PDEVICE Device; + PREQUEST CloseRequest; + PSPX_ADDR_FILE pRemAddr, *ppRemAddr; + + DBGPRINT(ADDRESS, INFO, + ("Destroy address file %lx\n", pAddrFile)); + + Address = pAddrFile->saf_Addr; + Device = pAddrFile->saf_Device; + + if (Address) + { + CTEGetLock (&Device->dev_Lock, &LockHandle1); + + // This addressfile was associated with an address. + CTEGetLock (&Address->sa_Lock, &LockHandle); + + // If the last reference on the address is being removed, set the + // closing flag to prevent further references. + + //if (Address->sa_RefCount == 1) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&Address->sa_RefCount, 0, &Address->sa_Lock) == 1) { + Address->sa_Flags |= SPX_ADDR_CLOSING; + } + + // Dequeue the address file from the address list. + for (ppRemAddr = &Address->sa_AddrFileList; (pRemAddr = *ppRemAddr) != NULL;) + { + if (pRemAddr == pAddrFile) + { + *ppRemAddr = pRemAddr->saf_Next; + break; + } + + ppRemAddr = &pRemAddr->saf_Next; + } + + pAddrFile->saf_Addr = NULL; + +#ifdef ISN_NT + pAddrFile->saf_FileObject->FsContext = NULL; + pAddrFile->saf_FileObject->FsContext2 = NULL; +#endif + + CTEFreeLock (&Address->sa_Lock, LockHandle); + CTEFreeLock (&Device->dev_Lock, LockHandle1); + + // We will already have been removed from the ShareAccess + // of the owning address. + // + // Now dereference the owning address. + SpxAddrDereference(Address, AREF_ADDR_FILE); + } + + // Save this for later completion. + CloseRequest = pAddrFile->saf_CloseReq; + + // Remove from the global list + spxAddrRemoveFromGlobalList(pAddrFile); + + // return the addressFile to the pool of address files + SpxFreeMemory (pAddrFile); + + if (CloseRequest != (PREQUEST)NULL) + { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + SpxCompleteRequest (CloseRequest); + SpxFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} + + + + +#if DBG + +VOID +SpxAddrFileRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} // SpxRefAddressFile + + + + +VOID +SpxAddrFileLockRef( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pAddrFile->saf_RefCount > 0); // not perfect, but... + (VOID)SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + 1, + pAddrFile->saf_AddrLock); + +} +#endif + + + + +VOID +SpxAddrFileDeref( + IN PSPX_ADDR_FILE pAddrFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyAddressFile to remove it from the system. + +Arguments: + + pAddrFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + + oldvalue = SPX_ADD_ULONG ( + &pAddrFile->saf_RefCount, + (ULONG)-1, + pAddrFile->saf_AddrLock); + + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + CTEAssert (oldvalue > 0); + + if (oldvalue == 1) + { + SpxAddrFileDestroy(pAddrFile); + } + +} + + + + +PSPX_ADDR +SpxAddrLookup( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + SpxAddrLockReference(Address, AREF_LOOKUP); + return Address; + + } + } + + // The specified address was not found. + return NULL; + +} + + + + +BOOLEAN +SpxAddrExists( + IN PDEVICE Device, + IN USHORT Socket + ) + +/*++ + +Routine Description: + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + Socket - The socket to look up. + +Return Value: + + TRUE if so, else FALSE + +--*/ + +{ + PSPX_ADDR Address; + int index = (int)(Socket & NUM_SPXADDR_HASH_MASK); + + for (Address = Device->dev_AddrHashTable[index]; + Address != NULL; + Address = Address->sa_Next) + { + if ((Address->sa_Flags & SPX_ADDR_CLOSING) != 0) + { + continue; + } + + if (Address->sa_Socket == Socket) + { + // We found the match + return TRUE; + } + } + + // The specified address was not found. + return FALSE; + +} // SpxAddrExists + + + + +NTSTATUS +SpxAddrConnByRemoteIdAddrLock( + IN PSPX_ADDR pSpxAddr, + IN USHORT SrcConnId, + IN PBYTE SrcIpxAddr, + OUT PSPX_CONN_FILE *ppSpxConnFile + ) +{ + PSPX_CONN_FILE pSpxConnFile; + NTSTATUS status = STATUS_INVALID_CONNECTION; + + for (pSpxConnFile = pSpxAddr->sa_ActiveConnList; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_Next) + { + if ((pSpxConnFile->scf_RemConnId == SrcConnId) && + (*((UNALIGNED ULONG *)SrcIpxAddr) == + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+4) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)) && + (*(UNALIGNED ULONG *)(SrcIpxAddr+8) == + *(UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+8))) + { + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + *ppSpxConnFile = pSpxConnFile; + status = STATUS_SUCCESS; + break; + } + } + + return(status); +} + + + + +NTSTATUS +SpxAddrFileStop( + IN PSPX_ADDR_FILE pAddrFile, + IN PSPX_ADDR Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an pAddrFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + pAddrFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile, pSpxConnFileNext; + CTELockHandle LockHandle; + + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileStop: %lx\n", pAddrFile)); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + + if (pAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING) + { + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + } + + pAddrFile->saf_Flags |= SPX_ADDRFILE_CLOSING; + + pSpxConnFileNext = NULL; + if (pSpxConnFile = pAddrFile->saf_AssocConnList) + { + pSpxConnFileNext = pSpxConnFile; + SpxConnFileReference(pSpxConnFile, CFREF_ADDR); + } + + while (pSpxConnFile) + { + if (pSpxConnFileNext = pSpxConnFile->scf_AssocNext) + { + SpxConnFileReference(pSpxConnFileNext, CFREF_ADDR); + } + CTEFreeLock (&Address->sa_Lock, LockHandle); + + + DBGPRINT(CREATE, INFO, + ("SpxAddrFileClose: Assoc conn stop %lx when %lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + SpxConnStop(pSpxConnFile); + SpxConnFileDereference(pSpxConnFile, CFREF_ADDR); + + CTEGetLock (&Address->sa_Lock, &LockHandle); + pSpxConnFile = pSpxConnFileNext; + } + + CTEFreeLock (&Address->sa_Lock, LockHandle); + return STATUS_SUCCESS; + +} + + + + +NTSTATUS +SpxAddrFileCleanup( + IN PDEVICE Device, + IN PREQUEST Request + ) +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, INFO, + ("SpxAddrFileCleanup: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + if (!NT_SUCCESS (status)) + { + return(status); + } + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + SpxAddrFileStop(pSpxAddrFile, Address); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_SUCCESS; +} + + + + +NTSTATUS +SpxAddrFileClose( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PSPX_ADDR Address; + PSPX_ADDR_FILE pSpxAddrFile; + NTSTATUS status; + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + + DBGPRINT(ADDRESS, DBG, + ("SpxAddrFileClose: %lx\n", pSpxAddrFile)); + + status = SpxAddrFileVerify(pSpxAddrFile); + + if (!NT_SUCCESS (status)) + { + return(status); + } + + pSpxAddrFile->saf_CloseReq = Request; + + // We assume that addressFile has already been verified + // at this point. + Address = pSpxAddrFile->saf_Addr; + CTEAssert (Address); + + // Remove us from the access info for this address. + ExAcquireResourceExclusive (&Device->dev_AddrResource, TRUE); + +#ifdef ISN_NT + IoRemoveShareAccess (pSpxAddrFile->saf_FileObject, &Address->u.sa_ShareAccess); +#endif + + ExReleaseResource (&Device->dev_AddrResource); + + + SpxAddrFileDereference (pSpxAddrFile, AFREF_CREATE); + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + return STATUS_PENDING; + +} // SpxCloseAddressFile + + + + +USHORT +SpxAddrAssignSocket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine assigns a socket that is unique within a range + of SocketUniqueness. + +Arguments: + + Device - Pointer to the device context. + +Return Value: + + The assigned socket number, or 0 if a unique one cannot + be found. + +--*/ + +{ + BOOLEAN wrapped = FALSE; + USHORT temp, Socket; + + // We have to auto-assign a socket. + temp = Device->dev_CurrentSocket; + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + while (TRUE) + { + Device->dev_CurrentSocket += (USHORT)PARAM(CONFIG_SOCKET_UNIQUENESS); + if (Device->dev_CurrentSocket > PARAM(CONFIG_SOCKET_RANGE_END)) + { + Device->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + wrapped = TRUE; + } + + if (!SpxAddrExists (Device, Socket)) + { + break; + } + + PUTSHORT2SHORT( + &Socket, + Device->dev_CurrentSocket); + + if (wrapped && (Device->dev_CurrentSocket >= temp)) + { + // If we have checked all possible values given SOCKET_UNIQUENESS... + // This may actually return ERROR even if there are + // available socket numbers although they may be + // implicitly in use due to SOCKET_UNIQUENESS being + // > 1. That is the way it is to work. + + Socket = 0; + break; + } + } + + DBGPRINT(ADDRESS, INFO, + ("OpenAddress, assigned socket %lx\n", Socket)); + + return(Socket); +} + + + + +VOID +spxAddrInsertIntoGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxAddrFile->saf_GlobalNext = SpxGlobalAddrList; + SpxGlobalAddrList = pSpxAddrFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxAddrRemoveFromGlobalList( + IN PSPX_ADDR_FILE pSpxAddrFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_ADDR_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalAddrList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxAddrFile) + { + DBGPRINT(SEND, INFO, + ("SpxAddrRemoveFromGlobal: %lx\n", pSpxAddrFile)); + + // Remove from list + *ppC = pC->saf_GlobalNext; + break; + } + + ppC = &pC->saf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_ADDRESS; + + return(status); +} + + + + diff --git a/private/ntos/tdi/isnp/spx/spxbind.c b/private/ntos/tdi/isnp/spx/spxbind.c new file mode 100644 index 000000000..7610939f7 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxbind.c @@ -0,0 +1,600 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxbind.c + +Abstract: + + This module contains the code to bind to the IPX transport, as well as the + indication routines for the IPX transport not including the send/recv ones. + +Author: + + Stefan Solomon (stefans) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXBIND + +VOID +SpxStatus ( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry); + +VOID +SpxLineDown ( + IN USHORT NicId); + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData); + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// globals and externs +// +extern CTELock spxTimerLock; +extern LARGE_INTEGER spxTimerTick; +extern KTIMER spxTimer; +extern KDPC spxTimerDpc; +extern BOOLEAN spxTimerStopped; +#endif _PNP_POWER + +NTSTATUS +SpxInitBindToIpx( + VOID + ) + +{ + NTSTATUS status; + IO_STATUS_BLOCK ioStatusBlock; + OBJECT_ATTRIBUTES objectAttr; + PIPX_INTERNAL_BIND_INPUT pBindInput; + PIPX_INTERNAL_BIND_OUTPUT pBindOutput; + + InitializeObjectAttributes( + &objectAttr, + &IpxDeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = NtCreateFile( + &IpxHandle, + SYNCHRONIZE | GENERIC_READ, + &objectAttr, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(status)) { + return status; + } + + if ((pBindInput = CTEAllocMem(sizeof(IPX_INTERNAL_BIND_INPUT))) == NULL) { + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Fill in our bind data +#if defined(_PNP_POWER) + pBindInput->Version = ISN_VERSION; +#else + pBindInput->Version = 1; +#endif _PNP_POWER + pBindInput->Identifier = IDENTIFIER_SPX; + pBindInput->BroadcastEnable = FALSE; + pBindInput->LookaheadRequired = IPX_HDRSIZE; + pBindInput->ProtocolOptions = 0; + pBindInput->ReceiveHandler = SpxReceive; + pBindInput->ReceiveCompleteHandler = SpxReceiveComplete; + pBindInput->StatusHandler = SpxStatus; + pBindInput->SendCompleteHandler = SpxSendComplete; + pBindInput->TransferDataCompleteHandler = SpxTransferDataComplete; + pBindInput->FindRouteCompleteHandler = SpxFindRouteComplete; + pBindInput->LineUpHandler = SpxLineUp; + pBindInput->LineDownHandler = SpxLineDown; + pBindInput->ScheduleRouteHandler = SpxScheduleRoute; +#if defined(_PNP_POWER) + pBindInput->PnPHandler = SpxPnPNotification; +#endif _PNP_POWER + + + // First get the length for the output buffer. + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + NULL, // Output Buffer + 0); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status != STATUS_BUFFER_TOO_SMALL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INVALID_PARAMETER); + } + + if ((pBindOutput = CTEAllocMem(ioStatusBlock.Information)) == NULL) { + CTEFreeMem(pBindInput); + NtClose(IpxHandle); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + status = NtDeviceIoControlFile( + IpxHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + pBindInput, // Input Buffer + sizeof(IPX_INTERNAL_BIND_INPUT), // Input Buffer Length + pBindOutput, // Output Buffer + ioStatusBlock.Information); + + if (status == STATUS_PENDING) { + status = NtWaitForSingleObject( + IpxHandle, + (BOOLEAN)FALSE, + NULL); + } + + if (status == STATUS_SUCCESS) { + + // Get all the info from the bind output buffer and save in + // appropriate places. + IpxLineInfo = pBindOutput->LineInfo; + IpxMacHdrNeeded = pBindOutput->MacHeaderNeeded; + IpxInclHdrOffset = pBindOutput->IncludedHeaderOffset; + + IpxSendPacket = pBindOutput->SendHandler; + IpxFindRoute = pBindOutput->FindRouteHandler; + IpxQuery = pBindOutput->QueryHandler; + IpxTransferData = pBindOutput->TransferDataHandler; + +#if !defined(_PNP_POWER) + // Copy over the network node info. + RtlCopyMemory( + SpxDevice->dev_Network, + pBindOutput->Network, + IPX_NET_LEN); + + RtlCopyMemory( + SpxDevice->dev_Node, + pBindOutput->Node, + IPX_NODE_LEN); + + + DBGPRINT(TDI, INFO, + ("SpxInitBindToIpx: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // Find out how many adapters IPX has, if this fails + // just assume one. + // + + if ((*IpxQuery)( + IPX_QUERY_MAXIMUM_NIC_ID, + 0, + &SpxDevice->dev_Adapters, + sizeof(USHORT), + NULL) != STATUS_SUCCESS) { + + SpxDevice->dev_Adapters = 1; + + } +#endif !_PNP_POWER + } else { + + NtClose(IpxHandle); + status = STATUS_INVALID_PARAMETER; + } + CTEFreeMem(pBindInput); + CTEFreeMem(pBindOutput); + + return status; +} + + + + +VOID +SpxUnbindFromIpx( + VOID + ) + +{ + NtClose(IpxHandle); + return; +} + + + + +VOID +SpxStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxStatus: CALLED WITH %lx\n", + GeneralStatus)); + + return; +} + + + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +{ + CTELockHandle lockHandle; + PSPX_FIND_ROUTE_REQUEST pSpxFrReq = (PSPX_FIND_ROUTE_REQUEST)FindRouteRequest; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)pSpxFrReq->fr_Ctx; + + // This will be on a connection. Grab the lock, check the state and go from + // there. + if (pSpxConnFile == NULL) + { + // Should this ever happen? + KeBugCheck(0); + return; + } + + // Check the state. The called routines release the lock, remove the reference. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + // We are doing an active connect! + SpxConnConnectFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + else // For all others call active + { + SpxConnActiveFindRouteComplete( + pSpxConnFile, + pSpxFrReq, + FoundRoute, + lockHandle); + } + + // Free the find route request. + SpxFreeMemory(pSpxFrReq); + + return; +} + + + + +VOID +SpxLineUp ( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + +{ + // With PnP, our local address is changed when we get PnP + // notification. +#if !defined(_PNP_POWER) + + // + // If we get a line up for NicId 0, it means our local + // network number has changed, re-query from IPX. + // + + if (NicId == 0) { + + TDI_ADDRESS_IPX IpxAddress; + + if ((*IpxQuery)( + IPX_QUERY_IPX_ADDRESS, + 0, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + RtlCopyMemory( + SpxDevice->dev_Network, + &IpxAddress.NetworkAddress, + IPX_NET_LEN); + + DBGPRINT(TDI, INFO, + ("SpxLineUp: Ipx Net %lx\n", + *(UNALIGNED ULONG *)SpxDevice->dev_Network)); + + // + // The node shouldn't change! + // + + if (!RtlEqualMemory( + SpxDevice->dev_Node, + IpxAddress.NodeAddress, + IPX_NODE_LEN)) { + + DBGPRINT(TDI, ERR, + ("SpxLineUp: Node address has changed\n")); + } + } + + } else { + + DBGPRINT(RECEIVE, ERR, + ("SpxLineUp: CALLED WITH %lx\n", + NicId)); + } + + return; +#endif !_PNP_POWER + +} + + + + +VOID +SpxLineDown ( + IN USHORT NicId + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxLineDown: CALLED WITH %lx\n", + NicId)); + + return; +} + + + + +VOID +SpxScheduleRoute ( + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +{ + DBGPRINT(RECEIVE, ERR, + ("SpxScheduleRoute: CALLED WITH %lx\n", + RouteEntry)); + + return; +} + +#if defined(_PNP_POWER) +VOID +SpxPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + NTSTATUS Status; + PDEVICE Device = SpxDevice; + UNICODE_STRING UnicodeDeviceName; + + DBGPRINT(DEVICE, DBG,("Received a pnp notification, opcode %d\n",OpCode)); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + CTELockHandle TimerLockHandle; + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->dev_State != DEVICE_STATE_OPEN ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + // + // Start the timer. It is possible that the timer + // was still running or we are still in the timer dpc + // from the previous ADD_DEVICE - DELETE_DEVICE execution + // cycle. But it is ok simply restart this, because + // KeSetTimer implicitly cancels the previous Dpc. + // + + CTEGetLock(&spxTimerLock, &TimerLockHandle); + spxTimerStopped = FALSE; + CTEFreeLock(&spxTimerLock, TimerLockHandle); + KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + + Device->dev_State = DEVICE_STATE_OPEN; + + + CTEAssert( !Device->dev_Adapters ); + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + IpxLineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumPacketSize; + // set the provider info + SpxDevice->dev_ProviderInfo.MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + }else { + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + + } + + Device->dev_Adapters++; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->dev_DeviceName; + UnicodeDeviceName.MaximumLength = Device->dev_DeviceNameLen; + UnicodeDeviceName.Length = Device->dev_DeviceNameLen - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to register Spx Device with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + CTEAssert( Device->dev_Adapters ); + Device->dev_Adapters--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->dev_State = DEVICE_STATE_LOADED; + Device->dev_Adapters = 0; + } + + IpxLineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + + if ( PnPInfo->FirstORLastDevice ) { + SpxTimerFlushAndStop(); + // + // inform tdi clients about the device deletion + // + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->dev_TdiRegistrationHandle ) )) { + DBGPRINT(TDI,ERR, ("Failed to Deregister Spx Device with TDI\n")); + } + } + // + // TBD: call ExNotifyCallback + // + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + CTEAssert( PnPInfo->NewReservedAddress ); + + *(UNALIGNED ULONG *)Device->dev_Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->dev_Node, PnPInfo->NodeAddress, 6); + + CTEFreeLock ( &Device->dev_Lock, LockHandle ); + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/spx/spxconn.c b/private/ntos/tdi/isnp/spx/spxconn.c new file mode 100644 index 000000000..4528b64a4 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxconn.c @@ -0,0 +1,3862 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxconn.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, SpxConnOpen) +#pragma alloc_text(PAGE, SpxConnCleanup) +#pragma alloc_text(PAGE, SpxConnClose) +#endif + +// Define module number for event logging entries +#define FILENUM SPXCONN + +VOID +SpxFindRouteComplete ( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute); + + +NTSTATUS +SpxConnOpen( + IN PDEVICE pDevice, + IN CONNECTION_CONTEXT ConnCtx, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + This routine is used to create a connection object and associate the + passed ConnectionContext with it. + +Arguments: + + pConnCtx - The TDI ConnectionContext to be associated with object + +Return Value: + + STATUS_SUCCESS if connection was successfully opened + Error otherwise. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PSPX_CONN_FILE pSpxConnFile; + +#ifdef ISN_NT + PIRP Irp = (PIRP)pRequest; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + + // Allocate memory for a connection object + if ((pSpxConnFile = SpxAllocateZeroedMemory(sizeof(SPX_CONN_FILE))) == NULL) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // Initialize values + pSpxConnFile->scf_Flags = 0; + pSpxConnFile->scf_Type = SPX_CONNFILE_SIGNATURE; + pSpxConnFile->scf_Size = sizeof (SPX_CONN_FILE); + + CTEInitLock (&pSpxConnFile->scf_Lock); + + pSpxConnFile->scf_ConnCtx = ConnCtx; + pSpxConnFile->scf_Device = pDevice; + + // Initialize list for requests. + InitializeListHead(&pSpxConnFile->scf_ReqLinkage); + InitializeListHead(&pSpxConnFile->scf_RecvLinkage); + InitializeListHead(&pSpxConnFile->scf_RecvDoneLinkage); + InitializeListHead(&pSpxConnFile->scf_ReqDoneLinkage); + InitializeListHead(&pSpxConnFile->scf_DiscLinkage); + +#ifdef ISN_NT + // easy backlink to file object. + pSpxConnFile->scf_FileObject = IrpSp->FileObject; +#endif + + // For connections we go from 0->0 with flags indicating if a close + // happened. + pSpxConnFile->scf_RefCount = 0; + + // Insert into a global connection list. + spxConnInsertIntoGlobalList(pSpxConnFile); + +#if DBG + + // Initialize this to 0xFFFF so we dont hit assert on first packet. + pSpxConnFile->scf_PktSeqNum = 0xFFFF; + +#endif + + // Set values in the request. + REQUEST_OPEN_CONTEXT(pRequest) = (PVOID)pSpxConnFile; + REQUEST_OPEN_TYPE(pRequest) = (PVOID)TDI_CONNECTION_FILE; + + DBGPRINT(CREATE, INFO, + ("SpxConnOpen: Opened %lx\n", pSpxConnFile)); + + ASSERT(status == STATUS_SUCCESS); + return(status); +} + + + + +NTSTATUS +SpxConnCleanup( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real connection + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + DBGBRK(FATAL); + return (status); + } + + DBGPRINT(CREATE, INFO, + ("SpxConnFileCleanup: %lx.%lx when %lx\n", + pSpxConnFile, Request, pSpxConnFile->scf_RefCount)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + pSpxConnFile->scf_CleanupReq = Request; + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // We have a reference, so it wont go to zero until stop returns. Therefore + // deref can expect flag to be set. + SpxConnStop(pSpxConnFile); + SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); + + // + // If this is a connection which is waiting for a local disconnect, + // deref it since we dont expect a disconnect after a cleanup. + // + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { + + CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + KdPrint(("Deref for DISCWAIT on connfile: %lx\n", pSpxConnFile)); + + SpxConnFileDereference (pSpxConnFile, CFREF_DISCWAITSPX); + } else { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + + return STATUS_PENDING; +} + + + + +NTSTATUS +SpxConnClose( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real connection + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + DBGBRK(FATAL); + return (status); + } + + DBGPRINT(CREATE, INFO, + ("SpxConnFileClose: %lx when %lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + pSpxConnFile->scf_CloseReq = Request; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + SpxConnFileDereference (pSpxConnFile, CFREF_VERIFY); + return STATUS_PENDING; +} + + + + +VOID +SpxConnStop( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + !!!Connection must have a reference when this is called!!! + +Arguments: + + +Return Value: + + +--*/ +{ + CTELockHandle lockHandle; + + DBGPRINT(CREATE, INFO, + ("SpxConnFileStop: %lx when %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RefCount, + pSpxConnFile->scf_Flags)); + + // Call disconnect and disassociate + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandle, + FALSE); // [SA] Bug #15249 + + } + else + { + // Disassociate if we are associated. + spxConnDisAssoc(pSpxConnFile, lockHandle); + } + + // Lock released at this point. + } + else + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + return; +} + + + + +NTSTATUS +SpxConnAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + This routine moves the connection from the device list to the inactive + connection list in the address of the address file specified. The address + file is pointed to by the connection and is referenced for the associate. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR_FILE pSpxAddrFile; + CTELockHandle lockHandle1, lockHandle2; + + BOOLEAN derefAddr = FALSE, derefConn = FALSE; + PFILE_OBJECT pFileObj = NULL; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + HANDLE AddrObjHandle = + ((PTDI_REQUEST_KERNEL_ASSOCIATE)(REQUEST_PARAMETERS(pRequest)))->AddressHandle; + + do + { + // Get the handle to the address object from the irp and map it to + // the corres. file object. + status = ObReferenceObjectByHandle( + AddrObjHandle, + 0, + 0, + KernelMode, + (PVOID *)&pFileObj, + NULL); + + if (!NT_SUCCESS(status)) + break; + + pSpxAddrFile = pFileObj->FsContext; + ASSERT(pFileObj->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + // Verify address file/connection file + if ((status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + break; + + derefAddr = TRUE; + + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + break; + + derefConn = TRUE; + + // Grab the addres file lock, then the connection lock for associate. + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle1); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + if (!SPX_CONN_FLAG(pSpxConnFile, (SPX_CONNFILE_CLOSING | + SPX_CONNFILE_STOPPING | + SPX_CONNFILE_ASSOC)) + && + !(pSpxAddrFile->saf_Flags & SPX_ADDRFILE_CLOSING)) + { + derefAddr = FALSE; + SpxAddrFileTransferReference( + pSpxAddrFile, AFREF_VERIFY, AFREF_CONN_ASSOC); + + // Queue in the inactive list in the address + pSpxConnFile->scf_Next = pSpxAddrFile->saf_Addr->sa_InactiveConnList; + pSpxAddrFile->saf_Addr->sa_InactiveConnList = pSpxConnFile; + + // Queue in the assoc list in the address file + pSpxConnFile->scf_AssocNext = pSpxAddrFile->saf_AssocConnList; + pSpxAddrFile->saf_AssocConnList = pSpxConnFile; + + // Remember the addrfile in the connection + pSpxConnFile->scf_AddrFile = pSpxAddrFile; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); + + status = STATUS_SUCCESS; + + DBGPRINT(CREATE, INFO, + ("SpxConnAssociate: %lx with address file %lx\n", + pSpxConnFile, pSpxAddrFile)); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandle2); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandle1); + + // Dereference the file object corres. to the address object + ObDereferenceObject(pFileObj); + + } while (FALSE); + + if (derefAddr) + { + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + } + + if (derefConn) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnDisAssociate( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Verify connection file + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + return (status); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_IDLE(pSpxConnFile) + || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // Unlink it if ok. + if (NT_SUCCESS(status)) + { + SpxConnStop(pSpxConnFile); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +spxConnDisAssoc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + CTELockHandle lockHandleAddr; + PSPX_ADDR_FILE pSpxAddrFile; + + if (SPX_CONN_IDLE(pSpxConnFile) + && + (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + } + else + { + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Unlink it if ok. + if (NT_SUCCESS(status)) + { + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + // Check again as we had released the lock + if (SPX_CONN_IDLE(pSpxConnFile) + && + (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC))) + { + pSpxConnFile->scf_AddrFile = NULL; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ASSOC); + + // Dequeue the connection from the address file + spxConnRemoveFromAssocList( + &pSpxAddrFile->saf_AssocConnList, + pSpxConnFile); + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + spxConnRemoveFromList( + &pSpxAddrFile->saf_Addr->sa_InactiveConnList, + pSpxConnFile); + } + else + { + status = STATUS_INVALID_CONNECTION; + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + + DBGPRINT(CREATE, INFO, + ("SpxConnDisAssociate: %lx from address file %lx\n", + pSpxConnFile, pSpxAddrFile)); + + if (NT_SUCCESS(status)) + { + // Remove reference on address for this association. + SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); + } + } + + return(status); +} + + + + +NTSTATUS +SpxConnConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + BUGBUG: + We need to have another timer that will be started on the connection + if the tdi client indicated a timeout value. 0 -> we do not start such + a timer, -1 implies, we let our connection timeout values do their thing. + Any other value will forcibly shutdown the connect process, when the timer + fires. + +Return Value: + + +--*/ + +{ + PTDI_REQUEST_KERNEL_CONNECT pParam; + TDI_ADDRESS_IPX UNALIGNED * pTdiAddr; + PNDIS_PACKET pCrPkt; + NTSTATUS status; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + PSPX_ADDR pSpxAddr; + BOOLEAN locksHeld = TRUE; + + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Unpack the connect parameters + pParam = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(pRequest); + pTdiAddr= SpxParseTdiAddress( + pParam->RequestConnectionInformation->RemoteAddress); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: Remote SOCKET %lx on %lx.%lx\n", + pTdiAddr->Socket, + pSpxConnFile, + pRequest)); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + do + { + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // Check if connection is associated, if so, the association cannot + // go away until the reference above is removed. So we are safe in + // releasing the lock. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + status = STATUS_INVALID_ADDRESS; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if ((PARAM(CONFIG_DISABLE_SPX2) == 0) && + (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2)) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: SPX2 requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: SOCK_STREAM requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + + } while (FALSE); + + if (!NT_SUCCESS(status)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: Failed %lx\n", status)); + + if (pFindRouteReq) + { + SpxFreeMemory(pFindRouteReq); + } + + return(status); + } + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(&pSpxAddr->sa_Lock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_IDLE(pSpxConnFile) && + ((pSpxConnFile->scf_LocalConnId = spxConnGetId()) != 0)) + { + // + // If this was a post-inactivated file, clear the disconnect flags + // + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + + SPX_DISC_SETSTATE(pSpxConnFile, 0); + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_CONNECTING); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) || + ((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE)) + { + PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE; + } + + pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1); + + // Move connection from inactive list to non-inactive list. + if (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile))) + { + // This should never happen! + KeBugCheck(0); + } + + // Put connection in the non-inactive list. Connection id must be set. + SPX_INSERT_ADDR_ACTIVE( + pSpxAddr, + pSpxConnFile); + + // Insert in the global connection tree on device + spxConnInsertIntoGlobalActiveList( + pSpxConnFile); + + // Store the remote address in the connection. + // !!NOTE!! We get both the network/socket in network form. + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) = + *((UNALIGNED ULONG *)(&pTdiAddr->NetworkAddress)); + + RtlCopyMemory( + pSpxConnFile->scf_RemAddr+4, + pTdiAddr->NodeAddress, + 6); + + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)) = + *((UNALIGNED USHORT *)(&pTdiAddr->Socket)); + + // Ok, we are all set, build connect packet, queue it into connection + // with the connect request. Ndis buffer already describes this memory + // Build IPX header. + + pCrPkt = NULL; // so it knows to allocate one. + + SpxPktBuildCr( + pSpxConnFile, + pSpxAddr, + &pCrPkt, + SPX_SENDPKT_IDLE, + SPX2_CONN(pSpxConnFile)); + + if (pCrPkt != NULL) + { + // Remember the request in the connection + // + // Dont queue for the failure case since we complete it in SpxInternalDispatch. + // + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + SpxConnQueueSendPktTail(pSpxConnFile, pCrPkt); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pIpxSpxHdr->hdr_DestNode, 6) ; + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNode); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= + *((UNALIGNED USHORT *)(pIpxSpxHdr->hdr_DestNode+4)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: NETWORK %lx\n", + *((UNALIGNED ULONG *)pIpxSpxHdr->hdr_DestNet))); + + DBGPRINT(CONNECT, DBG, + ("SpxConnConnect: NODE %02x-%02x-%02x-%02x-%02x-%02x\n", + pFindRouteReq->fr_FindRouteReq.Node[0], pFindRouteReq->fr_FindRouteReq.Node[1], + pFindRouteReq->fr_FindRouteReq.Node[2], pFindRouteReq->fr_FindRouteReq.Node[3], + pFindRouteReq->fr_FindRouteReq.Node[4], pFindRouteReq->fr_FindRouteReq.Node[5])); + + pFindRouteReq->fr_FindRouteReq.Identifier = IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // We wont force a rip for every connection. Only if its not + // in the IPX database. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // Reference for the find route. So that abort connect wont + // free up the connection until we return from here. + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + status = STATUS_PENDING; + } + else + { + // Abort connect attempt. + spxConnAbortConnect( + pSpxConnFile, + status, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + CTEAssert(pSpxConnFile->scf_ConnectReq == NULL); + + locksHeld = FALSE; + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (NT_SUCCESS(status)) + { + // Start off the find route request, We send the packet in completion. + // The verify reference is kept until the connect request completes. + // If connecting to network 0 we don't do this, proceed to find + // route completion which will send the request on very card. + + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + + SpxFindRouteComplete( + &pFindRouteReq->fr_FindRouteReq, + TRUE); + + } else { + + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + } + else + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: Failed %lx\n", status)); + + SpxFreeMemory(pFindRouteReq); + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnListen( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + We assume the connection passed in is already associated with an address. + If it is not, we will die! Is that ok? + +Return Value: + + +--*/ + +{ + NTSTATUS status; + CTELockHandle lockHandle1, lockHandle2; + PSPX_ADDR pSpxAddr; + + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + // Check if connection is associated, if so, the association cannot + // go away until the reference above is removed. So we are safe in + // releasing the lock. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + status = STATUS_INVALID_ADDRESS; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_SPX2) + { + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnConnect: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxConnFile->scf_AddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); + + if (NT_SUCCESS(status)) + { + CTEGetLock(&pSpxAddr->sa_Lock, &lockHandle1); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle2); + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_IDLE(pSpxConnFile)) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING); + + // Move connection from inactive list to listening list. + if (NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile))) + { + // Put connection in the listening list. + SPX_INSERT_ADDR_LISTEN(pSpxAddr, pSpxConnFile); + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + } + else + { + // This should never happen! + KeBugCheck(0); + } + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle2); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle1); + } + + + if (!NT_SUCCESS(status)) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnAccept( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_ADDR pSpxAddr; + NTSTATUS status; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + DBGPRINT(CONNECT, DBG, + ("SpxConnAccept: %lx\n", pSpxConnFile)); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return (status); + } + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + status = STATUS_SUCCESS; + pSpxAddr = pSpxConnFile->scf_AddrFile->saf_Addr; + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + + if (NT_SUCCESS(status)) + { + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + status = STATUS_INVALID_CONNECTION; + if ((SPX_CONN_LISTENING(pSpxConnFile)) && + (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_RECDREQ)) + { + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + // Call acceptcr now. + spxConnAcceptCr( + pSpxConnFile, + pSpxAddr, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + DBGPRINT(CONNECT, DBG, + ("SpxConnAccept: Accepted\n")); + + status = STATUS_PENDING; + } + else + { + // Free all locks. + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + } + + // Remove reference. Note: Listen reference will exist if ok. And that will + // be transferred to the fact that the connection is active when accepted. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +SpxConnDisconnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + If active, we do the following. + If informative disconnect, just remember the request in the connection. + We do not ref for request. Assume it will always be checked for when + changing from disconnect to idle. + +Arguments: + + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + NTSTATUS status; + CTELockHandle lockHandleConn; + BOOLEAN lockHeld; + SPX_SENDREQ_TYPE reqType; + int numDerefs = 0; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + pParam = (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + // Deref unless the disc request gets queued in as a send request. + numDerefs++; + + DBGPRINT(CONNECT, DBG, + ("spxConnDisconnect: %lx On %lx when %lx.%lx %lx Params %lx\n", + pRequest, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile), + SPX_DISC_STATE(pSpxConnFile), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC), + pParam->RequestFlags)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnDisconnect: %lx\n", pSpxConnFile)); + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + switch (pParam->RequestFlags) + { + case TDI_DISCONNECT_WAIT: + + // If informative disconnect, just remember in the connection. + status = STATUS_INVALID_CONNECTION; + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + } + + break; + + case TDI_DISCONNECT_ABORT: + case TDI_DISCONNECT_RELEASE: + + // NOTE! We don't honor the async disconnect symantics of tdi + // but map them to an abortive disconnect. + // NOTE! If our send list is not empty but our client tries to + // do a orderly release, we just queue the ord rel as a send + // data request. In process ack, we check for the next packet + // to not be a ord rel before giving up on window closure. + // NOTE! For spx1 connection, map TDI_DISCONNECT_RELEASE to + // TDI_DISCONNECT_ABORT (Informed disconnect) + + if (!SPX2_CONN(pSpxConnFile)) + { + pParam->RequestFlags = TDI_DISCONNECT_ABORT; + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + + // Since we are not a timer disconnect, then we need to keep + // retrying the disconnect packet. Change state to DISCONN if this + // is not an orderly release or we previously received an orderly + // release and are now confirming it. + // Retry timer will now keep sending out the disconnect packet. + + reqType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_ORDREL); + reqType = SPX_REQ_ORDREL; + } + else + { + // Abortive disconnect + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_POST_IDISC); + numDerefs++; + + spxConnAbortSends( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + // Abort all receives if we are informed disconnect. + spxConnAbortRecvs( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + lockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + // Since we released the lock, a remote IDISC could have come + // in in which case we really don't want to queue in the disc + // request. Instead, we set it as the disc request in the + // connection if one is not already there. + if (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_IDISC) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDisconnect: DISC not POST! %lx.%lx\n", + pSpxConnFile, SPX_DISC_STATE(pSpxConnFile))); + + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + break; + } + } + + // !NOTE + // AbortSends might leave send requests around as packets might + // have been with ipx at the time. That is why SendComplete should + // never call AbortSends but must call AbortPkt else it may complete + // the following disconnect request prematurely. + + // Creation reference for request. + REQUEST_INFORMATION(pRequest) = 1; + + // If we have no current requests, queue it in and + // set it to be the current request, else just queue it in. + // There may be other pending requests in queue. + if (pSpxConnFile->scf_ReqPkt == NULL) + { + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = reqType; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + + // Do not deref the connection, it is taken by the pending request + numDerefs--; + + // We packetize only upto the window we have. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandleConn); + + lockHeld = FALSE; + } + + status = STATUS_PENDING; + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + SPX_CALL_TDILEVEL, + lockHandleConn, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + status = STATUS_SUCCESS; + break; + + case SPX_CONNFILE_DISCONN: + + // When we queue in a disconnect as a send request, we expect + // to be able to set it into the scf_DiscReq when it is done. + // So we don't use scf_DiscReq here. This will be a problem if + // the client has a InformDiscReq pending, and a remote disconnect + // comes in, *and* the client then does a disc. We will be completing + // the request with STATUS_INVALID_CONNECTION. + status = STATUS_INVALID_CONNECTION; + if (pParam->RequestFlags != TDI_DISCONNECT_RELEASE) + { + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + status = STATUS_PENDING; + + // + // If this is a disconnect for a connection which was already + // disconnected (but AFD's disconnect handler was not called + // because the connfile could not be placed in the inactive list), + // set this flag so that the disconnect is not called from + // ConnInactivate now that the disconnect has occured here. + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } + + // + // If this was an SPXI connection where we indicated TDI_DISCONNECT_RELEASE + // to AFD, the ref count was bumped up to indicate a wait for local disconnect + // from AFD. Now that we have this disconnect, deref the connection file. Now + // we are ready to truly inactivate this connection file. + // + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT)) { + + CTEAssert( (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)); + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX]); + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + lockHeld = FALSE; + + SpxConnFileDereference(pSpxConnFile, CFREF_DISCWAITSPX); + } + } + + break; + + default: + + // Should never happen! + status = STATUS_INVALID_CONNECTION; + } + + break; + + default: + + status = STATUS_INVALID_PARAMETER; + break; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + DBGPRINT(CONNECT, INFO, + ("SpxConnDisconnect: returning for %lx.%lx\n", pSpxConnFile, status)); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnSend( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_SEND pParam; + NTSTATUS status; + CTELockHandle lockHandleConn; + BOOLEAN lockHeld; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + DBGPRINT(SEND, DBG, + ("SpxConnSend: %lx.%lx.%lx.%lx\n", + pSpxConnFile, pRequest, pParam->SendLength, pParam->SendFlags)); + + + // Check if we are in the correct state and associated. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + + DBGPRINT(SEND, INFO, + ("Send: %lx.%lx.%lx\n", + pParam->SendLength, pParam->SendFlags, pRequest)); + + status = STATUS_PENDING; + do + { + if (SPX_CONN_ACTIVE(pSpxConnFile) && + ((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_POST_ORDREL) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED))) + { + // Creation reference for request. + REQUEST_INFORMATION(pRequest) = 1; + + // If we have no current requests, queue it in and + // set it to be the current request, else just queue it in. + // There may be other pending requests in queue. + if (pSpxConnFile->scf_ReqPkt == NULL) + { + DBGPRINT(SEND, INFO, + ("%lx\n", + pRequest)); + + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = pParam->SendLength; + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pRequest)); + // + // Currently SPX implements DS_TYPE in such a way that the DS_TYPE field in a packet + // takes on the latest value from the connectionfile. Some customers complained that + // their apps assume that the DS_TYPE field should reflect the value at the time of + // the send (and Novell does this). + // + // So, instead of keeping a global value, we copy over the DS_TYPE value into the + // request if it pends. Look at spxpkt.c:SpxBuildData. + // + REQUEST_PARAMETERS(pRequest)->Others.Argument3 = (PVOID)pSpxConnFile->scf_DataType; + + } + else + { + // + // [SA] Bug #14655 + // Return the correct error message in case a send fails due to remote disconnect + // + + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) + { + status = STATUS_REMOTE_DISCONNECT ; + } + else + { + status = STATUS_INVALID_CONNECTION; + } + + break; + } + + // We packetize only upto the window we have. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize(pSpxConnFile, TRUE, lockHandleConn); + lockHeld = FALSE; + } + + } while (FALSE); + + + if (lockHeld) + { + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + } + + if (!NT_SUCCESS(status)) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(status); +} + + + + +NTSTATUS +SpxConnRecv( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status; + CTELockHandle lockHandle; + BOOLEAN fLockHeld; + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + // Check if the connection is in a valid state + if ((status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + { + return(status); + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnReceive: %lx.%lx\n", pSpxConnFile, pRequest)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + status = STATUS_INVALID_CONNECTION; + if (SPX_CONN_ACTIVE(pSpxConnFile) && + !(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC))) + { + status = STATUS_PENDING; + + // This routine adds its own reference. + SpxConnQueueRecv(pSpxConnFile, pRequest); + + // If recv pkt queue is non-empty then we have buffered data. Call + // process pkts/receives. + if ((SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) || + (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_POSTED)) + { + SpxRecvProcessPkts(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(status); +} + + + + +NTSTATUS +SpxConnAction( + IN PDEVICE pDevice, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS Status; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + PNWLINK_ACTION NwlinkAction; + CTELockHandle lockHandle; + PIPX_SPXCONNSTATUS_DATA pGetStats; + PSPX_CONN_FILE pSpxConnFile = NULL; + PSPX_ADDR_FILE pSpxAddrFile = NULL; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(pRequest); + if (NdisBuffer == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer( + REQUEST_NDIS_BUFFER(pRequest), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + return STATUS_NOT_SUPPORTED; + } + + // Make sure we have enough room for just the header not + // including the data. + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, buffer too small\n")); + + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + // Make sure that the correct file object is being used. + switch (NwlinkAction->OptionType) + { + case NWLINK_OPTION_CONNECTION: + + if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_CONNECTION_FILE) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, not connection file\n")); + + return STATUS_INVALID_HANDLE; + } + + pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + if ((Status = SpxConnFileVerify(pSpxConnFile)) != STATUS_SUCCESS) + return(Status); + + break; + + case NWLINK_OPTION_ADDRESS: + + if (REQUEST_OPEN_TYPE(pRequest) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) + { + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, not address file\n")); + + return STATUS_INVALID_HANDLE; + } + + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(pRequest); + + if ((Status = SpxAddrFileVerify(pSpxAddrFile)) != STATUS_SUCCESS) + return(Status); + + break; + + default: + + DBGPRINT(ACTION, ERR, + ("Nwlink action failed, option type %d\n", + NwlinkAction->OptionType)); + + return STATUS_INVALID_HANDLE; + } + + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + + Status = STATUS_SUCCESS; + + DBGPRINT(ACTION, INFO, + ("SpxConnAction: Option %x\n", NwlinkAction->Option)); + + switch (NwlinkAction->Option) + { + + // + // This first group support the winsock helper dll. + // In most cases the corresponding sockopt is shown in + // the comment, as well as the contents of the Data + // part of the action buffer. + // + + case MSPX_SETDATASTREAM: + + if (pSpxConnFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + if (DataLength >= 1) + { + DBGPRINT(ACTION, INFO, + ("%lx: MIPX_SETSENDPTYPE %x\n", + pSpxConnFile, NwlinkAction->Data[0])); + + pSpxConnFile->scf_DataType = NwlinkAction->Data[0]; + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + + case MSPX_SENDHEADER: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_SENDHEADER\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_IPXHDR; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break ; + + case MSPX_NOSENDHEADER: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_NOSENDHEADER\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_IPXHDR; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + case MSPX_GETSTATS: + + DBGPRINT(ACTION, INFO, + ("%lx: MSPX_GETSTATS\n", pSpxConnFile)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + USHORT TempRetryCount; + + // + // Status fields are returned in network order. + // + + pGetStats = (PIPX_SPXCONNSTATUS_DATA)&NwlinkAction->Data[0]; + + switch (SPX_MAIN_STATE(pSpxConnFile)) { + case SPX_CONNFILE_LISTENING: pGetStats->ConnectionState = 1; break; + case SPX_CONNFILE_CONNECTING: pGetStats->ConnectionState = 2; break; + case SPX_CONNFILE_ACTIVE: pGetStats->ConnectionState = 3; break; + case SPX_CONNFILE_DISCONN: pGetStats->ConnectionState = 4; break; + default: pGetStats->ConnectionState = 0; + } + pGetStats->WatchDogActive = 1; // Always 1 + GETSHORT2SHORT( // scf_LocalConnId is in host order + &pGetStats->LocalConnectionId, + &pSpxConnFile->scf_LocalConnId); + pGetStats->RemoteConnectionId = pSpxConnFile->scf_RemConnId; + + GETSHORT2SHORT(&pGetStats->LocalSequenceNumber, &pSpxConnFile->scf_SendSeqNum); + GETSHORT2SHORT(&pGetStats->LocalAckNumber, &pSpxConnFile->scf_RecvSeqNum); + GETSHORT2SHORT(&pGetStats->LocalAllocNumber, &pSpxConnFile->scf_SentAllocNum); + GETSHORT2SHORT(&pGetStats->RemoteAckNumber, &pSpxConnFile->scf_RecdAckNum); + GETSHORT2SHORT(&pGetStats->RemoteAllocNumber, &pSpxConnFile->scf_RecdAllocNum); + + pGetStats->LocalSocket = pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket; + + RtlZeroMemory(pGetStats->ImmediateAddress, 6); + + // Remote network returned in net order. + *((ULONG UNALIGNED *)pGetStats->RemoteNetwork) = + *((ULONG UNALIGNED *)pSpxConnFile->scf_RemAddr); + + RtlCopyMemory( + pGetStats->RemoteNode, + &pSpxConnFile->scf_RemAddr[4], + 6); + + pGetStats->RemoteSocket = *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+10)); + + TempRetryCount = (USHORT)pSpxConnFile->scf_WRetryCount; + GETSHORT2SHORT(&pGetStats->RetransmissionCount, &TempRetryCount); + GETSHORT2SHORT(&pGetStats->EstimatedRoundTripDelay, &pSpxConnFile->scf_BaseT1); + pGetStats->RetransmittedPackets = 0; + pGetStats->SuppressedPacket = 0; + + DBGPRINT(ACTION, INFO, + ("SSeq %lx RSeq %lx RecdAck %lx RemAllocNum %lx\n", + pGetStats->LocalSequenceNumber, + pGetStats->LocalAckNumber, + pGetStats->RemoteAckNumber, + pGetStats->RemoteAllocNumber)); + + DBGPRINT(ACTION, INFO, + ("LocalSkt %lx RemSkt %lx LocConnId %lx RemConnId %lx\n", + pGetStats->LocalSocket, + pGetStats->RemoteSocket, + pGetStats->LocalConnectionId, + pGetStats->RemoteConnectionId)); + } + else + { + Status = STATUS_INVALID_CONNECTION; + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + break; + + case MSPX_NOACKWAIT: + + DBGPRINT(ACTION, ERR, + ("%lx: MSPX_NOACKWAIT\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_NOACKWAIT; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + case MSPX_ACKWAIT: + + DBGPRINT(ACTION, ERR, + ("%lx: MSPX_ACKWAIT\n", pSpxAddrFile)); + + if (pSpxAddrFile == NULL) + { + Status = STATUS_INVALID_HANDLE; + break; + } + + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandle); + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_NOACKWAIT; + CTEFreeLock(pSpxAddrFile->saf_AddrLock, lockHandle); + break; + + + // + // These are new for ISN (not supported in NWLINK). + // + + // The Option was not supported, so fail. + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (Status != STATUS_SUCCESS) { + DBGPRINT(ACTION, ERR, + ("Nwlink action %lx failed, status %lx\n", + NwlinkAction->Option, Status)); + } + +#endif + + if (pSpxConnFile) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + if (pSpxAddrFile) + { + SpxAddrFileDereference(pSpxAddrFile, AFREF_VERIFY); + } + + return Status; +} + + + + +VOID +SpxConnConnectFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle + ) +/*++ + +Routine Description: + + This routine is called with the connection lock held and the conn refd. + It should deal with both. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrPkt; + PSPX_SEND_RESD pSendResd; + ULONG Timeout; + NTSTATUS status = STATUS_BAD_NETWORK_PATH; + + pSendResd = pSpxConnFile->scf_SendListHead; + pCrPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(CONNECT, INFO, + ("SpxConnConnectFindRouteComplete: %lx.%d\n", + pSpxConnFile, FoundRoute)); + +#if defined(_PNP_POWER) + + Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + + // Here we are going to send on every NIC ID. We adjust the + // timeout down so that a full run through all the NIC IDs will + // take one normal timeout. We don't adjust the timer below + // 100 ms however. + + Timeout = (PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR) / SpxDevice->dev_Adapters; + if (Timeout < (HALFSEC_TO_MS_FACTOR/5)) { + Timeout = HALFSEC_TO_MS_FACTOR / 5; + } + + } else { + + Timeout = PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR; + } +#endif + + + // Timeout value is in half-seconds + if ((FoundRoute) && + ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + Timeout, + pSpxConnFile)) != 0)) + { + // Add a reference for the connect timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + +#if 0 + { + int i; + char *address = pFrReq->fr_FindRouteReq.LocalTarget.MacAddress; + + DbgPrint("FIND ROUTE LOCALTARGET.MAC:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + + address = pSpxConnFile->scf_RemAddr+4; + DbgPrint("SPX DESTINATION ADDRESS:\n"); + for (i= 0; i < 6; i++) + { + UCHAR ch1, ch2; + + ch1 = ((address[i] >> 4) & 0x0F); + if (ch1 > 0x9) + { + ch1 -= 0xa; + ch1 += 'a'; + } + else + { + ch1 += '0'; + } + + ch2 = (address[i] & 0x0F); + if (ch2 > 0x9) + { + ch2 -= 0xa; + ch2 += 'a'; + } + else + { + ch2 += '0'; + } + + DbgPrint("%c%c", ch1, ch2); + } + DbgPrint("\n"); + } + + DbgPrint("NIC Id %lx\n", pFrReq->fr_FindRouteReq.LocalTarget.NicId); +#endif + + // If the mac address in local target is all zeros, fill it with our + // destination address. Also if this is a connect to network 0 fill + // it in with the destination address, and further down we will loop + // through all possible NIC IDs. + if (((*((UNALIGNED ULONG *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+2)) == (ULONG)0) + && + (*((UNALIGNED USHORT *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) + || + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) + { + DBGPRINT(CONNECT, INFO, + ("SpxConnConnectFindRouteComplete: LOCAL NET\n")); + + RtlCopyMemory( + pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, + pSpxConnFile->scf_RemAddr+4, + 6); + } + + // We are all set to go ahead with the connect. + // Timer is started on connection + status = STATUS_SUCCESS; + +#if defined(_PNP_POWER) + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT) * SpxDevice->dev_Adapters; + } else { + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + } +#endif _PNP_POWER + + SPX_CONN_SETFLAG(pSpxConnFile, + (SPX_CONNFILE_C_TIMER | SPX_CONNECT_SENTREQ)); + + pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; + pSpxConnFile->scf_AckLocalTarget= pFrReq->fr_FindRouteReq.LocalTarget; + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { +#if defined(_PNP_POWER) + pSpxConnFile->scf_LocalTarget.NicHandle.NicId = 0; + pSpxConnFile->scf_AckLocalTarget.NicHandle.NicId = 0; +#else + pSpxConnFile->scf_LocalTarget.NicId = 1; + pSpxConnFile->scf_AckLocalTarget.NicId = 1; +#endif _PNP_POWER + } + + // We will be giving the packet to ipx. + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pCrPkt, pSendResd); + } + + if (!NT_SUCCESS(status)) + { + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + DBGPRINT(CONNECT, ERR, + ("SpxConnConnectFindRouteComplete: FAILED on %lx.%d\n", + pSpxConnFile, FoundRoute)); + + spxConnAbortConnect( + pSpxConnFile, + status, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + } + + // Remove the reference for the call. + SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); + return; +} + + + + +VOID +SpxConnActiveFindRouteComplete( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_FIND_ROUTE_REQUEST pFrReq, + IN BOOLEAN FoundRoute, + IN CTELockHandle LockHandle + ) +/*++ + +Routine Description: + + This routine is called with the connection lock held and the conn refd. + It should deal with both. + +Arguments: + + +Return Value: + + +--*/ +{ + BOOLEAN fDisconnect = TRUE; + + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + + DBGPRINT(CONNECT, DBG, + ("SpxConnActiveFindRouteComplete: %lx.%lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // If we are disconnecting, just remove the reference and exit. + if (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE) + { + fDisconnect = FALSE; + + // We are here if either the wdog or the retry timer did a find + // route. We need to save the info from the find route if it was + // successful and just restart the timers. + if (FoundRoute) + { + // If the mac address in local target is all zeros, fill it with our + // destination address. + if ((*((UNALIGNED ULONG *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+2)) == (ULONG)0) + && + (*((UNALIGNED USHORT *) + (pFrReq->fr_FindRouteReq.LocalTarget.MacAddress+4)) == (USHORT)0)) + { + DBGPRINT(CONNECT, INFO, + ("SpxConnActiveFindRouteComplete: LOCAL NET\n")); + + RtlCopyMemory( + pFrReq->fr_FindRouteReq.LocalTarget.MacAddress, + pSpxConnFile->scf_RemAddr+4, + 6); + } + + pSpxConnFile->scf_LocalTarget = pFrReq->fr_FindRouteReq.LocalTarget; + } + + // Depending on state restart the wdog or retry timer. Add reference + // for it. + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_RETRY: + + // Set state to SPX_SEND_RETRYWD + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRYWD); + + // Start retry timer. + if ((pSpxConnFile->scf_RTimerId = + SpxTimerScheduleEvent( + spxConnRetryTimer, + pSpxConnFile->scf_BaseT1, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_WD: + + // Start watchdog timer. + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } + else + { + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_IDLE: + case SPX_SEND_PACKETIZE: + + // Do nothing, remove reference and leave. + break; + + default: + + KeBugCheck(0); + } + } + + if (fDisconnect) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnActiveFindRouteComplete: DISCONNECT %lx.%lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // Abortive disc will reset the funky state if necessary. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + SPX_CALL_TDILEVEL, + LockHandle, + FALSE); // [SA] Bug #15249 + } + else + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_FINDROUTE); + return; +} + + + + +ULONG +spxConnConnectTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + We enter this routine during the connection attempt. We could be at any + stage of sending either the CR or the SN packet. If we have reached the end of + the retry count, we need to know the substate at that point. For a CR, we give + up trying to connect, and for a SN we try the next lower packet size or if we + have reached the minimum packet size, we give up the connect. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN fAbort = FALSE, locksHeld = FALSE, sendPkt = FALSE; + PREQUEST pRequest = NULL; + + // Get all locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnConnectTimer: Entered\n")); + + do + { + if ((!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) || + (!SPX_CONN_CONNECTING(pSpxConnFile) && + !SPX_CONN_LISTENING(pSpxConnFile))) + { + TimerShuttingDown = TRUE; + } + + if (TimerShuttingDown) + { + break; + } + + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + switch (SPX_CONNECT_STATE(pSpxConnFile)) + { + case SPX_CONNECT_SENTREQ: + + // There should be only one packet in list, the cr. + CTEAssert(pSpxConnFile->scf_SendListHead == + pSpxConnFile->scf_SendListTail); + + pSendResd = pSpxConnFile->scf_SendListHead; + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, + NDIS_PACKET, + ProtocolReserved); + + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // No luck, we need to complete connect request with failure + ++SpxDevice->dev_Stat.NotFoundFailures; + fAbort = TRUE; + break; + } + + // We need to resend the packet + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try next time. + break; + } + + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + case SPX_CONNECT_NEG: + + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SN, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try when we come in next. + break; + } + + + // If we have exhausted current retries, try next smaller size. + // If this was the smallest size, we abort. + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // Have we tried the smallest size? + CTEAssert(pSpxConnFile->scf_MaxPktSize > 0); + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // Give up! Remove negotiate packet etc. + ++SpxDevice->dev_Stat.SessionTimeouts; + fAbort = TRUE; + break; + } + + // Set neg pkt size to new lower size + spxConnSetNegSize( + pPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + pSpxConnFile->scf_CRetryCount = + PARAM(CONFIG_CONNECTION_COUNT); + } + + // We need to resend the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + case SPX_CONNECT_W_SETUP: + default: + + DBGPRINT(CONNECT, ERR, + ("spxConnConnectTimer: state is W_Setup %lx\n", + pSpxConnFile)); + + KeBugCheck(0); + } + } + else + { + switch (SPX_LISTEN_STATE(pSpxConnFile)) + { + case SPX_LISTEN_SETUP: + + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SS, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try when we come in next. + break; + } + + // If we have exhausted current retries, try next smaller size. + // If this was the smallest size, we abort. + if (pSpxConnFile->scf_CRetryCount-- == 0) + { + // Have we tried the smallest size? + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // Give up! Remove negotiate packet etc. Have an abort + // kind of routine. + ++SpxDevice->dev_Stat.SessionTimeouts; + fAbort = TRUE; + break; + } + + // Set neg pkt size to new lower size + spxConnSetNegSize( + pPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + pSpxConnFile->scf_CRetryCount = + PARAM(CONFIG_CONNECTION_COUNT); + } + + // We need to resend the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + + pSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + sendPkt = TRUE; + break; + + default: + + KeBugCheck(0); + + } + } + + } while (FALSE); + + if (fAbort) + { + CTEAssert(!sendPkt); + + DBGPRINT(CONNECT, ERR, + ("spxConnConnectTimer: Expired for %lx\n", pSpxConnFile)); + + spxConnAbortConnect( + pSpxConnFile, + STATUS_BAD_NETWORK_PATH, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (sendPkt) + { + CTEAssert(!fAbort); + +#if !defined(_PNP_POWER) + if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0)) { + + // we are sending to all NICs because this is the initial + // connect frame and the remote network is 0. + + pSpxConnFile->scf_LocalTarget.NicId = (USHORT) + ((pSpxConnFile->scf_LocalTarget.NicId % SpxDevice->dev_Adapters) + 1); + + // we pass this a valid packet in pPkt, so it knows to + // just refresh the header and not update the protocol + // reserved variables. + + SpxPktBuildCr( + pSpxConnFile, + pSpxConnFile->scf_AddrFile->saf_Addr, + &pPkt, + 0, // state will not be updated + SPX2_CONN(pSpxConnFile)); + + } +#endif !_PNP_POWER + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + + if (TimerShuttingDown || fAbort) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(TIMER_DONT_REQUEUE); + } + + return(TIMER_REQUEUE_CUR_VALUE); +} + + + + +ULONG +spxConnWatchdogTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + This is started on a connection right after the CR or the CR ack is received. + During the connection establishment phase, it does nothing other than decrement + the retry count and upon reaching 0, it aborts the connection. When it goes off + and finds the connection is active, it sends a probe. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + CTELockHandle lockHandle; + PSPX_SEND_RESD pSendResd; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + PNDIS_PACKET pProbe = NULL; + BOOLEAN lockHeld, fSpx2 = SPX2_CONN(pSpxConnFile), + fDisconnect = FALSE, fFindRoute = FALSE, fSendProbe = FALSE; + + DBGPRINT(CONNECT, INFO, + ("spxConnWatchdogTimer: Entered\n")); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + do + { + if (TimerShuttingDown || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) + { +#if DBG + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + CTEAssert(FALSE); + } +#endif + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + TimerShuttingDown = TRUE; + break; + } + + // If the retry timer is active on this connection, and the watchdog + // timer happens to fire, just requeue ourselves for spx2. For spx1, + // we go ahead with sending a probe. Retry timer does the same things + // watchdog does for spx2. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + case SPX_CONNFILE_DISCONN: + + // Squash the race condition where a disconnect request is never + // packetized, because the send state was not IDLE. + if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC) + { + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: POST IDISC %lx\n", + pSpxConnFile)); + + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: PKT POST IDISC %lx\n", + pSpxConnFile)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandle); + + lockHeld = FALSE; + break; + } + } + + if (!fSpx2) + { + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + fSendProbe = TRUE; + } + else + { + fDisconnect = TRUE; + } + + break; + } + + // SPX2 connection. Watchdog algorithm needs to do lots of goody + // stuff. If retry is active, just requeue ourselves. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + break; + + // There is a race between watchdog and retry if its started. Who + // ever changes the state first gets to go do its thing. + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_IDLE: + + // Enter WD state only if we fired for the second time witout + // an ack. This prevents PACKETIZE from blocking due to being + // in a non-idle state. + CTEAssert(pSpxConnFile->scf_WRetryCount != 0); + if ((pSpxConnFile->scf_WRetryCount)-- != + (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)) + { + // We enter the WD state. Build and send a probe. + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_WD); + SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); + } + + fSendProbe = TRUE; + break; + + case SPX_SEND_PACKETIZE: + + // Do nothing. + break; + + case SPX_SEND_RETRY: + case SPX_SEND_RETRYWD: + case SPX_SEND_RENEG: + case SPX_SEND_RETRY2: + case SPX_SEND_RETRY3: + + // Do nothing. Send timer got in first. + DBGPRINT(CONNECT, DBG1, + ("SpxConnWDogTimer: When retry fired %lx\n", + pSpxConnFile)); + + break; + + case SPX_SEND_WD: + + // Decrement count. If not zero, send a probe. If half the + // count is reached, stop timer and call find route. + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + if (pSpxConnFile->scf_WRetryCount != + (LONG)PARAM(CONFIG_KEEPALIVE_COUNT)/2) + { + fSendProbe = TRUE; + break; + } + + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + fDisconnect = TRUE; + break; + } + + // Remove timer reference/ Add find route request ref + fFindRoute = TRUE; + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network) = + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6); + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4))= + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnWDogTimer: NETWORK %lx\n", + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); + + pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // Make sure we have IPX re-rip. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; + } + else + { + fDisconnect = TRUE; + } + + break; + + default: + + KeBugCheck(0); + } + + break; + + case SPX_CONNFILE_CONNECTING: + + if ((SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) || + (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_NEG)) + { + // Do nothing. Connect timer is active. + DBGPRINT(CONNECT, ERR, + ("SpxConnWDogTimer: CR Timer active %lx\n", + pSpxConnFile)); + + break; + } + + if (!(pSpxConnFile->scf_WRetryCount--)) + { + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_CONNFILE_LISTENING: + + if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP) + { + // Do nothing. Connect timer is active. + DBGPRINT(CONNECT, ERR, + ("SpxConnWDogTimer: CR Timer active %lx\n", + pSpxConnFile)); + + break; + } + + if (!(pSpxConnFile->scf_WRetryCount--)) + { + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + default: + + // Should never happen! + KeBugCheck(0); + } + + } while (FALSE); + + if (fSendProbe) + { + CTEAssert(lockHeld); + CTEAssert(!fDisconnect); + + DBGPRINT(CONNECT, DBG1, + ("spxConnWatchdogTimer: Send Probe from %lx.%lx\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + // Build a probe and send it out to the remote end. + SpxPktBuildProbe( + pSpxConnFile, + &pProbe, + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), + fSpx2); + + if (pProbe != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pProbe); + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + } + } + + if (fDisconnect) + { + CTEAssert(lockHeld); + CTEAssert(!fSendProbe); + + // Disconnect! + DBGPRINT(CONNECT, ERR, + ("spxConnWatchdogTimer: Connection %lx.%lx expired\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + + // If spx2, check if we need to do anything special. + // AbortiveDisc will reset funky state if needed. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LINK_TIMEOUT, + SPX_CALL_TDILEVEL, + lockHandle, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (fFindRoute) + { + CTEAssert(!fSendProbe); + CTEAssert(!fDisconnect); + CTEAssert(TimerShuttingDown); + + // Start off the find route request + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + + if (pProbe != NULL) + { + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); + } + + if (TimerShuttingDown) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return((TimerShuttingDown ? TIMER_DONT_REQUEUE : TIMER_REQUEUE_CUR_VALUE)); +} + + + +ULONG +spxConnRetryTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + PSPX_SEND_RESD pSendResd; + CTELockHandle lockHandleConn; + PIPXSPX_HDR pSendHdr; + PNDIS_PACKET pPkt; + PNDIS_PACKET pProbe = NULL; + PSPX_FIND_ROUTE_REQUEST pFindRouteReq; + USHORT reenqueueTime = TIMER_REQUEUE_CUR_VALUE; + BOOLEAN lockHeld, fResendPkt = FALSE, fDisconnect = FALSE, + fFindRoute = FALSE, fBackoffTimer = FALSE; + PREQUEST pRequest = NULL; + + DBGPRINT(CONNECT, INFO, + ("spxConnRetryTimer: Entered\n")); + + // Get lock + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + lockHeld = TRUE; + + do + { + // If timer is not up, no send pkts, just return. + if (TimerShuttingDown || + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL)) + { +#if DBG + if ((pSendResd = pSpxConnFile->scf_SendSeqListHead) == NULL) + { + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + CTEAssert(FALSE); + } + } +#endif + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + TimerShuttingDown = TRUE; + break; + } + + // In all other cases, reenqueue with potentially modified reenqueue + // time. + reenqueueTime = pSpxConnFile->scf_BaseT1; + DBGPRINT(SEND, INFO, + ("spxConnRetryTimer: BaseT1 %lx on %lx\n", + pSpxConnFile->scf_BaseT1, pSpxConnFile)); + + // If an ack for a packet was processed while we were out, reset + // retry count and return. Or if we are packetizing, return. + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) + { + break; + } + else if ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + (pSpxConnFile->scf_RetrySeqNum != pSendResd->sr_SeqNum)) + { + pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum; + break; + } + + // If packet is still with IPX, requeue for next time. + if (pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) + { + break; + } + + CTEAssert(pSendResd != NULL); + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_IDLE: + + // Set ack bit in packet. pSendResd initialized at beginning. + pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK; + + // Do we backoff the timer? + fBackoffTimer = + (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_REXMIT) != 0); + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + ++SpxDevice->dev_Stat.ResponseTimerExpirations; + + CTEAssert((ULONG)pSpxConnFile->scf_RRetryCount <= + PARAM(CONFIG_REXMIT_COUNT)); + + DBGPRINT(SEND, DBG1, + ("spxConnRetryTimer: Retry Count %lx on %lx\n", + pSpxConnFile->scf_RRetryCount, pSpxConnFile)); + + fResendPkt = TRUE; + if (pSpxConnFile->scf_RRetryCount-- != 0) + { + // We dont treat the IDISC packet as a data packet, so none + // of the fancy spx2 retry stuff if we are retrying the idisc. + if (SPX2_CONN(pSpxConnFile) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_IDISC)) + { + // We enter the RETRY state. Reference conn for this + // "funky" state. + CTEAssert(SPX2_CONN(pSpxConnFile)); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY); + SpxConnFileLockReference(pSpxConnFile, CFREF_ERRORSTATE); + } + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + fResendPkt = FALSE; + pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; + } + + break; + + case SPX_SEND_RETRY: + + // When we have reached retry_count/2 limit, start locate route. Do + // not queue ourselves. Handle restarting timer in find route + // completion. If timer starts successfully in find route comp, then + // it will change our state to RETRYWD. + + // Decrement count. If half the count is reached, stop timer and call + // find route. + if (pSpxConnFile->scf_RRetryCount-- != + (LONG)PARAM(CONFIG_REXMIT_COUNT)/2) + { + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + break; + } + + if ((pFindRouteReq = + (PSPX_FIND_ROUTE_REQUEST)SpxAllocateMemory( + sizeof(SPX_FIND_ROUTE_REQUEST))) == NULL) + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Alloc Mem %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + break; + } + + // Remove timer reference/ Add find route request ref + fFindRoute = TRUE; + TimerShuttingDown = TRUE; + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_FINDROUTE); + SpxConnFileLockReference(pSpxConnFile, CFREF_FINDROUTE); + + // Initialize the find route request + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Network)= + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr); + + // + // [SA] Bug #15094 + // We need to also pass in the node number to IPX so that IPX can + // compare the node addresses to determine the proper WAN NICid + // + + // RtlCopyMemory (pFindRouteReq->fr_FindRouteReq.Node, pSpxConnFile->scf_RemAddr+4, 6) ; + + *((UNALIGNED ULONG *)pFindRouteReq->fr_FindRouteReq.Node)= + *((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr+4)); + + *((UNALIGNED USHORT *)(pFindRouteReq->fr_FindRouteReq.Node+4)) = + *((UNALIGNED USHORT *)(pSpxConnFile->scf_RemAddr+8)); + + DBGPRINT(CONNECT, DBG, + ("SpxConnRetryTimer: NETWORK %lx\n", + *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr))); + + pFindRouteReq->fr_FindRouteReq.Identifier= IDENTIFIER_SPX; + pFindRouteReq->fr_Ctx = pSpxConnFile; + + // Make sure we have IPX re-rip. + pFindRouteReq->fr_FindRouteReq.Type = IPX_FIND_ROUTE_FORCE_RIP; + break; + + case SPX_SEND_RETRYWD: + + // Retry a watchdog packet WCount times (initialize to RETRY_COUNT). + // If process ack receives an ack (i.e. actual ack packet) while in + // this state, it will transition the state to RENEG. + // + // If the pending data gets acked while in this state, we go back + // to idle. + DBGPRINT(CONNECT, DBG1, + ("spxConnRetryTimer: Send Probe from %lx.%lx\n", + pSpxConnFile->scf_LocalConnId, pSpxConnFile)); + + // Use watchdog count here. + if (pSpxConnFile->scf_WRetryCount-- > 0) + { + // Build a probe and send it out to the remote end. + SpxPktBuildProbe( + pSpxConnFile, + &pProbe, + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY), + TRUE); + + if (pProbe != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pProbe); + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + break; + } + } + + // Just set state to retry data packet retry_count/2 times. + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY2); + break; + + case SPX_SEND_RENEG: + + // Renegotiate size. If we give up, goto RETRY3. + // For this both sides must have negotiated size to begin with. + // If they did not, we go on to retrying the data packet. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG)) + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: NO NEG FLAG SET: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_Flags)); + + // Reset count to be + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + break; + } + + // Send reneg packet, if we get the rr ack, then we resend data + // on queue. Note that each time we goto a new negotiate size, + // we rebuild the data packets. + if (pSpxConnFile->scf_RRetryCount-- == 0) + { + // Reset count. + pSpxConnFile->scf_RRetryCount = SPX_DEF_RENEG_RETRYCOUNT; + if ((ULONG)pSpxConnFile->scf_MaxPktSize <= + (SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)) + { + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + DBGPRINT(SEND, DBG3, + ("SpxConnRetryTimer: %lx MIN RENEG SIZE\n", + pSpxConnFile)); + } + + // Are we at the lowest possible reneg pkt size? If not, try + // next lower. When we do this, we free all pending send + // packets and reset the packetize queue to the first packet. + // Process ack will just do packetize and will not do anything + // more other than resetting state to proper value. + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG: %lx - CURRENT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + + if (!spxConnCheckNegSize(&pSpxConnFile->scf_MaxPktSize)) + { + // We tried lowest size and failed to receive ack. Just + // retry data packet, and disc if no ack. + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG(min), RETRY3: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + break; + } + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: RENEG(!min): %lx - ATTEMPT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_MaxPktSize)); + } + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: %lx.%lx.%lx RENEG SEQNUM %lx ACKNUM %lx\n", + pSpxConnFile, + pSpxConnFile->scf_RRetryCount, + pSpxConnFile->scf_MaxPktSize, + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), + pSpxConnFile->scf_SentAllocNum)); + + // Use first unused data packet sequence number. + SpxPktBuildRr( + pSpxConnFile, + &pPkt, + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1), + (SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY)); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + fResendPkt = TRUE; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + } + + break; + + case SPX_SEND_RETRY2: + + // Retry the data packet for remaining amount of RRetryCount. If not + // acked goto cleanup. If ack received while in this state, goto idle. + + if (pSpxConnFile->scf_RRetryCount-- > 0) + { + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: 2nd try Resend on %lx\n", + pSpxConnFile)); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_RETRY3: + + // Send data packet for RETRY_COUNT times initialized in RRetryCount + // before state changed to this state. If ok, process ack moves us + // back to PKT/IDLE. If not, we disconnect. + // We are going to resend this packet + + if (pSpxConnFile->scf_RRetryCount-- > 0) + { + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: 3rd try Resend on %lx\n", + pSpxConnFile)); + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + fBackoffTimer = TRUE; + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: Retry Count over on %lx\n", + pSpxConnFile)); + + fDisconnect = TRUE; + } + + break; + + case SPX_SEND_WD: + + // Do nothing. Watchdog timer has fired, just requeue. + break; + + default: + + KeBugCheck(0); + } + + if (fBackoffTimer) + { + // Increase retransmit timeout by 50% upto maximum indicated by + // initial retransmission value. + + reenqueueTime += reenqueueTime/2; + if (reenqueueTime > MAX_RETRY_DELAY) + reenqueueTime = MAX_RETRY_DELAY; + + pSpxConnFile->scf_BaseT1 = + pSpxConnFile->scf_AveT1 = reenqueueTime; + pSpxConnFile->scf_DevT1 = 0; + + DBGPRINT(SEND, DBG, + ("spxConnRetryTimer: Backed retry on %lx.%lx %lx\n", + pSpxConnFile, pSendResd->sr_SeqNum, reenqueueTime)); + } + + if (fDisconnect) + { + CTEAssert(lockHeld); + + // Do not requeue this timer. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + TimerShuttingDown = TRUE; + + // Disconnect the connection. + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LINK_TIMEOUT, + SPX_CALL_TDILEVEL, + lockHandleConn, + FALSE); // [SA] Bug #15249 + + lockHeld = FALSE; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + if (fResendPkt) + { + DBGPRINT(SEND, DBG, + ("spxConnRetryTimer: Resend pkt on %lx.%lx\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + ++SpxDevice->dev_Stat.DataFramesResent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesResent, + pSendResd->sr_Len - (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + else if (fFindRoute) + { + CTEAssert(!fResendPkt); + CTEAssert(!fDisconnect); + CTEAssert(TimerShuttingDown); + + DBGPRINT(SEND, DBG3, + ("spxConnRetryTimer: Find route on %lx\n", + pSpxConnFile)); + + // Start off the find route request + (*IpxFindRoute)( + &pFindRouteReq->fr_FindRouteReq); + } + else if (pProbe != NULL) + { + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pProbe, pSendResd); + } + + if (TimerShuttingDown) + { + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + reenqueueTime = TIMER_DONT_REQUEUE; + } + + DBGPRINT(SEND, INFO, + ("spxConnRetryTimer: Reenqueue time : %lx on %lx\n", + reenqueueTime, pSpxConnFile)); + + return(reenqueueTime); +} + + + + +ULONG +spxConnAckTimer( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile = (PSPX_CONN_FILE)Context; + CTELockHandle lockHandleConn; + + DBGPRINT(SEND, INFO, + ("spxConnAckTimer: Entered\n")); + + // Get lock + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + if (!TimerShuttingDown && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + // We didnt have any back traffic, until we do a send from this + // end, send acks immediately. Dont try to piggyback. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK); + + ++SpxDevice->dev_Stat.PiggybackAckTimeouts; + + DBGPRINT(SEND, DBG, + ("spxConnAckTimer: Send ack on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + SpxConnSendAck(pSpxConnFile, lockHandleConn); + } + else + { + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + } + + // Dereference connection for verify done in connect, for timer. This + // should complete any pending disconnects if they had come in in the + // meantime. + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + return(TIMER_DONT_REQUEUE); +} + + + +// +// DISCONNECT ROUTINES +// + + +VOID +spxConnAbortiveDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn, + IN BOOLEAN IDiscFlag // [SA] Bug #15249 + ) +/*++ + +Routine Description: + + This is called when: + We time out or have insufficient resources - + STATUS_LINK_TIMEOUT/STATUS_INSUFFICIENT_RESOURCES + - We abort everything. Could be from watchdog or retry. Stop both. + + We receive a informed disconnect packet - + STATUS_REMOTE_DISCONNECT + - We abort everything. Ack must be sent by caller as an orphan pkt. + + We receive a informed disconnect ack pkt + STATUS_SUCCESS + - We abort everything + - Abort is done with status success (this completes our disc req in + the send queue) + + NOTE: CALLED UNDER THE CONNECTION LOCK. + +Arguments: +[SA] Bug #15249: Added IDiscFlag to indicate if this is an Informed Disconnect. If so, indicate + TDI_DISCONNECT_RELEASE to AFD so it allows a receive of buffered pkts. This flag is TRUE + only if this routine is called from SpxConnProcessIDisc for SPX connections. + +Return Value: + + +--*/ +{ + int numDerefs = 0; + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + BOOLEAN lockHeld = TRUE; + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: %lx - On %lx when %lx\n", + Status, pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + switch (Status) { + case STATUS_LINK_TIMEOUT: ++SpxDevice->dev_Stat.LinkFailures; break; + case STATUS_INSUFFICIENT_RESOURCES: ++SpxDevice->dev_Stat.LocalResourceFailures; break; + case STATUS_REMOTE_DISCONNECT: ++SpxDevice->dev_Stat.RemoteDisconnects; break; + case STATUS_SUCCESS: + case STATUS_LOCAL_DISCONNECT: ++SpxDevice->dev_Stat.LocalDisconnects; break; + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_ACTIVE: + + // For transition from active to disconn. + numDerefs++; + + case SPX_CONNFILE_DISCONN: + + // If we are in any state other than idle/packetize, + // remove the reference for the funky state, and reset the send state to be + // idle. + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) + { +#if DBG + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)) + { + DBGPRINT(CONNECT, ERR, + ("spxConnAbortiveDisc: When DISC STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + } +#endif + + DBGPRINT(CONNECT, DBG1, + ("spxConnAbortiveDisc: When SEND ERROR STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + // This can be called when a idisc is received, or if a timer + // disconnect is happening, or if we sent a idisc/ordrel, but the retries + // timed out and we are aborting the connection. + // So if we are already aborting, never mind. + + // + // [SA] Bug #15249 + // SPX_DISC_INACTIVATED indicates a DISC_ABORT'ing connection that has been + // inactivated (connfile removed from active conn. list) + // + + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + ((SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT) || + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED))) + { + break; + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ABORT); + + // Stop all timers. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_RTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_WTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } +#if 0 + // + // [SA] We need to call AFD after aborting sends since this connection + // becomes a candidate for re-use as soon as the disconnect handler is + // called. + // We call the disconnect handler when the refcount falls to 0 and the + // connection transitions to the inactive list. + // + + // NOTE! We indicate disconnect to afd *before* aborting sends to avoid + // afd from calling us again with a disconnect. + // Get disconnect handler if we have one. And we have not indicated + // abortive disconnect on this connection to afd. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) + { + // Yeah, we set the flag regardless of whether a handler is + // present. + pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } +#endif + // + // [SA] Save the IDiscFlag in the Connection. + // + (IDiscFlag) ? + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC) : + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC); + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + DBGPRINT(CONNECT, INFO, + ("spxConnAbortiveDisc: Indicating to afd On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // First complete all requests waiting for receive completion on + // this conn before indicating disconnect. + spxConnCompletePended(pSpxConnFile); + + + // + // [SA] bug #15249 + // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD + // + + if (!IDiscFlag) + { + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_ABORT); + } + else + { + // + // [SA] bug #15249 + // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets + // it has buffered before the remote disconnect took place. + // + + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_RELEASE); + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Go through and kill all pending requests. + spxConnAbortRecvs( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + spxConnAbortSends( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn); + + lockHeld = FALSE; + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CONN/LIST Disc On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + lockHeld = FALSE; + + { + CTELockHandle lockHandleAddr, lockHandleDev; + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + // Ensure we are still in connecting/listening, else call abortive + // again. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CONN/LIST Disc2 On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + spxConnAbortConnect( + pSpxConnFile, + Status, + lockHandleDev, + lockHandleAddr, + LockHandleConn); + + break; + + case SPX_CONNFILE_ACTIVE: + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock( + pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock( + &SpxDevice->dev_Lock, lockHandleDev); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortiveDisc: CHG ACT Disc2 On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + spxConnAbortiveDisc( + pSpxConnFile, + Status, + CallLevel, + LockHandleConn, + FALSE); // [SA] Bug #15249 + + break; + + default: + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock( + pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock( + &SpxDevice->dev_Lock, lockHandleDev); + + break; + } + } + + default: + + // Already disconnected. + break; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxcpkt.c b/private/ntos/tdi/isnp/spx/spxcpkt.c new file mode 100644 index 000000000..deb185201 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxcpkt.c @@ -0,0 +1,4131 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxcpkt.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 14-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXCPKT + +VOID +spxConnHandleConnReq( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + BOOLEAN fNeg, fSpx2; + TA_IPX_ADDRESS srcIpxAddr; + PTDI_IND_CONNECT connHandler; + USHORT srcConnId, destConnId, destSkt, + pktLen, seqNum, ackNum, allocNum; + PVOID connHandlerCtx; + PREQUEST pListenReq; + PSPX_SEND_RESD pSendResd; + NTSTATUS status; + CTELockHandle lockHandle, lockHandleDev, lockHandleConn; + CONNECTION_CONTEXT connCtx; + PIRP acceptIrp; + PSPX_ADDR pSpxAddr; + PSPX_ADDR_FILE pSpxAddrFile, pSpxRefFile; + PSPX_CONN_FILE pSpxConnFile; + PNDIS_PACKET pCrAckPkt; + BOOLEAN connectAccepted = FALSE, delayAccept = FALSE, + addrLock = FALSE, tdiListen = FALSE; + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // Verify Connect Request + if (((pIpxSpxHdr->hdr_ConnCtrl & (SPX_CC_ACK | SPX_CC_SYS)) != + (SPX_CC_ACK | SPX_CC_SYS)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (seqNum != 0) || + (ackNum != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (destConnId != 0xFFFF)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifyCR Failed %lx.%lx\n", + srcConnId, destConnId)); + return; + } + + // Get the destination socket from the header + destSkt = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_DestSkt; + + SpxBuildTdiAddress( + &srcIpxAddr, + sizeof(srcIpxAddr), + (PBYTE)pIpxSpxHdr->hdr_SrcNet, + pIpxSpxHdr->hdr_SrcNode, + pIpxSpxHdr->hdr_SrcSkt); + + // Ok, get the address object this is destined for. + CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev); + pSpxAddr = SpxAddrLookup(SpxDevice, destSkt); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + if (pSpxAddr == NULL) + { + DBGPRINT(RECEIVE, DBG, + ("SpxReceive: No addr for %lx\n", destSkt)); + + return; + } + + fSpx2 = ((PARAM(CONFIG_DISABLE_SPX2) == 0) && + (BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2)); + fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG)); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleConnReq: Received connect req! %d.%d\n", + fSpx2, fNeg)); + + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + addrLock = TRUE; + + // We use a bit setting in the flag to prevent reentering + // per address file. + // + // We first search the list of non-inactive connections on the address + // this packet came in on to see if it is a duplicate. If it is, we just + // resend ack. Note we dont need to scan the global connection list. + status = SpxAddrConnByRemoteIdAddrLock( + pSpxAddr, srcConnId, pIpxSpxHdr->hdr_SrcNet, &pSpxConnFile); + + if (NT_SUCCESS(status)) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: Received duplicate connect req! %lx\n", + pSpxConnFile)); + + if (SPX_CONN_ACTIVE(pSpxConnFile) || + (SPX_CONN_LISTENING(pSpxConnFile) && + ((SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK) || + (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SETUP)))) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: Sending Duplicate CR - ACK! %lx\n", + pSpxConnFile)); + + // Build and send an ack + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + SpxPktBuildCrAck( + pSpxConnFile, + pSpxAddr, + &pCrAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG), + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2)); + + if (pCrAckPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt); + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + addrLock = FALSE; + + // Send the CR Ack packet! + if (pCrAckPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd); + } + } + + if (addrLock) + { + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + // We should return in this if, else addrLock should be set to + // FALSE. + } + + // Deref the connection + SpxConnFileDereference(pSpxConnFile, CFREF_ADDR); + + // Deref the address + SpxAddrDereference (pSpxAddr, AREF_LOOKUP); + return; + } + + do + { + // New connection request: + // Assume we will be able to accept it and allocate a packet for the ack. + // Walk list of listening connections if any. + + pSpxRefFile = NULL; + if ((pSpxConnFile = pSpxAddr->sa_ListenConnList) != NULL) + { + PTDI_REQUEST_KERNEL_LISTEN pParam; + + DBGPRINT(RECEIVE, INFO, + ("SpxConnIndicate: Listen available!\n")); + + // dequeue connection + pSpxAddr->sa_ListenConnList = pSpxConnFile->scf_Next; + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + CTEAssert(!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + pListenReq = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + pParam = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(pListenReq); + + // if autoaccept, acceptIrp = listenIrp, get connection id and + // process as we do for an indication. As the connection has a + // listen posted on it, it must have a reference for it. + // + // if !autoaccept, we need to complete the listen irp. + delayAccept = (BOOLEAN)((pParam->RequestFlags & TDI_QUERY_ACCEPT) != 0); + if (delayAccept) + { + // Remove the listen irp and prepare for completion. + // NOTE!! Here we do not remove the listen reference. This will + // be removed if disconnect happens, or if accept + // happens, it is transferred to being ref for connection + // being active. + RemoveEntryList(REQUEST_LINKAGE(pListenReq)); + REQUEST_STATUS(pListenReq) = STATUS_SUCCESS; + REQUEST_INFORMATION(pListenReq) = 0; + } + + // Are we ok with spx2? + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2)) || + !fSpx2) + { + // We better use spx only. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + fSpx2 = fNeg = FALSE; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + + connectAccepted = TRUE; + tdiListen = TRUE; + } + else + { + // No listens available. Check for connect handlers. + // Walk list of address files indicating to each until accepted. + for (pSpxAddrFile = pSpxAddr->sa_AddrFileList; + pSpxAddrFile != NULL; + pSpxAddrFile = pSpxAddrFile->saf_Next) + { + if ((pSpxAddrFile->saf_Flags & (SPX_ADDRFILE_CLOSING | + SPX_ADDRFILE_CONNIND)) || + ((connHandler = pSpxAddrFile->saf_ConnHandler) == NULL)) + { + continue; + } + + // Connect indication in progress, drop all subsequent. + pSpxAddrFile->saf_Flags |= SPX_ADDRFILE_CONNIND; + + connHandlerCtx = pSpxAddrFile->saf_ConnHandlerCtx; + SpxAddrFileLockReference(pSpxAddrFile, AFREF_INDICATION); + CTEFreeLock(&pSpxAddr->sa_Lock, lockHandle); + addrLock = FALSE; + + if (pSpxRefFile) + { + SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION); + pSpxRefFile = NULL; + } + + // Make the indication. We are always returned an accept irp on + // indication. Else we fail to accept the connection. + status = (*connHandler)( + connHandlerCtx, + sizeof(srcIpxAddr), + (PVOID)&srcIpxAddr, + 0, // User data length + NULL, // User data + 0, // Option length + NULL, // Options + &connCtx, + &acceptIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConn: indicate status %lx.%lx\n", + status, acceptIrp)); + + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + addrLock = TRUE; + pSpxAddrFile->saf_Flags &= ~SPX_ADDRFILE_CONNIND; + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + CTEAssert(acceptIrp != NULL); + + // Find the connection and accept the connection using that + // connection object. + SpxConnFileReferenceByCtxLock( + pSpxAddrFile, + connCtx, + &pSpxConnFile, + &status); + + if (!NT_SUCCESS(status)) + { + // The connection object is closing, or is not found + // in our list. The accept irp must have had the same + // connection object. AFD isnt behaving well. + KeBugCheck(0); + } + + // Only for debugging. + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_BYCTX, + CFREF_VERIFY); + + pListenReq = SpxAllocateRequest( + SpxDevice, + acceptIrp); + + IF_NOT_ALLOCATED(pListenReq) + { + acceptIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + // Setup for dereference + pSpxRefFile = pSpxAddrFile; + break; + } + + InsertTailList( + &pSpxConnFile->scf_ReqLinkage, + REQUEST_LINKAGE(pListenReq)); + + // Setup for dereference + pSpxRefFile = pSpxAddrFile; + connectAccepted = TRUE; + + // See if this connection is to be a spx2 connection. + SPX_CONN_RESETFLAG(pSpxConnFile, + (SPX_CONNFILE_SPX2 | + SPX_CONNFILE_NEG | + SPX_CONNFILE_STREAM)); + + if ((pSpxAddrFile->saf_Flags & SPX_ADDRFILE_SPX2) && fSpx2) + { + SPX_CONN_SETFLAG( + pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + } + else + { + fSpx2 = fNeg = FALSE; + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_STREAM) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_STREAM); + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_NOACKWAIT) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: NOACKWAIT requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT); + } + + if (pSpxAddrFile->saf_Flags & SPX_ADDRFILE_IPXHDR) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleConnReq: IPXHDR requested %lx\n", + pSpxConnFile)); + + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR); + } + + break; + } + else + { + // We are not going to accept the connection on this address. + // Try next one. + pSpxRefFile = pSpxAddrFile; + continue; + } + } + } + + } while (FALSE); + + if (addrLock) + { + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + // No need for flag from this point on. + // addrLock = FALSE; + } + + if (pSpxRefFile) + { + SpxAddrFileDereference(pSpxRefFile, AFREF_INDICATION); + pSpxRefFile = NULL; + } + + if (connectAccepted) + { + CTEGetLock (&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock (&pSpxAddr->sa_Lock, &lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + if (((USHORT)PARAM(CONFIG_WINDOW_SIZE) == 0) || + ((USHORT)PARAM(CONFIG_WINDOW_SIZE) > MAX_WINDOW_SIZE)) + { + PARAM(CONFIG_WINDOW_SIZE) = DEFAULT_WINDOW_SIZE; + } + + pSpxConnFile->scf_LocalConnId = spxConnGetId(); + pSpxConnFile->scf_RemConnId = srcConnId; + pSpxConnFile->scf_SendSeqNum = 0; + pSpxConnFile->scf_RecvSeqNum = 0; + pSpxConnFile->scf_RecdAckNum = 0; + pSpxConnFile->scf_RetrySeqNum = 0; + pSpxConnFile->scf_SentAllocNum = (USHORT)(PARAM(CONFIG_WINDOW_SIZE) - 1); + pSpxConnFile->scf_RecdAllocNum = allocNum; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleConnReq: %lx CONN L.R %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RemConnId)); + + pSpxConnFile->scf_LocalTarget = *pRemoteAddr; + pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr; + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); + + // Set max packet size in connection + SPX_MAX_PKT_SIZE(pSpxConnFile, (fSpx2 && fNeg), fSpx2, pIpxSpxHdr->hdr_SrcNet); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleConnReq: Accept connect req on %lx.%lx..%lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize)); + + // Aborts must now deal with the lists. Need this as Accept has to + // deal with it. + // Put in non-inactive list. All processing now is equivalent to + // that when a listen is completed on a connection. + if ((!tdiListen) && (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxAddr->sa_InactiveConnList, + pSpxConnFile)))) + { + // Should never happen! + KeBugCheck(0); + } + + SPX_INSERT_ADDR_ACTIVE(pSpxAddr, pSpxConnFile); + + // Insert in the global connection tree on device. + spxConnInsertIntoGlobalActiveList( + pSpxConnFile); + + SPX_CONN_SETFLAG(pSpxConnFile, + ((fNeg ? SPX_CONNFILE_NEG : 0) | + (fSpx2 ? SPX_CONNFILE_SPX2: 0))); + + // + // If this was a post-inactivated file, clear the disconnect flags + // + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + + SPX_DISC_SETSTATE(pSpxConnFile, 0); + } +#if 0 + // + // Make sure that this connection got a local disconnect if it was an SPXI + // connection earlier, in response to a TDI_DISCONNECT_RELEASE. + // + + CTEAssert(pSpxConnFile->scf_RefTypes[CFREF_DISCWAITSPX] == 0); +#endif + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_LISTENING); + SPX_LISTEN_SETSTATE(pSpxConnFile, (delayAccept ? SPX_LISTEN_RECDREQ : 0)); + + if (!delayAccept) + { + spxConnAcceptCr( + pSpxConnFile, + pSpxAddr, + lockHandleDev, + lockHandle, + lockHandleConn); + } + else + { + // Release the locks. + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, lockHandle); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + + // Complete the listen irp. Note reference is not removed. Done when + // accept is posted. + SpxCompleteRequest(pListenReq); + } + } else { + ++SpxDevice->dev_Stat.NoListenFailures; + } + + // Deref the address + SpxAddrDereference (pSpxAddr, AREF_LOOKUP); + return; +} + + + + +VOID +spxConnHandleSessPktFromClient( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + Packet received from the client side of the connection. + Handles: + Session Negotiate + Sends Session Setup, when recd, handles SS Ack + + STATE MACHINE: + + RR + / \ + / \ ReceivedAck(SPX1Connection) + / \ + / \--------> ACTIVE + / ^ + Send / | + ACK / | + / | + / RecvNeg/NoNeg | + / SendSS | + SA--------->SS---------------+ + ^ | SSAckRecv + | | + +-----+ + RecvNeg + + RR - Received Connect Request + SA - Sent CR Ack + SS - Sent Session Setup + + We move from SA to SS when connection is not negotiatiable and we + immediately send the SS, or when we receive negotiate packet and send the neg + ack and the session setup. + + Note we could receive a negotiate packet when in SS, as our ack to the + negotiate could have been dropped. We deal with this. + +Arguments: + + +Return Value: + + +--*/ + +{ + PNDIS_PACKET pSnAckPkt, pSsPkt = NULL; + PSPX_SEND_RESD pSendResd, pSsSendResd; + USHORT srcConnId, destConnId, + pktLen, seqNum, negSize, ackNum, allocNum; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN locksHeld = FALSE; + + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // If spx2 we convert neg size field too + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) + { + GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize); + CTEAssert(negSize > 0); + } + + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromClient: %lx\n", pSpxConnFile)); + + // Check substate + switch (SPX_LISTEN_STATE(pSpxConnFile)) + { + case SPX_LISTEN_RECDREQ: + + // Do nothing. + break; + + case SPX_LISTEN_SETUP: + + // Is this a setup ack? If so, yippee. Our ack to a negotiate packet + // could have been dropped, and so we could also get a negotiate packet + // in that case. If that happens, fall through. + // Verify Ss Ack + if (!SPX2_CONN(pSpxConnFile) || + (pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_SPX2)) != + (SPX_CC_SYS | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0)) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: VerifySSACK Failed Checking SN %lx.%lx\n", + srcConnId, destConnId)); + + // Fall through to see if this is a neg packet + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG))) + { + break; + } + } + else + { + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Recd SSACK %lx\n", + pSpxConnFile)); + + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + case SPX_LISTEN_SENTACK: + + // We expect a negotiate packet. + // We should have asked for SPX2/NEG to begin with. + // Verify Sn + if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) != + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0) || + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX))) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySN Failed %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + // Remember max packet size in connection. + pSpxConnFile->scf_MaxPktSize = negSize; + CTEAssert(negSize > 0); + + // Build sn ack, abort if we fail + SpxPktBuildSnAck( + pSpxConnFile, + &pSnAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY); + + if (pSnAckPkt == NULL) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Sending SNACK %lx\n", + pSpxConnFile)); + + // Queue in the packet. + SpxConnQueueSendPktTail(pSpxConnFile, pSnAckPkt); + + // The session packet should already be on queue. + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SS, + FALSE, + &pSsPkt)) + { + KeBugCheck(0); + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromClient: Sending SS %lx\n", + pSpxConnFile)); + + pSsSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + + // We need to resend the packet + if ((pSsSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Try next time. + pSsPkt = NULL; + } + else + { + // Set the size to the neg size indicated in connection. + // This could be lower than the size the packet was build + // with originally. But will never be higher. + pSsSendResd->sr_State |= SPX_SENDPKT_IPXOWNS; + spxConnSetNegSize( + pSsPkt, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + } + + // If we are actually LISTEN_SETUP, then send the ss packet also. + // We need to start the connect timer to resend the ss pkt. + if (SPX_LISTEN_STATE(pSpxConnFile) == SPX_LISTEN_SENTACK) + { + if ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + SPX_LISTEN_SETSTATE(pSpxConnFile, SPX_LISTEN_SETUP); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + } + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + locksHeld = FALSE; + + // Send ack packet + pSendResd = (PSPX_SEND_RESD)(pSnAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSnAckPkt, pSendResd); + + // If we have to send the session setup packet, send that too. + if (pSsPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd); + } + + break; + + default: + + // Ignore + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: UNKNOWN %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + return; +} + + + + +VOID +spxConnHandleSessPktFromSrv( + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + Packet received from the server side of the connection. This will both + release the lock and dereference the connection as it sees fit. + + STATE MACHINE: + + SR--CTimerExpires-->IDLE + /| \ + / | \ ReceivedAck(SPX1Connection) + / | \ + / | \--------> ACTIVE + (Neg) / | ^ + Send / |RecvAck | + SN / |NoNeg | + / | | + / | | + / v | + SN--------->WS---------------+ + RecvSNAck RecvSS + + SR - Sent Connect request + SN - Sent Session Negotiate + WS - Waiting for session setup packet + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + BOOLEAN fNeg, fSpx2; + USHORT srcConnId, destConnId, + pktLen, seqNum, negSize, ackNum, allocNum; + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + BOOLEAN cTimerCancelled = FALSE, fAbort = FALSE, locksHeld = FALSE; + PNDIS_PACKET pSsAckPkt, pSnPkt, pPkt = NULL; + + // We should get a CR Ack, or if our substate is sent session neg + // we should get a session neg ack, or if we are waiting for session + // setup, we should get one of those. + + fSpx2 = (BOOLEAN)(pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2); + fNeg = (BOOLEAN)(fSpx2 && (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_NEG)); + + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + // If spx2 we convert neg size field too + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) + { + GETSHORT2SHORT(&negSize, &pIpxSpxHdr->hdr_NegSize); + CTEAssert(negSize > 0); + } + + // Grab all three locks + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + locksHeld = TRUE; + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: %lx\n", pSpxConnFile)); + + // Check substate + switch (SPX_CONNECT_STATE(pSpxConnFile)) + { + case SPX_CONNECT_SENTREQ: + + // Check if this qualifies as the ack. + // Verify CR Ack + if ((pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (seqNum != 0) || + (ackNum != 0) || + ((pktLen != MIN_IPXSPX_HDRSIZE) && + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen != MIN_IPXSPX2_HDRSIZE))) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX)))) + { + DBGPRINT(CONNECT, ERR, + ("spxConnHandleSessPktFromSrv: CRAck Invalid %lx %lx.%lx.%lx\n", + pSpxConnFile, + pktLen, negSize, pIpxSpxHdr->hdr_ConnCtrl)); + + break; + } + + // !!!BUGBUG!!! + // From current spx code base: + // Do we need to send an ack to this ack? In case of SPX only? + // What if this ack is dropped? We need to send an ack, if in future + // we get CONNECT REQ Acks, until we reach active? + // * If they want an ack schedule it. The normal case is for this not + // * to happen, but some Novell mainframe front ends insist on having + // * this. And technically, it is OK for them to do this. + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: Recd CRACK %lx\n", pSpxConnFile)); + + // Grab the remote alloc num/conn id (in net format) + pSpxConnFile->scf_SendSeqNum = 0; + pSpxConnFile->scf_RecvSeqNum = 0; + pSpxConnFile->scf_RecdAckNum = 0; + pSpxConnFile->scf_RemConnId = srcConnId; + pSpxConnFile->scf_RecdAllocNum = allocNum; + + // If we have been looking for network 0, which means the + // packets were sent on all NIC IDs, update our local + // target now that we have received a response. + +#if defined(_PNP_POWER) + if (pSpxConnFile->scf_LocalTarget.NicHandle.NicId == 0) { +#else + if (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) { +#endif _PNP_POWER + pSpxConnFile->scf_LocalTarget = *pRemoteAddr; + pSpxConnFile->scf_AckLocalTarget= *pRemoteAddr; + } + + DBGPRINT(CONNECT, INFO, + ("spxConnHandleSessPktFromSrv: %lx CONN L.R %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RemConnId)); + + if (!fSpx2 || !fNeg) + { + cTimerCancelled = SpxTimerCancelEvent( + pSpxConnFile->scf_CTimerId, FALSE); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + fAbort = TRUE; + break; + } + + // Reference transferred to watchdog timer. + if (cTimerCancelled) + { + cTimerCancelled = FALSE; + } + else + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + // Set max packet size, assume not spx2 or !neg, so pass in FALSE + SPX_MAX_PKT_SIZE(pSpxConnFile, FALSE, FALSE, pIpxSpxHdr->hdr_SrcNet); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPSrv: Accept connect req on %lx.%lx.%lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_LocalConnId, + pSpxConnFile->scf_RecdAllocNum, pSpxConnFile->scf_MaxPktSize)); + + if (!fSpx2) + { + // Reset spx2 flags. + SPX_CONN_RESETFLAG(pSpxConnFile, (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)); + + // Complete connect request, this free the lock. + // Cancels tdi timer too. Sets all necessary flags. + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + break; + } + + if (!fNeg) + { + // Goto W_SETUP + // Reset all connect related flags, also spx2/neg flags. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_NEG); + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP); + break; + } + + // Reset max packet size. SPX2 and NEG. + SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, pIpxSpxHdr->hdr_SrcNet); + + CTEAssert(negSize > 0); + CTEAssert(pSpxConnFile->scf_MaxPktSize > 0); + pSpxConnFile->scf_MaxPktSize = + MIN(negSize, pSpxConnFile->scf_MaxPktSize); + + pSpxConnFile->scf_MaxPktSize = (USHORT) + MIN(pSpxConnFile->scf_MaxPktSize, PARAM(CONFIG_MAX_PACKET_SIZE)); + + // For SPX2 with negotiation, we set up sneg packet and move to + // SPX_CONNECT_NEG. + SpxPktBuildSn( + pSpxConnFile, + &pSnPkt, + SPX_SENDPKT_IPXOWNS); + + if (pSnPkt == NULL) + { + fAbort = TRUE; + break; + } + + // Queue in packet + SpxConnQueueSendPktTail(pSpxConnFile, pSnPkt); + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Sending SN %lx\n", + pSpxConnFile)); + + // Reset retry count for connect timer + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + // Change state. + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_NEG); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + locksHeld = FALSE; + + // Send the packet + pSendResd = (PSPX_SEND_RESD)(pSnPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSnPkt, pSendResd); + break; + + case SPX_CONNECT_NEG: + + // We expect a session neg ack. + // We should have asked for SPX2/NEG to begin with. + // Verify SN Ack + if (((pSpxConnFile->scf_Flags & (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) != + (SPX_CONNFILE_SPX2 | SPX_CONNFILE_NEG)) || + (pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) != + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySNACK Failed %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Recd SNACK %lx %lx.%lx\n", + pSpxConnFile, negSize, pSpxConnFile->scf_MaxPktSize)); + + if (negSize > pSpxConnFile->scf_MaxPktSize) + negSize = pSpxConnFile->scf_MaxPktSize; + + // Get the size to use + if (negSize <= pSpxConnFile->scf_MaxPktSize) + { + pSpxConnFile->scf_MaxPktSize = negSize; + if (!spxConnGetPktByType( + pSpxConnFile, + SPX_TYPE_SN, + FALSE, + &pPkt)) + { + KeBugCheck(0); + } + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0) + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + else + { + // Free the negotiate packet + SpxPktSendRelease(pPkt); + } + + CTEAssert(pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER); + cTimerCancelled = SpxTimerCancelEvent( + pSpxConnFile->scf_CTimerId, FALSE); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + + // Start the watchdog timer, if fail, we abort. + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) == 0) + { + // Complete cr with error. + fAbort = TRUE; + break; + } + + // Reference goes to watchdog timer. + if (cTimerCancelled) + { + cTimerCancelled = FALSE; + } + else + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + + // We move to the W_SETUP state. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + + SPX_CONNECT_SETSTATE(pSpxConnFile, SPX_CONNECT_W_SETUP); + } + + break; + + case SPX_CONNECT_W_SETUP: + + // Does this qualify as a session setup packet? + // Verify SS + if (!SPX2_CONN(pSpxConnFile) || + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2)) || + (pIpxSpxHdr->hdr_DataType != 0) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + (seqNum != 0) || + ((negSize < SPX_NEG_MIN) || + (negSize > SPX_NEG_MAX))) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: VerifySS Failed %lx.%lx, %lx %lx.%lx\n", + srcConnId, destConnId, negSize, + pIpxSpxHdr->hdr_ConnCtrl, + (SPX_CC_ACK | SPX_CC_SYS | SPX_CC_SPX2))); + + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Recd SS %lx\n", pSpxConnFile)); + + // Copy remote address over into connection (socket could change) + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); + + // Remember max packet size in connection. + pSpxConnFile->scf_MaxPktSize = negSize; + + // Build ss ack, abort if we fail + SpxPktBuildSsAck( + pSpxConnFile, + &pSsAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT); + + if (pSsAckPkt == NULL) + { + fAbort = TRUE; + break; + } + + DBGPRINT(CONNECT, DBG, + ("spxConnHandleSessPktFromSrv: Sending SSACK %lx\n", + pSpxConnFile)); + + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + + // We dont queue in the pkt as its already marked as abort. + // Queue in the packet. + // SpxConnQueueSendPktTail(pSpxConnFile, pSsAckPkt); + + // Complete connect, this releases lock. + spxConnCompleteConnect( + pSpxConnFile, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + + // Send ack packet + pSendResd = (PSPX_SEND_RESD)(pSsAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsAckPkt, pSendResd); + break; + + default: + + // Ignore + DBGPRINT(RECEIVE, DBG, + ("SpxConnSysPacket: UNKNOWN %lx.%lx\n", + srcConnId, destConnId)); + + break; + } + + if (fAbort) + { + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + lockHandleDev, + lockHandleAddr, + lockHandleConn); + + locksHeld = FALSE; + } + + if (locksHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock(pSpxConnFile->scf_AddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock(&SpxDevice->dev_Lock, lockHandleDev); + } + + if (cTimerCancelled) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This routine abort a connection (both client and server side) in the middle + of a connection establishment. + + !!! Called with connection lock held, releases lock before return !!! + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt; + PREQUEST pRequest = NULL; + int numDerefs = 0; + + + DBGPRINT(CONNECT, DBG, + ("spxConnAbortConnect: %lx\n", pSpxConnFile)); + +#if DBG + if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile)) + { + KeBugCheck(0); + } +#endif + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { // others should be counted elsewhere + ++SpxDevice->dev_Stat.LocalResourceFailures; + } + + // Free up all the packets + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + + // Cancel all timers + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_WTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + } + + // We could be called from disconnect for an accept in which case there + // will be no queued request. But we need to remove the reference if there + // is no request (an accept/listen irp) and listen state is on. + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)) + { + pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + REQUEST_STATUS(pRequest) = Status; + REQUEST_INFORMATION(pRequest) = 0; + + // Save req in conn for deref to complete. + pSpxConnFile->scf_ConnectReq = pRequest; + + numDerefs++; + } + else if (SPX_CONN_LISTENING(pSpxConnFile)) + { + numDerefs++; + } + + // Bug #20999 + // Race condition was an abort came in from timer, but the connect state + // was left unchanged. Due to an extra ref on the connection from the + // aborted cr, the state remained so, and then the cr ack came in, and + // a session neg was built and queued on the connection. Although it should + // not have been. And we hit the assert in deref where the connection is + // being reinitialized. Since this can be called for both listening and + // connecting connections, do the below. + SPX_LISTEN_SETSTATE(pSpxConnFile, 0); + if (SPX_CONN_CONNECTING(pSpxConnFile)) + { + SPX_CONNECT_SETSTATE(pSpxConnFile, 0); + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +VOID +spxConnCompleteConnect( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This routine completes a connection (both client and server side) + !!! Called with connection lock held, releases lock before return !!! + +Arguments: + + +Return Value: + + +--*/ +{ + PREQUEST pRequest; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + DBGPRINT(CONNECT, INFO, + ("spxConnCompleteConnect: %lx\n", pSpxConnFile)); + +#if DBG + if (!SPX_CONN_CONNECTING(pSpxConnFile) && !SPX_CONN_LISTENING(pSpxConnFile)) + { + DBGBRK(FATAL); + } +#endif + + // Free up all the packets + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + + // Cancel tdi connect timer if we are connecting. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_TTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_T_TIMER); + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER)) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + if (pSpxConnFile->scf_CRetryCount == (LONG)(PARAM(CONFIG_CONNECTION_COUNT))) { + ++SpxDevice->dev_Stat.ConnectionsAfterNoRetry; + } else { + ++SpxDevice->dev_Stat.ConnectionsAfterRetry; + } + + // Reset all connect related flags + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_CONNECT_SETSTATE(pSpxConnFile, 0); + break; + + case SPX_CONNFILE_LISTENING: + + if (pSpxConnFile->scf_Flags & SPX_CONNFILE_C_TIMER) + { + if (SpxTimerCancelEvent(pSpxConnFile->scf_CTimerId, FALSE)) + { + numDerefs++; + } + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + } + + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_LISTEN_SETSTATE(pSpxConnFile, 0); + break; + + default: + + KeBugCheck(0); + + } + + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_ACTIVE); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_IDLE); + + ++SpxDevice->dev_Stat.OpenConnections; + + // Initialize timer values + pSpxConnFile->scf_BaseT1 = + pSpxConnFile->scf_AveT1 = PARAM(CONFIG_INITIAL_RETRANSMIT_TIMEOUT); + pSpxConnFile->scf_DevT1 = 0; + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + pRequest = LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqLinkage.Flink); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + REQUEST_INFORMATION(pRequest) = 0; + + // When we complete the request, we essentially transfer the reference + // to the fact that the connection is active. This will be taken away + // when a Disconnect happens on the connection and we transition from + // ACTIVE to DISCONN. + // numDerefs++; + + CTEFreeLock (&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (pSpxConnFile->scf_AddrFile->saf_AddrLock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + + // Complete request + SpxCompleteRequest(pRequest); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +BOOLEAN +spxConnAcceptCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN CTELockHandle LockHandleDev, + IN CTELockHandle LockHandleAddr, + IN CTELockHandle LockHandleConn + ) +{ + PNDIS_PACKET pSsPkt, pCrAckPkt; + PSPX_SEND_RESD pSendResd; + + BOOLEAN fNeg = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG); + BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2); + + DBGPRINT(CONNECT, DBG, + ("spxConnAcceptCr: %lx.%d.%d\n", + pSpxConnFile, fSpx2, fNeg)); + + // Build and queue in packet. + SpxPktBuildCrAck( + pSpxConnFile, + pSpxAddr, + &pCrAckPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + fNeg, + fSpx2); + + if ((pCrAckPkt != NULL) && + (pSpxConnFile->scf_LocalConnId != 0)) + { + // Queue in the packet. + SpxConnQueueSendPktTail(pSpxConnFile, pCrAckPkt); + } + else + { + goto AbortConnect; + } + + + // Start the timer + if ((pSpxConnFile->scf_WTimerId = + SpxTimerScheduleEvent( + spxConnWatchdogTimer, + PARAM(CONFIG_KEEPALIVE_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER); + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + else + { + goto AbortConnect; + } + + + // We start the connect timer for retrying ss which we send out now + // if we are not negotiating. + if (fSpx2) + { + // Build the session setup packet also for spx2. + SpxPktBuildSs( + pSpxConnFile, + &pSsPkt, + (USHORT)(fNeg ? 0 : SPX_SENDPKT_IPXOWNS)); + + if (pSsPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pSsPkt); + } + else + { + goto AbortConnect; + } + + if (!fNeg) + { + if ((pSpxConnFile->scf_CTimerId = + SpxTimerScheduleEvent( + spxConnConnectTimer, + PARAM(CONFIG_CONNECTION_TIMEOUT) * HALFSEC_TO_MS_FACTOR, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_C_TIMER); + pSpxConnFile->scf_CRetryCount = PARAM(CONFIG_CONNECTION_COUNT); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + goto AbortConnect; + } + } + } + + CTEAssert((fNeg && fSpx2) || (!fSpx2 && !fNeg)); + + // For a SPX connection, we immediately become active. This happens + // in the completeConnect routine. !!Dont change it here!! + if (!fSpx2) + { + spxConnCompleteConnect( + pSpxConnFile, + LockHandleDev, + LockHandleAddr, + LockHandleConn); + } + else + { + SPX_LISTEN_SETSTATE( + pSpxConnFile, (fNeg ? SPX_LISTEN_SENTACK : SPX_LISTEN_SETUP)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + CTEFreeLock (&pSpxAddr->sa_Lock, LockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, LockHandleDev); + } + + // Send the CR Ack packet! + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pCrAckPkt, pSendResd); + + if (fSpx2 && !fNeg) + { + pSendResd = (PSPX_SEND_RESD)(pSsPkt->ProtocolReserved); + SPX_SENDPACKET(pSpxConnFile, pSsPkt, pSendResd); + } + + return(TRUE); + + +AbortConnect: + + spxConnAbortConnect( + pSpxConnFile, + STATUS_INSUFFICIENT_RESOURCES, + LockHandleDev, + LockHandleAddr, + LockHandleConn); + + return (FALSE); +} + + + +BOOLEAN +SpxConnPacketize( + IN PSPX_CONN_FILE pSpxConnFile, + IN BOOLEAN fNormalState, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + The caller needs to set the state to packetize before calling this + routine. This can be called when SEND state is RENEG also. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + + fNormalState - If true, it will assume it can release lock to send, + else, it just builds pkts without releasing lock and + releases lock at end. Used after reneg changes size. + +Return Value: + + +--*/ +{ + PLIST_ENTRY p; + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + USHORT windowSize; + ULONG dataLen; + USHORT sendFlags; + int numDerefs = 0; + BOOLEAN fFirstPass = TRUE, fSuccess = TRUE; + PREQUEST pRequest; + +#if DBG + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + fNormalState) + { + DBGBRK(FATAL); + KeBugCheck(0); + } +#endif + + // Build all of the packets. The firsttime flag is used so + // that if we get a 0 byte send, we will send it. The firsttime + // flag will be set and we will build the packet and send it. + // + // FOR SPX1, we cannot trust the remote window size. So we only send + // stuff if window size is greater than 0 *AND* we do not have any pending + // sends. Dont get in here if we are ABORT. Dont want to be handling any + // more requests. + while((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ABORT) && + ((pRequest = pSpxConnFile->scf_ReqPkt) != NULL) && + ((pSpxConnFile->scf_ReqPktSize > 0) || fFirstPass)) + { + fFirstPass = FALSE; + windowSize = pSpxConnFile->scf_RecdAllocNum - + pSpxConnFile->scf_SendSeqNum + 1; + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: WINDOW %lx for %lx\n", + windowSize, pSpxConnFile)); + + + DBGPRINT(SEND, DBG, + ("REMALLOC %lx SENDSEQ %lx\n", + pSpxConnFile->scf_RecdAllocNum, + pSpxConnFile->scf_SendSeqNum)); + + + CTEAssert(windowSize >= 0); + + // Disconnect/Orderly release is not subject to window closure. + if ((pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA) && + ((windowSize == 0) || + (!SPX2_CONN(pSpxConnFile) && + (pSpxConnFile->scf_SendSeqListHead != NULL)))) + { + break; + } + + if (pSpxConnFile->scf_ReqPktType == SPX_REQ_DATA) + { + CTEAssert(pRequest == pSpxConnFile->scf_ReqPkt); + + // Get data length + dataLen = (ULONG)MIN(pSpxConnFile->scf_ReqPktSize, + (pSpxConnFile->scf_MaxPktSize - + ((SPX2_CONN(pSpxConnFile) ? + MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)))); + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: %lx Sending %lx Size %lx Req %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_SendSeqNum, + dataLen, + pSpxConnFile->scf_ReqPkt, + pSpxConnFile->scf_ReqPktSize)); + + // Build data packet. Handles 0-length for data. Puts in seq num in + // send resd section of packet also. + sendFlags = + (USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) | + SPX_SENDPKT_REQ | + SPX_SENDPKT_SEQ | + ((!SPX2_CONN(pSpxConnFile) || (windowSize == 1)) ? + SPX_SENDPKT_ACKREQ : 0)); + + if (dataLen == pSpxConnFile->scf_ReqPktSize) + { + // Last packet of send, ask for a ack. + sendFlags |= (SPX_SENDPKT_LASTPKT | SPX_SENDPKT_ACKREQ); + if ((pSpxConnFile->scf_ReqPktFlags & TDI_SEND_PARTIAL) == 0) + sendFlags |= SPX_SENDPKT_EOM; + } + + SpxPktBuildData( + pSpxConnFile, + &pPkt, + sendFlags, + (USHORT)dataLen); + } + else + { + dataLen = 0; + + DBGPRINT(SEND, DBG, + ("Building DISC packet on %lx ReqPktSize %lx\n", + pSpxConnFile, pSpxConnFile->scf_ReqPktSize)); + + // Build informed disc/orderly rel packet, associate with request + SpxPktBuildDisc( + pSpxConnFile, + pRequest, + &pPkt, + (USHORT)((fNormalState ? SPX_SENDPKT_IPXOWNS : 0) | SPX_SENDPKT_REQ | + SPX_SENDPKT_SEQ | SPX_SENDPKT_LASTPKT), + (UCHAR)((pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL) ? + SPX2_DT_ORDREL : SPX2_DT_IDISC)); + } + + if (pPkt != NULL) + { + // If we were waiting to send an ack, we don't have to as we are + // piggybacking it now. Cancel ack timer, get out. + if (fNormalState && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: Piggyback happening for %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // We are sending data, allow piggybacks to happen. + SPX_CONN_RESETFLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + if (SpxTimerCancelEvent(pSpxConnFile->scf_ATimerId, FALSE)) + { + numDerefs++; + } + } + + if (pSpxConnFile->scf_ReqPktType != SPX_REQ_DATA) + { + // For a disconnect set the state + if (pSpxConnFile->scf_ReqPktType == SPX_REQ_ORDREL) + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL); + numDerefs++; + } + else if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_ORDREL) + { + CTEAssert((SPX_MAIN_STATE(pSpxConnFile) == + SPX_CONNFILE_ACTIVE) || + (SPX_MAIN_STATE(pSpxConnFile) == + SPX_CONNFILE_DISCONN)); + + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_ORDREL); + } + else + { + CTEAssert( + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL)); + } + } + else + { + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN); + CTEAssert(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_POST_IDISC); + + // Note we have send the idisc here. + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_SENT_IDISC); + } + } + } + else + { + fSuccess = FALSE; + break; + } + + + // Queue in packet, reference request for the packet + SpxConnQueueSendSeqPktTail(pSpxConnFile, pPkt); + REQUEST_INFORMATION(pRequest)++; + + pSpxConnFile->scf_ReqPktSize -= dataLen; + pSpxConnFile->scf_ReqPktOffset += dataLen; + + DBGPRINT(SEND, INFO, + ("SpxConnPacketize: Req %lx Size after pkt %lx.%lx\n", + pSpxConnFile->scf_ReqPkt, pSpxConnFile->scf_ReqPktSize, + dataLen)); + + // Even if window size if zero, setup next request is current one + // is done. We are here only after we have packetized this send req. + if (pSpxConnFile->scf_ReqPktSize == 0) + { + // This request has been fully packetized. Either go on to + // next request or we are done packetizing. + p = REQUEST_LINKAGE(pRequest); + if (p->Flink == &pSpxConnFile->scf_ReqLinkage) + { + DBGPRINT(SEND, INFO, + ("SpxConnPacketize: Req %lx done, no more\n", + pRequest)); + + pSpxConnFile->scf_ReqPkt = NULL; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktOffset = 0; + pRequest = NULL; + } + else + { + pRequest = LIST_ENTRY_TO_REQUEST(p->Flink); + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + DBGPRINT(SEND, DBG, + ("SpxConnPacketize: Req done, setting next %lx.%lx\n", + pRequest, pParam->SendLength)); + + DBGPRINT(SEND, INFO, + ("-%lx-\n", + pRequest)); + + // Set parameters in connection for another go. + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + if ((pSpxConnFile->scf_ReqPktSize = pParam->SendLength) == 0) + { + // Another zero length send. + fFirstPass = TRUE; + } + } + else + { + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + + pParam = + (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + pSpxConnFile->scf_ReqPkt = pRequest; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + fFirstPass = TRUE; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL; + } + } + } + } + + if (fNormalState) + { + // Send the packet if we are not at the reneg state + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + ++SpxDevice->dev_Stat.DataFramesSent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesSent, + dataLen); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Check if retry timer needs to be started. + if (!(SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER))) + { + if ((pSpxConnFile->scf_RTimerId = + SpxTimerScheduleEvent( + spxConnRetryTimer, + pSpxConnFile->scf_BaseT1, + pSpxConnFile)) != 0) + { + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER); + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + // Reference connection for the timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + } + else + { + DBGPRINT(SEND, ERR, + ("SpxConnPacketize: Failed to start retry timer\n")); + + fSuccess = FALSE; + break; + } + } + } + + // Dont overwrite an error state. + if (((fNormalState) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE)) || + ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) && + (pSpxConnFile->scf_SendSeqListHead == NULL))) + { + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) + { + DBGPRINT(SEND, ERR, + ("COULD NOT PACKETIZE AFTER RENEG %lx\n", pSpxConnFile)); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(fSuccess); +} + + + + +VOID +SpxConnQueueRecv( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PTDI_REQUEST_KERNEL_RECEIVE pParam; + NTSTATUS status = STATUS_PENDING; + + if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + pParam = (PTDI_REQUEST_KERNEL_RECEIVE)REQUEST_PARAMETERS(pRequest); + pSpxConnFile->scf_CurRecvReq = pRequest; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = pParam->ReceiveLength; + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnQueueRecv: %lx.%lx\n", pRequest, pParam->ReceiveLength)); + + // Reference connection for this recv. + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + InsertTailList( + &pSpxConnFile->scf_RecvLinkage, + REQUEST_LINKAGE(pRequest)); + + // RECV irps have no creation references. + REQUEST_INFORMATION(pRequest) = 0; + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + + // State to receive_posted if we are idle. + if (SPX_RECV_STATE(pSpxConnFile) == SPX_RECV_IDLE) + { + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_POSTED); + } + + return; +} + + + + +VOID +spxConnCompletePended( + IN PSPX_CONN_FILE pSpxConnFile + ) +{ + CTELockHandle lockHandleInter; + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + + InitializeListHead(&ReqList); + + DBGPRINT(RECEIVE, DBG, + ("spxConnCompletePended: PENDING RECV REQUESTS IN DONE LIST! %lx\n", + pSpxConnFile)); + + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + p = pSpxConnFile->scf_RecvDoneLinkage.Flink; + while (p != &pSpxConnFile->scf_RecvDoneLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + DBGPRINT(TDI, DBG, + ("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + +#if DBG + if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_RECEIVE) + { + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } + } +#endif + + SpxCompleteRequest(pRequest); + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +VOID +SpxConnQWaitAck( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + // If we are not already in ack queue, queue ourselves in starting + // ack timer. + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ACKQ)) + { + // First start ack timer. + if ((pSpxConnFile->scf_ATimerId = + SpxTimerScheduleEvent( + spxConnAckTimer, + 100, + pSpxConnFile)) != 0) + { + // Reference connection for timer + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_ACKQ); + ++SpxDevice->dev_Stat.PiggybackAckQueued; + } + } + + return; +} + + + + + +VOID +SpxConnSendAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + DBGPRINT(SEND, DBG, + ("spxConnSendAck: ACKING on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // Build an ack packet, queue it in non-sequenced queue. Only if we are + // active. + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + FALSE, + 0); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendAck: Could not allocate!\n")); + } + } +#if DBG + else + { + DBGPRINT(SEND, DBG, + ("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n")); + } +#endif + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Send it. + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +SpxConnSendNack( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT NumToSend, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + DBGPRINT(SEND, DBG, + ("spxConnSendNack: NACKING on %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_RecvSeqNum)); + + // Build an nack packet, queue it in non-sequenced queue. Only if we are + // active. + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + TRUE, + NumToSend); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendAck: Could not allocate!\n")); + } + } +#if DBG + else + { + DBGPRINT(SEND, DBG, + ("SpxConnSendAck: WHEN NOT ACTIVE STATE@!@\n")); + } +#endif + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Send it. + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + + +BOOLEAN +SpxConnProcessAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PREQUEST pRequest; + PSPX_SEND_RESD pSendResd; + CTELockHandle interLockHandle; + USHORT seqNum = 0, ackNum; + int numDerefs = 0; + BOOLEAN fLastPkt, lockHeld = TRUE, fAbort = FALSE, + fResetRetryTimer, fResendPkt = FALSE, fResetSendQueue = FALSE; + + if (pIpxSpxHdr != NULL) + { + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + + // Ack numbers should already be set in connection! + if (SPX2_CONN(pSpxConnFile)) + { + switch (SPX_SEND_STATE(pSpxConnFile)) + { + case SPX_SEND_RETRYWD: + + // Did we receive an ack for pending data? If so, we goto + // idle and process the ack. + if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: Data acked RETRYWD %lx.%lx!\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + else + { + // Ok, we received an ack for our probe retry, goto + // renegotiate packet size. + // For this both sides must have negotiated size to begin with. + // If they did not, we go on to retrying the data packet. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG)) + { + pSpxConnFile->scf_RRetryCount = SPX_DEF_RENEG_RETRYCOUNT; + if ((ULONG)pSpxConnFile->scf_MaxPktSize <= + (SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)) + { + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx MIN RENEG SIZE\n", + pSpxConnFile)); + } + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", + pSpxConnFile)); + } + else + { + DBGPRINT(SEND, ERR, + ("spxConnRetryTimer: NO NEG FLAG SET: %lx - %lx\n", + pSpxConnFile, + pSpxConnFile->scf_Flags)); + + // Reset count to be + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + } + } + + break; + + case SPX_SEND_RENEG: + + // We better have a data packet in the list. + CTEAssert(pSpxConnFile->scf_SendSeqListHead); + +#if DBG + if ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx.%lx.%lx RENEGACK SEQNUM %lx ACKNUM %lx EXPSEQ %lx\n", + pSpxConnFile, + pIpxSpxHdr->hdr_ConnCtrl, + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT), + seqNum, + ackNum, + (pSpxConnFile->scf_SendSeqListHead->sr_SeqNum + 1))); + } +#endif + + // Verify we received an RR ack. If so, we set state to + // SEND_RETRY3. First repacketize if we need to. + if ((SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT)) && + ((pIpxSpxHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2))) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: RENEG! NEW %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + // Dont allow anymore reneg packet acks to be looked at. + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RENEG_PKT); + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + + // Also set the new send sequence number. + pSpxConnFile->scf_SendSeqNum = + (USHORT)(pSpxConnFile->scf_SendSeqListTail->sr_SeqNum + 1); + + // Get the max packet size we will really use. Retry timer + // could have sent other sizes by now, so we can't depend + // on whats set. + // Remember max packet size in connection. + GETSHORT2SHORT( + &pSpxConnFile->scf_MaxPktSize, &pIpxSpxHdr->hdr_NegSize); + + // Basic sanity checking on the max packet size. + if (pSpxConnFile->scf_MaxPktSize < SPX_NEG_MIN) + pSpxConnFile->scf_MaxPktSize = SPX_NEG_MIN; + + // Get ready to reset the send queue. + fResetSendQueue = TRUE; + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: RENEG DONE : RETRY3 %lx.%lx MP %lx!\n", + pSpxConnFile, + pSpxConnFile->scf_SendSeqNum, + pSpxConnFile->scf_MaxPktSize)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RETRY3); + } + else + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: DUPLICATE RENEG ACK %lx!\n", + pSpxConnFile)); + } + + break; + + case SPX_SEND_RETRY: + case SPX_SEND_RETRY2: + case SPX_SEND_RETRY3: + + if (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: Data acked %lx.%lx!\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + +#if DBG + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_RETRY3) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: CONN RESTORED %lx.%lx!\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + } +#endif + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + break; + + case SPX_SEND_WD: + + // Ok, we received an ack for our watchdog. Done. + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + numDerefs++; + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + break; + + default: + + break; + } + +#if DBG + if (seqNum != 0) + { + // We have a nack, which contains an implicit ack. + // Instead of nack processing, what we do is we resend a + // packet left unacked after ack processing. ONLY if we + // either enter the loop below (fResetRetryTimer is FALSE) + // or if seqNum is non-zero (SPX2 only NACK) + } +#endif + } + } + + // Once our numbers are updated, we check to see if any of our packets + // have been acked. + fResetRetryTimer = TRUE; + while (((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + ((SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) || + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_PACKETIZE) || + fResetSendQueue) && + (UNSIGNED_GREATER_WITH_WRAP( + pSpxConnFile->scf_RecdAckNum, + pSendResd->sr_SeqNum))) + { + // Reset retry timer + if (fResetRetryTimer) + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_R_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_RTimerId, + TRUE); + + pSpxConnFile->scf_RRetryCount = PARAM(CONFIG_REXMIT_COUNT); + } + + fResetRetryTimer = FALSE; + } + + // Update the retry seq num. + pSpxConnFile->scf_RetrySeqNum = pSendResd->sr_SeqNum; + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pRequest = pSendResd->sr_Request; + +#if DBG + if (fResetSendQueue) + { + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: Data acked RENEG %lx.%lx!\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + } +#endif + + DBGPRINT(SEND, DBG, + ("%lx Acked\n", pSendResd->sr_SeqNum)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: %lx Seq %lx Acked Sr %lx Req %lx %lx.%lx\n", + pSpxConnFile, + pSendResd->sr_SeqNum, + pSendResd, + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // If this packet is the last one comprising this request, remove request + // from queue. Calculate retry time. + fLastPkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_LASTPKT) != 0); + if ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) && + ((pSendResd->sr_State & SPX_SENDPKT_REXMIT) == 0) && + ((pSendResd->sr_SeqNum + 1) == pSpxConnFile->scf_RecdAckNum)) + { + LARGE_INTEGER li, ntTime; + int value; + + // This is the packet which is being acked. Adjust round trip + // timer. + li = pSendResd->sr_SentTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new time + SpxCalculateNewT1(pSpxConnFile, value); + } + } + + if (fLastPkt) + { + // Set status + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Remove creation reference + --(REQUEST_INFORMATION(pRequest)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: LASTSEQ # %lx for Req %lx with %lx.%lx\n", + pSendResd->sr_SeqNum, + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + } + + // Dequeue the packet + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0); + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Dereference request for the dequeing of the packet + --(REQUEST_INFORMATION(pRequest)); + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Packet owned by IPX. What do we do now? Set acked pkt so request + // gets dereferenced in send completion. Note that the packet is already + // off the queue and is floating at this point. + + DBGPRINT(SEND, DBG, + ("SpxConnProcessAck: IPXOWNS Pkt %lx with %lx.%lx\n", + pPkt, pRequest, REQUEST_STATUS(pRequest))); + + pSendResd->sr_State |= SPX_SENDPKT_ACKEDPKT; + } + + if (SPX2_CONN(pSpxConnFile) && + (REQUEST_MINOR_FUNCTION(pRequest) == TDI_DISCONNECT) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_ORDREL)) + { + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_ORDREL_ACKED); + + // If we had received an ordrel in the meantime, we need + // to disconnect. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) + { + fAbort = TRUE; + } + } + + // All packets comprising a request have been acked! + if (REQUEST_INFORMATION(pRequest) == 0) + { + CTELockHandle lockHandleInter; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND) + REQUEST_PARAMETERS(pRequest); + + REQUEST_INFORMATION(pRequest) = pParam->SendLength; + + DBGPRINT(SEND, DBG, + ("SpxSendComplete: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + + } + } +#if DBG + else if (fLastPkt) + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessAck: ReqFloating %lx.%lx\n", + pSpxConnFile, pRequest)); + } +#endif + } + + // See if we reset the send queue and repacketize. + if (fResetSendQueue) + { + // Reset send queue and repacketize only if pkts left unacked. + if (pSpxConnFile->scf_SendSeqListHead) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Resetting send queue %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + spxConnResetSendQueue(pSpxConnFile); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Repacketizing %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + SpxConnPacketize(pSpxConnFile, FALSE, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + else + { + // We just go back to idle state now. + DBGPRINT(SEND, ERR, + ("SpxConnProcessAck: All packets acked reneg ack! %lx.%lx!\n", + pSpxConnFile, pSpxConnFile->scf_MaxPktSize)); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + numDerefs++; + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + } + } + + // See if we resend a packet. + if ((seqNum != 0) && + !fAbort && + ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0)) + { + PIPXSPX_HDR pSendHdr; + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + // Set ack bit in packet. pSendResd initialized at beginning. + pSendHdr->hdr_ConnCtrl |= SPX_CC_ACK; + + // We are going to resend this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_ACKREQ | + SPX_SENDPKT_REXMIT); + + fResendPkt = TRUE; + } + + // Push into packetize only if we received an ack. And if there arent any + // packets already waiting. Probably retransmit happening. + if (!fAbort && + SPX_CONN_ACTIVE(pSpxConnFile) && + (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) && + (pSpxConnFile->scf_ReqPkt != NULL) && + (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_PKTQ)) && + ((pSpxConnFile->scf_SendSeqListHead) == NULL) && + (!SPX2_CONN(pSpxConnFile) || + ((SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_ORDREL_ACKED) && + (SPX_DISC_STATE(pSpxConnFile) != SPX_DISC_SENT_ORDREL)))) + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessAck: Recd ack pktizng\n", pSpxConnFile)); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ); + SpxConnFileLockReference(pSpxConnFile, CFREF_PKTIZE); + + CTEGetLock(&SpxGlobalQInterlock, &interLockHandle); + SPX_QUEUE_TAIL_PKTLIST(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, interLockHandle); + } + else if (fAbort) + { + // Set IDISC flag so Abortive doesnt reindicate. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (fResendPkt) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: Resend pkt on %lx.%lx\n", + pSpxConnFile, pSendResd->sr_SeqNum)); + + ++SpxDevice->dev_Stat.DataFramesResent; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesResent, + pSendResd->sr_Len - (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return(TRUE); +} + + + + +VOID +SpxConnProcessRenegReq( + IN PSPX_CONN_FILE pSpxConnFile, + IN PIPXSPX_HDR pIpxSpxHdr, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + USHORT seqNum, ackNum, allocNum, maxPktSize; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + // The remote sent us a renegotiate request. We need to send an ack back + // ONLY if we have not acked a data packet with that same sequence number. + // This is guaranteed by the fact that we will not accept the reneg request + // if we have already acked a data packet with the same seq num, as our + // receive seq number would be incremented already. + // + // Note that if we have pending send packets we may end up doing a reneg + // also. + + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + GETSHORT2SHORT(&maxPktSize, &pIpxSpxHdr->hdr_PktLen); + + // If the received seq num is less than the expected receive sequence number + // we ignore this request. + if (!UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + (seqNum != pSpxConnFile->scf_RecvSeqNum)) + { + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx ERROR RENSEQ %lx RECVSEQ %lx %lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + return; + } + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx RENSEQ %lx RECVSEQ %lx MAXPKT %lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, maxPktSize)); + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + // Set RenegAckAckNum before calling buildrrack. If a previous reneg + // request was received with a greater maxpktsize, send an ack with + // that maxpktsize. + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD)) + { + pSpxConnFile->scf_RenegAckAckNum = pSpxConnFile->scf_RecvSeqNum; + pSpxConnFile->scf_RenegMaxPktSize= maxPktSize; + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_RENEGRECD); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx SENT ALLOC NUM CURRENT %lx\n", + pSpxConnFile, + pSpxConnFile->scf_SentAllocNum)); + + // Adjust sentallocnum now that recvseqnum might have moved up. + pSpxConnFile->scf_SentAllocNum += + (seqNum - pSpxConnFile->scf_RenegAckAckNum); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx SENT ALLOC NUM ADJUSTED %lx\n", + pSpxConnFile, + pSpxConnFile->scf_SentAllocNum)); + } + + // The recvseqnum for the reneg is always >= the renegackacknum. + pSpxConnFile->scf_RecvSeqNum = seqNum; + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessRenegReq: %lx RESET RECVSEQ %lx SavedACKACK %lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RenegAckAckNum)); + + // Build and send an ack. + SpxPktBuildRrAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY, + pSpxConnFile->scf_RenegMaxPktSize); + + if (pPkt != NULL) + { + SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + } +#if DBG + else + { + // Log error + DBGPRINT(SEND, ERR, + ("SpxConnSendRenegReqAck: Could not allocate!\n")); + } +#endif + + + // Check if we are an ack/nack packet in which case call process + // ack. Note that the spx2 orderly release ack is a normal spx2 ack. + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +SpxConnProcessOrdRel( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + int numDerefs = 0; + PNDIS_PACKET pPkt = NULL; + BOOLEAN lockHeld = TRUE, fAbort = FALSE; + + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + if (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ORDREL_ACKED) + { + fAbort = TRUE; + } + + // Send an ack if one was asked for. And we are done with this pkt + // Update seq numbers and stuff. + SPX_SET_RECVNUM(pSpxConnFile, FALSE); + + // Build and send an ack for this. Ordinary spx2 ack. + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT, + FALSE, + 0); + + if (pPkt != NULL) + { + // We don't queue this pkt in as we have the ABORT flag set in + // the packet, which implies the pkt is already dequeued. + // SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + + // Reference conn for the pkt. + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + // Get disconnect handler if we have one. And have not indicated + // abortive disconnect on this connection to afd. + + // + // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC)) + { + // Yeah, we set the flag regardless of whether a handler is + // present. + pDiscHandler =pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx=pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC); + } + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) + { + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + TDI_DISCONNECT_RELEASE); + } + + // We abort any receives here if !fAbort else we abort conn. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + if (fAbort) + { + // Set IDISC flag so Abortive doesnt reindicate. + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + else + { + // Go through and kill all pending requests. + spxConnAbortRecvs( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle); + + lockHeld = FALSE; + } + } + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +SpxConnProcessIDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle lockHandle + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!! + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pPkt = NULL; + + SPX_SET_RECVNUM(pSpxConnFile, FALSE); + + // Build and send an ack for the idisc. Need to modify data type + // and reset sys bit on ack. + // BUG #12344 - Fixing this led to the problem where we queue in + // the pkt below, but AbortSends could already have been called + // => this packet stays on queue without a ref, conn gets freed + // underneath, and in the sendcomplete we crash when this send + // completes. + // + // Fix is to setup this pkt as a aborted pkt to start with. + + SpxPktBuildAck( + pSpxConnFile, + &pPkt, + SPX_SENDPKT_IPXOWNS | SPX_SENDPKT_DESTROY | SPX_SENDPKT_ABORT, + FALSE, + 0); + + if (pPkt != NULL) + { + PIPXSPX_HDR pSendHdr; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + pSendHdr->hdr_ConnCtrl &= ~SPX_CC_SYS; + pSendHdr->hdr_DataType = SPX2_DT_IDISC_ACK; + + // We don't queue this pkt in as we have the ABORT flag set in + // the packet, which implies the pkt is already dequeued. + // SpxConnQueueSendPktTail(pSpxConnFile, pPkt); + + // Reference conn for the pkt. + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + // We better not have any received pkts, we ignore disconnect + // pkts when that happens. + CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + +#if DBG + if (pSpxConnFile->scf_SendSeqListHead != NULL) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: DATA/DISC %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_SendListHead, + pSpxConnFile->scf_SendSeqListHead)); + } +#endif + + // Call abortive disconnect on connection. + + // + // [SA] bug #15249 + // This is an informed disconnect, hence pass DISCONNECT_RELEASE to AFD (TRUE in last param) + // + // + // We pass true only in the case of an SPX connection. SPX2 connections follow the + // exact semantics of Informed Disconnect. + // + if (!SPX2_CONN(pSpxConnFile)) { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle, + TRUE); + } else { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_REMOTE_DISCONNECT, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); + } + + if (pPkt != NULL) + { + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + + // Send the packet + SPX_SENDACK(pSpxConnFile, pPkt, pSendResd); + } + + return; +} + + + + +VOID +spxConnResetSendQueue( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + + pSendResd = pSpxConnFile->scf_SendSeqListHead; + CTEAssert(pSendResd != NULL); + + pRequest = pSendResd->sr_Request; + + // Reset the current send request values + pSpxConnFile->scf_ReqPkt = pSendResd->sr_Request; + pSpxConnFile->scf_ReqPktOffset = pSendResd->sr_Offset; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(pRequest); + + DBGPRINT(SEND, DBG3, + ("spxConnResetSendQueue: %lx.%lx.%lx Reset SEND Req to %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2, + pRequest, pParam->SendLength)); + + // Set parameters in connection for another go. Size parameter is + // original size - offset at this point. + pSpxConnFile->scf_ReqPktFlags = pParam->SendFlags; + pSpxConnFile->scf_ReqPktSize = pParam->SendLength - + pSpxConnFile->scf_ReqPktOffset; + } + else + { + PTDI_REQUEST_KERNEL_DISCONNECT pParam; + + DBGPRINT(SEND, ERR, + ("spxConnResetSendQueue: %lx.%lx.%lx Reset DISC Req to %lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2, + pRequest)); + + DBGPRINT(SEND, ERR, + ("spxConnResetSendQueue: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + pParam = + (PTDI_REQUEST_KERNEL_DISCONNECT)REQUEST_PARAMETERS(pRequest); + + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DISC; + if (pParam->RequestFlags == TDI_DISCONNECT_RELEASE) + { + pSpxConnFile->scf_ReqPktType = SPX_REQ_ORDREL; + } + } + + DBGPRINT(SEND, DBG3, + ("spxConnResetSendQueue: Seq Num for %lx is now %lx\n", + pSpxConnFile, pSpxConnFile->scf_SendSeqNum)); + + // When we are trying to abort a pkt and it is in use by ipx, we simply let + // it float. + do + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0); + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + KeBugCheck(0); + } + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // We let send completion know that this packet is to be aborted. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + + } while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL); + + return; +} + + + + +VOID +spxConnAbortSendPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_SEND_RESD pSendResd, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + Called to abort either a sequenced or a non-sequenced packet ONLY from + send completion. + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + if ((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) + { + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0); + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + // Remove request from list its on + // BUG #11626 - request is already removed from list. + // RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Free the packet + SpxPktSendRelease(pPkt); + SpxConnFileDereference(pSpxConnFile, CFREF_ABORTPKT); + + if (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + SpxCompleteRequest(pRequest); + numDerefs++; + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortSends( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PSPX_SEND_RESD pSendResd; + PREQUEST pRequest; + PNDIS_PACKET pPkt; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + // We better be in disconnect state, abortive/informed/orderly initiate. + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN); + + // Reset the current send request values + pSpxConnFile->scf_ReqPkt = NULL; + pSpxConnFile->scf_ReqPktOffset = 0; + pSpxConnFile->scf_ReqPktSize = 0; + pSpxConnFile->scf_ReqPktType = SPX_REQ_DATA; + + // First go through the non-seq pkt queue.Just set abort flag if owned by ipx + while ((pSendResd = pSpxConnFile->scf_SendListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) == 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // Set abort flag and reference conn for the pkt if its not already. + // We only do this check for the non-sequenced packets. + // BUG #12344 (see SpxRecvDiscPacket()) + if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) == 0) + { + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + } + + // When we are trying to abort a pkt and it is in use by ipx, we simply let + // it float. + while ((pSendResd = pSpxConnFile->scf_SendSeqListHead) != NULL) + { + pPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSendResd, NDIS_PACKET, ProtocolReserved); + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0); + pRequest = pSendResd->sr_Request; + + CTEAssert(REQUEST_INFORMATION(pRequest) != 0); + + SpxConnDequeueSendPktLock(pSpxConnFile, pPkt); + if ((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) == 0) + { + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + // Remove request from list its on + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + REQUEST_INFORMATION(pRequest) = 0; + + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } + + // Free the packet + SpxPktSendRelease(pPkt); + } + else + { + // We let send completion know that this packet is to be aborted. + pSendResd->sr_State |= SPX_SENDPKT_ABORT; + SpxConnFileLockReference(pSpxConnFile, CFREF_ABORTPKT); + } + } + + // If retry timer state is on, then we need to reset and deref. + if ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_WD)) + { + DBGPRINT(SEND, DBG1, + ("spxConnAbortSends: When SEND ERROR STATE %lx.%lx\n", + pSpxConnFile, SPX_SEND_STATE(pSpxConnFile))); + + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_IDLE); + + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_ERRORSTATE, + CFREF_VERIFY); + + numDerefs++; + } + + // Remove creation references on all sends. + if (!IsListEmpty(&pSpxConnFile->scf_ReqLinkage)) + { + p = pSpxConnFile->scf_ReqLinkage.Flink; + while (p != &pSpxConnFile->scf_ReqLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // Remove request from list its on. Its complete or abort list for it. + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Aborting Send Request %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + if (REQUEST_MINOR_FUNCTION(pRequest) != TDI_DISCONNECT) + { + DBGPRINT(SEND, DBG, + ("SpxSendAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_ReqDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } + else + { + DBGPRINT(SEND, DBG1, + ("SpxSendComplete: DISC Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + + numDerefs++; + } + } +#if DBG + else + { + // Let it float, + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + SpxCompleteRequest(pRequest); + numDerefs++; + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +VOID +spxConnAbortRecvs( + IN PSPX_CONN_FILE pSpxConnFile, + IN NTSTATUS Status, + IN SPX_CALL_LEVEL CallLevel, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LIST_ENTRY ReqList, *p; + PREQUEST pRequest; + PSPX_RECV_RESD pRecvResd; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PBYTE pData; + ULONG dataLen; + int numDerefs = 0; + + InitializeListHead(&ReqList); + + // We better be in disconnect state, abortive/informed/orderly initiate. + // Reset the current receive request values + pSpxConnFile->scf_CurRecvReq = NULL; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = 0; + + // If we have any buffered data, abort it. + // Buffered data that is 0 bytes long (only eom) may not have a ndis + // buffer associated with it. + while ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) + { + if ((pSpxConnFile->scf_RecvListHead = pRecvResd->rr_Next) == NULL) + { + pSpxConnFile->scf_RecvListTail = NULL; + } + + pNdisPkt = (PNDIS_PACKET) + CONTAINING_RECORD(pRecvResd, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(RECEIVE, DBG1, + ("spxConnAbortRecvs: %lx in bufferlist on %lx\n", + pSpxConnFile, pNdisPkt)); + + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + + SpxFreeMemory(pData); + NdisFreeBuffer(pNdisBuffer); + } + + // Packet consumed. Free it up. + numDerefs++; + + // Free the ndis packet + SpxPktRecvRelease(pNdisPkt); + } + + // If packets are on this queue, they are waiting for transfer data to + // complete. Can't do much about that, just go and remove creation refs + // on the receives. + if (!IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + p = pSpxConnFile->scf_RecvLinkage.Flink; + while (p != &pSpxConnFile->scf_RecvLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // Remove request from list its on + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + + // Set status + REQUEST_STATUS(pRequest) = Status; + + DBGPRINT(RECEIVE, DBG1, + ("SpxRecvAbort: Aborting Recv Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (REQUEST_INFORMATION(pRequest) == 0) + { + DBGPRINT(RECEIVE, DBG, + ("SpxRecvAbort: QForComp Request %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + if (CallLevel == SPX_CALL_RECVLEVEL) + { + CTELockHandle lockHandleInter; + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + // If connection is not already in recv queue, put it in + // there. + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + else + { + InsertTailList( + &ReqList, + REQUEST_LINKAGE(pRequest)); + } + } +#if DBG + else + { + // Let it float, + DBGPRINT(SEND, DBG1, + ("SpxSendAbort: %lx Floating Send %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + } + } + + // Release + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + while (!IsListEmpty(&ReqList)) + { + p = RemoveHeadList(&ReqList); + pRequest = LIST_ENTRY_TO_REQUEST(p); + + numDerefs++; + + SpxCompleteRequest(pRequest); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + +#if 0 + +VOID +spxConnResendPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + USHORT startSeqNum; + BOOLEAN fLockHeld = TRUE, fDone = FALSE; + + pSendResd = pSpxConnFile->scf_SendSeqListHead; + if (pSendResd) + { + startSeqNum = pSendResd->sr_SeqNum; + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: StartSeqNum %lx for resend on %lx\n", + startSeqNum, pSpxConnFile)); + + while (spxConnGetPktBySeqNum(pSpxConnFile, startSeqNum++, &pPkt)) + { + CTEAssert(pPkt != NULL); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if (!(pSendResd->sr_State & SPX_SENDPKT_IPXOWNS)) + { + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: Pkt %lx.%lx resent on %lx\n", + pPkt, (startSeqNum - 1), pSpxConnFile)); + + // We are going to send this packet + pSendResd->sr_State |= (SPX_SENDPKT_IPXOWNS | + SPX_SENDPKT_REXMIT); + } + else + { + DBGPRINT(SEND, DBG, + ("spxConnResendPkts: Pkt %lx.%lx owned by ipx on %lx\n", + pPkt, (startSeqNum - 1), pSpxConnFile)); + break; + } + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + fLockHeld = FALSE; + + // If pkt has the ack bit set, we break. + fDone = ((pSendResd->sr_State & SPX_SENDPKT_ACKREQ) != 0); + + // Send the packet + SPX_SENDPACKET(pSpxConnFile, pPkt, pSendResd); + if (fDone) + { + break; + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + fLockHeld = TRUE; + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + return; +} +#endif diff --git a/private/ntos/tdi/isnp/spx/spxcutil.c b/private/ntos/tdi/isnp/spx/spxcutil.c new file mode 100644 index 000000000..3f85b2281 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxcutil.c @@ -0,0 +1,1738 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxcutil.c + +Abstract: + + This module contains code which implements the CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXCUTIL + +// +// Minor utility routines +// + + +BOOLEAN +spxConnCheckNegSize( + IN PUSHORT pNegSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + int i; + + // We go thru table and see if this is the minimum size or if it + // can go down further. Return true if it is not the minimum size. + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[0])); + + if ((ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE) <= SpxMaxPktSize[0]) + return(FALSE); + + for (i = SpxMaxPktSizeIndex-1; i > 0; i--) + { + DBGPRINT(CONNECT, INFO, + ("spxConnCheckNegSize: Current %lx Check Val %lx\n", + (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), + SpxMaxPktSize[i])); + + if (SpxMaxPktSize[i] < (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE)) + break; + } + + *pNegSize = (USHORT)(SpxMaxPktSize[i] + MIN_IPXSPX2_HDRSIZE); + + DBGPRINT(CONNECT, ERR, + ("spxConnCheckNegSize: Trying Size %lx Min size possible %lx\n", + *pNegSize, SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)); + + return(TRUE); +} + + + + +VOID +spxConnSetNegSize( + IN OUT PNDIS_PACKET pPkt, + IN ULONG Size + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + UINT bufCount; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + + CTEAssert(Size > 0); + NdisQueryPacket(pPkt, NULL, &bufCount, &pNdisBuffer, NULL); + CTEAssert (bufCount == 3); + + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); + NdisAdjustBufferLength(pNdisBuffer, Size); + + // Change it in send reserved + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Len = (Size + MIN_IPXSPX2_HDRSIZE); + + // Change in ipx header + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + PUTSHORT2SHORT((PUSHORT)&pIpxSpxHdr->hdr_PktLen, (Size + MIN_IPXSPX2_HDRSIZE)); + + // Change in the neg packet field of the header. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + (Size + MIN_IPXSPX2_HDRSIZE)); + + DBGPRINT(CONNECT, DBG, + ("spxConnSetNegSize: Setting size to %lx Hdr %lx\n", + Size, (Size + MIN_IPXSPX2_HDRSIZE))); + + return; +} + + + + +BOOLEAN +SpxConnDequeueSendPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, pListHead, pListTail; + PSPX_SEND_RESD pSendResd; + BOOLEAN removed = TRUE; + + // If we are sequenced or not decides which list we choose. + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pListHead = pSpxConnFile->scf_SendSeqListHead; + pListTail = pSpxConnFile->scf_SendSeqListTail; + } + else + { + pListHead = pSpxConnFile->scf_SendListHead; + pListTail = pSpxConnFile->scf_SendListTail; + } + + // Most often, we will be at the head of the list. + if (pListHead == pSendResd) + { + if ((pListHead = pSendResd->sr_Next) == NULL) + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pSendResd)); + + pListTail = NULL; + } + } + else + { + DBGPRINT(SEND, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pSendResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->sr_Next == pSendResd) + { + if ((pSr->sr_Next = pSendResd->sr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->sr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) + { + pSpxConnFile->scf_SendSeqListHead = pListHead; + pSpxConnFile->scf_SendSeqListTail = pListTail; + } + else + { + pSpxConnFile->scf_SendListHead = pListHead; + pSpxConnFile->scf_SendListTail = pListTail; + } + } + + return(removed); +} + + + + +BOOLEAN +SpxConnDequeueRecvPktLock( + IN PSPX_CONN_FILE pSpxConnFile, + IN PNDIS_PACKET pPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_RECV_RESD pSr, pListHead, pListTail; + PSPX_RECV_RESD pRecvResd; + BOOLEAN removed = TRUE; + + pRecvResd = (PSPX_RECV_RESD)(pPkt->ProtocolReserved); + pListHead = pSpxConnFile->scf_RecvListHead; + pListTail = pSpxConnFile->scf_RecvListTail; + + // Most often, we will be at the head of the list. + if (pListHead == pRecvResd) + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx first in list\n", pRecvResd)); + + if ((pListHead = pRecvResd->rr_Next) == NULL) + { + pListTail = NULL; + } + } + else + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDequeuePktLock: %lx !first in list\n", pRecvResd)); + + pSr = pListHead; + while (pSr != NULL) + { + if (pSr->rr_Next == pRecvResd) + { + if ((pSr->rr_Next = pRecvResd->rr_Next) == NULL) + { + pListTail = pSr; + } + + break; + } + + pSr = pSr->rr_Next; + } + + if (pSr == NULL) + removed = FALSE; + } + + if (removed) + { + pSpxConnFile->scf_RecvListHead = pListHead; + pSpxConnFile->scf_RecvListTail = pListTail; + } + + return(removed); +} + + + + +BOOLEAN +spxConnGetPktByType( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG PktType, + IN BOOLEAN fSeqList, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = (fSeqList ? + &pSpxConnFile->scf_SendSeqListHead : + &pSpxConnFile->scf_SendListHead); + + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_Type == PktType) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, INFO, + ("SpxConnFindByType: %lx.%lx.%d\n", pSr,*ppPkt, fSeqList)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +BOOLEAN +spxConnGetPktBySeqNum( + IN PSPX_CONN_FILE pSpxConnFile, + IN USHORT SeqNum, + IN PNDIS_PACKET * ppPkt + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSr, *ppSr; + + // Most often, we will be at the head of the list. + ppSr = &pSpxConnFile->scf_SendSeqListHead; + for (; (pSr = *ppSr) != NULL; ) + { + if (pSr->sr_SeqNum == SeqNum) + { + *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pSr, NDIS_PACKET, ProtocolReserved); + + DBGPRINT(SEND, DBG, + ("SpxConnFindBySeq: %lx.%lx.%d\n", pSr,*ppPkt, SeqNum)); + + break; + } + + ppSr = &pSr->sr_Next; + } + + return(pSr != NULL); +} + + + + +USHORT +spxConnGetId( + VOID + ) +/*++ + +Routine Description: + + This must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + BOOLEAN wrapped = FALSE; + USHORT startConnId, retConnId; + + startConnId = SpxDevice->dev_NextConnId; + + // Search the global active list. + do + { + if ((SpxDevice->dev_NextConnId >= startConnId) && wrapped) + { + retConnId = 0; + break; + } + + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + continue; + } + + // BUGBUG: Later this be a tree. + for (pSpxConnFile = SpxDevice->dev_GlobalActiveConnList[ + SpxDevice->dev_NextConnId & NUM_SPXCONN_HASH_MASK]; + pSpxConnFile != NULL; + pSpxConnFile = pSpxConnFile->scf_GlobalActiveNext) + { + if (pSpxConnFile->scf_LocalConnId == SpxDevice->dev_NextConnId) + { + break; + } + } + + // Increment for next time. + retConnId = SpxDevice->dev_NextConnId++; + + // Ensure we are still legal. We could return if connfile is null. + if (SpxDevice->dev_NextConnId == 0xFFFF) + { + wrapped = TRUE; + SpxDevice->dev_NextConnId = 1; + } + + if (pSpxConnFile != NULL) + { + continue; + } + + break; + + } while (TRUE); + + return(retConnId); +} + + + + +NTSTATUS +spxConnRemoveFromList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_Next; + break; + } + + ppRemConn = &pRemConn->scf_Next; + } + + if (pRemConn == NULL) + { + DBGBRK(FATAL); + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +NTSTATUS +spxConnRemoveFromAssocList( + IN PSPX_CONN_FILE * ppConnListHead, + IN PSPX_CONN_FILE pConnRemove + ) + +/*++ + +Routine Description: + + This routine must be called with the address lock (and the lock of the remove + connection will usually also be, but is not needed) held. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pRemConn, *ppRemConn; + NTSTATUS status = STATUS_SUCCESS; + + // Dequeue the connection file from the address list. It must be + // in the inactive list. + for (ppRemConn = ppConnListHead; + (pRemConn = *ppRemConn) != NULL;) + { + if (pRemConn == pConnRemove) + { + *ppRemConn = pRemConn->scf_AssocNext; + break; + } + + ppRemConn = &pRemConn->scf_AssocNext; + } + + if (pRemConn == NULL) + { + CTEAssert(0); + status = STATUS_INVALID_CONNECTION; + } + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + + // For now, its just a linear list. + pSpxConnFile->scf_GlobalActiveNext = + SpxDevice->dev_GlobalActiveConnList[index]; + + SpxDevice->dev_GlobalActiveConnList[index] = + pSpxConnFile; + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalActiveList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine must be called with the device lock held. + +Arguments: + + +Return Value: + + +--*/ + +{ + PSPX_CONN_FILE pC, *ppC; + int index = (int)(pSpxConnFile->scf_LocalConnId & + NUM_SPXCONN_HASH_MASK); + NTSTATUS status = STATUS_SUCCESS; + + // For now, its just a linear list. + for (ppC = &SpxDevice->dev_GlobalActiveConnList[index]; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalActiveNext; + break; + } + + ppC = &pC->scf_GlobalActiveNext; + } + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + +VOID +spxConnInsertIntoGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_GlobalNext = SpxGlobalConnList; + SpxGlobalConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +NTSTATUS +spxConnRemoveFromGlobalList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + PSPX_CONN_FILE pC, *ppC; + NTSTATUS status = STATUS_SUCCESS; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + for (ppC = &SpxGlobalConnList; + (pC = *ppC) != NULL;) + { + if (pC == pSpxConnFile) + { + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); + + // Remove from list + *ppC = pC->scf_GlobalNext; + break; + } + + ppC = &pC->scf_GlobalNext; + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + if (pC == NULL) + status = STATUS_INVALID_CONNECTION; + + return(status); +} + + + + + + +#if 0 + +VOID +spxConnPushIntoPktList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_PktNext = SpxPktConnList; + SpxPktConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromPktList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxPktConnList) != NULL) + { + SpxPktConnList = SpxPktConnList->scf_PktNext; + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + + + + +VOID +spxConnPushIntoRecvList( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + pSpxConnFile->scf_ProcessRecvNext = SpxRecvConnList; + SpxRecvConnList = pSpxConnFile; + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + + return; +} + + + + +VOID +spxConnPopFromRecvList( + IN PSPX_CONN_FILE * ppSpxConnFile + ) + +/*++ + +Routine Description: + + !!!MACROIZE!!! + +Arguments: + + +Return Value: + + +--*/ + +{ + CTELockHandle lockHandle; + + // Get the global q lock + CTEGetLock(&SpxGlobalQInterlock, &lockHandle); + if ((*ppSpxConnFile = SpxRecvConnList) != NULL) + { + SpxRecvConnList = SpxRecvConnList->scf_ProcessRecvNext; + DBGPRINT(SEND, INFO, + ("SpxConnRemoveFromRecv: %lx\n", *ppSpxConnFile)); + } + CTEFreeLock(&SpxGlobalQInterlock, lockHandle); + return; +} + +#endif + + +// +// Reference/Dereference routines +// + + +#if DBG + +VOID +SpxConnFileRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFile + + + + +VOID +SpxConnFileLockRef( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE CONNECTION LOCK HELD. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (pSpxConnFile->scf_RefCount >= 0); // not perfect, but... + + (VOID)SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + 1, + &pSpxConnFile->scf_Lock); + +} // SpxRefConnectionFileLock + +#endif + + + + +VOID +SpxConnFileRefByIdLock ( + IN USHORT ConnId, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) + +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE DEVICE LOCK HELD!!! + + All active connections should be on the device active list. Later, + this data structure will be a tree, caching the last accessed + connection. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = + SpxDevice->dev_GlobalActiveConnList[ConnId & NUM_SPXCONN_HASH_MASK]; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_GlobalActiveNext) + { + if (pSpxChkConn->scf_LocalConnId == ConnId) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYID); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; + +} + + + + +VOID +SpxConnFileRefByCtxLock( + IN PSPX_ADDR_FILE pSpxAddrFile, + IN CONNECTION_CONTEXT Ctx, + OUT PSPX_CONN_FILE * ppSpxConnFile, + OUT PNTSTATUS pStatus + ) +/*++ + +Routine Description: + + !!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!! + + Returns a referenced connection file with the associated context and + address file desired. + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxChkConn; + + *pStatus = STATUS_SUCCESS; + + for (pSpxChkConn = pSpxAddrFile->saf_Addr->sa_InactiveConnList; + pSpxChkConn != NULL; + pSpxChkConn = pSpxChkConn->scf_Next) + { + if ((pSpxChkConn->scf_ConnCtx == Ctx) && + (pSpxChkConn->scf_AddrFile == pSpxAddrFile)) + { + SpxConnFileReference(pSpxChkConn, CFREF_BYCTX); + *ppSpxConnFile = pSpxChkConn; + break; + } + } + + if (pSpxChkConn == NULL) + { + *pStatus = STATUS_INVALID_CONNECTION; + } + + return; +} + + + + +NTSTATUS +SpxConnFileVerify ( + IN PSPX_CONN_FILE pConnFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + + try + { + if ((pConnFile->scf_Size == sizeof (SPX_CONN_FILE)) && + (pConnFile->scf_Type == SPX_CONNFILE_SIGNATURE)) + { + CTEGetLock (&pConnFile->scf_Lock, &LockHandle); + if (!SPX_CONN_FLAG(pConnFile, SPX_CONNFILE_CLOSING)) + { + SpxConnFileLockReference(pConnFile, CFREF_VERIFY); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyConnFile: A %lx closing\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + CTEFreeLock (&pConnFile->scf_Lock, LockHandle); + } + else + { + DBGPRINT(TDI, ERR, + ("StVerifyAddressFile: AF %lx bad signature\n", pConnFile)); + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DBGPRINT(TDI, ERR, + ("SpxVerifyConnFile: AF %lx exception\n", pConnFile)); + + return GetExceptionCode(); + } + + return status; + +} // SpxVerifyConnFile + + + + +VOID +SpxConnFileDeref( + IN PSPX_CONN_FILE pSpxConnFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + SpxDestroyConnectionFile to remove it from the system. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + BOOLEAN fDiscNotIndicated = FALSE; + BOOLEAN fIDiscFlag = FALSE; + BOOLEAN fSpx2; + + CTEAssert(pSpxConnFile->scf_RefCount > 0); + oldvalue = SPX_ADD_ULONG ( + &pSpxConnFile->scf_RefCount, + (ULONG)-1, + &pSpxConnFile->scf_Lock); + + CTEAssert (oldvalue > 0); + if (oldvalue == 1) + { + CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; + LIST_ENTRY discReqList, *p; + PREQUEST pDiscReq; + PSPX_ADDR_FILE pSpxAddrFile = NULL; + PREQUEST pCloseReq = NULL, + pCleanupReq = NULL, + pConnectReq = NULL; + BOOLEAN fDisassoc = FALSE; + + InitializeListHead(&discReqList); + + // We may not be associated at this point. Note: When we are active we + // always have a reference. So its not like we execute this code very often. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + } + else + { + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + + if (pSpxAddrFile) + { + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + + //if (pSpxConnFile->scf_RefCount == 0) + + // + // ** The lock passed here is a dummy - it is pre-compiled out. + // + if (SPX_ADD_ULONG(&pSpxConnFile->scf_RefCount, 0, &pSpxConnFile->scf_Lock) == 0) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn is 0 %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // All pending requests on this connection are done. See if we + // need to complete the disconnect phase etc. + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_DISCONN: + + // Disconnect is done. Move connection out of all the lists + // it is on, reset states etc. + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn being inactivated %lx\n", + pSpxConnFile)); + + // Time to complete disc requests if present. + // There could be multiple of them. + p = pSpxConnFile->scf_DiscLinkage.Flink; + while (p != &pSpxConnFile->scf_DiscLinkage) + { + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Disc on %lx.%lx\n", + pSpxConnFile, pDiscReq)); + + RemoveEntryList(REQUEST_LINKAGE(pDiscReq)); + + // Make sure Stacy is always wearing a particular + // kind of dress if she isn't already. + if (REQUEST_STATUS(pDiscReq) == STATUS_PENDING) + { + REQUEST_STATUS(pDiscReq) = STATUS_SUCCESS; + } + + InsertTailList( + &discReqList, + REQUEST_LINKAGE(pDiscReq)); + } + + // + // Note the state here, and check after the conn has been inactivated. + // + + // + // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD + // + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) { + fDiscNotIndicated = TRUE; + } + + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC)) { + fIDiscFlag = TRUE; + } + + fSpx2 = (SPX2_CONN(pSpxConnFile)) ? TRUE : FALSE; + + // + // [SA] Bug #14655 + // Do not try to inactivate an already inactivated connection + // + + if (!(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { + spxConnInactivate(pSpxConnFile); + } else { + // + // This is an SPXI connection which has got the local disconnect. + // Reset the flags now. + // + CTEAssert(!fDiscNotIndicated); + + SPX_MAIN_SETSTATE(pSpxConnFile, 0); + SPX_DISC_SETSTATE(pSpxConnFile, 0); + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + } + + // + // [SA] If we were waiting for sends to be aborted and did not indicate this + // disconnect to AFD; and AFD did not call a disconnect on this connection, + // then call the disonnect handler now. + // + if (fDiscNotIndicated) { + PVOID pDiscHandlerCtx; + PTDI_IND_DISCONNECT pDiscHandler = NULL; + ULONG discCode = 0; + + pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; + pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; + + // Indicate disconnect to afd. + if (pDiscHandler != NULL) { + + // + // If this was an SPXI connection, the disconnect state is still + // DISCONN, so if this routine is re-entered, we need to prevent + // a re-indicate to AFD. + // Also, we need to wait for a local disconnect from AFD since + // we indicated a TDI_DISCONNECT_RELEASE. We bump up the ref count + // in this case. + // + if (!fSpx2) { + CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) ); + + SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); + + if (fIDiscFlag) { + SpxConnFileLockReference(pSpxConnFile, CFREF_DISCWAITSPX); + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); + } + } + + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + + DBGPRINT(CONNECT, INFO, + ("spxDerefConnectionFile: Indicating to afd On %lx when %lx\n", + pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); + + // First complete all requests waiting for receive completion on + // this conn before indicating disconnect. + spxConnCompletePended(pSpxConnFile); + + if (fIDiscFlag) { + // + // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets + // it has buffered before the remote disconnect took place. + // + discCode = TDI_DISCONNECT_RELEASE; + } else { + // + // [SA] bug #15249 + // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD + // + discCode = TDI_DISCONNECT_ABORT; + } + + (*pDiscHandler)( + pDiscHandlerCtx, + pSpxConnFile->scf_ConnCtx, + 0, // Disc data + NULL, + 0, // Disc info + NULL, + discCode); + + CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); + CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); + } + } + + --SpxDevice->dev_Stat.OpenConnections; + + break; + + case SPX_CONNFILE_CONNECTING: + case SPX_CONNFILE_LISTENING: + + // Get connect/accept request if present. + pConnectReq = pSpxConnFile->scf_ConnectReq; + pSpxConnFile->scf_ConnectReq = NULL; + + spxConnInactivate(pSpxConnFile); + break; + + case SPX_CONNFILE_ACTIVE: + + KeBugCheck(0); + + default: + + CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == 0); + break; + } + + // If stopping, disassociate from the address file. Complete + // cleanup request. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_CleanupReq)); + + // Save this for later completion. + pCleanupReq = pSpxConnFile->scf_CleanupReq; + pSpxConnFile->scf_CleanupReq = NULL; + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) + { + DBGPRINT(TDI, INFO, + ("SpxDerefConnectionFile: Conn stopping %lx\n", + pSpxConnFile)); + + pSpxAddrFile = pSpxConnFile->scf_AddrFile; + SPX_CONN_RESETFLAG(pSpxConnFile,SPX_CONNFILE_ASSOC); + + // Dequeue the connection from the address file + spxConnRemoveFromAssocList( + &pSpxAddrFile->saf_AssocConnList, + pSpxConnFile); + + // Dequeue the connection file from the address list. It must + // be in the inactive list. + spxConnRemoveFromList( + &pSpxAddrFile->saf_Addr->sa_InactiveConnList, + pSpxConnFile); + + DBGPRINT(CREATE, INFO, + ("SpxConnDerefDisAssociate: %lx from addr file %lx\n", + pSpxConnFile, pSpxAddrFile)); + + fDisassoc = TRUE; + } + } + + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Conn closing %lx\n", + pSpxConnFile)); + + // Save this for later completion. + pCloseReq = pSpxConnFile->scf_CloseReq; + + // + // Null this out so on a re-entrant case, we dont try to complete this again. + // + pSpxConnFile->scf_CloseReq = NULL; + CTEAssert(pCloseReq != NULL); + } + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + } + CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); + CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); + CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); + } + + if (fDisassoc) + { + // Remove reference on address for this association. + SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); + } + + if (pConnectReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Connect on %lx req %lx\n", + pSpxConnFile, pConnectReq)); + + // Status will already be set in here. We should be here only if + // connect is being aborted. + SpxCompleteRequest(pConnectReq); + } + + while (!IsListEmpty(&discReqList)) + { + p = RemoveHeadList(&discReqList); + pDiscReq = LIST_ENTRY_TO_REQUEST(p); + + DBGPRINT(CONNECT, DBG, + ("SpxConnFileDeref: DISC REQ %lx.%lx Completing\n", + pSpxConnFile, pDiscReq)); + + SpxCompleteRequest(pDiscReq); + } + + if (pCleanupReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Cleanup complete %lx req %lx\n", + pSpxConnFile, pCleanupReq)); + + REQUEST_INFORMATION(pCleanupReq) = 0; + REQUEST_STATUS(pCleanupReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCleanupReq); + } + + if (pCloseReq != (PREQUEST)NULL) + { + DBGPRINT(TDI, DBG, + ("SpxDerefConnectionFile: Freed %lx close req %lx\n", + pSpxConnFile, pCloseReq)); + + CTEAssert(pSpxConnFile->scf_RefCount == 0); + + // Remove from the global list + if (!NT_SUCCESS(spxConnRemoveFromGlobalList(pSpxConnFile))) + { + KeBugCheck(0); + } + + // Free it up. + SpxFreeMemory (pSpxConnFile); + + REQUEST_INFORMATION(pCloseReq) = 0; + REQUEST_STATUS(pCloseReq) = STATUS_SUCCESS; + SpxCompleteRequest (pCloseReq); + } + } + + return; + +} // SpxDerefConnectionFile + + + + +VOID +spxConnReInit( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + // Reinit all variables. + pSpxConnFile->scf_Flags2 = 0; + + pSpxConnFile->scf_GlobalActiveNext = NULL; + pSpxConnFile->scf_PktNext = NULL; + pSpxConnFile->scf_CRetryCount = 0; + pSpxConnFile->scf_WRetryCount = 0; + pSpxConnFile->scf_RRetryCount = 0; + pSpxConnFile->scf_RRetrySeqNum = 0; + + pSpxConnFile->scf_CTimerId = + pSpxConnFile->scf_RTimerId = + pSpxConnFile->scf_WTimerId = + pSpxConnFile->scf_TTimerId = + pSpxConnFile->scf_ATimerId = 0; + + pSpxConnFile->scf_LocalConnId = + pSpxConnFile->scf_SendSeqNum = + pSpxConnFile->scf_SentAllocNum = + pSpxConnFile->scf_RecvSeqNum = + pSpxConnFile->scf_RetrySeqNum = + pSpxConnFile->scf_RecdAckNum = + pSpxConnFile->scf_RemConnId = + pSpxConnFile->scf_RecdAllocNum = 0; + +#if DBG + // Initialize so we dont hit breakpoint on seq 0 + pSpxConnFile->scf_PktSeqNum = 0xFFFF; +#endif + + pSpxConnFile->scf_DataType = 0; + + CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); + CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendListTail == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListHead == NULL); + CTEAssert(pSpxConnFile->scf_SendSeqListTail == NULL); + pSpxConnFile->scf_CurRecvReq = NULL; + pSpxConnFile->scf_CurRecvOffset = 0; + pSpxConnFile->scf_CurRecvSize = 0; + + pSpxConnFile->scf_ReqPkt = NULL; + + return; +} + + + + +VOID +spxConnInactivate( + IN PSPX_CONN_FILE pSpxConnFile + ) +/*++ + +Routine Description: + + !!! Called with dev/addr/connection lock held !!! + +Arguments: + + This gets us back to associate SAVING the state of the STOPPING and + CLOSING flags so that dereference can go ahead and finish those. + +Return Value: + + +--*/ +{ + BOOLEAN fStopping, fClosing, fAborting; + + fStopping = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); + fClosing = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); + + // + // [SA] Bug #14655 + // Save the disconnect states so that a proper error can be given in the case of + // a send after a remote disconnection. + // + + // + // Bug #17729 + // Dont retain these flags if a local disconnect has already occured. + // + + fAborting = (!SPX2_CONN(pSpxConnFile) && + !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && + (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)); + +#if DBG + pSpxConnFile->scf_GhostFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_GhostFlags2 = pSpxConnFile->scf_Flags2; + pSpxConnFile->scf_GhostRefCount = pSpxConnFile->scf_RefCount; +#endif + + // Clear all flags, go back to the assoc state. Restore stop/close + pSpxConnFile->scf_Flags = SPX_CONNFILE_ASSOC; + SPX_CONN_SETFLAG(pSpxConnFile, + ((fStopping ? SPX_CONNFILE_STOPPING : 0) | + (fClosing ? SPX_CONNFILE_CLOSING : 0))); + + // + // [SA] bug #14655 + // In order to avoid a re-entry, mark connection as SPX_DISC_INACTIVATED + // + if (fAborting) + { + SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); + SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_INACTIVATED); + } + + // Remove connection from global list on device + if (!NT_SUCCESS(spxConnRemoveFromGlobalActiveList( + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Remove connection from active list on address + if (!NT_SUCCESS(spxConnRemoveFromList( + &pSpxConnFile->scf_AddrFile->saf_Addr->sa_ActiveConnList, + pSpxConnFile))) + { + KeBugCheck(0); + } + + // Put connection in inactive list on address + SPX_INSERT_ADDR_INACTIVE( + pSpxConnFile->scf_AddrFile->saf_Addr, + pSpxConnFile); + + spxConnReInit(pSpxConnFile); + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxdev.c b/private/ntos/tdi/isnp/spx/spxdev.c new file mode 100644 index 000000000..431498686 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxdev.c @@ -0,0 +1,242 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdev.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXDEV + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitCreateDevice) +#pragma alloc_text(PAGE, SpxDestroyDevice) +#endif + + + + +VOID +SpxDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->dev_RefCount); + + CTEAssert (result >= 0); + + if (result == 0) + { + // Close binding to IPX + SpxUnbindFromIpx(); + + // Set unload event. + KeSetEvent(&SpxUnloadEvent, IO_NETWORK_INCREMENT, FALSE); + } + +} // SpxDerefDevice + + + + +NTSTATUS +SpxInitCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE * DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + ULONG DeviceNameOffset; + + + DBGPRINT(DEVICE, INFO, + ("SpxInitCreateDevice - Create device %ws\n", DeviceName->Buffer)); + + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors). + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + DBGPRINT(DEVICE, ERR, ("IoCreateDevice failed\n")); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + Device = (PDEVICE)deviceObject; + + DBGPRINT(DEVICE, INFO, ("IoCreateDevice succeeded %lx\n", Device)); + + // Initialize our part of the device context. + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + DeviceNameOffset = sizeof(DEVICE); + + // Copy over the device name. + Device->dev_DeviceNameLen = DeviceName->Length + sizeof(WCHAR); + Device->dev_DeviceName = (PWCHAR)(((PUCHAR)Device) + DeviceNameOffset); + + RtlCopyMemory( + Device->dev_DeviceName, + DeviceName->Buffer, + DeviceName->Length); + + Device->dev_DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // Initialize the reference count. + Device->dev_RefCount = 1; + +#if DBG + Device->dev_RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->dev_Signature1, "IDC1", 4); + RtlCopyMemory(Device->dev_Signature2, "IDC2", 4); +#endif + + // Set next conn id to be used. + Device->dev_NextConnId = (USHORT)SpxRandomNumber(); + if (Device->dev_NextConnId == 0xFFFF) + { + Device->dev_NextConnId = 1; + } + + DBGPRINT(DEVICE, ERR, + ("SpxInitCreateDevice: Start Conn Id %lx\n", Device->dev_NextConnId)); + + // Initialize the resource that guards address ACLs. + ExInitializeResource (&Device->dev_AddrResource); + + // initialize the various fields in the device context + CTEInitLock (&Device->dev_Interlock); + CTEInitLock (&Device->dev_Lock); + KeInitializeSpinLock (&Device->dev_StatInterlock); + KeInitializeSpinLock (&Device->dev_StatSpinLock); + + Device->dev_State = DEVICE_STATE_CLOSED; + Device->dev_Type = SPX_DEVICE_SIGNATURE; + Device->dev_Size = sizeof (DEVICE); + + Device->dev_Stat.Version = 0x100; + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} // SpxCreateDevice + + + + +VOID +SpxDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ExDeleteResource (&Device->dev_AddrResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} // SpxDestroyDevice diff --git a/private/ntos/tdi/isnp/spx/spxdrvr.c b/private/ntos/tdi/isnp/spx/spxdrvr.c new file mode 100644 index 000000000..0e9935d1a --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxdrvr.c @@ -0,0 +1,1008 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxdrvr.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the SPX/SPXII module of the ISN transport. + +Author: + + Adam Barr (adamba) Original Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 14-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXDRVR + +// Forward declaration of various routines used in this module. + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath); + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject); + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SpxUnload) +#pragma alloc_text(PAGE, SpxDispatchOpenClose) +#pragma alloc_text(PAGE, SpxDispatch) +#pragma alloc_text(PAGE, SpxDeviceControl) +#pragma alloc_text(PAGE, SpxUnload) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the SPX ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of ST's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + UNICODE_STRING deviceName; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN BoundToIpx = FALSE; + + // DBGBRK(FATAL); + + // Initialize the Common Transport Environment. + if (CTEInitialize() == 0) { + return (STATUS_UNSUCCESSFUL); + } + + // We have this #define'd. Ugh, but CONTAINING_RECORD has problem owise. + CTEAssert(NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); + + // Create the device object. (IoCreateDevice zeroes the memory + // occupied by the object.) + RtlInitUnicodeString(&deviceName, SPX_DEVICE_NAME); + status = SpxInitCreateDevice( + DriverObject, + &deviceName, + &SpxDevice); + + if (!NT_SUCCESS(status)) + { + return(status); + } + + do + { + CTEInitLock (&SpxGlobalInterlock); + CTEInitLock (&SpxGlobalQInterlock); + + // Initialize the unload event + KeInitializeEvent( + &SpxUnloadEvent, + NotificationEvent, + FALSE); + + // !!!The device is created at this point!!! + // Get information from the registry. + status = SpxInitGetConfiguration( + RegistryPath, + &SpxDevice->dev_ConfigInfo); + + if (!NT_SUCCESS(status)) + { + break; + } + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif _PNP_POWER + + // Bind to the IPX transport. + if (!NT_SUCCESS(status = SpxInitBindToIpx())) + { + // BUGBUG: Have ipx name here as second string + LOG_ERROR( + EVENT_TRANSPORT_BINDING_FAILED, + status, + NULL, + NULL, + 0); + + break; + } + + BoundToIpx = TRUE; + +#if !defined(_PNP_POWER) + // Initialize the timer system + if (!NT_SUCCESS(status = SpxTimerInit())) + { + break; + } +#endif !_PNP_POWER + + // Initialize the block manager + if (!NT_SUCCESS(status = SpxInitMemorySystem(SpxDevice))) + { + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + break; + } + + // Initialize the driver object with this driver's entry points. + DriverObject->MajorFunction [IRP_MJ_CREATE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = SpxDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] + = SpxDispatch; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] + = SpxDispatchInternal; + DriverObject->DriverUnload = SpxUnload; + + // Initialize the provider info + SpxQueryInitProviderInfo(&SpxDevice->dev_ProviderInfo); + SpxDevice->dev_CurrentSocket = (USHORT)PARAM(CONFIG_SOCKET_RANGE_START); + +#if !defined(_PNP_POWER) + // We are open now. + SpxDevice->dev_State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + // Set the window size in statistics + SpxDevice->dev_Stat.MaximumSendWindow = + SpxDevice->dev_Stat.AverageSendWindow = PARAM(CONFIG_WINDOW_SIZE) * + IpxLineInfo.MaximumSendSize; + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == SpxDevice->dev_State ) { + SpxDevice->dev_State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + } while (FALSE); + + if (!NT_SUCCESS(status) ) + { + // Delete the device and any associated resources created. + if( BoundToIpx ) { + SpxDerefDevice(SpxDevice); + } + SpxDestroyDevice(SpxDevice); + } + + return (status); +} + + + + +VOID +SpxUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. The I/O system will not + call us until nobody above has ST open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + UNREFERENCED_PARAMETER (DriverObject); + + // Stop the timer subsystem + SpxTimerFlushAndStop(); + + // Remove creation reference count on the IPX device object. + SpxDerefDevice(SpxDevice); + + // Wait on the unload event. + KeWaitForSingleObject( + &SpxUnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL); + + // Release the block memory stuff + SpxDeInitMemorySystem(SpxDevice); + SpxDestroyDevice(SpxDevice); + return; +} + + + +NTSTATUS +SpxDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // Make sure status information is consistent every time. + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (IrpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + + Status = SpxDeviceControl (DeviceObject, Irp); + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + // + // Complete the Irp here instead of below. + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } // major function switch + + /* Commented out and re-located to the default case above. + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + */ + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatch + +VOID +SpxAssignControlChannelId( + IN PDEVICE Device, + IN PIRP Request + ) +/*++ + +Routine Description: + + This routine is required to ensure that the Device lock (to protect the ControlChannelId in the Device) + is not taken in a pageable routine (SpxDispatchOpenClose). + + NOTE: SPX returns the ControlChannelId in the Request, but never uses it later when it comes down in a + close/cleanup. The CCID is a ULONG; in future, if we start using this field (as in IPX which uses these Ids + to determine lineup Irps to complete), then we may run out of numbers (since we monotonically increase the CCID); + though there is a low chance of that since we will probably run out of memory before that! Anyhow, if that + happens, one solution (used in IPX) is to make the CCID into a Large Integer and pack the values into the + REQUEST_OPEN_TYPE(Irp) too. + + +Arguments: + + Device - Pointer to the device object for this driver. + + Request - Pointer to the request packet representing the I/O request. + +Return Value: + + None. + +--*/ +{ + CTELockHandle LockHandle; + + CTEGetLock (&Device->dev_Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->dev_CcId); + ++Device->dev_CcId; + if (Device->dev_CcId == 0) { + Device->dev_CcId = 1; + } + + CTEFreeLock (&Device->dev_Lock, LockHandle); +} + +NTSTATUS +SpxDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + BOOLEAN found; + PREQUEST Request; + UINT i; + PFILE_FULL_EA_INFORMATION openType; + CONNECTION_CONTEXT connCtx; + + +#if !defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->dev_State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = SpxAddrOpen (Device, Request); + break; + } + + // Connection? + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + if (openType->EaValueLength < sizeof(CONNECTION_CONTEXT)) + { + + DBGPRINT(CREATE, ERR, + ("Create: Context size %d\n", openType->EaValueLength)); + + Status = STATUS_EA_LIST_INCONSISTENT; + break; + } + + connCtx = + *((CONNECTION_CONTEXT UNALIGNED *) + &openType->EaName[openType->EaNameLength+1]); + + Status = SpxConnOpen( + Device, + connCtx, + Request); + + break; + } + + } else { + + // + // Takes a lock in a Pageable routine - call another (non-paged) function to do that. + // + SpxAssignControlChannelId(Device, Request); + + REQUEST_OPEN_TYPE(Request) = (PVOID)SPX_FILE_TYPE_CONTROL; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileClose(Device, Request); + break; + + case TDI_CONNECTION_FILE: + Status = SpxConnClose(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ((Device->dev_State != DEVICE_STATE_OPEN) && ( Device->dev_State != DEVICE_STATE_LOADED )) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + case TDI_TRANSPORT_ADDRESS_FILE: + + Status = SpxAddrFileCleanup(Device, Request); + break; + + case TDI_CONNECTION_FILE: + + Status = SpxConnCleanup(Device, Request); + break; + + case SPX_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } // major function switch + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchOpenClose + + + + +NTSTATUS +SpxDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // Convert the user call to the proper internal device call. + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + if (Status == STATUS_SUCCESS) { + + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + Status = SpxDispatchInternal (DeviceObject, Irp); + + // + // Return the proper error code here. If SpxDispatchInternal returns an error, + // then we used to map it to pending below; this is wrong since the client above + // us could wait for ever since the IO subsystem does not set the event if an + // error is returned and the Irp is not marked pending. + // + + // Status = STATUS_PENDING; + } else { + + DBGPRINT(TDI, DBG, + ("Unknown Tdi code in Irp: %lx\n", Irp)); + + // + // Complete the Irp.... + // + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + return Status; + +} // SpxDeviceControl + + + + +NTSTATUS +SpxDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PREQUEST Request; + KIRQL oldIrql; + NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; + PDEVICE Device = (PDEVICE)DeviceObject; + + + if (Device->dev_State != DEVICE_STATE_OPEN) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Allocate a request to track this IRP. + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // Make sure status information is consistent every time. + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // Cancel irp + IoAcquireCancelSpinLock(&oldIrql); + if (!Irp->Cancel) + { + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)SpxTdiCancel); + } + IoReleaseCancelSpinLock(oldIrql); + + if (Irp->Cancel) + return STATUS_CANCELLED; + + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + switch (REQUEST_MINOR_FUNCTION(Request)) + { + case TDI_ACCEPT: + + Status = SpxConnAccept( + Device, + Request); + + break; + + case TDI_SET_EVENT_HANDLER: + + Status = SpxAddrSetEventHandler( + Device, + Request); + + break; + + case TDI_RECEIVE: + + Status = SpxConnRecv( + Device, + Request); + break; + + + case TDI_SEND: + + Status = SpxConnSend( + Device, + Request); + break; + + case TDI_ACTION: + + Status = SpxConnAction( + Device, + Request); + break; + + case TDI_ASSOCIATE_ADDRESS: + + Status = SpxConnAssociate( + Device, + Request); + + break; + + case TDI_DISASSOCIATE_ADDRESS: + + Status = SpxConnDisAssociate( + Device, + Request); + + break; + + case TDI_CONNECT: + + Status = SpxConnConnect( + Device, + Request); + + break; + + case TDI_DISCONNECT: + + Status = SpxConnDisconnect( + Device, + Request); + break; + + case TDI_LISTEN: + + Status = SpxConnListen( + Device, + Request); + break; + + case TDI_QUERY_INFORMATION: + + Status = SpxTdiQueryInformation( + Device, + Request); + + break; + + case TDI_SET_INFORMATION: + + Status = SpxTdiSetInformation( + Device, + Request); + + break; + + // Something we don't know about was submitted. + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (Status != STATUS_PENDING) + { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + IoAcquireCancelSpinLock(&oldIrql); + IoSetCancelRoutine(Irp, (PDRIVER_CANCEL)NULL); + IoReleaseCancelSpinLock(oldIrql); + SpxCompleteRequest (Request); + SpxFreeRequest (Device, Request); + } + + // Return the immediate status code to the caller. + return Status; + +} // SpxDispatchInternal + + + + +VOID +SpxTdiCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles cancellation of IO requests + +Arguments: + + +Return Value: +--*/ +{ + PREQUEST Request; + PSPX_ADDR_FILE pSpxAddrFile; + PSPX_ADDR pSpxAddr; + PDEVICE Device = (PDEVICE)DeviceObject; + CTELockHandle connectIrql; + CTELockHandle TempIrql; + PSPX_CONN_FILE pSpxConnFile; + + Request = SpxAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) + { + return; + } + + DBGPRINT(TDI, ERR, + ("SpxTdiCancel: Cancel irp called %lx.%lx\n", + Irp, REQUEST_OPEN_CONTEXT(Request))); + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) + { + case TDI_CONNECTION_FILE: + pSpxConnFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + CTEGetLock(&pSpxConnFile->scf_Lock, &connectIrql); + + // + // Swap the irql + // + TempIrql = connectIrql; + connectIrql = Irp->CancelIrql; + Irp->CancelIrql = TempIrql; + + IoReleaseCancelSpinLock (Irp->CancelIrql); + if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) + { + if (!SPX_CONN_IDLE(pSpxConnFile)) + { + // + // This releases the lock + // + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_LOCAL_DISCONNECT, + SPX_CALL_TDILEVEL, + connectIrql, + FALSE); // [SA] bug #15249 + } + } + +// SpxConnStop((PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request)); + break; + + case TDI_TRANSPORT_ADDRESS_FILE: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + pSpxAddrFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + pSpxAddr = pSpxAddrFile->saf_Addr; + SpxAddrFileStop(pSpxAddrFile, pSpxAddr); + break; + + default: + + IoReleaseCancelSpinLock (Irp->CancelIrql); + break; + + } + +} diff --git a/private/ntos/tdi/isnp/spx/spxerror.c b/private/ntos/tdi/isnp/spx/spxerror.c new file mode 100644 index 000000000..7d2cc7444 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxerror.c @@ -0,0 +1,316 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxerror.c + +Abstract: + + This module contains code which provides error logging support. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXERROR + +LONG SpxLastRawDataLen = 0; +NTSTATUS SpxLastUniqueErrorCode = STATUS_SUCCESS; +NTSTATUS SpxLastNtStatusCode = STATUS_SUCCESS; +ULONG SpxLastErrorCount = 0; +LONG SpxLastErrorTime = 0; +BYTE SpxLastRawData[PORT_MAXIMUM_MESSAGE_LENGTH - \ + sizeof(IO_ERROR_LOG_PACKET)] = {0}; + +BOOLEAN +SpxFilterErrorLogEntry( + IN NTSTATUS UniqueErrorCode, + IN NTSTATUS NtStatusCode, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + + int insertionStringLength = 0; + + // Filter out events such that the same event recurring close together does not + // cause errorlog clogging. The scheme is - if the event is same as the last event + // and the elapsed time is > THRESHOLD and ERROR_CONSEQ_FREQ simulataneous errors + // have happened, then log it else skip + if ((UniqueErrorCode == SpxLastUniqueErrorCode) && + (NtStatusCode == SpxLastNtStatusCode)) + { + SpxLastErrorCount++; + if ((SpxLastRawDataLen == RawDataLen) && + (RtlEqualMemory(SpxLastRawData, RawDataBuf, RawDataLen)) && + ((SpxLastErrorCount % ERROR_CONSEQ_FREQ) != 0) && + ((SpxGetCurrentTime() - SpxLastErrorTime) < ERROR_CONSEQ_TIME)) + { + return(FALSE); + } + } + + SpxLastUniqueErrorCode = UniqueErrorCode; + SpxLastNtStatusCode = NtStatusCode; + SpxLastErrorCount = 0; + SpxLastErrorTime = SpxGetCurrentTime(); + if (RawDataLen != 0) + { + SpxLastRawDataLen = RawDataLen; + RtlCopyMemory( + SpxLastRawData, + RawDataBuf, + RawDataLen); + } + + return(TRUE); +} + + + + +VOID +SpxWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + UINT i; + + if (!SpxFilterErrorLogEntry( + EVENT_TRANSPORT_RESOURCE_POOL, + STATUS_INSUFFICIENT_RESOURCES, + (PVOID)&BytesNeeded, + sizeof(BytesNeeded))) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->dev_DeviceNameLen + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + // Convert the error value into a buffer. + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) + { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + RtlCopyMemory( + StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + } +} + + + + +VOID +SpxWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN PVOID RawDataBuf OPTIONAL, + IN LONG RawDataLen + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + RawDataBuf - The number of ULONGs of dump data. + + RawDataLen - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[4] = L"Spx"; + + if (!SpxFilterErrorLogEntry( + ErrorCode, + FinalStatus, + RawDataBuf, + RawDataLen)) + { + return; + } + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + EntrySize += (UCHAR)Device->dev_DeviceNameLen; + } + else + { + EntrySize += sizeof(DriverName); + } + + if (SecondString) + { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize); + + if (errorLogEntry != NULL) + { + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)RawDataLen; + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + RawDataLen; + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (RawDataLen != 0) + { + RtlCopyMemory(errorLogEntry->DumpData, RawDataBuf, RawDataLen); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->dev_Type == SPX_DEVICE_SIGNATURE) + { + RtlCopyMemory( + StringLoc, Device->dev_DeviceName, Device->dev_DeviceNameLen); + + StringLoc += Device->dev_DeviceNameLen; + } + else + { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + + if (SecondString) + { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxmem.c b/private/ntos/tdi/isnp/spx/spxmem.c new file mode 100644 index 000000000..9cd400e5b --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxmem.c @@ -0,0 +1,897 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxmem.c + +Abstract: + + This module contains code which implements the memory allocation wrappers. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + Jameel Hyder (jameelh) Initial Version + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, SpxInitMemorySystem) +#pragma alloc_text( PAGE, SpxDeInitMemorySystem) +#endif + +// Define module number for event logging entries +#define FILENUM SPXMEM + +// Globals for this module +// Some block sizes (like NDISSEND/NDISRECV are filled in after binding with IPX) +USHORT spxBlkSize[NUM_BLKIDS] = // Size of each block + { + sizeof(BLK_HDR)+sizeof(TIMERLIST), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +USHORT spxChunkSize[NUM_BLKIDS] = // Size of each Chunk + { + 512-BC_OVERHEAD, // BLKID_TIMERLIST + 512-BC_OVERHEAD, // BLKID_NDISSEND + 512-BC_OVERHEAD // BLKID_NDISRECV + }; + + +// Filled in after binding with IPX +// Reference for below. +// ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ +// (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST +USHORT spxNumBlks[NUM_BLKIDS] = // Number of blocks per chunk + { + ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/ + (sizeof(BLK_HDR)+sizeof(TIMERLIST)), // BLKID_TIMERLIST + 0, // BLKID_NDISSEND + 0 // BLKID_NDISRECV + }; + +CTELock spxBPLock[NUM_BLKIDS] = { 0 }; +PBLK_CHUNK spxBPHead[NUM_BLKIDS] = { 0 }; + + + + +NTSTATUS +SpxInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + !!! MUST BE CALLED AFTER BINDING TO IPX!!! + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i; + NDIS_STATUS ndisStatus; + + // Try to allocate the ndis buffer pool. + NdisAllocateBufferPool( + &ndisStatus, + &pSpxDevice->dev_NdisBufferPoolHandle, + 20); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + return(STATUS_INSUFFICIENT_RESOURCES); + + for (i = 0; i < NUM_BLKIDS; i++) + CTEInitLock (&spxBPLock[i]); + + // Set the sizes in the block id info arrays. + for (i = 0; i < NUM_BLKIDS; i++) + { + // BUGBUG: Do it. + switch (i) + { + case BLKID_NDISSEND: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_SEND_RESD) + + NDIS_PACKET_SIZE + + IpxMacHdrNeeded + + MIN_IPXSPX2_HDRSIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + case BLKID_NDISRECV: + +#ifdef SPX_OWN_PACKETS + spxBlkSize[i] = sizeof(BLK_HDR) + + sizeof(SPX_RECV_RESD) + + NDIS_PACKET_SIZE; +#else + spxBlkSize[i] = sizeof(PNDIS_PACKET); +#endif + + // + // Round the block size up to the next 8-byte boundary. + // + spxBlkSize[i] = QWORDSIZEBLOCK(spxBlkSize[i]); + + // Set number blocks + spxNumBlks[i] = ( 512-BC_OVERHEAD-sizeof(BLK_CHUNK))/spxBlkSize[i]; + break; + + default: + + break; + } + + } + + SpxTimerScheduleEvent((TIMER_ROUTINE)spxBPAgePool, + BLOCK_POOL_TIMER, + NULL); +} + + + + +VOID +SpxDeInitMemorySystem( + IN PDEVICE pSpxDevice + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LONG i, j, NumBlksPerChunk; + PBLK_CHUNK pChunk, pFree; + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + for (pChunk = spxBPHead[i]; + pChunk != NULL; ) + { + DBGPRINT(RESOURCES, ERR, + ("SpxInitMemorySystem: Freeing %lx\n", pChunk)); + + CTEAssert (pChunk->bc_NumFrees == NumBlksPerChunk); + + if ((pChunk->bc_BlkId == BLKID_NDISSEND) || + (pChunk->bc_BlkId == BLKID_NDISRECV)) + { + PBLK_HDR pBlkHdr; + + // We need to free the Ndis stuff for these guys + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + +#ifdef SPX_OWN_PACKETS + // Only need to free the ndis buffer. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + + // + // Free the second MDL also + // + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } +#else + // Need to free both the packet and the buffer. + ppNdisPkt = (PNDIS_PACKET *)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + + NdisUnchainBufferAtFront(*ppNdisPkt, &pNdisBuffer); + if (pNdisBuffer == NULL) + { + // Something is terribly awry. + KeBugCheck(0); + } + + NdisFreeBuffer(pNdisBuffer); + } + NdisFreePacket(*ppNdisPkt); +#endif + } + } + pFree = pChunk; + pChunk = pChunk->bc_Next; + +#ifndef SPX_OWN_PACKETS + // Free the ndis packet pool in chunk + NdisFreePacketPool((NDIS_HANDLE)pFree->bc_ChunkCtx); +#endif + SpxFreeMemory(pFree); + } + } + + // Free up the ndis buffer pool + NdisFreeBufferPool( + pSpxDevice->dev_NdisBufferPoolHandle); + + return; +} + + + + +PVOID +SpxAllocMem( +#ifdef TRACK_MEMORY_USAGE + IN ULONG Size, + IN ULONG FileLine +#else + IN ULONG Size +#endif // TRACK_MEMORY_USAGE + ) +/*++ + +Routine Description: + + Allocate a block of non-paged memory. This is just a wrapper over ExAllocPool. + Allocation failures are error-logged. We always allocate a ULONG more than + the specified size to accomodate the size. This is used by SpxFreeMemory + to update the statistics. + +Arguments: + + +Return Value: + + +--*/ +{ + PBYTE pBuf; + BOOLEAN zeroed; + + // round up the size so that we can put a signature at the end + // that is on a LARGE_INTEGER boundary + zeroed = ((Size & ZEROED_MEMORY_TAG) == ZEROED_MEMORY_TAG); + + Size = QWORDSIZEBLOCK(Size & ~ZEROED_MEMORY_TAG); + + // Do the actual memory allocation. Allocate eight extra bytes so + // that we can store the size of the allocation for the free routine + // and still keep the buffer quadword aligned. + + if ((pBuf = ExAllocatePoolWithTag(NonPagedPool, Size + sizeof(LARGE_INTEGER) +#if DBG + + sizeof(ULONG) +#endif + ,SPX_TAG)) == NULL) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxAllocMemory: failed - size %lx\n", Size)); + + TMPLOGERR(); + return NULL; + } + + // Save the size of this block in the four extra bytes we allocated. + *((PULONG)pBuf) = (Size + sizeof(LARGE_INTEGER)); + + // Return a pointer to the memory after the size longword. + pBuf += sizeof(LARGE_INTEGER); + +#if DBG + *((PULONG)(pBuf+Size)) = SPX_MEMORY_SIGNATURE; + DBGPRINT(RESOURCES, INFO, + ("SpxAllocMemory: %lx Allocated %lx bytes @%lx\n", + *(PULONG)((PBYTE)(&Size) - sizeof(Size)), Size, pBuf)); +#endif + + SpxTrackMemoryUsage((PVOID)(pBuf - sizeof(LARGE_INTEGER)), TRUE, FileLine); + + if (zeroed) + RtlZeroMemory(pBuf, Size); + + return (pBuf); +} + + + + +VOID +SpxFreeMemory( + IN PVOID pBuf + ) +/*++ + +Routine Description: + + Free the block of memory allocated via SpxAllocMemory. This is + a wrapper around ExFreePool. + +Arguments: + + +Return Value: + + +--*/ +{ + PULONG pRealBuffer; + + // Get a pointer to the block allocated by ExAllocatePool -- + // we allocate a LARGE_INTEGER at the front. + pRealBuffer = ((PULONG)pBuf - 2); + + SpxTrackMemoryUsage(pRealBuffer, FALSE, 0); + +#if DBG + // Check the signature at the end + if (*(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) + != SPX_MEMORY_SIGNATURE) + { + DBGPRINT(RESOURCES, FATAL, + ("SpxFreeMemory: Memory overrun on block %lx\n", pRealBuffer)); + + DBGBRK(FATAL); + } + + *(PULONG)((PCHAR)pRealBuffer + *(PULONG)pRealBuffer) = 0; +#endif + +#if DBG + *pRealBuffer = 0; +#endif + + // Free the pool and return. + ExFreePool(pRealBuffer); +} + + + + +#ifdef TRACK_MEMORY_USAGE + +#define MAX_PTR_COUNT 4*1024 +#define MAX_MEM_USERS 512 +CTELock spxMemTrackLock = {0}; +CTELockHandle lockHandle = {0}; +struct +{ + PVOID mem_Ptr; + ULONG mem_FileLine; +} spxMemPtrs[MAX_PTR_COUNT] = {0}; + +struct +{ + ULONG mem_FL; + ULONG mem_Count; +} spxMemUsage[MAX_MEM_USERS] = {0}; + +VOID +SpxTrackMemoryUsage( + IN PVOID pMem, + IN BOOLEAN Alloc, + IN ULONG FileLine + ) +/*++ + +Routine Description: + + Keep track of memory usage by storing and clearing away pointers as and + when they are allocated or freed. This helps in keeping track of memory + leaks. + +Arguments: + + +Return Value: + + +--*/ +{ + static int i = 0; + int j, k; + + CTEGetLock (&spxMemTrackLock, &lockHandle); + + if (Alloc) + { + for (j = 0; j < MAX_PTR_COUNT; i++, j++) + { + i = i & (MAX_PTR_COUNT-1); + if (spxMemPtrs[i].mem_Ptr == NULL) + { + spxMemPtrs[i].mem_Ptr = pMem; + spxMemPtrs[i++].mem_FileLine = FileLine; + break; + } + } + + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == FileLine) + { + spxMemUsage[k].mem_Count ++; + break; + } + } + if (k == MAX_MEM_USERS) + { + for (k = 0; k < MAX_MEM_USERS; k++) + { + if (spxMemUsage[k].mem_FL == 0) + { + spxMemUsage[k].mem_FL = FileLine; + spxMemUsage[k].mem_Count = 1; + break; + } + } + } + if (k == MAX_MEM_USERS) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: Out of space on spxMemUsage !!!\n")); + + DBGBRK(FATAL); + } + } + else + { + for (j = 0, k = i; j < MAX_PTR_COUNT; j++, k--) + { + k = k & (MAX_PTR_COUNT-1); + if (spxMemPtrs[k].mem_Ptr == pMem) + { + spxMemPtrs[k].mem_Ptr = 0; + spxMemPtrs[k].mem_FileLine = 0; + break; + } + } + } + + CTEFreeLock (&spxMemTrackLock, lockHandle); + + if (j == MAX_PTR_COUNT) + { + DBGPRINT(RESOURCES, ERR, + ("SpxTrackMemoryUsage: %s\n", Alloc ? "Table Full" : "Can't find")); + + DBGBRK(FATAL); + } +} + +#endif // TRACK_MEMORY_USAGE + + + + +PVOID +SpxBPAllocBlock( + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Alloc a block of memory from the block pool package. This is written to speed up + operations where a lot of small fixed size allocations/frees happen. Going to + ExAllocPool() in these cases is expensive. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_HDR pBlk = NULL; + PBLK_CHUNK pChunk, *ppChunkHead; + USHORT BlkSize; + CTELockHandle lockHandle; + PSPX_SEND_RESD pSendResd; + PSPX_RECV_RESD pRecvResd; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PNDIS_BUFFER pNdisIpxSpxBuffer; + + + CTEAssert (BlockId < NUM_BLKIDS); + + if (BlockId < NUM_BLKIDS) + { + BlkSize = spxBlkSize[BlockId]; + ppChunkHead = &spxBPHead[BlockId]; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = *ppChunkHead; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pChunk->bc_NumFrees > 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Found space in Chunk %lx\n", pChunk)); +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPHits); +#endif + break; + } + } + + if (pChunk == NULL) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Allocating a new chunk for Id %d\n", BlockId)); + +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPMisses); +#endif + pChunk = SpxAllocateMemory(spxChunkSize[BlockId]); + if (pChunk != NULL) + { + LONG i, j; + PBLK_HDR pBlkHdr; + USHORT NumBlksPerChunk; + + NumBlksPerChunk = spxNumBlks[BlockId]; + pChunk->bc_NumFrees = NumBlksPerChunk; + pChunk->bc_BlkId = BlockId; + pChunk->bc_FreeHead = (PBLK_HDR)((PBYTE)pChunk + sizeof(BLK_CHUNK)); + + DBGPRINT(SYSTEM, INFO, + ("SpxBPAllocBlock: Initializing chunk %lx\n", pChunk)); + + // Initialize the blocks in the chunk + for (i = 0, pBlkHdr = pChunk->bc_FreeHead; + i < NumBlksPerChunk; + i++, pBlkHdr = pBlkHdr->bh_Next) + { + NDIS_STATUS ndisStatus; + + pBlkHdr->bh_Next = (PBLK_HDR)((PBYTE)pBlkHdr + BlkSize); + if (BlockId == BLKID_NDISSEND) + { + PBYTE pHdrMem; + +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + // Allocate a ndis buffer descriptor describing hdr memory + // and queue it in. + pHdrMem = (PBYTE)pNdisPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD); + + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem, + IpxMacHdrNeeded); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + + + NdisAllocateBuffer( + &ndisStatus, + &pNdisIpxSpxBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pHdrMem + IpxMacHdrNeeded, + MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisIpxSpxBuffer); + + + + pSendResd = (PSPX_SEND_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + + // Initialize elements of the protocol reserved structure. + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = SPX_SENDPKT_IDLE; + } + else if (BlockId == BLKID_NDISRECV) + { +#ifdef SPX_OWN_PACKETS + // Point to the ndis packet,initialize it. + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisReinitializePacket(pNdisPkt); + + pRecvResd = (PSPX_RECV_RESD)pNdisPkt->ProtocolReserved; + +#else + // Allocate a ndis packet pool for this chunk + NdisAllocatePacketPool(); + etc. +#endif + + // Initialize elements of the protocol reserved structure. + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = SPX_RECVPKT_IDLE; + } + } + + if (i != NumBlksPerChunk) + { + // This has to be a failure from Ndis for send blocks!!! + // Undo a bunch of stuff + CTEAssert (BlockId == BLKID_NDISSEND); + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < i; j++, pBlkHdr = pBlkHdr->bh_Next) + { + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisBuffer); + + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)), + &pNdisIpxSpxBuffer); + + if (pNdisIpxSpxBuffer) + { + NdisFreeBuffer(pNdisIpxSpxBuffer); + } + } + + SpxFreeMemory(pChunk); + pChunk = NULL; + } + else + { + // Successfully initialized the chunk, link it in + pChunk->bc_Next = *ppChunkHead; + *ppChunkHead = pChunk; + } + } + } + + if (pChunk != NULL) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + DBGPRINT(RESOURCES, INFO, + ("SpxBPAllocBlock: Allocating a block out of chunk %lx(%d) for Id %d\n", + pChunk, pChunk->bc_NumFrees, BlockId)); + + pChunk->bc_NumFrees --; + pChunk->bc_Age = 0; // Reset age + pBlk = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlk->bh_Next; + pBlk->bh_pChunk = pChunk; + + // Skip the block header! + pBlk++; + } + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + } + + return pBlk; +} + + + +VOID +SpxBPFreeBlock( + IN PVOID pBlock, + IN BLKID BlockId + ) +/*++ + +Routine Description: + + Return a block to its owning chunk. + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk; + PBLK_HDR pBlkHdr = (PBLK_HDR)((PCHAR)pBlock - sizeof(BLK_HDR)); + CTELockHandle lockHandle; + + CTEGetLock(&spxBPLock[BlockId], &lockHandle); + + for (pChunk = spxBPHead[BlockId]; + pChunk != NULL; + pChunk = pChunk->bc_Next) + { + CTEAssert(pChunk->bc_BlkId == BlockId); + if (pBlkHdr->bh_pChunk == pChunk) + { + DBGPRINT(SYSTEM, INFO, + ("SpxBPFreeBlock: Returning Block %lx to chunk %lx for Id %d\n", + pBlkHdr, pChunk, BlockId)); + + CTEAssert (pChunk->bc_NumFrees < spxNumBlks[BlockId]); + pChunk->bc_NumFrees ++; + pBlkHdr->bh_Next = pChunk->bc_FreeHead; + pChunk->bc_FreeHead = pBlkHdr; + break; + } + } + CTEAssert ((pChunk != NULL) && (pChunk->bc_FreeHead == pBlkHdr)); + + CTEFreeLock(&spxBPLock[BlockId], lockHandle); + return; +} + + + + +ULONG +spxBPAgePool( + IN PVOID Context, + IN BOOLEAN TimerShuttingDown + ) +/*++ + +Routine Description: + + Age out the block pool of unused blocks + +Arguments: + + +Return Value: + + +--*/ +{ + PBLK_CHUNK pChunk, *ppChunk, pFree = NULL; + LONG i, j, NumBlksPerChunk; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + + if (TimerShuttingDown) + { + return TIMER_DONT_REQUEUE; + } + + for (i = 0; i < NUM_BLKIDS; i++) + { + NumBlksPerChunk = spxNumBlks[i]; + CTEGetLock(&spxBPLock[i], &lockHandle); + + for (ppChunk = &spxBPHead[i]; + (pChunk = *ppChunk) != NULL; ) + { + if ((pChunk->bc_NumFrees == NumBlksPerChunk) && + (++(pChunk->bc_Age) >= MAX_BLOCK_POOL_AGE)) + { + DBGPRINT(SYSTEM, INFO, + ("spxBPAgePool: freeing Chunk %lx, Id %d\n", + pChunk, pChunk->bc_BlkId)); + + *ppChunk = pChunk->bc_Next; +#ifdef PROFILING + InterlockedIncrement( &SpxStatistics.stat_NumBPAge); +#endif + if (pChunk->bc_BlkId == BLKID_NDISSEND) + { + PBLK_HDR pBlkHdr; + + // We need to free Ndis stuff for these guys + pBlkHdr = pChunk->bc_FreeHead; + for (j = 0, pBlkHdr = pChunk->bc_FreeHead; + j < NumBlksPerChunk; + j++, pBlkHdr = pBlkHdr->bh_Next) + { + pNdisPkt = (PNDIS_PACKET)((PBYTE)pBlkHdr + sizeof(BLK_HDR)); + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + + NdisUnchainBufferAtFront( + pNdisPkt, + &pNdisBuffer); + + NdisFreeBuffer(pNdisBuffer); + } + } + + SpxFreeMemory(pChunk); + } + else + { + ppChunk = &pChunk->bc_Next; + } + } + CTEFreeLock(&spxBPLock[i], lockHandle); + } + + return TIMER_REQUEUE_CUR_VALUE; +} diff --git a/private/ntos/tdi/isnp/spx/spxpkt.c b/private/ntos/tdi/isnp/spx/spxpkt.c new file mode 100644 index 000000000..5f472d8cd --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxpkt.c @@ -0,0 +1,1594 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxpkt.c + +Abstract: + + This module contains code that builds various spx packets. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXPKT + +VOID +SpxPktBuildCr( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + IN OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + NOTE: If *ppPkt is NULL, we allocate a packet. If not, we just + recreate the data and don't update the packet's state. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrPkt; + PSPX_SEND_RESD pSendResd; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + if (*ppPkt == NULL) { + + SpxAllocSendPacket(SpxDevice, &pCrPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + } else { + + pCrPkt = *ppPkt; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + NdisQueryPacket(pCrPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? (SPX_CC_SPX2 | SPX_CC_NEG) : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = 0xFFFF; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + // Initialize + + if (*ppPkt == NULL) { + + pSendResd = (PSPX_SEND_RESD)(pCrPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX_HDRSIZE; + + *ppPkt = pCrPkt; + } + + return; +} + + + + +VOID +SpxPktBuildCrAck( + IN PSPX_CONN_FILE pSpxConnFile, + IN PSPX_ADDR pSpxAddr, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fNeg, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pCrAckPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pCrAckPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnHandleConnReq: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pCrAckPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pCrAckPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxAddr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | + (fSpx2 ? SPX_CC_SPX2 : 0) | + (fNeg ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnBuildCrAck: Spx2 packet size %d.%lx\n", + pSpxConnFile->scf_MaxPktSize)); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + + pSendResd = (PSPX_SEND_RESD)(pCrAckPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_CRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pCrAckPkt; + return; +} + + + +VOID +SpxPktBuildSn( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSn: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SN; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSnAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SNACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildSs( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + + CTEAssert(pSpxConnFile->scf_MaxPktSize != 0); + DBGPRINT(SEND, DBG, + ("SpxPktBuildSs: Data size %lx\n", pSpxConnFile->scf_MaxPktSize)); + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SS; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + } while (FALSE); + + return; +} + + + +VOID +SpxPktBuildSsAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_SYS | SPX_CC_SPX2 | + ((pSpxConnFile->scf_Flags & SPX_CONNFILE_NEG) ? SPX_CC_NEG : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + pIpxSpxHdr->hdr_SeqNum = 0; + pIpxSpxHdr->hdr_AckNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_SSACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRr( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT SeqNum, + IN USHORT State + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pBuf; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PBYTE pData; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + if ((pData = + SpxAllocateMemory( + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE)) == NULL) + { + SpxPktSendRelease(pPkt); + break; + } + + // Build ndis buffer desc + NdisAllocateBuffer( + &ndisStatus, + &pBuf, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + pSpxConnFile->scf_MaxPktSize - MIN_IPXSPX2_HDRSIZE); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + SpxPktSendRelease(pPkt); + SpxFreeMemory(pData); + break; + } + + // Chain at back. + NdisChainBufferAtBack( + pPkt, + pBuf); + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + SpxBuildIpxHdr( + pIpxSpxHdr, + pSpxConnFile->scf_MaxPktSize, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = ( SPX_CC_SYS | SPX_CC_ACK | + SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + // For a renegotiate request, we use the sequence number of + // the first waiting data packet. Passed in. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + SeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + + // Init the data part to indicate no neg values + *(UNALIGNED ULONG *)pData = 0; + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RR; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = (State | SPX_SENDPKT_FREEDATA); + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_SeqNum = SeqNum; + pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + pSendResd->sr_Len = pSpxConnFile->scf_MaxPktSize; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildRrAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT MaxPktSize + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + PSPX_SEND_RESD pSendResd; + + do + { + *ppPkt = NULL; + + // Allocate a ndis packet for the cr. + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + + SpxBuildIpxHdr( + pIpxSpxHdr, + MIN_IPXSPX2_HDRSIZE, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_NEG | SPX_CC_SPX2); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + // For the RrAck, ack number will be the appropriate number + // for the last data packet received. + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RenegAckAckNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + MaxPktSize); + + DBGPRINT(SEND, DBG3, + ("SpxPktBuildRrAck: SEQ %lx ACKNUM %lx ALLOCNUM %lx MAXPKT %lx\n", + pSpxConnFile->scf_SendSeqNum, + pSpxConnFile->scf_RenegAckAckNum, + pSpxConnFile->scf_SentAllocNum, + MaxPktSize)); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_RRACK; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = MIN_IPXSPX2_HDRSIZE; + + *ppPkt = pPkt; + + } while (FALSE); + + return; +} + + + + +VOID +SpxPktBuildDisc( + IN PSPX_CONN_FILE pSpxConnFile, + IN PREQUEST pRequest, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN UCHAR DataType + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDiscPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDiscPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDiscPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pDiscPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (SPX_CC_ACK | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0) | + ((DataType == SPX2_DT_IDISC) ? 0 : SPX_CC_EOM)); + + pIpxSpxHdr->hdr_DataType = DataType; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDiscPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = + ((DataType == SPX2_DT_IDISC) ? SPX_TYPE_IDISC : SPX_TYPE_ORDREL); + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pRequest; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Offset = 0; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pDiscPkt; + } + + return; +} + + + + +VOID +SpxPktBuildProbe( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fSpx2 + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pProbe; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pProbe, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pProbe + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE); + + NdisQueryPacket(pProbe, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | SPX_CC_ACK | + (fSpx2 ? SPX_CC_SPX2 : 0)); + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pProbe->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = SPX_TYPE_PROBE; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = NULL; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Len = + pSendResd->sr_HdrLen = (fSpx2 ? MIN_IPXSPX2_HDRSIZE + : MIN_IPXSPX_HDRSIZE); + + *ppPkt = pProbe; + } + + return; +} + + + + +VOID +SpxPktBuildData( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN USHORT Length + ) +/*++ + +Routine Description: + + Handles zero length sends. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_BUFFER pNdisBuffer; + PSPX_SEND_RESD pSendResd; + PNDIS_PACKET pDataPkt; + NDIS_STATUS ndisStatus; + PIPXSPX_HDR pIpxSpxHdr; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pDataPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Make a ndis buffer descriptor for the data if present. + if (Length > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_ReqPkt), + pSpxConnFile->scf_ReqPktOffset, + Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the send packet + SpxPktSendRelease(pDataPkt); + return; + } + + // Chain this in the packet + NdisChainBufferAtBack(pDataPkt, pNdisBuffer); + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pDataPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + Length += hdrLen; + + NdisQueryPacket(pDataPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!SPX2_CONN(pSpxConnFile)) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + SpxBuildIpxHdr( + pIpxSpxHdr, + Length, + pSpxConnFile->scf_RemAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + // Build SPX Header. + pIpxSpxHdr->hdr_ConnCtrl = + (((State & SPX_SENDPKT_ACKREQ) ? SPX_CC_ACK : 0) | + ((State & SPX_SENDPKT_EOM) ? SPX_CC_EOM : 0) | + (SPX2_CONN(pSpxConnFile) ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = (UCHAR)REQUEST_PARAMETERS(pSpxConnFile->scf_ReqPkt)->Others.Argument3; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + pIpxSpxHdr->hdr_DestConnId = + *((UNALIGNED USHORT *)&pSpxConnFile->scf_RemConnId); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + if (SPX2_CONN(pSpxConnFile)) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + + pSendResd = (PSPX_SEND_RESD)(pDataPkt->ProtocolReserved); + + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_State = State; + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_Type = SPX_TYPE_DATA; + pSendResd->sr_Next = NULL; + pSendResd->sr_Request = pSpxConnFile->scf_ReqPkt; + pSendResd->sr_Offset = pSpxConnFile->scf_ReqPktOffset; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_SeqNum = pSpxConnFile->scf_SendSeqNum; + pSendResd->sr_Len = Length; + pSendResd->sr_HdrLen = hdrLen; + + if (State & SPX_SENDPKT_ACKREQ) + { + KeQuerySystemTime((PLARGE_INTEGER)&pSendResd->sr_SentTime); + } + + CTEAssert(pSendResd->sr_Len <= pSpxConnFile->scf_MaxPktSize); + *ppPkt = pDataPkt; + + // Ok, allocation succeeded. Increment send seq. + pSpxConnFile->scf_SendSeqNum++; + } + + return; +} + + +VOID +SpxCopyBufferChain( + OUT PNDIS_STATUS Status, + OUT PNDIS_BUFFER * TargetChain, + IN NDIS_HANDLE PoolHandle, + IN PNDIS_BUFFER SourceChain, + IN UINT Offset, + IN UINT Length + ) +/*++ + +Routine Description: + + Creates a TargetBufferChain from the SourceBufferChain. The copy begins at + the 'Offset' location in the source chain. It copies 'Length' bytes. It also + handles Length = 0. If we run out of source chain before copying length amount + of bytes or run out of memory to create any more buffers for the target chain, + we clean up the partial chain created so far. + +Arguments: + + Status - Status of the request. + TargetChain - Pointer to the allocated buffer descriptor. + PoolHandle - Handle that is used to specify the pool. + SourceChain - Pointer to the descriptor of the source memory. + Offset - The Offset in the sources memory from which the copy is to + begin + Length - Number of Bytes to copy. + +Return Value: + + None. + +--*/ +{ + UINT BytesBeforeCurBuffer = 0; + PNDIS_BUFFER CurBuffer = SourceChain; + UINT BytesLeft; + UINT AvailableBytes; + PNDIS_BUFFER NewNdisBuffer, StartTargetChain; + + CTEAssert( SourceChain ); + + // First of all find the source buffer that contains data that starts at + // Offset. + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( BytesBeforeCurBuffer + AvailableBytes <= Offset ) { + BytesBeforeCurBuffer += AvailableBytes; + CurBuffer = CurBuffer->Next; + if ( CurBuffer ) { + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } else { + break; + } + } + + if ( ! CurBuffer ) { + *Status = STATUS_UNSUCCESSFUL; + return; + } + + // + // Copy the first buffer. This takes care of Length = 0. + // + BytesLeft = Length; + + // + // ( Offset - BytesBeforeCurBuffer ) gives us the offset within this buffer. + // + + AvailableBytes -= ( Offset - BytesBeforeCurBuffer ); + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &NewNdisBuffer, + PoolHandle, + CurBuffer, + Offset - BytesBeforeCurBuffer, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + return; + } + + StartTargetChain = NewNdisBuffer; + BytesLeft -= AvailableBytes; + + // + // Did the first buffer have enough data. If so, we r done. + // + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + // + // Now follow the Mdl chain and copy more buffers. + // + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + while ( CurBuffer ) { + + if ( AvailableBytes > BytesLeft ) { + AvailableBytes = BytesLeft; + } + + NdisCopyBuffer( + Status, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + PoolHandle, + CurBuffer, + 0, + AvailableBytes); + + if ( *Status != NDIS_STATUS_SUCCESS ) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + return; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + BytesLeft -= AvailableBytes; + + if ( ! BytesLeft ) { + *TargetChain = StartTargetChain; + return; + } + + CurBuffer = CurBuffer->Next; + NdisQueryBuffer( CurBuffer, NULL, &AvailableBytes ); + } + + // + // Ran out of source chain. This should not happen. + // + + CTEAssert( FALSE ); + + // For Retail build we clean up anyways. + + while ( StartTargetChain != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE( StartTargetChain ); + NdisFreeBuffer ( StartTargetChain ); + StartTargetChain = NewNdisBuffer; + } + + *Status = STATUS_UNSUCCESSFUL; + return; +} + + +VOID +SpxPktBuildAck( + IN PSPX_CONN_FILE pSpxConnFile, + OUT PNDIS_PACKET * ppPkt, + IN USHORT State, + IN BOOLEAN fBuildNack, + IN USHORT NumToResend + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pPkt; + PSPX_SEND_RESD pSendResd; + PIPXSPX_HDR pIpxSpxHdr; + NDIS_STATUS ndisStatus; + USHORT hdrLen; + PNDIS_BUFFER pNdisMacHdr, pNdisIpxHdr; + + BOOLEAN fSpx2 = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2); + + *ppPkt = NULL; + + SpxAllocSendPacket(SpxDevice, &pPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, ERR, + ("SpxPktBuildAck: Could not allocate ndis packet\n")); + return; + } + + pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + + NDIS_PACKET_SIZE + + sizeof(SPX_SEND_RESD) + + IpxInclHdrOffset); + + hdrLen = SPX2_CONN(pSpxConnFile) ? MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE; + NdisQueryPacket(pPkt, NULL, NULL, &pNdisMacHdr, NULL); + pNdisIpxHdr = NDIS_BUFFER_LINKAGE(pNdisMacHdr); + if (!fSpx2) + { + NdisAdjustBufferLength(pNdisIpxHdr, MIN_IPXSPX_HDRSIZE); + } + + // Send where data came from + SpxBuildIpxHdr( + pIpxSpxHdr, + hdrLen, + pSpxConnFile->scf_RemAckAddr, + pSpxConnFile->scf_AddrFile->saf_Addr->sa_Socket); + + pIpxSpxHdr->hdr_ConnCtrl = (SPX_CC_SYS | (fSpx2 ? SPX_CC_SPX2 : 0)); + + pIpxSpxHdr->hdr_DataType = 0; + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SrcConnId, + pSpxConnFile->scf_LocalConnId); + + pIpxSpxHdr->hdr_DestConnId = pSpxConnFile->scf_RemConnId; + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AckNum, + pSpxConnFile->scf_RecvSeqNum); + + if (fSpx2) + { + pIpxSpxHdr->hdr_SeqNum = 0; + if (fBuildNack) + { + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + NumToResend); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_NegSize, + pSpxConnFile->scf_MaxPktSize); + } + else + { + // Put current send seq number in packet for spx1 + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_SeqNum, + pSpxConnFile->scf_SendSeqNum); + } + + PUTSHORT2SHORT( + &pIpxSpxHdr->hdr_AllocNum, + pSpxConnFile->scf_SentAllocNum); + + pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); + pSendResd->sr_Id = IDENTIFIER_SPX; + pSendResd->sr_Type = (fBuildNack ? SPX_TYPE_DATANACK : SPX_TYPE_DATAACK); + pSendResd->sr_Reserved1 = NULL; + pSendResd->sr_Reserved2 = NULL; + pSendResd->sr_State = State; + pSendResd->sr_ConnFile = pSpxConnFile; + pSendResd->sr_Request = NULL; + pSendResd->sr_Next = NULL; + pSendResd->sr_Len = pSendResd->sr_HdrLen = hdrLen; + + *ppPkt = pPkt; + return; +} + + + +VOID +SpxPktRecvRelease( + IN PNDIS_PACKET pPkt + ) +{ + ((PSPX_RECV_RESD)(pPkt->ProtocolReserved))->rr_State = SPX_RECVPKT_IDLE; + SpxFreeRecvPacket(SpxDevice, pPkt); + return; +} + + + + +VOID +SpxPktSendRelease( + IN PNDIS_PACKET pPkt + ) +{ + PNDIS_BUFFER pBuf, pIpxSpxBuf, pFreeBuf; + UINT bufCount; + + CTEAssert((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_IPXOWNS) == 0); + + NdisQueryPacket(pPkt, NULL, &bufCount, &pBuf, NULL); + + // BufCount == 1 for only the header. That's ok, we just reset the length + // and free the packet to the buffer pools. Else we need to free user buffers + // before that. + + NdisUnchainBufferAtFront( + pPkt, + &pBuf); + + NdisUnchainBufferAtFront( + pPkt, + &pIpxSpxBuf); + + // + // Set the header length to the max. that can be needed. + // + NdisAdjustBufferLength(pIpxSpxBuf, MIN_IPXSPX2_HDRSIZE); + + while (bufCount-- > 2) + { + PBYTE pData; + ULONG dataLen; + + NdisUnchainBufferAtBack( + pPkt, + &pFreeBuf); + + // See if we free data associated with the buffer + if ((((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State & + SPX_SENDPKT_FREEDATA) != 0) + { + NdisQueryBuffer(pFreeBuf, &pData, &dataLen); + CTEAssert(pData != NULL); + SpxFreeMemory(pData); + } + + CTEAssert(pFreeBuf != NULL); + NdisFreeBuffer(pFreeBuf); + } + + NdisReinitializePacket(pPkt); + + // Initialize elements of the protocol reserved structure. + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Id = IDENTIFIER_SPX; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_State = SPX_SENDPKT_IDLE; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved1= NULL; + ((PSPX_SEND_RESD)(pPkt->ProtocolReserved))->sr_Reserved2= NULL; + + NdisChainBufferAtFront( + pPkt, + pBuf); + + NdisChainBufferAtBack( + pPkt, + pIpxSpxBuf); + + SpxFreeSendPacket(SpxDevice, pPkt); + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxquery.c b/private/ntos/tdi/isnp/spx/spxquery.c new file mode 100644 index 000000000..047ecabe8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxquery.c @@ -0,0 +1,259 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxquery.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + +Author: + + Adam Barr (adamba) Initial Version + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxQueryInitProviderInfo) +#endif + +// Define module number for event logging entries +#define FILENUM SPXQUERY + +// Useful macro to obtain the total length of an MDL chain. +#define SpxGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + + +VOID +SpxQueryInitProviderInfo( + PTDI_PROVIDER_INFO ProviderInfo + ) +{ + // Initialize to defaults first + RtlZeroMemory((PVOID)ProviderInfo, sizeof(TDI_PROVIDER_INFO)); + + ProviderInfo->Version = SPX_TDI_PROVIDERINFO_VERSION; + KeQuerySystemTime (&ProviderInfo->StartTime); + ProviderInfo->MinimumLookaheadData = SPX_PINFOMINMAXLOOKAHEAD; + ProviderInfo->MaximumLookaheadData = IpxLineInfo.MaximumPacketSize; + ProviderInfo->MaxSendSize = SPX_PINFOSENDSIZE; + ProviderInfo->ServiceFlags = SPX_PINFOSERVICEFLAGS; + return; +} + + + + +NTSTATUS +SpxTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PSPX_ADDR_FILE AddressFile; + PSPX_CONN_FILE ConnectionFile; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + struct { + ULONG ActivityCount; + TA_IPX_ADDRESS SpxAddress; + } AddressInfo; + + + + // what type of status do we want? + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (query->QueryType) + { + case TDI_QUERY_CONNECTION_INFO: + + status = STATUS_NOT_IMPLEMENTED; + break; + + case TDI_QUERY_ADDRESS_INFO: + + // The caller wants the exact address value. + + ConnectionFile = (PSPX_CONN_FILE)REQUEST_OPEN_CONTEXT(Request); + status = SpxConnFileVerify(ConnectionFile); + + if (status == STATUS_SUCCESS) { + AddressFile = ConnectionFile->scf_AddrFile; + SpxConnFileDereference(ConnectionFile, CFREF_VERIFY); + } else { + AddressFile = (PSPX_ADDR_FILE)REQUEST_OPEN_CONTEXT(Request); + } + + status = SpxAddrFileVerify(AddressFile); + + if (status == STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, INFO, + ("SpxTdiQuery: Net.Socket %lx.%lx\n", + *(PULONG)Device->dev_Network, + AddressFile->saf_Addr->sa_Socket)); + + AddressInfo.ActivityCount = 0; + (VOID)SpxBuildTdiAddress( + &AddressInfo.SpxAddress, + sizeof(TA_IPX_ADDRESS), + Device->dev_Network, + Device->dev_Node, + AddressFile->saf_Addr->sa_Socket); + + status = TdiCopyBufferToMdl( + &AddressInfo, + 0, + sizeof(AddressInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + SpxAddrFileDereference(AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_PROVIDER_INFO: { + BYTE socketType; + TDI_PROVIDER_INFO providerInfo = Device->dev_ProviderInfo; + + // + // The device name extension comes down in the Irp + // + if (!NT_SUCCESS(status = SpxUtilGetSocketType( + REQUEST_OPEN_NAME(Request), + &socketType))) { + DBGPRINT(RECEIVE, ERR, ("TDI_QUERY_PROVIDER_INFO: SpxUtilGetSocketType failed: %lx\n", status)); + return(status); + } + + // + // The Catapult folks had a problem where AFD was discarding buffered sends on the NT box when it got a + // local disconnect on SPX1. This was because the Orderly release flag was always set in the provider + // info. AFD queries this once per device type. We detect the device above and OR in the orderly release + // flag if this query came down on an SPX2 endpoint. + // This is to make sure that AFD follows the correct disconnect semantics for SPX1 and SPX2 (SPX1 does + // only abortive; SPX2 does both abortive and orderly). + // + // BUGBUG: this will still not solve the problem completely since a connection that starts off as an SPX2 + // one can still be negotiated to SPX1 if the remote supports only SPX1. + // + if ((socketType == SOCKET2_TYPE_SEQPKT) || + (socketType == SOCKET2_TYPE_STREAM)) { + + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX2 socket\n")); + providerInfo.ServiceFlags |= TDI_SERVICE_ORDERLY_RELEASE; + } else { + DBGPRINT(RECEIVE, INFO, ("TDI_QUERY_PROVIDER_INFO: SPX1 socket\n")); + } + + status = TdiCopyBufferToMdl ( + &providerInfo, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + } + + case TDI_QUERY_PROVIDER_STATISTICS: + + status = TdiCopyBufferToMdl ( + &Device->dev_Stat, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_TDI_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} // SpxTdiQueryInformation + + + +NTSTATUS +SpxTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} // SpxTdiSetInformation + diff --git a/private/ntos/tdi/isnp/spx/spxrecv.c b/private/ntos/tdi/isnp/spx/spxrecv.c new file mode 100644 index 000000000..2408f25e8 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxrecv.c @@ -0,0 +1,2837 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxrecv.c + +Abstract: + + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + Sanjay Anand (SanjayAn) 5-July-1995 + Bug fixes - tagged [SA] + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXRECV + +VOID +SpxReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +{ + PIPXSPX_HDR pHdr; + + // We have a separate routine to process SYS packets. DATA packets are + // processed within this routine. + if (LookaheadBufferSize < MIN_IPXSPX_HDRSIZE) + { + DBGPRINT(RECEIVE, ERR, + ("SpxReceive: Invalid length %lx\n", LookaheadBufferSize)); + + return; + } + + ++SpxDevice->dev_Stat.PacketsReceived; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + if ((pHdr->hdr_ConnCtrl & SPX_CC_SYS) == 0) + { + // Check for data packets + if ((pHdr->hdr_DataType != SPX2_DT_ORDREL) && + (pHdr->hdr_DataType != SPX2_DT_IDISC) && + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK)) + { + // HANDLE DATA PACKET + SpxRecvDataPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + else + { + // The whole packet better be in the lookahead, else we ignore. + if (LookaheadBufferSize == PacketSize) + { + SpxRecvDiscPacket( + LookaheadBuffer, + RemoteAddress, + LookaheadBufferSize); + } + } + } + else + { + SpxRecvSysPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } + + return; +} + + + + +VOID +SpxTransferDataComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PSPX_CONN_FILE pSpxConnFile; + PREQUEST pRequest; + PSPX_RECV_RESD pRecvResd; + CTELockHandle lockHandle; + NTSTATUS status; + BOOLEAN fAck, fEom, fBuffered, fImmedAck, fLockHeld; + PNDIS_BUFFER pNdisBuffer; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: For %lx with status %lx\n", pNdisPkt, NdisStatus)); + + pRecvResd = RECV_RESD(pNdisPkt); + pSpxConnFile = pRecvResd->rr_ConnFile; + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + + fEom = ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + fImmedAck = ((pRecvResd->rr_State & SPX_RECVPKT_IMMEDACK) != 0); + fBuffered = ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + fAck = ((pRecvResd->rr_State & SPX_RECVPKT_SENDACK) != 0); + + // Check if receive is done. If we remove the reference for this + // packet and it goes to zero, that means the receive was aborted. + // Move to the completion queue. + // If receive is filled up, then remove the creation reference + // i.e. just complete the receive at this point. + // There can be only one packet per receive, we dont support + // out of order reception. + + if (!fBuffered) + { + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert((pNdisBuffer != NULL) || (BytesTransferred == 0)); + + // BUG #11772 + // On MP-machines scf_CurRecvReq could be set to NULL. Get the req + // from the recv packet. + // pRequest = pSpxConnFile->scf_CurRecvReq; + // CTEAssert(pRequest == pRecvResd->rr_Request); + pRequest = pRecvResd->rr_Request; + + // Remove reference for this packet. + --(REQUEST_INFORMATION(pRequest)); + + if (NdisStatus == NDIS_STATUS_SUCCESS) + { + pSpxConnFile->scf_CurRecvOffset += BytesTransferred; + pSpxConnFile->scf_CurRecvSize -= BytesTransferred; + +#if DBG + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (BytesTransferred != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= BytesTransferred; + } + } +#endif + + if (REQUEST_INFORMATION(pRequest) == 0) + { + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom || + ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL))) + { + CTELockHandle lockHandleInter; + + // We are done with this receive. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + + status = STATUS_SUCCESS; + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + status = STATUS_RECEIVE_PARTIAL; + } + + if ((REQUEST_STATUS(pRequest) != STATUS_SUCCESS) && + (REQUEST_STATUS(pRequest) != STATUS_RECEIVE_PARTIAL)) + { + status = REQUEST_STATUS(pRequest); + } + + REQUEST_STATUS(pRequest) = status; + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferDataComplete: Request %lx ref %lx Cur %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest), + REQUEST_STATUS(pRequest), + pSpxConnFile->scf_CurRecvSize)); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + } + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + else + { + // Buffered receive, queue it in if successful. + // BUG #18363 + // IF WE DISCONNECTED in the meantime, we need to just dump this + // packet. + if (SPX_CONN_ACTIVE(pSpxConnFile) && + (NdisStatus == NDIS_STATUS_SUCCESS)) + { + // Queue packet in connection. Reference connection for this. + SpxConnQueueRecvPktTail(pSpxConnFile, pNdisPkt); + SpxConnFileLockReference(pSpxConnFile, CFREF_VERIFY); + + DBGPRINT(RECEIVE, DBG, + ("SpxTransferData: Buffering: %lx Pkt %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, BytesTransferred, pRecvResd->rr_State)); + + // There could either be queued receives. (This could happen in + // a partial receive case. Or if a receive got queued in while we + // were processing this packet (Possible on MP)), or a packet was + // buffered while we were completing some receives + + CTEAssert(pSpxConnFile->scf_RecvListHead); + + if ((pSpxConnFile->scf_CurRecvReq != NULL) || + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)) + { + CTELockHandle interLockHandle; + + // Push this connection into a ProcessRecv queue which will be + // dealt with in receive completion. + + DBGPRINT(RECEIVE, DBG, + ("spxRecvTransferData: Queueing for recvp %lx.%lx\n", + pSpxConnFile, pSpxConnFile->scf_Flags)); + + // Get the global q lock, push into recv list. + CTEGetLock(&SpxGlobalQInterlock, &interLockHandle); + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, interLockHandle); + } + } + else + { + PBYTE pData; + ULONG dataLen; + + // Get pointer to the buffer descriptor and its memory. + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + + // Free the data, ndis buffer. + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + SpxFreeMemory(pData); + } + + // Dont send ack, set status to be failure so we free packet/buffer. + fAck = FALSE; + NdisStatus = NDIS_STATUS_FAILURE; + } + } + + END_PROCESS_PACKET( + pSpxConnFile, fBuffered, (NdisStatus == NDIS_STATUS_SUCCESS)); + + if (fAck) + { + // Rem ack addr should have been copied in receive. + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (!fBuffered || (NdisStatus != STATUS_SUCCESS)) + { + // Free the ndis packet/buffer + SpxPktRecvRelease(pNdisPkt); + } + + return; +} + + + + +VOID +SpxReceiveComplete( + IN USHORT NicId + ) + +{ + CTELockHandle lockHandleInter, lockHandle; + PREQUEST pRequest; + BOOLEAN fConnLockHeld, fInterlockHeld; + PSPX_CONN_FILE pSpxConnFile; + int numDerefs = 0; + + // See if any connections need recv processing. This will also take + // care of any acks opening up window so our sends go to the max. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + fInterlockHeld = TRUE; + + while ((pSpxConnFile = SpxRecvConnList.pcl_Head) != NULL) + { + // Reset for each connection + numDerefs = 0; + + if ((SpxRecvConnList.pcl_Head = pSpxConnFile->scf_ProcessRecvNext) == NULL) + SpxRecvConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_ProcessRecvNext = NULL; + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromRecv: %lx\n", pSpxConnFile)); + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + do + { + // Complete pending requests. + while (!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) + { + pRequest = + LIST_ENTRY_TO_REQUEST(pSpxConnFile->scf_ReqDoneLinkage.Flink); + + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert (REQUEST_MINOR_FUNCTION(pRequest) != TDI_RECEIVE); + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + + // Call process pkts if we have any packets or if any receives to + // complete. Note this will call even when there are no receives + // queued and the first packet has already been indicated. + if ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage) || + (pSpxConnFile->scf_RecvListHead != NULL))) + { + // We have the flag reference on the connection. + SpxRecvProcessPkts(pSpxConnFile, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + } + +#if DBG + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: RecvDone left %lx\n", + pSpxConnFile)); + } +#endif + + // Hmm. This check is rather expensive, and essentially we are doing + // it twice. Should look to see if this can be modified safely. + } while ((!IsListEmpty(&pSpxConnFile->scf_ReqDoneLinkage)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) && + ((!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + (SPX_RECVPKT_BUFFERING | SPX_RECVPKT_INDICATED)) == + SPX_RECVPKT_BUFFERING))))); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_RECVQ); + SpxConnFileTransferReference( + pSpxConnFile, + CFREF_RECV, + CFREF_VERIFY); + + numDerefs++; + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + + // First see if we need to packetize. + while ((pSpxConnFile = SpxPktConnList.pcl_Head) != NULL) + { + if ((SpxPktConnList.pcl_Head = pSpxConnFile->scf_PktNext) == NULL) + SpxPktConnList.pcl_Tail = NULL; + + // Reset next field to NULL + pSpxConnFile->scf_PktNext = NULL; + + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + + DBGPRINT(SEND, DBG, + ("SpxConnRemoveFromPkt: %lx\n", pSpxConnFile)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fConnLockHeld = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("SpxReceiveComplete: Packetizing %lx\n", pSpxConnFile)); + + SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_PKTQ); + if (SPX_SEND_STATE(pSpxConnFile) == SPX_SEND_IDLE) + { + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_PACKETIZE); + if (SpxConnPacketize( + pSpxConnFile, + TRUE, + lockHandle)) + { + // Done. + fConnLockHeld = FALSE; + } + } + + if (fConnLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_PKTIZE); + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + } + + if (fInterlockHeld) + { + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + return; +} + + + + +// +// PACKET HANDLING ROUTINES +// + + +VOID +SpxRecvSysPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming system packet. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld = FALSE; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvSysPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + if ((pktLen == SPX_CR_PKTLEN) && + (destConnId == 0xFFFF) && + (pHdr->hdr_ConnCtrl & SPX_CC_CR)) + { + spxConnHandleConnReq( + pHdr, + pRemoteAddr); + + return; + } + + // + // [SA] Bug #14917 + // Some SPX SYS packets (no extended ack field) may come in with the SPX2 bit set. + // Make sure we don't discard these packets. + // + + // if ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && (pktLen < MIN_IPXSPX2_HDRSIZE)) + // { + // return; + // } + + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnSysPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnSysPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Id %lx NOT FOUND\n", destConnId)); + return; + } + + do + { + + DBGPRINT(RECEIVE, INFO, + ("SpxConnSysPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // This could be one of many packets. Connection ack/Session negotiate/ + // Session setup, Data Ack, Probe/Ack, Renegotiate/Ack. We shunt + // off all the packets to different routines but process the data + // ack packets here. + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAddr); + + lockHeld = TRUE; + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + switch (SPX_MAIN_STATE(pSpxConnFile)) + { + case SPX_CONNFILE_CONNECTING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromSrv( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_LISTENING: + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + lockHeld = FALSE; + + spxConnHandleSessPktFromClient( + pHdr, + pRemoteAddr, + pSpxConnFile); + + break; + + case SPX_CONNFILE_ACTIVE: + case SPX_CONNFILE_DISCONN: + + // NOTE: Our ack to a session setup might get dropped. + // But the SS Ack is similar to a normal SPX2 ack. + // We dont have to do anything special. + + // Received ack/nack/reneg/reneg ack/disc associated packet. + // Disc packets except ordrel ack have non-zero datastream type. + if ((pHdr->hdr_ConnCtrl & + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) == + (SPX_CC_SYS | SPX_CC_ACK | SPX_CC_NEG | SPX_CC_SPX2)) + { + // We received a renegotiate packet. Ignore all ack values + // in a reneg req. + SpxConnProcessRenegReq(pSpxConnFile, pHdr, pRemoteAddr, lockHandle); + lockHeld = FALSE; + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + // Check if we are an ack/nack packet in which case call process + // ack. Note that the spx2 orderly release ack is a normal spx2 ack. + if (((pHdr->hdr_ConnCtrl & SPX_CC_ACK) == 0) && + (pHdr->hdr_DataType == 0)) + { + SpxConnProcessAck(pSpxConnFile, pHdr, lockHandle); + lockHeld = FALSE; + } + else + { + // Just process the numbers we got. + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + lockHeld = FALSE; + } + + // If the remote wants us to send an ack, do it. + if (pHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // First copy the remote address in connection. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (!lockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + } + + SpxConnSendAck(pSpxConnFile, lockHandle); + lockHeld = FALSE; + break; + } + + break; + + default: + + // Ignore this packet. + DBGPRINT(RECEIVE, WARN, + ("SpxConnSysPacket: Ignoring packet, state is not active\n")); + break; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvDiscPacket( + IN PUCHAR LookaheadBuffer, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN UINT LookaheadSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status; + PIPXSPX_HDR pHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + PSPX_CONN_FILE pSpxConnFile; + CTELockHandle lockHandle; + BOOLEAN lockHeld; + + pHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (LookaheadSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pHdr->hdr_SeqNum); + GETSHORT2SHORT(&ackNum, &pHdr->hdr_AckNum); + GETSHORT2SHORT(&allocNum, &pHdr->hdr_AllocNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvDiscPacket: Packet Size %lx\n", + pktLen)); + + return; + } + + // We keep and use the remote id in the net format. This maintains the + // 0x0 and 0xFFFF to be as in the host format. + srcConnId = *(USHORT UNALIGNED *)&pHdr->hdr_SrcConnId; + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDiscPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: packet received dest %lx src %lx\n", + pHdr->hdr_DestSkt, pHdr->hdr_SrcSkt)); + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDiscPacket: Id %lx NOT FOUND", destConnId)); + + return; + } + + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: Id %lx Conn %lx DiscType %lx\n", + destConnId, pSpxConnFile, pHdr->hdr_DataType)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; + + // Unless we are in the active/disconnecting, but send state = idle + // and recv state = idle/recv posted, we ignore all disconnect packets. + if (((SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_ACTIVE) && + (SPX_MAIN_STATE(pSpxConnFile) != SPX_CONNFILE_DISCONN)) || + ((SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_IDLE) && + (SPX_SEND_STATE(pSpxConnFile) != SPX_SEND_PACKETIZE)) || + ((SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_IDLE) && + (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_POSTED)) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT))) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx, %lx, %lx.%lx, %d.%d\n", + pSpxConnFile, + SPX_MAIN_STATE(pSpxConnFile), + SPX_SEND_STATE(pSpxConnFile), SPX_RECV_STATE(pSpxConnFile), + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)), + (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)))); + + break; + } + + // If we have received a disconnect, process received ack to complete any + // pending sends before we allow the disconnect. This ack number will be + // the last word on this session. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + switch (pHdr->hdr_DataType) + { + case SPX2_DT_ORDREL: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: Recd ORDREl!\n")); + + // BUGBUG: Need to deal with all sthe states. + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + // On receive, we do check the seq num for the orderly release, just + // like for a data packet. + // If this was not already indicated, indicate it now. That is all + // we do for an orderly release. When our client does a orderly rel + // and we receive the ack for that, call abortive with success. + + // Verify ord rel packet, this checks if seq nums match also. + if ((pktLen != MIN_IPXSPX2_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) != + (SPX_CC_ACK | SPX_CC_EOM | SPX_CC_SPX2)) || + (pHdr->hdr_DataType != SPX2_DT_ORDREL) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: OR Failed/Ignored %lx, %lx.%lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDiscPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + lockHeld = FALSE; + } + + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessOrdRel(pSpxConnFile, lockHandle); + lockHeld = FALSE; + } + else + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, DBG1, + ("SpxConnDiscPacket: DATA ORDREL %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + // Set flag in last recd buffer + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_ORD_DISC; + } + + break; + + case SPX2_DT_IDISC: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC %lx!\n", + pSpxConnFile, pSpxConnFile->scf_RefCount)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDiscPacket: SEND %d. RECV %d.%lx!\n", + IsListEmpty(&pSpxConnFile->scf_ReqLinkage), + IsListEmpty(&pSpxConnFile->scf_RecvLinkage), + pSpxConnFile->scf_RecvDoneLinkage)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + !(pHdr->hdr_ConnCtrl & SPX_CC_ACK) || + (pHdr->hdr_DataType != SPX2_DT_IDISC) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:IDISC Ignored %lx.%lx.%lx.%lx\n", + pSpxConnFile, seqNum, + pSpxConnFile->scf_RecvSeqNum, + pSpxConnFile->scf_RecvListTail)); + break; + } + + // Copy address for when ack is to be sent. + SpxCopyIpxAddr(pHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + + if (pSpxConnFile->scf_RecvListHead == NULL) + { + // No received data, go ahead and process now. + DBGPRINT(CONNECT, INFO, + ("SpxConnDiscPacket: NO RECV DATA IDISC %lx.%lx.%lx\n", + pSpxConnFile, + pSpxConnFile->scf_RecvListHead, + pSpxConnFile->scf_SendSeqListHead)); + + SpxConnProcessIDisc(pSpxConnFile, lockHandle); + + lockHeld = FALSE; + } + else + { + // Set flag in last recd buffer + + pSpxConnFile->scf_RecvListTail->rr_State |= SPX_RECVPKT_IDISC; + } + + break; + + case SPX2_DT_IDISC_ACK: + + // Done with informed disconnect. Call abort connection with + // status success. That completes the pending disconnect request + // with status_success. + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDiscPacket: %lx Recd IDISC ack!\n", pSpxConnFile)); + + if (!((pktLen == MIN_IPXSPX_HDRSIZE) || + ((pHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen == MIN_IPXSPX2_HDRSIZE))) || + (pHdr->hdr_DataType != SPX2_DT_IDISC_ACK) || + (srcConnId == 0) || + (srcConnId == 0xFFFF) || + (srcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId == 0) || + (destConnId == 0xFFFF) || + (destConnId != pSpxConnFile->scf_LocalConnId)) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDiscPacket:Ver idisc ack Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + break; + } + + // We should be in the right state to accept this. + if ((SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && + (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_SENT_IDISC)) + { + spxConnAbortiveDisc( + pSpxConnFile, + STATUS_SUCCESS, + SPX_CALL_RECVLEVEL, + lockHandle, + FALSE); // [SA] bug #15249 + + lockHeld = FALSE; + } + + break; + + default: + + KeBugCheck(0); + } + + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Remove reference added on connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvBufferPkt( + IN PSPX_CONN_FILE pSpxConnFile, + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT LookaheadOffset, + IN PIPXSPX_HDR pIpxSpxHdr, + IN UINT PacketSize, + IN PIPX_LOCAL_TARGET pRemoteAddr, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PSPX_RECV_RESD pRecvResd; + ULONG bytesCopied; + BOOLEAN fEom; + NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; + PBYTE pData = NULL; + PNDIS_BUFFER pNdisBuffer = NULL; + + if (PacketSize > 0) + { + // Allocate memory for this data. + if (pData = (PBYTE)SpxAllocateMemory(PacketSize)) + { + // Describe memory with a ndis buffer descriptor. + NdisAllocateBuffer( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + pData, + PacketSize); + } + else + { + ndisStatus = NDIS_STATUS_RESOURCES; + } + } + + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus == NDIS_STATUS_SUCCESS) + { + // Queue the buffer into the packet if there is one. + if (pNdisBuffer) + { + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_DataOffset= 0; + +#if DBG + // Store seq number + GETSHORT2SHORT(&pRecvResd->rr_SeqNum , &pIpxSpxHdr->hdr_SeqNum); +#endif + + pRecvResd->rr_State = + (SPX_RECVPKT_BUFFERING | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fEom ? SPX_RECVPKT_EOM : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *pRemoteAddr; + } + + pRecvResd->rr_Request = NULL; + pRecvResd->rr_ConnFile = pSpxConnFile; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvBufferPkt: %lx Len %lx DataPts %lx F %lx\n", + pSpxConnFile, PacketSize, pData, pRecvResd->rr_State)); + + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + // Call ndis transfer data. Copy ENTIRE packet. copySize has + // been modified so use original values. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (PacketSize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadOffset, + PacketSize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + + // BUG: FDDI returns pending which screws us up here. Stupid bug + ndisStatus = NDIS_STATUS_SUCCESS; + } + } + + // ASSERT: Lock will be freed in the success case. + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(RECEIVE, ERR, + ("SpxRecvBufferPkt: FAILED!\n")); + + END_PROCESS_PACKET(pSpxConnFile, FALSE, FALSE); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + if (pData != NULL) + { + SpxFreeMemory(pData); + } + + if (pNdisBuffer != NULL) + { + NdisFreeBuffer(pNdisBuffer); + } + } + + return; +} + + + + +VOID +SpxRecvDataPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadSize, + IN UINT LookaheadOffset, + IN UINT PacketSize + ) +/*++ + +Routine Description: + + This is called to indicate an incoming connection. + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS status; + PIPXSPX_HDR pIpxSpxHdr; + USHORT srcConnId, destConnId, + pktLen, seqNum, ackNum, allocNum; + ULONG receiveFlags; + PSPX_CONN_FILE pSpxConnFile; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PIRP pRecvIrp; + ULONG bytesTaken, iOffset, copySize, bytesCopied; + CTELockHandle lockHandle; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + NDIS_STATUS ndisStatus; + PREQUEST pRequest = NULL; + BOOLEAN fEom, + fImmedAck = FALSE, fLockHeld = FALSE, fPktDone = FALSE; + + pIpxSpxHdr = (PIPXSPX_HDR)LookaheadBuffer; + + // check minimum length + if (PacketSize < MIN_IPXSPX_HDRSIZE) + { + return; + } + + // Convert hdr to host format as needed. + GETSHORT2SHORT(&pktLen, &pIpxSpxHdr->hdr_PktLen); + GETSHORT2SHORT(&destConnId, &pIpxSpxHdr->hdr_DestConnId); + GETSHORT2SHORT(&seqNum, &pIpxSpxHdr->hdr_SeqNum); + GETSHORT2SHORT(&allocNum, &pIpxSpxHdr->hdr_AllocNum); + GETSHORT2SHORT(&ackNum, &pIpxSpxHdr->hdr_AckNum); + + if ((pktLen < MIN_IPXSPX_HDRSIZE) || + (pktLen > PacketSize) || + (pIpxSpxHdr->hdr_PktType != SPX_PKT_TYPE)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Packet Size %lx.%lx\n", + pktLen, PacketSize)); + + return; + } + + // We keep and use the remote id in the net format. + srcConnId = *(USHORT UNALIGNED *)&pIpxSpxHdr->hdr_SrcConnId; + + if ((srcConnId == 0) || (srcConnId == 0xFFFF) || (destConnId == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: Incorrect conn id %lx.%lx\n", + srcConnId, destConnId)); + + return; + } + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: packet received dest %lx src %lx seq %lx\n", + pIpxSpxHdr->hdr_DestSkt, pIpxSpxHdr->hdr_SrcSkt, seqNum)); + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen < MIN_IPXSPX2_HDRSIZE)) + { + return; + } + + // Find the connection this is destined for and reference it. + SpxConnFileReferenceById(destConnId, &pSpxConnFile, &status); + if (!NT_SUCCESS(status)) + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: Id %lx NOT FOUND", destConnId)); + return; + } + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + +#if 0 + // + // We have the connection. We should update the dest. sock # in + // it in case it changed. Unix machines do do that sometimes. + // SCO bug 7676 + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAddr); +#endif + + fLockHeld = TRUE; + do + { + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: Id %lx Conn %lx\n", + destConnId, pSpxConnFile)); + + // Restart watchdog timer if started. + if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_W_TIMER)) + { + // This will either successfully restart or not affect the timer + // if it is currently running. + SpxTimerCancelEvent( + pSpxConnFile->scf_WTimerId, + TRUE); + + pSpxConnFile->scf_WRetryCount = PARAM(CONFIG_KEEPALIVE_COUNT); + } + + if (SPX_CONN_ACTIVE(pSpxConnFile)) + { + // Verify data packet, this checks if seq nums match also. + if ((pIpxSpxHdr->hdr_SrcConnId != pSpxConnFile->scf_RemConnId) || + (destConnId != pSpxConnFile->scf_LocalConnId) || + !((pktLen >= MIN_IPXSPX_HDRSIZE) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_SPX2) && + (pktLen >= MIN_IPXSPX2_HDRSIZE)))) + { + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Failed %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // If it passed above test, but seq number is incorrect, schedule + // to send an ack. + if (seqNum != pSpxConnFile->scf_RecvSeqNum) + { + USHORT NumToResend; + + DBGPRINT(CONNECT, DBG, + ("SpxConnDataPacket: Unexpected seq on %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + ++SpxDevice->dev_Stat.DataFramesRejected; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesRejected, + pktLen - (SPX2_CONN(pSpxConnFile) ? + MIN_IPXSPX2_HDRSIZE : MIN_IPXSPX_HDRSIZE)); + + // + // Bug #16975: Set the remote ack addr for use in SpxConnSendAck() + // + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // Calculate number to be resent. If we expect sequence 1 and receive + // 2 for eg., we need to send a nack, else we send an ack. + if (SPX2_CONN(pSpxConnFile) && + UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_RecvSeqNum) && + !UNSIGNED_GREATER_WITH_WRAP( + seqNum, + pSpxConnFile->scf_SentAllocNum)) + { + NumToResend = (USHORT)(seqNum - pSpxConnFile->scf_RecvSeqNum + 1); + SpxConnSendNack(pSpxConnFile, NumToResend, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + + break; + } + + // If we have received an orderly release, we accept no more data + // packets. + if (SPX_CONN_FLAG( + pSpxConnFile, + (SPX_CONNFILE_IND_IDISC | + SPX_CONNFILE_IND_ODISC)) + + || + + ((pSpxConnFile->scf_RecvListTail != NULL) && + ((pSpxConnFile->scf_RecvListTail->rr_State & + SPX_RECVPKT_DISCMASK) != 0))) + { + DBGPRINT(CONNECT, ERR, + ("SpxConnDataPacket: After ord rel %lx, %lx.%lx\n", + pSpxConnFile, seqNum, pSpxConnFile->scf_RecvSeqNum)); + + break; + } + + // We are processing a packet OR a receive is about to complete. + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT)) + { + BEGIN_PROCESS_PACKET(pSpxConnFile, seqNum); + } + else + { + // Already processing a packet. Or a receive is waiting to + // complete. Get out. + break; + } + + // Set ack numbers for connection. + SPX_SET_ACKNUM( + pSpxConnFile, ackNum, allocNum); + + SpxConnProcessAck(pSpxConnFile, NULL, lockHandle); + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + iOffset = MIN_IPXSPX2_HDRSIZE; + if (!SPX2_CONN(pSpxConnFile)) + { + iOffset = 0; + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)) + { + iOffset = MIN_IPXSPX_HDRSIZE; + } + } + + copySize = pktLen - iOffset; + fEom = ((SPX_CONN_MSG(pSpxConnFile) && + (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM)) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IPXHDR)); + + // Do we attempt to piggyback? If not, fImmedAck is true. + // For SPX1 we dont piggyback. + // Bug #18253 + fImmedAck = (!SPX2_CONN(pSpxConnFile) || + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_EOM) == 0)); + + // If we do not have EOM to indicate AND we are a zero-sized packet + // then just consume this packet. + if (!fEom && (copySize == 0)) + { + DBGPRINT(RECEIVE, ERR, + ("SpxConnDataPacket: ZERO LENGTH PACKET NO EOM %lx.%lx\n", + pSpxConnFile, seqNum)); + + fPktDone = TRUE; + break; + } + + receiveFlags = TDI_RECEIVE_NORMAL; + receiveFlags |= ((fEom ? TDI_RECEIVE_ENTIRE_MESSAGE : 0) | + (((MacOptions & + NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0) ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0)); + + ++SpxDevice->dev_Stat.DataFramesReceived; + ExInterlockedAddLargeStatistic( + &SpxDevice->dev_Stat.DataFrameBytesReceived, + copySize); + + // Ok, we accept this packet. Depending on our state. + switch (SPX_RECV_STATE(pSpxConnFile)) + { + case SPX_RECV_PROCESS_PKTS: + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: recv completions on %lx\n", + pSpxConnFile)); + + goto BufferPacket; + + case SPX_RECV_IDLE: + + // If recv q is non-empty we are buffering data. + // Also, if no receive handler goto buffer data. Also, if receives + // are being completed, buffer this packet. + if ((pSpxConnFile->scf_RecvListHead != NULL) || + !(IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) || + !(pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler)) + { + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: RecvListHead non-null %lx\n", + pSpxConnFile)); + + goto BufferPacket; + } + + if (!SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + + // Don't indicate this packet again. + SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND); + +#if DBG + CTEAssert(pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + + // + // Comment this out for Buf # 10394. we'r hitting this assert + // even when there was no data loss. + // + // CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != seqNum); + + pSpxConnFile->scf_PktSeqNum = seqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; + + pSpxConnFile->scf_IndBytes = copySize; + pSpxConnFile->scf_IndLine = __LINE__; + + +#endif + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + receiveFlags, + LookaheadSize - iOffset, + copySize, + &bytesTaken, + LookaheadBuffer + iOffset, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: IND Flags %lx.%lx ConnID %lx,\ + %lx Ctx %lx SEQ %lx Size %lx . %lx .%lx IND Status %lx\n", + pIpxSpxHdr->hdr_ConnCtrl, + receiveFlags, + destConnId, + pSpxConnFile, + pSpxConnFile->scf_ConnCtx, + seqNum, + LookaheadSize - iOffset, + copySize, + bytesTaken, + status)); + + DBGPRINT(RECEIVE, INFO, + ("SpxConnDataPacket: %x %x %x %x %x %x %x %x %x %x %x %x\n", + *(LookaheadBuffer+iOffset), + *(LookaheadBuffer+iOffset+1), + *(LookaheadBuffer+iOffset+2), + *(LookaheadBuffer+iOffset+3), + *(LookaheadBuffer+iOffset+4), + *(LookaheadBuffer+iOffset+5), + *(LookaheadBuffer+iOffset+6), + *(LookaheadBuffer+iOffset+7), + *(LookaheadBuffer+iOffset+8), + *(LookaheadBuffer+iOffset+9), + *(LookaheadBuffer+iOffset+10), + *(LookaheadBuffer+iOffset+11))); + + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. + CTEAssert((bytesTaken != 0) || fEom); + fPktDone = TRUE; + +#if DBG + // Set this to 0, since we just indicated, there could + // not have been other data. + pSpxConnFile->scf_IndBytes = 0; +#endif + + break; + } + + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + break; + } + + // If there was indicated but not received data waiting + // (which in this path there will never be, the request + // could be completed given the data filled it up, and + // the lock released. + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + } + else if (IsListEmpty(&pSpxConnFile->scf_RecvLinkage)) + { + // Data was not accepted. Need to buffer data and + // reduce window. + goto BufferPacket; + } + + // Fall through to recv_posted. + } + else + { + DBGPRINT(RECEIVE, WARN, + ("SpxConnDataPacket: !!!Ignoring %lx Seq %lx\n", + pSpxConnFile, + seqNum)); + + break; + } + + case SPX_RECV_POSTED: + + if (pSpxConnFile->scf_RecvListHead != NULL) + { + // This can happen also. Buffer packet if it does. + goto BufferPacket; + } + + // If a receive irp is posted, then process the receive irp. If + // we fell thru we MAY already will have an irp. + if (pRequest == NULL) + { + CTEAssert(!IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); + CTEAssert(pSpxConnFile->scf_CurRecvReq != NULL); + pRequest = pSpxConnFile->scf_CurRecvReq; + } + + // Process receive. Here we do not need to worry about + // indicated yet not received data. We just deal with + // servicing the current packet. + CTEAssert(pRequest == pSpxConnFile->scf_CurRecvReq); + if ((LookaheadSize == PacketSize) && + (pSpxConnFile->scf_CurRecvSize >= copySize)) + { + bytesCopied = 0; + status = STATUS_SUCCESS; + if (copySize > 0) + { + status = TdiCopyBufferToMdl( + LookaheadBuffer, + iOffset, + copySize, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + } + + DBGPRINT(RECEIVE, DBG, + ("BytesCopied %lx CopySize %lx, Recv Size %lx.%lx\n", + bytesCopied, copySize, + pSpxConnFile->scf_CurRecvSize, + pSpxConnFile->scf_CurRecvOffset)); + } + + // Update current request values and see if this request + // is to be completed. Either zero or fEom. + pSpxConnFile->scf_CurRecvOffset += bytesCopied; + pSpxConnFile->scf_CurRecvSize -= bytesCopied; + +#if DBG + // Decrement indicated data count + if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_PKT_NOIND)) + { + if (bytesCopied != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= bytesCopied; + } + } +#endif + + if (SPX_CONN_STREAM(pSpxConnFile) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + CTELockHandle lockHandleInter; + + // Set status + REQUEST_STATUS(pRequest) = STATUS_SUCCESS; + REQUEST_INFORMATION(pRequest)= + pSpxConnFile->scf_CurRecvOffset; + + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnData: Completing recv %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + // Dequeue this request, Set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + // Request is done. Move to completion list. + CTEGetLock(&SpxGlobalQInterlock, &lockHandleInter); + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + + SPX_QUEUE_FOR_RECV_COMPLETION(pSpxConnFile); + CTEFreeLock(&SpxGlobalQInterlock, lockHandleInter); + } + + fPktDone = TRUE; + } + else + { + // Need to allocate a ndis receive packet for transfer + // data. + DBGPRINT(RECEIVE, DBG, + ("SpxConnDataPacket: %lx.%lx Tranfer data needed!\n", + copySize, pSpxConnFile->scf_CurRecvSize)); + + if (copySize > pSpxConnFile->scf_CurRecvSize) + { + // Partial receive. Buffer and then deal with it. + goto BufferPacket; + } + + // Allocate a ndis receive packet. + SpxAllocRecvPacket(SpxDevice, &pNdisPkt, &ndisStatus); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + // Describe the receive irp's data with a ndis buffer + // descriptor. + if (copySize > 0) + { + SpxCopyBufferChain( + &ndisStatus, + &pNdisBuffer, + SpxDevice->dev_NdisBufferPoolHandle, + REQUEST_TDI_BUFFER(pRequest), + pSpxConnFile->scf_CurRecvOffset, + copySize); + + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + // Free the recv packet + SpxPktRecvRelease(pNdisPkt); + break; + } + + // Queue the buffer into the packet + // Link the buffer descriptor into the packet descriptor + NdisChainBufferAtBack( + pNdisPkt, + pNdisBuffer); + } + + // Don't care about whether this is indicated or not here + // as it is not a buffering packet. + pRecvResd = RECV_RESD(pNdisPkt); + pRecvResd->rr_Id = IDENTIFIER_SPX; + pRecvResd->rr_State = + ((fEom ? SPX_RECVPKT_EOM : 0) | + (SPX_CONN_FLAG2( + pSpxConnFile, SPX_CONNFILE2_PKT_NOIND) ? SPX_RECVPKT_INDICATED : 0) | + (fImmedAck ? SPX_RECVPKT_IMMEDACK : 0) | + ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) ? + SPX_RECVPKT_SENDACK : 0)); + + if (pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) + { + // copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + } + + pRecvResd->rr_Request = pRequest; + pRecvResd->rr_ConnFile = pSpxConnFile; + + // reference receive request + REQUEST_INFORMATION(pRequest)++; + + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + fLockHeld = FALSE; + + // Call ndis transfer data. + ndisStatus = NDIS_STATUS_SUCCESS; + bytesCopied = 0; + if (copySize > 0) + { + (*IpxTransferData)( + &ndisStatus, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + copySize, + pNdisPkt, + &bytesCopied); + } + + if (ndisStatus != STATUS_PENDING) + { + SpxTransferDataComplete( + pNdisPkt, + ndisStatus, + bytesCopied); + } + } + + break; + + default: + + KeBugCheck(0); + break; + } + + break; + +BufferPacket: + + SpxRecvBufferPkt( + pSpxConnFile, + MacBindingHandle, + MacReceiveContext, + iOffset + LookaheadOffset, + pIpxSpxHdr, + copySize, + RemoteAddress, + lockHandle); + + fLockHeld = FALSE; + } + + } while (FALSE); + + // Here we process a received ack. + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // Send an ack if one was asked for. And we are done with this packet. + if (fPktDone) + { + END_PROCESS_PACKET(pSpxConnFile, FALSE, TRUE); + } + + if ((pIpxSpxHdr->hdr_ConnCtrl & SPX_CC_ACK) && fPktDone) + { + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + fLockHeld = TRUE; + } + + // First copy the remote address in connection. + SpxCopyIpxAddr(pIpxSpxHdr, pSpxConnFile->scf_RemAckAddr); + pSpxConnFile->scf_AckLocalTarget = *RemoteAddress; + + // #17564 + if (fImmedAck || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_NOACKWAIT) || + SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IMMED_ACK)) + { + SpxConnSendAck(pSpxConnFile, lockHandle); + fLockHeld = FALSE; + } + else + { + SpxConnQWaitAck(pSpxConnFile); + } + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + // Deref the connection + SpxConnFileDereference(pSpxConnFile, CFREF_BYID); + return; +} + + + + +VOID +SpxRecvFlushBytes( + IN PSPX_CONN_FILE pSpxConnFile, + IN ULONG BytesToFlush, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + +--*/ +{ + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + PBYTE pData; + ULONG dataLen, copyLen; + BOOLEAN fLockHeld = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numPkts = 0, numDerefs = 0; + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Flush %lx\n", + pSpxConnFile, BytesToFlush)); + + while (((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + ((BytesToFlush > 0) || + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0))) + { + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + if ((BytesToFlush == 0) && (dataLen != 0)) + { + // Don't flush this packet. + break; + } + + // Allow for zero data, eom only packets. + copyLen = MIN((dataLen - pRecvResd->rr_DataOffset), BytesToFlush); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: %lx Pkt %lx DataLen %lx Copy %lx Flush %lx\n", + pSpxConnFile, pNdisPkt, dataLen, copyLen, BytesToFlush)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + BytesToFlush -= (ULONG)copyLen; + +#if DBG + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } +#endif + + if (pRecvResd->rr_DataOffset == dataLen) + { + // Packet consumed. Free it up. Check if disc happened. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + numDerefs++; + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + CTEAssert(pNdisBuffer != NULL); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + + DBGPRINT(RECEIVE, DBG, + ("SpxRecvFlushBytes: !!!ALL INDICATED on %lx.%lx.%lx.%lx\n", + pSpxConnFile, pNdisPkt, pNdisBuffer, pData)); + + INCREMENT_WINDOW(pSpxConnFile); + fWdwOpen = TRUE; + } + else + { + // Took only part of this packet. Get out. + break; + } + } + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxRecvFlushBytes: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + DBGPRINT(RECEIVE, ERR, + ("spxRecvFlushBytes: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} + + + + +BOOLEAN +SpxRecvIndicatePendingData( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN - Receive was queued => TRUE + +--*/ +{ + ULONG indicateFlags; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PREQUEST pRequest; + PIRP pRecvIrp; + ULONG bytesTaken, totalSize, bufSize; + PTDI_IND_RECEIVE pRecvHandler; + PVOID pRecvCtx; + PSPX_RECV_RESD pRecvResd; + NTSTATUS status; + PBYTE lookaheadData; + ULONG lookaheadSize; + BOOLEAN fLockHeld = TRUE, fRecvQueued = FALSE; + + + while ((pRecvHandler = pSpxConnFile->scf_AddrFile->saf_RecvHandler) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL) && + (IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) && + ((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) == 0)) + { + // Once a receive is queued we better get out. + CTEAssert(!fRecvQueued); + + // Initialize lookahead values + lookaheadData = NULL; + lookaheadSize = 0; + + // We have no indicated but pending data, and there is some data to + // indicate. Figure out how much. Indicate upto end of message or as + // much as we have. + + // A buffering recv packet will have ATMOST one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &lookaheadData, &lookaheadSize); + CTEAssert(lookaheadData != NULL); + CTEAssert(lookaheadSize >= 0); + } + + // Allow for zero data, eom only packets. + lookaheadSize -= pRecvResd->rr_DataOffset; + totalSize = lookaheadSize; + lookaheadData += pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((lookaheadSize > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + +#if DBG + CTEAssert (pSpxConnFile->scf_CurRecvReq == NULL); + + // Debug code to ensure we dont reindicate data/indicate + // when previously indicated data waiting with afd. + CTEAssert(pSpxConnFile->scf_IndBytes == 0); + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + // Go ahead and walk the list of waiting packets. Get total size. + while ((pRecvResd->rr_Next != NULL) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) == 0)) + { + // Check next packet. + pRecvResd = pRecvResd->rr_Next; + +#if DBG + CTEAssert(pSpxConnFile->scf_PktSeqNum != pRecvResd->rr_SeqNum); + + pSpxConnFile->scf_PktSeqNum = pRecvResd->rr_SeqNum; + pSpxConnFile->scf_PktFlags = pSpxConnFile->scf_Flags; + pSpxConnFile->scf_PktFlags2 = pSpxConnFile->scf_Flags2; +#endif + + pRecvResd->rr_State |= SPX_RECVPKT_INDICATED; + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, NULL, &bufSize); + CTEAssert(bufSize >= 0); + + // Allow for zero data, eom only packets. + totalSize += bufSize; + } + +#if DBG + pSpxConnFile->scf_IndBytes = totalSize; + pSpxConnFile->scf_IndLine = __LINE__; + + // There better not be any pending receives. If so, we have data + // corruption about to happen. + if (!IsListEmpty(&pSpxConnFile->scf_RecvDoneLinkage)) + { + DBGBRK(FATAL); + KeBugCheck(0); + } +#endif + + indicateFlags = TDI_RECEIVE_NORMAL | TDI_RECEIVE_COPY_LOOKAHEAD; + if ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0) + { + indicateFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + + pRecvCtx = pSpxConnFile->scf_AddrFile->saf_RecvHandlerCtx; + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + bytesTaken = 0; + status = (*pRecvHandler)( + pRecvCtx, + pSpxConnFile->scf_ConnCtx, + indicateFlags, + lookaheadSize, + totalSize, + &bytesTaken, + lookaheadData, + &pRecvIrp); + + DBGPRINT(RECEIVE, DBG, + ("SpxConnIndicatePendingData: IND Flags %lx Size %lx .%lx IND Status %lx\n", + indicateFlags, + totalSize, + bytesTaken, + status)); + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + if (status == STATUS_SUCCESS) + { + // Assume all data accepted. Free bytesTaken worth of data packets. + // Sometimes AFD returns STATUS_SUCCESS to just flush the data, so + // we can't assume it took only one packet (since lookahead only + // had that information). + CTEAssert(bytesTaken == totalSize); + SpxRecvFlushBytes(pSpxConnFile, totalSize, LockHandleConn); + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + continue; + } + else if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + + // Queue irp into connection, change state to receive + // posted and fall thru. + pRequest = SpxAllocateRequest( + SpxDevice, + pRecvIrp); + + IF_NOT_ALLOCATED(pRequest) + { + pRecvIrp->IoStatus.Status = + STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (pRecvIrp, IO_NETWORK_INCREMENT); + return (FALSE); + } + + SpxConnQueueRecv( + pSpxConnFile, + pRequest); + + fRecvQueued = TRUE; + } + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + return fRecvQueued; +} + + + + +VOID +SpxRecvProcessPkts( + IN PSPX_CONN_FILE pSpxConnFile, + IN CTELockHandle LockHandleConn + ) +/*++ + +Routine Description: + + Handle buffered data, complete irp if necessary. Set state to idle + if list becomes empty. + +Arguments: + + pSpxConnFile - Pointer to a transport address file object. + +Return Value: + + BOOLEAN: More data left to indicate => TRUE + +--*/ +{ + ULONG remainingDataLen, copyLen, bytesCopied; + PREQUEST pRequest; + NTSTATUS status; + BOOLEAN fEom; + PNDIS_PACKET pNdisPkt; + PNDIS_BUFFER pNdisBuffer; + PSPX_RECV_RESD pRecvResd; + ULONG dataLen; + PBYTE pData; + LIST_ENTRY *p; + BOOLEAN fLockHeld = TRUE, fMoreData = TRUE, fWdwOpen = FALSE; + USHORT discState = 0; + int numDerefs = 0; + + if (SPX_RECV_STATE(pSpxConnFile) != SPX_RECV_PROCESS_PKTS) + { + SPX_RECV_SETSTATE(pSpxConnFile, SPX_RECV_PROCESS_PKTS); + +ProcessReceives: + + while ((pSpxConnFile->scf_CurRecvReq != NULL) && + ((pRecvResd = pSpxConnFile->scf_RecvListHead) != NULL)) + { + // A buffering recv packet will have one ndis buffer descriptor + // queued in, which will describe a segment of memory we have + // allocated. An offset will also be present indicating the data + // to start reading from (or to indicate from to AFD). + CTEAssert((pRecvResd->rr_State & SPX_RECVPKT_BUFFERING) != 0); + + pNdisPkt = (PNDIS_PACKET)CONTAINING_RECORD( + pRecvResd, NDIS_PACKET, ProtocolReserved); + + NdisQueryPacket(pNdisPkt, NULL, NULL, &pNdisBuffer, NULL); + + // Initialize pData + pData = NULL; + dataLen = 0; + + if (pNdisBuffer != NULL) + { + NdisQueryBuffer(pNdisBuffer, &pData, &dataLen); + CTEAssert(pData != NULL); + CTEAssert(dataLen >= 0); + } + + // Allow for zero data, eom only packets. + remainingDataLen = dataLen - pRecvResd->rr_DataOffset; + + // If this packet contained data then eom must also have been + // indicated at the time all the data was consumed. + CTEAssert((remainingDataLen > 0) || + ((pRecvResd->rr_DataOffset == 0) && + ((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0))); + + status = STATUS_SUCCESS; + copyLen = 0; + if (remainingDataLen > 0) + { + copyLen = MIN(remainingDataLen, pSpxConnFile->scf_CurRecvSize); + status = TdiCopyBufferToMdl( + pData, + pRecvResd->rr_DataOffset, + copyLen, + REQUEST_TDI_BUFFER(pSpxConnFile->scf_CurRecvReq), + pSpxConnFile->scf_CurRecvOffset, + &bytesCopied); + + CTEAssert(NT_SUCCESS(status)); + if (!NT_SUCCESS(status)) + { + // Abort request with this status. Reset request + // queue to next request if one is available. + copyLen = pSpxConnFile->scf_CurRecvSize; + } + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx Size %lx F %lx\n", + pSpxConnFile, pNdisPkt, pData, copyLen, pRecvResd->rr_State)); + + // Adjust various values to see whats done whats not + pRecvResd->rr_DataOffset += (USHORT)copyLen; + pSpxConnFile->scf_CurRecvSize -= (USHORT)copyLen; + pSpxConnFile->scf_CurRecvOffset += (USHORT)copyLen; + +#if DBG + // If this packet was part of indicated data count, decrement. + if ((pRecvResd->rr_State & SPX_RECVPKT_INDICATED) != 0) + { + if (copyLen != 0) + { + CTEAssert (pSpxConnFile->scf_IndBytes != 0); + pSpxConnFile->scf_IndBytes -= copyLen; + } + } +#endif + + // Set fEom/discState (init to 0) only if all of packet was consumed. + fEom = FALSE; + if (pRecvResd->rr_DataOffset == dataLen) + { + fEom = (BOOLEAN)((pRecvResd->rr_State & SPX_RECVPKT_EOM) != 0); + + // Remember if disconnect needed to happen. If set, this better be + // last packet received. Again, only if entire pkt was consumed. + discState = (pRecvResd->rr_State & SPX_RECVPKT_DISCMASK); + CTEAssert((discState == 0) || + (pRecvResd == pSpxConnFile->scf_RecvListTail)); + + // Packet consumed. Free it up. + numDerefs++; + + SpxConnDequeueRecvPktLock(pSpxConnFile, pNdisPkt); + INCREMENT_WINDOW(pSpxConnFile); + + fWdwOpen = TRUE; + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx Data %lx DEQUEUED\n", + pSpxConnFile, pNdisPkt, pData)); + + if (pNdisBuffer != NULL) + { + NdisUnchainBufferAtFront(pNdisPkt, &pNdisBuffer); + NdisFreeBuffer(pNdisBuffer); + SpxFreeMemory(pData); + } + + SpxPktRecvRelease(pNdisPkt); + } + else + { + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Pkt %lx PARTIAL USE %lx.%lx\n", + pSpxConnFile, pNdisPkt, pRecvResd->rr_DataOffset, dataLen)); + } + + // Don't complete until we are out of all packets and stream mode or... + if (((pSpxConnFile->scf_RecvListHead == NULL) && + SPX_CONN_STREAM(pSpxConnFile)) || + (pSpxConnFile->scf_CurRecvSize == 0) || + fEom) + { + // Done with receive, move to completion or complete depending on + // call level. + pRequest = pSpxConnFile->scf_CurRecvReq; + + // Set status. Complete with error from TdiCopy if so. + REQUEST_INFORMATION(pRequest) = pSpxConnFile->scf_CurRecvOffset; + REQUEST_STATUS(pRequest) = status; + + // Ensure we dont overwrite an error status. + if (!SPX_CONN_STREAM(pSpxConnFile) && + (pSpxConnFile->scf_CurRecvSize == 0) && + !fEom && + NT_SUCCESS(status)) + { + REQUEST_STATUS(pRequest) = STATUS_RECEIVE_PARTIAL; + } + + // Dequeue this request, set next recv if one exists. + SPX_CONN_SETNEXT_CUR_RECV(pSpxConnFile, pRequest); + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: %lx Recv %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + // Request is done. Move to receive completion list. There + // could already be previously queued requests in here. + InsertTailList( + &pSpxConnFile->scf_RecvDoneLinkage, + REQUEST_LINKAGE(pRequest)); + } + + CTEAssert((discState == 0) || + (pSpxConnFile->scf_RecvListHead == NULL)); + } + + // Complete any completed receives + while ((p = pSpxConnFile->scf_RecvDoneLinkage.Flink) != + &pSpxConnFile->scf_RecvDoneLinkage) + { + pRequest = LIST_ENTRY_TO_REQUEST(p); + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + + DBGPRINT(TDI, DBG, + ("SpxConnDiscPkt: PENDING REQ COMP %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + +#if DBG + if ((REQUEST_STATUS(pRequest) == STATUS_SUCCESS) && + (REQUEST_INFORMATION(pRequest) == 0)) + { + DBGPRINT(TDI, DBG, + ("SpxReceiveComplete: Completing %lx with %lx.%lx\n", + pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + } +#endif + + SpxCompleteRequest(pRequest); + numDerefs++; + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + + while (fMoreData) + { + // Bug #21036 + // If there is a receive waiting to be processed, we better not + // indicate data before we finish it. + if (pSpxConnFile->scf_CurRecvReq != NULL) + goto ProcessReceives; + + // If a receive was queued the goto beginning again. + if (SpxRecvIndicatePendingData(pSpxConnFile, LockHandleConn)) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + goto ProcessReceives; + } + + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + fMoreData = ((pSpxConnFile->scf_RecvListHead != NULL) && + ((pSpxConnFile->scf_RecvListHead ->rr_State & + SPX_RECVPKT_BUFFERING) != 0) && + ((pSpxConnFile->scf_RecvListHead->rr_State & + SPX_RECVPKT_INDICATED) == 0)); + } + + // Set state + SPX_RECV_SETSTATE( + pSpxConnFile, + (pSpxConnFile->scf_CurRecvReq == NULL) ? + SPX_RECV_IDLE : SPX_RECV_POSTED); + } +#if DBG + else + { + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Already processing pkts %lx\n", + pSpxConnFile)); + } +#endif + + if (fWdwOpen && (pSpxConnFile->scf_RecvListHead == NULL)) + { + // Send an ack as our windows probably opened up. Dont wait to + // piggyback here... + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Send ACK %lx\n", + pSpxConnFile)); + +#if DBG_WDW_CLOSE + // If packets been indicated we have started buffering. Also + // check if window is now zero. + { + LARGE_INTEGER li, ntTime; + int value; + + li = pSpxConnFile->scf_WdwCloseTime; + if (li.LowPart && li.HighPart) + { + KeQuerySystemTime(&ntTime); + + // Get the difference + ntTime.QuadPart = ntTime.QuadPart - li.QuadPart; + + // Convert to milliseconds. If the highpart is 0, we + // take a shortcut. + if (ntTime.HighPart == 0) + { + value = ntTime.LowPart/10000; + } + else + { + ntTime = SPX_CONVERT100NSTOCENTISEC(ntTime); + value = ntTime.LowPart << 4; + } + + // Set new average close time + pSpxConnFile->scf_WdwCloseAve += value; + pSpxConnFile->scf_WdwCloseAve /= 2; + DBGPRINT(RECEIVE, DBG, + ("V %ld AVE %ld\n", + value, pSpxConnFile->scf_WdwCloseAve)); + } + } +#endif + + SpxConnSendAck(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + } + + // Check if disconnect happened + switch (discState) + { + case SPX_RECVPKT_IDISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered IDISC %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case SPX_RECVPKT_ORD_DISC: + + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, DBG, + ("spxConnProcessRecdPkts: Buffered ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessOrdRel(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + case (SPX_RECVPKT_IDISC | SPX_RECVPKT_ORD_DISC): + + // IDISC has more priority. + CTEAssert(!fMoreData); + CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); + + if (!fLockHeld) + { + CTEGetLock(&pSpxConnFile->scf_Lock, &LockHandleConn); + } + + DBGPRINT(RECEIVE, ERR, + ("spxConnProcessRecdPkts: Buffered IDISC *AND* ORDREL %lx\n", + pSpxConnFile, fMoreData)); + + SpxConnProcessIDisc(pSpxConnFile, LockHandleConn); + fLockHeld = FALSE; + break; + + default: + + break; + } + + if (fLockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, LockHandleConn); + } + + while (numDerefs-- > 0) + { + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; +} diff --git a/private/ntos/tdi/isnp/spx/spxreg.c b/private/ntos/tdi/isnp/spx/spxreg.c new file mode 100644 index 000000000..4389dca5f --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxreg.c @@ -0,0 +1,400 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxreg.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXREG + +// Local functions used to access the registry. +NTSTATUS +SpxInitReadIpxDeviceName( + VOID); + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxInitGetConfiguration) +#pragma alloc_text(INIT, SpxInitFreeConfiguration) +#pragma alloc_text(INIT, SpxInitGetConfigValue) +#pragma alloc_text(INIT, SpxInitReadIpxDeviceName) +#pragma alloc_text(INIT, SpxInitSetIpxDeviceName) +#endif + + +NTSTATUS +SpxInitGetConfiguration ( + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + RegistryPath - The name of ST's node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + NTSTATUS Status; + UINT i; + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + + ULONG Zero = 0; + ULONG Two = 2; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG Twelve = 12; + ULONG Fifteen = 15; + ULONG Thirty = 30; + ULONG FiveHundred = 500; + ULONG Hex4000 = 0x4000; + ULONG Hex7FFF = 0x7FFF; + ULONG FourK = 4096; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"ConnectionCount", &Five }, + { L"ConnectionTimeout", &Two }, + { L"InitPackets", &Five }, + { L"MaxPackets", &Thirty}, + { L"InitialRetransmissionTime", &FiveHundred}, + { L"KeepAliveCount", &Eight}, + { L"KeepAliveTimeout", &Twelve}, + { L"WindowSize", &Four}, + { L"SpxSocketRangeStart", &Hex4000}, + { L"SpxSocketRangeEnd", &Hex7FFF}, + { L"SpxSocketUniqueness", &Eight}, + { L"MaxPacketSize", &FourK}, + { L"RetransmissionCount", &Eight}, + { L"DisableSpx2", &Zero}, + { L"RouterMtu", &Zero}, + { L"BackCompSpx", &Zero} + }; + + if (!NT_SUCCESS(SpxInitReadIpxDeviceName())) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Allocate memory for the main config structure. + Config = CTEAllocMem (sizeof(CONFIG)); + if (Config == NULL) { + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->cf_DeviceName.Buffer = NULL; + + // SpxReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + RegistryPathBuffer = (PWSTR)CTEAllocMem(RegistryPath->Length + sizeof(WCHAR)); + + if (RegistryPathBuffer == NULL) { + + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory ( + RegistryPathBuffer, + RegistryPath->Buffer, + RegistryPath->Length); + + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->cf_RegistryPathBuffer = RegistryPathBuffer; + + // Read the per-transport (as opposed to per-binding) + // parameters. + // + // Set up QueryTable to do the following: + // 1) Switch to the Parameters key below SPX + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // 2-14) Call SpxSetBindingValue for each of the keys we + // care about. + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = SpxInitGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + } + + // 15) Stop + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->cf_RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + SpxInitFreeConfiguration(Config); + + TMPLOGERR(); + return Status; + } + + CTEFreeMem (RegistryPathBuffer); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} // SpxInitGetConfiguration + + + + +VOID +SpxInitFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by SPX to get free any storage that was allocated + by SpxGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + CTEFreeMem (Config); + +} // SpxInitFreeConfig + + + + +NTSTATUS +SpxInitGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + DBGPRINT(CONFIG, INFO, + ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, *(UNALIGNED ULONG *)ValueData)); + + Config->cf_Parameters[(ULONG)EntryContext] = *(UNALIGNED ULONG *)ValueData; + return STATUS_SUCCESS; + +} // SpxInitGetConfigValue + + + + +NTSTATUS +SpxInitReadIpxDeviceName( + VOID + ) + +{ + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + PWSTR Export = L"Export"; + PWSTR IpxRegistryPath = IPX_REG_PATH; + + // Set up QueryTable to do the following: + // + // 1) Call SetIpxDeviceName for the string in "Export" + QueryTable[0].QueryRoutine = SpxInitSetIpxDeviceName; + QueryTable[0].Flags = 0; + QueryTable[0].Name = Export; + QueryTable[0].EntryContext = NULL; + QueryTable[0].DefaultType = REG_NONE; + + // 2) Stop + QueryTable[1].QueryRoutine = NULL; + QueryTable[1].Flags = 0; + QueryTable[1].Name = NULL; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES, + IpxRegistryPath, + QueryTable, + NULL, + NULL); + + return Status; +} + + + + +NTSTATUS +SpxInitSetIpxDeviceName( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - NULL. + + EntryContext - NULL. + +Return Value: + + status + +--*/ + +{ + PWSTR fileName; + NTSTATUS status = STATUS_SUCCESS; + + fileName = (PWSTR)CTEAllocMem(ValueLength); + if (fileName != NULL) { + RtlCopyMemory(fileName, ValueData, ValueLength); + RtlInitUnicodeString (&IpxDeviceName, fileName); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + return(status); +} + diff --git a/private/ntos/tdi/isnp/spx/spxsend.c b/private/ntos/tdi/isnp/spx/spxsend.c new file mode 100644 index 000000000..6b856953d --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxsend.c @@ -0,0 +1,262 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxsend.c + +Abstract: + + This module contains code that implements the send engine for the + SPX transport provider. + +Author: + + Nikhil Kamkolkar (nikhilk) 11-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// Define module number for event logging entries +#define FILENUM SPXSEND + +VOID +SpxSendComplete( + IN PNDIS_PACKET pNdisPkt, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + ProtocolBindingContext - The ADAPTER structure for this binding. + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + PSPX_CONN_FILE pSpxConnFile; + PSPX_SEND_RESD pSendResd; + PNDIS_BUFFER pNdisBuffer; + CTELockHandle lockHandle; + UINT bufCount; + PREQUEST pRequest = NULL; + BOOLEAN completeReq = FALSE, freePkt = FALSE, + orphaned = FALSE, lockHeld = FALSE; + + pSendResd = (PSPX_SEND_RESD)(pNdisPkt->ProtocolReserved); + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: For %lx with status **%lx**\n", + pNdisPkt, NdisStatus)); + } +#endif + + // IPX changes the length set for the first ndis buffer descriptor. + // Change it back to its original value here. + NdisQueryPacket(pNdisPkt, NULL, &bufCount, &pNdisBuffer, NULL); + NdisAdjustBufferLength(pNdisBuffer, IpxMacHdrNeeded + MIN_IPXSPX2_HDRSIZE); + + do + { + pSpxConnFile = pSendResd->sr_ConnFile; + CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); + lockHeld = TRUE; +#if defined(__PNP) + // + // if IPX gave us a new LocalTarget, use for our next send. + // + // But if we are sending connect requests by iterating over NicIds, + // dont update the local target bcoz that will screw up our iteration + // logic. + // + if ( DEVICE_NETWORK_PATH_NOT_FOUND == NdisStatus + && + !( + SPX_CONN_CONNECTING(pSpxConnFile) && + (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && + (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) + ) ) { + + pSpxConnFile->scf_LocalTarget = pSendResd->LocalTarget; + + // + // Renegotiate the max packet size if we have an active SPX2 + // session going on and we negotiated the max size originally. + // + if ( SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2) && + SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG) ) { + + // + // this call will get the local max size on this new local target + // from IPX. + // + SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr ); + SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); + + DBGPRINT(SEND, DBG3, + ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", + pSpxConnFile)); + } + + } +#endif __PNP + + CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0); + + // IPX dont own this packet nomore. + pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; + + // If a send packet has been aborted, then we need to call + // abort send to go ahead and free up this packet, and deref associated + // request, if there is one, potentially completing it. + if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) != 0) + { + spxConnAbortSendPkt( + pSpxConnFile, + pSendResd, + SPX_CALL_TDILEVEL, + lockHandle); + + lockHeld = FALSE; + break; + } + + // If there is an associated request, remove reference on it. BUT for a + // sequenced packet only if it has been acked and is waiting for the request + // to be dereferenced. It is already dequeued from queue, just free it up. + if ((((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) && + ((pSendResd->sr_State & SPX_SENDPKT_SEQ) == 0)) || + ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0)) + { + freePkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0); + + pRequest = pSendResd->sr_Request; + CTEAssert(pRequest != NULL); + + DBGPRINT(SEND, DBG, + ("IpxSendComplete: ReqRef before dec %lx.%lx\n", + pRequest, REQUEST_INFORMATION(pRequest))); + + // Deref the request and see if we complete it now. We always have our + // own reference on the request. + // !!! Status should already have been set in request...!!! + if (--(REQUEST_INFORMATION(pRequest)) == 0) + { + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + + completeReq = TRUE; + + // If this is acked already, request is not on list. + // BUG #11626 + if ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) == 0) + { + RemoveEntryList(REQUEST_LINKAGE(pRequest)); + } + } + } + + // Do we destroy this packet? + if ((pSendResd->sr_State & SPX_SENDPKT_DESTROY) != 0) + { + // Remove this packet from the send list in the connection. + DBGPRINT(SEND, INFO, + ("IpxSendComplete: destroy packet...\n")); + + SpxConnDequeueSendPktLock(pSpxConnFile, pNdisPkt); + freePkt = TRUE; + } + + } while (FALSE); + + if (lockHeld) + { + CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); + } + + if (freePkt) + { + DBGPRINT(SEND, INFO, + ("IpxSendComplete: free packet...\n")); + + SpxPktSendRelease(pNdisPkt); + } + + if (completeReq) + { + // If this is a send request, set info to data sent, else it will be + // zero. + if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_SEND) + { + PTDI_REQUEST_KERNEL_SEND pParam; + + pParam = (PTDI_REQUEST_KERNEL_SEND) + REQUEST_PARAMETERS(pRequest); + + REQUEST_INFORMATION(pRequest) = pParam->SendLength; + DBGPRINT(SEND, DBG, + ("IpxSendComplete: complete req %lx.%lx...\n", + REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + CTEAssert(pRequest != NULL); + CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); + SpxCompleteRequest(pRequest); + } + else + { + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx DISC Request %lx with %lx.%lx\n", + pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), + REQUEST_INFORMATION(pRequest))); + + DBGPRINT(SEND, DBG, + ("SpxSendComplete: %lx.%lx.%lx\n", + pSpxConnFile->scf_RefCount, + pSpxConnFile->scf_Flags, + pSpxConnFile->scf_Flags2)); + + // Set the request in the connection, and deref for it. + InsertTailList( + &pSpxConnFile->scf_DiscLinkage, + REQUEST_LINKAGE(pRequest)); + } + + SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); + } + + return; + +} // SpxSendComplete + + + diff --git a/private/ntos/tdi/isnp/spx/spxtimer.c b/private/ntos/tdi/isnp/spx/spxtimer.c new file mode 100644 index 000000000..bdb4e1d7f --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxtimer.c @@ -0,0 +1,637 @@ +/* + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + spxtimer.c + +Abstract: + + This file implements the timer routines used by the stack. + +Author: + + Jameel Hyder (jameelh@microsoft.com) + Nikhil Kamkolkar (nikhilk@microsoft.com) + + +Revision History: + 23 Feb 1993 Initial Version + +Notes: Tab stop: 4 +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXTIMER + +// Discardable code after Init time +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, SpxTimerInit) +#endif + +// Globals for this module +PTIMERLIST spxTimerList = NULL; +PTIMERLIST spxTimerTable[TIMER_HASH_TABLE] = {0}; +PTIMERLIST spxTimerActive = NULL; +CTELock spxTimerLock = {0}; +LARGE_INTEGER spxTimerTick = {0}; +KTIMER spxTimer = {0}; +KDPC spxTimerDpc = {0}; +ULONG spxTimerId = 1; +LONG spxTimerCount = 0; +USHORT spxTimerDispatchCount = 0; +BOOLEAN spxTimerStopped = FALSE; + + +NTSTATUS +SpxTimerInit( + VOID + ) +/*++ + +Routine Description: + + Initialize the timer component for the appletalk stack. + +Arguments: + + +Return Value: + + +--*/ +{ +#if !defined(_PNP_POWER) + BOOLEAN TimerStarted; +#endif !_PNP_POWER + + // Initialize the timer and its associated Dpc. timer will be kicked + // off when we get the first card arrival notification from ipx + KeInitializeTimer(&spxTimer); + CTEInitLock(&spxTimerLock); + KeInitializeDpc(&spxTimerDpc, spxTimerDpcRoutine, NULL); + spxTimerTick = RtlConvertLongToLargeInteger(SPX_TIMER_TICK); +#if !defined(_PNP_POWER) + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); +#endif !_PNP_POWER + return STATUS_SUCCESS; +} + + + + +ULONG +SpxTimerScheduleEvent( + IN TIMER_ROUTINE Worker, // Routine to invoke when time expires + IN ULONG MsTime, // Schedule after this much time + IN PVOID pContext // Context(s) to pass to the routine + ) +/*++ + +Routine Description: + + Insert an event in the timer event list. If the list is empty, then + fire off a timer. The time is specified in ms. We convert to ticks. + Each tick is currently 100ms. It may not be zero or negative. The internal + timer fires at 100ms granularity. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + ULONG DeltaTime; + ULONG Id = 0; + + // Convert to ticks. + DeltaTime = MsTime/SPX_MS_TO_TICKS; + if (DeltaTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + DeltaTime = 1; + } + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Converting %ld to ticks %ld\n", + MsTime, DeltaTime)); + + // Negative or Zero DeltaTime is invalid. + CTEAssert (DeltaTime > 0); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerScheduleEvent: Routine %lx, Time %d, Context %lx\n", + Worker, DeltaTime, pContext)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, FATAL, + ("SpxTimerScheduleEvent: Called after Flush !!\n")); + } + + else do + { + pList = SpxBPAllocBlock(BLKID_TIMERLIST); + + if (pList == NULL) + { + break; + } + +#if DBG + pList->tmr_Signature = TMR_SIGNATURE; +#endif + pList->tmr_Cancelled = FALSE; + pList->tmr_Worker = Worker; + pList->tmr_AbsTime = DeltaTime; + pList->tmr_Context = pContext; + + Id = pList->tmr_Id = spxTimerId++; + + // Take care of wrap around + if (spxTimerId == 0) + spxTimerId = 1; + + // Enqueue this handler + spxTimerEnqueue(pList); + } while (FALSE); + + CTEFreeLock(&spxTimerLock, lockHandle); + + return Id; +} + + + +VOID +spxTimerDpcRoutine( + IN PKDPC pKDpc, + IN PVOID pContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +/*++ + +Routine Description: + + This is called in at DISPATCH_LEVEL when the timer expires. The entry at + the head of the list is decremented and if ZERO unlinked and dispatched. + If the list is non-empty, the timer is fired again. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + BOOLEAN TimerStarted; + ULONG ReEnqueueTime; + CTELockHandle lockHandle; + + pKDpc; pContext; SystemArgument1; SystemArgument2; + +#if defined(_PNP_POWER) + CTEGetLock(&spxTimerLock, &lockHandle); + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + + CTEFreeLock(&spxTimerLock, lockHandle); + return; + } +#else + if (spxTimerStopped) + { + DBGPRINT(SYSTEM, ERR, + ("spxTimerDpc: Enetered after Flush !!!\n")); + return; + } + + CTEGetLock(&spxTimerLock, &lockHandle); +#endif _PNP_POWER + + SpxTimerCurrentTime ++; // Update our relative time + +#ifdef PROFILING + // This is the only place where this is changed. And it always increases. + SpxStatistics.stat_ElapsedTime = SpxTimerCurrentTime; +#endif + + // We should never be here if we have no work to do + if ((spxTimerList != NULL)) + { + // Careful here. If two guys wanna go off together - let them !! + if (spxTimerList->tmr_RelDelta != 0) + (spxTimerList->tmr_RelDelta)--; + + // Dispatch the entry if it is ready to go + if (spxTimerList->tmr_RelDelta == 0) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + + // Unlink from the list + spxTimerList = pList->tmr_Next; + if (spxTimerList != NULL) + spxTimerList->tmr_Prev = &spxTimerList; + + // Unlink from the hash table now + for (ppList = &spxTimerTable[pList->tmr_Id % TIMER_HASH_TABLE]; + *ppList != NULL; + ppList = &((*ppList)->tmr_Overflow)) + { + CTEAssert(VALID_TMR(*ppList)); + if (*ppList == pList) + { + *ppList = pList->tmr_Overflow; + break; + } + } + + CTEAssert (*ppList == pList->tmr_Overflow); + + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Dispatching %lx\n", + pList->tmr_Worker)); + + spxTimerDispatchCount ++; + spxTimerCount --; + spxTimerActive = pList; + CTEFreeLock(&spxTimerLock, lockHandle); + + // If reenqueue time is 0, do not requeue. If 1, then requeue with + // current value, else use value specified. + ReEnqueueTime = (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + DBGPRINT(SYSTEM, INFO, + ("spxTimerDpcRoutine: Reenequeu time %lx.%lx\n", + ReEnqueueTime, pList->tmr_AbsTime)); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerActive = NULL; + spxTimerDispatchCount --; + + if (ReEnqueueTime != TIMER_DONT_REQUEUE) + { + // If this chappie was cancelled while it was running + // and it wants to be re-queued, do it right away. + if (pList->tmr_Cancelled) + { + (*pList->tmr_Worker)(pList->tmr_Context, FALSE); + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + else + { + if (ReEnqueueTime != TIMER_REQUEUE_CUR_VALUE) + { + pList->tmr_AbsTime = ReEnqueueTime/SPX_MS_TO_TICKS; + if (pList->tmr_AbsTime == 0) + { + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld\n", + pList->tmr_AbsTime)); + } + DBGPRINT(SYSTEM, INFO, + ("SpxTimerDispatch: Requeue at %ld.%ld\n", + ReEnqueueTime, pList->tmr_AbsTime)); + } + + spxTimerEnqueue(pList); + } + } + else + { + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + } + } + +#if defined(_PNP_POWER) + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + + // it is possible that while we were here in Dpc, PNP_ADD_DEVICE + // restarted the timer, so this assert is commented out for PnP +// CTEAssert(!TimerStarted); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +#else + CTEFreeLock(&spxTimerLock, lockHandle); + + if (!spxTimerStopped) + { + TimerStarted = KeSetTimer(&spxTimer, + spxTimerTick, + &spxTimerDpc); + CTEAssert(!TimerStarted); + } +#endif _PNP_POWER +} + + +VOID +spxTimerEnqueue( + IN PTIMERLIST pListNew + ) +/*++ + +Routine Description: + + Here is a thesis on the code that follows. + + The timer events are maintained as a list which the timer dpc routine + looks at every timer tick. The list is maintained in such a way that only + the head of the list needs to be updated every tick i.e. the entire list + is never scanned. The way this is achieved is by keeping delta times + relative to the previous entry. + + Every timer tick, the relative time at the head of the list is decremented. + When that goes to ZERO, the head of the list is unlinked and dispatched. + + To give an example, we have the following events queued at time slots + X Schedule A after 10 ticks. + X+3 Schedule B after 5 ticks. + X+5 Schedule C after 4 ticks. + X+8 Schedule D after 6 ticks. + + So A will schedule at X+10, B at X+8 (X+3+5), C at X+9 (X+5+4) and + D at X+14 (X+8+6). + + The above example covers all the situations. + + - NULL List. + - Inserting at head of list. + - Inserting in the middle of the list. + - Appending to the list tail. + + The list will look as follows. + + BEFORE AFTER + ------ ----- + + X Head -->| Head -> A(10) ->| + A(10) + + X+3 Head -> A(7) ->| Head -> B(5) -> A(2) ->| + B(5) + + X+5 Head -> B(3) -> A(2) ->| Head -> B(3) -> C(1) -> A(1) ->| + C(4) + + X+8 Head -> C(1) -> A(1) ->| Head -> C(1) -> A(1) -> D(4) ->| + D(6) + + The granularity is one tick. THIS MUST BE CALLED WITH THE TIMER LOCK HELD. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + ULONG DeltaTime = pListNew->tmr_AbsTime; + + // The DeltaTime is adjusted in every pass of the loop to reflect the + // time after the previous entry that the new entry will schedule. + for (ppList = &spxTimerList; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Next) + { + CTEAssert(VALID_TMR(pList)); + if (DeltaTime <= pList->tmr_RelDelta) + { + pList->tmr_RelDelta -= DeltaTime; + break; + } + DeltaTime -= pList->tmr_RelDelta; + } + + + // Link this in the chain + pListNew->tmr_RelDelta = DeltaTime; + pListNew->tmr_Next = pList; + pListNew->tmr_Prev = ppList; + *ppList = pListNew; + if (pList != NULL) + { + pList->tmr_Prev = &pListNew->tmr_Next; + } + + // Now link it in the hash table + pListNew->tmr_Overflow = spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE]; + spxTimerTable[pListNew->tmr_Id % TIMER_HASH_TABLE] = pListNew; + spxTimerCount ++; +} + + + + +VOID +SpxTimerFlushAndStop( + VOID + ) +/*++ + +Routine Description: + + Force all entries in the timer queue to be dispatched immediately. No + more queue'ing of timer routines is permitted after this. The timer + essentially shuts down. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList; + CTELockHandle lockHandle; + + CTEAssert (KeGetCurrentIrql() == LOW_LEVEL); + + DBGPRINT(SYSTEM, ERR, + ("SpxTimerFlushAndStop: Entered\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + spxTimerStopped = TRUE; + + KeCancelTimer(&spxTimer); + + if (spxTimerList != NULL) + { + // Dispatch all entries right away + while (spxTimerList != NULL) + { + pList = spxTimerList; + CTEAssert(VALID_TMR(pList)); + spxTimerList = pList->tmr_Next; + + DBGPRINT(SYSTEM, INFO, + ("spxTimerFlushAndStop: Dispatching %lx\n", + pList->tmr_Worker)); + + // The timer routines assume they are being called at DISPATCH + // level. This is OK since we are calling with SpinLock held. + + (*pList->tmr_Worker)(pList->tmr_Context, TRUE); + + spxTimerCount --; + SpxBPFreeBlock(pList, BLKID_TIMERLIST); + } + RtlZeroMemory(spxTimerTable, sizeof(spxTimerTable)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + // Wait for all timer routines to complete + while (spxTimerDispatchCount != 0) + { + SpxSleep(SPX_TIMER_WAIT); + } +} + + + + +BOOLEAN +SpxTimerCancelEvent( + IN ULONG TimerId, + IN BOOLEAN ReEnqueue + ) +/*++ + +Routine Description: + + Cancel a previously scheduled timer event, if it hasn't fired already. + +Arguments: + + +Return Value: + + +--*/ +{ + PTIMERLIST pList, *ppList; + CTELockHandle lockHandle; + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: Entered for TimerId %ld\n", TimerId)); + + CTEAssert(TimerId != 0); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (ppList = &spxTimerTable[TimerId % TIMER_HASH_TABLE]; + (pList = *ppList) != NULL; + ppList = &pList->tmr_Overflow) + { + CTEAssert(VALID_TMR(pList)); + // If we find it, cancel it + if (pList->tmr_Id == TimerId) + { + // Unlink this from the hash table + *ppList = pList->tmr_Overflow; + + // ... and from the list + if (pList->tmr_Next != NULL) + { + pList->tmr_Next->tmr_RelDelta += pList->tmr_RelDelta; + pList->tmr_Next->tmr_Prev = pList->tmr_Prev; + } + *(pList->tmr_Prev) = pList->tmr_Next; + + spxTimerCount --; + if (ReEnqueue) + spxTimerEnqueue(pList); + else SpxBPFreeBlock(pList, BLKID_TIMERLIST); + break; + } + } + + // If we could not find it in the list, see if it currently running. + // If so mark him to not reschedule itself, only if reenqueue was false. + if (pList == NULL) + { + if ((spxTimerActive != NULL) && + (spxTimerActive->tmr_Id == TimerId) && + !ReEnqueue) + { + spxTimerActive->tmr_Cancelled = TRUE; + } + } + + CTEFreeLock(&spxTimerLock, lockHandle); + + DBGPRINT(SYSTEM, INFO, + ("SpxTimerCancelEvent: %s for Id %ld\n", + (pList != NULL) ? "Success" : "Failure", TimerId)); + + return (pList != NULL); +} + + + + +#if DBG + +VOID +SpxTimerDumpList( + VOID + ) +{ + PTIMERLIST pList; + ULONG CumTime = 0; + CTELockHandle lockHandle; + + DBGPRINT(DUMP, FATAL, + ("TIMER LIST: (Times are in %dms units\n", 1000)); + DBGPRINT(DUMP, FATAL, + ("\tTimerId Time(Abs) Time(Rel) Routine Address\n")); + + CTEGetLock(&spxTimerLock, &lockHandle); + + for (pList = spxTimerList; + pList != NULL; + pList = pList->tmr_Next) + { + CumTime += pList->tmr_RelDelta; + DBGPRINT(DUMP, FATAL, + ("\t% 6lx %5d %5ld %lx\n", + pList->tmr_Id, pList->tmr_AbsTime, CumTime, pList->tmr_Worker)); + } + + CTEFreeLock(&spxTimerLock, lockHandle); +} + +#endif diff --git a/private/ntos/tdi/isnp/spx/spxutils.c b/private/ntos/tdi/isnp/spx/spxutils.c new file mode 100644 index 000000000..024a36988 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/spxutils.c @@ -0,0 +1,484 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + spxutils.c + +Abstract: + + This contains all utility routines for the ISN SPX module. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Define module number for event logging entries +#define FILENUM SPXUTILS + +UINT +SpxUtilWstrLength( + IN PWSTR Wstr + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + UINT length = 0; + + while (*Wstr++) + { + length += sizeof(WCHAR); + } + + return length; +} + + + + +LONG +SpxRandomNumber( + VOID + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LARGE_INTEGER Li; + static LONG seed = 0; + + // Return a positive pseudo-random number; simple linear congruential + // algorithm. ANSI C "rand()" function. + + if (seed == 0) + { + KeQuerySystemTime(&Li); + seed = Li.LowPart; + } + + seed *= (0x41C64E6D + 0x3039); + + return (seed & 0x7FFFFFFF); +} + + + + +NTSTATUS +SpxUtilGetSocketType( + PUNICODE_STRING RemainingFileName, + PBYTE SocketType + ) +/*++ + +Routine Description: + + For PROTO_SPX, i'd return a device name from the dll of the form + \Device\IsnSpx\SpxStream (for SOCK_STREAM) or + \Device\IsnSpx\Spx (for SOCK_SEQPKT) + + and for PROTO_SPXII (the more common case we hope, even if + internally we degrade to SPX1 cause of the remote client's + limitations) + \Device\IsnSpx\Stream (for SOCK_STREAM) or + \Device\IsnSpx (for SOCK_SEQPKT) + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING typeString; + + *SocketType = SOCKET2_TYPE_SEQPKT; + + // Check for the socket type + do + { + if (RemainingFileName->Length == 0) + { + break; + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_STREAM; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET1_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET1_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET1_TYPE_SEQPKT; + break; + } + } + + if ((UINT)RemainingFileName->Length == + SpxUtilWstrLength(SOCKET2STREAM_SUFFIX)) + { + RtlInitUnicodeString(&typeString, SOCKET2STREAM_SUFFIX); + + // Case insensitive compare + if (RtlEqualUnicodeString(&typeString, RemainingFileName, TRUE)) + { + *SocketType = SOCKET2_TYPE_STREAM; + break; + } + } + + status = STATUS_NO_SUCH_DEVICE; + + } while (FALSE); + + return(status); +} + + + + +#define ONE_MS_IN_100ns -10000L // 1ms in 100ns units + +VOID +SpxSleep( + IN ULONG TimeInMs + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + KTIMER SleepTimer; + + ASSERT (KeGetCurrentIrql() == LOW_LEVEL); + + KeInitializeTimer(&SleepTimer); + + KeSetTimer(&SleepTimer, + RtlConvertLongToLargeInteger(TimeInMs * ONE_MS_IN_100ns), + NULL); + + KeWaitForSingleObject(&SleepTimer, UserRequest, KernelMode, FALSE, NULL); + return; +} + + + + +TDI_ADDRESS_IPX UNALIGNED * +SpxParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_IPX. + +Arguments: + + Transport - The generic TDI address. + +Return Value: + + A pointer to the IPX address, or NULL if none is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // The name can be passed with multiple entries; we'll take and use only + // the IPX one. + for (i=0;i<TransportAddress->TAAddressCount;i++) + { + if (addressName->AddressType == TDI_ADDRESS_TYPE_IPX) + { + if (addressName->AddressLength >= sizeof(TDI_ADDRESS_IPX)) + { + return ((TDI_ADDRESS_IPX UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} // SpxParseTdiAddress + + + +BOOLEAN +SpxValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: runt address\n")); + + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;i<TransportAddress->TAAddressCount;i++) + { + if (addressName->Address > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) + { + DBGPRINT(TDI, ERR, + ("SpxValidateTdiAddress: address too short\n")); + + return FALSE; + } + return TRUE; + +} // SpxValidateTdiAddress + + + + +ULONG +SpxBuildTdiAddress( + IN PVOID AddressBuffer, + IN ULONG AddressBufferLength, + IN UCHAR Network[4], + IN UCHAR Node[6], + IN USHORT Socket + ) + +/*++ + +Routine Description: + + This routine fills in a TRANSPORT_ADDRESS in the specified + buffer, given the socket, network and node. It will write + less than the full address if the buffer is too short. + +Arguments: + + AddressBuffer - The buffer that will hold the address. + + AddressBufferLength - The length of the buffer. + + Network - The network number. + + Node - The node address. + + Socket - The socket. + +Return Value: + + The number of bytes written into AddressBuffer. + +--*/ + +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + TA_IPX_ADDRESS TempAddress; + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + } + else + { + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)&TempAddress; + } + + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)Network; + SpxAddress->Address[0].Address[0].Socket = Socket; + RtlCopyMemory(SpxAddress->Address[0].Address[0].NodeAddress, Node, 6); + + if (AddressBufferLength >= sizeof(TA_IPX_ADDRESS)) + { + return sizeof(TA_IPX_ADDRESS); + } + else + { + RtlCopyMemory(AddressBuffer, &TempAddress, AddressBufferLength); + return AddressBufferLength; + } + +} // SpxBuildTdiAddress + + + +VOID +SpxBuildTdiAddressFromIpxAddr( + IN PVOID AddressBuffer, + IN PBYTE pIpxAddr + ) +{ + TA_IPX_ADDRESS UNALIGNED * SpxAddress; + + SpxAddress = (TA_IPX_ADDRESS UNALIGNED *)AddressBuffer; + SpxAddress->TAAddressCount = 1; + SpxAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IPX); + SpxAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX; + SpxAddress->Address[0].Address[0].NetworkAddress = *(UNALIGNED LONG *)pIpxAddr; + RtlCopyMemory( + SpxAddress->Address[0].Address[0].NodeAddress, + pIpxAddr+4, + 6); + + GETSHORT2SHORT( + &SpxAddress->Address[0].Address[0].Socket, + pIpxAddr + 10); + + return; +} + + + +VOID +SpxCalculateNewT1( + IN struct _SPX_CONN_FILE * pSpxConnFile, + IN int NewT1 + ) +/*++ + +Routine Description: + + +Arguments: + + NewT1 - New value for the RTT in ms. + +Return Value: + + +--*/ +{ + int baseT1, error; + + // + // VAN JACOBSEN Algorithm. From Internetworking with Tcp/ip + // (Comer) book. + // + + error = NewT1 - (pSpxConnFile->scf_AveT1 >> 3); + pSpxConnFile->scf_AveT1 += error; + if (pSpxConnFile->scf_AveT1 <= 0) // Make sure not too small + { + pSpxConnFile->scf_AveT1 = SPX_T1_MIN; + } + + if (error < 0) + error = -error; + + error -= (pSpxConnFile->scf_DevT1 >> 2); + pSpxConnFile->scf_DevT1 += error; + if (pSpxConnFile->scf_DevT1 <= 0) + pSpxConnFile->scf_DevT1 = 1; + + baseT1 = (((pSpxConnFile->scf_AveT1 >> 2) + pSpxConnFile->scf_DevT1) >> 1); + + // If less then min - set it + if (baseT1 < SPX_T1_MIN) + baseT1 = SPX_T1_MIN; + + // Set the new value + DBGPRINT(TDI, DBG, + ("SpxCalculateNewT1: Old value %lx New %lx\n", + pSpxConnFile->scf_BaseT1, baseT1)); + + pSpxConnFile->scf_BaseT1 = baseT1; + + // At the time of restarting the timer,we convert this to a tick value. + return; +} + diff --git a/private/ntos/tdi/isnp/spx/up/makefile b/private/ntos/tdi/isnp/spx/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/spx/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/spx/up/sources b/private/ntos/tdi/isnp/spx/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/spx/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc |